diff --git a/core/Document/ProcessedText.go b/core/Document/ProcessedText.go new file mode 100644 index 0000000..0ae9f62 --- /dev/null +++ b/core/Document/ProcessedText.go @@ -0,0 +1,63 @@ +package document + +type ProcessedBoundingBox struct { + X0 int32 + Y0 int32 + X1 int32 + Y1 int32 +} + +type ProcessedSymbol struct { + Text string + Confidence float32 + BoundingBox ProcessedBoundingBox +} + +type ProcessedWord struct { + FullText string + Symbols []ProcessedSymbol + Confidence float32 + Direction string + BoundingBox ProcessedBoundingBox +} + +type ProcessedLine struct { + FullText string + Words []ProcessedWord +} + +type ProcessedArea struct { + Id string + DocumentId string + FullText string + Lines []ProcessedLine +} + +type ProcessedAreaCollection struct { + Areas []ProcessedArea +} + +var processedAreaCollectionInstnace *ProcessedAreaCollection + +func GetProcessedAreaCollection() *ProcessedAreaCollection { + if processedAreaCollectionInstnace == nil { + processedAreaCollectionInstnace = &ProcessedAreaCollection{} + } + return processedAreaCollectionInstnace +} + +func (collection *ProcessedAreaCollection) AddProcessedArea(area ProcessedArea) { + collection.Areas = append(collection.Areas, area) +} + +func (collection *ProcessedAreaCollection) GetAreasByDocumentId(id string) []*ProcessedArea { + var foundAreas []*ProcessedArea + + for index, a := range collection.Areas { + if a.DocumentId == id { + foundAreas = append(foundAreas, &collection.Areas[index]) + } + } + + return foundAreas +} diff --git a/frontend/components/workspace/DocumentRenderer.tsx b/frontend/components/workspace/DocumentRenderer.tsx index bf9e3dc..35cf211 100644 --- a/frontend/components/workspace/DocumentRenderer.tsx +++ b/frontend/components/workspace/DocumentRenderer.tsx @@ -3,7 +3,7 @@ import React, { useEffect, useRef } from 'react' import { useProject } from '../../context/Project/provider' import loadImage from '../../useCases/loadImage' -import processImageData from '../../useCases/processImageData' +import processImageArea from '../../useCases/processImageArea' const DocumentRenderer = () => { const { getSelectedDocument, requestAddArea } = useProject() @@ -111,10 +111,8 @@ const DocumentRenderer = () => { } if (selectedDocument?.id) { - await requestAddArea(selectedDocument.id, { startX, startY, endX, endY }) - processImageData(selectedDocument.id).then(results => { - console.log(results) - }).catch(err => console.log(err)) + const addedArea = await requestAddArea(selectedDocument.id, { startX, startY, endX, endY }) + processImageArea(selectedDocument.id, addedArea) } const context = drawingCanvasInstance.getContext('2d') diff --git a/frontend/components/workspace/Main.tsx b/frontend/components/workspace/Main.tsx index a33e899..5ba8e2e 100644 --- a/frontend/components/workspace/Main.tsx +++ b/frontend/components/workspace/Main.tsx @@ -1,12 +1,20 @@ 'use client' +import { useNavigation } from '../../context/Navigation/provider' +import { workspaces } from '../../context/Navigation/types' import { useProject } from '../../context/Project/provider' import DocumentRenderer from './DocumentRenderer' import NoSelectedDocument from './NoSelectedDocument' +import TextEditor from './TextEditor' const MainWorkspace = () => { const { getSelectedDocument, selectedDocumentId } = useProject() - + const { selectedWorkspace } = useNavigation() + +const renderSelectedWorkSpace = () => { + if (selectedWorkspace === workspaces.TEXTEDITOR) return + else return !selectedDocumentId ? : +} return
@@ -18,10 +26,7 @@ const MainWorkspace = () => { {getSelectedDocument()?.name || 'Image Processor'}
- {!selectedDocumentId - ? - : - } + { renderSelectedWorkSpace() } diff --git a/frontend/components/workspace/Sidebar.tsx b/frontend/components/workspace/Sidebar.tsx index 9ef6144..a921f90 100644 --- a/frontend/components/workspace/Sidebar.tsx +++ b/frontend/components/workspace/Sidebar.tsx @@ -117,8 +117,6 @@ function Sidebar() { const onAreaDoubleclick = (areaId: string) => { const documentIdOfArea = getDocumentIdFromAreaId(areaId) setIsEditAreaNameInputShowing(true) - // const groupIdOfArea = getGroupIdFromAreaId(areaId) - console.log(documentIdOfArea, selectedDocumentId) console.log('double click') } @@ -142,7 +140,6 @@ function Sidebar() { } const onConfirmAreaNameChangeHandler = async (areaDetails: { areaId: string, areaName: string }) => { - console.log(areaDetails) const { areaId, areaName } = areaDetails const areaToUpdate = getAreaById(areaId) diff --git a/frontend/components/workspace/TextEditor.tsx b/frontend/components/workspace/TextEditor.tsx new file mode 100644 index 0000000..9449dff --- /dev/null +++ b/frontend/components/workspace/TextEditor.tsx @@ -0,0 +1,50 @@ +import Editor, { DiffEditor, useMonaco } from '@monaco-editor/react' +import { useEffect, useState } from 'react' +import { useProject } from '../../context/Project/provider' + +const TextEditor = () => { + const monaco = useMonaco() + const { selectedDocumentId, getProcessedAreasByDocumentId } = useProject() + const [editorHeight, setEditorHeight] = useState(window.innerHeight - 200) + const [editorValue, setEditorValue] = useState('') + + // useEffect(() => { + // if (monaco) { + // console.log("here is the monaco instance:", monaco); + // } + // }, [monaco]) + + useEffect(() => { + if (!selectedDocumentId) { + setEditorValue('# No Document Selected') + return + } + + getProcessedAreasByDocumentId(selectedDocumentId) + .then(response => { + if (!response || response.length === 0) return + + const fullTextOfAreas = response + .map(area => area.fullText + '\n') + .join('\n') + + setEditorValue(fullTextOfAreas) + }) + .catch(console.error) + + }, [selectedDocumentId]) + + window.addEventListener('resize', () => { + setEditorHeight(window.innerHeight - 200) + }) + + return
+ +
+} + +export default TextEditor diff --git a/frontend/components/workspace/ToolTabs.tsx b/frontend/components/workspace/ToolTabs.tsx index 22ec26a..79a25c8 100644 --- a/frontend/components/workspace/ToolTabs.tsx +++ b/frontend/components/workspace/ToolTabs.tsx @@ -1,10 +1,11 @@ -import { useState } from 'react' +import { useNavigation } from '../../context/Navigation/provider' +import { workspaces } from '../../context/Navigation/types' const tabs = [ - { name: 'Processor', id: 0 }, - { name: 'Text Editor', id: 1 }, - { name: 'Translator', id: 2 }, - { name: 'Details', id: 3 }, + { displayName: 'Processor', type: workspaces.PROCESSOR }, + { displayName: 'Text Editor', type: workspaces.TEXTEDITOR }, + { displayName: 'Translator', type: workspaces.TRANSLATOR }, + { displayName: 'Details', type: workspaces.DETAILS }, ] function classNames(...classes: string[]) { @@ -12,9 +13,9 @@ function classNames(...classes: string[]) { } export default function ToolTabs() { - const [selectedTabId, setSelectedTabId] = useState(0) + const { selectedWorkspace, setSelectedWorkspace } = useNavigation() - const getIsSelectedTab = (tabId: number) => tabId === selectedTabId + const getIsSelectedTab = (type: workspaces) => type === selectedWorkspace return (
@@ -23,17 +24,17 @@ export default function ToolTabs() { diff --git a/frontend/context/Navigation/makeDefaultNavigation.ts b/frontend/context/Navigation/makeDefaultNavigation.ts new file mode 100644 index 0000000..fa9e663 --- /dev/null +++ b/frontend/context/Navigation/makeDefaultNavigation.ts @@ -0,0 +1,8 @@ +import { NavigationContextType, workspaces } from './types' + +const makeDefaultNavigation = (): NavigationContextType => ({ + selectedWorkspace: workspaces.PROCESSOR, + setSelectedWorkspace: (workspace) => {} +}) + +export default makeDefaultNavigation diff --git a/frontend/context/Navigation/provider.tsx b/frontend/context/Navigation/provider.tsx new file mode 100644 index 0000000..52eb033 --- /dev/null +++ b/frontend/context/Navigation/provider.tsx @@ -0,0 +1,25 @@ +'use client' + +import { createContext, ReactNode, useContext, useState } from 'react' +import makeDefaultNavigation from './makeDefaultNavigation' +import { NavigationContextType, NavigationProps, workspaces } from './types' + +const NavigationContext = createContext(makeDefaultNavigation()) + +export function useNavigation() { + return useContext(NavigationContext) +} + +type Props = { children: ReactNode, navigationProps: NavigationProps } +export function NavigationProvidor({ children, navigationProps }: Props) { + const [selectedWorkspace, setSelectedWorkspace] = useState(navigationProps.selectedWorkspace) + + const value = { + selectedWorkspace, + setSelectedWorkspace + } + + return + { children } + +} \ No newline at end of file diff --git a/frontend/context/Navigation/types.ts b/frontend/context/Navigation/types.ts new file mode 100644 index 0000000..2e8d9cf --- /dev/null +++ b/frontend/context/Navigation/types.ts @@ -0,0 +1,17 @@ +enum workspaces { + PROCESSOR = 'PROCESSOR', + TEXTEDITOR = 'TEXTEDITOR', + TRANSLATOR = 'TRANSLATOR', + DETAILS = 'DETAILS', +} + +export { workspaces } + +export type NavigationContextType = { + selectedWorkspace: workspaces, + setSelectedWorkspace: (workspace: workspaces) => void +} + +export type NavigationProps = { + selectedWorkspace: workspaces +} diff --git a/frontend/context/Project/makeDefaultProject.ts b/frontend/context/Project/makeDefaultProject.ts index 192788b..a9385ac 100644 --- a/frontend/context/Project/makeDefaultProject.ts +++ b/frontend/context/Project/makeDefaultProject.ts @@ -9,6 +9,8 @@ const makeDefaultProject = (): ProjectContextType => ({ selectedDocumentId: '', getSelectedDocument: () => new ipc.Document(), getAreaById: (areaId) => undefined, + getProcessedAreasByDocumentId: (documentId) => Promise.resolve([new ipc.ProcessedArea()]), + requestAddProcessedArea: (processesArea) => Promise.resolve(new ipc.ProcessedArea()), requestAddArea: (documentId, area) => Promise.resolve(new ipc.Area()), requestUpdateArea: (updatedArea) => Promise.resolve(new ipc.Area()), requestAddDocument: (groupId, documentName) => Promise.resolve(new ipc.Document()), diff --git a/frontend/context/Project/provider.tsx b/frontend/context/Project/provider.tsx index 8035603..d6007e5 100644 --- a/frontend/context/Project/provider.tsx +++ b/frontend/context/Project/provider.tsx @@ -1,7 +1,7 @@ 'use client' import { createContext, ReactNode, useContext, useEffect, useState } from 'react' -import { GetDocuments, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup, RequestUpdateArea } from '../../wailsjs/wailsjs/go/ipc/Channel' +import { GetDocuments, GetProcessedAreasByDocumentId, RequestAddArea, RequestAddDocument, RequestAddDocumentGroup, RequestAddProcessedArea, RequestUpdateArea } from '../../wailsjs/wailsjs/go/ipc/Channel' import { ipc } from '../../wailsjs/wailsjs/go/models' import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps } from './types' import makeDefaultProject from './makeDefaultProject' @@ -41,7 +41,6 @@ export function ProjectProvider({ children, projectProps }: Props) { const requestAddArea = async (documentId: string, area: AddAreaProps): Promise => { const response = await RequestAddArea(documentId, new ipc.Area(area)) - if (response.id) await updateDocuments() return response } @@ -59,6 +58,18 @@ export function ProjectProvider({ children, projectProps }: Props) { const getSelectedDocument = () => documents.find(d => d.id === selectedDocumentId) + const getProcessedAreasByDocumentId = async (documentId: string) => { + let response: ipc.ProcessedArea[] = [] + try { + response = await GetProcessedAreasByDocumentId(documentId) + } catch (err) { + console.log(err) + } + return response + } + + const requestAddProcessedArea = async (processedArea: ipc.ProcessedArea) => await RequestAddProcessedArea(processedArea) + useEffect(() => { if (!documents.length && !groups.length) updateDocuments() }, [documents.length, groups.length]) @@ -77,6 +88,8 @@ export function ProjectProvider({ children, projectProps }: Props) { setSelectedAreaId, selectedDocumentId, setSelectedDocumentId, + getProcessedAreasByDocumentId, + requestAddProcessedArea, } return diff --git a/frontend/context/Project/types.ts b/frontend/context/Project/types.ts index 788d72e..58b1286 100644 --- a/frontend/context/Project/types.ts +++ b/frontend/context/Project/types.ts @@ -19,6 +19,8 @@ export type AreaProps = { id: string } & AddAreaProps export type ProjectContextType = { getSelectedDocument: () => ipc.Document | undefined getAreaById: (areaId: string) => ipc.Area | undefined + getProcessedAreasByDocumentId: (documentId: string) => Promise + requestAddProcessedArea: (processedArea: ipc.ProcessedArea) => Promise requestAddArea: (documentId: string, area: AddAreaProps) => Promise requestUpdateArea: (area: AreaProps) => Promise requestAddDocument: (groupId: string, documentName: string) => Promise diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a6a79f6..1e73f4b 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -10,7 +10,9 @@ "dependencies": { "@headlessui/react": "^1.7.4", "@heroicons/react": "^2.0.13", + "@monaco-editor/react": "^4.4.6", "@tailwindcss/forms": "^0.5.3", + "monaco-editor": "^0.34.1", "next": "^13.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", @@ -399,6 +401,31 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "node_modules/@monaco-editor/loader": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.2.tgz", + "integrity": "sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g==", + "dependencies": { + "state-local": "^1.0.6" + }, + "peerDependencies": { + "monaco-editor": ">= 0.21.0 < 1" + } + }, + "node_modules/@monaco-editor/react": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.4.6.tgz", + "integrity": "sha512-Gr3uz3LYf33wlFE3eRnta4RxP5FSNxiIV9ENn2D2/rN8KgGAD8ecvcITRtsbbyuOuNkwbuHYxfeaz2Vr+CtyFA==", + "dependencies": { + "@monaco-editor/loader": "^1.3.2", + "prop-types": "^15.7.2" + }, + "peerDependencies": { + "monaco-editor": ">= 0.25.0 < 1", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/@next/env": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.1.tgz", @@ -2923,6 +2950,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -3064,7 +3096,6 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "engines": { "node": ">=0.10.0" } @@ -3456,7 +3487,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -3527,8 +3557,7 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "node_modules/read-cache": { "version": "1.0.0", @@ -3756,6 +3785,11 @@ "node": ">=0.10.0" } }, + "node_modules/state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, "node_modules/string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", @@ -4519,6 +4553,23 @@ "@jridgewell/sourcemap-codec": "1.4.14" } }, + "@monaco-editor/loader": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@monaco-editor/loader/-/loader-1.3.2.tgz", + "integrity": "sha512-BTDbpHl3e47r3AAtpfVFTlAi7WXv4UQ/xZmz8atKl4q7epQV5e7+JbigFDViWF71VBi4IIBdcWP57Hj+OWuc9g==", + "requires": { + "state-local": "^1.0.6" + } + }, + "@monaco-editor/react": { + "version": "4.4.6", + "resolved": "https://registry.npmjs.org/@monaco-editor/react/-/react-4.4.6.tgz", + "integrity": "sha512-Gr3uz3LYf33wlFE3eRnta4RxP5FSNxiIV9ENn2D2/rN8KgGAD8ecvcITRtsbbyuOuNkwbuHYxfeaz2Vr+CtyFA==", + "requires": { + "@monaco-editor/loader": "^1.3.2", + "prop-types": "^15.7.2" + } + }, "@next/env": { "version": "13.1.1", "resolved": "https://registry.npmjs.org/@next/env/-/env-13.1.1.tgz", @@ -6270,6 +6321,11 @@ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==" }, + "monaco-editor": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.34.1.tgz", + "integrity": "sha512-FKc80TyiMaruhJKKPz5SpJPIjL+dflGvz4CpuThaPMc94AyN7SeC9HQ8hrvaxX7EyHdJcUY5i4D0gNyJj1vSZQ==" + }, "ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -6350,8 +6406,7 @@ "object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" }, "object-hash": { "version": "3.0.0", @@ -6595,7 +6650,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "requires": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -6637,8 +6691,7 @@ "react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, "read-cache": { "version": "1.0.0", @@ -6793,6 +6846,11 @@ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" }, + "state-local": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/state-local/-/state-local-1.0.7.tgz", + "integrity": "sha512-HTEHMNieakEnoe33shBYcZ7NX83ACUjCu8c40iOGEZsngj9zRnkqS9j1pqQPXwobB0ZcVTk27REb7COQ0UR59w==" + }, "string.prototype.matchall": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.8.tgz", diff --git a/frontend/package.json b/frontend/package.json index 3093ef6..cf62339 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -15,7 +15,9 @@ "dependencies": { "@headlessui/react": "^1.7.4", "@heroicons/react": "^2.0.13", + "@monaco-editor/react": "^4.4.6", "@tailwindcss/forms": "^0.5.3", + "monaco-editor": "^0.34.1", "next": "^13.1.1", "react": "^18.2.0", "react-dom": "^18.2.0", diff --git a/frontend/package.json.md5 b/frontend/package.json.md5 index 1b256c9..00578c3 100755 --- a/frontend/package.json.md5 +++ b/frontend/package.json.md5 @@ -1 +1 @@ -181c6c1e8f906e05414ee1ebe8117fcb \ No newline at end of file +6ae330b5487329c78105faea78224dd4 \ No newline at end of file diff --git a/frontend/pages/_app.tsx b/frontend/pages/_app.tsx index 15e032f..678294a 100644 --- a/frontend/pages/_app.tsx +++ b/frontend/pages/_app.tsx @@ -5,6 +5,8 @@ import { ProjectProvider } from '../context/Project/provider' import '../styles/globals.css' import { ipc } from '../wailsjs/wailsjs/go/models' import '../styles/globals.css' +import { NavigationProvidor } from '../context/Navigation/provider' +import { workspaces } from '../context/Navigation/types' const initialProjectProps = { id: '', @@ -12,10 +14,16 @@ const initialProjectProps = { groups: [] as ipc.Group[] } +const initialNavigationProps = { + selectedWorkspace: workspaces.PROCESSOR +} + export default function MainAppLayout({ Component, pageProps }: AppProps) { return
- - - + + + + +
} diff --git a/frontend/pages/index.tsx b/frontend/pages/index.tsx index cc6dc57..4446638 100644 --- a/frontend/pages/index.tsx +++ b/frontend/pages/index.tsx @@ -1,14 +1,22 @@ import { NextPage } from 'next' +import { useState } from 'react' import MainHead from '../components/head' import MainWorkspace from '../components/workspace/Main' import Navigation from '../components/workspace/Navigation' +enum workspaces { + PROCESSOR = 'PROCESSOR', + TEXTEDITOR = 'TEXTEDITOR' +} + const Home: NextPage = () => { + const [ selectedWorkSpace, setSelectedWorkSpace ] = useState(workspaces.PROCESSOR) + return ( <> - + ) } diff --git a/frontend/useCases/processImageArea.ts b/frontend/useCases/processImageArea.ts new file mode 100644 index 0000000..dc5f0b3 --- /dev/null +++ b/frontend/useCases/processImageArea.ts @@ -0,0 +1,61 @@ +import { createScheduler, createWorker } from 'tesseract.js' +import { GetDocumentById, RequestAddProcessedArea } from '../wailsjs/wailsjs/go/ipc/Channel' +import { ipc } from '../wailsjs/wailsjs/go/models' +import loadImage from './loadImage' + +const processImageArea = async (documentId: string, area: ipc.Area) => { + const foundDocument = await GetDocumentById(documentId) + if (!foundDocument.path || !foundDocument.areas?.length) return + + const { path } = foundDocument + const imageData = await loadImage(path) + + const scheduler = createScheduler() + const worker = await createWorker() + await worker.loadLanguage('eng') // TODO: change this when multilangiage system is implementd + await worker.initialize('eng') // TODO: same here + scheduler.addWorker(worker) + + const result = await scheduler.addJob('recognize', imageData, { + rectangle: { + left: area.startX, + top: area.startY, + width: area.endX - area.startX, + height: area.endY - area.startY, + } + }) + + const addProcessesAreaRequest = await RequestAddProcessedArea(new ipc.ProcessedArea({ + id: area.id, + documentId, + fullText: result.data.text, + lines: result.data.lines.map((l: any) => new ipc.ProcessedLine({ + fullText: l.text, + words: l.words.map((w: any) => new ipc.ProcessedWord({ + fullText: w.text, + direction: w.direction, + confidence: w.confidence, + boundingBox: new ipc.ProcessedBoundingBox({ + x0: w.bbox.x0, + y0: w.bbox.y0, + x1: w.bbox.x1, + y1: w.bbox.y1, + }), + symbols: w.symbols.map((s: any) => new ipc.ProcessedSymbol({ + fullText: s.text, + confidence: s.confidence, + boundingBox: new ipc.ProcessedBoundingBox({ + x0: s.bbox.x0, + y0: s.bbox.y0, + x1: s.bbox.x1, + y1: s.bbox.y1, + }) + })) + })) + })) + })) + + return addProcessesAreaRequest +} + +export default processImageArea diff --git a/frontend/useCases/processImageData.ts b/frontend/useCases/processImageData.ts index 38d487c..d2f7d59 100644 --- a/frontend/useCases/processImageData.ts +++ b/frontend/useCases/processImageData.ts @@ -17,16 +17,11 @@ const processImageData = async (documentId: string) => { if (!foundDocument.path || !foundDocument.areas?.length) return const { areas, path } = foundDocument - - console.log(`about to load: ${path}`) - const imageData = await loadImage(path) const scheduler = createScheduler() - const workerCount = getImageWorkerCount(areas.length) for (let index = 0; index < workerCount; index++) { - console.log('add worker stuff') const worker = await createWorker() await worker.loadLanguage('eng') // TODO: change this when multilangiage system is implementd await worker.initialize('eng') // TODO: same here @@ -34,7 +29,6 @@ const processImageData = async (documentId: string) => { } const results = await Promise.allSettled(areas.map(a => { - console.log('adding job') return scheduler.addJob('recognize', imageData, { rectangle: { left: a.startX, top: a.startY, diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts index 0e42200..018c908 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts @@ -6,10 +6,14 @@ export function GetDocumentById(arg1:string):Promise; export function GetDocuments():Promise; +export function GetProcessedAreasByDocumentId(arg1:string):Promise>; + export function RequestAddArea(arg1:string,arg2:ipc.Area):Promise; export function RequestAddDocument(arg1:string,arg2:string):Promise; export function RequestAddDocumentGroup(arg1:string):Promise; +export function RequestAddProcessedArea(arg1:ipc.ProcessedArea):Promise; + export function RequestUpdateArea(arg1:ipc.Area):Promise; diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.js b/frontend/wailsjs/wailsjs/go/ipc/Channel.js index 6b21989..121e0f0 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.js +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.js @@ -10,6 +10,10 @@ export function GetDocuments() { return window['go']['ipc']['Channel']['GetDocuments'](); } +export function GetProcessedAreasByDocumentId(arg1) { + return window['go']['ipc']['Channel']['GetProcessedAreasByDocumentId'](arg1); +} + export function RequestAddArea(arg1, arg2) { return window['go']['ipc']['Channel']['RequestAddArea'](arg1, arg2); } @@ -22,6 +26,10 @@ export function RequestAddDocumentGroup(arg1) { return window['go']['ipc']['Channel']['RequestAddDocumentGroup'](arg1); } +export function RequestAddProcessedArea(arg1) { + return window['go']['ipc']['Channel']['RequestAddProcessedArea'](arg1); +} + export function RequestUpdateArea(arg1) { return window['go']['ipc']['Channel']['RequestUpdateArea'](arg1); } diff --git a/frontend/wailsjs/wailsjs/go/models.ts b/frontend/wailsjs/wailsjs/go/models.ts index 2c474bc..16db623 100755 --- a/frontend/wailsjs/wailsjs/go/models.ts +++ b/frontend/wailsjs/wailsjs/go/models.ts @@ -112,6 +112,168 @@ export namespace ipc { return a; } } + + export class ProcessedBoundingBox { + x0: number; + y0: number; + x1: number; + y1: number; + + static createFrom(source: any = {}) { + return new ProcessedBoundingBox(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.x0 = source["x0"]; + this.y0 = source["y0"]; + this.x1 = source["x1"]; + this.y1 = source["y1"]; + } + } + export class ProcessedSymbol { + text: string; + confidence: number; + boundingBox: ProcessedBoundingBox; + + static createFrom(source: any = {}) { + return new ProcessedSymbol(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.text = source["text"]; + this.confidence = source["confidence"]; + this.boundingBox = this.convertValues(source["boundingBox"], ProcessedBoundingBox); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class ProcessedWord { + fullText: string; + symbols: ProcessedSymbol[]; + confidence: number; + direction: string; + boundingBox: ProcessedBoundingBox; + + static createFrom(source: any = {}) { + return new ProcessedWord(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.fullText = source["fullText"]; + this.symbols = this.convertValues(source["symbols"], ProcessedSymbol); + this.confidence = source["confidence"]; + this.direction = source["direction"]; + this.boundingBox = this.convertValues(source["boundingBox"], ProcessedBoundingBox); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class ProcessedLine { + fullText: string; + words: ProcessedWord[]; + + static createFrom(source: any = {}) { + return new ProcessedLine(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.fullText = source["fullText"]; + this.words = this.convertValues(source["words"], ProcessedWord); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + export class ProcessedArea { + id: string; + documentId: string; + fullText: string; + lines: ProcessedLine[]; + + static createFrom(source: any = {}) { + return new ProcessedArea(source); + } + + constructor(source: any = {}) { + if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; + this.documentId = source["documentId"]; + this.fullText = source["fullText"]; + this.lines = this.convertValues(source["lines"], ProcessedLine); + } + + convertValues(a: any, classs: any, asMap: boolean = false): any { + if (!a) { + return a; + } + if (a.slice) { + return (a as any[]).map(elem => this.convertValues(elem, classs)); + } else if ("object" === typeof a) { + if (asMap) { + for (const key of Object.keys(a)) { + a[key] = new classs(a[key]); + } + return a; + } + return new classs(a); + } + return a; + } + } + + + } diff --git a/ipc/JsonEntities.go b/ipc/JsonEntities.go index cb8e3a5..c9daafc 100644 --- a/ipc/JsonEntities.go +++ b/ipc/JsonEntities.go @@ -35,3 +35,36 @@ type Area struct { EndX int `json:"endX"` EndY int `json:"endY"` } + +type ProcessedBoundingBox struct { + X0 int32 `json:"x0"` + Y0 int32 `json:"y0"` + X1 int32 `json:"x1"` + Y1 int32 `json:"y1"` +} + +type ProcessedSymbol struct { + Text string `json:"text"` + Confidence float32 `json:"confidence"` + BoundingBox ProcessedBoundingBox `json:"boundingBox"` +} + +type ProcessedWord struct { + FullText string `json:"fullText"` + Symbols []ProcessedSymbol `json:"symbols"` + Confidence float32 `json:"confidence"` + Direction string `json:"direction"` + BoundingBox ProcessedBoundingBox `json:"boundingBox"` +} + +type ProcessedLine struct { + FullText string `json:"fullText"` + Words []ProcessedWord `json:"words"` +} + +type ProcessedArea struct { + Id string `json:"id"` + DocumentId string `json:"documentId"` + FullText string `json:"fullText"` + Lines []ProcessedLine `json:"lines"` +} diff --git a/ipc/ProcessedDocument.go b/ipc/ProcessedDocument.go new file mode 100644 index 0000000..dbabc51 --- /dev/null +++ b/ipc/ProcessedDocument.go @@ -0,0 +1,159 @@ +package ipc + +import ( + document "textualize/core/Document" +) + +func serializeBoundingBox(bbox document.ProcessedBoundingBox) ProcessedBoundingBox { + return ProcessedBoundingBox{ + X0: bbox.X0, + Y0: bbox.Y0, + X1: bbox.X1, + Y1: bbox.Y1, + } +} + +func serializeSymbol(symbol document.ProcessedSymbol) ProcessedSymbol { + return ProcessedSymbol{ + Text: symbol.Text, + Confidence: symbol.Confidence, + BoundingBox: serializeBoundingBox(symbol.BoundingBox), + } +} + +func serialzeWord(word document.ProcessedWord) ProcessedWord { + var symbols []ProcessedSymbol + + for _, symbol := range word.Symbols { + symbols = append(symbols, serializeSymbol(symbol)) + } + + return ProcessedWord{ + FullText: word.FullText, + Symbols: symbols, + Confidence: word.Confidence, + Direction: word.Direction, + BoundingBox: serializeBoundingBox(word.BoundingBox), + } +} + +func serializeLine(line document.ProcessedLine) ProcessedLine { + var words []ProcessedWord + + for _, word := range line.Words { + words = append(words, serialzeWord((word))) + } + + return ProcessedLine{ + FullText: line.FullText, + Words: words, + } +} + +func serializeProcessedArea(area document.ProcessedArea) ProcessedArea { + var lines []ProcessedLine + + for _, line := range area.Lines { + lines = append(lines, serializeLine(line)) + } + + return ProcessedArea{ + Id: area.Id, + DocumentId: area.DocumentId, + FullText: area.FullText, + Lines: lines, + } +} + +func (c *Channel) GetProcessedAreasByDocumentId(id string) []ProcessedArea { + areas := document.GetProcessedAreaCollection().GetAreasByDocumentId(id) + + var response []ProcessedArea + + for _, a := range areas { + response = append(response, serializeProcessedArea(*a)) + } + + return response +} + +func deserializeBoundingBox(bbox ProcessedBoundingBox) document.ProcessedBoundingBox { + return document.ProcessedBoundingBox{ + X0: bbox.X0, + Y0: bbox.Y0, + X1: bbox.X1, + Y1: bbox.Y1, + } +} + +func deserializeSymbol(symbol ProcessedSymbol) document.ProcessedSymbol { + return document.ProcessedSymbol{ + Text: symbol.Text, + Confidence: symbol.Confidence, + BoundingBox: deserializeBoundingBox(symbol.BoundingBox), + } +} + +func deserialzeWord(word ProcessedWord) document.ProcessedWord { + var symbols []document.ProcessedSymbol + + for _, symbol := range word.Symbols { + symbols = append(symbols, deserializeSymbol(symbol)) + } + + return document.ProcessedWord{ + FullText: word.FullText, + Symbols: symbols, + Confidence: word.Confidence, + Direction: word.Direction, + BoundingBox: deserializeBoundingBox(word.BoundingBox), + } +} + +func deserializeLine(line ProcessedLine) document.ProcessedLine { + var words []document.ProcessedWord + + for _, word := range line.Words { + words = append(words, deserialzeWord((word))) + } + + return document.ProcessedLine{ + FullText: line.FullText, + Words: words, + } +} + +func deserializeProcessedArea(area ProcessedArea) document.ProcessedArea { + var lines []document.ProcessedLine + + for _, line := range area.Lines { + lines = append(lines, deserializeLine(line)) + } + + return document.ProcessedArea{ + Id: area.Id, + DocumentId: area.DocumentId, + FullText: area.FullText, + Lines: lines, + } +} + +func (c *Channel) RequestAddProcessedArea(area ProcessedArea) ProcessedArea { + var currentAreaIds []string + + for _, a := range document.GetProcessedAreaCollection().Areas { + currentAreaIds = append(currentAreaIds, a.Id) + } + + areaAlreadyExists := false + for _, areaId := range currentAreaIds { + if area.Id == areaId { + areaAlreadyExists = true + } + } + + if !areaAlreadyExists { + document.GetProcessedAreaCollection().AddProcessedArea((deserializeProcessedArea(area))) + } + return area +} diff --git a/main.go b/main.go index 5fa0d3f..a6bf926 100644 --- a/main.go +++ b/main.go @@ -32,10 +32,7 @@ func ClientFileLoader() *FileLoader { func (h *FileLoader) ServeHTTP(res http.ResponseWriter, req *http.Request) { var err error - // Make sure to prefix all local files with this in renderer - // requestedFilename := strings.TrimPrefix(req.URL.Path, "/textualizeFileAssets") requestedFilename := req.URL.Path - fmt.Println(requestedFilename) fileData, err := os.ReadFile(requestedFilename) if err != nil { fmt.Println("was eror: ")