kviewer 0.0.1

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.
Files changed (197) hide show
  1. package/README.md +134 -0
  2. package/dist/module.d.mts +15 -0
  3. package/dist/module.json +9 -0
  4. package/dist/module.mjs +26 -0
  5. package/dist/runtime/annotation/engine/config.d.ts +52 -0
  6. package/dist/runtime/annotation/engine/config.js +283 -0
  7. package/dist/runtime/annotation/engine/const.d.ts +6 -0
  8. package/dist/runtime/annotation/engine/const.js +7 -0
  9. package/dist/runtime/annotation/engine/cursor-preview.d.ts +2 -0
  10. package/dist/runtime/annotation/engine/cursor-preview.js +88 -0
  11. package/dist/runtime/annotation/engine/editor/editor.d.ts +69 -0
  12. package/dist/runtime/annotation/engine/editor/editor.js +233 -0
  13. package/dist/runtime/annotation/engine/editor/selector.d.ts +74 -0
  14. package/dist/runtime/annotation/engine/editor/selector.js +594 -0
  15. package/dist/runtime/annotation/engine/import-normalize.d.ts +5 -0
  16. package/dist/runtime/annotation/engine/import-normalize.js +99 -0
  17. package/dist/runtime/annotation/engine/input-device.d.ts +53 -0
  18. package/dist/runtime/annotation/engine/input-device.js +64 -0
  19. package/dist/runtime/annotation/engine/painter.d.ts +97 -0
  20. package/dist/runtime/annotation/engine/painter.js +591 -0
  21. package/dist/runtime/annotation/engine/store.d.ts +11 -0
  22. package/dist/runtime/annotation/engine/store.js +47 -0
  23. package/dist/runtime/annotation/engine/tools/arrow.d.ts +22 -0
  24. package/dist/runtime/annotation/engine/tools/arrow.js +126 -0
  25. package/dist/runtime/annotation/engine/tools/circle.d.ts +45 -0
  26. package/dist/runtime/annotation/engine/tools/circle.js +148 -0
  27. package/dist/runtime/annotation/engine/tools/cloud.d.ts +50 -0
  28. package/dist/runtime/annotation/engine/tools/cloud.js +244 -0
  29. package/dist/runtime/annotation/engine/tools/free-highlight.d.ts +43 -0
  30. package/dist/runtime/annotation/engine/tools/free-highlight.js +165 -0
  31. package/dist/runtime/annotation/engine/tools/free-text.d.ts +27 -0
  32. package/dist/runtime/annotation/engine/tools/free-text.js +114 -0
  33. package/dist/runtime/annotation/engine/tools/freehand.d.ts +44 -0
  34. package/dist/runtime/annotation/engine/tools/freehand.js +151 -0
  35. package/dist/runtime/annotation/engine/tools/highlight.d.ts +87 -0
  36. package/dist/runtime/annotation/engine/tools/highlight.js +215 -0
  37. package/dist/runtime/annotation/engine/tools/note.d.ts +9 -0
  38. package/dist/runtime/annotation/engine/tools/note.js +34 -0
  39. package/dist/runtime/annotation/engine/tools/rectangle.d.ts +45 -0
  40. package/dist/runtime/annotation/engine/tools/rectangle.js +142 -0
  41. package/dist/runtime/annotation/engine/tools/signature.d.ts +16 -0
  42. package/dist/runtime/annotation/engine/tools/signature.js +74 -0
  43. package/dist/runtime/annotation/engine/tools/stamp.d.ts +18 -0
  44. package/dist/runtime/annotation/engine/tools/stamp.js +94 -0
  45. package/dist/runtime/annotation/engine/types.d.ts +170 -0
  46. package/dist/runtime/annotation/engine/types.js +67 -0
  47. package/dist/runtime/annotation/engine/utils.d.ts +40 -0
  48. package/dist/runtime/annotation/engine/utils.js +257 -0
  49. package/dist/runtime/annotation/parsers/parseFormFields.d.ts +9 -0
  50. package/dist/runtime/annotation/parsers/parseFormFields.js +101 -0
  51. package/dist/runtime/annotation/pdf-export/download.d.ts +1 -0
  52. package/dist/runtime/annotation/pdf-export/download.js +10 -0
  53. package/dist/runtime/annotation/pdf-export/export-form-fields.d.ts +9 -0
  54. package/dist/runtime/annotation/pdf-export/export-form-fields.js +90 -0
  55. package/dist/runtime/annotation/pdf-export/export.d.ts +15 -0
  56. package/dist/runtime/annotation/pdf-export/export.js +145 -0
  57. package/dist/runtime/annotation/pdf-export/parse.d.ts +10 -0
  58. package/dist/runtime/annotation/pdf-export/parse.js +19 -0
  59. package/dist/runtime/annotation/pdf-export/parse_circle.d.ts +4 -0
  60. package/dist/runtime/annotation/pdf-export/parse_circle.js +41 -0
  61. package/dist/runtime/annotation/pdf-export/parse_freetext.d.ts +4 -0
  62. package/dist/runtime/annotation/pdf-export/parse_freetext.js +54 -0
  63. package/dist/runtime/annotation/pdf-export/parse_highlight.d.ts +4 -0
  64. package/dist/runtime/annotation/pdf-export/parse_highlight.js +134 -0
  65. package/dist/runtime/annotation/pdf-export/parse_ink.d.ts +4 -0
  66. package/dist/runtime/annotation/pdf-export/parse_ink.js +124 -0
  67. package/dist/runtime/annotation/pdf-export/parse_line.d.ts +4 -0
  68. package/dist/runtime/annotation/pdf-export/parse_line.js +71 -0
  69. package/dist/runtime/annotation/pdf-export/parse_polyline.d.ts +4 -0
  70. package/dist/runtime/annotation/pdf-export/parse_polyline.js +93 -0
  71. package/dist/runtime/annotation/pdf-export/parse_square.d.ts +4 -0
  72. package/dist/runtime/annotation/pdf-export/parse_square.js +41 -0
  73. package/dist/runtime/annotation/pdf-export/parse_stamp.d.ts +4 -0
  74. package/dist/runtime/annotation/pdf-export/parse_stamp.js +195 -0
  75. package/dist/runtime/annotation/pdf-export/parse_strikeout.d.ts +4 -0
  76. package/dist/runtime/annotation/pdf-export/parse_strikeout.js +59 -0
  77. package/dist/runtime/annotation/pdf-export/parse_text.d.ts +4 -0
  78. package/dist/runtime/annotation/pdf-export/parse_text.js +42 -0
  79. package/dist/runtime/annotation/pdf-export/parse_underline.d.ts +4 -0
  80. package/dist/runtime/annotation/pdf-export/parse_underline.js +59 -0
  81. package/dist/runtime/assets/kviewer.css +1 -0
  82. package/dist/runtime/components/AnnotationToolbar.d.vue.ts +3 -0
  83. package/dist/runtime/components/AnnotationToolbar.vue +125 -0
  84. package/dist/runtime/components/AnnotationToolbar.vue.d.ts +3 -0
  85. package/dist/runtime/components/FloatingPageIndicator.d.vue.ts +6 -0
  86. package/dist/runtime/components/FloatingPageIndicator.vue +93 -0
  87. package/dist/runtime/components/FloatingPageIndicator.vue.d.ts +6 -0
  88. package/dist/runtime/components/FormFieldLayer.d.vue.ts +11 -0
  89. package/dist/runtime/components/FormFieldLayer.vue +40 -0
  90. package/dist/runtime/components/FormFieldLayer.vue.d.ts +11 -0
  91. package/dist/runtime/components/PdfPage.d.vue.ts +9 -0
  92. package/dist/runtime/components/PdfPage.vue +199 -0
  93. package/dist/runtime/components/PdfPage.vue.d.ts +9 -0
  94. package/dist/runtime/components/ToolButton.d.vue.ts +13 -0
  95. package/dist/runtime/components/ToolButton.vue +26 -0
  96. package/dist/runtime/components/ToolButton.vue.d.ts +13 -0
  97. package/dist/runtime/components/Toolbar.d.vue.ts +3 -0
  98. package/dist/runtime/components/Toolbar.vue +11 -0
  99. package/dist/runtime/components/Toolbar.vue.d.ts +3 -0
  100. package/dist/runtime/components/Viewer.d.vue.ts +45 -0
  101. package/dist/runtime/components/Viewer.vue +617 -0
  102. package/dist/runtime/components/Viewer.vue.d.ts +45 -0
  103. package/dist/runtime/components/ViewerBar.d.vue.ts +3 -0
  104. package/dist/runtime/components/ViewerBar.vue +91 -0
  105. package/dist/runtime/components/ViewerBar.vue.d.ts +3 -0
  106. package/dist/runtime/components/ViewerTabs.d.vue.ts +381 -0
  107. package/dist/runtime/components/ViewerTabs.vue +171 -0
  108. package/dist/runtime/components/ViewerTabs.vue.d.ts +381 -0
  109. package/dist/runtime/components/form-fields/FormButton.d.vue.ts +7 -0
  110. package/dist/runtime/components/form-fields/FormButton.vue +39 -0
  111. package/dist/runtime/components/form-fields/FormButton.vue.d.ts +7 -0
  112. package/dist/runtime/components/form-fields/FormCheckbox.d.vue.ts +7 -0
  113. package/dist/runtime/components/form-fields/FormCheckbox.vue +28 -0
  114. package/dist/runtime/components/form-fields/FormCheckbox.vue.d.ts +7 -0
  115. package/dist/runtime/components/form-fields/FormDropdown.d.vue.ts +7 -0
  116. package/dist/runtime/components/form-fields/FormDropdown.vue +112 -0
  117. package/dist/runtime/components/form-fields/FormDropdown.vue.d.ts +7 -0
  118. package/dist/runtime/components/form-fields/FormFieldWrapper.d.vue.ts +8 -0
  119. package/dist/runtime/components/form-fields/FormFieldWrapper.vue +41 -0
  120. package/dist/runtime/components/form-fields/FormFieldWrapper.vue.d.ts +8 -0
  121. package/dist/runtime/components/form-fields/FormRadioButton.d.vue.ts +7 -0
  122. package/dist/runtime/components/form-fields/FormRadioButton.vue +30 -0
  123. package/dist/runtime/components/form-fields/FormRadioButton.vue.d.ts +7 -0
  124. package/dist/runtime/components/form-fields/FormSignatureField.d.vue.ts +7 -0
  125. package/dist/runtime/components/form-fields/FormSignatureField.vue +54 -0
  126. package/dist/runtime/components/form-fields/FormSignatureField.vue.d.ts +7 -0
  127. package/dist/runtime/components/form-fields/FormTextField.d.vue.ts +7 -0
  128. package/dist/runtime/components/form-fields/FormTextField.vue +66 -0
  129. package/dist/runtime/components/form-fields/FormTextField.vue.d.ts +7 -0
  130. package/dist/runtime/components/modals/FreeTextModal.d.vue.ts +25 -0
  131. package/dist/runtime/components/modals/FreeTextModal.vue +89 -0
  132. package/dist/runtime/components/modals/FreeTextModal.vue.d.ts +25 -0
  133. package/dist/runtime/components/modals/SignatureDrawModal.d.vue.ts +14 -0
  134. package/dist/runtime/components/modals/SignatureDrawModal.vue +120 -0
  135. package/dist/runtime/components/modals/SignatureDrawModal.vue.d.ts +14 -0
  136. package/dist/runtime/components/panels/SignaturePicker.d.vue.ts +3 -0
  137. package/dist/runtime/components/panels/SignaturePicker.vue +85 -0
  138. package/dist/runtime/components/panels/SignaturePicker.vue.d.ts +3 -0
  139. package/dist/runtime/components/panels/StampPicker.d.vue.ts +3 -0
  140. package/dist/runtime/components/panels/StampPicker.vue +46 -0
  141. package/dist/runtime/components/panels/StampPicker.vue.d.ts +3 -0
  142. package/dist/runtime/components/tools/ActionTools.d.vue.ts +3 -0
  143. package/dist/runtime/components/tools/ActionTools.vue +32 -0
  144. package/dist/runtime/components/tools/ActionTools.vue.d.ts +3 -0
  145. package/dist/runtime/components/tools/DrawingTools.d.vue.ts +6 -0
  146. package/dist/runtime/components/tools/DrawingTools.vue +57 -0
  147. package/dist/runtime/components/tools/DrawingTools.vue.d.ts +6 -0
  148. package/dist/runtime/components/tools/HandTool.d.vue.ts +3 -0
  149. package/dist/runtime/components/tools/HandTool.vue +14 -0
  150. package/dist/runtime/components/tools/HandTool.vue.d.ts +3 -0
  151. package/dist/runtime/components/tools/MarqueeTool.d.vue.ts +3 -0
  152. package/dist/runtime/components/tools/MarqueeTool.vue +15 -0
  153. package/dist/runtime/components/tools/MarqueeTool.vue.d.ts +3 -0
  154. package/dist/runtime/components/tools/PageInfo.d.vue.ts +3 -0
  155. package/dist/runtime/components/tools/PageInfo.vue +10 -0
  156. package/dist/runtime/components/tools/PageInfo.vue.d.ts +3 -0
  157. package/dist/runtime/components/tools/PageSettings.d.vue.ts +3 -0
  158. package/dist/runtime/components/tools/PageSettings.vue +92 -0
  159. package/dist/runtime/components/tools/PageSettings.vue.d.ts +3 -0
  160. package/dist/runtime/components/tools/SearchTool.d.vue.ts +3 -0
  161. package/dist/runtime/components/tools/SearchTool.vue +149 -0
  162. package/dist/runtime/components/tools/SearchTool.vue.d.ts +3 -0
  163. package/dist/runtime/components/tools/ToolProperties.d.vue.ts +7 -0
  164. package/dist/runtime/components/tools/ToolProperties.vue +174 -0
  165. package/dist/runtime/components/tools/ToolProperties.vue.d.ts +7 -0
  166. package/dist/runtime/components/tools/ZoomControls.d.vue.ts +3 -0
  167. package/dist/runtime/components/tools/ZoomControls.vue +59 -0
  168. package/dist/runtime/components/tools/ZoomControls.vue.d.ts +3 -0
  169. package/dist/runtime/composables/search-utils.d.ts +20 -0
  170. package/dist/runtime/composables/search-utils.js +55 -0
  171. package/dist/runtime/composables/useAnnotationEngine.d.ts +7 -0
  172. package/dist/runtime/composables/useAnnotationEngine.js +70 -0
  173. package/dist/runtime/composables/useAnnotationHistory.d.ts +12 -0
  174. package/dist/runtime/composables/useAnnotationHistory.js +69 -0
  175. package/dist/runtime/composables/useFormFields.d.ts +26 -0
  176. package/dist/runtime/composables/useFormFields.js +112 -0
  177. package/dist/runtime/composables/usePageProxyCache.d.ts +8 -0
  178. package/dist/runtime/composables/usePageProxyCache.js +73 -0
  179. package/dist/runtime/composables/usePageSettings.d.ts +16 -0
  180. package/dist/runtime/composables/usePageSettings.js +66 -0
  181. package/dist/runtime/composables/usePageVirtualization.d.ts +19 -0
  182. package/dist/runtime/composables/usePageVirtualization.js +203 -0
  183. package/dist/runtime/composables/useSearchIndex.d.ts +11 -0
  184. package/dist/runtime/composables/useSearchIndex.js +71 -0
  185. package/dist/runtime/composables/useViewerSearch.d.ts +32 -0
  186. package/dist/runtime/composables/useViewerSearch.js +418 -0
  187. package/dist/runtime/composables/useViewerState.d.ts +62 -0
  188. package/dist/runtime/composables/useViewerState.js +189 -0
  189. package/dist/runtime/composables/viewMode.d.ts +11 -0
  190. package/dist/runtime/composables/viewMode.js +19 -0
  191. package/dist/runtime/plugin.d.ts +2 -0
  192. package/dist/runtime/plugin.js +3 -0
  193. package/dist/runtime/public-types.d.ts +2 -0
  194. package/dist/runtime/public-types.js +0 -0
  195. package/dist/runtime/server/tsconfig.json +3 -0
  196. package/dist/types.d.mts +5 -0
  197. package/package.json +64 -0
@@ -0,0 +1,591 @@
1
+ import Konva from "konva";
2
+ import {
3
+ annotationDefinitions
4
+ } from "./config.js";
5
+ import {
6
+ AnnotationType
7
+ } from "./types.js";
8
+ import {
9
+ PAINTER_IS_PAINTING_STYLE,
10
+ PAINTER_PAINTING_TYPE
11
+ } from "./const.js";
12
+ import { hideCursorPreview } from "./cursor-preview.js";
13
+ import { EditorCircle } from "./tools/circle.js";
14
+ import { EditorFreeHand } from "./tools/freehand.js";
15
+ import { EditorFreeHighlight } from "./tools/free-highlight.js";
16
+ import { EditorFreeText } from "./tools/free-text.js";
17
+ import { EditorHighLight } from "./tools/highlight.js";
18
+ import { EditorRectangle } from "./tools/rectangle.js";
19
+ import { EditorSignature } from "./tools/signature.js";
20
+ import { EditorStamp } from "./tools/stamp.js";
21
+ import { EditorNote } from "./tools/note.js";
22
+ import { EditorArrow } from "./tools/arrow.js";
23
+ import { EditorCloud } from "./tools/cloud.js";
24
+ import { Selector } from "./editor/selector.js";
25
+ import { Store } from "./store.js";
26
+ export class Painter {
27
+ userName;
28
+ konvaCanvasStore = /* @__PURE__ */ new Map();
29
+ editorStore = /* @__PURE__ */ new Map();
30
+ currentAnnotation = null;
31
+ store;
32
+ selector;
33
+ tempDataTransfer = null;
34
+ callbacks;
35
+ getStylusModeEnabled;
36
+ constructor({
37
+ userName,
38
+ callbacks,
39
+ getStylusModeEnabled
40
+ }) {
41
+ this.userName = userName;
42
+ this.callbacks = callbacks;
43
+ this.getStylusModeEnabled = getStylusModeEnabled;
44
+ this.store = new Store();
45
+ this.selector = new Selector({
46
+ konvaCanvasStore: this.konvaCanvasStore,
47
+ getAnnotationStore: (id) => this.store.annotation(id),
48
+ onSelected: (id, isClick, transformerRect) => {
49
+ const ann = this.store.annotation(id);
50
+ if (ann) {
51
+ const viewportRect = this.toViewportRect(ann.pageNumber, transformerRect);
52
+ this.callbacks.onAnnotationSelected(ann, isClick, viewportRect);
53
+ }
54
+ },
55
+ onMultiSelected: (ids, combinedRect) => {
56
+ const anns = ids.map((id) => this.store.annotation(id)).filter((a) => !!a);
57
+ if (anns.length > 0) {
58
+ const viewportRect = this.toViewportRect(anns[0].pageNumber, combinedRect);
59
+ this.callbacks.onMultiSelected(anns, viewportRect);
60
+ }
61
+ },
62
+ onChanged: async (id, groupString, _rawAnnotationStore, konvaClientRect, transformerRect) => {
63
+ const editor = this.findEditorForGroupId(id);
64
+ if (editor) {
65
+ this.updateStore(id, { konvaString: groupString, konvaClientRect });
66
+ }
67
+ const ann = this.store.annotation(id);
68
+ if (ann) {
69
+ const viewportRect = this.toViewportRect(ann.pageNumber, transformerRect);
70
+ this.callbacks.onAnnotationChanged(ann, viewportRect);
71
+ }
72
+ },
73
+ onCancel: () => {
74
+ this.callbacks.onAnnotationChanging();
75
+ },
76
+ onDelete: (id) => {
77
+ this.deleteAnnotation(id, true);
78
+ },
79
+ onRequestDeleteConfirm: callbacks.onRequestDeleteConfirm,
80
+ onFreeTextDoubleClick: (id) => {
81
+ void this.editFreeText(id);
82
+ },
83
+ getStylusModeEnabled: this.getStylusModeEnabled
84
+ });
85
+ this.bindGlobalEvents();
86
+ }
87
+ async editFreeText(id) {
88
+ const ann = this.store.annotation(id);
89
+ if (!ann) return;
90
+ const result = await this.callbacks.onRequestTextInput({
91
+ defaultColor: ann.color ?? "#ff0000",
92
+ defaultFontSize: ann.fontSize ?? 18,
93
+ initialText: ann.contentsObj?.text ?? ""
94
+ });
95
+ if (result.cancelled) return;
96
+ const editor = this.findEditorForGroupId(id);
97
+ if (editor instanceof EditorFreeText) {
98
+ editor.applyTextContent(id, result.inputValue, result.color, result.fontSize);
99
+ const newTransformerRect = this.selector.refreshCurrentSelection();
100
+ if (newTransformerRect) {
101
+ const updatedAnn = this.store.annotation(id);
102
+ if (updatedAnn) {
103
+ const viewportRect = this.toViewportRect(updatedAnn.pageNumber, newTransformerRect);
104
+ this.callbacks.onAnnotationSelected(updatedAnn, false, viewportRect);
105
+ }
106
+ }
107
+ }
108
+ }
109
+ bindGlobalEvents() {
110
+ window.addEventListener("keyup", this.globalKeyUpHandler);
111
+ }
112
+ globalKeyUpHandler = (e) => {
113
+ if (e.code === "Escape" && this.currentAnnotation && (this.currentAnnotation.type === AnnotationType.SIGNATURE || this.currentAnnotation.type === AnnotationType.STAMP)) {
114
+ hideCursorPreview();
115
+ this.callbacks.forceReset();
116
+ }
117
+ };
118
+ createKonvaStage(container, width, height, scale) {
119
+ const stage = new Konva.Stage({
120
+ container,
121
+ width: Math.floor(width * scale),
122
+ height: Math.floor(height * scale),
123
+ scale: { x: scale, y: scale }
124
+ });
125
+ const backgroundLayer = new Konva.Layer();
126
+ stage.add(backgroundLayer);
127
+ if (this.getStylusModeEnabled?.()) {
128
+ this.applyStylusTouchAction(stage);
129
+ }
130
+ return stage;
131
+ }
132
+ applyStylusTouchAction(stage) {
133
+ stage.preventDefault(false);
134
+ Konva.pixelRatio = 1;
135
+ const content = stage.content;
136
+ if (content) content.style.touchAction = "pan-x pan-y";
137
+ stage.container().querySelectorAll("canvas").forEach((c) => {
138
+ c.style.touchAction = "pan-x pan-y";
139
+ });
140
+ }
141
+ removeStylusTouchAction(stage) {
142
+ stage.preventDefault(true);
143
+ Konva.pixelRatio = window.devicePixelRatio || 1;
144
+ const content = stage.content;
145
+ if (content) content.style.touchAction = "";
146
+ stage.container().querySelectorAll("canvas").forEach((c) => {
147
+ c.style.touchAction = "";
148
+ });
149
+ }
150
+ saveToStore(annotationStore, isOriginal = false) {
151
+ const currentAnnotation = annotationDefinitions.find(
152
+ (item) => item.pdfjsAnnotationType === annotationStore.pdfjsType
153
+ );
154
+ this.callbacks.onStoreAdd(
155
+ this.store.save(annotationStore, isOriginal),
156
+ isOriginal,
157
+ currentAnnotation
158
+ );
159
+ }
160
+ updateStore(id, updates) {
161
+ const updated = this.store.update(id, updates);
162
+ if (updated) this.callbacks.onAnnotationChange(updated);
163
+ }
164
+ /**
165
+ * Convert a Konva stage-relative rect to viewport-relative coordinates
166
+ * by combining with the stage container's DOM position.
167
+ */
168
+ toViewportRect(pageNumber, stageRect) {
169
+ const canvas = this.konvaCanvasStore.get(pageNumber);
170
+ if (!canvas) return stageRect;
171
+ const containerBounds = canvas.wrapper.getBoundingClientRect();
172
+ return {
173
+ x: containerBounds.left + stageRect.x,
174
+ y: containerBounds.top + stageRect.y,
175
+ width: stageRect.width,
176
+ height: stageRect.height
177
+ };
178
+ }
179
+ parseStampData() {
180
+ const raw = this.tempDataTransfer;
181
+ if (!raw) return { url: null };
182
+ try {
183
+ const parsed = JSON.parse(raw);
184
+ if (parsed && typeof parsed === "object" && parsed.url) {
185
+ return { url: parsed.url, width: parsed.width, height: parsed.height };
186
+ }
187
+ } catch {
188
+ }
189
+ return { url: raw };
190
+ }
191
+ findEditorForGroupId(groupId) {
192
+ let editor = null;
193
+ this.editorStore.forEach((_editor) => {
194
+ if (_editor.shapeGroupStore?.has(groupId)) {
195
+ editor = _editor;
196
+ }
197
+ });
198
+ return editor;
199
+ }
200
+ findEditor(pageNumber, editorType) {
201
+ return this.editorStore.get(`${pageNumber}_${editorType}`);
202
+ }
203
+ enableEditor({
204
+ konvaStage,
205
+ pageNumber,
206
+ annotation
207
+ }) {
208
+ const storeEditor = this.findEditor(pageNumber, annotation.type);
209
+ if (storeEditor) {
210
+ if (storeEditor instanceof EditorSignature) {
211
+ storeEditor.activateWithSignature(
212
+ konvaStage,
213
+ annotation,
214
+ this.tempDataTransfer
215
+ );
216
+ return;
217
+ }
218
+ if (storeEditor instanceof EditorStamp) {
219
+ const stampData = this.parseStampData();
220
+ storeEditor.activateWithStamp(
221
+ konvaStage,
222
+ annotation,
223
+ stampData.url,
224
+ stampData.width,
225
+ stampData.height
226
+ );
227
+ return;
228
+ }
229
+ storeEditor.activate(konvaStage, annotation);
230
+ return;
231
+ }
232
+ const editorOpts = {
233
+ userName: this.userName,
234
+ konvaStage,
235
+ pageNumber,
236
+ annotation,
237
+ onAdd: (annotationStore) => {
238
+ this.saveToStore(annotationStore);
239
+ },
240
+ onChange: (id, updates) => {
241
+ this.updateStore(id, updates);
242
+ },
243
+ getStylusModeEnabled: this.getStylusModeEnabled
244
+ };
245
+ let editor = null;
246
+ switch (annotation.type) {
247
+ case AnnotationType.FREETEXT:
248
+ editor = new EditorFreeText({
249
+ ...editorOpts,
250
+ onRequestTextInput: this.callbacks.onRequestTextInput
251
+ });
252
+ break;
253
+ case AnnotationType.RECTANGLE:
254
+ editor = new EditorRectangle(editorOpts);
255
+ break;
256
+ case AnnotationType.ARROW:
257
+ editor = new EditorArrow(editorOpts);
258
+ break;
259
+ case AnnotationType.CLOUD:
260
+ editor = new EditorCloud(editorOpts);
261
+ break;
262
+ case AnnotationType.CIRCLE:
263
+ editor = new EditorCircle(editorOpts);
264
+ break;
265
+ case AnnotationType.NOTE:
266
+ editor = new EditorNote(editorOpts);
267
+ break;
268
+ case AnnotationType.FREEHAND:
269
+ editor = new EditorFreeHand(editorOpts);
270
+ break;
271
+ case AnnotationType.FREE_HIGHLIGHT:
272
+ editor = new EditorFreeHighlight(editorOpts);
273
+ break;
274
+ case AnnotationType.SIGNATURE:
275
+ editor = new EditorSignature(editorOpts, this.tempDataTransfer);
276
+ break;
277
+ case AnnotationType.STAMP: {
278
+ const stampData = this.parseStampData();
279
+ editor = new EditorStamp(editorOpts, stampData.url);
280
+ if (editor instanceof EditorStamp && (stampData.width || stampData.height)) {
281
+ editor.activateWithStamp(
282
+ konvaStage,
283
+ annotation,
284
+ stampData.url,
285
+ stampData.width,
286
+ stampData.height
287
+ );
288
+ }
289
+ break;
290
+ }
291
+ case AnnotationType.HIGHLIGHT:
292
+ case AnnotationType.UNDERLINE:
293
+ case AnnotationType.STRIKEOUT:
294
+ editor = new EditorHighLight(editorOpts, annotation.type);
295
+ break;
296
+ case AnnotationType.SELECT:
297
+ this.selector.activate(pageNumber);
298
+ break;
299
+ default:
300
+ console.warn(`Unimplemented annotation type: ${annotation.type}`);
301
+ return;
302
+ }
303
+ if (editor) {
304
+ this.editorStore.set(editor.id, editor);
305
+ }
306
+ }
307
+ enablePainting() {
308
+ this.konvaCanvasStore.forEach(({ konvaStage, pageNumber }) => {
309
+ if (this.currentAnnotation) {
310
+ this.enableEditor({
311
+ konvaStage,
312
+ pageNumber,
313
+ annotation: this.currentAnnotation
314
+ });
315
+ }
316
+ });
317
+ }
318
+ reDrawAnnotation(pageNumber) {
319
+ const annotationStores = this.store.getByPage(pageNumber);
320
+ annotationStores.forEach((annotationStore) => this.drawAnnotation(annotationStore));
321
+ }
322
+ drawAnnotation(annotationStore) {
323
+ const konvaCanvasStore = this.konvaCanvasStore.get(annotationStore.pageNumber);
324
+ if (!konvaCanvasStore) return;
325
+ let storeEditor = this.findEditor(annotationStore.pageNumber, annotationStore.type);
326
+ if (!storeEditor) {
327
+ const annotationDefinition = annotationDefinitions.find(
328
+ (item) => item.type === annotationStore.type
329
+ );
330
+ if (annotationDefinition) {
331
+ this.enableEditor({
332
+ konvaStage: konvaCanvasStore.konvaStage,
333
+ pageNumber: annotationStore.pageNumber,
334
+ annotation: annotationDefinition
335
+ });
336
+ storeEditor = this.findEditor(annotationStore.pageNumber, annotationStore.type);
337
+ }
338
+ }
339
+ if (storeEditor) {
340
+ storeEditor.addSerializedGroupToLayer(
341
+ konvaCanvasStore.konvaStage,
342
+ annotationStore.konvaString
343
+ );
344
+ }
345
+ }
346
+ deleteAnnotation(id, emit = false) {
347
+ const annotationStore = this.store.annotation(id);
348
+ if (!annotationStore) return;
349
+ const konvaCanvasStore = this.konvaCanvasStore.get(
350
+ annotationStore.pageNumber
351
+ );
352
+ this.store.delete(id);
353
+ if (konvaCanvasStore) {
354
+ const storeEditor = this.findEditor(
355
+ annotationStore.pageNumber,
356
+ annotationStore.type
357
+ );
358
+ if (storeEditor) {
359
+ storeEditor.deleteGroup(id, konvaCanvasStore.konvaStage);
360
+ }
361
+ }
362
+ if (emit) {
363
+ this.callbacks.onStoreDelete(id);
364
+ }
365
+ }
366
+ setMode(mode) {
367
+ const isPainting = mode === "painting";
368
+ document.body.classList.toggle(PAINTER_IS_PAINTING_STYLE, isPainting);
369
+ const allAnnotationClasses = Object.values(AnnotationType).filter((type) => typeof type === "number").map((type) => `${PAINTER_PAINTING_TYPE}_${type}`);
370
+ allAnnotationClasses.forEach(
371
+ (cls) => document.body.classList.remove(cls)
372
+ );
373
+ hideCursorPreview();
374
+ if (this.currentAnnotation) {
375
+ document.body.classList.add(
376
+ `${PAINTER_PAINTING_TYPE}_${this.currentAnnotation.type}`
377
+ );
378
+ }
379
+ }
380
+ disablePainting() {
381
+ this.setMode("default");
382
+ this.clearTempDataTransfer();
383
+ this.selector.clear();
384
+ }
385
+ /**
386
+ * Toggle hit detection on annotation layers.
387
+ * When a drawing tool is active, disable listening on the entire layer
388
+ * so Konva skips the expensive hit canvas redraw on every touchmove.
389
+ * Re-enable when switching to select/hand mode where clicks need to work.
390
+ */
391
+ setShapeGroupsListening(listening) {
392
+ this.konvaCanvasStore.forEach(({ konvaStage }) => {
393
+ const layer = konvaStage.getLayers()[0];
394
+ if (!layer) return;
395
+ layer.listening(listening);
396
+ });
397
+ }
398
+ saveTempDataTransfer(data) {
399
+ this.tempDataTransfer = data ?? null;
400
+ }
401
+ clearTempDataTransfer() {
402
+ this.tempDataTransfer = null;
403
+ }
404
+ /**
405
+ * Programmatic imports/additions (replace/merge/redo) add Konva groups
406
+ * directly, so selector handlers need to be rebound when hand/select mode
407
+ * is currently active.
408
+ */
409
+ refreshSelectorForPages(pageNumbers) {
410
+ if (this.currentAnnotation?.type !== AnnotationType.SELECT) return;
411
+ for (const pageNumber of pageNumbers) {
412
+ this.selector.activate(pageNumber);
413
+ }
414
+ }
415
+ // --- Public API ---
416
+ refreshStylusMode() {
417
+ const enabled = this.getStylusModeEnabled?.() ?? false;
418
+ this.konvaCanvasStore.forEach(({ konvaStage }) => {
419
+ if (enabled) {
420
+ this.applyStylusTouchAction(konvaStage);
421
+ } else {
422
+ this.removeStylusTouchAction(konvaStage);
423
+ }
424
+ });
425
+ this.editorStore.forEach((editor) => {
426
+ editor.rebindEvents();
427
+ });
428
+ }
429
+ initCanvasForPage(pageNumber, container, width, height, scale) {
430
+ const konvaStage = this.createKonvaStage(container, width, height, scale);
431
+ this.konvaCanvasStore.set(pageNumber, {
432
+ pageNumber,
433
+ konvaStage,
434
+ wrapper: container,
435
+ isActive: false
436
+ });
437
+ this.reDrawAnnotation(pageNumber);
438
+ this.enablePainting();
439
+ }
440
+ destroyCanvasForPage(pageNumber) {
441
+ const konvaCanvas = this.konvaCanvasStore.get(pageNumber);
442
+ if (konvaCanvas) {
443
+ konvaCanvas.konvaStage.destroy();
444
+ this.konvaCanvasStore.delete(pageNumber);
445
+ }
446
+ }
447
+ activate(annotation, dataTransfer) {
448
+ this.currentAnnotation = annotation;
449
+ this.disablePainting();
450
+ this.saveTempDataTransfer(dataTransfer);
451
+ if (!annotation) return;
452
+ const isDrawingTool = annotation.type !== AnnotationType.SELECT;
453
+ this.setShapeGroupsListening(!isDrawingTool);
454
+ switch (annotation.type) {
455
+ case AnnotationType.FREETEXT:
456
+ case AnnotationType.RECTANGLE:
457
+ case AnnotationType.CIRCLE:
458
+ case AnnotationType.FREEHAND:
459
+ case AnnotationType.FREE_HIGHLIGHT:
460
+ case AnnotationType.SIGNATURE:
461
+ case AnnotationType.STAMP:
462
+ case AnnotationType.SELECT:
463
+ case AnnotationType.NOTE:
464
+ case AnnotationType.ARROW:
465
+ case AnnotationType.CLOUD:
466
+ this.setMode("painting");
467
+ break;
468
+ default:
469
+ this.setMode("default");
470
+ break;
471
+ }
472
+ this.enablePainting();
473
+ }
474
+ activateMarquee() {
475
+ this.currentAnnotation = null;
476
+ this.disablePainting();
477
+ this.konvaCanvasStore.forEach(({ pageNumber }) => {
478
+ this.selector.activateMarquee(pageNumber);
479
+ });
480
+ }
481
+ activateEraser() {
482
+ this.currentAnnotation = null;
483
+ this.disablePainting();
484
+ this.konvaCanvasStore.forEach(({ pageNumber }) => {
485
+ this.selector.activateEraser(pageNumber);
486
+ });
487
+ }
488
+ highlightRange(range, annotation) {
489
+ this.currentAnnotation = annotation;
490
+ }
491
+ selectAnnotation(id) {
492
+ this.selector.select(id);
493
+ this.callbacks.setDefaultMode();
494
+ }
495
+ async initAnnotations(annotations) {
496
+ annotations.forEach((annotation) => {
497
+ this.saveToStore(annotation, true);
498
+ });
499
+ }
500
+ update(id, updates) {
501
+ this.store.update(id, updates);
502
+ }
503
+ delete(id, emit = false) {
504
+ this.selector.delete();
505
+ this.deleteAnnotation(id, emit);
506
+ }
507
+ updateAnnotationStyle(annotationStore, style) {
508
+ const editor = this.findEditorForGroupId(annotationStore.id);
509
+ if (editor) {
510
+ editor.updateStyle(annotationStore, style);
511
+ const newTransformerRect = this.selector.refreshCurrentSelection();
512
+ if (newTransformerRect) {
513
+ const ann = this.store.annotation(annotationStore.id);
514
+ if (ann) {
515
+ const viewportRect = this.toViewportRect(ann.pageNumber, newTransformerRect);
516
+ this.callbacks.onAnnotationSelected(ann, false, viewportRect);
517
+ }
518
+ }
519
+ }
520
+ }
521
+ reAddAnnotation(annotation) {
522
+ this.store.save(annotation, false);
523
+ const konvaCanvas = this.konvaCanvasStore.get(annotation.pageNumber);
524
+ if (!konvaCanvas) return;
525
+ let editor = this.findEditor(annotation.pageNumber, annotation.type);
526
+ if (!editor) {
527
+ const def = annotationDefinitions.find((d) => d.type === annotation.type);
528
+ if (def) {
529
+ this.enableEditor({
530
+ konvaStage: konvaCanvas.konvaStage,
531
+ pageNumber: annotation.pageNumber,
532
+ annotation: def
533
+ });
534
+ editor = this.findEditor(annotation.pageNumber, annotation.type);
535
+ }
536
+ }
537
+ if (editor) {
538
+ editor.addSerializedGroupToLayer(konvaCanvas.konvaStage, annotation.konvaString);
539
+ this.refreshSelectorForPages([annotation.pageNumber]);
540
+ }
541
+ }
542
+ getData() {
543
+ return this.store.annotations;
544
+ }
545
+ getAnnotation(id) {
546
+ return this.store.annotation(id);
547
+ }
548
+ clearAllAnnotations(emit = false) {
549
+ this.selector.clear();
550
+ const ids = this.store.annotations.map((annotation) => annotation.id);
551
+ ids.forEach((id) => this.deleteAnnotation(id, emit));
552
+ }
553
+ replaceAnnotations(annotations) {
554
+ this.clearAllAnnotations(false);
555
+ const touchedPages = /* @__PURE__ */ new Set();
556
+ annotations.forEach((annotation) => {
557
+ this.saveToStore(annotation, true);
558
+ this.drawAnnotation(annotation);
559
+ touchedPages.add(annotation.pageNumber);
560
+ });
561
+ this.refreshSelectorForPages(touchedPages);
562
+ }
563
+ mergeAnnotations(annotations) {
564
+ const touchedPages = /* @__PURE__ */ new Set();
565
+ annotations.forEach((annotation) => {
566
+ if (this.store.annotation(annotation.id)) {
567
+ this.deleteAnnotation(annotation.id, false);
568
+ }
569
+ this.saveToStore(annotation, true);
570
+ this.drawAnnotation(annotation);
571
+ touchedPages.add(annotation.pageNumber);
572
+ });
573
+ this.refreshSelectorForPages(touchedPages);
574
+ }
575
+ getCanvasState() {
576
+ const state = {};
577
+ this.konvaCanvasStore.forEach(({ pageNumber, konvaStage }) => {
578
+ state[pageNumber] = konvaStage.toJSON();
579
+ });
580
+ return state;
581
+ }
582
+ destroy() {
583
+ window.removeEventListener("keyup", this.globalKeyUpHandler);
584
+ this.selector.destroy();
585
+ this.konvaCanvasStore.forEach((canvas) => {
586
+ canvas.konvaStage.destroy();
587
+ });
588
+ this.konvaCanvasStore.clear();
589
+ this.editorStore.clear();
590
+ }
591
+ }
@@ -0,0 +1,11 @@
1
+ import type { IAnnotationStore } from './types.js';
2
+ export declare class Store {
3
+ private annotationStore;
4
+ private originalAnnotationStore;
5
+ get annotation(): (id: string) => IAnnotationStore | undefined;
6
+ get annotations(): IAnnotationStore[];
7
+ save(store: IAnnotationStore, isOriginal: boolean): IAnnotationStore;
8
+ update(id: string, updates: Partial<IAnnotationStore>): IAnnotationStore | null;
9
+ getByPage(pageNumber: number): IAnnotationStore[];
10
+ delete(id: string): void;
11
+ }
@@ -0,0 +1,47 @@
1
+ import { formatTimestamp } from "./utils.js";
2
+ export class Store {
3
+ annotationStore = /* @__PURE__ */ new Map();
4
+ originalAnnotationStore = /* @__PURE__ */ new Map();
5
+ get annotation() {
6
+ return (id) => this.annotationStore.get(id);
7
+ }
8
+ get annotations() {
9
+ return Array.from(this.annotationStore.values());
10
+ }
11
+ save(store, isOriginal) {
12
+ this.annotationStore.set(store.id, store);
13
+ if (isOriginal) {
14
+ this.originalAnnotationStore.set(store.id, store);
15
+ }
16
+ return store;
17
+ }
18
+ update(id, updates) {
19
+ if (this.annotationStore.has(id)) {
20
+ const existingAnnotation = this.annotationStore.get(id);
21
+ if (existingAnnotation) {
22
+ const updatedAnnotation = {
23
+ ...existingAnnotation,
24
+ ...updates,
25
+ date: formatTimestamp(Date.now())
26
+ };
27
+ this.annotationStore.set(id, updatedAnnotation);
28
+ return updatedAnnotation;
29
+ }
30
+ } else {
31
+ console.warn(`Annotation with id ${id} not found.`);
32
+ }
33
+ return null;
34
+ }
35
+ getByPage(pageNumber) {
36
+ return Array.from(this.annotationStore.values()).filter(
37
+ (annotation) => annotation.pageNumber === pageNumber
38
+ );
39
+ }
40
+ delete(id) {
41
+ if (this.annotationStore.has(id)) {
42
+ this.annotationStore.delete(id);
43
+ } else {
44
+ console.warn(`Annotation with id ${id} not found.`);
45
+ }
46
+ }
47
+ }
@@ -0,0 +1,22 @@
1
+ import type { KonvaEventObject } from 'konva/lib/Node';
2
+ import type { IAnnotationStore, IAnnotationStyle } from '../types.js';
3
+ import { Editor, type IEditorOptions } from '../editor/editor.js';
4
+ /**
5
+ * Arrow editor class that extends the base Editor and draws arrowed lines on the canvas.
6
+ */
7
+ export declare class EditorArrow extends Editor {
8
+ private arrow;
9
+ private startPoint;
10
+ constructor(EditorOptions: IEditorOptions);
11
+ protected mouseDownHandler(e: KonvaEventObject<MouseEvent | TouchEvent>): void;
12
+ protected mouseMoveHandler(e: KonvaEventObject<MouseEvent | TouchEvent>): void;
13
+ protected mouseUpHandler(): void;
14
+ private globalPointerUpHandler;
15
+ private isTooShort;
16
+ /**
17
+ * @description Update annotation styles
18
+ * @param annotationStore
19
+ * @param styles
20
+ */
21
+ protected changeStyle(annotationStore: IAnnotationStore, styles: IAnnotationStyle): void;
22
+ }