feat(devtool): ImageAnnotation 中支持修改现有定义的标注类型

This commit is contained in:
XcantloadX 2025-03-18 11:18:20 +08:00
parent cc73188637
commit 7bd921b51c
4 changed files with 62 additions and 14 deletions

View File

@ -5,6 +5,12 @@ export interface PropertyRenderBase {
required?: boolean,
}
type SelectOption<T extends string> = {
value: T,
options: Array<{ value: T, label: string }>,
onChange: (value: T) => void
}
interface PropertyRenderInputOptions {
text: {
value: string,
@ -17,7 +23,8 @@ interface PropertyRenderInputOptions {
'long-text': {
value: string,
onChange: (value: string) => void
}
},
select: SelectOption<string>
}
type RenderType = keyof PropertyRenderInputOptions;
@ -162,6 +169,21 @@ const PropertyGrid: React.FC<PropertyGridProps> = ({ properties, titleColumnWidt
} else if (type === 'long-text') {
const propertyLongText = property.render as PropertyRenderInputOptions['long-text'];
field = <textarea value={propertyLongText.value} onChange={(e) => propertyLongText.onChange(e.target.value)} />;
} else if (type === 'select') {
const propertySelect = property.render as PropertyRenderInputOptions['select'];
field = (
<select
value={propertySelect.value}
onChange={(e) => propertySelect.onChange(e.target.value as any)}
style={{ width: '100%', padding: '4px' }}
>
{propertySelect.options.map(option => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
);
}
} else {
console.error('Invalid property render type:', property.render);

View File

@ -1,10 +1,9 @@
import { useState } from "react";
import { Annotation } from "../components/ImageEditor/types";
import { useImmer } from "use-immer";
export type DefinitionType = 'template' | 'ocr' | 'color' | 'hint-box' | 'hint-point';
export interface Definition {
export interface BaseDefinition {
/** 最终出现在 R.py 中的名称 */
name: string;
/** 显示在调试器与调试输出中的名称 */
@ -17,7 +16,7 @@ export interface Definition {
}
export interface TemplateDefinition extends Definition {
export interface TemplateDefinition extends BaseDefinition {
type: 'template';
/**
*
@ -29,6 +28,15 @@ export interface TemplateDefinition extends Definition {
useHintRect: boolean
}
export interface HintBoxDefinition extends BaseDefinition {
type: 'hint-box';
}
export interface HintPointDefinition extends BaseDefinition {
type: 'hint-point';
}
export type Definition = TemplateDefinition | HintBoxDefinition | HintPointDefinition;
export type Definitions = Record<string, Definition>;

View File

@ -5,7 +5,7 @@ import PropertyGrid, { Property, PropertyCategory } from '../../components/Prope
import ImageEditor, { AnnotationChangedEvent } from '../../components/ImageEditor/ImageEditor';
import { Annotation, Tool as EditorTool } from '../../components/ImageEditor/types';
import { BsCursor, BsFolder2Open, BsFloppy, BsCardImage, BsQuestionSquare, BsPinMap } from 'react-icons/bs';
import useImageMetaData, { DefinitionType, ImageMetaData, TemplateDefinition, Definitions } from '../../hooks/useImageMetaData';
import useImageMetaData, { DefinitionType, ImageMetaData, TemplateDefinition, Definitions, Definition } from '../../hooks/useImageMetaData';
import { useImageViewerModal } from '../../components/ImageViewerModal';
import { useMessageBox } from '../../hooks/useMessageBox';
import { useToast } from '../../components/ToastMessage';
@ -138,7 +138,7 @@ const usePropertyGridData = (
definitions: Definitions,
image: HTMLImageElement | null,
onImageClick: (imageUrl: string) => void,
onDefinitionChange?: (id: string, changes: Partial<TemplateDefinition>) => void,
onDefinitionChange?: (id: string, changes: Partial<Definition>) => void,
imageFileName?: string,
annotations?: Annotation[],
currentFileResult?: FileResult | null
@ -246,7 +246,25 @@ const usePropertyGridData = (
},
{
title: '类型',
render: () => definition.type,
render: {
type: 'select',
required: true,
value: definition.type,
options: selectedAnnotation.type === 'rect'
? [
{ value: 'template', label: '模板' },
{ value: 'hint-box', label: 'HintBox' }
]
: [
{ value: 'hint-point', label: 'HintPoint' }
],
onChange: (value) => {
if (value === definition.type) return;
onDefinitionChange?.(selectedAnnotation.id, {
type: value as "template" | "hint-box" | "hint-point",
});
}
}
}
],
foldable: true
@ -540,7 +558,7 @@ const ImageAnnotation: React.FC = () => {
setSelectedAnnotation(annotation);
};
const handleDefinitionChange = (id: string, changes: Partial<TemplateDefinition>) => {
const handleDefinitionChange = (id: string, changes: Partial<Definition>) => {
Definitions.update({
...changes,
annotationId: id

View File

@ -9,7 +9,7 @@ import { Splitable } from '../../components/Splitable';
import { Tool, Annotation } from '../../components/ImageEditor/types';
import { create } from 'zustand';
import { css } from '@emotion/react';
import useImageMetaData, { Definition, DefinitionType, ImageMetaData, TemplateDefinition } from '../../hooks/useImageMetaData';
import useImageMetaData, { BaseDefinition, DefinitionType, ImageMetaData, TemplateDefinition } from '../../hooks/useImageMetaData';
import { useDarkMode } from '../../hooks/useDarkMode';
import { useDebugClient } from '../../store/debugStore';
import useLatestCallback from '../../hooks/useLatestCallback';
@ -157,25 +157,25 @@ const useScriptRecorderStore = create<ScriptRecorderState>((set) => ({
}));
interface ToolConfigItem {
code?: (d: Definition, a: Annotation) => string;
code?: (d: BaseDefinition, a: Annotation) => string;
}
const ToolConfig: Record<ScriptRecorderTool, ToolConfigItem> = {
'drag': {
},
'template': {
code: (d: Definition) => `image.find(R.${d.name})`,
code: (d: BaseDefinition) => `image.find(R.${d.name})`,
},
'template-click': {
code: (d: Definition) =>
code: (d: BaseDefinition) =>
`if image.find(R.${d.name}):\n\tdevice.click()`,
},
'ocr': {
code: (d: Definition) => `ocr.ocr(R.${d.name})`,
code: (d: BaseDefinition) => `ocr.ocr(R.${d.name})`,
},
'ocr-click': {
code: (d: Definition) =>
code: (d: BaseDefinition) =>
`if ocr.ocr(R.${d.name}):\n\tdevice.click()`,
},
'hint-box': {