@godscene/shared 1.7.11

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 (236) hide show
  1. package/README.md +9 -0
  2. package/dist/es/baseDB.mjs +109 -0
  3. package/dist/es/cli/cli-args.mjs +95 -0
  4. package/dist/es/cli/cli-error.mjs +24 -0
  5. package/dist/es/cli/cli-runner.mjs +122 -0
  6. package/dist/es/cli/index.mjs +4 -0
  7. package/dist/es/common.mjs +37 -0
  8. package/dist/es/constants/example-code.mjs +227 -0
  9. package/dist/es/constants/index.mjs +124 -0
  10. package/dist/es/env/basic.mjs +6 -0
  11. package/dist/es/env/constants.mjs +110 -0
  12. package/dist/es/env/global-config-manager.mjs +94 -0
  13. package/dist/es/env/helper.mjs +43 -0
  14. package/dist/es/env/index.mjs +5 -0
  15. package/dist/es/env/init-debug.mjs +18 -0
  16. package/dist/es/env/model-config-manager.mjs +79 -0
  17. package/dist/es/env/parse-model-config.mjs +165 -0
  18. package/dist/es/env/types.mjs +232 -0
  19. package/dist/es/env/utils.mjs +18 -0
  20. package/dist/es/extractor/constants.mjs +2 -0
  21. package/dist/es/extractor/cs_postmessage.mjs +61 -0
  22. package/dist/es/extractor/customLocator.mjs +641 -0
  23. package/dist/es/extractor/debug.mjs +6 -0
  24. package/dist/es/extractor/dom-util.mjs +96 -0
  25. package/dist/es/extractor/index.mjs +5 -0
  26. package/dist/es/extractor/locator.mjs +250 -0
  27. package/dist/es/extractor/tree.mjs +78 -0
  28. package/dist/es/extractor/util.mjs +245 -0
  29. package/dist/es/extractor/web-extractor.mjs +393 -0
  30. package/dist/es/img/box-select.mjs +824 -0
  31. package/dist/es/img/canvas-fallback.mjs +238 -0
  32. package/dist/es/img/get-photon.mjs +45 -0
  33. package/dist/es/img/get-sharp.mjs +11 -0
  34. package/dist/es/img/index.mjs +4 -0
  35. package/dist/es/img/info.mjs +35 -0
  36. package/dist/es/img/transform.mjs +275 -0
  37. package/dist/es/index.mjs +2 -0
  38. package/dist/es/key-alias-utils.mjs +19 -0
  39. package/dist/es/logger.mjs +64 -0
  40. package/dist/es/mcp/base-server.mjs +282 -0
  41. package/dist/es/mcp/base-tools.mjs +159 -0
  42. package/dist/es/mcp/chrome-path.mjs +35 -0
  43. package/dist/es/mcp/cli-report-session.mjs +78 -0
  44. package/dist/es/mcp/error-formatter.mjs +19 -0
  45. package/dist/es/mcp/index.mjs +9 -0
  46. package/dist/es/mcp/init-arg-utils.mjs +38 -0
  47. package/dist/es/mcp/inject-report-html-plugin.mjs +53 -0
  48. package/dist/es/mcp/launcher-helper.mjs +52 -0
  49. package/dist/es/mcp/tool-generator.mjs +419 -0
  50. package/dist/es/mcp/types.mjs +3 -0
  51. package/dist/es/node/fs.mjs +44 -0
  52. package/dist/es/node/index.mjs +2 -0
  53. package/dist/es/node/port.mjs +24 -0
  54. package/dist/es/polyfills/async-hooks.mjs +2 -0
  55. package/dist/es/polyfills/index.mjs +1 -0
  56. package/dist/es/types/index.mjs +3 -0
  57. package/dist/es/us-keyboard-layout.mjs +1414 -0
  58. package/dist/es/us-keyboard-layout.mjs.LICENSE.txt +5 -0
  59. package/dist/es/utils.mjs +72 -0
  60. package/dist/es/zod-schema-utils.mjs +54 -0
  61. package/dist/lib/baseDB.js +149 -0
  62. package/dist/lib/cli/cli-args.js +138 -0
  63. package/dist/lib/cli/cli-error.js +61 -0
  64. package/dist/lib/cli/cli-runner.js +181 -0
  65. package/dist/lib/cli/index.js +53 -0
  66. package/dist/lib/common.js +93 -0
  67. package/dist/lib/constants/example-code.js +264 -0
  68. package/dist/lib/constants/index.js +221 -0
  69. package/dist/lib/env/basic.js +40 -0
  70. package/dist/lib/env/constants.js +153 -0
  71. package/dist/lib/env/global-config-manager.js +128 -0
  72. package/dist/lib/env/helper.js +80 -0
  73. package/dist/lib/env/index.js +90 -0
  74. package/dist/lib/env/init-debug.js +52 -0
  75. package/dist/lib/env/model-config-manager.js +113 -0
  76. package/dist/lib/env/parse-model-config.js +211 -0
  77. package/dist/lib/env/types.js +572 -0
  78. package/dist/lib/env/utils.js +61 -0
  79. package/dist/lib/extractor/constants.js +42 -0
  80. package/dist/lib/extractor/cs_postmessage.js +98 -0
  81. package/dist/lib/extractor/customLocator.js +693 -0
  82. package/dist/lib/extractor/debug.js +12 -0
  83. package/dist/lib/extractor/dom-util.js +157 -0
  84. package/dist/lib/extractor/index.js +87 -0
  85. package/dist/lib/extractor/locator.js +296 -0
  86. package/dist/lib/extractor/tree.js +124 -0
  87. package/dist/lib/extractor/util.js +336 -0
  88. package/dist/lib/extractor/web-extractor.js +442 -0
  89. package/dist/lib/img/box-select.js +875 -0
  90. package/dist/lib/img/canvas-fallback.js +305 -0
  91. package/dist/lib/img/get-photon.js +82 -0
  92. package/dist/lib/img/get-sharp.js +45 -0
  93. package/dist/lib/img/index.js +95 -0
  94. package/dist/lib/img/info.js +92 -0
  95. package/dist/lib/img/transform.js +364 -0
  96. package/dist/lib/index.js +36 -0
  97. package/dist/lib/key-alias-utils.js +62 -0
  98. package/dist/lib/logger.js +114 -0
  99. package/dist/lib/mcp/base-server.js +332 -0
  100. package/dist/lib/mcp/base-tools.js +193 -0
  101. package/dist/lib/mcp/chrome-path.js +72 -0
  102. package/dist/lib/mcp/cli-report-session.js +121 -0
  103. package/dist/lib/mcp/error-formatter.js +53 -0
  104. package/dist/lib/mcp/index.js +114 -0
  105. package/dist/lib/mcp/init-arg-utils.js +78 -0
  106. package/dist/lib/mcp/inject-report-html-plugin.js +98 -0
  107. package/dist/lib/mcp/launcher-helper.js +86 -0
  108. package/dist/lib/mcp/tool-generator.js +456 -0
  109. package/dist/lib/mcp/types.js +40 -0
  110. package/dist/lib/node/fs.js +97 -0
  111. package/dist/lib/node/index.js +65 -0
  112. package/dist/lib/node/port.js +61 -0
  113. package/dist/lib/polyfills/async-hooks.js +36 -0
  114. package/dist/lib/polyfills/index.js +58 -0
  115. package/dist/lib/types/index.js +37 -0
  116. package/dist/lib/us-keyboard-layout.js +1457 -0
  117. package/dist/lib/us-keyboard-layout.js.LICENSE.txt +5 -0
  118. package/dist/lib/utils.js +148 -0
  119. package/dist/lib/zod-schema-utils.js +97 -0
  120. package/dist/types/baseDB.d.ts +25 -0
  121. package/dist/types/cli/cli-args.d.ts +8 -0
  122. package/dist/types/cli/cli-error.d.ts +5 -0
  123. package/dist/types/cli/cli-runner.d.ts +19 -0
  124. package/dist/types/cli/index.d.ts +4 -0
  125. package/dist/types/common.d.ts +12 -0
  126. package/dist/types/constants/example-code.d.ts +2 -0
  127. package/dist/types/constants/index.d.ts +61 -0
  128. package/dist/types/env/basic.d.ts +6 -0
  129. package/dist/types/env/constants.d.ts +50 -0
  130. package/dist/types/env/global-config-manager.d.ts +32 -0
  131. package/dist/types/env/helper.d.ts +4 -0
  132. package/dist/types/env/index.d.ts +4 -0
  133. package/dist/types/env/init-debug.d.ts +1 -0
  134. package/dist/types/env/model-config-manager.d.ts +25 -0
  135. package/dist/types/env/parse-model-config.d.ts +31 -0
  136. package/dist/types/env/types.d.ts +339 -0
  137. package/dist/types/env/utils.d.ts +7 -0
  138. package/dist/types/extractor/constants.d.ts +1 -0
  139. package/dist/types/extractor/cs_postmessage.d.ts +2 -0
  140. package/dist/types/extractor/customLocator.d.ts +69 -0
  141. package/dist/types/extractor/debug.d.ts +1 -0
  142. package/dist/types/extractor/dom-util.d.ts +57 -0
  143. package/dist/types/extractor/index.d.ts +33 -0
  144. package/dist/types/extractor/locator.d.ts +9 -0
  145. package/dist/types/extractor/tree.d.ts +6 -0
  146. package/dist/types/extractor/util.d.ts +47 -0
  147. package/dist/types/extractor/web-extractor.d.ts +24 -0
  148. package/dist/types/img/box-select.d.ts +26 -0
  149. package/dist/types/img/canvas-fallback.d.ts +105 -0
  150. package/dist/types/img/get-photon.d.ts +19 -0
  151. package/dist/types/img/get-sharp.d.ts +3 -0
  152. package/dist/types/img/index.d.ts +3 -0
  153. package/dist/types/img/info.d.ts +34 -0
  154. package/dist/types/img/transform.d.ts +98 -0
  155. package/dist/types/index.d.ts +2 -0
  156. package/dist/types/key-alias-utils.d.ts +9 -0
  157. package/dist/types/logger.d.ts +5 -0
  158. package/dist/types/mcp/base-server.d.ts +93 -0
  159. package/dist/types/mcp/base-tools.d.ts +148 -0
  160. package/dist/types/mcp/chrome-path.d.ts +2 -0
  161. package/dist/types/mcp/cli-report-session.d.ts +12 -0
  162. package/dist/types/mcp/error-formatter.d.ts +12 -0
  163. package/dist/types/mcp/index.d.ts +9 -0
  164. package/dist/types/mcp/init-arg-utils.d.ts +13 -0
  165. package/dist/types/mcp/inject-report-html-plugin.d.ts +18 -0
  166. package/dist/types/mcp/launcher-helper.d.ts +94 -0
  167. package/dist/types/mcp/tool-generator.d.ts +10 -0
  168. package/dist/types/mcp/types.d.ts +113 -0
  169. package/dist/types/node/fs.d.ts +15 -0
  170. package/dist/types/node/index.d.ts +2 -0
  171. package/dist/types/node/port.d.ts +8 -0
  172. package/dist/types/polyfills/async-hooks.d.ts +6 -0
  173. package/dist/types/polyfills/index.d.ts +4 -0
  174. package/dist/types/types/index.d.ts +36 -0
  175. package/dist/types/us-keyboard-layout.d.ts +32 -0
  176. package/dist/types/utils.d.ts +34 -0
  177. package/dist/types/zod-schema-utils.d.ts +23 -0
  178. package/package.json +125 -0
  179. package/src/baseDB.ts +158 -0
  180. package/src/cli/cli-args.ts +173 -0
  181. package/src/cli/cli-error.ts +24 -0
  182. package/src/cli/cli-runner.ts +230 -0
  183. package/src/cli/index.ts +4 -0
  184. package/src/common.ts +67 -0
  185. package/src/constants/example-code.ts +227 -0
  186. package/src/constants/index.ts +139 -0
  187. package/src/env/basic.ts +12 -0
  188. package/src/env/constants.ts +303 -0
  189. package/src/env/global-config-manager.ts +191 -0
  190. package/src/env/helper.ts +58 -0
  191. package/src/env/index.ts +4 -0
  192. package/src/env/init-debug.ts +34 -0
  193. package/src/env/model-config-manager.ts +149 -0
  194. package/src/env/parse-model-config.ts +357 -0
  195. package/src/env/types.ts +583 -0
  196. package/src/env/utils.ts +39 -0
  197. package/src/extractor/constants.ts +5 -0
  198. package/src/extractor/cs_postmessage.ts +136 -0
  199. package/src/extractor/customLocator.ts +1245 -0
  200. package/src/extractor/debug.ts +10 -0
  201. package/src/extractor/dom-util.ts +231 -0
  202. package/src/extractor/index.ts +50 -0
  203. package/src/extractor/locator.ts +469 -0
  204. package/src/extractor/tree.ts +179 -0
  205. package/src/extractor/util.ts +482 -0
  206. package/src/extractor/web-extractor.ts +617 -0
  207. package/src/img/box-select.ts +588 -0
  208. package/src/img/canvas-fallback.ts +393 -0
  209. package/src/img/get-photon.ts +108 -0
  210. package/src/img/get-sharp.ts +18 -0
  211. package/src/img/index.ts +27 -0
  212. package/src/img/info.ts +102 -0
  213. package/src/img/transform.ts +553 -0
  214. package/src/index.ts +1 -0
  215. package/src/key-alias-utils.ts +23 -0
  216. package/src/logger.ts +96 -0
  217. package/src/mcp/base-server.ts +500 -0
  218. package/src/mcp/base-tools.ts +391 -0
  219. package/src/mcp/chrome-path.ts +48 -0
  220. package/src/mcp/cli-report-session.ts +130 -0
  221. package/src/mcp/error-formatter.ts +52 -0
  222. package/src/mcp/index.ts +9 -0
  223. package/src/mcp/init-arg-utils.ts +105 -0
  224. package/src/mcp/inject-report-html-plugin.ts +119 -0
  225. package/src/mcp/launcher-helper.ts +200 -0
  226. package/src/mcp/tool-generator.ts +658 -0
  227. package/src/mcp/types.ts +131 -0
  228. package/src/node/fs.ts +84 -0
  229. package/src/node/index.ts +2 -0
  230. package/src/node/port.ts +37 -0
  231. package/src/polyfills/async-hooks.ts +6 -0
  232. package/src/polyfills/index.ts +4 -0
  233. package/src/types/index.ts +54 -0
  234. package/src/us-keyboard-layout.ts +723 -0
  235. package/src/utils.ts +149 -0
  236. package/src/zod-schema-utils.ts +133 -0
@@ -0,0 +1,588 @@
1
+ import assert from 'node:assert';
2
+ import type { PhotonImage as PhotonImageType } from '@silvia-odwyer/photon-node';
3
+ import { NodeType } from '../constants';
4
+ import type { BaseElement, Rect } from '../types';
5
+ import getPhoton from './get-photon';
6
+ import { photonFromBase64, photonToBase64 } from './transform';
7
+
8
+ // Simple 5x7 bitmap font for digits 0-9
9
+ const DIGIT_FONT: Record<string, number[][]> = {
10
+ '0': [
11
+ [0, 1, 1, 1, 0],
12
+ [1, 0, 0, 0, 1],
13
+ [1, 0, 0, 0, 1],
14
+ [1, 0, 0, 0, 1],
15
+ [1, 0, 0, 0, 1],
16
+ [1, 0, 0, 0, 1],
17
+ [0, 1, 1, 1, 0],
18
+ ],
19
+ '1': [
20
+ [0, 0, 1, 0, 0],
21
+ [0, 1, 1, 0, 0],
22
+ [0, 0, 1, 0, 0],
23
+ [0, 0, 1, 0, 0],
24
+ [0, 0, 1, 0, 0],
25
+ [0, 0, 1, 0, 0],
26
+ [0, 1, 1, 1, 0],
27
+ ],
28
+ '2': [
29
+ [0, 1, 1, 1, 0],
30
+ [1, 0, 0, 0, 1],
31
+ [0, 0, 0, 0, 1],
32
+ [0, 0, 1, 1, 0],
33
+ [0, 1, 0, 0, 0],
34
+ [1, 0, 0, 0, 0],
35
+ [1, 1, 1, 1, 1],
36
+ ],
37
+ '3': [
38
+ [0, 1, 1, 1, 0],
39
+ [1, 0, 0, 0, 1],
40
+ [0, 0, 0, 0, 1],
41
+ [0, 0, 1, 1, 0],
42
+ [0, 0, 0, 0, 1],
43
+ [1, 0, 0, 0, 1],
44
+ [0, 1, 1, 1, 0],
45
+ ],
46
+ '4': [
47
+ [0, 0, 0, 1, 0],
48
+ [0, 0, 1, 1, 0],
49
+ [0, 1, 0, 1, 0],
50
+ [1, 0, 0, 1, 0],
51
+ [1, 1, 1, 1, 1],
52
+ [0, 0, 0, 1, 0],
53
+ [0, 0, 0, 1, 0],
54
+ ],
55
+ '5': [
56
+ [1, 1, 1, 1, 1],
57
+ [1, 0, 0, 0, 0],
58
+ [1, 1, 1, 1, 0],
59
+ [0, 0, 0, 0, 1],
60
+ [0, 0, 0, 0, 1],
61
+ [1, 0, 0, 0, 1],
62
+ [0, 1, 1, 1, 0],
63
+ ],
64
+ '6': [
65
+ [0, 1, 1, 1, 0],
66
+ [1, 0, 0, 0, 0],
67
+ [1, 0, 0, 0, 0],
68
+ [1, 1, 1, 1, 0],
69
+ [1, 0, 0, 0, 1],
70
+ [1, 0, 0, 0, 1],
71
+ [0, 1, 1, 1, 0],
72
+ ],
73
+ '7': [
74
+ [1, 1, 1, 1, 1],
75
+ [0, 0, 0, 0, 1],
76
+ [0, 0, 0, 1, 0],
77
+ [0, 0, 1, 0, 0],
78
+ [0, 0, 1, 0, 0],
79
+ [0, 0, 1, 0, 0],
80
+ [0, 0, 1, 0, 0],
81
+ ],
82
+ '8': [
83
+ [0, 1, 1, 1, 0],
84
+ [1, 0, 0, 0, 1],
85
+ [1, 0, 0, 0, 1],
86
+ [0, 1, 1, 1, 0],
87
+ [1, 0, 0, 0, 1],
88
+ [1, 0, 0, 0, 1],
89
+ [0, 1, 1, 1, 0],
90
+ ],
91
+ '9': [
92
+ [0, 1, 1, 1, 0],
93
+ [1, 0, 0, 0, 1],
94
+ [1, 0, 0, 0, 1],
95
+ [0, 1, 1, 1, 1],
96
+ [0, 0, 0, 0, 1],
97
+ [0, 0, 0, 0, 1],
98
+ [0, 1, 1, 1, 0],
99
+ ],
100
+ };
101
+
102
+ const FONT_WIDTH = 5;
103
+ const FONT_HEIGHT = 7;
104
+ const FONT_SCALE = 2; // Scale up for better visibility
105
+
106
+ interface ElementForOverlay {
107
+ rect: Rect;
108
+ indexId?: number;
109
+ }
110
+
111
+ function drawDigit(
112
+ pixels: Uint8Array,
113
+ width: number,
114
+ height: number,
115
+ digit: string,
116
+ startX: number,
117
+ startY: number,
118
+ color: { r: number; g: number; b: number; a: number },
119
+ ) {
120
+ const bitmap = DIGIT_FONT[digit];
121
+ if (!bitmap) return;
122
+
123
+ for (let row = 0; row < FONT_HEIGHT; row++) {
124
+ for (let col = 0; col < FONT_WIDTH; col++) {
125
+ if (bitmap[row][col] === 1) {
126
+ // Scale the pixel
127
+ for (let sy = 0; sy < FONT_SCALE; sy++) {
128
+ for (let sx = 0; sx < FONT_SCALE; sx++) {
129
+ const x = startX + col * FONT_SCALE + sx;
130
+ const y = startY + row * FONT_SCALE + sy;
131
+ if (x >= 0 && x < width && y >= 0 && y < height) {
132
+ const idx = (y * width + x) * 4;
133
+ pixels[idx + 0] = color.r;
134
+ pixels[idx + 1] = color.g;
135
+ pixels[idx + 2] = color.b;
136
+ pixels[idx + 3] = color.a;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ }
144
+
145
+ function drawNumber(
146
+ pixels: Uint8Array,
147
+ width: number,
148
+ height: number,
149
+ num: number,
150
+ startX: number,
151
+ startY: number,
152
+ color: { r: number; g: number; b: number; a: number },
153
+ ) {
154
+ const str = num.toString();
155
+ let x = startX;
156
+ for (const digit of str) {
157
+ drawDigit(pixels, width, height, digit, x, startY, color);
158
+ x += FONT_WIDTH * FONT_SCALE + 1; // 1px spacing between digits
159
+ }
160
+ }
161
+
162
+ function getNumberWidth(num: number): number {
163
+ return num.toString().length * (FONT_WIDTH * FONT_SCALE + 1) - 1;
164
+ }
165
+
166
+ function drawRect(
167
+ pixels: Uint8Array,
168
+ width: number,
169
+ height: number,
170
+ rect: { x: number; y: number; w: number; h: number },
171
+ color: { r: number; g: number; b: number; a: number },
172
+ thickness: number,
173
+ ) {
174
+ // Round to integers to avoid floating point precision issues
175
+ const x = Math.floor(rect.x);
176
+ const y = Math.floor(rect.y);
177
+ const w = Math.floor(rect.w);
178
+ const h = Math.floor(rect.h);
179
+
180
+ for (let py = y; py < y + h && py < height; py++) {
181
+ for (let px = x; px < x + w && px < width; px++) {
182
+ if (px < 0 || py < 0) continue;
183
+
184
+ // Check if this pixel is on the border
185
+ const isLeftBorder = px >= x && px < x + thickness;
186
+ const isRightBorder = px <= x + w - 1 && px > x + w - thickness - 1;
187
+ const isTopBorder = py >= y && py < y + thickness;
188
+ const isBottomBorder = py <= y + h - 1 && py > y + h - thickness - 1;
189
+
190
+ if (isLeftBorder || isRightBorder || isTopBorder || isBottomBorder) {
191
+ const idx = (py * width + px) * 4;
192
+ pixels[idx + 0] = color.r;
193
+ pixels[idx + 1] = color.g;
194
+ pixels[idx + 2] = color.b;
195
+ pixels[idx + 3] = color.a;
196
+ }
197
+ }
198
+ }
199
+ }
200
+
201
+ function fillRect(
202
+ pixels: Uint8Array,
203
+ width: number,
204
+ height: number,
205
+ rect: { x: number; y: number; w: number; h: number },
206
+ color: { r: number; g: number; b: number; a: number },
207
+ ) {
208
+ // Round to integers to avoid floating point precision issues
209
+ const x = Math.floor(rect.x);
210
+ const y = Math.floor(rect.y);
211
+ const w = Math.floor(rect.w);
212
+ const h = Math.floor(rect.h);
213
+
214
+ for (let py = y; py < y + h && py < height; py++) {
215
+ for (let px = x; px < x + w && px < width; px++) {
216
+ if (px < 0 || py < 0) continue;
217
+ const idx = (py * width + px) * 4;
218
+ pixels[idx + 0] = color.r;
219
+ pixels[idx + 1] = color.g;
220
+ pixels[idx + 2] = color.b;
221
+ pixels[idx + 3] = color.a;
222
+ }
223
+ }
224
+ }
225
+
226
+ function blendPixels(
227
+ basePixels: Uint8Array,
228
+ overlayPixels: Uint8Array,
229
+ width: number,
230
+ height: number,
231
+ ): Uint8Array {
232
+ const result = new Uint8Array(basePixels.length);
233
+ for (let i = 0; i < basePixels.length; i += 4) {
234
+ const overlayAlpha = overlayPixels[i + 3] / 255;
235
+ const baseAlpha = basePixels[i + 3] / 255;
236
+
237
+ if (overlayAlpha === 0) {
238
+ result[i + 0] = basePixels[i + 0];
239
+ result[i + 1] = basePixels[i + 1];
240
+ result[i + 2] = basePixels[i + 2];
241
+ result[i + 3] = basePixels[i + 3];
242
+ } else {
243
+ const outAlpha = overlayAlpha + baseAlpha * (1 - overlayAlpha);
244
+ if (outAlpha === 0) {
245
+ // Both alphas are effectively zero, copy overlay pixel
246
+ result[i + 0] = overlayPixels[i + 0];
247
+ result[i + 1] = overlayPixels[i + 1];
248
+ result[i + 2] = overlayPixels[i + 2];
249
+ result[i + 3] = overlayPixels[i + 3];
250
+ } else {
251
+ result[i + 0] = Math.round(
252
+ (overlayPixels[i + 0] * overlayAlpha +
253
+ basePixels[i + 0] * baseAlpha * (1 - overlayAlpha)) /
254
+ outAlpha,
255
+ );
256
+ result[i + 1] = Math.round(
257
+ (overlayPixels[i + 1] * overlayAlpha +
258
+ basePixels[i + 1] * baseAlpha * (1 - overlayAlpha)) /
259
+ outAlpha,
260
+ );
261
+ result[i + 2] = Math.round(
262
+ (overlayPixels[i + 2] * overlayAlpha +
263
+ basePixels[i + 2] * baseAlpha * (1 - overlayAlpha)) /
264
+ outAlpha,
265
+ );
266
+ result[i + 3] = Math.round(outAlpha * 255);
267
+ }
268
+ }
269
+ }
270
+ return result;
271
+ }
272
+
273
+ const createSvgOverlay = async (
274
+ elements: Array<ElementForOverlay>,
275
+ imageWidth: number,
276
+ imageHeight: number,
277
+ boxPadding = 5,
278
+ borderThickness = 2,
279
+ prompt?: string,
280
+ ): Promise<Uint8Array> => {
281
+ // Create transparent overlay
282
+ const overlayPixels = new Uint8Array(imageWidth * imageHeight * 4);
283
+
284
+ // Define color array
285
+ const colors = [
286
+ {
287
+ rect: { r: 0xc6, g: 0x23, b: 0x00, a: 0xff },
288
+ text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
289
+ }, // red, white
290
+ {
291
+ rect: { r: 0x00, g: 0x00, b: 0xff, a: 0xff },
292
+ text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
293
+ }, // blue, white
294
+ {
295
+ rect: { r: 0x8b, g: 0x45, b: 0x13, a: 0xff },
296
+ text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
297
+ }, // brown, white
298
+ {
299
+ rect: { r: 0x3e, g: 0x7b, b: 0x27, a: 0xff },
300
+ text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
301
+ }, // green, white
302
+ {
303
+ rect: { r: 0x50, g: 0x00, b: 0x73, a: 0xff },
304
+ text: { r: 0xff, g: 0xff, b: 0xff, a: 0xff },
305
+ }, // purple, white
306
+ ];
307
+
308
+ // Draw prompt text if provided
309
+ if (prompt) {
310
+ const promptMargin = 20;
311
+ const promptHeight = 30;
312
+ const promptY = imageHeight - promptHeight - promptMargin;
313
+
314
+ // Draw prompt background (semi-transparent black)
315
+ fillRect(
316
+ overlayPixels,
317
+ imageWidth,
318
+ imageHeight,
319
+ {
320
+ x: 0,
321
+ y: promptY,
322
+ w: imageWidth,
323
+ h: promptHeight,
324
+ },
325
+ { r: 0x00, g: 0x00, b: 0x00, a: 0xcc },
326
+ );
327
+
328
+ // Note: We skip drawing prompt text since we only have digit font
329
+ // The prompt feature was mostly for debugging anyway
330
+ }
331
+
332
+ for (let index = 0; index < elements.length; index++) {
333
+ const element = elements[index];
334
+ const color = colors[index % colors.length];
335
+
336
+ // Add padding to the rect
337
+ const paddedLeft = Math.max(0, element.rect.left - boxPadding);
338
+ const paddedTop = Math.max(0, element.rect.top - boxPadding);
339
+ const paddedWidth = Math.min(
340
+ imageWidth - paddedLeft,
341
+ element.rect.width + boxPadding * 2,
342
+ );
343
+ const paddedHeight = Math.min(
344
+ imageHeight - paddedTop,
345
+ element.rect.height + boxPadding * 2,
346
+ );
347
+ const paddedRect = {
348
+ x: paddedLeft,
349
+ y: paddedTop,
350
+ w: paddedWidth,
351
+ h: paddedHeight,
352
+ };
353
+
354
+ // Draw rectangle border
355
+ drawRect(
356
+ overlayPixels,
357
+ imageWidth,
358
+ imageHeight,
359
+ paddedRect,
360
+ color.rect,
361
+ borderThickness,
362
+ );
363
+
364
+ // Calculate text position
365
+ const indexId = element.indexId;
366
+ if (typeof indexId !== 'number') {
367
+ continue;
368
+ }
369
+
370
+ const textWidth = getNumberWidth(indexId);
371
+ const textHeight = FONT_HEIGHT * FONT_SCALE;
372
+ const rectWidth = textWidth + 5;
373
+ const rectHeight = textHeight + 4;
374
+ let rectX = paddedLeft - rectWidth;
375
+ let rectY =
376
+ paddedTop + Math.floor(paddedHeight / 2) - Math.floor(textHeight / 2) - 2;
377
+
378
+ // Check if this new position overlaps with any existing boxes
379
+ const checkOverlap = (x: number, y: number) => {
380
+ return elements.slice(0, index).some((otherElement) => {
381
+ return (
382
+ x < otherElement.rect.left + otherElement.rect.width &&
383
+ x + rectWidth > otherElement.rect.left &&
384
+ y < otherElement.rect.top + otherElement.rect.height &&
385
+ y + rectHeight > otherElement.rect.top
386
+ );
387
+ });
388
+ };
389
+
390
+ const isWithinBounds = (x: number, y: number) => {
391
+ return (
392
+ x >= 0 &&
393
+ x + rectWidth <= imageWidth &&
394
+ y >= 0 &&
395
+ y + rectHeight <= imageHeight
396
+ );
397
+ };
398
+
399
+ // Check left side (original position)
400
+ if (checkOverlap(rectX, rectY) || !isWithinBounds(rectX, rectY)) {
401
+ // Check top position
402
+ if (
403
+ !checkOverlap(paddedLeft, paddedTop - rectHeight - 2) &&
404
+ isWithinBounds(paddedLeft, paddedTop - rectHeight - 2)
405
+ ) {
406
+ rectX = paddedLeft;
407
+ rectY = paddedTop - rectHeight - 2;
408
+ }
409
+ // Check bottom position
410
+ else if (
411
+ !checkOverlap(paddedLeft, paddedTop + paddedHeight + 2) &&
412
+ isWithinBounds(paddedLeft, paddedTop + paddedHeight + 2)
413
+ ) {
414
+ rectX = paddedLeft;
415
+ rectY = paddedTop + paddedHeight + 2;
416
+ }
417
+ // Check right position
418
+ else if (
419
+ !checkOverlap(paddedLeft + paddedWidth + 2, paddedTop) &&
420
+ isWithinBounds(paddedLeft + paddedWidth + 2, paddedTop)
421
+ ) {
422
+ rectX = paddedLeft + paddedWidth + 2;
423
+ rectY = paddedTop;
424
+ }
425
+ // If all sides are overlapped or out of bounds, place it inside the box at the top
426
+ else {
427
+ rectX = paddedLeft;
428
+ rectY = paddedTop + 2;
429
+ }
430
+ }
431
+
432
+ // Draw text background
433
+ fillRect(
434
+ overlayPixels,
435
+ imageWidth,
436
+ imageHeight,
437
+ {
438
+ x: rectX,
439
+ y: rectY,
440
+ w: rectWidth,
441
+ h: rectHeight,
442
+ },
443
+ color.rect,
444
+ );
445
+
446
+ // Draw text (centered in the background rect)
447
+ const textX = rectX + Math.floor((rectWidth - textWidth) / 2);
448
+ const textY = rectY + Math.floor((rectHeight - textHeight) / 2);
449
+ drawNumber(
450
+ overlayPixels,
451
+ imageWidth,
452
+ imageHeight,
453
+ indexId,
454
+ textX,
455
+ textY,
456
+ color.text,
457
+ );
458
+ }
459
+
460
+ return overlayPixels;
461
+ };
462
+
463
+ export const compositeElementInfoImg = async (options: {
464
+ inputImgBase64: string;
465
+ elementsPositionInfo: Array<ElementForOverlay>;
466
+ size?: { width: number; height: number };
467
+ annotationPadding?: number;
468
+ borderThickness?: number;
469
+ prompt?: string;
470
+ }) => {
471
+ assert(options.inputImgBase64, 'inputImgBase64 is required');
472
+ const { PhotonImage, SamplingFilter, resize } = await getPhoton();
473
+
474
+ let width = 0;
475
+ let height = 0;
476
+
477
+ if (options.size) {
478
+ width = options.size.width;
479
+ height = options.size.height;
480
+ }
481
+
482
+ let photonImage = await photonFromBase64(options.inputImgBase64);
483
+
484
+ if (!width || !height) {
485
+ width = photonImage.get_width();
486
+ height = photonImage.get_height();
487
+ } else {
488
+ const imageWidth = photonImage.get_width();
489
+ const imageHeight = photonImage.get_height();
490
+ // Resize the image to the specified width and height if it's not already the same
491
+ if (imageWidth !== width || imageHeight !== height) {
492
+ const resized = resize(
493
+ photonImage,
494
+ width,
495
+ height,
496
+ SamplingFilter.Nearest,
497
+ );
498
+ photonImage.free();
499
+ photonImage = resized;
500
+ }
501
+ }
502
+
503
+ if (!width || !height) {
504
+ photonImage.free();
505
+ throw Error('Image processing failed because width or height is undefined');
506
+ }
507
+
508
+ const { elementsPositionInfo, prompt } = options;
509
+
510
+ try {
511
+ // Get base image pixels
512
+ const basePixels = photonImage.get_raw_pixels();
513
+
514
+ // Create overlay with annotations
515
+ const overlayPixels = await createSvgOverlay(
516
+ elementsPositionInfo,
517
+ width,
518
+ height,
519
+ options.annotationPadding,
520
+ options.borderThickness,
521
+ prompt,
522
+ );
523
+
524
+ // Blend overlay onto base image
525
+ const blendedPixels = blendPixels(basePixels, overlayPixels, width, height);
526
+
527
+ // Create result image
528
+ const resultImage = new PhotonImage(blendedPixels, width, height);
529
+ const base64 = await photonToBase64(resultImage, 90);
530
+
531
+ resultImage.free();
532
+ return base64;
533
+ } finally {
534
+ photonImage.free();
535
+ }
536
+ };
537
+
538
+ export const processImageElementInfo = async (options: {
539
+ inputImgBase64: string;
540
+ elementsPositionInfo: Array<BaseElement>;
541
+ elementsPositionInfoWithoutText: Array<BaseElement>;
542
+ }) => {
543
+ // Get the size of the original image
544
+ const base64Image = options.inputImgBase64.split(';base64,').pop();
545
+ assert(base64Image, 'base64Image is undefined');
546
+
547
+ const [
548
+ compositeElementInfoImgBase64,
549
+ compositeElementInfoImgWithoutTextBase64,
550
+ ] = await Promise.all([
551
+ compositeElementInfoImg({
552
+ inputImgBase64: options.inputImgBase64,
553
+ elementsPositionInfo: options.elementsPositionInfo,
554
+ }),
555
+ compositeElementInfoImg({
556
+ inputImgBase64: options.inputImgBase64,
557
+ elementsPositionInfo: options.elementsPositionInfoWithoutText,
558
+ }),
559
+ ]);
560
+
561
+ return {
562
+ compositeElementInfoImgBase64,
563
+ compositeElementInfoImgWithoutTextBase64,
564
+ };
565
+ };
566
+
567
+ export async function annotateRects(
568
+ imgBase64: string,
569
+ rects: Rect[],
570
+ prompt?: string,
571
+ ) {
572
+ const markedImage = await compositeElementInfoImg({
573
+ inputImgBase64: imgBase64,
574
+ elementsPositionInfo: rects.map((rect, index) => {
575
+ return {
576
+ id: `rect-${index}`,
577
+ rect,
578
+ indexId: index + 1,
579
+ attributes: { nodeType: NodeType.CONTAINER },
580
+ content: '',
581
+ center: [rect.left + rect.width / 2, rect.top + rect.height / 2],
582
+ };
583
+ }),
584
+ annotationPadding: 0,
585
+ prompt,
586
+ });
587
+ return markedImage;
588
+ }