diff --git a/src/app/line/line.less b/src/app/line/line.less
index 661cda984..9519c2d81 100644
--- a/src/app/line/line.less
+++ b/src/app/line/line.less
@@ -235,7 +235,7 @@
.cmdtext-expanded {
overflow: auto;
- max-height: calc(var(--termlineheight) * 3.3);
+ max-height: calc(var(--termlineheight) * 3);
white-space: pre;
color: var(--term-bright-white);
font-weight: bold;
diff --git a/src/app/line/linecomps.tsx b/src/app/line/linecomps.tsx
index b525fb391..d2c645f3a 100644
--- a/src/app/line/linecomps.tsx
+++ b/src/app/line/linecomps.tsx
@@ -25,9 +25,31 @@ import * as lineutil from "./lineutil";
import { ErrorBoundary } from "@/common/error/errorboundary";
import * as appconst from "@/app/appconst";
import * as util from "@/util/util";
+import * as textmeasure from "@/util/textmeasure";
import "./line.less";
+const DebugHeightProblems = false;
+const MinLine = 0;
+const MaxLine = 1000;
+let heightLog = {};
+(window as any).heightLog = heightLog;
+(window as any).findHeightProblems = function () {
+ for (let linenum in heightLog) {
+ let lh = heightLog[linenum];
+ if (lh.heightArr == null || lh.heightArr.length < 2) {
+ continue;
+ }
+ let firstHeight = lh.heightArr[0];
+ for (let i = 1; i < lh.heightArr.length; i++) {
+ if (lh.heightArr[i] != firstHeight) {
+ console.log("line", linenum, "heights", lh.heightArr);
+ break;
+ }
+ }
+ }
+};
+
dayjs.extend(localizedFormat);
function cmdHasError(cmd: Cmd): boolean {
@@ -459,6 +481,12 @@ class LineCmd extends React.Component<
if (elem != null) {
curHeight = elem.offsetHeight;
}
+ let linenum = line.linenum;
+ if (DebugHeightProblems && linenum >= MinLine && linenum <= MaxLine) {
+ heightLog[linenum] = heightLog[linenum] || {};
+ heightLog[linenum].heightArr = heightLog[linenum].heightArr || [];
+ heightLog[linenum].heightArr.push(curHeight);
+ }
if (this.lastHeight == curHeight) {
return;
}
@@ -486,12 +514,11 @@ class LineCmd extends React.Component<
getTerminalRendererHeight(cmd: Cmd): number {
const { screen, line, width } = this.props;
- 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.getTermFontSize(), cmd.getTermMaxRows());
+ if (usedRows == 0) {
+ return 0;
}
- return height;
+ return termHeightFromRows(usedRows, GlobalModel.getTermFontSize(), cmd.getTermMaxRows());
}
@boundMethod
@@ -512,18 +539,18 @@ class LineCmd extends React.Component<
renderSimple() {
const { screen, line } = this.props;
const cmd = screen.getCmd(line);
- let height: number = 0;
+ let contentHeight: number = 0;
if (isBlank(line.renderer) || line.renderer == "terminal") {
- height = this.getTerminalRendererHeight(cmd);
+ contentHeight = this.getTerminalRendererHeight(cmd);
} else {
- // header is 16px tall with hide-prompt, 36px otherwise
const { screen, line, width } = this.props;
- const hidePrompt = getIsHidePrompt(line);
- const usedRows = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width);
- height = (hidePrompt ? 16 + 6 : 36 + 6) + usedRows;
+ contentHeight = screen.getUsedRows(lineutil.getRendererContext(line), line, cmd, width);
+ }
+ const mainDivCn = cn("line", "line-cmd");
+ if (DebugHeightProblems && line.linenum >= MinLine && line.linenum <= MaxLine) {
+ heightLog[line.linenum] = heightLog[line.linenum] || {};
+ heightLog[line.linenum].contentHeight = contentHeight;
}
- const formattedTime = lineutil.getLineDateTimeStr(line.ts);
- const mainDivCn = cn("line", "line-cmd", "line-simple");
return (
);
}
@@ -695,6 +722,12 @@ class LineCmd extends React.Component<
const termFontSize = GlobalModel.getTermFontSize();
const containerType = screen.getContainerType();
const isMinimized = line.linestate["wave:min"] && containerType == appconst.LineContainer_Main;
+ const lhv: LineChromeHeightVars = {
+ numCmdLines: lineutil.countCmdLines(cmd.getCmdStr()),
+ zeroHeight: isMinimized,
+ hasLine2: !hidePrompt,
+ };
+ const chromeHeight = textmeasure.calcLineChromeHeight(GlobalModel.lineHeightEnv, lhv);
return (
this.sidebarSize.set(size))();
diff --git a/src/app/workspace/workspaceview.tsx b/src/app/workspace/workspaceview.tsx
index db81e444a..eee8db511 100644
--- a/src/app/workspace/workspaceview.tsx
+++ b/src/app/workspace/workspaceview.tsx
@@ -12,7 +12,7 @@ import { CmdInput } from "./cmdinput/cmdinput";
import { ScreenView } from "./screen/screenview";
import { ScreenTabs } from "./screen/tabs";
import { ErrorBoundary } from "@/common/error/errorboundary";
-import { MagicLayout } from "../magiclayout";
+import * as textmeasure from "@/util/textmeasure";
import "./workspace.less";
dayjs.extend(localizedFormat);
@@ -34,7 +34,7 @@ class WorkspaceView extends React.Component<{}, {}> {
let activeScreen = session.getActiveScreen();
let cmdInputHeight = model.inputModel.cmdInputHeight.get();
if (cmdInputHeight == 0) {
- cmdInputHeight = MagicLayout.CmdInputHeight; // this is the base size of cmdInput (measured using devtools)
+ cmdInputHeight = textmeasure.baseCmdInputHeight(GlobalModel.lineHeightEnv); // this is the base size of cmdInput (measured using devtools)
}
let isHidden = GlobalModel.activeMainView.get() != "session";
let mainSidebarModel = GlobalModel.mainSidebarModel;
diff --git a/src/models/forwardlinecontainer.ts b/src/models/forwardlinecontainer.ts
index d04b6abb8..948b6f50d 100644
--- a/src/models/forwardlinecontainer.ts
+++ b/src/models/forwardlinecontainer.ts
@@ -8,6 +8,7 @@ import { Model } from "./model";
import { GlobalCommandRunner } from "./global";
import { Cmd } from "./cmd";
import { Screen } from "./screen";
+import * as lineutil from "@/app/line/lineutil";
class ForwardLineContainer {
globalModel: Model;
@@ -30,7 +31,7 @@ class ForwardLineContainer {
if (termWrap != null) {
let fontSize = this.globalModel.getTermFontSize();
let cols = windowWidthToCols(winSize.width, fontSize);
- let rows = windowHeightToRows(winSize.height, fontSize);
+ let rows = windowHeightToRows(Model.getInstance().lineHeightEnv, this.winSize.height);
termWrap.resizeCols(cols);
GlobalCommandRunner.resizeScreen(this.screen.screenId, rows, cols, { include: [this.lineId] });
}
diff --git a/src/models/model.ts b/src/models/model.ts
index 94452043e..025fd4e63 100644
--- a/src/models/model.ts
+++ b/src/models/model.ts
@@ -104,6 +104,7 @@ class Model {
name: "devicePixelRatio",
});
remotesModel: RemotesModel;
+ lineHeightEnv: LineHeightEnv;
inputModel: InputModel;
pluginsModel: PluginsModel;
@@ -343,27 +344,31 @@ class Model {
return this.termFontSize.get();
}
- 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;
- }
+ updateTermFontSizeVars() {
+ let lhe = this.recomputeLineHeightEnv();
+ mobx.action(() => {
+ this.bumpRenderVersion();
+ this.setStyleVar("--termfontsize", lhe.fontSize + "px");
+ this.setStyleVar("--termlineheight", lhe.lineHeight + "px");
+ this.setStyleVar("--termpad", lhe.pad + "px");
+ this.setStyleVar("--termfontsize-sm", lhe.fontSizeSm + "px");
+ this.setStyleVar("--termlineheight-sm", lhe.lineHeightSm + "px");
+ })();
+ }
+
+ recomputeLineHeightEnv(): LineHeightEnv {
+ const fontSize = this.getTermFontSize();
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", monoFontSize.pad + "px");
- this.setStyleVar("--termfontsize-sm", fontSizeSm + "px");
- this.setStyleVar("--termlineheight-sm", monoFontSizeSm.height + "px");
- })();
+ this.lineHeightEnv = {
+ fontSize: fontSize,
+ fontSizeSm: fontSizeSm,
+ lineHeight: monoFontSize.height,
+ lineHeightSm: monoFontSizeSm.height,
+ pad: monoFontSize.pad,
+ };
+ return this.lineHeightEnv;
}
setStyleVar(name: string, value: string) {
@@ -1181,11 +1186,11 @@ class Model {
loadFonts(newFontFamily);
document.fonts.ready.then(() => {
clearMonoFontCache();
- this.updateTermFontSizeVars(this.termFontSize.get(), true); // forces an update of css vars
+ this.updateTermFontSizeVars(); // forces an update of css vars
this.bumpRenderVersion();
});
} else if (fsUpdated) {
- this.updateTermFontSizeVars(newFontSize, true);
+ this.updateTermFontSizeVars();
}
}
diff --git a/src/models/screen.ts b/src/models/screen.ts
index 0bec6e49c..6b88e29db 100644
--- a/src/models/screen.ts
+++ b/src/models/screen.ts
@@ -16,6 +16,7 @@ import { GlobalCommandRunner } from "./global";
import { Cmd } from "./cmd";
import { ScreenLines } from "./screenlines";
import { getTermPtyData } from "@/util/modelutil";
+import * as textmeasure from "@/util/textmeasure";
class Screen {
globalModel: Model;
@@ -402,8 +403,9 @@ class Screen {
return;
}
this.lastScreenSize = winSize;
+ let useableHeight = winSize.height - textmeasure.calcMaxLineChromeHeight(this.globalModel.lineHeightEnv);
let cols = windowWidthToCols(winSize.width, this.globalModel.getTermFontSize());
- let rows = windowHeightToRows(winSize.height, this.globalModel.getTermFontSize());
+ let rows = windowHeightToRows(this.globalModel.lineHeightEnv, winSize.height);
this._termSizeCallback(rows, cols);
}
@@ -417,7 +419,8 @@ class Screen {
let minSize = MagicLayout.ScreenMinContentSize;
let maxSize = MagicLayout.ScreenMaxContentSize;
let width = boundInt(winSize.width - MagicLayout.ScreenMaxContentWidthBuffer, minSize, maxSize);
- let height = boundInt(winSize.height - MagicLayout.ScreenMaxContentHeightBuffer, minSize, maxSize);
+ let maxLineBuffer = textmeasure.calcMaxLineChromeHeight(this.globalModel.lineHeightEnv);
+ let height = boundInt(winSize.height - maxLineBuffer, minSize, maxSize);
return { width, height };
}
diff --git a/src/plugins/openai/openai.less b/src/plugins/openai/openai.less
index 359b2b561..01071aa51 100644
--- a/src/plugins/openai/openai.less
+++ b/src/plugins/openai/openai.less
@@ -1,4 +1,8 @@
.openai-renderer {
+ font-family: var(--termfontfamily);
+ font-size: var(--termfontsize);
+ line-height: var(--termlineheight);
+
.openai-message {
display: flex;
flex-direction: row;
@@ -19,11 +23,11 @@
.openai-content-user {
color: var(--app-text-color);
font-family: var(--markdown-font);
- font-size: var(--markdown-font-size);
font-weight: normal;
}
.openai-content-assistant {
+ font-family: var(--markdown-font);
color: var(--app-text-color);
}
diff --git a/src/plugins/terminal/term.ts b/src/plugins/terminal/term.ts
index aa5258f27..f39417d90 100644
--- a/src/plugins/terminal/term.ts
+++ b/src/plugins/terminal/term.ts
@@ -305,7 +305,7 @@ class TermWrap {
resizeWindow(size: WindowSize): void {
let cols = windowWidthToCols(size.width, this.fontSize);
- let rows = windowHeightToRows(size.height, this.fontSize);
+ let rows = windowHeightToRows(GlobalModel.lineHeightEnv, size.height);
this.resize({ rows, cols });
}
diff --git a/src/types/custom.d.ts b/src/types/custom.d.ts
index a4187d49c..56a286269 100644
--- a/src/types/custom.d.ts
+++ b/src/types/custom.d.ts
@@ -845,6 +845,22 @@ declare global {
getContainerType(): LineContainerStrs;
};
+ // the "environment" for computing a line's height (stays constant for a given term font family / size)
+ type LineHeightEnv = {
+ fontSize: number;
+ fontSizeSm: number;
+ lineHeight: number;
+ lineHeightSm: number;
+ pad: number;
+ };
+
+ // the "variables" for computing a line's height (changes per line)
+ type LineChromeHeightVars = {
+ numCmdLines: number;
+ zeroHeight: boolean;
+ hasLine2: boolean;
+ };
+
type MonoFontSize = {
height: number;
width: number;
diff --git a/src/util/textmeasure.ts b/src/util/textmeasure.ts
index 67ea95243..0d7c24c2e 100644
--- a/src/util/textmeasure.ts
+++ b/src/util/textmeasure.ts
@@ -71,9 +71,8 @@ function windowWidthToCols(width: number, fontSize: number): number {
return cols;
}
-function windowHeightToRows(height: number, fontSize: number): number {
- let dr = getMonoFontSize(fontSize);
- let rows = Math.floor((height - MagicLayout.ScreenMaxContentHeightBuffer) / dr.height) - 1;
+function windowHeightToRows(lhe: LineHeightEnv, height: number): number {
+ let rows = Math.floor((height - calcMaxLineChromeHeight(lhe)) / lhe.lineHeight) - 1;
if (rows <= 0) {
rows = 1;
}
@@ -99,6 +98,28 @@ function termHeightFromRows(rows: number, fontSize: number, totalRows: number):
return Math.ceil(realHeight * rows);
}
+function calcLineChromeHeight(lhe: LineHeightEnv, lhv: LineChromeHeightVars): number {
+ const topPadding = lhe.pad * 2;
+ const botPadding = lhe.pad * 2 + 1;
+ const headerLine1 = lhe.lineHeightSm;
+ const headerLine2 = lhv.hasLine2 ? lhe.lineHeight * Math.min(lhv.numCmdLines, 3) + 2 : 0;
+ const contentSpacer = lhv.zeroHeight ? 0 : lhe.pad + 2;
+ return topPadding + botPadding + headerLine1 + headerLine2 + contentSpacer;
+}
+
+function calcMaxLineChromeHeight(lhe: LineHeightEnv): number {
+ return calcLineChromeHeight(lhe, { numCmdLines: 3, hasLine2: true, zeroHeight: false });
+}
+
+function baseCmdInputHeight(lhe: LineHeightEnv): number {
+ const topPadding = lhe.pad * 2;
+ const botPadding = lhe.pad * 2;
+ const border = 2;
+ const cmdInputContext = lhe.lineHeight;
+ const textArea = lhe.lineHeight + lhe.pad * 2 + lhe.pad * 2; // lineHeight + innerPad + outerPad
+ return topPadding + botPadding + border + cmdInputContext + textArea;
+}
+
export {
measureText,
getMonoFontSize,
@@ -108,4 +129,7 @@ export {
termHeightFromRows,
clearMonoFontCache,
MonoFontSizes,
+ calcLineChromeHeight,
+ calcMaxLineChromeHeight,
+ baseCmdInputHeight,
};