diff --git a/core/Document/ProcessedText.go b/core/Document/ProcessedText.go index d7363d9..749f723 100644 --- a/core/Document/ProcessedText.go +++ b/core/Document/ProcessedText.go @@ -14,6 +14,7 @@ type ProcessedSymbol struct { } type ProcessedWord struct { + Id string FullText string Symbols []ProcessedSymbol Confidence float32 diff --git a/frontend/components/workspace/DocumentRenderer.tsx b/frontend/components/workspace/DocumentRenderer.tsx index adf8fd6..b8ced79 100644 --- a/frontend/components/workspace/DocumentRenderer.tsx +++ b/frontend/components/workspace/DocumentRenderer.tsx @@ -9,22 +9,32 @@ import classNames from '../../utils/classNames' import LanguageSelect from './LanguageSelect' import isInBounds from '../../utils/isInBounds' import { ipc } from '../../wailsjs/wailsjs/go/models' +import onEnterHandler from '../../utils/onEnterHandler' const zoomStep = 0.025 const maxZoomLevel = 4 const DocumentRenderer = () => { - const { getSelectedDocument, requestAddArea, selectedAreaId, setSelectedAreaId, getProcessedAreasByDocumentId } = useProject() + const { + getSelectedDocument, + requestAddArea, + selectedAreaId, + setSelectedAreaId, + getProcessedAreasByDocumentId, + requestUpdateProcessedWordById + } = useProject() const selectedDocument = getSelectedDocument() const areas = selectedDocument?.areas const documentCanvas = useRef(null) const areaCanvas = useRef(null) const uiCanvas = useRef(null) const drawingCanvas = useRef(null) + const editWordInput = useRef(null) const [zoomLevel, setZoomLevel] = useState(1) const [hoverOverAreaId, setHoverOverAreaId] = useState('') const [hoveredProcessedArea, setHoveredProcessedArea] = useState() + const [wordToEdit, setWordToEdit] = useState<{ word: ipc.ProcessedWord, areaId: string } | undefined>() let downClickX = 0 let downClickY = 0 @@ -134,10 +144,16 @@ const DocumentRenderer = () => { } const getProcessedAreaById = async (areaId: string) => { - if (!selectedDocument || !selectedDocument.id || !areaId) return - const processedAreas = await getProcessedAreasByDocumentId(selectedDocument.id) - const foundProcessedArea = processedAreas.find(a => a.id === areaId) - return foundProcessedArea + try { + if (!selectedDocument || !selectedDocument.id || !areaId) return + const processedAreas = await getProcessedAreasByDocumentId(selectedDocument.id) + const foundProcessedArea = processedAreas.find(a => a.id === areaId) + console.log(foundProcessedArea) + return foundProcessedArea + } catch (err) { + console.error(err) + return + } } const handleHoverOverArea = (e: React.MouseEvent) => { @@ -155,7 +171,6 @@ const DocumentRenderer = () => { setHoverOverAreaId(areaContainingCoords?.id || '') getProcessedAreaById(areaContainingCoords?.id || '').then(response => { - console.log(response) setHoveredProcessedArea(response) }) } @@ -237,6 +252,19 @@ const DocumentRenderer = () => { else if (zoomLevel > (zoomStep * 2)) setZoomLevel(zoomLevel - zoomStep) } + const handleWordCorrectionSubmit = (wordId: string, newWordValue: string) => { + console.log(newWordValue) + requestUpdateProcessedWordById(wordId, newWordValue) + .then(res => { + console.log('res', res) + getProcessedAreaById(hoverOverAreaId|| '').then(response => { + setHoveredProcessedArea(response) + }) + }) + .catch(console.error) + setWordToEdit(undefined) + } + useEffect(() => { if (selectedDocument?.path) applyDocumentToCanvas(selectedDocument.path) }) @@ -260,13 +288,17 @@ const DocumentRenderer = () => { return + }} + onDoubleClick={() => setWordToEdit({ word: w, areaId: hoverArea.id })}> {w.fullText} }) @@ -274,6 +306,56 @@ const DocumentRenderer = () => { } + const renderEditWord = () => { + if (!wordToEdit) return <> + + const { word, areaId } = wordToEdit + const width = Math.floor((word.boundingBox.x1 - word.boundingBox.x0) * zoomLevel) + 2 + const height = Math.floor(((word.boundingBox.y1 - word.boundingBox.y0) * zoomLevel) * 2) + 4 + return
setWordToEdit(undefined)} + > +
+ {word.fullText} +
+ + e.currentTarget.select()} + onBlur={(e) => handleWordCorrectionSubmit(word.id, e.currentTarget.value)} + onKeyDown={(e) => onEnterHandler(e, () => handleWordCorrectionSubmit(word.id, e.currentTarget.value))} + /> +
+ } + + return
@@ -315,6 +397,7 @@ const DocumentRenderer = () => { onMouseMove={handleMouseMove} /> {renderAreaPreview()} + {renderEditWord()}
} diff --git a/frontend/context/Project/makeDefaultProject.ts b/frontend/context/Project/makeDefaultProject.ts index 8d9b7cc..6a48af3 100644 --- a/frontend/context/Project/makeDefaultProject.ts +++ b/frontend/context/Project/makeDefaultProject.ts @@ -30,6 +30,7 @@ const makeDefaultProject = (): ProjectContextType => ({ requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise.resolve(new ipc.Group()), getGroupById: (groupId) => undefined, requestSelectProjectByName: (projectName) => Promise.resolve(false), + requestUpdateProcessedWordById: (wordId, newTestValue) => Promise.resolve(false), }) export default makeDefaultProject diff --git a/frontend/context/Project/provider.tsx b/frontend/context/Project/provider.tsx index fd6f972..5e6e84a 100644 --- a/frontend/context/Project/provider.tsx +++ b/frontend/context/Project/provider.tsx @@ -13,6 +13,7 @@ import { RequestChangeGroupOrder, RequestChangeSessionProjectByName, RequestDeleteDocumentAndChildren, + RequestUpdateProcessedWordById } from '../../wailsjs/wailsjs/go/ipc/Channel' import { ipc } from '../../wailsjs/wailsjs/go/models' import { AddAreaProps, AreaProps, ProjectContextType, ProjectProps, UpdateDocumentRequest, UserProps } from './types' @@ -35,7 +36,6 @@ export function ProjectProvider({ children, projectProps }: Props) { const updateDocuments = async () => { GetDocuments().then(response => { - console.log(response) setDocuments(response.documents) setGroups(response.groups) Promise.resolve(response) @@ -184,6 +184,13 @@ export function ProjectProvider({ children, projectProps }: Props) { return successfulResponse } + const requestUpdateProcessedWordById = async (wordId: string, newTextValue: string) => { + const successfulResponse = await RequestUpdateProcessedWordById(wordId, newTextValue) + // if (successfulResponse) await updateDocuments() + return successfulResponse + } + + useEffect(() => { if (!documents.length && !groups.length) updateDocuments() }, [documents.length, groups.length]) @@ -224,6 +231,7 @@ export function ProjectProvider({ children, projectProps }: Props) { requestChangeGroupOrder, getGroupById, requestSelectProjectByName, + requestUpdateProcessedWordById, } return diff --git a/frontend/context/Project/types.ts b/frontend/context/Project/types.ts index 194e45c..115c70b 100644 --- a/frontend/context/Project/types.ts +++ b/frontend/context/Project/types.ts @@ -62,4 +62,5 @@ export type ProjectContextType = { requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise getGroupById: (groupId: string) => ipc.Group | undefined requestSelectProjectByName: (projectName: string) => Promise + requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise } & ProjectProps \ No newline at end of file diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts index 72c5042..9a00487 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts @@ -59,3 +59,5 @@ export function RequestUpdateCurrentUser(arg1:ipc.User):Promise; export function RequestUpdateDocument(arg1:ipc.Document):Promise; export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise; + +export function RequestUpdateProcessedWordById(arg1:string,arg2:string):Promise; diff --git a/frontend/wailsjs/wailsjs/go/ipc/Channel.js b/frontend/wailsjs/wailsjs/go/ipc/Channel.js index b9ee10c..af6b702 100755 --- a/frontend/wailsjs/wailsjs/go/ipc/Channel.js +++ b/frontend/wailsjs/wailsjs/go/ipc/Channel.js @@ -117,3 +117,7 @@ export function RequestUpdateDocument(arg1) { export function RequestUpdateDocumentUserMarkdown(arg1, arg2) { return window['go']['ipc']['Channel']['RequestUpdateDocumentUserMarkdown'](arg1, arg2); } + +export function RequestUpdateProcessedWordById(arg1, arg2) { + return window['go']['ipc']['Channel']['RequestUpdateProcessedWordById'](arg1, arg2); +} diff --git a/frontend/wailsjs/wailsjs/go/models.ts b/frontend/wailsjs/wailsjs/go/models.ts index fdf2610..ec72d83 100755 --- a/frontend/wailsjs/wailsjs/go/models.ts +++ b/frontend/wailsjs/wailsjs/go/models.ts @@ -269,6 +269,7 @@ export namespace ipc { } } export class ProcessedWord { + id: string; fullText: string; symbols: ProcessedSymbol[]; confidence: number; @@ -281,6 +282,7 @@ export namespace ipc { constructor(source: any = {}) { if ('string' === typeof source) source = JSON.parse(source); + this.id = source["id"]; this.fullText = source["fullText"]; this.symbols = this.convertValues(source["symbols"], ProcessedSymbol); this.confidence = source["confidence"]; diff --git a/ipc/Documents.go b/ipc/Documents.go index e881ff1..8c2489c 100644 --- a/ipc/Documents.go +++ b/ipc/Documents.go @@ -536,6 +536,7 @@ func (c *Channel) RequestSaveProcessedTextCollection() bool { } wordsOfLineToWrite = append(wordsOfLineToWrite, storageEntity.ProcessedWord{ + Id: w.Id, FullText: w.FullText, Confidence: w.Confidence, Direction: w.Direction, diff --git a/ipc/JsonEntities.go b/ipc/JsonEntities.go index 2738cc1..56a7d48 100644 --- a/ipc/JsonEntities.go +++ b/ipc/JsonEntities.go @@ -54,6 +54,7 @@ type ProcessedSymbol struct { } type ProcessedWord struct { + Id string `json:"id"` FullText string `json:"fullText"` Symbols []ProcessedSymbol `json:"symbols"` Confidence float32 `json:"confidence"` diff --git a/ipc/ProcessedDocument.go b/ipc/ProcessedDocument.go index d506af3..220885e 100644 --- a/ipc/ProcessedDocument.go +++ b/ipc/ProcessedDocument.go @@ -3,6 +3,8 @@ package ipc import ( "sort" document "textualize/core/Document" + + "github.com/google/uuid" ) func serializeBoundingBox(bbox document.ProcessedBoundingBox) ProcessedBoundingBox { @@ -30,6 +32,7 @@ func serialzeWord(word document.ProcessedWord) ProcessedWord { } return ProcessedWord{ + Id: word.Id, FullText: word.FullText, Symbols: symbols, Confidence: word.Confidence, @@ -106,8 +109,15 @@ func deserialzeWord(word ProcessedWord) document.ProcessedWord { for _, symbol := range word.Symbols { symbols = append(symbols, deserializeSymbol(symbol)) } + var wordId string + if word.Id == "" { + wordId = uuid.NewString() + } else { + wordId = word.Id + } return document.ProcessedWord{ + Id: wordId, FullText: word.FullText, Symbols: symbols, Confidence: word.Confidence, @@ -169,3 +179,52 @@ func (c *Channel) RequestAddProcessedArea(processedArea ProcessedArea) Processed return processedArea } + +func (c *Channel) RequestUpdateProcessedWordById(wordId string, newTextValue string) bool { + areas := document.GetProcessedAreaCollection().Areas + + var areaOfWordIndex int = -1 + var lineOfWordIndex int = -1 + var foundWordIndex int = -1 + for areaIndex, area := range areas { + + for lineIndex, line := range area.Lines { + + for wordIndex, word := range line.Words { + if word.Id == wordId { + areaOfWordIndex = areaIndex + lineOfWordIndex = lineIndex + foundWordIndex = wordIndex + break + } + } + + if foundWordIndex >= 0 { + break + } + } + + if foundWordIndex >= 0 { + break + } + } + + if areaOfWordIndex < 0 || lineOfWordIndex < 0 || foundWordIndex < 0 { + return false + } + + wordProps := areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] + areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex] = document.ProcessedWord{ + Id: wordProps.Id, + Direction: wordProps.Direction, + FullText: newTextValue, + BoundingBox: wordProps.BoundingBox, + } + + if areas[areaOfWordIndex].Lines[lineOfWordIndex].Words[foundWordIndex].FullText == newTextValue { + successfulSave := c.RequestSaveDocumentCollection() + return successfulSave + } else { + return false + } +} diff --git a/storage/Entities/ProcessedText.go b/storage/Entities/ProcessedText.go index caf95da..24ffb1f 100644 --- a/storage/Entities/ProcessedText.go +++ b/storage/Entities/ProcessedText.go @@ -14,6 +14,7 @@ type ProcessedSymbol struct { } type ProcessedWord struct { + Id string `json:"id"` FullText string `json:"fullText"` Symbols []ProcessedSymbol `json:"symbols"` Confidence float32 `json:"confidence"`