@yuwenovo/ocr-result-view 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2 @@
1
+ // Type-only entry for the public side-effect CSS import.
2
+ export {}
@@ -0,0 +1,36 @@
1
+ import { type ReviewFilter } from '../utils/diff-items';
2
+ import type { DiffDetail } from '../types';
3
+ type __VLS_Props = {
4
+ diffDetail: DiffDetail;
5
+ activeIndex?: number;
6
+ reviewFilter: ReviewFilter;
7
+ busy: boolean;
8
+ readonly: boolean;
9
+ /** 当前激活的 tab */
10
+ activeTab?: 'all' | 'modify' | 'delete' | 'insert';
11
+ };
12
+ declare var __VLS_1: {
13
+ item: import("..").DeleteItem | import("..").InsertItem | import("..").ModifyItem;
14
+ index: number;
15
+ active: boolean;
16
+ select: () => void;
17
+ ignore: (ignored: boolean) => void;
18
+ };
19
+ type __VLS_Slots = {} & {
20
+ default?: (props: typeof __VLS_1) => any;
21
+ };
22
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
23
+ select: (index: number) => any;
24
+ ignore: (diffId: string, ignored: boolean) => any;
25
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
26
+ onSelect?: ((index: number) => any) | undefined;
27
+ onIgnore?: ((diffId: string, ignored: boolean) => any) | undefined;
28
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
29
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
30
+ declare const _default: typeof __VLS_export;
31
+ export default _default;
32
+ type __VLS_WithSlots<T, S> = T & {
33
+ new (): {
34
+ $slots: S;
35
+ };
36
+ };
@@ -0,0 +1,71 @@
1
+ import { type ReviewFilter } from '../utils/diff-items';
2
+ import type { DiffItem, DiffTab, DocumentSide, OcrResultViewProps } from '../types';
3
+ declare function requestLayout(): void;
4
+ declare function selectDiff(indexOrId: number | string): Promise<boolean>;
5
+ declare function goToPage(side: DocumentSide, index: number): boolean;
6
+ declare function setFilter(tab: DiffTab, review?: ReviewFilter): void;
7
+ declare function retryImages(): void;
8
+ declare var __VLS_1: {
9
+ activeIndex: number;
10
+ selectDiff: typeof selectDiff;
11
+ setFilter: typeof setFilter;
12
+ }, __VLS_3: {
13
+ filename: string | undefined;
14
+ }, __VLS_25: {
15
+ filename: string | undefined;
16
+ }, __VLS_47: {}, __VLS_74: {
17
+ item: import("../types").DeleteItem | import("../types").InsertItem | import("../types").ModifyItem;
18
+ index: number;
19
+ active: boolean;
20
+ select: () => void;
21
+ ignore: (ignored: boolean) => void;
22
+ }, __VLS_86: {
23
+ activeIndex: number;
24
+ review: import("../types").ComparisonReview | undefined;
25
+ };
26
+ type __VLS_Slots = {} & {
27
+ toolbar?: (props: typeof __VLS_1) => any;
28
+ } & {
29
+ 'source-title'?: (props: typeof __VLS_3) => any;
30
+ } & {
31
+ 'target-title'?: (props: typeof __VLS_25) => any;
32
+ } & {
33
+ empty?: (props: typeof __VLS_47) => any;
34
+ } & {
35
+ 'diff-card'?: (props: typeof __VLS_74) => any;
36
+ } & {
37
+ footer?: (props: typeof __VLS_86) => any;
38
+ };
39
+ declare const __VLS_base: import("vue").DefineComponent<OcrResultViewProps, {
40
+ selectDiff: typeof selectDiff;
41
+ nextDiff: () => boolean;
42
+ previousDiff: () => boolean;
43
+ goToPage: typeof goToPage;
44
+ setFilter: typeof setFilter;
45
+ resize: typeof requestLayout;
46
+ retryImages: typeof retryImages;
47
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
48
+ select: (item: DiffItem | null, index: number) => any;
49
+ ignore: (diffId: string | null, ignored: boolean) => any;
50
+ "page-change": (side: DocumentSide, index: number) => any;
51
+ "filter-change": (tab: DiffTab, review: ReviewFilter) => any;
52
+ "image-error": (url: string) => any;
53
+ }, string, import("vue").PublicProps, Readonly<OcrResultViewProps> & Readonly<{
54
+ onSelect?: ((item: DiffItem | null, index: number) => any) | undefined;
55
+ onIgnore?: ((diffId: string | null, ignored: boolean) => any) | undefined;
56
+ "onPage-change"?: ((side: DocumentSide, index: number) => any) | undefined;
57
+ "onFilter-change"?: ((tab: DiffTab, review: ReviewFilter) => any) | undefined;
58
+ "onImage-error"?: ((url: string) => any) | undefined;
59
+ }>, {
60
+ readonly: boolean;
61
+ reviewBusy: boolean;
62
+ imageCrossOrigin: "anonymous" | "use-credentials" | null;
63
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
64
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
65
+ declare const _default: typeof __VLS_export;
66
+ export default _default;
67
+ type __VLS_WithSlots<T, S> = T & {
68
+ new (): {
69
+ $slots: S;
70
+ };
71
+ };
@@ -0,0 +1,2 @@
1
+ export { default as OcrResultView } from './components/OcrDiffViewer.vue';
2
+ export type * from './types';
@@ -0,0 +1,100 @@
1
+ export interface Gap {
2
+ x: number;
3
+ y: number;
4
+ w: number;
5
+ h: number;
6
+ }
7
+ export type TableChange = 'table_insert' | 'table_delete' | 'row_insert' | 'row_delete' | 'column_insert' | 'column_delete' | 'cell_insert' | 'cell_delete' | 'cell_content_modify' | 'cell_merge' | 'cell_split' | 'cell_span_modify';
8
+ interface DiffMetadata {
9
+ diffId: string;
10
+ ignored: boolean;
11
+ domain?: 'table';
12
+ tableChange?: TableChange;
13
+ }
14
+ export interface DeleteItem extends DiffMetadata {
15
+ type: 'delete';
16
+ chars: string;
17
+ sourceLine: number;
18
+ targetLine: number;
19
+ sourceText: string;
20
+ targetText: string;
21
+ sourceBox: number[] | null;
22
+ tgtGap: Gap | null;
23
+ /** 0-based 索引 → source.pages / target.pages */
24
+ srcPage: number;
25
+ tgtPage: number;
26
+ }
27
+ export interface InsertItem extends DiffMetadata {
28
+ type: 'insert';
29
+ chars: string;
30
+ sourceLine: number;
31
+ targetLine: number;
32
+ sourceText: string;
33
+ targetText: string;
34
+ targetBox: number[] | null;
35
+ srcGap: Gap | null;
36
+ srcPage: number;
37
+ tgtPage: number;
38
+ }
39
+ export interface ModifyItem extends DiffMetadata {
40
+ type: 'modify';
41
+ delChars: string;
42
+ insChars: string;
43
+ sourceChangeRanges?: TextRange[];
44
+ targetChangeRanges?: TextRange[];
45
+ sourceLine: number;
46
+ targetLine: number;
47
+ sourceText: string;
48
+ targetText: string;
49
+ sourceBox: number[] | null;
50
+ targetBox: number[] | null;
51
+ srcPage: number;
52
+ tgtPage: number;
53
+ }
54
+ export interface TextRange {
55
+ start: number;
56
+ end: number;
57
+ }
58
+ export type DiffItem = DeleteItem | InsertItem | ModifyItem;
59
+ /** 后端已排序的扁平差异列表。 */
60
+ export type DiffDetail = DiffItem[];
61
+ /** 页面元数据(按数组顺序对应页码) */
62
+ export interface PageMeta {
63
+ imageUrl: string;
64
+ /** 可选原图尺寸;用于在图片按需加载前建立精确的虚拟滚动布局。 */
65
+ width?: number;
66
+ height?: number;
67
+ }
68
+ /** 服务端审阅汇总,组件不会自行修改此对象。 */
69
+ export interface ComparisonReview {
70
+ review_version: number;
71
+ total_difference_count: number;
72
+ ignored_difference_count: number;
73
+ remaining_difference_count: number;
74
+ comparison_passed: boolean;
75
+ }
76
+ export type DiffTab = 'all' | 'modify' | 'delete' | 'insert';
77
+ export type ReviewFilter = 'pending' | 'ignored' | 'all';
78
+ export type DocumentSide = 'source' | 'target';
79
+ export interface OcrResultViewProps {
80
+ diffDetail: DiffDetail;
81
+ sourcePages: PageMeta[];
82
+ targetPages: PageMeta[];
83
+ sourceFilename?: string;
84
+ targetFilename?: string;
85
+ review?: ComparisonReview;
86
+ reviewBusy?: boolean;
87
+ /** 默认只读;忽略操作仅发出事件,由宿主保存并回传新数据。 */
88
+ readonly?: boolean;
89
+ imageCrossOrigin?: 'anonymous' | 'use-credentials' | null;
90
+ }
91
+ export interface OcrResultViewExpose {
92
+ selectDiff: (indexOrId: number | string) => Promise<boolean>;
93
+ nextDiff: () => boolean;
94
+ previousDiff: () => boolean;
95
+ goToPage: (side: DocumentSide, index: number) => boolean;
96
+ setFilter: (tab: DiffTab, review?: ReviewFilter) => void;
97
+ resize: () => void;
98
+ retryImages: () => void;
99
+ }
100
+ export {};
@@ -0,0 +1,18 @@
1
+ import type { DeleteItem, DiffDetail, InsertItem, ModifyItem } from '../types';
2
+ export type ReviewFilter = 'pending' | 'ignored' | 'all';
3
+ export type DiffTab = 'all' | 'modify' | 'delete' | 'insert';
4
+ export type VisibleDiffItem = {
5
+ type: 'modify';
6
+ data: ModifyItem;
7
+ idx: number;
8
+ } | {
9
+ type: 'delete';
10
+ data: DeleteItem;
11
+ idx: number;
12
+ } | {
13
+ type: 'insert';
14
+ data: InsertItem;
15
+ idx: number;
16
+ };
17
+ /** 保持后端数组顺序,仅按当前 Tab 过滤。 */
18
+ export declare function filteredDiffItems(detail: DiffDetail, tab?: DiffTab, review?: ReviewFilter): VisibleDiffItem[];
@@ -0,0 +1,26 @@
1
+ <script setup lang="ts">
2
+ import { ref } from 'vue'
3
+ import { OcrResultView, type OcrResultViewExpose, type OcrResultViewProps } from '@yuwenovo/ocr-result-view'
4
+ import '@yuwenovo/ocr-result-view/style.css'
5
+
6
+ // Supply your own result and accessible preview URLs; this component makes no business API calls.
7
+ defineProps<{ result: OcrResultViewProps }>()
8
+ const viewer = ref<OcrResultViewExpose>()
9
+ </script>
10
+
11
+ <template>
12
+ <div style="height: 720px">
13
+ <OcrResultView ref="viewer" v-bind="result">
14
+ <template #toolbar>
15
+ <div style="padding: 8px">
16
+ <button @click="viewer?.previousDiff()">上一处</button>
17
+ <button @click="viewer?.nextDiff()">下一处</button>
18
+ </div>
19
+ </template>
20
+ <template #footer="{ activeIndex }">
21
+ <div style="padding: 8px">当前差异:{{ activeIndex >= 0 ? activeIndex + 1 : '无' }}</div>
22
+ </template>
23
+ </OcrResultView>
24
+ </div>
25
+ </template>
26
+
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@yuwenovo/ocr-result-view",
3
+ "version": "1.0.0",
4
+ "description": "Vue 3 OCR document comparison result viewer with virtualized Canvas rendering",
5
+ "type": "module",
6
+ "main": "./dist/ocr-result-view.js",
7
+ "module": "./dist/ocr-result-view.js",
8
+ "types": "./dist/ocr-result-view.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/ocr-result-view.d.ts",
12
+ "import": "./dist/ocr-result-view.js"
13
+ },
14
+ "./style.css": {
15
+ "types": "./dist/style.d.ts",
16
+ "default": "./dist/ocr-result-view.css"
17
+ }
18
+ },
19
+ "files": [
20
+ "dist",
21
+ "README.md",
22
+ "examples"
23
+ ],
24
+ "sideEffects": [
25
+ "**/*.css"
26
+ ],
27
+ "peerDependencies": {
28
+ "vue": "^3.5.0"
29
+ },
30
+ "devDependencies": {
31
+ "@iconify-json/lucide": "^1.2.132",
32
+ "@vitejs/plugin-vue": "^6.0.9",
33
+ "@vue/tsconfig": "^0.9.1",
34
+ "typescript": "~6.0.3",
35
+ "unplugin-icons": "^24.0.0",
36
+ "vite": "^8.3.0",
37
+ "vue": "^3.5.42",
38
+ "vue-tsc": "^3.3.11"
39
+ },
40
+ "engines": {
41
+ "node": "^22.18.0 || ^24.12.0 || >=26.0.0"
42
+ },
43
+ "license": "ISC",
44
+ "scripts": {
45
+ "build": "vite build && vue-tsc -p tsconfig.build.json",
46
+ "type-check": "vue-tsc -p tsconfig.json --noEmit"
47
+ }
48
+ }