diff --git a/.vscode/settings.json b/.vscode/settings.json index 9c609c2..2884d00 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "headlessui", "heroicons", "konva", + "libretranslate", "Tesseract", "wailsjs" ] diff --git a/entities/ContextGroup.go b/entities/ContextGroup.go new file mode 100644 index 0000000..1526091 --- /dev/null +++ b/entities/ContextGroup.go @@ -0,0 +1,163 @@ +package entities + +import ( + "errors" + "fmt" + + "github.com/google/uuid" +) + +type IndependentTranslatedWord struct { + Id string + ProcessedWordId string + Value string +} + +type LinkedProcessedArea struct { + Area ProcessedArea + previous *LinkedProcessedArea + next *LinkedProcessedArea +} + +type LinkedAreaList struct { + head *LinkedProcessedArea + tail *LinkedProcessedArea +} + +func (l *LinkedAreaList) First() *LinkedProcessedArea { + return l.head +} + +func (linkedProcessedWord *LinkedProcessedArea) Next() *LinkedProcessedArea { + return linkedProcessedWord.next +} + +func (linkedProcessedWord *LinkedProcessedArea) Prev() *LinkedProcessedArea { + return linkedProcessedWord.previous +} + +// Create new node with value +func (l *LinkedAreaList) Push(processedArea ProcessedArea) *LinkedAreaList { + n := &LinkedProcessedArea{Area: processedArea} + if l.head == nil { + l.head = n // First node + } else { + l.tail.next = n // Add after prev last node + n.previous = l.tail // Link back to prev last node + } + l.tail = n // reset tail to newly added node + return l +} +func (l *LinkedAreaList) Find(id string) *LinkedProcessedArea { + found := false + var ret *LinkedProcessedArea = nil + for n := l.First(); n != nil && !found; n = n.Next() { + if n.Area.Id == id { + found = true + ret = n + } + } + return ret +} +func (l *LinkedAreaList) Delete(id string) bool { + success := false + node2del := l.Find(id) + if node2del != nil { + fmt.Println("Delete - FOUND: ", id) + prev_node := node2del.previous + next_node := node2del.next + // Remove this node + prev_node.next = node2del.next + next_node.previous = node2del.previous + success = true + } + return success +} + +var errEmpty = errors.New("ERROR - List is empty") + +// Pop last item from list +func (l *LinkedAreaList) Pop() (processedArea ProcessedArea, err error) { + if l.tail == nil { + err = errEmpty + } else { + processedArea = l.tail.Area + l.tail = l.tail.previous + if l.tail == nil { + l.head = nil + } + } + return processedArea, err +} + +type ContextGroup struct { // TODO: possibly remove this and expand the LinkedAreaList struct instead + Id string + DocumentId string + LinkedAreaList LinkedAreaList + TranslationText string +} + +type ContextGroupCollection struct { // TODO: these methods should live in core not entitites + Groups []ContextGroup +} + +var contextGroupCollectionInstance *ContextGroupCollection + +func GetContextGroupCollection() *ContextGroupCollection { + if contextGroupCollectionInstance == nil { + contextGroupCollectionInstance = &ContextGroupCollection{} + } + + return contextGroupCollectionInstance +} + +func SetContextGroupCollection(collection ContextGroupCollection) *ContextGroupCollection { + contextGroupCollectionInstance = &collection + return contextGroupCollectionInstance +} + +func (collection *ContextGroupCollection) FindContextGroupByNodeId(id string) *ContextGroup { + var foundContextGroup *ContextGroup + for i, g := range collection.Groups { + if g.LinkedAreaList.Find(id) != nil { + foundContextGroup = &collection.Groups[i] + break + } + } + + return foundContextGroup +} + +func (collection *ContextGroupCollection) CreateContextGroupFromProcessedArea(area ProcessedArea) bool { + fmt.Println("CreateContextGroupFromProcessedArea") + + newLinkedAreaList := LinkedAreaList{} + newLinkedAreaList.Push(area) + + newContextGroup := ContextGroup{ + Id: uuid.NewString(), + DocumentId: area.DocumentId, + LinkedAreaList: newLinkedAreaList, + } + + collection.Groups = append(collection.Groups, newContextGroup) + return true +} + +// TODO: completely rework this linked list and the collection +func (collection *ContextGroupCollection) ConnectAreaAsTailToNode(tailArea ProcessedArea, headArea ProcessedArea) bool { + headNodeContextGroup := collection.FindContextGroupByNodeId(headArea.Id) + + if headNodeContextGroup == nil { + collection.CreateContextGroupFromProcessedArea(headArea) + headNodeContextGroup = collection.FindContextGroupByNodeId(headArea.Id) + } + + headNode := headNodeContextGroup.LinkedAreaList.Find(headArea.Id) + headNode.next = &LinkedProcessedArea{ + Area: tailArea, + previous: headNode, + } + + return true +} diff --git a/entities/Document.go b/entities/Document.go index ef6c9fe..90bef9f 100644 --- a/entities/Document.go +++ b/entities/Document.go @@ -16,12 +16,13 @@ type Document struct { } type Area struct { - Id string `json:"id"` - Name string `json:"name"` - StartX int `json:"startX"` - StartY int `json:"startY"` - EndX int `json:"endX"` - EndY int `json:"endY"` - Language Language `json:"language"` - Order int `json:"order"` + Id string `json:"id"` + Name string `json:"name"` + StartX int `json:"startX"` + StartY int `json:"startY"` + EndX int `json:"endX"` + EndY int `json:"endY"` + Language Language `json:"language"` + TranslateLanguage Language `json:"translateLanguage"` + Order int `json:"order"` } diff --git a/entities/ProcessedText.go b/entities/ProcessedText.go index 961105b..7d59e5f 100644 --- a/entities/ProcessedText.go +++ b/entities/ProcessedText.go @@ -15,6 +15,7 @@ type ProcessedSymbol struct { type ProcessedWord struct { Id string `json:"id"` + AreaId string `json:"areaId"` FullText string `json:"fullText"` Symbols []ProcessedSymbol `json:"symbols"` Confidence float32 `json:"confidence"` diff --git a/frontend/components/DocumentCanvas/Area.tsx b/frontend/components/DocumentCanvas/Area.tsx index df532da..31a7e17 100644 --- a/frontend/components/DocumentCanvas/Area.tsx +++ b/frontend/components/DocumentCanvas/Area.tsx @@ -1,17 +1,17 @@ 'use client' import React, { useState } from 'react' +import Konva from 'konva' import { Group, Rect } from 'react-konva' +import { KonvaEventObject } from 'konva/lib/Node' import { entities } from '../../wailsjs/wailsjs/go/models' import { useProject } from '../../context/Project/provider' -import { KonvaEventObject } from 'konva/lib/Node' -import Konva from 'konva' import AreaContextMenu from './AreaContextMenu' +import { useStage } from './context/provider' type Props = { isActive: boolean, area: entities.Area, - scale: number, setHoveredOverAreaIds: Function setHoveredProcessedArea: Function } @@ -20,11 +20,12 @@ type coordinates = { x: number, y: number } const Area = (props: Props) => { const { getProcessedAreaById, selectedAreaId, setSelectedAreaId } = useProject() + const { scale } = useStage() const shapeRef = React.useRef(null) const [isAreaContextMenuOpen, setIsAreaContextMenuOpen] = useState(false) const [areaContextMenuPosition, setAreaContextMenuPosition] = useState() - const { area, scale, isActive, setHoveredOverAreaIds, setHoveredProcessedArea } = props + const { area, isActive, setHoveredOverAreaIds, setHoveredProcessedArea } = props const a = area const width = (a.endX - a.startX) const height = (a.endY - a.startY) @@ -79,15 +80,16 @@ const Area = (props: Props) => { onMouseLeave={handleEnterOrLeave} onClick={() => handleAreaClick(a.id)} onContextMenu={handleContextMenu} - isArea /> - {!isAreaContextMenuOpen - ? <> - : + {isAreaContextMenuOpen + ? + : <> } } diff --git a/frontend/components/DocumentCanvas/AreaContextMenu/index.tsx b/frontend/components/DocumentCanvas/AreaContextMenu/index.tsx index da53c98..1c5eee6 100644 --- a/frontend/components/DocumentCanvas/AreaContextMenu/index.tsx +++ b/frontend/components/DocumentCanvas/AreaContextMenu/index.tsx @@ -11,6 +11,7 @@ import processImageArea from '../../../useCases/processImageArea' import classNames from '../../../utils/classNames' import { useNotification } from '../../../context/Notification/provider' import LanguageSelect from '../../utils/LanguageSelect' +import { RequestTranslateArea } from '../../../wailsjs/wailsjs/go/ipc/Channel' type Props = { x: number, @@ -77,6 +78,19 @@ const AreaContextMenu = (props: Props) => { } } + + const handleTranslateArea = async () => { + setIsAreaContextMenuOpen(false) + + try { + const wasSuccessful = await RequestTranslateArea(area.id) + if (wasSuccessful) addNotificationToQueue({ message: 'Successfully translated area' }) + else addNotificationToQueue({ message: 'Issue translating area', level: 'warning' }) + } catch (err) { + addNotificationToQueue({ message: 'Error translating area', level: 'error' }) + } + } + const handleProcessLanguageSelect = async (selectedLanguage: entities.Language) => { setIsAreaContextMenuOpen(false) @@ -88,7 +102,6 @@ const AreaContextMenu = (props: Props) => { return } - const selectedDocumentId = getSelectedDocument()?.id if (!successfullyUpdatedLanguageOnArea || !selectedDocumentId) { addNotificationToQueue({ message: 'Did not successfully update area language', level: 'warning' }) @@ -114,7 +127,7 @@ const AreaContextMenu = (props: Props) => { return
@@ -136,6 +149,17 @@ const AreaContextMenu = (props: Props) => {
+} + +export default ToolToggleButton diff --git a/frontend/components/DocumentCanvas/ToolingOverlay/index.tsx b/frontend/components/DocumentCanvas/ToolingOverlay/index.tsx new file mode 100644 index 0000000..6921b11 --- /dev/null +++ b/frontend/components/DocumentCanvas/ToolingOverlay/index.tsx @@ -0,0 +1,68 @@ +'use client' + +import React, { useEffect, useState } from 'react' +import { DocumentTextIcon, LanguageIcon, LinkIcon, MagnifyingGlassMinusIcon, MagnifyingGlassPlusIcon, SquaresPlusIcon } from '@heroicons/react/24/outline' +import { useProject } from '../../../context/Project/provider' +import { entities } from '../../../wailsjs/wailsjs/go/models' +import LanguageSelect from '../../utils/LanguageSelect' +import { useStage } from '../context/provider' +import ToolToggleButton from './ToolToggleButton' + + +const ToolingOverlay = () => { + const { getSelectedDocument, selectedAreaId, } = useProject() + const { + scale, scaleStep, maxScale, setScale, + isLinkAreaContextsVisible, setIsLinkAreaContextsVisible, + isAreasVisible, setIsAreasVisible, + isProcessedWordsVisible, setIsProcessedWordsVisible, + isTranslatedWordsVisible, setIsTranslatedWordsVisible, + } = useStage() + + const selectedDocument = getSelectedDocument() + const [selectedArea, setSelectedArea] = useState() + + useEffect(() => { + setSelectedArea(selectedDocument?.areas.find(a => a.id == selectedAreaId)) + }, [selectedAreaId]) + + return <> + {/* Top buttons */} +
+
+

+ {selectedArea?.name + ? `${selectedDocument?.name} / ${selectedArea?.name}` + : selectedDocument?.name + } +

+ +
+
+ + { setScale(e.currentTarget.valueAsNumber) }} + /> + +
+
+ + {/* Right Buttons */} +
+ {isAreasVisible + ? <> + setIsLinkAreaContextsVisible(!isLinkAreaContextsVisible)} /> + setIsTranslatedWordsVisible(!isTranslatedWordsVisible)} /> + setIsProcessedWordsVisible(!isProcessedWordsVisible)} /> + + : <> + } + + setIsAreasVisible(!isAreasVisible)} /> +
+ +} + +export default ToolingOverlay diff --git a/frontend/components/DocumentCanvas/context/makeDefaultStage.ts b/frontend/components/DocumentCanvas/context/makeDefaultStage.ts new file mode 100644 index 0000000..197efb0 --- /dev/null +++ b/frontend/components/DocumentCanvas/context/makeDefaultStage.ts @@ -0,0 +1,24 @@ +import { StageContextType } from './types' + +const makeDefaultStage = (): StageContextType => ({ + scale: 1, + maxScale: 4, + scaleStep: 0.01, + setScale: (_) => {}, + isAreasVisible: true, + setIsAreasVisible: (_) => {}, + isProcessedWordsVisible: true, + setIsProcessedWordsVisible: (_) => {}, + isTranslatedWordsVisible: true, + setIsTranslatedWordsVisible: (_) => {}, + isLinkAreaContextsVisible: false, + setIsLinkAreaContextsVisible: (_) => {}, + size: { width: 1, height: 1 }, + setSize: (_) => {}, + isDrawingArea: false, + setIsDrawingArea: (_) => {}, + startingContextConnection: null, + setStartingContextConnection: (_) => {}, +}) + +export default makeDefaultStage diff --git a/frontend/components/DocumentCanvas/context/provider.tsx b/frontend/components/DocumentCanvas/context/provider.tsx new file mode 100644 index 0000000..5e8bb3d --- /dev/null +++ b/frontend/components/DocumentCanvas/context/provider.tsx @@ -0,0 +1,51 @@ +'use client' + +import { createContext, useContext, useState, ReactNode, } from 'react' +import makeDefaultStage from './makeDefaultStage' +import { StageContextType, StartingContextConnection } from './types' + +const StageContext = createContext(makeDefaultStage()) + +export function useStage() { + return useContext(StageContext) +} + +const maxScale = 4 +const scaleStep = 0.01 + +type Props = { children: ReactNode } +export function StageProvider({ children }: Props) { + const [size, setSize] = useState({width: 1, height: 1}) + const [scale, setScale] = useState(1) + const [isAreasVisible, setIsAreasVisible] = useState(true) + const [isProcessedWordsVisible, setIsProcessedWordsVisible] = useState(true) + const [isTranslatedWordsVisible, setIsTranslatedWordsVisible] = useState(true) + const [isLinkAreaContextsVisible, setIsLinkAreaContextsVisible] = useState(false) + const [isDrawingArea, setIsDrawingArea] = useState(false) + const [startingContextConnection, setStartingContextConnection] = useState(null) + + const value = { + scale, + maxScale, + scaleStep, + setScale, + isAreasVisible, + setIsAreasVisible, + isProcessedWordsVisible, + setIsProcessedWordsVisible, + isTranslatedWordsVisible, + setIsTranslatedWordsVisible, + isLinkAreaContextsVisible, + setIsLinkAreaContextsVisible, + size, + setSize, + isDrawingArea, + setIsDrawingArea, + startingContextConnection, + setStartingContextConnection, + } + + return + { children } + +} \ No newline at end of file diff --git a/frontend/components/DocumentCanvas/context/types.ts b/frontend/components/DocumentCanvas/context/types.ts new file mode 100644 index 0000000..5c7a8a6 --- /dev/null +++ b/frontend/components/DocumentCanvas/context/types.ts @@ -0,0 +1,25 @@ +export type StartingContextConnection = { + isHead: boolean, + areaId: string, +} + +export type StageContextType = { + scale: number, + maxScale: number, + scaleStep: number, + setScale: (value: number) => void, + isAreasVisible: boolean, + setIsAreasVisible: (value: boolean) => void, + isProcessedWordsVisible: boolean, + setIsProcessedWordsVisible: (value: boolean) => void, + isTranslatedWordsVisible: boolean, + setIsTranslatedWordsVisible: (value: boolean) => void, + isLinkAreaContextsVisible: boolean, + setIsLinkAreaContextsVisible: (value: boolean) => void, + size: { width: number, height: number } + setSize: (size: {width: number, height: number}) => void, + isDrawingArea: boolean, + setIsDrawingArea: (value: boolean) => void, + startingContextConnection: StartingContextConnection | null, + setStartingContextConnection: (value: StartingContextConnection | null) => void, +} \ No newline at end of file diff --git a/frontend/components/DocumentCanvas/index.tsx b/frontend/components/DocumentCanvas/index.tsx index 414ebca..8ee8ddf 100644 --- a/frontend/components/DocumentCanvas/index.tsx +++ b/frontend/components/DocumentCanvas/index.tsx @@ -1,29 +1,16 @@ 'use client' import dynamic from 'next/dynamic' -import React, { useEffect, useRef, useState } from 'react' -import { useProject, } from '../../context/Project/provider' -import { MagnifyingGlassMinusIcon, MagnifyingGlassPlusIcon } from '@heroicons/react/24/outline' -import LanguageSelect from '../utils/LanguageSelect' -import { entities } from '../../wailsjs/wailsjs/go/models' +import React, { useEffect, useRef } from 'react' +import ToolingOverlay from './ToolingOverlay' +import { useStage } from './context/provider' -const CanvasStage = dynamic(() => import('./CanvasStage'), { - ssr: false, -}) - -const zoomStep = 0.01 -const maxZoomLevel = 4 +const CanvasStage = dynamic(() => import('./CanvasStage'), { ssr: false }) const DocumentCanvas = () => { - const { getSelectedDocument, selectedAreaId, } = useProject() - const selectedDocument = getSelectedDocument() - const [ selectedArea, setSelectedArea ] = useState() - - const [zoomLevel, setZoomLevel] = useState(1) - const [size, setSize] = useState({ width: 0, height: 0 }) + const { setSize } = useStage() const thisRef = useRef(null) - const handleWindowResize = () => { const width = thisRef?.current?.clientWidth || 0 const height = thisRef?.current?.clientHeight || 0 @@ -36,33 +23,10 @@ const DocumentCanvas = () => { return () => window.removeEventListener('resize', handleWindowResize) }, [thisRef?.current?.clientWidth, thisRef?.current?.clientHeight]) - useEffect(() => { - setSelectedArea(selectedDocument?.areas.find(a => a.id == selectedAreaId)) - }, [selectedAreaId]) - return
- -
-
-

- {selectedArea?.name - ? `${selectedDocument?.name} / ${selectedArea?.name}` - : selectedDocument?.name - } -

- -
-
- - { setZoomLevel(e.currentTarget.valueAsNumber) }} - /> - -
-
+ +
} diff --git a/frontend/components/DocumentCanvas/types.ts b/frontend/components/DocumentCanvas/types.ts index cf61d59..c9ba010 100644 --- a/frontend/components/DocumentCanvas/types.ts +++ b/frontend/components/DocumentCanvas/types.ts @@ -6,6 +6,8 @@ export type RectangleCoordinates = { startX: number, startY: number, endX: number, endY: number } +export type Coordinates = { x: number, y: number } + export type AddAreaToStoreCallback = (startX: number, startY: number, endX: number, endY: number) => Promise export type SetZoomCallback = (newZoomLevel: number) => void diff --git a/frontend/components/workspace/Main.tsx b/frontend/components/workspace/Main.tsx index ac4654f..0b6598b 100644 --- a/frontend/components/workspace/Main.tsx +++ b/frontend/components/workspace/Main.tsx @@ -4,6 +4,7 @@ import { useNavigation } from '../../context/Navigation/provider' import { workspaces } from '../../context/Navigation/types' import { useProject } from '../../context/Project/provider' import DocumentCanvas from '../DocumentCanvas' +import { StageProvider } from '../DocumentCanvas/context/provider' import NoSelectedDocument from './NoSelectedDocument' import TextEditor from './TextEditor' @@ -11,10 +12,14 @@ const MainWorkspace = () => { const { getSelectedDocument, selectedDocumentId } = useProject() const { selectedWorkspace } = useNavigation() -const renderSelectedWorkSpace = () => { - if (selectedWorkspace === workspaces.TEXTEDITOR) return - else return !selectedDocumentId ? : -} + const renderSelectedWorkSpace = () => { + if (selectedWorkspace === workspaces.TEXTEDITOR) return + else return !selectedDocumentId + ? + : + + + } return
@@ -26,7 +31,7 @@ const renderSelectedWorkSpace = () => { Image Processor : ''}
- { renderSelectedWorkSpace() } + {renderSelectedWorkSpace()}
diff --git a/frontend/components/workspace/Sidebar/DocumentLineItem.tsx b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx index aa2890f..cfc3d57 100644 --- a/frontend/components/workspace/Sidebar/DocumentLineItem.tsx +++ b/frontend/components/workspace/Sidebar/DocumentLineItem.tsx @@ -135,7 +135,7 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i props.document.id === selectedDocumentId ? 'bg-gray-900 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', - 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 ' + 'text-left font-medium text-sm rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 inline-block' )} > {props.document.name} @@ -143,7 +143,7 @@ const DocumentLineItem = (props: { document: SidebarDocument, groupId: string, i } requestDeleteDocumentById(props.document.id)} />
    diff --git a/frontend/useCases/processImageArea.ts b/frontend/useCases/processImageArea.ts index 9caaef7..b4298e9 100644 --- a/frontend/useCases/processImageArea.ts +++ b/frontend/useCases/processImageArea.ts @@ -51,6 +51,7 @@ const processImageArea = async (documentId: string, areaId: string) => { lines: result.data.lines.map((l: any) => new entities.ProcessedLine({ fullText: l.text, words: l.words.map((w: any) => new entities.ProcessedWord({ + areaId: foundArea.id, fullText: w.text, direction: w.direction, confidence: w.confidence, diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts index 9532f79..86df433 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts @@ -43,6 +43,8 @@ export function RequestChangeSessionProjectByName(arg1:string):Promise; export function RequestChooseUserAvatar():Promise; +export function RequestConnectAreaAsTailToNode(arg1:string,arg2:string):Promise; + export function RequestDeleteAreaById(arg1:string):Promise; export function RequestDeleteDocumentAndChildren(arg1:string):Promise; @@ -57,6 +59,8 @@ export function RequestSaveLocalUserProcessedMarkdownCollection():Promise; +export function RequestTranslateArea(arg1:string):Promise; + export function RequestUpdateArea(arg1:entities.Area):Promise; export function RequestUpdateCurrentUser(arg1:entities.User):Promise; diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.js b/frontend/wailsjs/wailsjs/go/ipc/Channel.js index 7154b6b..0cff44e 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.js +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.js @@ -82,6 +82,10 @@ export function RequestChooseUserAvatar() { return window['go']['ipc']['Channel']['RequestChooseUserAvatar'](); } +export function RequestConnectAreaAsTailToNode(arg1, arg2) { + return window['go']['ipc']['Channel']['RequestConnectAreaAsTailToNode'](arg1, arg2); +} + export function RequestDeleteAreaById(arg1) { return window['go']['ipc']['Channel']['RequestDeleteAreaById'](arg1); } @@ -110,6 +114,10 @@ export function RequestSaveProcessedTextCollection() { return window['go']['ipc']['Channel']['RequestSaveProcessedTextCollection'](); } +export function RequestTranslateArea(arg1) { + return window['go']['ipc']['Channel']['RequestTranslateArea'](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 8230fb8..a70c55d 100755 --- a/frontend/wailsjs/wailsjs/go/models.ts +++ b/frontend/wailsjs/wailsjs/go/models.ts @@ -26,6 +26,7 @@ export namespace entities { endX: number; endY: number; language: Language; + translateLanguage: Language; order: number; static createFrom(source: any = {}) { @@ -41,6 +42,7 @@ export namespace entities { this.endX = source["endX"]; this.endY = source["endY"]; this.language = this.convertValues(source["language"], Language); + this.translateLanguage = this.convertValues(source["translateLanguage"], Language); this.order = source["order"]; } @@ -239,6 +241,7 @@ export namespace entities { } export class ProcessedWord { id: string; + areaId: string; fullText: string; symbols: ProcessedSymbol[]; confidence: number; @@ -252,6 +255,7 @@ export namespace entities { constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); this.id = source["id"]; + this.areaId = source["areaId"]; this.fullText = source["fullText"]; this.symbols = this.convertValues(source["symbols"], ProcessedSymbol); this.confidence = source["confidence"]; diff --git a/go.mod b/go.mod index 24596d4..1084105 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ go 1.18 require ( github.com/google/uuid v1.3.0 + github.com/snakesel/libretranslate v0.0.2 github.com/wailsapp/wails/v2 v2.5.1 ) diff --git a/go.sum b/go.sum index 34e34da..6b0b3f7 100644 --- a/go.sum +++ b/go.sum @@ -38,6 +38,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/samber/lo v1.36.0 h1:4LaOxH1mHnbDGhTVE0i1z8v/lWaQW8AIfOD3HU4mSaw= github.com/samber/lo v1.36.0/go.mod h1:HLeWcJRRyLKp3+/XBJvOrerCQn9mhdKMHyd7IRlgeQ8= +github.com/snakesel/libretranslate v0.0.2 h1:6LG/UMMpGtoj3NXvlzsxZgQEH0Qsi62jCDd5Yq5ALL8= +github.com/snakesel/libretranslate v0.0.2/go.mod h1:B8F8Dda8RlkHRMzs/aw8DWj9HfyHSXpaJTFD391hEUI= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= diff --git a/ipc/Channel.go b/ipc/Channel.go index c4b0b79..079cf93 100644 --- a/ipc/Channel.go +++ b/ipc/Channel.go @@ -1,5 +1,10 @@ package ipc +import ( + document "textualize/core/Document" + "textualize/translate" +) + type Channel struct{} var channelInstance *Channel @@ -11,3 +16,34 @@ func GetInstance() *Channel { return channelInstance } + +func (c *Channel) RequestTranslateArea(areaId string) bool { + documentOfArea := document.GetDocumentCollection().GetDocumentByAreaId(areaId) + area := documentOfArea.GetAreaById(areaId) + processedArea := document.GetProcessedAreaCollection().GetAreaById(area.Id) + + var textToTranslate string + for _, line := range processedArea.Lines { + for _, word := range line.Words { + textToTranslate = textToTranslate + " " + word.FullText + } + } + + var sourceLanguage string + if area.Language.TranslateCode != "" { + sourceLanguage = area.Language.TranslateCode + } else if documentOfArea.DefaultLanguage.TranslateCode != "" { + sourceLanguage = documentOfArea.DefaultLanguage.TranslateCode + } else { + return false + } + + sourceLanguage = "he" + targetLanguage := "en" + translatedText := translate.Text(textToTranslate, sourceLanguage, targetLanguage) + if translatedText == "" { + return true + } else { + return false + } +} diff --git a/ipc/ContextGroup.go b/ipc/ContextGroup.go new file mode 100644 index 0000000..0af4db2 --- /dev/null +++ b/ipc/ContextGroup.go @@ -0,0 +1,22 @@ +package ipc + +import ( + document "textualize/core/Document" + "textualize/entities" +) + +func (c *Channel) RequestConnectAreaAsTailToNode(tailId string, headId string) bool { + processedAreaOfTail := document.GetProcessedAreaCollection().GetAreaById(tailId) + if processedAreaOfTail == nil { + return false + } + + processedAreaOfHead := document.GetProcessedAreaCollection().GetAreaById(headId) + if processedAreaOfHead == nil { + return false + } + + entities.GetContextGroupCollection().ConnectAreaAsTailToNode(*processedAreaOfTail, *processedAreaOfHead) + + return true +} diff --git a/ipc/ProcessedDocument.go b/ipc/ProcessedDocument.go index 52f0603..93571d3 100644 --- a/ipc/ProcessedDocument.go +++ b/ipc/ProcessedDocument.go @@ -10,7 +10,12 @@ import ( ) func (c *Channel) GetProcessedAreaById(id string) entities.ProcessedArea { - return *document.GetProcessedAreaCollection().GetAreaById(id) + foundArea := document.GetProcessedAreaCollection().GetAreaById(id) + if foundArea != nil { + return *foundArea + } else { + return entities.ProcessedArea{} + } } func (c *Channel) GetProcessedAreasByDocumentId(id string) []entities.ProcessedArea { diff --git a/translate/Translate.go b/translate/Translate.go new file mode 100644 index 0000000..294615d --- /dev/null +++ b/translate/Translate.go @@ -0,0 +1,29 @@ +package translate + +import ( + "fmt" + + "github.com/snakesel/libretranslate" + // tr "github.com/snakesel/libretranslate" +) + +var translatorInstance *libretranslate.Translation + +func GetTranslator() *libretranslate.Translation { + return libretranslate.New(libretranslate.Config{ + Url: "http://localhost:9090", + }) +} + +func Text(value string, sourceLanguage string, targetLanguage string) string { + translator := GetTranslator() + + responseText, err := translator.Translate(value, sourceLanguage, targetLanguage) + if err == nil { + fmt.Println(responseText) + return responseText + } else { + fmt.Println(err.Error()) + return ("") + } +}