diff --git a/package.json b/package.json index c438ac4f..72cf93d9 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "@ant-design/icons": "^6.1.0", "@ant-design/pro-components": "3.1.0-0", "@braintree/sanitize-url": "^7.1.1", - "@gpustack/core-ui": "^1.0.1", + "@gpustack/core-ui": "^1.0.2", "@huggingface/gguf": "^0.1.7", "@huggingface/hub": "^0.15.1", "@huggingface/tasks": "^0.11.6", @@ -45,19 +45,16 @@ "dompurify": "^3.2.6", "driver.js": "^1.3.1", "echarts": "^5.5.1", - "epubjs": "^0.3.93", "file-saver": "^2.0.5", "has-ansi": "^5.0.1", "highlight.js": "^11.10.0", "jdenticon": "^3.3.0", "jotai": "^2.8.4", "js-yaml": "^4.1.0", - "jszip": "^3.10.1", "katex": "^0.16.21", "lamejs": "github:zhuker/lamejs", "localforage": "^1.10.0", "lodash": "^4.17.21", - "mammoth": "^1.8.0", "marked": "^14.1.0", "minimatch": "^3.1.2", "ml-dataset-iris": "^1.2.1", @@ -67,7 +64,6 @@ "numeral": "^2.0.6", "overlayscrollbars": "^2.10.0", "overlayscrollbars-react": "^0.5.6", - "pdfjs-dist": "^4.7.76", "query-string": "^9.0.0", "rc-resize-observer": "^1.4.3", "rc-virtual-list": "^3.14.8", @@ -84,10 +80,8 @@ "semver": "^7.7.3", "simplebar-react": "^3.2.6", "styled-components": "^6.1.15", - "tinycolor2": "^1.6.0", "umi-presets-pro": "^2.0.3", - "wavesurfer.js": "^7.8.8", - "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.3/xlsx-0.20.3.tgz" + "wavesurfer.js": "^7.8.8" }, "devDependencies": { "@eslint/js": "^9.39.4", diff --git a/src/components/templates/card-list.tsx b/src/components/templates/card-list.tsx deleted file mode 100644 index 0c79631d..00000000 --- a/src/components/templates/card-list.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import { - InfiniteScroller, - ResizeContainer, - TemplateCardSkeleton, - useScrollerContext -} from '@gpustack/core-ui'; -import { Spin } from 'antd'; -import React from 'react'; -import styled from 'styled-components'; - -const SpinWrapper = styled.div` - width: 100%; - position: absolute; - display: flex; - align-items: center; - justify-content: center; - top: 0; - left: 0; - max-height: 400px; - right: 0; - .skelton-wrapper { - width: 100%; - } -`; - -interface CatalogListProps { - defaultSpan?: number; - resizable?: boolean; - dataList: any[]; - loading: boolean; - activeId: Global.WithFalse; - isFirst: boolean; - renderItem: (data: any) => React.ReactNode; -} - -const ListSkeleton: React.FC<{ - loading: boolean; - isFirst: boolean; -}> = ({ loading, isFirst }) => { - return ( -
- {loading && ( - - - {isFirst && } - - - )} -
- ); -}; - -const CardList: React.FC = (props) => { - const { - dataList, - loading, - isFirst, - defaultSpan = 8, - resizable = true, - renderItem - } = props; - const { - total, - current, - loading: contextLoading, - refresh - } = useScrollerContext(); - - return ( - - - - - ); -}; - -export default CardList; diff --git a/src/layouts/index.tsx b/src/layouts/index.tsx index 292dd43d..fd582864 100644 --- a/src/layouts/index.tsx +++ b/src/layouts/index.tsx @@ -4,7 +4,6 @@ import DarkMask from '@/components/dark-mask'; import routeCachekey from '@/config/route-cachekey'; import { DEFAULT_ENTER_PAGE, GPUSTACK_API_BASE_URL } from '@/config/settings'; import { COLOR_PRIMARY } from '@/config/theme'; -import useOverlayScroller from '@/hooks/use-overlay-scroller'; import useUserSettings from '@/hooks/use-user-settings'; import useUserSettingsStorage from '@/hooks/use-user-settings-storage'; import useAddResource from '@/pages/dashboard/hooks/use-add-resource'; @@ -18,7 +17,11 @@ import { import { useAccessMarkedRoutes } from '@@/plugin-access'; import { useModel } from '@@/plugin-model'; import { ProLayout } from '@ant-design/pro-components'; -import { CoreUIProvider, IconFont } from '@gpustack/core-ui'; +import { + CoreUIProvider, + IconFont, + useOverlayScroller +} from '@gpustack/core-ui'; import { Outlet, dropByCacheKey, diff --git a/src/pages/_components/infinite-scroller/index.tsx b/src/pages/_components/infinite-scroller/index.tsx deleted file mode 100644 index bdee2a38..00000000 --- a/src/pages/_components/infinite-scroller/index.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { DoubleRightOutlined } from '@ant-design/icons'; -import { useIntl } from '@umijs/max'; -import { Button, FloatButton } from 'antd'; -import React from 'react'; - -import { - useInfiniteScroll, - UseInfiniteScrollOptions -} from './use-infinite-scroll'; - -const InfiniteScroller: React.FC< - UseInfiniteScrollOptions & { children: React.ReactNode } -> = (props) => { - const { children, ...restProps } = props; - const intl = useIntl(); - const { observerRef, throttledLoadMore } = useInfiniteScroll(restProps); - - return ( -
- {children} -
- {restProps.current < restProps.total && ( -
- -
- )} -
- -
- ); -}; - -export default InfiniteScroller; diff --git a/src/pages/_components/infinite-scroller/use-infinite-scroll.ts b/src/pages/_components/infinite-scroller/use-infinite-scroll.ts deleted file mode 100644 index f249276e..00000000 --- a/src/pages/_components/infinite-scroller/use-infinite-scroll.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { useMemoizedFn } from 'ahooks'; -import { throttle } from 'lodash'; -import { useEffect, useRef } from 'react'; -import { useInView } from 'react-intersection-observer'; - -export interface UseInfiniteScrollOptions { - total: number; - current: number; - loading: boolean; - refresh: (nextPage: number) => void; - onBottom?: () => void; - throttleDelay?: number; -} - -export function useInfiniteScroll({ - total, - current, - loading, - refresh, - onBottom, - throttleDelay = 300 -}: UseInfiniteScrollOptions) { - const { ref: observerRef, inView } = useInView({ - threshold: 0.2 - }); - const isInitialLoad = useRef(true); - - const throttledLoadMore = useMemoizedFn( - throttle(() => { - if (loading) return; - if (current >= total) return; - - onBottom?.(); - refresh(current + 1); - }, throttleDelay) - ); - - useEffect(() => { - if (inView) { - if (isInitialLoad.current) { - isInitialLoad.current = false; - return; - } - throttledLoadMore(); - } - }, [inView, throttledLoadMore]); - - return { observerRef, throttledLoadMore }; -} diff --git a/src/pages/_components/infinite-scroller/use-scroller-context.ts b/src/pages/_components/infinite-scroller/use-scroller-context.ts deleted file mode 100644 index 89689351..00000000 --- a/src/pages/_components/infinite-scroller/use-scroller-context.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { createContext, useContext } from 'react'; - -interface ScrollerContextProps { - total: number; // total pages - current: number; - loading: boolean; - refresh: (nextPage: number) => void; - onBottom?: () => void; - throttleDelay?: number; -} - -export const ScrollerContext = createContext( - {} as ScrollerContextProps -); - -export const useScrollerContext = () => { - const context = useContext(ScrollerContext); - if (!context) { - throw new Error( - 'useScrollerContext must be used within a ScrollerProvider' - ); - } - return context; -}; diff --git a/src/pages/_components/no-result.tsx b/src/pages/_components/no-result.tsx deleted file mode 100644 index 6a02b622..00000000 --- a/src/pages/_components/no-result.tsx +++ /dev/null @@ -1,127 +0,0 @@ -import { Button, Empty, EmptyProps, Typography } from 'antd'; -import _ from 'lodash'; -import React, { useMemo } from 'react'; -import styled from 'styled-components'; - -const StyledEmpty = styled(Empty)` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - margin-block: 60px 32px; - .ant-empty-image { - margin-bottom: 0; - height: auto; - line-height: 1; - font-size: 42px; - .anticon { - color: var(--ant-color-primary); - } - } - .ant-empty-footer { - display: flex; - } -`; - -const ImageWrapper = styled.div` - margin-bottom: 16px; - display: flex; - justify-content: center; - align-items: center; -`; - -const SimpleImageWrapper = styled.div` - margin-bottom: 8px; - display: flex; - justify-content: center; - align-items: center; -`; - -const Description = styled.div` - display: flex; - flex-direction: column; - gap: 12px; - justify-content: center; - align-items: center; -`; - -const NoResult: React.FC< - EmptyProps & { - title?: React.ReactNode; - subTitle?: React.ReactNode; - noFoundText?: React.ReactNode; - filters?: Record; - loading?: boolean; - loadend?: boolean; - dataSource?: any[]; - buttonText?: React.ReactNode; - onClick?: () => void; - } -> = (props) => { - const { - filters, - noFoundText, - loadend, - loading, - dataSource, - buttonText, - onClick - } = props; - - const hasFilters = useMemo(() => { - const filterValues = _.omit(filters, ['page', 'perPage']); - - return Object.values(filterValues || {}).some((value) => { - if (Array.isArray(value)) { - return value.length > 0; - } - - return !!value; - }); - }, [filters]); - - const renderChildren = () => { - if (!buttonText || !onClick) return null; - return ( - - ); - }; - - return ( - <> - {!loading && loadend && !dataSource?.length ? ( - - {Empty.PRESENTED_IMAGE_SIMPLE} - - ) : ( - {props.image} - ) - } - description={ - - {!hasFilters && ( - - {props.title} - - )} - - {hasFilters ? noFoundText : props.subTitle} - - - } - > - {!hasFilters && renderChildren()} - - ) : ( - - )} - - ); -}; - -export default NoResult; diff --git a/src/pages/_components/terminal-tabs/footer.less b/src/pages/_components/terminal-tabs/footer.less deleted file mode 100644 index 94b5e429..00000000 --- a/src/pages/_components/terminal-tabs/footer.less +++ /dev/null @@ -1,13 +0,0 @@ -.ant-pro-footer-bar { - padding-inline: 0; - border-block-start: 1px solid rgba(5, 5, 5, 6%); - border-radius: 4px 4px 0 0; - - .ant-pro-footer-bar-left { - display: none; - } - - .ant-pro-footer-bar-right { - flex: 1; - } -} diff --git a/src/pages/_components/terminal-tabs/index.tsx b/src/pages/_components/terminal-tabs/index.tsx deleted file mode 100644 index 78e10f16..00000000 --- a/src/pages/_components/terminal-tabs/index.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import './footer.less'; -import TerminalTabs from './tabs'; - -export interface TerminalModalProps { - terminals: { url: string; name: string }[]; - open: boolean; - onClose: () => void; - currentActive?: string; -} - -const TerminalModal: React.FC = ({ - terminals, - open, - onClose, - currentActive -}) => { - return ( - - ); -}; - -export default TerminalModal; diff --git a/src/pages/_components/terminal-tabs/resize-container.tsx b/src/pages/_components/terminal-tabs/resize-container.tsx deleted file mode 100644 index b5835b53..00000000 --- a/src/pages/_components/terminal-tabs/resize-container.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { HolderOutlined } from '@ant-design/icons'; -import { useIntl } from '@umijs/max'; -import { Button } from 'antd'; -import { Resizable, ResizableProps } from 're-resizable'; -import React, { forwardRef, useImperativeHandle, useRef } from 'react'; -import styled from 'styled-components'; - -const StyledButton = styled(Button)` - position: absolute; - padding: 0; - top: -12px; - font-size: var(--font-size-middle); - left: calc(50% + 10px); - transform: translateX(-50%); - background: none !important; - cursor: ns-resize; - &::hover { - background: none !important; - } -`; - -const ResizeContainer: React.FC< - React.PropsWithChildren< - ResizableProps & { - ref?: any; - defaultHeight?: number; - defaultWidth?: number; - minHeight?: number; - maxHeight?: number; - onReSize?: ( - e: MouseEvent | TouchEvent, - dir: any, - refToElement: HTMLElement - ) => void; - onReSizeStop?: ( - e: MouseEvent | TouchEvent, - dir: any, - refToElement: HTMLElement, - delta: { width: number; height: number } - ) => void; - } - > -> = forwardRef((props, ref) => { - const { - defaultWidth, - defaultHeight = 180, - minHeight = 180, - maxHeight = 400, - children, - onReSize, - onReSizeStop, - ...restProps - } = props; - const intl = useIntl(); - const resizeRef = useRef(null); - - useImperativeHandle(ref, () => ({ - container: resizeRef.current - })); - - const dragHanle = ( - - } - > - ); - - return ( - - {children} - - ); -}); - -export default ResizeContainer; diff --git a/src/pages/_components/terminal-tabs/tabs.tsx b/src/pages/_components/terminal-tabs/tabs.tsx deleted file mode 100644 index 0c3dc6a6..00000000 --- a/src/pages/_components/terminal-tabs/tabs.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { CloseOutlined } from '@ant-design/icons'; -import { XTerminal } from '@gpustack/core-ui'; -import { Button, Tabs } from 'antd'; -import { throttle } from 'lodash'; -import React, { useEffect, useMemo, useState } from 'react'; -import styled from 'styled-components'; -import ResizeContainer from './resize-container'; -import { TerminalProps } from './types'; - -const TabsContainer = styled.div` - .ant-tabs { - .ant-tabs-tab { - positon: relative; - &::after { - content: ''; - position: absolute; - right: 0; - top: 50%; - transform: translateY(-50%); - width: 1px; - border-right: 1px solid var(--ant-color-split); - height: 20px; - } - } - } - .ant-tabs-nav { - .ant-tabs-tab { - border: none; - background-color: transparent; - - .ant-tabs-tab-remove .anticon-close { - // display: none; - height: 0; - overflow: hidden; - transition: all 0.3s; - } - - &::before { - display: none; - content: ''; - left: 5px; - top: 5px; - bottom: 5px; - right: 5px; - position: absolute; - border-radius: 4px; - background-color: var(--ant-color-fill-tertiary); - } - - &:hover { - &::before { - display: block; - } - .ant-tabs-tab-remove .anticon-close { - // display: inline-block; - height: auto; - } - } - } - - .ant-tabs-tab:not(.ant-tabs-tab-active) { - color: var(--ant-color-text-secondary); - } - - .ant-tabs-tab-active { - background-color: var(--ant-color-bg-elevated); - color: var(--ant-color-text); - } - } -`; - -export interface TerminalTabsProps { - terminals: TerminalProps[]; - currentActive?: string; - onClose: () => void; -} - -const TerminalTabs: React.FC = ({ - terminals, - currentActive, - onClose -}) => { - const [activeKey, setActiveKey] = useState( - currentActive || terminals[0]?.url || '' - ); - const [height, setHeight] = useState(300); - const resizeRef = React.useRef(null); - - const items = useMemo(() => { - return terminals.map((terminal) => { - return { - key: terminal.url, - label: terminal.name, - children: - }; - }); - }, [terminals, height]); - - const handleOnResize = throttle(() => { - const newHeight = resizeRef.current?.container?.state?.height; - console.log('newHeight', newHeight); - setHeight(newHeight - 43); // 43 is the height of the tabs header - }, 200); - - useEffect(() => { - if (currentActive) { - setActiveKey(currentActive); - } - }, [currentActive]); - - return ( - - - } - type="text" - size="small" - style={{ marginRight: 8 }} - onClick={onClose} - > - ) - }} - > - - - ); -}; - -export default TerminalTabs; diff --git a/src/pages/_components/terminal-tabs/types.ts b/src/pages/_components/terminal-tabs/types.ts deleted file mode 100644 index 340c6eb4..00000000 --- a/src/pages/_components/terminal-tabs/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export interface TerminalProps { - url: string; - name: string; -} diff --git a/src/pages/_components/yaml-editor/editor.tsx b/src/pages/_components/yaml-editor/editor.tsx deleted file mode 100644 index 28860747..00000000 --- a/src/pages/_components/yaml-editor/editor.tsx +++ /dev/null @@ -1,144 +0,0 @@ -import { LoadingOutlined } from '@ant-design/icons'; -import { EditorWrap } from '@gpustack/core-ui'; -import Editor, { loader } from '@monaco-editor/react'; -import * as monaco from 'monaco-editor'; -import { yamlDefaults } from 'monaco-yaml'; -import React, { - forwardRef, - useEffect, - useImperativeHandle, - useRef -} from 'react'; - -loader.config({ - monaco -}); - -interface ViewerProps { - ref?: any; - defaultLang?: string; - config?: any; - value: string; - height?: string | number; - theme?: string; - header?: React.ReactNode; - placeholder?: string; - variant?: 'bordered' | 'borderless'; - schema?: any; -} - -const path = 'inmemory://model/config.yaml'; - -const EditorInner: React.FC = forwardRef((props, ref) => { - const { - value, - height = 380, - theme = 'vs-dark', - header, - variant = 'borderless', - schema, - placeholder - } = props; - - const editorRef = useRef(null); - const monacoRef = useRef(null); - const monacoYamlRef = useRef(null); - - const handleBeforeMount = (monaco: any) => { - const yamlUri = monaco.Uri.parse(path); - - yamlDefaults.setDiagnosticsOptions({ - validate: false, - enableSchemaRequest: true, - hover: false, - schemas: [ - { - uri: 'http://example.com/schema-name.json', - fileMatch: [yamlUri.toString()], - schema: schema - } - ] - }); - }; - - const handleEditorDidMount = (editor: any, monaco: any) => { - editorRef.current = editor; - monacoRef.current = monaco; - }; - - const formatCode = () => { - if (editorRef.current) { - setTimeout(() => { - editorRef.current - ?.getAction?.('editor.action.formatDocument') - ?.run() - .then(() => { - console.log('format success'); - }); - }, 100); - } - }; - - // Currently, do not use this function, but keep it for future validation needs - const getMarkers = () => { - const uri = editorRef.current?.getModel()?.uri; - const markers = monacoRef.current?.editor.getModelMarkers({ - resource: uri - }); - return markers; - }; - - useImperativeHandle(ref, () => ({ - format: () => { - formatCode(); - }, - getValue: () => { - return editorRef.current?.getValue?.(); - }, - setValue: (val: string) => { - editorRef.current?.setValue?.(val); - }, - dispose: () => { - editorRef.current?.dispose?.(); - monacoYamlRef.current?.dispose?.(); - }, - validate() { - return getMarkers(); - }, - editor: editorRef.current - })); - - useEffect(() => { - formatCode(); - }, [value]); - - return ( - - } - beforeMount={handleBeforeMount} - onMount={handleEditorDidMount} - /> - - ); -}); - -export default EditorInner; diff --git a/src/pages/_components/yaml-editor/index.tsx b/src/pages/_components/yaml-editor/index.tsx deleted file mode 100644 index 2aabd2a9..00000000 --- a/src/pages/_components/yaml-editor/index.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import useUserSettings from '@/hooks/use-user-settings'; -import { ImportOutlined } from '@ant-design/icons'; -import { loader } from '@monaco-editor/react'; -import { useIntl } from '@umijs/max'; -import { Button, message, Typography, Upload } from 'antd'; -import { RcFile } from 'antd/lib/upload'; -import * as monaco from 'monaco-editor'; -import React, { - forwardRef, - useEffect, - useImperativeHandle, - useRef -} from 'react'; -import styled from 'styled-components'; -import EditorInner from './editor'; - -const { Text } = Typography; - -loader.config({ monaco }); - -const Container = styled.div` - position: relative; - border: 1px solid var(--ant-color-border); - border-radius: var(--ant-border-radius); - overflow: hidden; - .monaco-editor .scroll-decoration { - box-shadow: none; - } -`; - -const ErrorText = styled(Text)` - position: absolute; - left: 0; - right: 0; - bottom: 0; - padding: 4px 6px; - background-color: var(--ant-color-bg-elevated); - border-radius: 0 0 var(--ant-border-radius) var(--ant-border-radius); -`; - -const Header = styled.div` - width: 100%; - display: flex; - align-items: center; - justify-content: space-between; - height: 40px; - padding-inline: 10px; - font-size: 14px; - border-bottom: 1px solid var(--ant-color-border); - background-color: var(--ant-color-fill-quaternary); -`; - -interface ViewerProps { - ref?: any; - title?: React.ReactNode; - defaultLang?: string; - config?: any; - value: string; - height?: string | number; - placeholder?: string; - variant?: 'bordered' | 'borderless'; - validateMessage?: React.ReactNode; - schema?: any; - onUpload?: (content: string) => void; -} - -const YamlEditor: React.FC = forwardRef((props, ref) => { - const { - value, - height = 380, - variant = 'borderless', - schema, - placeholder, - validateMessage, - title, - onUpload - } = props; - - const intl = useIntl(); - const { userSettings } = useUserSettings(); - - const editorRef = useRef(null); - - const setContent = (val: string) => { - editorRef.current?.setValue?.(val); - }; - - const beforeUpload = (file: RcFile) => { - const isYaml = - file.type === 'application/x-yaml' || - file.type === 'text/yaml' || - file.name.endsWith('.yaml') || - file.name.endsWith('.yml'); - if (!isYaml) { - message.error('You can only upload YAML file!'); - } - const reader = new FileReader(); - reader.onload = (e) => { - const content = e.target?.result; - if (typeof content === 'string') { - onUpload?.(content); - setContent(content); - } else { - message.error('Failed to read file content!'); - } - }; - reader.readAsText(file); - // Prevent upload - return false; - }; - - const renderHeader = () => { - return ( -
- {title || 'YAML'} - - - -
- ); - }; - - useImperativeHandle(ref, () => ({ - format: () => { - editorRef.current?.format(); - }, - getValue: () => { - return editorRef.current?.getValue?.(); - }, - setValue: (val: string) => { - editorRef.current?.setValue?.(val); - }, - dispose: () => { - editorRef.current?.dispose?.(); - }, - validate() { - return editorRef.current?.validate(); - }, - editor: editorRef.current - })); - - useEffect(() => { - editorRef.current?.format(); - }, [value]); - - return ( - - - {validateMessage && ( - {validateMessage} - )} - - ); -}); - -export default YamlEditor; diff --git a/src/pages/api-keys/index.tsx b/src/pages/api-keys/index.tsx index ace36760..14aeb49e 100644 --- a/src/pages/api-keys/index.tsx +++ b/src/pages/api-keys/index.tsx @@ -4,13 +4,12 @@ import type { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; import useQueryUserList from '@/pages/users/services/use-query-user-list'; import { useModel } from '@@/plugin-model'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import useMemoizedFn from 'ahooks/lib/useMemoizedFn'; import { ConfigProvider, Table } from 'antd'; import _ from 'lodash'; import { useEffect, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { deleteApisKey, queryApisKeysList } from './apis'; import AddAPIKeyModal from './components/add-apikey-modal'; diff --git a/src/pages/backends/community/backend-detail.tsx b/src/pages/backends/community/backend-detail.tsx index 6a9ae093..0ba6f7e7 100644 --- a/src/pages/backends/community/backend-detail.tsx +++ b/src/pages/backends/community/backend-detail.tsx @@ -1,5 +1,6 @@ import { BulbOutlined } from '@ant-design/icons'; -import { AutoTooltip, FullMarkdown } from '@gpustack/core-ui'; +import { AutoTooltip } from '@gpustack/core-ui'; +import { FullMarkdown } from '@gpustack/core-ui/markdown'; import { useIntl } from '@umijs/max'; import { Button, Tag } from 'antd'; import _ from 'lodash'; diff --git a/src/pages/backends/community/commnity-backends.tsx b/src/pages/backends/community/commnity-backends.tsx index 5b6113aa..ceceefa7 100644 --- a/src/pages/backends/community/commnity-backends.tsx +++ b/src/pages/backends/community/commnity-backends.tsx @@ -1,15 +1,18 @@ import { enabledBackendsAtom } from '@/atoms/backend'; import useTableFetch from '@/hooks/use-table-fetch'; import { SearchOutlined } from '@ant-design/icons'; -import { IconFont, ThemeTag } from '@gpustack/core-ui'; +import { + IconFont, + InfiniteScrollerProvider, + NoResult, + ThemeTag +} from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Input, Tooltip } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; import React, { useEffect } from 'react'; import styled from 'styled-components'; -import { ScrollerContext } from '../../_components/infinite-scroller/use-scroller-context'; -import NoResult from '../../_components/no-result'; import { INFERENCE_BACKEND_API, queryBackendsList } from '../apis'; import BackendCard from '../components/backend-card'; import BackendCardList from '../components/backend-list'; @@ -144,7 +147,7 @@ const CommunityBackends: React.FC<{ onChange={handleNameChange} > - - + ); }; diff --git a/src/pages/backends/components/import-yaml.tsx b/src/pages/backends/components/import-yaml.tsx index 0ae373f3..7f638d1f 100644 --- a/src/pages/backends/components/import-yaml.tsx +++ b/src/pages/backends/components/import-yaml.tsx @@ -1,6 +1,7 @@ import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; -import { YamlEditor } from '@gpustack/core-ui'; +import useUserSettings from '@/hooks/use-user-settings'; +import { YamlEditor } from '@gpustack/core-ui/yaml-editor'; import { useIntl } from '@umijs/max'; import React, { forwardRef, @@ -27,6 +28,7 @@ interface ImportYAMLProps { const ImportYAML: React.FC = forwardRef( ({ actionStatus, content = '', height }, ref) => { const intl = useIntl(); + const { isDarkTheme } = useUserSettings(); const editorRef = useRef(null); const [fileContent, setFileContent] = useState( actionStatus.action === PageAction.CREATE ? yamlTemplate : content @@ -116,6 +118,7 @@ const ImportYAML: React.FC = forwardRef( setError(''); setContent(content); }} + isDarkTheme={isDarkTheme} schema={ actionStatus.action === PageAction.CREATE ? createSchema diff --git a/src/pages/backends/index.tsx b/src/pages/backends/index.tsx index 76b26c81..22aa6ca0 100644 --- a/src/pages/backends/index.tsx +++ b/src/pages/backends/index.tsx @@ -1,12 +1,16 @@ import { PageAction } from '@/config'; import useTableFetch from '@/hooks/use-table-fetch'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { + DeleteModal, + FilterBar, + IconFont, + InfiniteScrollerProvider, + NoResult +} from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import useMemoizedFn from 'ahooks/lib/useMemoizedFn'; import _ from 'lodash'; import { useState } from 'react'; -import { ScrollerContext } from '../_components/infinite-scroller/use-scroller-context'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createBackend, @@ -249,7 +253,7 @@ const BackendList = () => { value: item.value }))} > - { onClick={() => handleAddBackend({ key: 'community' })} buttonText={intl.formatMessage({ id: 'noresult.button.add' })} > - + closeBackendModal('custom')} diff --git a/src/pages/benchmark/hooks/use-export-data.ts b/src/pages/benchmark/hooks/use-export-data.ts index 70e042c2..81aff1f6 100644 --- a/src/pages/benchmark/hooks/use-export-data.ts +++ b/src/pages/benchmark/hooks/use-export-data.ts @@ -1,4 +1,4 @@ -import { exportJsonToExcel } from '@/utils/excel-reader'; +import { exportJsonToExcel } from '@gpustack/core-ui/excel'; const useExportData = (params: { columns: any[] }) => { const { columns } = params; diff --git a/src/pages/benchmark/index.tsx b/src/pages/benchmark/index.tsx index 08daddf5..bccf5dff 100644 --- a/src/pages/benchmark/index.tsx +++ b/src/pages/benchmark/index.tsx @@ -3,13 +3,12 @@ import { PaginationKey, TABLE_SORT_DIRECTIONS } from '@/config/settings'; import useTableFetch from '@/hooks/use-table-fetch'; import { useBenchmarkTargetInstance } from '@/pages/llmodels/hooks/use-run-benchmark'; import { useQueryModelList } from '@/pages/llmodels/services/use-query-model-list'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui'; import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn, useToggle } from 'ahooks'; import { ConfigProvider, Table, message } from 'antd'; import _ from 'lodash'; import { useEffect, useRef, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { useQueryClusterList } from '../cluster-management/services/use-query-cluster-list'; import { diff --git a/src/pages/cluster-management/clusters.tsx b/src/pages/cluster-management/clusters.tsx index f19476ac..dc5998a1 100644 --- a/src/pages/cluster-management/clusters.tsx +++ b/src/pages/cluster-management/clusters.tsx @@ -9,6 +9,7 @@ import { DeleteModal, FilterBar, IconFont, + NoResult, Table as SealTable, TableOrder, TableProvider @@ -19,7 +20,6 @@ import { message } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; import { useEffect, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import useGranfanaLink from '../resources/hooks/use-grafana-link'; import { diff --git a/src/pages/cluster-management/credentials.tsx b/src/pages/cluster-management/credentials.tsx index 77360b32..3b7581d6 100644 --- a/src/pages/cluster-management/credentials.tsx +++ b/src/pages/cluster-management/credentials.tsx @@ -3,14 +3,13 @@ import { PageAction } from '@/config'; import { PaginationKey, TABLE_SORT_DIRECTIONS } from '@/config/settings'; import type { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui'; import { useIntl, useNavigate } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { ConfigProvider, Table, message } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; import { useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createCredential, diff --git a/src/pages/cluster-management/step-forms/advance-config.tsx b/src/pages/cluster-management/step-forms/advance-config.tsx index 7806dd3b..d9d52e00 100644 --- a/src/pages/cluster-management/step-forms/advance-config.tsx +++ b/src/pages/cluster-management/step-forms/advance-config.tsx @@ -1,6 +1,8 @@ import { PageAction } from '@/config'; import { PageActionType } from '@/config/types'; -import { Input as CInput, IconFont, YamlEditor } from '@gpustack/core-ui'; +import useUserSettings from '@/hooks/use-user-settings'; +import { Input as CInput, IconFont } from '@gpustack/core-ui'; +import { YamlEditor } from '@gpustack/core-ui/yaml-editor'; import { useIntl } from '@umijs/max'; import { Button, Form } from 'antd'; import React, { forwardRef, useEffect, useImperativeHandle } from 'react'; @@ -31,6 +33,7 @@ const ClusterAdvanceConfig: React.FC<{ const [form] = Form.useForm(); const intl = useIntl(); const editorRef = React.useRef(null); + const { isDarkTheme } = useUserSettings(); const [fileContent, setFileContent] = React.useState(''); useImperativeHandle(ref, () => ({ @@ -79,6 +82,7 @@ const ClusterAdvanceConfig: React.FC<{ {`YAML`} diff --git a/src/pages/dashboard/components/usage-inner/export-data.tsx b/src/pages/dashboard/components/usage-inner/export-data.tsx index 68be9295..5c235a68 100644 --- a/src/pages/dashboard/components/usage-inner/export-data.tsx +++ b/src/pages/dashboard/components/usage-inner/export-data.tsx @@ -1,5 +1,5 @@ -import { exportJsonToExcel } from '@/utils/excel-reader'; import { AutoTooltip, ModalFooter, ScrollerModal } from '@gpustack/core-ui'; +import { exportJsonToExcel } from '@gpustack/core-ui/excel'; import { useIntl } from '@umijs/max'; import { Table, TableColumnType } from 'antd'; import dayjs from 'dayjs'; diff --git a/src/pages/llmodels/catalog.tsx b/src/pages/llmodels/catalog.tsx index f6df02ae..46091144 100644 --- a/src/pages/llmodels/catalog.tsx +++ b/src/pages/llmodels/catalog.tsx @@ -7,14 +7,14 @@ import { SearchOutlined } from '@ant-design/icons'; import { FilterBar, IconFont, - InfiniteScrollerProvider + InfiniteScrollerProvider, + NoResult } from '@gpustack/core-ui'; import { useIntl, useNavigate } from '@umijs/max'; import { message } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; import React, { useCallback, useEffect, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createModel, queryCatalogItemSpec, queryCatalogList } from './apis'; import CatalogList from './components/catalog/catalog-list'; diff --git a/src/pages/llmodels/components/model-source/model-card.tsx b/src/pages/llmodels/components/model-source/model-card.tsx index 63ae1a27..fd416b8c 100644 --- a/src/pages/llmodels/components/model-source/model-card.tsx +++ b/src/pages/llmodels/components/model-source/model-card.tsx @@ -5,16 +5,11 @@ import { FileMarkdownOutlined, RightOutlined } from '@ant-design/icons'; -import { - IconFont, - MarkdownViewer, - SimpleOverlay, - ThemeTag -} from '@gpustack/core-ui'; +import { IconFont, SimpleOverlay, ThemeTag } from '@gpustack/core-ui'; +import { MarkdownViewer } from '@gpustack/core-ui/markdown'; import { useIntl } from '@umijs/max'; import { Button, Empty, Spin, Tooltip } from 'antd'; import { some } from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { useCallback, useEffect, diff --git a/src/pages/maas-provider/forms/advance-config.tsx b/src/pages/maas-provider/forms/advance-config.tsx index 6f5f1948..f8bb6a29 100644 --- a/src/pages/maas-provider/forms/advance-config.tsx +++ b/src/pages/maas-provider/forms/advance-config.tsx @@ -1,5 +1,7 @@ import { PageActionType } from '@/config/types'; -import { Input as CInput, IconFont, YamlEditor } from '@gpustack/core-ui'; +import useUserSettings from '@/hooks/use-user-settings'; +import { Input as CInput, IconFont } from '@gpustack/core-ui'; +import { YamlEditor } from '@gpustack/core-ui/yaml-editor'; import { useIntl } from '@umijs/max'; import { Button, Form } from 'antd'; import React, { forwardRef, useImperativeHandle } from 'react'; @@ -11,6 +13,7 @@ const AdvanceConfig: React.FC<{ action: PageActionType; ref?: any; }> = forwardRef(({ action }, ref) => { + const { isDarkTheme } = useUserSettings(); const form = Form.useFormInstance(); const intl = useIntl(); const editorRef = React.useRef(null); @@ -48,6 +51,7 @@ const AdvanceConfig: React.FC<{ {`${intl.formatMessage({ id: 'providers.form.customConfig' })} (YAML)`} diff --git a/src/pages/maas-provider/index.tsx b/src/pages/maas-provider/index.tsx index 5a5afe63..d29af478 100644 --- a/src/pages/maas-provider/index.tsx +++ b/src/pages/maas-provider/index.tsx @@ -1,12 +1,11 @@ import { PageAction } from '@/config'; import { PaginationKey, TABLE_SORT_DIRECTIONS } from '@/config/settings'; import useTableFetch from '@/hooks/use-table-fetch'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { ConfigProvider, message, Table } from 'antd'; import _ from 'lodash'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createProvider, diff --git a/src/pages/model-routes/index.tsx b/src/pages/model-routes/index.tsx index 242abc27..0e198632 100644 --- a/src/pages/model-routes/index.tsx +++ b/src/pages/model-routes/index.tsx @@ -10,6 +10,7 @@ import { DeleteModal, FilterBar, IconFont, + NoResult, Table as SealTable, TableOrder, TableProvider @@ -20,7 +21,6 @@ import { message } from 'antd'; import { useAtom } from 'jotai'; import _ from 'lodash'; import { useEffect, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { queryModelsList } from '../llmodels/apis'; import AccessControlModal from '../llmodels/components/access-control-modal'; diff --git a/src/pages/playground/chat/single-chat.tsx b/src/pages/playground/chat/single-chat.tsx index 84ab7ae8..31a7ef5b 100644 --- a/src/pages/playground/chat/single-chat.tsx +++ b/src/pages/playground/chat/single-chat.tsx @@ -1,5 +1,4 @@ import { Spin } from 'antd'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useImperativeHandle, diff --git a/src/pages/playground/components/multiple-chat/message-body.tsx b/src/pages/playground/components/multiple-chat/message-body.tsx index 64fb88bf..da7af455 100644 --- a/src/pages/playground/components/multiple-chat/message-body.tsx +++ b/src/pages/playground/components/multiple-chat/message-body.tsx @@ -1,4 +1,5 @@ -import { FullMarkdown, SimpleAudio } from '@gpustack/core-ui'; +import { SimpleAudio } from '@gpustack/core-ui'; +import { FullMarkdown } from '@gpustack/core-ui/markdown'; import { Input } from 'antd'; import classNames from 'classnames'; import _ from 'lodash'; diff --git a/src/pages/playground/components/multiple-chat/model-item.tsx b/src/pages/playground/components/multiple-chat/model-item.tsx index 1f85df17..7a85c0e5 100644 --- a/src/pages/playground/components/multiple-chat/model-item.tsx +++ b/src/pages/playground/components/multiple-chat/model-item.tsx @@ -13,7 +13,6 @@ import { import { useIntl } from '@umijs/max'; import { Button, Checkbox, Dropdown, Popover, Spin } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/playground/components/multiple-chat/think-content.tsx b/src/pages/playground/components/multiple-chat/think-content.tsx index 4ee34e9f..d5e8f815 100644 --- a/src/pages/playground/components/multiple-chat/think-content.tsx +++ b/src/pages/playground/components/multiple-chat/think-content.tsx @@ -1,4 +1,4 @@ -import { FullMarkdown } from '@gpustack/core-ui'; +import { FullMarkdown } from '@gpustack/core-ui/markdown'; import React from 'react'; import '../../style/think-content.less'; diff --git a/src/pages/playground/components/upload-file.tsx b/src/pages/playground/components/upload-file.tsx index 7ff63c6d..30bb490f 100644 --- a/src/pages/playground/components/upload-file.tsx +++ b/src/pages/playground/components/upload-file.tsx @@ -1,11 +1,13 @@ import { readBlob } from '@/utils'; -import readEpubContent from '@/utils/epub-reader'; -import readExcelContent from '@/utils/excel-reader'; -import readPDFContent from '@/utils/pdf-reader'; -import readPptxContent from '@/utils/pptx-reader'; -import readHtmlContent from '@/utils/read-html'; -import readWordContent from '@/utils/word-reader'; import { PaperClipOutlined } from '@ant-design/icons'; +import { + readEpubContent, + readExcelContent, + readHtmlContent, + readPDFContent, + readPptxContent, + readWordContent +} from '@gpustack/core-ui/file-readers'; import { useIntl } from '@umijs/max'; import { Button, Tooltip, Upload } from 'antd'; import type { UploadFile } from 'antd/es/upload'; diff --git a/src/pages/playground/embedding/page.tsx b/src/pages/playground/embedding/page.tsx index 3febd6e9..40c67f70 100644 --- a/src/pages/playground/embedding/page.tsx +++ b/src/pages/playground/embedding/page.tsx @@ -18,7 +18,6 @@ import { useIntl } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { Button, Checkbox, Form, Segmented, Spin, Tabs, Tooltip } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/playground/images/create.tsx b/src/pages/playground/images/create.tsx index 05218d8c..2b15da90 100644 --- a/src/pages/playground/images/create.tsx +++ b/src/pages/playground/images/create.tsx @@ -7,7 +7,6 @@ import { AlertInfo, IconFont } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Button, Tooltip } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/playground/images/edit.tsx b/src/pages/playground/images/edit.tsx index 1821657f..010b0a6f 100644 --- a/src/pages/playground/images/edit.tsx +++ b/src/pages/playground/images/edit.tsx @@ -12,7 +12,6 @@ import { import { useIntl } from '@umijs/max'; import { Divider } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/playground/rerank/page.tsx b/src/pages/playground/rerank/page.tsx index 6b88e35f..975a7fe1 100644 --- a/src/pages/playground/rerank/page.tsx +++ b/src/pages/playground/rerank/page.tsx @@ -9,7 +9,6 @@ import { AlertInfo, useOverlayScroller } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Button, Checkbox, Input, Spin, Tag, Tooltip, Typography } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/playground/speech/tts.tsx b/src/pages/playground/speech/tts.tsx index a53a734a..81971165 100644 --- a/src/pages/playground/speech/tts.tsx +++ b/src/pages/playground/speech/tts.tsx @@ -4,7 +4,6 @@ import { AlertInfo, IconFont, SpeechContent } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Spin } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useImperativeHandle, diff --git a/src/pages/playground/video/page.tsx b/src/pages/playground/video/page.tsx index 41ae42b5..6856f235 100644 --- a/src/pages/playground/video/page.tsx +++ b/src/pages/playground/video/page.tsx @@ -6,7 +6,6 @@ import { AlertInfo, IconFont } from '@gpustack/core-ui'; import { useIntl } from '@umijs/max'; import { Button, Spin, Tooltip } from 'antd'; import _ from 'lodash'; -import 'overlayscrollbars/overlayscrollbars.css'; import React, { forwardRef, useCallback, diff --git a/src/pages/usage/components/export-data.tsx b/src/pages/usage/components/export-data.tsx index 242ff258..1bdf57a1 100644 --- a/src/pages/usage/components/export-data.tsx +++ b/src/pages/usage/components/export-data.tsx @@ -1,5 +1,5 @@ -import { exportJsonToExcel } from '@/utils/excel-reader'; import { AutoTooltip, ModalFooter, ScrollerModal } from '@gpustack/core-ui'; +import { exportJsonToExcel } from '@gpustack/core-ui/excel'; import { useIntl } from '@umijs/max'; import { Table, TableColumnType } from 'antd'; import React, { useEffect } from 'react'; diff --git a/src/pages/usage/hooks/use-export-table.tsx b/src/pages/usage/hooks/use-export-table.tsx index c5df062d..3eb72c89 100644 --- a/src/pages/usage/hooks/use-export-table.tsx +++ b/src/pages/usage/hooks/use-export-table.tsx @@ -3,7 +3,7 @@ import { modelsTableDataAtom, usersTableDataAtom } from '@/atoms/usage'; -import { exportJsonToExcel } from '@/utils/excel-reader'; +import { exportJsonToExcel } from '@gpustack/core-ui/excel'; import { useStore } from 'jotai'; import _ from 'lodash'; import useAPIKeysColumns from './use-apikeys-columns'; diff --git a/src/pages/usage/hooks/use-usage-filters.ts b/src/pages/usage/hooks/use-usage-filters.ts index f4a8ec1f..e919c611 100644 --- a/src/pages/usage/hooks/use-usage-filters.ts +++ b/src/pages/usage/hooks/use-usage-filters.ts @@ -1,4 +1,4 @@ -import { exportJsonToExcel } from '@/utils/excel-reader'; +import { exportJsonToExcel } from '@gpustack/core-ui/excel'; import dayjs from 'dayjs'; import { useEffect, useMemo, useState } from 'react'; import { diff --git a/src/pages/users/index.tsx b/src/pages/users/index.tsx index db966a16..78c819a2 100644 --- a/src/pages/users/index.tsx +++ b/src/pages/users/index.tsx @@ -2,13 +2,12 @@ import { PageAction } from '@/config'; import { PaginationKey, TABLE_SORT_DIRECTIONS } from '@/config/settings'; import type { PageActionType } from '@/config/types'; import useTableFetch from '@/hooks/use-table-fetch'; -import { DeleteModal, FilterBar, IconFont } from '@gpustack/core-ui'; +import { DeleteModal, FilterBar, IconFont, NoResult } from '@gpustack/core-ui'; import { useIntl, useModel } from '@umijs/max'; import { useMemoizedFn } from 'ahooks'; import { ConfigProvider, message, Table } from 'antd'; import _ from 'lodash'; import { useMemo, useState } from 'react'; -import NoResult from '../_components/no-result'; import PageBox from '../_components/page-box'; import { createUser, diff --git a/src/utils/epub-reader.ts b/src/utils/epub-reader.ts deleted file mode 100644 index f10492bc..00000000 --- a/src/utils/epub-reader.ts +++ /dev/null @@ -1,29 +0,0 @@ -import ePub from 'epubjs'; - -export default function readEpubContent(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (e: any) { - const arrayBuffer = e.target.result; - const book = ePub(arrayBuffer); - - book.loaded?.spine?.then?.((spine: any) => { - const chapterPromises = spine.spineItems?.map?.((chapter: any) => { - return book.load(chapter.href).then((content: any) => { - return content.body?.textContent || ''; - }); - }); - Promise.all(chapterPromises) - .then((chaptersText) => { - const result = chaptersText.join(''); - resolve(result); - }) - .catch((error) => { - reject(error); - }); - }); - }; - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); -} diff --git a/src/utils/excel-reader.ts b/src/utils/excel-reader.ts deleted file mode 100644 index 79100d45..00000000 --- a/src/utils/excel-reader.ts +++ /dev/null @@ -1,92 +0,0 @@ -import { saveAs } from 'file-saver'; -import XLSX from 'xlsx'; - -export default function readExcelContent(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (e: any) { - const arrayBuffer = e.target.result; - const workbook = XLSX.read(arrayBuffer, { type: 'string' }); - const ws = workbook.Sheets[workbook.SheetNames[0]]; // get the first worksheet - const data = XLSX.utils.sheet_to_json(ws); - resolve(JSON.stringify(data)); - }; - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); -} - -/** - * @param jsonData raw JSON data to export - * @param fields export fields (keys in the JSON objects) - * @param fieldLabels custom the table header labels - * @param formatMap custom the cell format functions - * @param fileName file name for the exported Excel file - */ - -interface FormatMap { - [key: string]: (value: any, row?: any) => any; -} - -interface ExportSheetConfig { - sheetName: string; - jsonData: any[]; - fields: string[]; - fieldLabels?: Record; - formatMap?: FormatMap; -} - -interface ExportExcelOptions { - sheets: ExportSheetConfig[]; - fileName: string; -} - -export function exportJsonToExcel({ - sheets, - fileName = 'data.xlsx' -}: ExportExcelOptions) { - const workbook = XLSX.utils.book_new(); - - sheets.forEach((sheet) => { - const { sheetName, jsonData, fields, fieldLabels, formatMap } = sheet; - - // 1. format data - const formattedData = jsonData.map((row) => { - const result: Record = {}; - - for (const field of fields) { - const rawValue = row[field]; - const formatFn = formatMap?.[field]; - result[field] = formatFn ? formatFn(rawValue, row) : rawValue; - } - - return result; - }); - - // 2. convert to worksheet - const worksheet = XLSX.utils.json_to_sheet(formattedData, { - header: fields - }); - - // 3. customize headers - if (fieldLabels) { - const headerRow = fields.map((key) => fieldLabels[key] || key); - XLSX.utils.sheet_add_aoa(worksheet, [headerRow], { origin: 'A1' }); - } - - // 4. add to workbook - XLSX.utils.book_append_sheet(workbook, worksheet, sheetName); - }); - - // 5. export file - const excelBuffer = XLSX.write(workbook, { - bookType: 'xlsx', - type: 'array' - }); - - const blob = new Blob([excelBuffer], { - type: 'application/octet-stream' - }); - - saveAs(blob, fileName); -} diff --git a/src/utils/index.ts b/src/utils/index.ts index 89f4eed5..80fab049 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -1,5 +1,4 @@ import _ from 'lodash'; -import tinycolor from 'tinycolor2'; export const isNotEmptyValue = (value: any) => { if (Array.isArray(value)) { @@ -245,25 +244,6 @@ export const isOnline = () => { return window.navigator.onLine; }; -export const genColors = ({ - color, - alpha1, - alpha2 -}: { - color: string; - alpha1?: number; - alpha2?: number; -}) => { - const base = tinycolor(color); - const alpha_start = alpha1 || base.getAlpha(); - const alpha_end = alpha2 || base.getAlpha(); - - return [ - base.setAlpha(alpha_start).toRgbString(), - base.setAlpha(alpha_end).toRgbString() - ]; -}; - /** * Parse a command-line parameter string into an array format. * @param paramsString - The parameter string to parse (supports multiline). diff --git a/src/utils/pdf-reader.ts b/src/utils/pdf-reader.ts deleted file mode 100644 index 52a1e93a..00000000 --- a/src/utils/pdf-reader.ts +++ /dev/null @@ -1,48 +0,0 @@ -import * as pdfjsLib from 'pdfjs-dist'; - -pdfjsLib.GlobalWorkerOptions.workerSrc = new URL( - 'pdfjs-dist/build/pdf.worker.mjs', - // @ts-ignore - import.meta.url -).href; - -const readPDFContent = (file: File): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = async function (e: any) { - try { - const arrayBuffer = e.target.result; - const loadingTask = pdfjsLib.getDocument({ data: arrayBuffer }); - - const pdf = await loadingTask.promise; - const numPages = pdf.numPages; - - const pagePromises = []; - - for (let i = 1; i <= numPages; i++) { - const pagePromise = pdf.getPage(i).then(function (page) { - return page.getTextContent().then(function (textContent) { - return textContent.items - .map(function (item: any) { - return item.str; - }) - .join(' '); - }); - }); - pagePromises.push(pagePromise); - } - - const pageTexts = await Promise.all(pagePromises); - const result = pageTexts?.join(' '); - resolve(result); - } catch (error) { - reject(error); - } - }; - - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); -}; - -export default readPDFContent; diff --git a/src/utils/pptx-reader.ts b/src/utils/pptx-reader.ts deleted file mode 100644 index 8f2cd915..00000000 --- a/src/utils/pptx-reader.ts +++ /dev/null @@ -1,40 +0,0 @@ -import JSZip from 'jszip'; - -const readPptxContent = (file: File): Promise => { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (e: any) { - const arrayBuffer = e.target.result; - JSZip.loadAsync(arrayBuffer).then(function (zip: any) { - const slideFiles = Object.keys(zip.files).filter(function (fileName) { - return ( - fileName.startsWith('ppt/slides/slide') && fileName.endsWith('.xml') - ); - }); - - let slideText = ''; - const slidePromises = slideFiles.map((slideFile) => - zip - .file(slideFile) - .async('string') - .then(function (content: any) { - const parser = new DOMParser(); - const xmlDoc = parser.parseFromString(content, 'application/xml'); - const texts = xmlDoc.getElementsByTagName('a:t'); - for (let i = 0; i < texts.length; i++) { - slideText += texts[i].textContent + '\n'; - } - }) - ); - - Promise.all(slidePromises).then(() => { - resolve(slideText); - }); - }); - }; - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); -}; - -export default readPptxContent; diff --git a/src/utils/word-reader.ts b/src/utils/word-reader.ts deleted file mode 100644 index e07c6a86..00000000 --- a/src/utils/word-reader.ts +++ /dev/null @@ -1,18 +0,0 @@ -import mammoth from 'mammoth'; - -export default function readWordContent(file: File): Promise { - return new Promise((resolve, reject) => { - const reader = new FileReader(); - reader.onload = function (e: any) { - const arrayBuffer = e.target.result; - mammoth - .extractRawText({ arrayBuffer }) - .then((result) => { - resolve(result.value); - }) - .catch((error) => reject(error)); - }; - reader.onerror = (error) => reject(error); - reader.readAsArrayBuffer(file); - }); -} diff --git a/tsconfig.json b/tsconfig.json index d030004f..e2c8f685 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,7 +3,7 @@ "baseUrl": "./", "target": "esnext", "module": "esnext", - "moduleResolution": "node", + "moduleResolution": "bundler", "jsx": "react-jsx", "esModuleInterop": true, "experimentalDecorators": true,