feat: text editor & preview imporovments

This commit is contained in:
Joshua Shoemaker 2023-03-24 19:19:53 -05:00
parent c94d0ed78b
commit 01529b731c
12 changed files with 385 additions and 51 deletions

View File

@ -178,13 +178,12 @@ const DocumentRenderer = () => {
}) })
return <div className='relative'> return <div className='relative'>
<div className='flex justify-between mt-2'> <div className='flex justify-between align-top mb-2'>
<h1 className="text-2xl font-semibold text-gray-900"> <div className='flex align-top'>
{getSelectedDocument()?.name} <h1 className="text-xl font-semibold text-gray-900 inline-block mr-2">{getSelectedDocument()?.name}</h1>
</h1>
<div>
<LanguageSelect shouldUpdateDocument defaultLanguage={selectedDocument?.defaultLanguage} /> <LanguageSelect shouldUpdateDocument defaultLanguage={selectedDocument?.defaultLanguage} />
<div className='flex justify-evenly items-center mt-2 mb-0'> </div>
<div className='flex justify-evenly items-center'>
<MagnifyingGlassMinusIcon className='w-4 h-4' /> <MagnifyingGlassMinusIcon className='w-4 h-4' />
<input <input
id="zoomRange" type="range" min={zoomStep} max={maxZoomLevel} step={zoomStep} id="zoomRange" type="range" min={zoomStep} max={maxZoomLevel} step={zoomStep}
@ -193,8 +192,6 @@ const DocumentRenderer = () => {
/> />
<MagnifyingGlassPlusIcon className='w-4 h-4' /> <MagnifyingGlassPlusIcon className='w-4 h-4' />
</div> </div>
</div>
</div> </div>
<div <div
onWheelCapture={handleWheelEvent} onWheelCapture={handleWheelEvent}

View File

@ -41,10 +41,11 @@ const LanguageSelect = (props?: Props) => {
}, [selectedLanguage]) }, [selectedLanguage])
return <Combobox as="div" value={selectedLanguage} onChange={setSelectedLanguage}> return <Combobox as="div" value={selectedLanguage} onChange={setSelectedLanguage} className='inline-block'>
<div className="inline-block relative"> <div className="inline-block relative">
<Combobox.Input <Combobox.Input
className="w-full rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm" style={{'maxWidth': '240px', 'height': '30px'}}
className="rounded-md border border-gray-300 bg-white py-2 pl-3 pr-10 shadow-sm focus:border-indigo-500 focus:outline-none focus:ring-1 focus:ring-indigo-500 sm:text-sm"
onChange={(event) => setQuery(event.target.value)} onChange={(event) => setQuery(event.target.value)}
displayValue={(language: ipc.Language) => language?.displayName} displayValue={(language: ipc.Language) => language?.displayName}
placeholder='Document Language' placeholder='Document Language'

View File

@ -1,13 +1,13 @@
'use client' 'use client'
import React, { useRef, useState } from 'react' import React, { useRef, useState } from 'react'
import { XMarkIcon } from '@heroicons/react/20/solid'
import { useProject } from '../../../context/Project/provider' import { useProject } from '../../../context/Project/provider'
import classNames from '../../../utils/classNames' import classNames from '../../../utils/classNames'
import { ArrowPathIcon } from '@heroicons/react/24/outline' import { ArrowPathIcon, XMarkIcon } from '@heroicons/react/24/outline'
import { SidebarArea } from './types' import { SidebarArea } from './types'
import { useSidebar } from './provider' import { useSidebar } from './provider'
import onEnterHandler from '../../../utils/onEnterHandler' import onEnterHandler from '../../../utils/onEnterHandler'
import processImageArea from '../../../useCases/processImageArea'
const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: number }) => { const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: number }) => {
@ -76,6 +76,11 @@ const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: num
requestDeleteAreaById(areaId) requestDeleteAreaById(areaId)
} }
const handleReprocessAreaButtonClick = async () => {
const response = await processImageArea(props.documentId, props.area.id)
console.log(response)
}
return <li> return <li>
{selectedAreaId === props.area.id && isEditAreaNameInputShowing {selectedAreaId === props.area.id && isEditAreaNameInputShowing
? <input ? <input
@ -114,7 +119,7 @@ const AreaLineItem = (props: { area: SidebarArea, documentId: string, index: num
<ArrowPathIcon <ArrowPathIcon
className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5' className='w-6 h-5 mr-2 text-white hover:bg-white hover:text-gray-700 rounded-full p-0.5'
aria-hidden="true" aria-hidden="true"
onClick={() => console.log('refresh')} onClick={handleReprocessAreaButtonClick}
/> />
<XMarkIcon <XMarkIcon
className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5' className='w-6 h-5 mr-2 text-white hover:bg-red-400 hover:text-gray-100 rounded-full p-0.5'

View File

@ -1,4 +1,5 @@
import { loader, DiffEditor } from '@monaco-editor/react' import { loader, DiffEditor } from '@monaco-editor/react'
import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'
import { useEffect, useState } from 'react' import { useEffect, useState } from 'react'
import { useProject } from '../../context/Project/provider' import { useProject } from '../../context/Project/provider'
import type { DiffOnMount } from '@monaco-editor/react/' import type { DiffOnMount } from '@monaco-editor/react/'
@ -6,7 +7,7 @@ import TextEditorButtons from './TextEditorButtons'
import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions' import createDiffEditorInteractions from '../../useCases/createDiffEditorInteractions'
import TextPreview from './TextPreview' import TextPreview from './TextPreview'
import createDebounce from '../../utils/createDebounce' import createDebounce from '../../utils/createDebounce'
import LanguageSelect from './LanguageSelect' import { MagnifyingGlassMinusIcon, MagnifyingGlassPlusIcon } from '@heroicons/react/24/outline'
loader.config({ loader.config({
paths: { paths: {
@ -17,6 +18,11 @@ loader.config({
let editorInteractions: ReturnType<typeof createDiffEditorInteractions> let editorInteractions: ReturnType<typeof createDiffEditorInteractions>
const editorHeightOffset = 234 const editorHeightOffset = 234
const fontSizeStep = 1
const maxFontSize = 36
let editorRefernce: monaco.editor.IStandaloneDiffEditor | null
const TextEditor = () => { const TextEditor = () => {
const { getSelectedDocument, getProcessedAreasByDocumentId, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId } = useProject() const { getSelectedDocument, getProcessedAreasByDocumentId, requestUpdateDocumentUserMarkdown, getUserMarkdownByDocumentId } = useProject()
const [editorHeight, setEditorHeight] = useState(window.innerHeight - editorHeightOffset) const [editorHeight, setEditorHeight] = useState(window.innerHeight - editorHeightOffset)
@ -24,6 +30,7 @@ const TextEditor = () => {
const [isEditorReady, setIsEditorReady] = useState(false) const [isEditorReady, setIsEditorReady] = useState(false)
const [isPreviewOpen, setIsPreviewOpen] = useState(false) const [isPreviewOpen, setIsPreviewOpen] = useState(false)
const [modifiedEditorValue, setModifiedEditorValue] = useState('') const [modifiedEditorValue, setModifiedEditorValue] = useState('')
const [fontSize, setFontSize] = useState(16)
const selectedDocument = getSelectedDocument() const selectedDocument = getSelectedDocument()
const selectedDocumentId = selectedDocument?.id || '' const selectedDocumentId = selectedDocument?.id || ''
@ -53,6 +60,7 @@ const TextEditor = () => {
setModifiedEditorValue(modifiedMarkdown) setModifiedEditorValue(modifiedMarkdown)
})) }))
editorRefernce = editor
setIsEditorReady(true) setIsEditorReady(true)
} }
@ -76,12 +84,16 @@ const TextEditor = () => {
requestProcessedArea() requestProcessedArea()
}, [selectedDocumentId, getProcessedAreasByDocumentId]) }, [selectedDocumentId, getProcessedAreasByDocumentId])
useEffect(() => {
editorRefernce?.updateOptions({ fontSize })
}, [fontSize, isEditorReady])
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
setEditorHeight(window.innerHeight - editorHeightOffset) setEditorHeight(window.innerHeight - editorHeightOffset)
}) })
return <div className='m-0 p-0 relative'> return <div className='m-0 p-0 relative'>
<span className="flex z-0 rounded-md shadow-sm mb-2 mt-2 justify-between"> <span className="flex z-0 rounded-md shadow-sm mb-2 justify-between align-top">
{isEditorReady {isEditorReady
? <> ? <>
<TextEditorButtons <TextEditorButtons
@ -89,7 +101,18 @@ const TextEditor = () => {
togglePreview={() => setIsPreviewOpen(!isPreviewOpen)} togglePreview={() => setIsPreviewOpen(!isPreviewOpen)}
editorInteractions={editorInteractions} editorInteractions={editorInteractions}
/> />
<LanguageSelect shouldUpdateDocument defaultLanguage={selectedDocument?.defaultLanguage} /> <div>
<div className='flex justify-evenly items-center mt-2 mb-0'>
<MagnifyingGlassMinusIcon className='w-4 h-4' />
<input
id="zoomRange" type="range" min={fontSizeStep} max={maxFontSize} step={fontSizeStep}
value={fontSize} className="w-[calc(100%-50px)] h-2 bg-indigo-200 rounded-lg appearance-none cursor-pointer p-0"
onChange={(e) => { setFontSize(e.currentTarget.valueAsNumber) }}
/>
<MagnifyingGlassPlusIcon className='w-4 h-4' />
</div>
</div>
</> </>
: '' : ''
} }

View File

@ -1,5 +1,4 @@
import { ListBulletIcon, MinusIcon } from '@heroicons/react/20/solid' import { Bars3BottomRightIcon, MinusIcon, ListBulletIcon, ChatBubbleLeftEllipsisIcon, EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
import { EyeIcon, EyeSlashIcon } from '@heroicons/react/24/outline'
import createDiffEditorInteractions, { MarkdownOperator } from '../../useCases/createDiffEditorInteractions' import createDiffEditorInteractions, { MarkdownOperator } from '../../useCases/createDiffEditorInteractions'
import classNames from '../../utils/classNames' import classNames from '../../utils/classNames'
@ -124,6 +123,21 @@ const TextEditorButtons = (props: Props) => {
B B
</button> </button>
<button
type="button"
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.QUOTE)}
className={classNames(
'text-sm relative inline-flex items-center border',
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
'focus:ring-indigo-500 font-extrabold',
)}>
<span className="sr-only">Quote</span>
<ChatBubbleLeftEllipsisIcon className="h-5 w-5" aria-hidden="true" />
</button>
<button <button
type="button" type="button"
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BULLET)} onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.BULLET)}
@ -137,11 +151,26 @@ const TextEditorButtons = (props: Props) => {
<ListBulletIcon className="h-5 w-5" aria-hidden="true" /> <ListBulletIcon className="h-5 w-5" aria-hidden="true" />
</button> </button>
<button
type="button"
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.RIGHTALIGN)}
className={classNames(
'text-sm relative inline-flex items-center border',
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
'focus:ring-indigo-500 italic',
)}>
<span className="sr-only">Right Align</span>
<Bars3BottomRightIcon className="h-5 w-5" aria-hidden="true" />
</button>
<button <button
type="button" type="button"
onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.DIVIDER)} onClick={() => editorInteractions.insertMarkdownOperator(MarkdownOperator.DIVIDER)}
className={classNames( className={classNames(
'text-sm relative inline-flex items-center border', 'text-sm relative inline-flex items-center rounded-r-md border',
'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50', 'border-gray-300 bg-white px-2 py-0 text-gray-700 hover:bg-gray-50',
'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1', 'focus:z-10 focus:border-indigo-500 focus:outline-none focus:ring-1',
'focus:ring-indigo-500 italic', 'focus:ring-indigo-500 italic',
@ -150,7 +179,6 @@ const TextEditorButtons = (props: Props) => {
<MinusIcon className="h-5 w-5" aria-hidden="true" /> <MinusIcon className="h-5 w-5" aria-hidden="true" />
</button> </button>
</span> </span>
} }

View File

@ -1,12 +1,14 @@
import { ReactMarkdown } from 'react-markdown/lib/react-markdown' import { ReactMarkdown } from 'react-markdown/lib/react-markdown'
import rehypeRaw from 'rehype-raw'
type Props = { markdown: string, height: number } type Props = { markdown: string, height: number }
const TextPreview = (props: Props) => ( const TextPreview = (props: Props) => (
<div <div
className='absolute w-[calc(50%-14px)] top-[46px] bg-white overflow-y-scroll p-4 m-0' className='absolute w-[calc(50%-14px)] top-[38px] bg-white overflow-y-scroll p-4 m-0'
style={{ 'height': `${props.height}px` }}> style={{ 'height': `${props.height}px` }}>
<ReactMarkdown <ReactMarkdown
rehypePlugins={[rehypeRaw]}
components={{ components={{
h1: ({ node, ...props }) => <h1 {...props} className='font-black text-2xl' />, h1: ({ node, ...props }) => <h1 {...props} className='font-black text-2xl' />,
h2: ({ node, ...props }) => <h2 {...props} className='font-extrabold text-xl' />, h2: ({ node, ...props }) => <h2 {...props} className='font-extrabold text-xl' />,
@ -18,6 +20,7 @@ const TextPreview = (props: Props) => (
ol: ({ node, ...props }) => <ol {...props} className='list-decimal list-inside ml-2' />, ol: ({ node, ...props }) => <ol {...props} className='list-decimal list-inside ml-2' />,
em: ({ node, ...props }) => <em {...props} className='italic font-light' />, em: ({ node, ...props }) => <em {...props} className='italic font-light' />,
p: ({ node, ...props }) => <p {...props} className='text-base mb-2' />, p: ({ node, ...props }) => <p {...props} className='text-base mb-2' />,
blockquote: ({ node, ...props }) => <blockquote {...props} className='text-base mb-2 block p-3 bg-slate-50 italic' />,
}} }}
> >
{props.markdown} {props.markdown}

View File

@ -16,6 +16,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^8.0.5", "react-markdown": "^8.0.5",
"rehype-raw": "^6.1.1",
"tesseract.js": "^4.0.2", "tesseract.js": "^4.0.2",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },
@ -753,6 +754,11 @@
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"dev": true "dev": true
}, },
"node_modules/@types/parse5": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
"integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
},
"node_modules/@types/prop-types": { "node_modules/@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@ -2518,6 +2524,75 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/hast-util-from-parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
"integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==",
"dependencies": {
"@types/hast": "^2.0.0",
"@types/unist": "^2.0.0",
"hastscript": "^7.0.0",
"property-information": "^6.0.0",
"vfile": "^5.0.0",
"vfile-location": "^4.0.0",
"web-namespaces": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-parse-selector": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
"integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
"dependencies": {
"@types/hast": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-raw": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz",
"integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==",
"dependencies": {
"@types/hast": "^2.0.0",
"@types/parse5": "^6.0.0",
"hast-util-from-parse5": "^7.0.0",
"hast-util-to-parse5": "^7.0.0",
"html-void-elements": "^2.0.0",
"parse5": "^6.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0",
"vfile": "^5.0.0",
"web-namespaces": "^2.0.0",
"zwitch": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-to-parse5": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz",
"integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==",
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0",
"web-namespaces": "^2.0.0",
"zwitch": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/hast-util-whitespace": { "node_modules/hast-util-whitespace": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz",
@ -2527,6 +2602,31 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/hastscript": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
"integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
"dependencies": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-parse-selector": "^3.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/html-void-elements": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -3937,6 +4037,11 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
},
"node_modules/path-exists": { "node_modules/path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -4299,6 +4404,20 @@
"url": "https://github.com/sponsors/mysticatea" "url": "https://github.com/sponsors/mysticatea"
} }
}, },
"node_modules/rehype-raw": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz",
"integrity": "sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==",
"dependencies": {
"@types/hast": "^2.0.0",
"hast-util-raw": "^7.2.0",
"unified": "^10.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/remark-parse": { "node_modules/remark-parse": {
"version": "10.0.1", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz",
@ -5059,6 +5178,19 @@
"url": "https://opencollective.com/unified" "url": "https://opencollective.com/unified"
} }
}, },
"node_modules/vfile-location": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz",
"integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==",
"dependencies": {
"@types/unist": "^2.0.0",
"vfile": "^5.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/vfile-message": { "node_modules/vfile-message": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz",
@ -5077,6 +5209,15 @@
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.3.0.tgz", "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.3.0.tgz",
"integrity": "sha512-w9datO3OReMouWgKOelvu1CozmLK/VbkXOtlzNTanBJpR0uBHyUwS3EYdXf5vBPoHKYS0lpuYo91rpqMNIZM9g==" "integrity": "sha512-w9datO3OReMouWgKOelvu1CozmLK/VbkXOtlzNTanBJpR0uBHyUwS3EYdXf5vBPoHKYS0lpuYo91rpqMNIZM9g=="
}, },
"node_modules/web-namespaces": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
"integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
},
"node_modules/webidl-conversions": { "node_modules/webidl-conversions": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@ -5174,6 +5315,15 @@
"engines": { "engines": {
"node": "*" "node": "*"
} }
},
"node_modules/zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/wooorm"
}
} }
}, },
"dependencies": { "dependencies": {
@ -5657,6 +5807,11 @@
"integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==", "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==",
"dev": true "dev": true
}, },
"@types/parse5": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/@types/parse5/-/parse5-6.0.3.tgz",
"integrity": "sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g=="
},
"@types/prop-types": { "@types/prop-types": {
"version": "15.7.5", "version": "15.7.5",
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.5.tgz",
@ -6919,11 +7074,81 @@
"has-symbols": "^1.0.2" "has-symbols": "^1.0.2"
} }
}, },
"hast-util-from-parse5": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-7.1.2.tgz",
"integrity": "sha512-Nz7FfPBuljzsN3tCQ4kCBKqdNhQE2l0Tn+X1ubgKBPRoiDIu1mL08Cfw4k7q71+Duyaw7DXDN+VTAp4Vh3oCOw==",
"requires": {
"@types/hast": "^2.0.0",
"@types/unist": "^2.0.0",
"hastscript": "^7.0.0",
"property-information": "^6.0.0",
"vfile": "^5.0.0",
"vfile-location": "^4.0.0",
"web-namespaces": "^2.0.0"
}
},
"hast-util-parse-selector": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-3.1.1.tgz",
"integrity": "sha512-jdlwBjEexy1oGz0aJ2f4GKMaVKkA9jwjr4MjAAI22E5fM/TXVZHuS5OpONtdeIkRKqAaryQ2E9xNQxijoThSZA==",
"requires": {
"@types/hast": "^2.0.0"
}
},
"hast-util-raw": {
"version": "7.2.3",
"resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-7.2.3.tgz",
"integrity": "sha512-RujVQfVsOrxzPOPSzZFiwofMArbQke6DJjnFfceiEbFh7S05CbPt0cYN+A5YeD3pso0JQk6O1aHBnx9+Pm2uqg==",
"requires": {
"@types/hast": "^2.0.0",
"@types/parse5": "^6.0.0",
"hast-util-from-parse5": "^7.0.0",
"hast-util-to-parse5": "^7.0.0",
"html-void-elements": "^2.0.0",
"parse5": "^6.0.0",
"unist-util-position": "^4.0.0",
"unist-util-visit": "^4.0.0",
"vfile": "^5.0.0",
"web-namespaces": "^2.0.0",
"zwitch": "^2.0.0"
}
},
"hast-util-to-parse5": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-7.1.0.tgz",
"integrity": "sha512-YNRgAJkH2Jky5ySkIqFXTQiaqcAtJyVE+D5lkN6CdtOqrnkLfGYYrEcKuHOJZlp+MwjSwuD3fZuawI+sic/RBw==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0",
"web-namespaces": "^2.0.0",
"zwitch": "^2.0.0"
}
},
"hast-util-whitespace": { "hast-util-whitespace": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz", "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-2.0.1.tgz",
"integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng==" "integrity": "sha512-nAxA0v8+vXSBDt3AnRUNjyRIQ0rD+ntpbAp4LnPkumc5M9yUbSMa4XDU9Q6etY4f1Wp4bNgvc1yjiZtsTTrSng=="
}, },
"hastscript": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/hastscript/-/hastscript-7.2.0.tgz",
"integrity": "sha512-TtYPq24IldU8iKoJQqvZOuhi5CyCQRAbvDOX0x1eW6rsHSxa/1i2CCiptNTotGHJ3VoHRGmqiv6/D3q113ikkw==",
"requires": {
"@types/hast": "^2.0.0",
"comma-separated-tokens": "^2.0.0",
"hast-util-parse-selector": "^3.0.0",
"property-information": "^6.0.0",
"space-separated-tokens": "^2.0.0"
}
},
"html-void-elements": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-2.0.1.tgz",
"integrity": "sha512-0quDb7s97CfemeJAnW9wC0hw78MtW7NU3hqtCD75g2vFlDLt36llsYD7uB7SUzojLMP24N5IatXf7ylGXiGG9A=="
},
"iconv-lite": { "iconv-lite": {
"version": "0.6.3", "version": "0.6.3",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@ -7799,6 +8024,11 @@
"callsites": "^3.0.0" "callsites": "^3.0.0"
} }
}, },
"parse5": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz",
"integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw=="
},
"path-exists": { "path-exists": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@ -8022,6 +8252,16 @@
"resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz",
"integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg=="
}, },
"rehype-raw": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-6.1.1.tgz",
"integrity": "sha512-d6AKtisSRtDRX4aSPsJGTfnzrX2ZkHQLE5kiUuGOeEoLpbEulFF4hj0mLPbsa+7vmguDKOVVEQdHKDSwoaIDsQ==",
"requires": {
"@types/hast": "^2.0.0",
"hast-util-raw": "^7.2.0",
"unified": "^10.0.0"
}
},
"remark-parse": { "remark-parse": {
"version": "10.0.1", "version": "10.0.1",
"resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz", "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-10.0.1.tgz",
@ -8551,6 +8791,15 @@
"vfile-message": "^3.0.0" "vfile-message": "^3.0.0"
} }
}, },
"vfile-location": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-4.1.0.tgz",
"integrity": "sha512-YF23YMyASIIJXpktBa4vIGLJ5Gs88UB/XePgqPmTa7cDA+JeO3yclbpheQYCHjVHBn/yePzrXuygIL+xbvRYHw==",
"requires": {
"@types/unist": "^2.0.0",
"vfile": "^5.0.0"
}
},
"vfile-message": { "vfile-message": {
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz", "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-3.1.3.tgz",
@ -8565,6 +8814,11 @@
"resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.3.0.tgz", "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.3.0.tgz",
"integrity": "sha512-w9datO3OReMouWgKOelvu1CozmLK/VbkXOtlzNTanBJpR0uBHyUwS3EYdXf5vBPoHKYS0lpuYo91rpqMNIZM9g==" "integrity": "sha512-w9datO3OReMouWgKOelvu1CozmLK/VbkXOtlzNTanBJpR0uBHyUwS3EYdXf5vBPoHKYS0lpuYo91rpqMNIZM9g=="
}, },
"web-namespaces": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
"integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="
},
"webidl-conversions": { "webidl-conversions": {
"version": "3.0.1", "version": "3.0.1",
"resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz",
@ -8635,6 +8889,11 @@
"version": "0.3.1", "version": "0.3.1",
"resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz", "resolved": "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz",
"integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w==" "integrity": "sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w=="
},
"zwitch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
"integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="
} }
} }
} }

View File

@ -21,6 +21,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-markdown": "^8.0.5", "react-markdown": "^8.0.5",
"rehype-raw": "^6.1.1",
"tesseract.js": "^4.0.2", "tesseract.js": "^4.0.2",
"uuid": "^9.0.0" "uuid": "^9.0.0"
}, },

View File

@ -1 +1 @@
db04e8c6f18963bc94c89200059f2f38 e8bab26469d3f6b725d33cef9359048b

View File

@ -10,7 +10,9 @@ export enum MarkdownOperator {
ITALLICS = '_', ITALLICS = '_',
BOLD = '**', BOLD = '**',
BULLET = '* ', BULLET = '* ',
DIVIDER = '\n\n---\n' DIVIDER = '\n\n---\n',
QUOTE = '> ',
RIGHTALIGN = 'htmlRightAlign' // Not a real operator
} }
const wrapperOperators = [ const wrapperOperators = [
@ -18,6 +20,16 @@ const wrapperOperators = [
MarkdownOperator.BOLD MarkdownOperator.BOLD
] ]
const htmlWrappers = [
MarkdownOperator.RIGHTALIGN
]
const getHtmlWrappedText = (text: string, htmlWrapper: (typeof htmlWrappers)[number]) => {
if (htmlWrapper === MarkdownOperator.RIGHTALIGN) {
return `<span style="text-align:right">\n\n${text}\n\n</span>\n`
}
}
const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEditor) => { const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEditor) => {
const modifiedEditor = editor.getModifiedEditor() const modifiedEditor = editor.getModifiedEditor()
@ -51,14 +63,16 @@ const createDiffEditorInteractions = (editor: monaco.editor.IStandaloneDiffEdito
} }
newText = `${operator}` newText = `${operator}`
} else if (wrapperOperators.includes(operator)) { } else if (wrapperOperators.includes(operator) || htmlWrappers.includes(operator)) {
if (!doesSelectionHaveRange && wordAtStartPosition) range = { if (!doesSelectionHaveRange && wordAtStartPosition) range = {
startLineNumber, startLineNumber,
startColumn: wordAtStartPosition.startColumn, startColumn: wordAtStartPosition.startColumn,
endLineNumber, endLineNumber,
endColumn: wordAtStartPosition.endColumn endColumn: wordAtStartPosition.endColumn
} }
newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}${operator}` if (htmlWrappers.includes(operator)) {
newText = getHtmlWrappedText(modifiedEditor.getModel()?.getValueInRange(range) || '', operator) || ''
} else newText = `${operator}${modifiedEditor.getModel()?.getValueInRange(range)}${operator}`
} else { } else {
range = { range = {
startLineNumber, startLineNumber,

View File

@ -10,6 +10,8 @@ const processImageArea = async (documentId: string, areaId: string) => {
const processLanguage = foundDocument.defaultLanguage.processCode const processLanguage = foundDocument.defaultLanguage.processCode
if (!processLanguage) return console.error('No process language selected')
const { path } = foundDocument const { path } = foundDocument
const imageData = await loadImage(path) const imageData = await loadImage(path)
@ -31,6 +33,7 @@ const processImageArea = async (documentId: string, areaId: string) => {
const addProcessesAreaRequest = await RequestAddProcessedArea(new ipc.ProcessedArea({ const addProcessesAreaRequest = await RequestAddProcessedArea(new ipc.ProcessedArea({
id: foundArea.id, id: foundArea.id,
documentId, documentId,
order: foundArea.order,
fullText: result.data.text, fullText: result.data.text,
lines: result.data.lines.map((l: any) => new ipc.ProcessedLine({ lines: result.data.lines.map((l: any) => new ipc.ProcessedLine({
fullText: l.text, fullText: l.text,

View File

@ -139,32 +139,32 @@ func deserializeProcessedArea(area ProcessedArea) document.ProcessedArea {
return document.ProcessedArea{ return document.ProcessedArea{
Id: area.Id, Id: area.Id,
DocumentId: area.DocumentId, DocumentId: area.DocumentId,
Order: area.Order,
FullText: area.FullText, FullText: area.FullText,
Lines: lines, Lines: lines,
} }
} }
func (c *Channel) RequestAddProcessedArea(area ProcessedArea) ProcessedArea { func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) ProcessedArea {
var currentAreaIds []string doesAreaAlreadyExist := false
processedAreasOfDocument := document.GetProcessedAreaCollection().GetAreasByDocumentId(processedArea.DocumentId)
processedAreasCollection := document.GetProcessedAreaCollection() for _, a := range processedAreasOfDocument {
if a.Order == processedArea.Order {
for _, a := range processedAreasCollection.Areas { doesAreaAlreadyExist = true
currentAreaIds = append(currentAreaIds, a.Id) break
}
areaAlreadyExists := false
for _, areaId := range currentAreaIds {
if area.Id == areaId {
areaAlreadyExists = true
} }
} }
if !areaAlreadyExists { deserializedProcessedArea := deserializeProcessedArea(processedArea)
processedArea := deserializeProcessedArea(area)
currentAreasOfDocument := processedAreasCollection.GetAreasByDocumentId(area.DocumentId) if doesAreaAlreadyExist {
processedArea.Order = len(currentAreasOfDocument) storedProcessedArea := document.GetProcessedAreaCollection().GetAreaById(processedArea.Id)
document.GetProcessedAreaCollection().AddProcessedArea(processedArea) if storedProcessedArea.Id != "" {
storedProcessedArea = &deserializedProcessedArea
} }
return area } else {
document.GetProcessedAreaCollection().AddProcessedArea((deserializedProcessedArea))
}
return processedArea
} }