feat: update processed word
This commit is contained in:
parent
1f8ffc01b1
commit
a323a387e5
@ -14,6 +14,7 @@ type ProcessedSymbol struct {
|
||||
}
|
||||
|
||||
type ProcessedWord struct {
|
||||
Id string
|
||||
FullText string
|
||||
Symbols []ProcessedSymbol
|
||||
Confidence float32
|
||||
|
@ -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<HTMLCanvasElement>(null)
|
||||
const areaCanvas = useRef<HTMLCanvasElement>(null)
|
||||
const uiCanvas = useRef<HTMLCanvasElement>(null)
|
||||
const drawingCanvas = useRef<HTMLCanvasElement>(null)
|
||||
const editWordInput = useRef<HTMLInputElement>(null)
|
||||
|
||||
const [zoomLevel, setZoomLevel] = useState(1)
|
||||
const [hoverOverAreaId, setHoverOverAreaId] = useState('')
|
||||
const [hoveredProcessedArea, setHoveredProcessedArea] = useState<ipc.ProcessedArea | undefined>()
|
||||
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 <span
|
||||
key={i}
|
||||
dir={w.direction === 'RIGHT_TO_LEFT' ? 'rtl' : 'ltr'}
|
||||
className='absolute text-center inline-block p-1 bg-opacity-60 bg-black text-white rounded-md shadow-zinc-900 shadow-2xl'
|
||||
className={classNames('absolute text-center inline-block p-1 rounded-md shadow-zinc-900 shadow-2xl',
|
||||
'hover:bg-opacity-60 hover:bg-black hover:text-white',
|
||||
'bg-opacity-80 bg-slate-300 text-slate-500'
|
||||
)}
|
||||
style={{
|
||||
fontSize: `${3.4 * zoomLevel}vmin`,
|
||||
width,
|
||||
top: Math.floor(w.boundingBox.y0 * zoomLevel) + height,
|
||||
left: Math.floor(w.boundingBox.x0 * zoomLevel)
|
||||
}}>
|
||||
}}
|
||||
onDoubleClick={() => setWordToEdit({ word: w, areaId: hoverArea.id })}>
|
||||
{w.fullText}
|
||||
</span>
|
||||
})
|
||||
@ -274,6 +306,56 @@ const DocumentRenderer = () => {
|
||||
</div>
|
||||
}
|
||||
|
||||
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 <div
|
||||
dir={word.direction === 'RIGHT_TO_LEFT' ? 'rtl' : 'ltr'}
|
||||
className={classNames('absolute inline-block p-1 rounded-md',
|
||||
'bg-opacity-60 bg-black text-white',
|
||||
)}
|
||||
style={{
|
||||
width,
|
||||
height,
|
||||
top: Math.floor(word.boundingBox.y0 * zoomLevel) + (height / 2),
|
||||
left: Math.floor(word.boundingBox.x0 * zoomLevel)
|
||||
}}
|
||||
onBlur={() => setWordToEdit(undefined)}
|
||||
>
|
||||
<div
|
||||
className={classNames('text-center align-middle block p-1 rounded-md shadow-zinc-900 shadow-2xl',
|
||||
'bg-opacity-60 bg-black text-white',
|
||||
)}
|
||||
style={{
|
||||
fontSize: `${3.4 * zoomLevel}vmin`,
|
||||
height: height / 2,
|
||||
}}>
|
||||
{word.fullText}
|
||||
</div>
|
||||
|
||||
<input
|
||||
type='text'
|
||||
className='inline-block text-slate-900 p-0 m-0 w-full'
|
||||
autoFocus
|
||||
width={width}
|
||||
ref={editWordInput}
|
||||
placeholder={word.fullText}
|
||||
defaultValue={word.fullText}
|
||||
style={{
|
||||
fontSize: `${3.4 * zoomLevel}vmin`,
|
||||
height: height / 2,
|
||||
}}
|
||||
onFocus={(e) => e.currentTarget.select()}
|
||||
onBlur={(e) => handleWordCorrectionSubmit(word.id, e.currentTarget.value)}
|
||||
onKeyDown={(e) => onEnterHandler(e, () => handleWordCorrectionSubmit(word.id, e.currentTarget.value))}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
return <div className='relative'>
|
||||
<div className='flex justify-between align-top mb-2'>
|
||||
<div className='flex align-top'>
|
||||
@ -315,6 +397,7 @@ const DocumentRenderer = () => {
|
||||
onMouseMove={handleMouseMove}
|
||||
/>
|
||||
{renderAreaPreview()}
|
||||
{renderEditWord()}
|
||||
</div>
|
||||
</div >
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 <ProjectContext.Provider value={value}>
|
||||
|
@ -62,4 +62,5 @@ export type ProjectContextType = {
|
||||
requestChangeGroupOrder: (groupId: string, newOrder: number) => Promise<ipc.Group>
|
||||
getGroupById: (groupId: string) => ipc.Group | undefined
|
||||
requestSelectProjectByName: (projectName: string) => Promise<boolean>
|
||||
requestUpdateProcessedWordById: (wordId: string, newTextValue: string) => Promise<boolean>
|
||||
} & ProjectProps
|
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
2
frontend/wailsjs/wailsjs/go/ipc/Channel.d.ts
vendored
@ -59,3 +59,5 @@ export function RequestUpdateCurrentUser(arg1:ipc.User):Promise<ipc.User>;
|
||||
export function RequestUpdateDocument(arg1:ipc.Document):Promise<ipc.Document>;
|
||||
|
||||
export function RequestUpdateDocumentUserMarkdown(arg1:string,arg2:string):Promise<ipc.UserMarkdown>;
|
||||
|
||||
export function RequestUpdateProcessedWordById(arg1:string,arg2:string):Promise<boolean>;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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"];
|
||||
|
@ -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,
|
||||
|
@ -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"`
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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"`
|
||||
|
Loading…
x
Reference in New Issue
Block a user