+ );
+};
+
+
+export default ResizeHandle;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx
new file mode 100644
index 00000000..30dd7444
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomListItem.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+
+import {
+ ListItem,
+ ListItemContent,
+ ListItemDecorator,
+ Typography,
+ TypographyProps,
+} from "@mui/joy";
+
+
+interface CustomListItemProps {
+ content: string,
+ icon: React.ReactNode,
+ slotProps?: {
+ content?: TypographyProps
+ },
+ title: string
+}
+
+/**
+ * Renders a custom list item with an icon, a title and a context text.
+ *
+ * @param props
+ * @param props.content
+ * @param props.icon
+ * @param props.slotProps
+ * @param props.title
+ * @return
+ */
+const CustomListItem = ({content, icon, slotProps, title}: CustomListItemProps) => (
+
+
+ {icon}
+
+
+
+ {title}
+
+
+ {content}
+
+
+
+);
+
+export default CustomListItem;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css
new file mode 100644
index 00000000..8169315f
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.css
@@ -0,0 +1,14 @@
+.sidebar-tab-panel {
+ padding: 0.75rem;
+}
+
+.sidebar-tab-panel-title-container {
+ user-select: none;
+ margin-bottom: 0.5rem !important;
+}
+
+.sidebar-tab-panel-title {
+ font-size: 0.875rem !important;
+ font-weight: 400 !important;
+ text-transform: uppercase;
+}
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx
new file mode 100644
index 00000000..9d1ab3e7
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/CustomTabPanel.tsx
@@ -0,0 +1,50 @@
+import React from "react";
+
+import {
+ DialogContent,
+ DialogTitle,
+ TabPanel,
+ Typography,
+} from "@mui/joy";
+
+import "./CustomTabPanel.css";
+
+
+interface CustomTabPanelProps {
+ children: React.ReactNode,
+ tabName: string,
+ title: string,
+}
+
+/**
+ * Renders a customized tab panel to be extended for displaying extra information in the sidebar.
+ *
+ * @param props
+ * @param props.children
+ * @param props.tabName
+ * @param props.title
+ * @return
+ */
+const CustomTabPanel = ({children, tabName, title}: CustomTabPanelProps) => {
+ return (
+
+
+
+ {title}
+
+
+
+ {children}
+
+
+ );
+};
+
+
+export default CustomTabPanel;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx
new file mode 100644
index 00000000..7b487f3d
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/FileInfoTabPanel.tsx
@@ -0,0 +1,61 @@
+import {
+ useContext,
+ useMemo,
+} from "react";
+
+import {
+ Divider,
+ List,
+} from "@mui/joy";
+
+import AbcIcon from "@mui/icons-material/Abc";
+import StorageIcon from "@mui/icons-material/Storage";
+
+import {StateContext} from "../../../../contexts/StateContextProvider";
+import {
+ TAB_DISPLAY_NAMES,
+ TAB_NAME,
+} from "../../../../typings/tab";
+import {formatSizeInBytes} from "../../../../utils/units";
+import CustomListItem from "./CustomListItem";
+import CustomTabPanel from "./CustomTabPanel";
+
+
+/**
+ * Displays a panel containing the file name and on-disk size of the selected file.
+ *
+ * @return
+ */
+const FileInfoTabPanel = () => {
+ const {fileName, onDiskFileSizeInBytes} = useContext(StateContext);
+
+ const isFileUnloaded = 0 === fileName.length;
+ const formattedOnDiskSize = useMemo(
+ () => formatSizeInBytes(onDiskFileSizeInBytes, false),
+ [onDiskFileSizeInBytes]
+ );
+
+ return (
+
+ {isFileUnloaded ?
+ "No file is open." :
+
+ }
+ slotProps={{content: {sx: {wordBreak: "break-word"}}}}
+ title={"Name"}/>
+
+ }
+ title={"On-disk Size"}/>
+
}
+
+ );
+};
+
+export default FileInfoTabPanel;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css
new file mode 100644
index 00000000..11fe18bd
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.css
@@ -0,0 +1,10 @@
+.sidebar-tab-button {
+ justify-content: center !important;
+ width: 48px;
+ height: 48px;
+ padding: 0 !important;
+}
+
+.sidebar-tab-button-icon {
+ font-size: 32px !important;
+}
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx
new file mode 100644
index 00000000..846860f2
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/TabButton.tsx
@@ -0,0 +1,57 @@
+import {
+ Tab,
+ Tooltip,
+} from "@mui/joy";
+
+import {SvgIconComponent} from "@mui/icons-material";
+
+import {
+ TAB_DISPLAY_NAMES,
+ TAB_NAME,
+} from "../../../../typings/tab";
+
+import "./TabButton.css";
+
+
+interface TabButtonProps {
+ tabName: TAB_NAME,
+ Icon: SvgIconComponent,
+ onTabButtonClick: (tabName: TAB_NAME) => void
+}
+
+/**
+ * Renders a tooltip-wrapped tab button.
+ *
+ * @param props
+ * @param props.tabName
+ * @param props.Icon
+ * @param props.onTabButtonClick
+ * @return
+ */
+const TabButton = ({tabName, Icon, onTabButtonClick}: TabButtonProps) => {
+ const handleClick = () => {
+ onTabButtonClick(tabName);
+ };
+
+ return (
+
+
+
+
+
+ );
+};
+
+export default TabButton;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css
new file mode 100644
index 00000000..f9b4e2c3
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.css
@@ -0,0 +1,9 @@
+.sidebar-tabs {
+ flex-grow: 1;
+ width: calc(100% - var(--ylv-panel-resize-handle-width));
+ height: 100%;
+}
+
+.sidebar-tab-list-spacing {
+ flex-grow: 1;
+}
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx
new file mode 100644
index 00000000..c8a262ed
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/SidebarTabs/index.tsx
@@ -0,0 +1,105 @@
+import {
+ forwardRef,
+ useState,
+} from "react";
+
+import {
+ TabList,
+ Tabs,
+} from "@mui/joy";
+
+import {SvgIconComponent} from "@mui/icons-material";
+import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined";
+import SettingsOutlinedIcon from "@mui/icons-material/SettingsOutlined";
+
+import {TAB_NAME} from "../../../../typings/tab";
+import SettingsModal from "../../../modals/SettingsModal";
+import FileInfoTabPanel from "./FileInfoTabPanel";
+import TabButton from "./TabButton";
+
+import "./index.css";
+
+
+/**
+ * Lists information for each tab.
+ */
+const TABS_INFO_LIST: Readonly
> = Object.freeze([
+ {tabName: TAB_NAME.FILE_INFO, Icon: InfoOutlinedIcon},
+]);
+
+interface SidebarTabsProps {
+ activeTabName: TAB_NAME,
+ onActiveTabNameChange: (newValue: TAB_NAME) => void,
+}
+
+/**
+ * Displays a set of tabs in a vertical orientation.
+ *
+ * @param tabListRef Reference object used to access the TabList DOM element.
+ * @return
+ */
+const SidebarTabs = forwardRef((
+ {
+ activeTabName,
+ onActiveTabNameChange,
+ },
+ tabListRef
+) => {
+ const [isSettingsModalOpen, setIsSettingsModalOpen] = useState(false);
+
+ const handleSettingsModalClose = () => {
+ setIsSettingsModalOpen(false);
+ };
+
+ const handleTabButtonClick = (tabName: TAB_NAME) => {
+ switch (tabName) {
+ case TAB_NAME.SETTINGS:
+ setIsSettingsModalOpen(true);
+ break;
+ default:
+ onActiveTabNameChange(tabName);
+ }
+ };
+
+ return (
+ <>
+
+
+ {TABS_INFO_LIST.map(({tabName, Icon}) => (
+
+ ))}
+
+ {/* Forces the settings tab to bottom of sidebar. */}
+
+
+
+
+
+
+
+ >
+ );
+});
+
+SidebarTabs.displayName = "SidebarTabs";
+export default SidebarTabs;
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/index.css b/new-log-viewer/src/components/CentralContainer/Sidebar/index.css
new file mode 100644
index 00000000..323fb710
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/index.css
@@ -0,0 +1,3 @@
+.sidebar-tabs-container {
+ display: flex;
+}
diff --git a/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx b/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx
new file mode 100644
index 00000000..f6a96d32
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/Sidebar/index.tsx
@@ -0,0 +1,108 @@
+import {
+ useCallback,
+ useRef,
+ useState,
+} from "react";
+
+import {TAB_NAME} from "../../../typings/tab";
+import ResizeHandle from "./ResizeHandle";
+import SidebarTabs from "./SidebarTabs";
+
+import "./index.css";
+
+
+const PANEL_DEFAULT_WIDTH_IN_PIXELS = 360;
+const PANEL_CLIP_THRESHOLD_IN_PIXELS = 250;
+const PANEL_MAX_WIDTH_TO_WINDOW_WIDTH_RATIO = 0.8;
+
+/**
+ * Gets width of the panel from body style properties.
+ *
+ * @return the width in pixels as a number.
+ */
+const getPanelWidth = () => parseInt(
+ document.body.style.getPropertyValue("--ylv-panel-width"),
+ 10
+);
+
+/**
+ * Sets width of the panel in body style properties.
+ *
+ * @param newValue in pixels.
+ */
+const setPanelWidth = (newValue: number) => {
+ document.body.style.setProperty("--ylv-panel-width", `${newValue}px`);
+};
+
+
+/**
+ * Renders a sidebar component that displays tabbed panels and a resize handle.
+ * The active tab can be changed and the sidebar can be resized by dragging the handle.
+ *
+ * @return
+ */
+const Sidebar = () => {
+ const [activeTabName, setActiveTabName] = useState(TAB_NAME.FILE_INFO);
+
+ const tabListRef = useRef(null);
+
+ const handleActiveTabNameChange = useCallback((tabName: TAB_NAME) => {
+ if (null === tabListRef.current) {
+ console.error("Unexpected null tabListRef.current");
+
+ return;
+ }
+
+ if (activeTabName === tabName) {
+ setActiveTabName(TAB_NAME.NONE);
+ setPanelWidth(tabListRef.current.clientWidth);
+
+ return;
+ }
+ setActiveTabName(tabName);
+ setPanelWidth(PANEL_DEFAULT_WIDTH_IN_PIXELS);
+ }, [activeTabName]);
+
+ const handleResizeHandleRelease = useCallback(() => {
+ if (getPanelWidth() === tabListRef.current?.clientWidth) {
+ setActiveTabName(TAB_NAME.NONE);
+ }
+ }, []);
+
+ const handleResize = useCallback((resizeHandlePosition: number) => {
+ if (null === tabListRef.current) {
+ console.error("Unexpected null tabListRef.current");
+
+ return;
+ }
+ if (
+ tabListRef.current.clientWidth + PANEL_CLIP_THRESHOLD_IN_PIXELS >
+ resizeHandlePosition
+ ) {
+ // If the resize handle is positioned to the right of the 's right edge
+ // with a clipping threshold accounted, close the panel.
+ setPanelWidth(tabListRef.current.clientWidth);
+ } else if (
+ resizeHandlePosition < window.innerWidth * PANEL_MAX_WIDTH_TO_WINDOW_WIDTH_RATIO
+ ) {
+ // If the resize handle is positioned to the left of 80% of the window's width,
+ // update the panel width with the distance between the mouse pointer and the
+ // window's left edge.
+ setPanelWidth(resizeHandlePosition);
+ }
+ }, []);
+
+ return (
+
+
+ {TAB_NAME.NONE !== activeTabName && }
+
+ );
+};
+
+export default Sidebar;
diff --git a/new-log-viewer/src/components/CentralContainer/index.css b/new-log-viewer/src/components/CentralContainer/index.css
new file mode 100644
index 00000000..7252d1dc
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/index.css
@@ -0,0 +1,9 @@
+:root {
+ --ylv-panel-width: 360px;
+}
+
+.central-container {
+ display: grid;
+ grid-template-columns: var(--ylv-panel-width) calc(100vw - var(--ylv-panel-width));
+ width: 100vw;
+}
diff --git a/new-log-viewer/src/components/CentralContainer/index.tsx b/new-log-viewer/src/components/CentralContainer/index.tsx
new file mode 100644
index 00000000..810aa489
--- /dev/null
+++ b/new-log-viewer/src/components/CentralContainer/index.tsx
@@ -0,0 +1,24 @@
+import DropFileContainer from "../DropFileContainer";
+import Editor from "../Editor";
+import Sidebar from "./Sidebar";
+
+import "./index.css";
+
+
+/**
+ * Located in the center of the .
+ *
+ * @return
+ */
+const CentralContainer = () => {
+ return (
+
+
+
+
+
+
+ );
+};
+
+export default CentralContainer;
diff --git a/new-log-viewer/src/components/DropFileContainer/index.css b/new-log-viewer/src/components/DropFileContainer/index.css
index 6d6ca94b..0b653f15 100644
--- a/new-log-viewer/src/components/DropFileContainer/index.css
+++ b/new-log-viewer/src/components/DropFileContainer/index.css
@@ -2,11 +2,6 @@
position: relative;
}
-.drop-file-children {
- width: 100%;
- height: 100%;
-}
-
.hover-mask {
position: absolute;
z-index: var(--ylv-drop-file-container-hover-mask-z-index);
diff --git a/new-log-viewer/src/components/DropFileContainer/index.tsx b/new-log-viewer/src/components/DropFileContainer/index.tsx
index a355c9e8..f37fa6fa 100644
--- a/new-log-viewer/src/components/DropFileContainer/index.tsx
+++ b/new-log-viewer/src/components/DropFileContainer/index.tsx
@@ -70,10 +70,7 @@ const DropFileContainer = ({children}: DropFileContextProviderProps) => {
onDragOver={handleDrag}
onDrop={handleDrop}
>
-
+
{children}
{isFileHovering && (
{
modeStorageKey={CONFIG_KEY.THEME}
theme={APP_THEME}
>
-
-
-
-
-
-
-
+
+
+
);
};
diff --git a/new-log-viewer/src/components/MenuBar/index.css b/new-log-viewer/src/components/MenuBar/index.css
index c9f0f0c6..08fe4c66 100644
--- a/new-log-viewer/src/components/MenuBar/index.css
+++ b/new-log-viewer/src/components/MenuBar/index.css
@@ -1,6 +1,28 @@
.menu-bar {
+ z-index: var(--ylv-menu-bar-z-index);
+
display: flex;
flex-direction: row;
align-items: center;
+
height: var(--ylv-status-bar-height);
+
+ /* stylelint-disable-next-line custom-property-pattern */
+ box-shadow: 0 1px 0 0 var(--joy-palette-neutral-outlinedBorder);
+}
+
+.menu-bar-logo-container {
+ display: flex;
+ justify-content: center;
+ width: 48px;
+ height: var(--ylv-status-bar-height);
+}
+
+.menu-bar-open-file-icon {
+ font-size: 24px !important;
+}
+
+.menu-bar-filename {
+ flex-grow: 1;
+ padding-left: 10px;
}
diff --git a/new-log-viewer/src/components/MenuBar/index.tsx b/new-log-viewer/src/components/MenuBar/index.tsx
index e2a21487..aac5c979 100644
--- a/new-log-viewer/src/components/MenuBar/index.tsx
+++ b/new-log-viewer/src/components/MenuBar/index.tsx
@@ -1,26 +1,20 @@
-import {
- useContext,
- useState,
-} from "react";
+import {useContext} from "react";
import {
Divider,
+ IconButton,
Sheet,
- Stack,
+ Tooltip,
Typography,
} from "@mui/joy";
-import DescriptionIcon from "@mui/icons-material/Description";
-import FileOpenIcon from "@mui/icons-material/FileOpen";
-import SettingsIcon from "@mui/icons-material/Settings";
+import FolderOpenIcon from "@mui/icons-material/FolderOpen";
import {StateContext} from "../../contexts/StateContextProvider";
import {CURSOR_CODE} from "../../typings/worker";
import {openFile} from "../../utils/file";
-import SettingsModal from "../modals/SettingsModal";
import ExportLogsButton from "./ExportLogsButton";
import NavigationBar from "./NavigationBar";
-import SmallIconButton from "./SmallIconButton";
import "./index.css";
@@ -33,55 +27,51 @@ import "./index.css";
const MenuBar = () => {
const {fileName, loadFile} = useContext(StateContext);
- const [isSettingsModalOpen, setIsSettingsModalOpen] = useState
(false);
-
- const handleOpenFileButtonClick = () => {
+ const handleOpenFile = () => {
openFile((file) => {
loadFile(file, {code: CURSOR_CODE.LAST_EVENT, args: null});
});
};
- const handleSettingsModalClose = () => {
- setIsSettingsModalOpen(false);
- };
-
- const handleSettingsModalOpen = () => {
- setIsSettingsModalOpen(true);
- };
-
return (
<>
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
- {fileName}
-
-
+ {fileName}
+
-
-
-
-
-
-
-
+
-
>
);
};
diff --git a/new-log-viewer/src/components/StatusBar/index.css b/new-log-viewer/src/components/StatusBar/index.css
index 770b5336..d732d42e 100644
--- a/new-log-viewer/src/components/StatusBar/index.css
+++ b/new-log-viewer/src/components/StatusBar/index.css
@@ -1,5 +1,6 @@
.status-bar {
position: absolute;
+ z-index: var(--ylv-status-bar-z-index);
bottom: 0;
display: flex;
@@ -7,6 +8,9 @@
align-items: center;
width: 100%;
+
+ /* stylelint-disable-next-line custom-property-pattern */
+ box-shadow: 0 -1px 0 0 var(--joy-palette-neutral-outlinedBorder);
}
.status-message {
diff --git a/new-log-viewer/src/contexts/StateContextProvider.tsx b/new-log-viewer/src/contexts/StateContextProvider.tsx
index 407adc96..dec854c2 100644
--- a/new-log-viewer/src/contexts/StateContextProvider.tsx
+++ b/new-log-viewer/src/contexts/StateContextProvider.tsx
@@ -1,4 +1,4 @@
-/* eslint max-lines: ["error", 450] */
+/* eslint max-lines: ["error", 500] */
import React, {
createContext,
useCallback,
@@ -53,6 +53,7 @@ interface StateContextType {
logData: string,
numEvents: number,
numPages: number,
+ onDiskFileSizeInBytes: number,
pageNum: number,
exportLogs: () => void,
@@ -69,9 +70,10 @@ const STATE_DEFAULT: Readonly = Object.freeze({
beginLineNumToLogEventNum: new Map(),
exportProgress: null,
fileName: "",
- logData: "Loading...",
+ logData: "No file is open.",
numEvents: 0,
numPages: 0,
+ onDiskFileSizeInBytes: 0,
pageNum: 0,
exportLogs: () => null,
@@ -228,6 +230,8 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
const [logData, setLogData] = useState(STATE_DEFAULT.logData);
const [numEvents, setNumEvents] = useState(STATE_DEFAULT.numEvents);
const [numPages, setNumPages] = useState(STATE_DEFAULT.numPages);
+ const [onDiskFileSizeInBytes, setOnDiskFileSizeInBytes] =
+ useState(STATE_DEFAULT.onDiskFileSizeInBytes);
const [pageNum, setPageNum] = useState(STATE_DEFAULT.pageNum);
const beginLineNumToLogEventNumRef =
useRef(STATE_DEFAULT.beginLineNumToLogEventNum);
@@ -254,6 +258,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
case WORKER_RESP_CODE.LOG_FILE_INFO:
setFileName(args.fileName);
setNumEvents(args.numEvents);
+ setOnDiskFileSizeInBytes(args.onDiskFileSizeInBytes);
break;
case WORKER_RESP_CODE.NOTIFICATION:
// eslint-disable-next-line no-warning-comments
@@ -322,6 +327,9 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
decoderOptions: getConfig(CONFIG_KEY.DECODER_OPTIONS),
});
+ setFileName("Loading...");
+ setLogData("Loading...");
+ setOnDiskFileSizeInBytes(STATE_DEFAULT.onDiskFileSizeInBytes);
setExportProgress(STATE_DEFAULT.exportProgress);
}, [
handleMainWorkerResp,
@@ -427,6 +435,7 @@ const StateContextProvider = ({children}: StateContextProviderProps) => {
logData: logData,
numEvents: numEvents,
numPages: numPages,
+ onDiskFileSizeInBytes: onDiskFileSizeInBytes,
pageNum: pageNum,
exportLogs: exportLogs,
diff --git a/new-log-viewer/src/index.css b/new-log-viewer/src/index.css
index e354c3b9..ecf9a72c 100644
--- a/new-log-viewer/src/index.css
+++ b/new-log-viewer/src/index.css
@@ -18,6 +18,7 @@ html {
/* size globals */
--ylv-status-bar-height: 32px;
--ylv-menu-bar-height: 32px;
+ --ylv-panel-resize-handle-width: 4px;
/* z-index globals
*
@@ -26,6 +27,9 @@ html {
* .monaco-editor .minimap { z-index: 5; }
* ```
*/
+ --ylv-resize-handle-z-index: 1;
+ --ylv-menu-bar-z-index: 6;
+ --ylv-status-bar-z-index: 6;
--ylv-drop-file-container-hover-mask-z-index: 10;
--ylv-drop-file-container-hover-message-z-index: 11;
}
diff --git a/new-log-viewer/src/services/LogFileManager/index.ts b/new-log-viewer/src/services/LogFileManager/index.ts
index 271ba2bd..b0012b27 100644
--- a/new-log-viewer/src/services/LogFileManager/index.ts
+++ b/new-log-viewer/src/services/LogFileManager/index.ts
@@ -35,6 +35,8 @@ class LogFileManager {
readonly #fileName: string;
+ readonly #onDiskFileSizeInBytes: number;
+
#decoder: Decoder;
#numEvents: number = 0;
@@ -45,14 +47,17 @@ class LogFileManager {
*
* @param decoder
* @param fileName
+ * @param onDiskFileSizeInBytes
* @param pageSize Page size for setting up pagination.
*/
constructor (
decoder: Decoder,
fileName: string,
+ onDiskFileSizeInBytes: number,
pageSize: number,
) {
this.#fileName = fileName;
+ this.#onDiskFileSizeInBytes = onDiskFileSizeInBytes;
this.#pageSize = pageSize;
this.#decoder = decoder;
@@ -70,6 +75,10 @@ class LogFileManager {
return this.#fileName;
}
+ get onDiskFileSizeInBytes () {
+ return this.#onDiskFileSizeInBytes;
+ }
+
get numEvents () {
return this.#numEvents;
}
@@ -91,7 +100,7 @@ class LogFileManager {
const {fileName, fileData} = await loadFile(fileSrc);
const decoder = await LogFileManager.#initDecoder(fileName, fileData, decoderOptions);
- return new LogFileManager(decoder, fileName, pageSize);
+ return new LogFileManager(decoder, fileName, fileData.length, pageSize);
}
/**
diff --git a/new-log-viewer/src/services/MainWorker.ts b/new-log-viewer/src/services/MainWorker.ts
index 56313e47..751d4cfc 100644
--- a/new-log-viewer/src/services/MainWorker.ts
+++ b/new-log-viewer/src/services/MainWorker.ts
@@ -69,6 +69,7 @@ onmessage = async (ev: MessageEvent) => {
postResp(WORKER_RESP_CODE.LOG_FILE_INFO, {
fileName: LOG_FILE_MANAGER.fileName,
numEvents: LOG_FILE_MANAGER.numEvents,
+ onDiskFileSizeInBytes: LOG_FILE_MANAGER.onDiskFileSizeInBytes,
});
postResp(
WORKER_RESP_CODE.PAGE_DATA,
diff --git a/new-log-viewer/src/typings/tab.ts b/new-log-viewer/src/typings/tab.ts
new file mode 100644
index 00000000..b2e8b1bf
--- /dev/null
+++ b/new-log-viewer/src/typings/tab.ts
@@ -0,0 +1,19 @@
+enum TAB_NAME {
+ NONE = "none",
+ FILE_INFO = "fileInfo",
+ SETTINGS = "settings",
+}
+
+/**
+ * Maps the TAB_NAME enum values to their corresponding display names.
+ */
+const TAB_DISPLAY_NAMES: Record = Object.freeze({
+ [TAB_NAME.NONE]: "None",
+ [TAB_NAME.FILE_INFO]: "File info",
+ [TAB_NAME.SETTINGS]: "Settings",
+});
+
+export {
+ TAB_DISPLAY_NAMES,
+ TAB_NAME,
+};
diff --git a/new-log-viewer/src/typings/worker.ts b/new-log-viewer/src/typings/worker.ts
index 24b2afb9..9ceeea4c 100644
--- a/new-log-viewer/src/typings/worker.ts
+++ b/new-log-viewer/src/typings/worker.ts
@@ -105,6 +105,7 @@ type WorkerRespMap = {
[WORKER_RESP_CODE.LOG_FILE_INFO]: {
fileName: string,
numEvents: number,
+ onDiskFileSizeInBytes: number,
},
[WORKER_RESP_CODE.PAGE_DATA]: {
beginLineNumToLogEventNum: BeginLineNumToLogEventNumMap,