@calmlens/sdk 0.16.3 → 0.17.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 (112) hide show
  1. package/README.md +398 -163
  2. package/cjs/BrowserUpload-DZ0Vbmxi.js +10 -0
  3. package/cjs/BrowserUpload-uZjVFGfP.d.ts +11 -0
  4. package/cjs/CalmLensAppClient-CV_K26Ce.d.ts +40 -0
  5. package/cjs/CalmLensAppClient-DBGmaDlG.js +89 -0
  6. package/cjs/CalmLensConnection-K1TAD1Z-.js +659 -0
  7. package/cjs/CalmLensServerClient-C-pxiz0C.js +645 -0
  8. package/cjs/CalmLensServerClient-JtP687a3.d.ts +235 -0
  9. package/cjs/CalmLensTypes-9Vt16pc7.d.ts +40400 -0
  10. package/cjs/Classification-94kSYWZw-CMnQKW13.d.ts +5873 -0
  11. package/cjs/CryptoUtils-B_x8lMIx-YBW1-bvL.js +42 -0
  12. package/cjs/FileIcons-Ob003MdS.js +175 -0
  13. package/cjs/Jurisdiction-FusEXb4U-zVs21tDv.js +91 -0
  14. package/cjs/ModerationContext-9lGm-Mna-CcmbRnOy.d.ts +67 -0
  15. package/cjs/ModerationContextLimits-Dj1YAl3G.js +1311 -0
  16. package/cjs/ReactUpload-DxtFMr06.js +54 -0
  17. package/cjs/ReactUpload-nZahpmyg.d.ts +17 -0
  18. package/cjs/TestTriggers-By07LR31-CmK0yTyR.d.ts +271 -0
  19. package/cjs/TestTriggers-DcArt0Nu-CvoaONUB.js +1326 -0
  20. package/cjs/UploadController-BBkN4Ni6.js +618 -0
  21. package/{esm/index-C9Xw1aid.d.ts → cjs/WebhookPayload-2aL31v0J.d.ts} +131 -155
  22. package/cjs/app.d.ts +7 -44
  23. package/cjs/app.js +11 -89
  24. package/cjs/elements.d.ts +88 -0
  25. package/cjs/elements.js +555 -0
  26. package/cjs/file-icons.d.ts +67 -0
  27. package/cjs/file-icons.js +10 -0
  28. package/cjs/index-CI4BzN4W.d.ts +252 -0
  29. package/cjs/index.d.ts +6 -4
  30. package/cjs/index.js +9 -4
  31. package/cjs/react-native.d.ts +56 -0
  32. package/cjs/react-native.js +495 -0
  33. package/cjs/react.d.ts +30 -0
  34. package/cjs/react.js +370 -0
  35. package/cjs/server.d.ts +12 -191
  36. package/cjs/server.js +18 -378
  37. package/cjs/test-inputs.d.ts +2 -2
  38. package/cjs/test-inputs.js +9 -264
  39. package/cjs/testing.d.ts +285 -190
  40. package/cjs/testing.js +2393 -1203
  41. package/cjs/ui-CgZpgpNo.d.ts +526 -0
  42. package/cjs/ui-KceuoVCM.js +583 -0
  43. package/cjs/ui.d.ts +2 -0
  44. package/cjs/ui.js +27 -0
  45. package/cjs/vue.d.ts +490 -0
  46. package/cjs/vue.js +419 -0
  47. package/esm/BrowserUpload-D7-zNmc0.js +5 -0
  48. package/esm/BrowserUpload-uZjVFGfP.d.ts +11 -0
  49. package/esm/CalmLensAppClient-CV_K26Ce.d.ts +40 -0
  50. package/esm/CalmLensAppClient-FVMwrHlH.js +84 -0
  51. package/esm/CalmLensConnection-iyNQHA8L.js +582 -0
  52. package/esm/CalmLensServerClient-JtP687a3.d.ts +235 -0
  53. package/esm/CalmLensServerClient-fQEYKLw4.js +586 -0
  54. package/esm/CalmLensTypes-9Vt16pc7.d.ts +40400 -0
  55. package/esm/Classification-94kSYWZw-CMnQKW13.d.ts +5873 -0
  56. package/esm/CryptoUtils-B_x8lMIx-DhuWGzqn.js +25 -0
  57. package/esm/FileIcons-M6dzjFxd.js +128 -0
  58. package/esm/Jurisdiction-FusEXb4U-1ov6m5yE.js +46 -0
  59. package/esm/ModerationContext-9lGm-Mna-CcmbRnOy.d.ts +67 -0
  60. package/esm/ModerationContextLimits-D1x_WAtP.js +1114 -0
  61. package/esm/ReactUpload-D6yBCkcw.js +37 -0
  62. package/esm/ReactUpload-nZahpmyg.d.ts +17 -0
  63. package/esm/TestTriggers-By07LR31-CmK0yTyR.d.ts +271 -0
  64. package/esm/TestTriggers-DcArt0Nu-s0AQZuWE.js +1266 -0
  65. package/esm/UploadController-txp5NuzH.js +589 -0
  66. package/{cjs/index-C9Xw1aid.d.ts → esm/WebhookPayload-2aL31v0J.d.ts} +131 -155
  67. package/esm/app.d.ts +7 -44
  68. package/esm/app.js +3 -86
  69. package/esm/elements.d.ts +88 -0
  70. package/esm/elements.js +528 -0
  71. package/esm/file-icons.d.ts +67 -0
  72. package/esm/file-icons.js +1 -0
  73. package/esm/index-CI4BzN4W.d.ts +252 -0
  74. package/esm/index.d.ts +6 -4
  75. package/esm/index.js +1 -1
  76. package/esm/react-native.d.ts +56 -0
  77. package/esm/react-native.js +467 -0
  78. package/esm/react.d.ts +30 -0
  79. package/esm/react.js +343 -0
  80. package/esm/server.d.ts +12 -191
  81. package/esm/server.js +3 -369
  82. package/esm/styles.css +147 -0
  83. package/esm/svelte/CalmLensMedia.svelte +134 -0
  84. package/esm/svelte/CalmLensUploader.svelte +175 -0
  85. package/esm/svelte/Icon.svelte +7 -0
  86. package/esm/svelte/index.d.ts +37 -0
  87. package/esm/svelte/index.js +6 -0
  88. package/esm/test-inputs.d.ts +2 -2
  89. package/esm/test-inputs.js +1 -256
  90. package/esm/testing.d.ts +285 -190
  91. package/esm/testing.js +2329 -1122
  92. package/esm/ui-Ca_SpJXf.js +458 -0
  93. package/esm/ui-CgZpgpNo.d.ts +526 -0
  94. package/esm/ui.d.ts +2 -0
  95. package/esm/ui.js +3 -0
  96. package/esm/vue.d.ts +490 -0
  97. package/esm/vue.js +392 -0
  98. package/package.json +110 -11
  99. package/cjs/CalmLensConnection-BNUHp_AQ.js +0 -234
  100. package/cjs/CalmLensUtils-o171ownK.js +0 -699
  101. package/cjs/CalmLensVersion-DwhgxQK5.js +0 -480
  102. package/cjs/CalmLensWebhooks-BKrq30Yp.js +0 -201
  103. package/cjs/Classification-BNhgwGEm-DmA9VEuE.d.ts +0 -4806
  104. package/cjs/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +0 -44
  105. package/cjs/WebhookPayload-CJoIkGBD.d.ts +0 -11753
  106. package/esm/CalmLensConnection-XTqmlwzG.js +0 -193
  107. package/esm/CalmLensUtils-DsF8Aa_x.js +0 -574
  108. package/esm/CalmLensVersion-DcifRKsx.js +0 -415
  109. package/esm/CalmLensWebhooks-BvGyk5d3.js +0 -148
  110. package/esm/Classification-BNhgwGEm-DmA9VEuE.d.ts +0 -4806
  111. package/esm/TestTriggers-CRKtulyp-vrzEq2wm.d.ts +0 -44
  112. package/esm/WebhookPayload-CJoIkGBD.d.ts +0 -11753
@@ -0,0 +1,175 @@
1
+ <script lang="ts">
2
+ import { onMount } from "svelte";
3
+ import {
4
+ createUploadController,
5
+ describeUploader,
6
+ describeUploadItem,
7
+ resolveMessages,
8
+ type UploaderProps,
9
+ } from "../ui.js";
10
+ import Icon from "./Icon.svelte";
11
+
12
+ let {
13
+ disabled = false,
14
+ capture,
15
+ messages: overrides,
16
+ controller: external,
17
+ ...options
18
+ }: UploaderProps = $props();
19
+
20
+ // The latest options, without restarting uploads when the parent re-renders.
21
+ const own = createUploadController(options);
22
+ $effect.pre(() => {
23
+ if (!external) own.setOptions({ ...options });
24
+ });
25
+ onMount(() => () => own.clear());
26
+
27
+ const queue = $derived(external ?? own);
28
+ const messages = $derived(resolveMessages(overrides));
29
+ const view = $derived(describeUploader($queue, queue.options, messages));
30
+ let dragging = $state(false);
31
+ let input: HTMLInputElement | undefined = $state();
32
+
33
+ const openPicker = () => {
34
+ if (!disabled) input?.click();
35
+ };
36
+ const onDragOver = (event: DragEvent) => {
37
+ event.preventDefault();
38
+ if (!disabled) dragging = true;
39
+ };
40
+ </script>
41
+
42
+ <div class="calmlens-uploader">
43
+ {#if view.showDropzone}
44
+ <!-- Keyboard users open the picker with the button; the zone adds drop and click targets. -->
45
+ <!-- svelte-ignore a11y_click_events_have_key_events, a11y_no_static_element_interactions -->
46
+ <!-- biome-ignore lint/a11y/noStaticElementInteractions: the button inside is the accessible control -->
47
+ <div
48
+ class="calmlens-dropzone"
49
+ data-dragging={dragging || undefined}
50
+ data-disabled={disabled || undefined}
51
+ onclick={openPicker}
52
+ ondragover={onDragOver}
53
+ ondragenter={onDragOver}
54
+ ondragleave={(event) => {
55
+ if (!event.currentTarget.contains(event.relatedTarget as Node | null)) dragging = false;
56
+ }}
57
+ ondrop={(event) => {
58
+ event.preventDefault();
59
+ dragging = false;
60
+ if (!disabled && event.dataTransfer) queue.addFiles(event.dataTransfer.files);
61
+ }}
62
+ onpaste={(event) => {
63
+ const files = event.clipboardData?.files;
64
+ if (!disabled && files?.length) queue.addFiles(files);
65
+ }}
66
+ >
67
+ <span class="calmlens-dropzone__icon"><Icon name="upload" /></span>
68
+ <div class="calmlens-dropzone__text">
69
+ <p class="calmlens-dropzone__title">
70
+ {dragging ? messages.dropzoneTitleDragging : messages.dropzoneTitle}
71
+ </p>
72
+ {#if view.hint}<p class="calmlens-dropzone__hint">{view.hint}</p>{/if}
73
+ </div>
74
+ <button
75
+ type="button"
76
+ class="calmlens-button"
77
+ data-variant="outline"
78
+ {disabled}
79
+ onclick={(event) => {
80
+ event.stopPropagation();
81
+ openPicker();
82
+ }}
83
+ >
84
+ {view.chooseLabel}
85
+ </button>
86
+ </div>
87
+ {/if}
88
+ <input
89
+ bind:this={input}
90
+ type="file"
91
+ hidden
92
+ accept={queue.options.accept}
93
+ multiple={queue.options.multiple}
94
+ {capture}
95
+ {disabled}
96
+ onchange={(event) => {
97
+ if (event.currentTarget.files) queue.addFiles(event.currentTarget.files);
98
+ event.currentTarget.value = "";
99
+ }}
100
+ />
101
+ {#if $queue.items.length > 0}
102
+ <ul class="calmlens-uploads">
103
+ {#each $queue.items as item (item.key)}
104
+ {@const itemView = describeUploadItem(item, messages)}
105
+ <li class="calmlens-upload" data-stage={item.stage} data-outcome={itemView.outcome}>
106
+ {#if item.previewUrl}
107
+ <img class="calmlens-upload__thumb" src={item.previewUrl} alt="" />
108
+ {:else}
109
+ <span class="calmlens-upload__thumb" data-kind="file" aria-hidden="true"
110
+ >{@html itemView.fileIcon}</span
111
+ >
112
+ {/if}
113
+ <div class="calmlens-upload__body">
114
+ <span class="calmlens-upload__name">{item.file.name}</span>
115
+ <span class="calmlens-upload__size">{itemView.size}</span>
116
+ {#if itemView.busy}
117
+ <div
118
+ class="calmlens-progress"
119
+ role="progressbar"
120
+ aria-label={itemView.message}
121
+ aria-valuemin={0}
122
+ aria-valuemax={100}
123
+ aria-valuenow={itemView.progressPercent}
124
+ >
125
+ <div
126
+ class="calmlens-progress__bar"
127
+ style:width={itemView.progressPercent === undefined
128
+ ? undefined
129
+ : `${itemView.progressPercent}%`}
130
+ ></div>
131
+ </div>
132
+ {/if}
133
+ {#if itemView.message}
134
+ <p class="calmlens-upload__message" role="status">{itemView.message}</p>
135
+ {/if}
136
+ </div>
137
+ <div class="calmlens-upload__actions">
138
+ {#if !view.showDropzone}
139
+ <button
140
+ type="button"
141
+ class="calmlens-button"
142
+ data-variant="outline"
143
+ data-size="small"
144
+ {disabled}
145
+ onclick={openPicker}
146
+ >
147
+ {messages.chooseAnother}
148
+ </button>
149
+ {/if}
150
+ {#if itemView.canRetry}
151
+ <button
152
+ type="button"
153
+ class="calmlens-icon-button"
154
+ aria-label={messages.retry}
155
+ title={messages.retry}
156
+ onclick={() => queue.retry(item.key)}
157
+ >
158
+ <Icon name="retry" />
159
+ </button>
160
+ {/if}
161
+ <button
162
+ type="button"
163
+ class="calmlens-icon-button"
164
+ aria-label={itemView.removeLabel}
165
+ title={itemView.removeLabel}
166
+ onclick={() => queue.remove(item.key)}
167
+ >
168
+ <Icon name="remove" />
169
+ </button>
170
+ </div>
171
+ </li>
172
+ {/each}
173
+ </ul>
174
+ {/if}
175
+ </div>
@@ -0,0 +1,7 @@
1
+ <script lang="ts">
2
+ import { ICON_ATTRIBUTES, ICONS, type IconName } from "../ui.js";
3
+
4
+ let { name }: { name: IconName } = $props();
5
+ </script>
6
+
7
+ <svg {...ICON_ATTRIBUTES}><path d={ICONS[name]} /></svg>
@@ -0,0 +1,37 @@
1
+ import type { Component } from "svelte";
2
+ import type {
3
+ CalmLensUpload as CoreCalmLensUpload,
4
+ UploadControllerOptions as CoreUploadControllerOptions,
5
+ UploaderProps as CoreUploaderProps,
6
+ UploadItem as CoreUploadItem,
7
+ UploadSnapshot as CoreUploadSnapshot,
8
+ MediaProps,
9
+ UploadController,
10
+ UploadFile,
11
+ } from "../ui.js";
12
+
13
+ // The browser's files are `File`s, so the upload types default to `File` here.
14
+ export type UploadItem<F extends UploadFile = File> = CoreUploadItem<F>;
15
+ export type CalmLensUpload<F extends UploadFile = File> = CoreCalmLensUpload<F>;
16
+ export type UploadSnapshot<F extends UploadFile = File> = CoreUploadSnapshot<F>;
17
+ export type UploadControllerOptions<F extends UploadFile = File> = CoreUploadControllerOptions<F>;
18
+ export type UploaderProps<F extends UploadFile = File> = CoreUploaderProps<F>;
19
+ /** Creates an upload queue for the components, such as one a form starts on submit. */
20
+ export declare const createUploadController: <F extends UploadFile = File>(
21
+ options: UploadControllerOptions<F>,
22
+ ) => UploadController<F>;
23
+
24
+ /**
25
+ * A drop zone and file list that uploads through an upload preset, shows each file's progress
26
+ * and moderation decision, and reports finished uploads to `onUploaded`.
27
+ */
28
+ export declare const CalmLensUploader: Component<UploaderProps>;
29
+
30
+ /**
31
+ * Shows an asset from the CalmLens CDN with responsive widths and a blurred placeholder while
32
+ * it loads. Age-restricted assets stay behind the placeholder and a reveal button until
33
+ * `ageConfirmed` is set; blocked and purged assets show an unavailable notice.
34
+ */
35
+ export declare const CalmLensMedia: Component<MediaProps>;
36
+
37
+ export * from "../ui.js";
@@ -0,0 +1,6 @@
1
+ // Svelte 5 components for uploads and media. The upload controller is a Svelte store, so
2
+ // `createUploadController(...)` and `$controller.items` build your own upload UI.
3
+
4
+ export * from "../ui.js";
5
+ export { default as CalmLensMedia } from "./CalmLensMedia.svelte";
6
+ export { default as CalmLensUploader } from "./CalmLensUploader.svelte";
@@ -1,2 +1,2 @@
1
- import { a as detectTestImage, c as getImageTestCategories, d as paintTestImage, i as TestImageKeyCell, l as getTestImageKeyCells, n as TEST_IMAGE_PALETTES, o as detectTestTrigger, r as TEST_INPUT_KEY_PATTERN, s as generateTestTextCode, t as ClassificationTestSignal, u as getTestImageKeyColors } from "./TestTriggers-CRKtulyp-vrzEq2wm.js";
2
- export { type ClassificationTestSignal, TEST_IMAGE_PALETTES, TEST_INPUT_KEY_PATTERN, type TestImageKeyCell, detectTestImage, detectTestTrigger, generateTestTextCode, getImageTestCategories, getTestImageKeyCells, getTestImageKeyColors, paintTestImage };
1
+ import { a as detectTestImage, c as getImageTestCategories, i as TestInput, l as paintTestImage, n as TEST_INPUT_KEY_PATTERN, o as detectTestTrigger, r as TEST_INPUT_PRESETS, s as generateTestTextCode, t as ClassificationTestSignal, u as testInputForSignal } from "./TestTriggers-By07LR31-CmK0yTyR.js";
2
+ export { type ClassificationTestSignal, TEST_INPUT_KEY_PATTERN, TEST_INPUT_PRESETS, type TestInput, detectTestImage, detectTestTrigger, generateTestTextCode, getImageTestCategories, paintTestImage, testInputForSignal };
@@ -1,256 +1 @@
1
- //#region src/contract/TestTriggers-7yylawYV.js
2
- const TEST_IMAGE_PALETTES = {
3
- sexual: [
4
- [
5
- 255,
6
- 20,
7
- 147,
8
- 255
9
- ],
10
- [
11
- 255,
12
- 105,
13
- 180,
14
- 255
15
- ],
16
- [
17
- 199,
18
- 21,
19
- 133,
20
- 255
21
- ],
22
- [
23
- 255,
24
- 69,
25
- 0,
26
- 255
27
- ]
28
- ],
29
- violence: [
30
- [
31
- 139,
32
- 0,
33
- 0,
34
- 255
35
- ],
36
- [
37
- 220,
38
- 20,
39
- 60,
40
- 255
41
- ],
42
- [
43
- 178,
44
- 34,
45
- 34,
46
- 255
47
- ],
48
- [
49
- 255,
50
- 0,
51
- 0,
52
- 255
53
- ]
54
- ],
55
- harassment: [
56
- [
57
- 180,
58
- 83,
59
- 9,
60
- 255
61
- ],
62
- [
63
- 217,
64
- 119,
65
- 6,
66
- 255
67
- ],
68
- [
69
- 245,
70
- 158,
71
- 11,
72
- 255
73
- ],
74
- [
75
- 251,
76
- 191,
77
- 36,
78
- 255
79
- ]
80
- ],
81
- hate: [
82
- [
83
- 30,
84
- 64,
85
- 175,
86
- 255
87
- ],
88
- [
89
- 37,
90
- 99,
91
- 235,
92
- 255
93
- ],
94
- [
95
- 59,
96
- 130,
97
- 246,
98
- 255
99
- ],
100
- [
101
- 96,
102
- 165,
103
- 250,
104
- 255
105
- ]
106
- ],
107
- "criminal-activity": [
108
- [
109
- 6,
110
- 95,
111
- 70,
112
- 255
113
- ],
114
- [
115
- 5,
116
- 150,
117
- 105,
118
- 255
119
- ],
120
- [
121
- 16,
122
- 185,
123
- 129,
124
- 255
125
- ],
126
- [
127
- 52,
128
- 211,
129
- 153,
130
- 255
131
- ]
132
- ],
133
- "self-harm": [
134
- [
135
- 75,
136
- 0,
137
- 130,
138
- 255
139
- ],
140
- [
141
- 138,
142
- 43,
143
- 226,
144
- 255
145
- ],
146
- [
147
- 72,
148
- 61,
149
- 139,
150
- 255
151
- ],
152
- [
153
- 147,
154
- 112,
155
- 219,
156
- 255
157
- ]
158
- ]
159
- };
160
- /** A project's test input key: eight hexadecimal characters only its members can see. */
161
- const TEST_INPUT_KEY_PATTERN = /^[a-f0-9]{8}$/;
162
- const TEXT_CODE_SIGNALS = [
163
- "harassment",
164
- "hate",
165
- "criminal-activity",
166
- "sexual.involving-minors"
167
- ];
168
- /**
169
- * The text code that CalmLens classifies as `category` for the project with this test input key.
170
- * @returns The code, or an empty string for a category without a text code
171
- */
172
- const generateTestTextCode = (category, testInputKey) => TEXT_CODE_SIGNALS.includes(category) ? `<!calmlens-test ${testInputKey} ${category}>` : "";
173
- const TEST_TRIGGER_PATTERN = /<!calmlens-test\s+([a-f0-9]{8})\s+(harassment|hate|criminal-activity|sexual\.involving-minors)\s*>/gi;
174
- /**
175
- * The test signals in `text` whose codes carry the project's test input key. Codes with another
176
- * key are ignored, so a code can only be used by the project it belongs to.
177
- */
178
- const detectTestTrigger = (text, testInputKey) => {
179
- const signals = [];
180
- for (const [, key, signal] of text.matchAll(TEST_TRIGGER_PATTERN)) {
181
- if (key.toLowerCase() !== testInputKey.toLowerCase()) continue;
182
- const testSignal = signal.toLowerCase();
183
- if (!signals.includes(testSignal)) signals.push(testSignal);
184
- }
185
- return {
186
- isTestTrigger: signals.length > 0,
187
- signals
188
- };
189
- };
190
- /** Sixteen cells encode two bits each using the category's four existing colours. */
191
- const getTestImageKeyCells = (width, height) => {
192
- return Array.from({ length: 16 }, (_, index) => {
193
- const column = index % 4;
194
- const row = Math.floor(index / 4);
195
- const x = Math.floor(column * width / 4);
196
- const y = Math.floor(row * height / 4);
197
- return {
198
- x,
199
- y,
200
- width: Math.floor((column + 1) * width / 4) - x,
201
- height: Math.floor((row + 1) * height / 4) - y
202
- };
203
- });
204
- };
205
- const getTestImageKeyColors = (signal, testInputKey) => {
206
- const colors = TEST_IMAGE_PALETTES[signal];
207
- if (!colors || !TEST_INPUT_KEY_PATTERN.test(testInputKey)) throw new Error("Invalid image test signal or key");
208
- return [...testInputKey].flatMap((character) => {
209
- const value = Number.parseInt(character, 16);
210
- return [colors[value >> 2], colors[value & 3]];
211
- });
212
- };
213
- /**
214
- * Gets all test trigger categories that support images
215
- */
216
- const getImageTestCategories = () => {
217
- return Object.keys(TEST_IMAGE_PALETTES);
218
- };
219
- /** Paint a full-image 4×4 grid as RGBA pixels; callers choose their image encoder. */
220
- const paintTestImage = (signal, testInputKey, width = 512, height = width) => {
221
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width < 24 || height < 24) throw new RangeError("Test image dimensions must be integers of at least 24 pixels");
222
- const colors = getTestImageKeyColors(signal, testInputKey);
223
- const rgba = new Uint8Array(width * height * 4);
224
- getTestImageKeyCells(width, height).forEach((cell, index) => {
225
- for (let y = cell.y; y < cell.y + cell.height; y++) for (let x = cell.x; x < cell.x + cell.width; x++) rgba.set(colors[index], (y * width + x) * 4);
226
- });
227
- return rgba;
228
- };
229
- /** Decode the project key from pixels, tolerating resizing and lossy compression. */
230
- const detectTestImage = (image, testInputKey) => {
231
- const { width, height, rgba } = image;
232
- if (!Number.isSafeInteger(width) || !Number.isSafeInteger(height) || width < 24 || height < 24 || rgba.length !== width * height * 4 || !TEST_INPUT_KEY_PATTERN.test(testInputKey)) return void 0;
233
- const cells = getTestImageKeyCells(width, height);
234
- for (const category of getImageTestCategories()) {
235
- const colors = getTestImageKeyColors(category, testInputKey);
236
- if (cells.every((cell, index) => {
237
- const sample = [
238
- 0,
239
- 0,
240
- 0
241
- ];
242
- let count = 0;
243
- for (let y = cell.y + Math.ceil(cell.height / 4); y < cell.y + cell.height * 3 / 4; y++) for (let x = cell.x + Math.ceil(cell.width / 4); x < cell.x + cell.width * 3 / 4; x++) {
244
- const offset = (y * width + x) * 4;
245
- if (rgba[offset + 3] < 250) return false;
246
- for (let c = 0; c < 3; c++) sample[c] += rgba[offset + c];
247
- count++;
248
- }
249
- const distances = TEST_IMAGE_PALETTES[category].map((color) => sample.reduce((sum, value, c) => sum + (value / count - color[c]) ** 2, 0));
250
- const nearest = distances.indexOf(Math.min(...distances));
251
- return distances[nearest] <= 900 && TEST_IMAGE_PALETTES[category][nearest] === colors[index];
252
- })) return category;
253
- }
254
- };
255
- //#endregion
256
- export { TEST_IMAGE_PALETTES, TEST_INPUT_KEY_PATTERN, detectTestImage, detectTestTrigger, generateTestTextCode, getImageTestCategories, getTestImageKeyCells, getTestImageKeyColors, paintTestImage };
1
+ export { t as TEST_INPUT_KEY_PATTERN, n as TEST_INPUT_PRESETS, r as detectTestImage, i as detectTestTrigger, a as generateTestTextCode, o as getImageTestCategories, c as paintTestImage, l as testInputForSignal } from "./TestTriggers-DcArt0Nu-s0AQZuWE.js";