diff --git a/package-lock.json b/package-lock.json index 8f5a42f3a..22650f462 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,7 @@ "@mui/icons-material": "^7.1.0", "@mui/joy": "^5.0.0-beta.52", "axios": "^1.11.0", - "clp-ffi-js": "^0.6.0", + "clp-ffi-js": "^0.6.1", "comlink": "^4.4.2", "dayjs": "^1.11.13", "js-beautify": "^1.15.4", @@ -5382,9 +5382,9 @@ } }, "node_modules/clp-ffi-js": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/clp-ffi-js/-/clp-ffi-js-0.6.0.tgz", - "integrity": "sha512-X92ayhgslgFkGXFj/cAb/rCLlldp/BO5JquLFRiLFYqms4NbT5UiZytjZ+ITou0JqhJZ9Rsf4DKLOUGqXY49FQ==", + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/clp-ffi-js/-/clp-ffi-js-0.6.1.tgz", + "integrity": "sha512-FTFD6IE4l3xqWCh/1Wqgdk6cxOnH5CbESWCnRF/8CQDRXDFPUwlvmTQJ23X6QvRLfDP81a7Y23tqdlPULO7Ebg==", "license": "Apache-2.0" }, "node_modules/clsx": { diff --git a/package.json b/package.json index b526fd5ee..1cf198d40 100644 --- a/package.json +++ b/package.json @@ -32,7 +32,7 @@ "@mui/icons-material": "^7.1.0", "@mui/joy": "^5.0.0-beta.52", "axios": "^1.11.0", - "clp-ffi-js": "^0.6.0", + "clp-ffi-js": "^0.6.1", "comlink": "^4.4.2", "dayjs": "^1.11.13", "js-beautify": "^1.15.4", diff --git a/src/components/AppController.tsx b/src/components/AppController.tsx index 17b015f75..02fb73ddc 100644 --- a/src/components/AppController.tsx +++ b/src/components/AppController.tsx @@ -9,19 +9,9 @@ import useQueryStore from "../stores/queryStore"; import useUiStore from "../stores/uiStore"; import useViewStore from "../stores/viewStore"; import {TAB_NAME} from "../typings/tab"; -import { - HASH_PARAM_NAMES, - UrlHashParams, -} from "../typings/url"; -import { - CURSOR_CODE, - CursorType, -} from "../typings/worker"; import { getWindowUrlHashParams, getWindowUrlSearchParams, - updateWindowUrlHashParams, - URL_HASH_PARAMS_DEFAULT, URL_SEARCH_PARAMS_DEFAULT, } from "../utils/url"; import { @@ -43,32 +33,6 @@ const handleHashChange = () => { } }; -/** - * Returns the initial load file cursor based on the URL hash parameters. - * - * @param hashParams - * @return - */ -const getInitialCursor = (hashParams: UrlHashParams) => { - let cursor: CursorType = {code: CURSOR_CODE.LAST_EVENT, args: null}; - - if (URL_HASH_PARAMS_DEFAULT.timestamp !== hashParams.timestamp) { - cursor = { - code: CURSOR_CODE.TIMESTAMP, - args: {timestamp: hashParams.timestamp}, - }; - } else if (URL_HASH_PARAMS_DEFAULT.logEventNum !== hashParams.logEventNum) { - const {setLogEventNum} = useViewStore.getState(); - setLogEventNum(hashParams.logEventNum); - cursor = { - code: CURSOR_CODE.EVENT_NUM, - args: {eventNum: hashParams.logEventNum}, - }; - } - - return cursor; -}; - interface AppControllerProps { children: React.ReactNode; } @@ -96,26 +60,19 @@ const AppController = ({children}: AppControllerProps) => { // Handle initial page load and maintain full URL state const hashParams = getWindowUrlHashParams(); - updateWindowUrlHashParams({ - isPrettified: hashParams[HASH_PARAM_NAMES.IS_PRETTIFIED], - timestamp: URL_HASH_PARAMS_DEFAULT.timestamp, - }); const {setIsPrettified} = useViewStore.getState(); setIsPrettified(hashParams.isPrettified); const searchParams = getWindowUrlSearchParams(); if (URL_SEARCH_PARAMS_DEFAULT.filePath !== searchParams.filePath) { - const {loadFile} = useLogFileStore.getState(); (async () => { + const {setLogEventNum} = useViewStore.getState(); + setLogEventNum(hashParams.logEventNum); + + const {loadFile} = useLogFileStore.getState(); await loadFile(searchParams.filePath); - const {loadPageByCursor} = useViewStore.getState(); - await loadPageByCursor(getInitialCursor(hashParams)); - if (updateQueryHashParams()) { - const {setActiveTabName} = useUiStore.getState(); - setActiveTabName(TAB_NAME.SEARCH); - const {startQuery} = useQueryStore.getState(); - startQuery(); - } + + handleHashChange(); })().catch(handleErrorWithNotification); } diff --git a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css new file mode 100644 index 000000000..a98035639 --- /dev/null +++ b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.css @@ -0,0 +1,8 @@ +.filter-input-box-textarea { + font-family: monospace !important; + word-break: break-all; +} + +.filter-button { + width: 48px; +} diff --git a/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx new file mode 100644 index 000000000..a2165222f --- /dev/null +++ b/src/components/CentralContainer/Sidebar/SidebarTabs/SearchTabPanel/FilterInputBox.tsx @@ -0,0 +1,90 @@ +import React, {useCallback} from "react"; + +import { + Button, + Textarea, +} from "@mui/joy"; + +import {handleErrorWithNotification} from "../../../../../stores/notificationStore"; +import useQueryStore from "../../../../../stores/queryStore"; +import useUiStore from "../../../../../stores/uiStore"; +import useViewStore from "../../../../../stores/viewStore"; +import {UI_ELEMENT} from "../../../../../typings/states"; +import {CURSOR_CODE} from "../../../../../typings/worker"; +import {isDisabled} from "../../../../../utils/states"; + +import "./FilterInputBox.css"; +import "./InputBox.css"; + + +/** + * Provides a text input to apply a KQL filter. + * + * @return + */ +const FilterInputBox = () => { + const kqlFilterInput = useViewStore((state) => state.kqlFilterInput); + const kqlFilter = useViewStore((state) => state.kqlFilter); + const uiState = useUiStore((state) => state.uiState); + + const handleFilterInputChange = useCallback((ev: React.ChangeEvent) => { + const newFilterString = ev.target.value; + const {setKqlFilterInput} = useViewStore.getState(); + setKqlFilterInput(newFilterString); + }, []); + + const handleFilterButtonClick = useCallback(() => { + const {setKqlFilter, filterLogs, loadPageByCursor, logEventNum} = useViewStore.getState(); + setKqlFilter(kqlFilterInput); + filterLogs(); + + (async () => { + await loadPageByCursor({ + code: CURSOR_CODE.EVENT_NUM, + args: {eventNum: logEventNum}, + }); + + const {startQuery} = useQueryStore.getState(); + startQuery(); + })().catch(handleErrorWithNotification); + }, [kqlFilterInput]); + + const handleTextareaKeyDown = useCallback((ev: React.KeyboardEvent) => { + if (ev.shiftKey && "Enter" === ev.key) { + ev.preventDefault(); + handleFilterButtonClick(); + } + }, [handleFilterButtonClick]); + + const isFilterInputBoxDisabled = isDisabled(uiState, UI_ELEMENT.QUERY_INPUT_BOX); + const isKqlFilterModified = kqlFilter !== kqlFilterInput; + + return ( +