Commit 791e653c authored by dechen lin's avatar dechen lin
Browse files

feat: add web project

parent 9d689790
.code-mirror {
:global {
.ͼ1 .cm-scroller {
overflow-x: visible !important;
border: none !important;
}
.cm-editor.cm-focused {
outline: none !important;
}
}
}
\ No newline at end of file
import React, { PropsWithChildren } from "react";
import ReactCodeMirror, { EditorView, Extension } from "@uiw/react-codemirror";
import { loadLanguage } from "@uiw/codemirror-extensions-langs";
import cls from "classnames";
import style from "./index.module.scss";
// import { scrollPastEnd } from "@codemirror/view";
interface IProps {
className?: string;
editable?: boolean;
language?: "json" | "markdown" | "yaml";
value: string;
onChange?: (value: string) => void;
lineWrapping?: boolean;
onBeforeChange?: (editor: any, data: any, value: any) => void;
}
const CodeMirror: React.FC<PropsWithChildren<IProps>> = ({
language = "markdown",
value,
className,
onChange,
lineWrapping,
onBeforeChange,
editable,
}) => {
// const noScrollPastEnd = scrollPastEnd();
const extensions = [
{
ext: EditorView.lineWrapping,
on: lineWrapping,
},
{
ext: loadLanguage(language),
on: true,
},
]
.map((i) => (i.on ? i.ext : null))
.filter(Boolean) as Extension[];
return (
<ReactCodeMirror
className={cls("odl-code-mirror", className, style.codeMirror)}
value={value}
theme="light"
basicSetup={{
lineNumbers: false,
highlightActiveLineGutter: false,
foldGutter: false,
highlightActiveLine: false,
// syntaxHighlighting: true,
}}
editable={editable}
extensions={extensions}
onChange={(v) => {
console.log("test-rest-change", v);
onChange?.(v);
}}
onBeforeInput={(v) => {
console.log("test-onBeforeInput", v);
}}
/>
);
};
export default React.memo(CodeMirror);
import { Component, ErrorInfo, ReactNode } from "react";
interface Props {
children: ReactNode;
fallback?: ReactNode;
}
interface State {
hasError: boolean;
}
class ErrorBoundary extends Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = { hasError: false };
}
static getDerivedStateFromError(_: Error): State {
return { hasError: true };
}
componentDidCatch(error: Error, errorInfo: ErrorInfo) {
console.error("ErrorBoundary caught an error:", error, errorInfo);
}
render() {
if (this.state.hasError) {
return (
this.props.children || (
<h1 className="text-red-500 flex items-center justify-center">
Error
</h1>
)
);
}
return this.props.children;
}
}
export default ErrorBoundary;
import { createFromIconfontCN } from "@ant-design/icons";
const IconFont = createFromIconfontCN({
scriptUrl: `/iconfont.js`,
});
export default IconFont;
import styles from './loadingAnimation.module.scss';
interface ILoadingAnimationProps {
className?: string;
}
const LoadingAnimation = (props: ILoadingAnimationProps) => {
const { className } = props;
return <div className={`${styles.loader} ${className}`}></div>;
};
export default LoadingAnimation;
.loader {
width: 14px;
height: 14px;
border-radius: 50%;
display: inline-block;
position: relative;
/* stylelint-disable-next-line alpha-value-notation */
background: linear-gradient(0deg, rgba(13,83,222,1) 0%, rgba(43,105,226,1) 30%, rgba(13, 20, 222, 0) 100%);
box-sizing: border-box;
animation: rotation 1.5s linear infinite;
}
.loader::after {
content: '';
box-sizing: border-box;
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
width: 10px;
height: 10px;
border-radius: 50%;
background: #fff;
}
@keyframes rotation {
0% { transform: rotate(0deg) }
100% { transform: rotate(360deg)}
}
.textTooltip {
:global {
.ant-tooltip-arrow {
// display: none !important;
}
.ant-tooltip-inner, .ant-tooltip-content, .ant-tooltip-inner-content {
padding: 0px !important;
border-radius: 4px !important;
overflow: hidden;
}
}
}
import { Popover, Tooltip } from 'antd';
import React, { useRef, useState } from 'react';
import styles from './index.module.scss';
import { RefObject } from 'react';
import { useIsomorphicLayoutEffect, useMemoizedFn } from 'ahooks';
export function useResizeEffect<T extends HTMLElement>(effect: (target: T) => void, targetRef: RefObject<T>) {
const fn = useMemoizedFn(effect);
useIsomorphicLayoutEffect(() => {
const target = targetRef.current;
if (!target) return;
if (window.ResizeObserver) {
let animationFrame: number;
const observer = new ResizeObserver(() => {
animationFrame = window.requestAnimationFrame(() => fn(target));
});
observer.observe(target);
return () => {
window.cancelAnimationFrame(animationFrame);
observer.disconnect();
};
} else {
fn(target);
}
}, [targetRef]);
}
interface ITextTooltip {
style?: React.CSSProperties;
str: string;
suffix?: React.ReactNode | string;
trigger?: 'hover' | 'click';
handleClick?: () => void;
}
export const TextTooltip = (props: ITextTooltip) => {
const { style = {}, str, trigger = 'click', suffix, handleClick } = props;
const rootRef = useRef<HTMLDivElement>(null);
const tooltipRef = useRef<HTMLDivElement>(null);
const [clickable, setClickable] = useState(false);
function calcEllipsised() {
// 没有被截断
if (tooltipRef!?.current!?.scrollWidth > tooltipRef!?.current!?.clientWidth) {
setClickable(true);
} else {
setClickable(false);
}
}
useResizeEffect(calcEllipsised, rootRef);
return (
<Tooltip
title={<div className="bg-black/[0.85] text-white p-[6px]">{str}</div>}
trigger={clickable ? trigger : ('' as 'click')}
overlayClassName={styles.textTooltip}
style={{ width: '100%' }}
zIndex={999999}
placement="right"
align={{
offset: [72, 0]
}}
>
<div style={{ width: '100%', ...style }} className="flex" ref={rootRef}>
<div className="text-ellipsis overflow-hidden whitespace-nowrap" ref={tooltipRef}>
<span onClick={() => handleClick?.()}>{str}</span>
</div>
{suffix}
</div>
</Tooltip>
);
};
// cl 2022/4/21 18:22
import { customUploadToOss } from "@/api/oss";
import { Upload as AntdUpload } from "antd";
import { DraggerProps, UploadProps } from "antd/es/upload";
import React from "react";
interface IProps extends UploadProps, DraggerProps {
isDragger?: boolean;
openRead?: boolean;
taskType?: string;
changeOption?: (option: any) => any;
}
const Upload: React.FC<IProps> = (props) => {
const { isDragger, openRead, taskType, changeOption, ...rest } = props;
const Component = isDragger ? AntdUpload.Dragger : AntdUpload;
return (
<Component
{...rest}
customRequest={(options: any) =>
customUploadToOss(changeOption ? changeOption?.(options) : options, {
openRead: openRead || false,
fileType: taskType || "pdf",
uploadType: "local",
})
}
>
{props.children}
</Component>
);
};
export default Upload;
export const PDF_DRIVE_MD = "pdf-drive-md";
export const MD_DRIVE_PDF = "pdf-drive-md";
export const ADD_TASK_LIST = "add-task-list";
export const UPDATE_TASK_LIST = "update-task-list";
export enum Language {
ZH_CN = "zh-CN",
EN_US = "en-US",
}
export const PDF_COLOR_PICKER = {
title: {
line: 'rgba(121, 124, 255, 1)',
fill: 'rgba(121, 124, 255, 0.4)'
},
text: {
line: 'rgba(230, 122, 171, 1)',
fill: 'rgba(230, 122, 171, 0.4)'
},
interline_equation: {
line: 'rgba(240, 240, 124, 1)',
fill: 'rgba(240, 240, 124, 0.4)'
},
discarded: {
line: 'rgba(164,164,164,1)',
fill: 'rgba(164,164,164,0.4)'
},
image: {
line: 'rgba(149, 226, 115, 1)',
fill: 'rgba(149, 226, 115, 0.4)'
},
table: {
line: 'rgba(230, 113, 230, 1)',
fill: 'rgba(230, 113, 230, 0.4)'
},
inline_equation: {
line: 'rgba(150, 232, 172, 1)',
fill: 'rgba(150, 232, 172, 0.4)'
}
};
export const DEFAULT_COLOR_SECTION = {
line: 'rgba(166, 113, 230, 1)',
fill: 'rgba(166, 113, 230, 0.4)'
};
export const PDF_TEMPLATE_URL_KEY = 't';
\ No newline at end of file
export enum Path {
Home = "/",
Settings = "/settings",
}
export enum SlotID {
AppBody = "app-body",
}
export const LOCALE_STORAGE_KEY = "locale-minerU"
\ No newline at end of file
import React from "react";
import { IntlProvider } from "react-intl";
import { useLanguageStore } from "@/store/languageStore";
import contentEn from "@/locale/en.json";
import contentZh from "@/locale/zh.json";
import sideEn from "@/locale/side/en.ts";
import sideZh from "@/locale/side/zh.ts";
import commonEn from "@/locale/common/en.json";
import commonZh from "@/locale/common/zh.json";
import { Language } from "@/constant";
const messages = {
[Language.EN_US]: {
...contentEn,
...sideEn,
...commonEn,
},
[Language.ZH_CN]: {
...contentZh,
...sideZh,
...commonZh,
},
};
export const LanguageProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const { language } = useLanguageStore();
return (
<IntlProvider
messages={messages[language] as unknown as Record<string, string>}
locale={language}
defaultLocale="en"
>
{children}
</IntlProvider>
);
};
// QueryProvider.tsx
import React from "react";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
const defaultQueryClientConfig = {
defaultOptions: {
queries: {
retry: false,
refetchOnWindowFocus: false,
staleTime: 5 * 60 * 1000, // 5 minutes
},
},
};
interface QueryProviderProps {
children: React.ReactNode;
}
const QueryProvider: React.FC<QueryProviderProps> = ({ children }) => {
const queryClient = new QueryClient({
...defaultQueryClientConfig,
});
return (
<QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
);
};
export default QueryProvider;
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
line-height: 1.5;
font-weight: 400;
color-scheme: light dark;
color: rgba(0, 0, 0, 0.87);
background-color: white;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
{
"common.cancel": "Cancel",
"common.confirm": "Confirm",
"common.retry": "retry"
}
\ No newline at end of file
{
"common.cancel": "取消",
"common.confirm": "确定",
"common.retry": "retry"
}
\ No newline at end of file
{
"extractor.common.upload": "Click to upload file",
"extractor.common.try": "Try:",
"extractor.home": "Home",
"extractor.button.download": "Download",
"extractor.button.lineWrap": "Line Wrap",
"extractor.button.fullScreen": "Fullscreen",
"extractor.button.exitFullScreen": "Exit Fullscreen",
"extractor.button.showLayer": "Display recognition results",
"extractor.button.hiddenLayer": "Hide recognition results",
"extractor.error": "Extraction failed",
"extractor.law": "Please ensure that the documents you upload are legal and compliant, and we do not assume legal responsibility for the content of the documents. Data Protection Policy Children's Information Protection Policy Service Agreement | © All Rights Reserved.Shanghai ICP 2021009351-21",
"extractor.button.reUpload": "Re-Upload",
"extractor.failed": "Unable to extract, no data to display yet",
"extractor.common.extracting": "Extracting, please wait",
"extractor.common.extracting.queue": "Queuing for extraction, currently ranked at {id}",
"extractor.common.loading": "Loading",
"extractor.common.pdf.demo1": "Example1.pdf",
"extractor.common.pdf.demo2": "Example2.pdf",
"extractor.common.formula.detect.demo1": "FormulaDetail1.jpg",
"extractor.common.formula.extract.demo1": "FormulaExtract1.jpg",
"extractor.common.login.desc": "Functionality available after login",
"extractor.markdown.preview": "Preview",
"extractor.markdown.code": "Code",
"extractor.home.title": "Welcome to Miner U",
"extractor.home.subTitle": "Upload documents and intelligently extract them into Markdown format",
"extractor.side.extractTask": "Extraction Tasks",
"extractor.side.extractTask.title": "Please upload a PDF document within 5M (within 10 pages) or a JPG/PNG image",
"extractor.pdf.title": "PDF Document Extraction",
"extractor.pdf.subTitle": "Supports text/scanned PDF parsing, identifies various layout elements and converts them into multimodal Markdown format",
"extractor.common.pdf.upload.tip": "Please upload a PDF document",
"extractor.pdf.ocr": "OCR Identify Pattern",
"extractor.pdf.ocr.popover": "By default, PDF types (text-based, scanned) will be automatically recognized, and based on the recognition results, you can choose to use text recognition or OCR recognition. If enabled, all types of PDF will be recognized by OCR.",
"extractor.formula.title2": "Recognize mathematical formulas in images as LaTex format, support multi-line formulas and handwritten formula recognition",
"extractor.formula.title": "Locate the formulas within and between rows in the image and generate bounding boxes",
"extractor.formula.upload.text": "Click to upload an image",
"extractor.formula.popover.extract": "In order to obtain the best formula recognition effect, please crop the image, focus on the formula part, and upload a clear, watermark-free image of the mathematical formula, as shown below",
"extractor.formula.popover.detect": "In order to get the best formula recognition effect, please upload clear, non-watermarked images containing mathematical formulas, as shown below",
"extractor.formula.upload.accept": "Please upload a JPG/PNG image within 5M",
"extractor.formula.upload.try": "Please upload an image containing a mathematical formula Example: ",
"extractor.guide.title": "Welcome to use more open source products 🎉",
"extractor.queue": "Extract records",
"extractor.queue.delete": "Confirm to delete this file?",
"extractor.queue.extracting": "Extracting",
"extractor.feedback.title1": "Are you satisfied with the overall extraction performance?",
"extractor.feedback.title3": "Look forward to your suggestions to help us better optimize",
"extractor.feedback.up.title": "What improvements are you expecting to see?",
"extractor.feedback.down.title": "What is the reason for your dissatisfaction? ",
"extractor.feedback.up": "Satisfied",
"extractor.feedback.down": "Dissatisfied",
"extractor.feedback.input.placeholder": "Please enter your suggestions for improvement",
"extractor.feedback.input.submit": "Submit",
"extractor.feedback.success": "Thank you for the feedback",
"extractor.queue.delete.success": "Delete successfully"
}
\ No newline at end of file
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment