mirror of
https://github.com/wavetermdev/backup.git
synced 2026-08-05 13:57:07 -07:00
move codeedit to directory. new useLongClick hook. show quick navig… (#105)
move codeedit to directory. new useLongClick hook. show quick navigation for directoryview on longclick of folder icon. lots more generic stuff for header new fitaddon for xtermjs more fixes for xtermjs scrollbars
This commit is contained in:
@@ -77,21 +77,21 @@
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
border-radius: 8px 8px 0 0;
|
||||
|
||||
.block-frame-preicon-button {
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.block-frame-default-header-iconview {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
color: var(--main-text-color);
|
||||
|
||||
.block-frame-preicon-button {
|
||||
opacity: 0.7;
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.block-frame-view-icon {
|
||||
font-size: var(--header-icon-size);
|
||||
opacity: 0.5;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { useLongClick } from "@/app/hook/useLongClick";
|
||||
import { CodeEdit } from "@/app/view/codeedit/codeedit";
|
||||
import { ErrorBoundary } from "@/element/errorboundary";
|
||||
import { CenteredDiv } from "@/element/quickelems";
|
||||
import { ContextMenuModel } from "@/store/contextmenu";
|
||||
@@ -8,7 +10,6 @@ import { atoms, globalStore, setBlockFocus, useBlockAtom } from "@/store/global"
|
||||
import * as services from "@/store/services";
|
||||
import * as WOS from "@/store/wos";
|
||||
import * as util from "@/util/util";
|
||||
import { CodeEdit } from "@/view/codeedit";
|
||||
import { PlotView } from "@/view/plotview";
|
||||
import { PreviewView, makePreviewModel } from "@/view/preview";
|
||||
import { TerminalView } from "@/view/term/term";
|
||||
@@ -210,6 +211,16 @@ function handleHeaderContextMenu(
|
||||
ContextMenuModel.showContextMenu(menu, e);
|
||||
}
|
||||
|
||||
const IconButton = React.memo(({ decl, className }: { decl: HeaderIconButton; className?: string }) => {
|
||||
const buttonRef = React.useRef<HTMLDivElement>(null);
|
||||
useLongClick(buttonRef, decl.click, decl.longClick);
|
||||
return (
|
||||
<div ref={buttonRef} className={clsx(className)} title={decl.title}>
|
||||
<i className={util.makeIconClass(decl.icon, true)} />
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
const BlockFrame_Default_Component = ({
|
||||
blockId,
|
||||
layoutModel,
|
||||
@@ -228,10 +239,10 @@ const BlockFrame_Default_Component = ({
|
||||
});
|
||||
});
|
||||
let isFocused = jotai.useAtomValue(isFocusedAtom);
|
||||
const viewIcon = jotai.useAtomValue(viewModel.viewIcon);
|
||||
const viewText = jotai.useAtomValue(viewModel.viewText);
|
||||
const preIconButton = jotai.useAtomValue(viewModel.preIconButton);
|
||||
const endIconButtons = jotai.useAtomValue(viewModel.endIconButtons);
|
||||
const viewIconUnion = util.useAtomValueSafe(viewModel.viewIcon) ?? "square";
|
||||
const headerTextUnion = util.useAtomValueSafe(viewModel.viewText);
|
||||
const preIconButton = util.useAtomValueSafe(viewModel.preIconButton);
|
||||
const endIconButtons = util.useAtomValueSafe(viewModel.endIconButtons);
|
||||
if (preview) {
|
||||
isFocused = true;
|
||||
}
|
||||
@@ -242,43 +253,65 @@ const BlockFrame_Default_Component = ({
|
||||
if (isFocused && blockData?.meta?.["frame:bordercolor:focused"]) {
|
||||
style.borderColor = blockData.meta["frame:bordercolor:focused"];
|
||||
}
|
||||
let viewIconElem: JSX.Element = null;
|
||||
if (viewIconUnion == null || typeof viewIconUnion === "string") {
|
||||
const viewIcon = viewIconUnion as string;
|
||||
viewIconElem = <div className="block-frame-view-icon">{getBlockHeaderIcon(viewIcon, blockData)}</div>;
|
||||
} else {
|
||||
viewIconElem = <IconButton decl={viewIconUnion} className="block-frame-view-icon" />;
|
||||
}
|
||||
let preIconButtonElem: JSX.Element = null;
|
||||
if (preIconButton) {
|
||||
preIconButtonElem = (
|
||||
<div className="block-frame-preicon-button" title={preIconButton.title} onClick={preIconButton.click}>
|
||||
<i className={util.makeIconClass(preIconButton.icon, true)} />
|
||||
</div>
|
||||
);
|
||||
preIconButtonElem = <IconButton decl={preIconButton} className="block-frame-preicon-button" />;
|
||||
}
|
||||
let endIconsElem: JSX.Element[] = [];
|
||||
if (endIconButtons && endIconButtons.length > 0) {
|
||||
for (let idx = 0; idx < endIconButtons.length; idx++) {
|
||||
const button = endIconButtons[idx];
|
||||
endIconsElem.push(
|
||||
<div key={idx} className="block-frame-endicon-button" title={button.title} onClick={button.click}>
|
||||
<i className={util.makeIconClass(button.icon, true)} />
|
||||
endIconsElem.push(<IconButton key={idx} decl={button} className="block-frame-endicon-button" />);
|
||||
}
|
||||
}
|
||||
const settingsDecl: HeaderIconButton = {
|
||||
elemtype: "iconbutton",
|
||||
icon: "cog",
|
||||
title: "Settings",
|
||||
click: (e) => handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose),
|
||||
};
|
||||
endIconsElem.push(
|
||||
<IconButton key="settings" decl={settingsDecl} className="block-frame-endicon-button block-frame-settings" />
|
||||
);
|
||||
const closeDecl: HeaderIconButton = {
|
||||
elemtype: "iconbutton",
|
||||
icon: "xmark-large",
|
||||
title: "Close",
|
||||
click: layoutModel?.onClose,
|
||||
};
|
||||
endIconsElem.push(
|
||||
<IconButton key="close" decl={closeDecl} className="block-frame-endicon-button block-frame-default-close" />
|
||||
);
|
||||
let headerTextElems: JSX.Element[] = [];
|
||||
if (typeof headerTextUnion === "string") {
|
||||
if (!util.isBlank(headerTextUnion)) {
|
||||
headerTextElems.push(
|
||||
<div key="text" className="block-frame-text">
|
||||
{headerTextUnion}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
} else if (Array.isArray(headerTextUnion)) {
|
||||
for (let idx = 0; idx < headerTextUnion.length; idx++) {
|
||||
const elem = headerTextUnion[idx];
|
||||
if (elem.elemtype == "iconbutton") {
|
||||
headerTextElems.push(<IconButton key={idx} decl={elem} className="block-frame-header-iconbutton" />);
|
||||
} else if (elem.elemtype == "text") {
|
||||
headerTextElems.push(
|
||||
<div key={idx} className="block-frame-text">
|
||||
{elem.text}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
endIconsElem.push(
|
||||
<div
|
||||
key="settings"
|
||||
className="block-frame-endicon-button block-frame-settings"
|
||||
onClick={(e) => handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)}
|
||||
>
|
||||
<i className="fa fa-solid fa-cog fa-fw" />
|
||||
</div>
|
||||
);
|
||||
endIconsElem.push(
|
||||
<div
|
||||
key="close"
|
||||
className={clsx("block-frame-endicon-button block-frame-default-close")}
|
||||
onClick={layoutModel?.onClose}
|
||||
>
|
||||
<i className="fa fa-solid fa-xmark-large fa-fw" />
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
@@ -300,15 +333,15 @@ const BlockFrame_Default_Component = ({
|
||||
ref={layoutModel?.dragHandleRef}
|
||||
onContextMenu={(e) => handleHeaderContextMenu(e, blockData, viewModel, layoutModel?.onClose)}
|
||||
>
|
||||
{preIconButtonElem}
|
||||
<div className="block-frame-default-header-iconview">
|
||||
{preIconButtonElem}
|
||||
<div className="block-frame-view-icon">{getBlockHeaderIcon(viewIcon, blockData)}</div>
|
||||
{viewIconElem}
|
||||
<div className="block-frame-view-type">{blockViewToName(blockData?.view)}</div>
|
||||
{settingsConfig?.blockheader?.showblockids && (
|
||||
<div className="block-frame-blockid">[{blockId.substring(0, 8)}]</div>
|
||||
)}
|
||||
</div>
|
||||
{util.isBlank(viewText) ? null : <div className="block-frame-text">{viewText}</div>}
|
||||
{headerTextElems}
|
||||
<div className="flex-spacer"></div>
|
||||
<div className="block-frame-end-icons">{endIconsElem}</div>
|
||||
</div>
|
||||
@@ -322,7 +355,7 @@ const BlockFrame_Default = React.memo(BlockFrame_Default_Component) as typeof Bl
|
||||
|
||||
const BlockFrame = React.memo((props: BlockFrameProps) => {
|
||||
const blockId = props.blockId;
|
||||
const [blockData, blockDataLoading] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const [blockData] = WOS.useWaveObjectValue<Block>(WOS.makeORef("block", blockId));
|
||||
const tabData = jotai.useAtomValue(atoms.tabAtom);
|
||||
|
||||
if (!blockId || !blockData) {
|
||||
@@ -439,7 +472,6 @@ function makeDefaultViewModel(blockId: string): ViewModel {
|
||||
}),
|
||||
preIconButton: jotai.atom(null),
|
||||
endIconButtons: jotai.atom(null),
|
||||
hasSearch: jotai.atom(false),
|
||||
};
|
||||
return viewModel;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
|
||||
export const useLongClick = (ref, onClick, onLongClick, ms = 300) => {
|
||||
const timerRef = useRef(null);
|
||||
const [longClickTriggered, setLongClickTriggered] = useState(false);
|
||||
|
||||
const startPress = useCallback(
|
||||
(e: React.MouseEvent<any>) => {
|
||||
if (onLongClick == null) {
|
||||
return;
|
||||
}
|
||||
setLongClickTriggered(false);
|
||||
timerRef.current = setTimeout(() => {
|
||||
setLongClickTriggered(true);
|
||||
onLongClick?.(e);
|
||||
}, ms);
|
||||
},
|
||||
[onLongClick, ms]
|
||||
);
|
||||
|
||||
const stopPress = useCallback(() => {
|
||||
clearTimeout(timerRef.current);
|
||||
}, []);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(e: React.MouseEvent<any>) => {
|
||||
if (longClickTriggered) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
onClick?.(e);
|
||||
},
|
||||
[longClickTriggered, onClick]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const element = ref.current;
|
||||
|
||||
if (!element) return;
|
||||
|
||||
element.addEventListener("mousedown", startPress);
|
||||
element.addEventListener("mouseup", stopPress);
|
||||
element.addEventListener("mouseleave", stopPress);
|
||||
element.addEventListener("click", handleClick);
|
||||
|
||||
return () => {
|
||||
element.removeEventListener("mousedown", startPress);
|
||||
element.removeEventListener("mouseup", stopPress);
|
||||
element.removeEventListener("mouseleave", stopPress);
|
||||
element.removeEventListener("click", handleClick);
|
||||
};
|
||||
}, [ref.current, startPress, stopPress, handleClick]);
|
||||
|
||||
return ref;
|
||||
};
|
||||
@@ -13,6 +13,12 @@ import * as services from "./services";
|
||||
import * as WOS from "./wos";
|
||||
import { WSControl } from "./ws";
|
||||
|
||||
let PLATFORM: NodeJS.Platform = "darwin";
|
||||
|
||||
function setPlatform(platform: NodeJS.Platform) {
|
||||
PLATFORM = platform;
|
||||
}
|
||||
|
||||
// TODO remove the window dependency completely
|
||||
// we should have the initialization be more orderly -- proceed directly from wave.ts instead of on its own.
|
||||
const globalStore = jotai.createStore();
|
||||
@@ -363,6 +369,7 @@ function getObjectId(obj: any): number {
|
||||
}
|
||||
|
||||
export {
|
||||
PLATFORM,
|
||||
WOS,
|
||||
atoms,
|
||||
createBlock,
|
||||
@@ -378,6 +385,7 @@ export {
|
||||
initWS,
|
||||
sendWSCommand,
|
||||
setBlockFocus,
|
||||
setPlatform,
|
||||
useBlockAtom,
|
||||
useBlockCache,
|
||||
useSettingsAtom,
|
||||
|
||||
@@ -476,9 +476,9 @@ function DirectoryPreview({ fileNameAtom, model }: DirectoryPreviewProps) {
|
||||
const [focusIndex, setFocusIndex] = React.useState(0);
|
||||
const [content, setContent] = React.useState<FileInfo[]>([]);
|
||||
const [fileName, setFileName] = jotai.useAtom(fileNameAtom);
|
||||
const [hideHiddenFiles, setHideHiddenFiles] = jotai.useAtom(model.showHiddenFiles);
|
||||
const hideHiddenFiles = jotai.useAtomValue(model.showHiddenFiles);
|
||||
const [selectedPath, setSelectedPath] = React.useState("");
|
||||
const [refreshVersion, setRefreshVersion] = React.useState(0);
|
||||
const [refreshVersion, setRefreshVersion] = jotai.useAtom(model.refreshVersion);
|
||||
|
||||
React.useEffect(() => {
|
||||
model.refreshCallback = () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { ContextMenuModel } from "@/app/store/contextmenu";
|
||||
import { Markdown } from "@/element/markdown";
|
||||
import { getBackendHostPort, globalStore, useBlockAtom } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
@@ -11,10 +12,9 @@ import * as jotai from "jotai";
|
||||
import { loadable } from "jotai/utils";
|
||||
import { useRef } from "react";
|
||||
import { CenteredDiv } from "../element/quickelems";
|
||||
import { CodeEdit } from "./codeedit";
|
||||
import { CodeEdit } from "./codeedit/codeedit";
|
||||
import { CSVView } from "./csvview";
|
||||
import { DirectoryPreview } from "./directorypreview";
|
||||
|
||||
import "./view.less";
|
||||
|
||||
const MaxFileSize = 1024 * 1024 * 10; // 10MB
|
||||
@@ -23,12 +23,11 @@ const MaxCSVSize = 1024 * 1024 * 1; // 1MB
|
||||
export class PreviewModel implements ViewModel {
|
||||
blockId: string;
|
||||
blockAtom: jotai.Atom<Block>;
|
||||
viewIcon: jotai.Atom<string>;
|
||||
viewIcon: jotai.Atom<string | HeaderIconButton>;
|
||||
viewName: jotai.Atom<string>;
|
||||
viewText: jotai.Atom<string>;
|
||||
preIconButton: jotai.Atom<IconButtonDecl>;
|
||||
endIconButtons: jotai.Atom<IconButtonDecl[]>;
|
||||
hasSearch: jotai.Atom<boolean>;
|
||||
preIconButton: jotai.Atom<HeaderIconButton>;
|
||||
endIconButtons: jotai.Atom<HeaderIconButton[]>;
|
||||
|
||||
fileName: jotai.WritableAtom<string, [string], void>;
|
||||
statFile: jotai.Atom<Promise<FileInfo>>;
|
||||
@@ -38,6 +37,7 @@ export class PreviewModel implements ViewModel {
|
||||
fileContent: jotai.Atom<Promise<string>>;
|
||||
|
||||
showHiddenFiles: jotai.PrimitiveAtom<boolean>;
|
||||
refreshVersion: jotai.PrimitiveAtom<number>;
|
||||
refreshCallback: () => void;
|
||||
|
||||
setPreviewFileName(fileName: string) {
|
||||
@@ -47,6 +47,7 @@ export class PreviewModel implements ViewModel {
|
||||
constructor(blockId: string) {
|
||||
this.blockId = blockId;
|
||||
this.showHiddenFiles = jotai.atom(true);
|
||||
this.refreshVersion = jotai.atom(0);
|
||||
this.blockAtom = WOS.getWaveObjectAtom<Block>(`block:${blockId}`);
|
||||
this.viewIcon = jotai.atom((get) => {
|
||||
let blockData = get(this.blockAtom);
|
||||
@@ -54,6 +55,30 @@ export class PreviewModel implements ViewModel {
|
||||
return blockData.meta.icon;
|
||||
}
|
||||
const mimeType = util.jotaiLoadableValue(get(this.fileMimeTypeLoadable), "");
|
||||
if (mimeType == "directory") {
|
||||
return {
|
||||
elemtype: "iconbutton",
|
||||
icon: "folder-open",
|
||||
longClick: (e: React.MouseEvent<any>) => {
|
||||
let menuItems: ContextMenuItem[] = [];
|
||||
menuItems.push({ label: "Go to Home", click: () => globalStore.set(this.fileName, "~") });
|
||||
menuItems.push({
|
||||
label: "Go to Desktop",
|
||||
click: () => globalStore.set(this.fileName, "~/Desktop"),
|
||||
});
|
||||
menuItems.push({
|
||||
label: "Go to Downloads",
|
||||
click: () => globalStore.set(this.fileName, "~/Downloads"),
|
||||
});
|
||||
menuItems.push({
|
||||
label: "Go to Documents",
|
||||
click: () => globalStore.set(this.fileName, "~/Documents"),
|
||||
});
|
||||
menuItems.push({ label: "Go to Root", click: () => globalStore.set(this.fileName, "/") });
|
||||
ContextMenuModel.showContextMenu(menuItems, e);
|
||||
},
|
||||
};
|
||||
}
|
||||
const fileName = get(this.fileName);
|
||||
return iconForFile(mimeType, fileName);
|
||||
});
|
||||
@@ -67,6 +92,7 @@ export class PreviewModel implements ViewModel {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
elemtype: "iconbutton",
|
||||
icon: "chevron-left",
|
||||
click: this.onBack.bind(this),
|
||||
};
|
||||
@@ -77,12 +103,14 @@ export class PreviewModel implements ViewModel {
|
||||
let showHiddenFiles = get(this.showHiddenFiles);
|
||||
return [
|
||||
{
|
||||
elemtype: "iconbutton",
|
||||
icon: showHiddenFiles ? "eye" : "eye-slash",
|
||||
click: () => {
|
||||
globalStore.set(this.showHiddenFiles, (prev) => !prev);
|
||||
},
|
||||
},
|
||||
{
|
||||
elemtype: "iconbutton",
|
||||
icon: "arrows-rotate",
|
||||
click: () => this.refreshCallback?.(),
|
||||
},
|
||||
@@ -90,7 +118,6 @@ export class PreviewModel implements ViewModel {
|
||||
}
|
||||
return null;
|
||||
});
|
||||
this.hasSearch = jotai.atom(false);
|
||||
|
||||
this.fileName = jotai.atom<string, [string], void>(
|
||||
(get) => {
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/**
|
||||
* Copyright (c) 2017 The xterm.js authors. All rights reserved.
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
// This file is a copy of the original xterm.js file, with the following changes:
|
||||
// - removed the allowance for the scrollbar
|
||||
|
||||
import type { FitAddon as IFitApi } from "@xterm/addon-fit";
|
||||
import type { ITerminalAddon, Terminal } from "@xterm/xterm";
|
||||
import { IRenderDimensions } from "@xterm/xterm/src/browser/renderer/shared/Types";
|
||||
|
||||
interface ITerminalDimensions {
|
||||
/**
|
||||
* The number of rows in the terminal.
|
||||
*/
|
||||
rows: number;
|
||||
|
||||
/**
|
||||
* The number of columns in the terminal.
|
||||
*/
|
||||
cols: number;
|
||||
}
|
||||
|
||||
const MINIMUM_COLS = 2;
|
||||
const MINIMUM_ROWS = 1;
|
||||
|
||||
export class FitAddon implements ITerminalAddon, IFitApi {
|
||||
private _terminal: Terminal | undefined;
|
||||
public noScrollbar: boolean = false;
|
||||
|
||||
public activate(terminal: Terminal): void {
|
||||
this._terminal = terminal;
|
||||
}
|
||||
|
||||
public dispose(): void {}
|
||||
|
||||
public fit(): void {
|
||||
const dims = this.proposeDimensions();
|
||||
if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Remove reliance on private API
|
||||
const core = (this._terminal as any)._core;
|
||||
|
||||
// Force a full render
|
||||
if (this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols) {
|
||||
core._renderService.clear();
|
||||
this._terminal.resize(dims.cols, dims.rows);
|
||||
}
|
||||
}
|
||||
|
||||
public proposeDimensions(): ITerminalDimensions | undefined {
|
||||
if (!this._terminal) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if (!this._terminal.element || !this._terminal.element.parentElement) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// TODO: Remove reliance on private API
|
||||
const core = (this._terminal as any)._core;
|
||||
const dims: IRenderDimensions = core._renderService.dimensions;
|
||||
|
||||
if (dims.css.cell.width === 0 || dims.css.cell.height === 0) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// UPDATED CODE (removed reliance on FALLBACK_SCROLL_BAR_WIDTH in viewport)
|
||||
const measuredScrollBarWidth =
|
||||
core.viewport._viewportElement.offsetWidth - core.viewport._scrollArea.offsetWidth;
|
||||
let scrollbarWidth = this._terminal.options.scrollback === 0 ? 0 : measuredScrollBarWidth;
|
||||
if (this.noScrollbar) {
|
||||
scrollbarWidth = 0;
|
||||
}
|
||||
// END UPDATED CODE
|
||||
|
||||
const parentElementStyle = window.getComputedStyle(this._terminal.element.parentElement);
|
||||
const parentElementHeight = parseInt(parentElementStyle.getPropertyValue("height"));
|
||||
const parentElementWidth = Math.max(0, parseInt(parentElementStyle.getPropertyValue("width")));
|
||||
const elementStyle = window.getComputedStyle(this._terminal.element);
|
||||
const elementPadding = {
|
||||
top: parseInt(elementStyle.getPropertyValue("padding-top")),
|
||||
bottom: parseInt(elementStyle.getPropertyValue("padding-bottom")),
|
||||
right: parseInt(elementStyle.getPropertyValue("padding-right")),
|
||||
left: parseInt(elementStyle.getPropertyValue("padding-left")),
|
||||
};
|
||||
const elementPaddingVer = elementPadding.top + elementPadding.bottom;
|
||||
const elementPaddingHor = elementPadding.right + elementPadding.left;
|
||||
const availableHeight = parentElementHeight - elementPaddingVer;
|
||||
// UPDATED added 6 here (adjustment in xterm.css, right: -6px for scrollbar)
|
||||
const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth - 6;
|
||||
const geometry = {
|
||||
cols: Math.max(MINIMUM_COLS, Math.floor(availableWidth / dims.css.cell.width)),
|
||||
rows: Math.max(MINIMUM_ROWS, Math.floor(availableHeight / dims.css.cell.height)),
|
||||
};
|
||||
return geometry;
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
border-left: 4px solid transparent;
|
||||
padding-left: 4px;
|
||||
position: relative;
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { fetchWaveFile, getFileSubject, sendWSCommand } from "@/store/global";
|
||||
import { PLATFORM, fetchWaveFile, getFileSubject, sendWSCommand } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import { base64ToArray } from "@/util/util";
|
||||
import { FitAddon } from "@xterm/addon-fit";
|
||||
import { SerializeAddon } from "@xterm/addon-serialize";
|
||||
import * as TermTypes from "@xterm/xterm";
|
||||
import { Terminal } from "@xterm/xterm";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { FitAddon } from "./fitaddon";
|
||||
|
||||
export class TermWrap {
|
||||
blockId: string;
|
||||
@@ -18,7 +18,7 @@ export class TermWrap {
|
||||
connectElem: HTMLDivElement;
|
||||
fitAddon: FitAddon;
|
||||
serializeAddon: SerializeAddon;
|
||||
mainFileSubject: SubjectWithRef<Uint8Array>;
|
||||
mainFileSubject: SubjectWithRef<WSFileEventData>;
|
||||
loaded: boolean;
|
||||
heldData: Uint8Array[];
|
||||
handleResize_debounced: () => void;
|
||||
@@ -36,6 +36,7 @@ export class TermWrap {
|
||||
this.dataBytesProcessed = 0;
|
||||
this.terminal = new Terminal(options);
|
||||
this.fitAddon = new FitAddon();
|
||||
this.fitAddon.noScrollbar = PLATFORM == "darwin";
|
||||
this.serializeAddon = new SerializeAddon();
|
||||
this.terminal.loadAddon(this.fitAddon);
|
||||
this.terminal.loadAddon(this.serializeAddon);
|
||||
|
||||
Vendored
+16
-9
@@ -116,20 +116,27 @@ declare global {
|
||||
|
||||
type SubjectWithRef<T> = rxjs.Subject<T> & { refCount: number; release: () => void };
|
||||
|
||||
type IconButtonDecl = {
|
||||
type HeaderElem = HeaderIconButton | HeaderText;
|
||||
|
||||
type HeaderIconButton = {
|
||||
elemtype: "iconbutton";
|
||||
icon: string;
|
||||
title?: string;
|
||||
click: () => void;
|
||||
click?: (e: React.MouseEvent<any>) => void;
|
||||
longClick?: (e: React.MouseEvent<any>) => void;
|
||||
};
|
||||
|
||||
type HeaderText = {
|
||||
elemtype: "text";
|
||||
text: string;
|
||||
};
|
||||
|
||||
interface ViewModel {
|
||||
viewIcon: jotai.Atom<string>;
|
||||
viewName: jotai.Atom<string>;
|
||||
viewText: jotai.Atom<string>;
|
||||
preIconButton: jotai.Atom<IconButtonDecl>;
|
||||
endIconButtons: jotai.Atom<IconButtonDecl[]>;
|
||||
|
||||
hasSearch: jotai.Atom<boolean>;
|
||||
viewIcon?: jotai.Atom<string | HeaderIconButton>;
|
||||
viewName?: jotai.Atom<string>;
|
||||
viewText?: jotai.Atom<string | HeaderElem[]>;
|
||||
preIconButton?: jotai.Atom<HeaderIconButton>;
|
||||
endIconButtons?: jotai.Atom<HeaderIconButton[]>;
|
||||
|
||||
onBack?: () => void;
|
||||
onForward?: () => void;
|
||||
|
||||
+4
-2
@@ -1,7 +1,7 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
import { atoms, getApi, globalStore, globalWS, initWS } from "@/store/global";
|
||||
import { atoms, getApi, globalStore, globalWS, initWS, setPlatform } from "@/store/global";
|
||||
import * as services from "@/store/services";
|
||||
import * as WOS from "@/store/wos";
|
||||
import * as keyutil from "@/util/keyutil";
|
||||
@@ -17,7 +17,9 @@ let clientId = urlParams.get("clientid");
|
||||
console.log("Wave Starting");
|
||||
console.log("clientid", clientId, "windowid", windowId);
|
||||
|
||||
keyutil.setKeyUtilPlatform(getApi().getPlatform());
|
||||
let platform = getApi().getPlatform();
|
||||
setPlatform(platform);
|
||||
keyutil.setKeyUtilPlatform(platform);
|
||||
|
||||
loadFonts();
|
||||
(window as any).globalWS = globalWS;
|
||||
|
||||
+1
-1
@@ -96,7 +96,7 @@
|
||||
overflow-y: scroll;
|
||||
cursor: default;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
right: -6px; /* if this gets updated, must update fitaddon.ts */
|
||||
left: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
|
||||
Reference in New Issue
Block a user