From 61de455b9048554fb48944847678881cf4b8d417 Mon Sep 17 00:00:00 2001 From: Mike Sawka Date: Fri, 23 Feb 2024 09:39:27 -0800 Subject: [PATCH] terminal font-size updates (margins, paddings, etc.) (#315) * reorganize line styles, split lines.less into lines.less and line.less * switch everyone to use termFontSize getter (easier to find usages) * better font-family font-size update logic * update line styles, clean up more * replace icons, fix line heights * make paddings/margins more consistent * fix more margins, make command completions use termfontfamily * updates for cmdinput margins, font sizes, etc. * fix more font sizes and margins (mostly command input) * add comment --- src/app/app.less | 6 + src/app/bookmarks/bookmarks.tsx | 2 +- src/app/clientsettings/clientsettings.tsx | 4 +- src/app/common/elements/markdown.less | 5 +- src/app/common/modals/alert.tsx | 2 +- src/app/common/modals/userinput.tsx | 2 +- .../common/modals/viewremoteconndetail.tsx | 2 +- src/app/common/prompt/prompt.less | 9 +- src/app/common/prompt/prompt.tsx | 4 +- src/app/line/line.less | 366 ++++++++++++++ src/app/line/linecomps.tsx | 193 ++++---- src/app/line/lines.less | 457 ------------------ src/app/workspace/cmdinput/cmdinput.less | 40 +- src/app/workspace/cmdinput/cmdinput.tsx | 31 -- src/app/workspace/cmdinput/textareainput.tsx | 10 +- src/app/workspace/screen/screenview.tsx | 2 +- src/models/forwardlinecontainer.ts | 2 +- src/models/historyview.ts | 4 +- src/models/model.ts | 44 +- src/models/screen.ts | 12 +- src/models/speciallinecontainer.ts | 2 +- src/plugins/code/code.tsx | 4 +- src/plugins/core/basicrenderer.tsx | 5 +- src/plugins/csv/csv.tsx | 2 +- src/plugins/image/image.less | 1 - src/plugins/markdown/markdown.less | 1 - src/plugins/terminal/terminal.tsx | 2 +- 27 files changed, 569 insertions(+), 645 deletions(-) create mode 100644 src/app/line/line.less diff --git a/src/app/app.less b/src/app/app.less index 64cc31eb5..c0e80248e 100644 --- a/src/app/app.less +++ b/src/app/app.less @@ -2,6 +2,12 @@ :root { --fa-style-family: "Font Awesome 6 Sharp"; + + // these variables are overridden by user settings + --termfontfamily: "JetBrains Mono", monospace; + --termfontsize: 12px; + --termlineheight: 15px; + --termpad: 7px; // padding value (scaled to termfontsize) } html, diff --git a/src/app/bookmarks/bookmarks.tsx b/src/app/bookmarks/bookmarks.tsx index 133fa3d89..b3023220c 100644 --- a/src/app/bookmarks/bookmarks.tsx +++ b/src/app/bookmarks/bookmarks.tsx @@ -160,7 +160,7 @@ class Bookmark extends React.Component {
{bm.bookmarkid.substr(0, 8)}
- + {
- + {message?.message} diff --git a/src/app/common/modals/userinput.tsx b/src/app/common/modals/userinput.tsx index 89223783f..c43abfc87 100644 --- a/src/app/common/modals/userinput.tsx +++ b/src/app/common/modals/userinput.tsx @@ -59,7 +59,7 @@ export const UserInputModal = (userInputRequest: UserInputRequest) => {
- + {userInputRequest.querytext}
diff --git a/src/app/common/modals/viewremoteconndetail.tsx b/src/app/common/modals/viewremoteconndetail.tsx index cc93e122c..27cff7bfb 100644 --- a/src/app/common/modals/viewremoteconndetail.tsx +++ b/src/app/common/modals/viewremoteconndetail.tsx @@ -291,7 +291,7 @@ class ViewRemoteConnDetailModal extends React.Component<{}, {}> { let model = this.model; let isTermFocused = this.model.remoteTermWrapFocus.get(); - let termFontSize = GlobalModel.termFontSize.get(); + let termFontSize = GlobalModel.getTermFontSize(); let termWidth = textmeasure.termWidthFromCols(appconst.RemotePtyCols, termFontSize); let remoteAliasText = util.isBlank(remote.remotealias) ? "(none)" : remote.remotealias; let selectedRemoteStatus = this.getSelectedRemote().status; diff --git a/src/app/common/prompt/prompt.less b/src/app/common/prompt/prompt.less index fd3f6e1a9..0a59057fd 100644 --- a/src/app/common/prompt/prompt.less +++ b/src/app/common/prompt/prompt.less @@ -2,6 +2,8 @@ .term-prompt { font-weight: normal; + font-size: var(--termfontsize); + line-height: var(--termlineheight); font-family: var(--termfontfamily); .icon { @@ -12,6 +14,10 @@ fill: @wave-green; } + i { + margin-right: 0.25em; // em for relative sizing + } + .term-prompt-branch { color: @term-white; } @@ -21,9 +27,6 @@ } .term-prompt-remote { - i { - margin-right: 0; - } } .term-prompt-remote { diff --git a/src/app/common/prompt/prompt.tsx b/src/app/common/prompt/prompt.tsx index 66e337990..347c8c96c 100644 --- a/src/app/common/prompt/prompt.tsx +++ b/src/app/common/prompt/prompt.tsx @@ -8,7 +8,6 @@ import localizedFormat from "dayjs/plugin/localizedFormat"; import { GlobalModel } from "@/models"; import cn from "classnames"; import { isBlank } from "@/util/util"; -import { ReactComponent as FolderIcon } from "@/assets/icons/folder.svg"; import "./prompt.less"; @@ -76,6 +75,7 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record ; } + let termFontSize = GlobalModel.getTermFontSize(); let remote = GlobalModel.getRemote(this.props.rptr.remoteid); let remoteStr = getRemoteStr(rptr); let festate = this.props.festate ?? {}; @@ -96,7 +96,7 @@ class Prompt extends React.Component<{ rptr: RemotePtrType; festate: Record - + {cwd} ); diff --git a/src/app/line/line.less b/src/app/line/line.less new file mode 100644 index 000000000..6793d4ed7 --- /dev/null +++ b/src/app/line/line.less @@ -0,0 +1,366 @@ +@import "@/common/themes/themes.less"; + +.line { + margin: 1rem 1rem 0 1rem; + padding: var(--termpad) var(--termpad) var(--termpad) var(--termpad); + border-radius: 6px; + display: flex; + overflow: hidden; + flex-shrink: 0; + position: relative; + background: @base-background; + border: 1px solid transparent; + + &.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; + } + } + + &.line-invalid { + color: @term-white; + margin-left: 5px; + } + + &.selected { + border: 1px solid rgba(@base-color, 0.8) !important; + box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset; + } + + &.active { + border: 1px solid rgba(@wave-green, 0.8) !important; + box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset; + } + + .load-error-text { + color: @term-red; + padding-top: 5px; + } + + .renderer-loading { + padding-top: 5px; + } + + .sidebar-message { + color: @term-yellow; + } + + .focus-indicator { + height: calc(100% - 7px); + top: 5px; + } + + &.line-simple { + font-size: 11px; + line-height: 1.2; + color: rgba(@base-color, 0.6); + + .simple-line-header { + display: flex; + margin-top: 5px; + flex-direction: row; + } + + .ts { + display: flex; + } + } + + .simple-line-status { + .linenum { + cursor: pointer; + } + + .avatar { + display: inline-block; + height: 1em; + margin-left: 0.5em; + margin-right: 0.5em; + vertical-align: text-top; + fill: @base-color; + } + + .success { + color: @wave-green; + fill: @wave-green; + } + + .fail { + color: @error-red; + fill: @error-red; + } + + .warning { + color: @warning-yellow; + fill: @warning-yellow; + } + } + + &.top-border { + border-top: 1px solid #777; + padding: 10px; + } + + &:nth-child(2) { + margin: 0px 5px 5px 5px; + padding: 0px 5px 5px 12px; + border-top: none; + } + + &:hover .meta .termopts { + display: block; + } + + &:hover .meta .settings { + display: block; + } + + .line-header { + display: flex; + flex-direction: row; + width: 100%; + font-weight: normal; + font-family: var(--termfontfamily); + font-size: var(--termfontsize); + line-height: var(--termlineheight); + + &.is-expanded { + height: auto; + } + + .line-icon { + display: block; + visibility: hidden; + cursor: pointer; + padding: 3px; + border-radius: 50%; + } + + .line-icon-show { + visibility: visible; + } + + .line-icon + .line-icon { + margin-left: 5px; + } + + .line-icon.active { + visibility: visible; + display: block; + } + + &:hover .line-icon { + visibility: visible; + display: block; + opacity: 1; + } + + .meta.meta-line1 { + color: rgba(@base-color, 0.6) !important; + } + + .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: lighten(@soft-blue, 10%); + font-weight: bold; + margin-top: 1px; + margin-right: 10px; + } + + .ts, + .termopts, + .renderer { + display: flex; + } + + .renderer { + margin-left: 3px; + svg { + width: 1em; + margin-right: 0.5em; + fill: rgba(@base-color, 0.6); + } + } + + .settings { + display: none; + margin-left: 0.5em; + cursor: pointer; + width: 1em; + height: 1em; + border-radius: 50%; + line-height: 1em; + svg { + fill: rgba(@base-color, 0.6); + &:hover { + fill: @base-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: @term-white; + cursor: pointer; + } + } + + .cmdtext-expanded-wrapper { + padding-left: 6px; + overflow-y: auto; + max-height: 60px; + border-left: 2px solid #333; + margin-left: 3px; + + .cmdtext-expanded { + white-space: pre; + + color: @term-white; + padding-bottom: 5px; + } + } + + .cmd-hints { + position: absolute; + bottom: 0px; + right: 0px; + display: none; + + .hint-item { + border-radius: 3px; + } + } + + .image-wrapper { + .loading-div { + height: 20px; + margin-left: 50px; + } + } + + &.has-rtnstate .terminal-wrapper { + padding-bottom: 0; + } + + .terminal-wrapper { + line-height: normal; + + &.zero-height { + padding: 0; + margin: 0; + } + + .terminal-connectelem { + overflow-y: hidden; + overflow-x: hidden; + } + + &.cmd-done .terminal .xterm-cursor { + display: none; + } + + .terminal-loading-message { + position: absolute; + top: calc(40% - 8px); + left: 50px; + height: 20px; + } + } + + .cmd-rtnstate { + position: relative; + + .cmd-rtnstate-label { + font-family: var(--termfontfamily); + position: relative; + font-size: 10px; + z-index: 2; + margin: 6px 0 2px 10px; + padding: 2px 5px 0px 5px; + display: inline-block; + font-size: 10px; + color: @term-white; + background-color: #151715; + } + + .cmd-rtnstate-diff { + font-family: var(--termfontfamily); + color: @term-white; + white-space: pre; + margin-left: 10px; + padding-left: 10px; + padding-bottom: 1px; + font-size: 11px; + line-height: 1.2; + max-height: 50px; + overflow-y: auto; + direction: rtl; + + .cmd-rtnstate-diff-inner { + direction: ltr; + } + } + + .cmd-rtnstate-sep { + height: 1px; + border-bottom: 1px solid #333; + position: relative; + top: -10px; + width: min(300px, 50%); + margin-bottom: -4px; + } + } +} diff --git a/src/app/line/linecomps.tsx b/src/app/line/linecomps.tsx index cdeb506cf..d33c73a03 100644 --- a/src/app/line/linecomps.tsx +++ b/src/app/line/linecomps.tsx @@ -25,17 +25,9 @@ import * as lineutil from "./lineutil"; import { ErrorBoundary } from "@/common/error/errorboundary"; import * as appconst from "@/app/appconst"; -import { ReactComponent as CheckIcon } from "@/assets/icons/line/check.svg"; -import { ReactComponent as CommentIcon } from "@/assets/icons/line/comment.svg"; -import { ReactComponent as QuestionIcon } from "@/assets/icons/line/question.svg"; -import { ReactComponent as WarningIcon } from "@/assets/icons/line/triangle-exclamation.svg"; -import { ReactComponent as XmarkIcon } from "@/assets/icons/line/xmark.svg"; import { ReactComponent as FillIcon } from "@/assets/icons/line/fill.svg"; -import { ReactComponent as GearIcon } from "@/assets/icons/line/gear.svg"; -import { RotateIcon } from "@/common/icons/icons"; - -import "./lines.less"; +import "./line.less"; dayjs.extend(localizedFormat); @@ -44,41 +36,40 @@ class SmallLineAvatar extends React.Component<{ line: LineType; cmd: Cmd; onRigh render() { const { line, cmd } = this.props; const lineNumStr = (line.linenumtemp ? "~" : "#") + String(line.linenum); - const status = cmd != null ? cmd.getStatus() : "done"; - const rtnstate = cmd != null ? cmd.getRtnState() : false; + let status = cmd != null ? cmd.getStatus() : "done"; const exitcode = cmd != null ? cmd.getExitCode() : 0; const isComment = line.linetype == "text"; let icon = null; let iconTitle = null; if (isComment) { - icon = ; + icon = ; iconTitle = "comment"; } else if (status == "done") { if (exitcode === 0) { - icon = ; + icon = ; iconTitle = "success"; } else { - icon = ; + icon = ; iconTitle = "exitcode " + exitcode; } } else if (status == "hangup") { - icon = ; + icon = ; iconTitle = status; } else if (status == "error") { - icon = ; + icon = ; iconTitle = "error"; - } else if (status == "running" || "detached") { - icon = ; + } else if (status == "running" || status == "detached") { + icon = ; iconTitle = "running"; } else { - icon = ; + icon = ; iconTitle = "unknown"; } return (
{lineNumStr}
{icon}
@@ -88,52 +79,22 @@ class SmallLineAvatar extends React.Component<{ line: LineType; cmd: Cmd; onRigh } @mobxReact.observer -class LineCmd extends React.Component< - { - screen: LineContainerType; - line: LineType; - width: number; - staticRender: boolean; - visible: OV; - onHeightChange: LineHeightChangeCallbackType; - renderMode: RenderModeType; - overrideCollapsed: OV; - noSelect?: boolean; - showHints?: boolean; - }, - {} -> { - lineRef: React.RefObject = React.createRef(); - cmdTextRef: React.RefObject = React.createRef(); +class RtnState extends React.Component<{ cmd: Cmd; line: LineType }> { rtnStateDiff: mobx.IObservableValue = mobx.observable.box(null, { name: "linecmd-rtn-state-diff", }); rtnStateDiffFetched: boolean = false; - lastHeight: number; - isOverflow: OV = mobx.observable.box(false, { - name: "line-overflow", - }); - isCmdExpanded: OV = mobx.observable.box(false, { - name: "cmd-expanded", - }); - constructor(props) { - super(props); + componentDidMount() { + this.componentDidUpdate(); + } + + componentDidUpdate() { + this.checkStateDiffLoad(); } checkStateDiffLoad(): void { - const { screen, line, staticRender, visible } = this.props; - if (staticRender) { - return; - } - if (!visible.get()) { - if (this.rtnStateDiffFetched) { - this.rtnStateDiffFetched = false; - this.setRtnStateDiff(null); - } - return; - } - const cmd = screen.getCmd(line); + let cmd = this.props.cmd; if (cmd == null || !cmd.getRtnState() || this.rtnStateDiffFetched) { return; } @@ -179,6 +140,68 @@ class LineCmd extends React.Component< })(); } + render() { + let { cmd } = this.props; + const rsdiff = this.rtnStateDiff.get(); + const termFontSize = GlobalModel.getTermFontSize(); + let rtnStateDiffSize = termFontSize - 2; + if (rtnStateDiffSize < 10) { + rtnStateDiffSize = Math.max(termFontSize, 10); + } + return ( +
+ +
state unchanged
+
+
+ +
new state
+
+
+
{this.rtnStateDiff.get()}
+
+
+
+ ); + } +} + +@mobxReact.observer +class LineCmd extends React.Component< + { + screen: LineContainerType; + line: LineType; + width: number; + staticRender: boolean; + visible: OV; + onHeightChange: LineHeightChangeCallbackType; + renderMode: RenderModeType; + overrideCollapsed: OV; + noSelect?: boolean; + showHints?: boolean; + }, + {} +> { + lineRef: React.RefObject = React.createRef(); + cmdTextRef: React.RefObject = React.createRef(); + lastHeight: number; + isOverflow: OV = mobx.observable.box(false, { + name: "line-overflow", + }); + isCmdExpanded: OV = mobx.observable.box(false, { + name: "cmd-expanded", + }); + + constructor(props) { + super(props); + } + componentDidMount() { this.componentDidUpdate(null, null, null); this.checkCmdText(); @@ -241,7 +264,6 @@ class LineCmd extends React.Component< componentDidUpdate(prevProps, prevState, snapshot: { height: number }): void { this.handleHeightChange(); - this.checkStateDiffLoad(); this.checkCmdText(); } @@ -385,7 +407,7 @@ class LineCmd extends React.Component< let height = 45 + 24; // height of zero height terminal const usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width); if (usedRows > 0) { - height = 48 + 24 + termHeightFromRows(usedRows, GlobalModel.termFontSize.get(), cmd.getTermMaxRows()); + height = 48 + 24 + termHeightFromRows(usedRows, GlobalModel.getTermFontSize(), cmd.getTermMaxRows()); } return height; } @@ -452,7 +474,6 @@ class LineCmd extends React.Component< renderMeta1(cmd: Cmd) { let { line } = this.props; - let termOpts = cmd.getTermOpts(); let formattedTime: string = ""; let restartTs = cmd.getRestartTs(); let timeTitle: string = null; @@ -472,16 +493,10 @@ class LineCmd extends React.Component<
 
- + {renderer} 
-
- ({termOpts.rows}x{termOpts.cols}) -
-
- -
); } @@ -583,6 +598,7 @@ class LineCmd extends React.Component<
); } + const isRtnState = cmd.getRtnState() && false; // turning off rtnstate for now const isSelected = mobx .computed(() => screen.getSelectedLine() == line.linenum, { name: "computed-isSelected", @@ -621,14 +637,13 @@ class LineCmd extends React.Component< .get(); const isRunning = cmd.isRunning(); const isExpanded = this.isCmdExpanded.get(); - const rsdiff = this.rtnStateDiff.get(); const mainDivCn = cn( "line", "line-cmd", { selected: isSelected }, { active: isSelected && isFocused }, { "cmd-done": !isRunning }, - { "has-rtnstate": cmd.getRtnState() } + { "has-rtnstate": isRtnState } ); let rendererPlugin: RendererPluginType = null; const isNoneRenderer = line.renderer == "none"; @@ -637,11 +652,7 @@ class LineCmd extends React.Component< } const rendererType = lineutil.getRendererType(line); const hidePrompt = rendererPlugin?.hidePrompt; - const termFontSize = GlobalModel.termFontSize.get(); - let rtnStateDiffSize = termFontSize - 2; - if (rtnStateDiffSize < 10) { - rtnStateDiffSize = Math.max(termFontSize, 10); - } + const termFontSize = GlobalModel.getTermFontSize(); const containerType = screen.getContainerType(); return (
+
+ +
- +
  showing in sidebar =>
@@ -752,26 +771,8 @@ class LineCmd extends React.Component< />
- -
- -
state unchanged
-
-
- -
new state
-
-
-
{this.rtnStateDiff.get()}
-
-
-
+ +
diff --git a/src/app/line/lines.less b/src/app/line/lines.less index 8d722de0d..043f90dd0 100644 --- a/src/app/line/lines.less +++ b/src/app/line/lines.less @@ -1,462 +1,5 @@ @import "@/common/themes/themes.less"; -.line.line-text { - flex-direction: row; - padding-top: 5px; - - .line-content { - display: flex; - flex-direction: column; - flex-grow: 1; - margin-left: 10px; - } -} - -.line .load-error-text { - color: @term-red; - padding-top: 5px; -} - -.line .renderer-loading { - padding-top: 5px; -} - -.line.line-cmd { - flex-direction: column; - scroll-margin-bottom: 20px; - position: relative; - - .plus { - opacity: 0.5; - } - &:hover { - .plus { - opacity: 1; - } - } - - .sidebar-message { - color: @term-yellow; - } - - .line-header { - display: flex; - flex-direction: row; - padding-bottom: 0.7rem; - width: 100%; - line-height: 1.2; - - &.is-expanded { - height: auto; - } - - &.hide-prompt { - height: 32px; - } - - .line-icon { - display: block; - visibility: hidden; - cursor: pointer; - padding: 3px; - border-radius: 50%; - font-size: 14px; - } - - .line-icon-show { - visibility: visible; - } - - .line-icon + .line-icon { - margin-left: 5px; - } - - .line-icon.active { - visibility: visible; - display: block; - } - - &:hover .line-icon { - visibility: visible; - display: block; - opacity: 1; - } - } - - .meta.meta-line1 { - color: rgba(@base-color, 0.6) !important; - font-size: 11px; - } - - .meta.meta-line2 { - margin-left: -2px; - } - - &.has-rtnstate .terminal-wrapper { - padding-bottom: 0; - } - - .image-wrapper { - .loading-div { - height: 20px; - margin-left: 50px; - } - } - - .terminal-wrapper { - line-height: normal; - - &.zero-height { - padding: 0; - margin: 0; - } - - .terminal-connectelem { - overflow-y: hidden; - overflow-x: hidden; - } - - &.cmd-done .terminal .xterm-cursor { - display: none; - } - - .terminal-loading-message { - position: absolute; - top: calc(40% - 8px); - left: 50px; - height: 20px; - } - } - - .cmd-rtnstate { - position: relative; - - .cmd-rtnstate-label { - font-family: var(--termfontfamily); - position: relative; - font-size: 10px; - z-index: 2; - margin: 6px 0 2px 10px; - padding: 2px 5px 0px 5px; - display: inline-block; - font-size: 10px; - color: @term-white; - background-color: #151715; - } - - .cmd-rtnstate-diff { - font-family: var(--termfontfamily); - color: @term-white; - white-space: pre; - margin-left: 10px; - padding-left: 10px; - padding-bottom: 1px; - font-size: 11px; - line-height: 1.2; - max-height: 50px; - overflow-y: auto; - direction: rtl; - - .cmd-rtnstate-diff-inner { - direction: ltr; - } - } - - .cmd-rtnstate-sep { - height: 1px; - border-bottom: 1px solid #333; - position: relative; - top: -10px; - width: min(300px, 50%); - margin-bottom: -4px; - } - } -} - -.line { - margin: 1rem 1rem 0 1rem; - padding: 1rem; - border-radius: 6px; - display: flex; - overflow: hidden; - flex-shrink: 0; - position: relative; - background: @base-background; - border: 1px solid transparent; - - &.selected { - border: 1px solid rgba(@base-color, 0.8) !important; - box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset; - } - - &.active { - border: 1px solid rgba(@wave-green, 0.8) !important; - box-shadow: 0px 0px 0.5px 0px rgba(255, 255, 255, 0.5) inset, 0px 0.5px 0px 0px rgba(255, 255, 255, 0.2) inset; - } - - .focus-indicator { - height: calc(100% - 7px); - top: 5px; - } - - &.line-simple { - font-size: 11px; - line-height: 1.2; - color: rgba(@base-color, 0.6); - - .simple-line-header { - display: flex; - margin-top: 5px; - flex-direction: row; - } - - .ts { - display: flex; - } - } - - .simple-line-status { - .linenum { - cursor: pointer; - } - - &.has-rtnstate { - .linenum { - font-weight: bold; - } - } - - .avatar { - display: inline-block; - width: 1em; - height: 1em; - margin: 0 0.5em; - vertical-align: text-top; - fill: @base-color; - } - - .success { - fill: @wave-green; - } - - .fail { - fill: @error-red; - } - - .warning { - fill: @warning-yellow; - } - } - - &.top-border { - border-top: 1px solid #777; - padding: 10px; - } - - &:nth-child(2) { - margin: 0px 5px 5px 5px; - padding: 0px 5px 5px 12px; - border-top: none; - } - - &:hover .meta .termopts { - display: block; - } - - &:hover .meta .settings { - display: block; - } - - .avatar { - max-height: 38px; - width: 38px; - display: flex; - flex-shrink: 0; - align-items: center; - justify-content: center; - font-weight: bold; - color: @term-white; - - border-radius: 5px; - position: relative; - - &.rtnstate { - border: 1px solid white; - } - - .status-icon { - position: absolute; - top: 2px; - right: 2px; - } - - .comment-icon { - position: absolute; - top: 0px; - right: -4px; - } - - &.status-done { - background-color: #555; - } - - &.ephemeral { - opacity: 0.5; - outline: 1px solid white; - } - - &.status-error { - .status-icon { - color: @error-red; - } - } - - &.status-hangup { - .status-icon { - color: @term-yellow; - } - } - - &.status-running { - background-color: @soft-blue; - } - - &.status-waiting { - background-color: @term-yellow; - } - - &.status-detached { - background-color: @soft-blue; - .status-icon { - top: 3px; - right: 3px; - color: @term-white; - } - } - } - - .meta-wrap { - flex: 1 1 0px; - min-width: 0; - display: flex; - flex-direction: column; - justify-content: flex-start; - line-height: 1.2; - } - - .meta { - display: flex; - flex-direction: row; - - .simple-line-status { - margin-top: 5px; - } - - .user { - color: lighten(@soft-blue, 10%); - font-weight: bold; - margin-top: 1px; - margin-right: 10px; - } - - .ts, - .termopts, - .renderer { - display: flex; - margin-top: 5px; - } - - .renderer { - margin-left: 3px; - margin-top: 5px; - svg { - width: 1em; - margin-right: 0.5em; - fill: rgba(@base-color, 0.6); - } - } - - .settings { - display: none; - margin-left: 0.5em; - margin-top: 0.65em; - cursor: pointer; - width: 1em; - height: 1em; - border-radius: 50%; - line-height: 1em; - svg { - fill: rgba(@base-color, 0.6); - &:hover { - fill: @base-color; - } - } - } - - .termopts { - margin-top: 5px; - display: none; - .resize-button { - cursor: pointer; - padding-left: 3px; - padding-right: 3px; - } - } - - .metapart-mono { - margin-left: 8px; - margin-top: 4px; - white-space: nowrap; - } - - .cmdtext { - overflow: hidden; - margin-left: 0; - } - - .cmdtext-overflow { - flex-shrink: 0; - padding-right: 2px; - color: @term-white; - cursor: pointer; - - margin-top: 4px; - } - } - - .cmdtext-expanded-wrapper { - margin-top: 2px; - padding-left: 6px; - overflow-y: auto; - max-height: 60px; - border-left: 2px solid #333; - margin-left: 3px; - - .cmdtext-expanded { - white-space: pre; - - color: @term-white; - padding-bottom: 5px; - } - } - - .cmd-hints { - position: absolute; - bottom: 0px; - right: 0px; - display: none; - - .hint-item { - border-radius: 3px; - } - } -} - -.line.line-invalid { - color: @term-white; - margin-left: 5px; -} - .lines { display: flex; flex-direction: column; diff --git a/src/app/workspace/cmdinput/cmdinput.less b/src/app/workspace/cmdinput/cmdinput.less index fbb8d941d..148bbb384 100644 --- a/src/app/workspace/cmdinput/cmdinput.less +++ b/src/app/workspace/cmdinput/cmdinput.less @@ -6,11 +6,10 @@ display: flex; flex-direction: column; position: absolute; - bottom: 12px; - right: 12px; - width: calc(100% - 24px); - padding: 12px; - padding-bottom: 6px; + bottom: var(--termfontsize); + right: var(--termfontsize); + width: calc(100% - 2 * var(--termfontsize)); + padding: var(--termfontsize); z-index: 100; background: @background-session-components; box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.5), 0px 3px 8px 0px rgba(0, 0, 0, 0.35), @@ -24,7 +23,7 @@ } &.has-info { - padding-top: 10px; + padding-top: var(--termpad); } .focus-indicator { @@ -34,7 +33,7 @@ } &.has-history { - padding-top: 5px; + padding-top: var(--termpad); height: max(300px, 40%); } @@ -101,7 +100,11 @@ .cmd-input-field { position: relative; - padding-right: 17em; + padding-right: var(--termpad); + font-family: var(--termfontfamily); + font-weight: normal; + line-height: var(--termlineheight); + font-size: var(--termfontsize); .cmd-hints { position: absolute; @@ -115,10 +118,15 @@ .textareainput-div { position: relative; + &.control { + padding: var(--termpad) 0; + } + .shelltag { position: absolute; - bottom: 4px; - right: 3px; + // 13px = 10px height + 3px padding. subtract termpad to account for textareainput-div padding + bottom: calc(-13px + var(--termpad)); + right: 0px; font-size: 10px; color: @text-secondary; line-height: 1; @@ -131,14 +139,15 @@ textarea { color: @term-bright-white; background-color: @textarea-background; - padding: 0.5em; + padding: var(--termpad); resize: none; overflow: auto; overflow-wrap: anywhere; border-color: transparent; border: none; - font-family: var(--termfontfamily); - font-weight: normal; + line-height: var(--termlineheight); + font-size: var(--termfontsize); + &.display-disabled { background-color: #444; } @@ -350,7 +359,7 @@ .cmd-input-info { flex-shrink: 1; overflow-y: auto; - margin-bottom: 5px; + margin-bottom: var(--termpad); .info-msg { color: @soft-blue; @@ -377,6 +386,9 @@ flex-direction: row; flex-wrap: wrap; padding-bottom: 5px; + font-weight: normal; + font-family: var(--termfontfamily); + font-size: var(--termfontsize); .info-comp { min-width: 200px; diff --git a/src/app/workspace/cmdinput/cmdinput.tsx b/src/app/workspace/cmdinput/cmdinput.tsx index 70cc8c863..767089396 100644 --- a/src/app/workspace/cmdinput/cmdinput.tsx +++ b/src/app/workspace/cmdinput/cmdinput.tsx @@ -214,37 +214,6 @@ class CmdInput extends React.Component<{}, {}> { screen={screen} onHeightChange={this.handleInnerHeightUpdate} /> -
- {/**
- {inputModel.inputExpanded.get() ? "shrink" : "expand"} input ({renderCmdText("E")}) -
**/} - {!focusVal && ( -
-
focus input ({renderCmdText("I")})
-
- )} - {focusVal && ( -
- -
- close (esc) -
-
- -
- history (ctrl-r) -
-
- AI (ctrl-space) -
-
-
- )} - -
); diff --git a/src/app/workspace/cmdinput/textareainput.tsx b/src/app/workspace/cmdinput/textareainput.tsx index 6ca454e03..fbcb97038 100644 --- a/src/app/workspace/cmdinput/textareainput.tsx +++ b/src/app/workspace/cmdinput/textareainput.tsx @@ -579,11 +579,11 @@ class TextAreaInput extends React.Component<{ screen: Screen; onHeightChange: () if (activeScreen != null) { activeScreen.focusType.get(); // for reaction } - let termFontSize = 14; - // fontSize*1.5 (line-height) + 2 * 0.5em padding - let computedInnerHeight = displayLines * (termFontSize * 1.5) + 2 * 0.5 * termFontSize; - // inner height + 2*1em padding - let computedOuterHeight = computedInnerHeight + 2 * 1.0 * termFontSize; + let termFontSize = GlobalModel.getTermFontSize(); + let fontSize = getMonoFontSize(termFontSize); + let termPad = Math.floor(fontSize.height / 2); + let computedInnerHeight = displayLines * fontSize.height + 2 * termPad; + let computedOuterHeight = computedInnerHeight + 2 * termPad; let shellType: string = ""; let screen = GlobalModel.getActiveScreen(); if (screen != null) { diff --git a/src/app/workspace/screen/screenview.tsx b/src/app/workspace/screen/screenview.tsx index f37dce701..7cb8dc807 100644 --- a/src/app/workspace/screen/screenview.tsx +++ b/src/app/workspace/screen/screenview.tsx @@ -108,7 +108,7 @@ class ScreenView extends React.Component<{ session: Session; screen: Screen }, { if (screenWidth == null) { return
; } - let fontSize = GlobalModel.termFontSize.get(); + let fontSize = GlobalModel.getTermFontSize(); let dprStr = sprintf("%0.3f", GlobalModel.devicePixelRatio.get()); let viewOpts = screen.viewOpts.get(); let hasSidebar = viewOpts?.sidebar?.open; diff --git a/src/models/forwardlinecontainer.ts b/src/models/forwardlinecontainer.ts index ce0aed3b0..d04b6abb8 100644 --- a/src/models/forwardlinecontainer.ts +++ b/src/models/forwardlinecontainer.ts @@ -28,7 +28,7 @@ class ForwardLineContainer { this.winSize = winSize; let termWrap = this.getTermWrap(this.lineId); if (termWrap != null) { - let fontSize = this.globalModel.termFontSize.get(); + let fontSize = this.globalModel.getTermFontSize(); let cols = windowWidthToCols(winSize.width, fontSize); let rows = windowHeightToRows(winSize.height, fontSize); termWrap.resizeCols(cols); diff --git a/src/models/historyview.ts b/src/models/historyview.ts index b309ac9ed..6892b854a 100644 --- a/src/models/historyview.ts +++ b/src/models/historyview.ts @@ -114,8 +114,8 @@ class HistoryViewModel { this.specialLineContainer = null; } else { this.activeItem.set(hitem.historyid); - let width = termWidthFromCols(80, this.globalModel.termFontSize.get()); - let height = termHeightFromRows(25, this.globalModel.termFontSize.get(), 25); + let width = termWidthFromCols(80, this.globalModel.getTermFontSize()); + let height = termHeightFromRows(25, this.globalModel.getTermFontSize(), 25); this.specialLineContainer = new SpecialLineContainer( this, { width, height }, diff --git a/src/models/model.ts b/src/models/model.ts index 1dd71b203..32ebbd106 100644 --- a/src/models/model.ts +++ b/src/models/model.ts @@ -33,7 +33,7 @@ import { MainSidebarModel } from "./mainsidebar"; import { Screen } from "./screen"; import { Cmd } from "./cmd"; import { GlobalCommandRunner } from "./global"; -import { clearMonoFontCache } from "@/util/textmeasure"; +import { clearMonoFontCache, getMonoFontSize } from "@/util/textmeasure"; import type { TermWrap } from "@/plugins/terminal/term"; type SWLinePtr = { @@ -338,19 +338,29 @@ class Model { return this.termFontSize.get(); } - setTermFontSize(fontSize: number) { + updateTermFontSizeVars(fontSize: number, force: boolean) { + if (!force && fontSize == this.termFontSize.get()) { + return; + } if (fontSize < appconst.MinFontSize) { fontSize = appconst.MinFontSize; } if (fontSize > appconst.MaxFontSize) { fontSize = appconst.MaxFontSize; } + const monoFontSize = getMonoFontSize(fontSize); mobx.action(() => { - this.termFontSize.set(fontSize); this.bumpRenderVersion(); + this.setStyleVar("--termfontsize", fontSize + "px"); + this.setStyleVar("--termlineheight", monoFontSize.height + "px"); + this.setStyleVar("--termpad", Math.floor(monoFontSize.height / 2) + "px"); })(); } + setStyleVar(name: string, value: string) { + document.documentElement.style.setProperty(name, value); + } + getBaseWsHostPort(): string { if (this.isDev) { return appconst.DevServerWsEndpoint; @@ -1131,6 +1141,16 @@ class Model { } setClientData(clientData: ClientDataType) { + let newFontFamily = clientData?.feopts?.termfontfamily; + if (newFontFamily == null) { + newFontFamily = "JetBrains Mono"; + } + let newFontSize = clientData?.feopts?.termfontsize; + if (newFontSize == null) { + newFontSize = appconst.DefaultTermFontSize; + } + const ffUpdated = newFontFamily != this.getTermFontFamily(); + const fsUpdated = newFontSize != this.getTermFontSize(); mobx.action(() => { this.clientData.set(clientData); })(); @@ -1139,15 +1159,17 @@ class Model { shortcut = clientData?.clientopts?.globalshortcut; } getApi().reregisterGlobalShortcut(shortcut); - let fontFamily = clientData?.feopts?.termfontfamily; - if (fontFamily == null) { - fontFamily = "JetBrains Mono"; + if (ffUpdated) { + // this also updates fontSize vars + loadFonts(newFontFamily); + document.fonts.ready.then(() => { + clearMonoFontCache(); + this.updateTermFontSizeVars(this.termFontSize.get(), true); // forces an update of css vars + this.bumpRenderVersion(); + }); + } else if (fsUpdated) { + this.updateTermFontSizeVars(newFontSize, true); } - loadFonts(fontFamily); - document.fonts.ready.then(() => { - clearMonoFontCache(); - this.bumpRenderVersion(); - }); } submitCommandPacket(cmdPk: FeCmdPacketType, interactive: boolean): Promise { diff --git a/src/models/screen.ts b/src/models/screen.ts index ba8af8cab..0bec6e49c 100644 --- a/src/models/screen.ts +++ b/src/models/screen.ts @@ -402,15 +402,15 @@ class Screen { return; } this.lastScreenSize = winSize; - let cols = windowWidthToCols(winSize.width, this.globalModel.termFontSize.get()); - let rows = windowHeightToRows(winSize.height, this.globalModel.termFontSize.get()); + let cols = windowWidthToCols(winSize.width, this.globalModel.getTermFontSize()); + let rows = windowHeightToRows(winSize.height, this.globalModel.getTermFontSize()); this._termSizeCallback(rows, cols); } getMaxContentSize(): WindowSize { if (this.lastScreenSize == null) { - let width = termWidthFromCols(80, this.globalModel.termFontSize.get()); - let height = termHeightFromRows(25, this.globalModel.termFontSize.get(), 25); + let width = termWidthFromCols(80, this.globalModel.getTermFontSize()); + let height = termHeightFromRows(25, this.globalModel.getTermFontSize(), 25); return { width, height }; } let winSize = this.lastScreenSize; @@ -423,8 +423,8 @@ class Screen { getIdealContentSize(): WindowSize { if (this.lastScreenSize == null) { - let width = termWidthFromCols(80, this.globalModel.termFontSize.get()); - let height = termHeightFromRows(25, this.globalModel.termFontSize.get(), 25); + let width = termWidthFromCols(80, this.globalModel.getTermFontSize()); + let height = termHeightFromRows(25, this.globalModel.getTermFontSize(), 25); return { width, height }; } let winSize = this.lastScreenSize; diff --git a/src/models/speciallinecontainer.ts b/src/models/speciallinecontainer.ts index 77c7c98a2..0d445f41f 100644 --- a/src/models/speciallinecontainer.ts +++ b/src/models/speciallinecontainer.ts @@ -127,7 +127,7 @@ class SpecialLineContainer { } let termWrap = this.getTermWrap(cmd.lineId); if (termWrap == null) { - let cols = windowWidthToCols(width, this.globalModel.termFontSize.get()); + let cols = windowWidthToCols(width, this.globalModel.getTermFontSize()); let usedRows = this.globalModel.getContentHeight(context); if (usedRows != null) { return usedRows; diff --git a/src/plugins/code/code.tsx b/src/plugins/code/code.tsx index 69c4372ec..9e5bb5d89 100644 --- a/src/plugins/code/code.tsx +++ b/src/plugins/code/code.tsx @@ -307,7 +307,7 @@ class SourceCodeRenderer extends React.Component< let allowEditing = this.getAllowEditing(); if (!allowEditing) { const noOfLines = Math.max(this.state.code.split("\n").length, 5); - const lineHeight = Math.ceil(GlobalModel.termFontSize.get() * 1.5); + const lineHeight = Math.ceil(GlobalModel.getTermFontSize() * 1.5); _editorHeight = Math.min(noOfLines * lineHeight + 10, fullWindowHeight); } this.setState({ editorHeight: _editorHeight }, () => { @@ -434,7 +434,7 @@ class SourceCodeRenderer extends React.Component<
diff --git a/src/plugins/core/basicrenderer.tsx b/src/plugins/core/basicrenderer.tsx index 49aaab99f..e0dde114b 100644 --- a/src/plugins/core/basicrenderer.tsx +++ b/src/plugins/core/basicrenderer.tsx @@ -7,6 +7,7 @@ import * as mobx from "mobx"; import { debounce } from "throttle-debounce"; import * as util from "@/util/util"; import { GlobalModel } from "@/models"; +import cn from "classnames"; class SimpleBlobRendererModel { context: RendererContext; @@ -246,7 +247,7 @@ class SimpleBlobRenderer extends React.Component< return (
loading content @@ -259,7 +260,7 @@ class SimpleBlobRenderer extends React.Component< } let { festate, cmdstr, exitcode } = this.props.initParams.rawCmd; return ( -
+
= (props: Props) => { if (isFileTooLarge) { return ( -
+
The file size exceeds 10MB and cannot be displayed.
); diff --git a/src/plugins/image/image.less b/src/plugins/image/image.less index 9c4208fe2..248dc2096 100644 --- a/src/plugins/image/image.less +++ b/src/plugins/image/image.less @@ -1,7 +1,6 @@ @import "@/common/themes/themes.less"; .image-renderer { - padding: 10px; img { display: block; } diff --git a/src/plugins/markdown/markdown.less b/src/plugins/markdown/markdown.less index 96c0e7d73..caa57a5f7 100644 --- a/src/plugins/markdown/markdown.less +++ b/src/plugins/markdown/markdown.less @@ -2,7 +2,6 @@ .markdown-renderer { color: @term-white; - margin-bottom: 10px; .scroller { overflow-y: auto; diff --git a/src/plugins/terminal/terminal.tsx b/src/plugins/terminal/terminal.tsx index 31bd33f1e..6ea5268aa 100644 --- a/src/plugins/terminal/terminal.tsx +++ b/src/plugins/terminal/terminal.tsx @@ -148,7 +148,7 @@ class TerminalRenderer extends React.Component< .get(); let cmd = screen.getCmd(line); // will not be null let usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width); - let termHeight = termHeightFromRows(usedRows, GlobalModel.termFontSize.get(), cmd.getTermMaxRows()); + let termHeight = termHeightFromRows(usedRows, GlobalModel.getTermFontSize(), cmd.getTermMaxRows()); if (usedRows === 0) { termHeight = 0; }