satyamark-react 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.
package/README.md ADDED
@@ -0,0 +1,220 @@
1
+ <p align="center">
2
+ <img src="./assets/CoverImage_1.png" alt="SatyaMark React" width="100%" />
3
+ </p>
4
+
5
+ # satyamark-react
6
+
7
+ **satyamark-react** is the official React client library for **SatyaMark** — a real-time, AI-powered content verification infrastructure that surfaces clear, evidence-backed trust signals directly inside your UI.
8
+
9
+ It extracts text and images from rendered DOM elements, submits them for verification, and renders standardized credibility icons that update live as results arrive.
10
+
11
+ SatyaMark is built as **trust infrastructure**, not a fact-checking oracle.
12
+
13
+ <p align="center">
14
+ <a href="https://github.com/DhirajKarangale/SatyaMark/tree/main/Frontend/satyamark-react">GitHub Repository</a>
15
+ ·
16
+ <a href="https://www.npmjs.com/package/satyamark-react">npm Package</a>
17
+ </p>
18
+
19
+ ---
20
+
21
+ ## What This Library Does
22
+
23
+ - Connects your React app to the SatyaMark verification network
24
+ - Extracts visible text and images from DOM elements
25
+ - Submits content for real-time verification
26
+ - Displays standardized trust icons inside your UI
27
+ - Updates verification status live
28
+ - Allows users to open detailed verification explanations
29
+
30
+ This library prioritizes **transparency over certainty**.
31
+
32
+ ---
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ npm install satyamark-react
38
+ ```
39
+
40
+ ---
41
+
42
+ ## Quick Start
43
+
44
+ ### 1. Initialize the SatyaMark connection (once)
45
+
46
+ ```tsx
47
+ import { useEffect } from "react";
48
+ import { init, onConnected } from "satyamark-react";
49
+
50
+ useEffect(() => {
51
+ init({
52
+ app_id: "YOUR_APP_ID",
53
+ user_id: "USER_ID",
54
+ });
55
+ }, []);
56
+
57
+ onConnected((data) => {
58
+ console.log("Connected to SatyaMark:", data);
59
+ });
60
+ ```
61
+
62
+ ---
63
+
64
+ ### 2. Process content from a DOM element
65
+
66
+ `satyamark-react` works directly on **rendered UI**, not raw strings.
67
+
68
+ ```tsx
69
+ import { useRef, useEffect, useState } from "react";
70
+ import { process, registerStatus } from "satyamark-react";
71
+
72
+ const cardRef = useRef<HTMLDivElement>(null);
73
+ const [jobId, setJobId] = useState<string | null>(null);
74
+
75
+ useEffect(() => {
76
+ if (!cardRef.current) return;
77
+
78
+ process(cardRef.current, "POST_ID")
79
+ .then(setJobId)
80
+ .catch(console.error);
81
+ }, []);
82
+ ```
83
+
84
+ ---
85
+
86
+ ### 3. Display verification status
87
+
88
+ Add a container where the status icon should appear:
89
+
90
+ ```tsx
91
+ <div data-satyamark-status-container />
92
+ ```
93
+
94
+ Register the status handler:
95
+
96
+ ```tsx
97
+ useEffect(() => {
98
+ if (!jobId || !cardRef.current) return;
99
+
100
+ registerStatus(jobId, cardRef.current);
101
+ }, [jobId]);
102
+ ```
103
+
104
+ ---
105
+
106
+ ## How It Works
107
+
108
+ <p align="center">
109
+ <img src="./assets/CoverImage_2.png" alt="SatyaMark Architecture" width="90%" />
110
+ </p>
111
+
112
+ 1. Extracts visible text + first valid image
113
+ 2. Sends content via WebSocket
114
+ 3. Receives live verification updates
115
+ 4. Updates icons automatically
116
+ 5. Click icon → open detailed verification page
117
+
118
+ ---
119
+
120
+ ## Verification Marks
121
+
122
+ - `correct`
123
+ - `incorrect`
124
+ - `verifyable`
125
+ - `unverifyable`
126
+ - `insufficient`
127
+ - `pending`
128
+ - `ai`
129
+ - `nonai`
130
+
131
+ ---
132
+
133
+ ## API Reference
134
+
135
+ ### `init()`
136
+
137
+ ```ts
138
+ init({
139
+ app_id: string;
140
+ user_id: string;
141
+ });
142
+ ```
143
+
144
+ ---
145
+
146
+ ### `process()`
147
+
148
+ ```ts
149
+ process(
150
+ rootElement: HTMLDivElement,
151
+ dataId: string
152
+ ): Promise<string>
153
+ ```
154
+
155
+ ---
156
+
157
+ ### `registerStatus()`
158
+
159
+ ```ts
160
+ registerStatus(
161
+ jobId: string,
162
+ rootElement: HTMLElement,
163
+ options?: {
164
+ iconSize?: number;
165
+ }
166
+ );
167
+ ```
168
+
169
+ ---
170
+
171
+ ### `onReceive()`
172
+
173
+ ```ts
174
+ onReceive((data) => {
175
+ console.log(data);
176
+ });
177
+ ```
178
+
179
+ ---
180
+
181
+ ### `onConnected()`
182
+
183
+ ```ts
184
+ onConnected((connection) => {
185
+ console.log("Connected:", connection);
186
+ });
187
+ ```
188
+
189
+ ---
190
+
191
+ ## Design Principles
192
+
193
+ - Transparency over certainty
194
+ - Trust signals, not guarantees
195
+ - Privacy-first
196
+ - Incremental accuracy
197
+
198
+ ---
199
+
200
+ ## Current Limitations
201
+
202
+ - Text verification is most reliable
203
+ - Image verification is experimental
204
+ - Video & audio not yet supported
205
+ - Results are not absolute truth
206
+
207
+ ---
208
+
209
+ ## Privacy Notes
210
+
211
+ - Original text is not stored
212
+ - Short AI-generated summaries may be retained
213
+ - Images may be temporarily cached
214
+ - No advertising or profiling
215
+
216
+ ---
217
+
218
+ ## One-Line Summary
219
+
220
+ **satyamark-react lets React apps surface real-time, evidence-backed credibility signals directly inside the UI.**
Binary file
package/dist/ai.png ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,22 @@
1
+ type SatyaMarkConnectionData = {
2
+ app_id: string;
3
+ user_id: string;
4
+ };
5
+ type ConnectedCallback = (data: SatyaMarkConnectionData) => void;
6
+ type ReceiveCallback = (data: any) => void;
7
+ declare function onReceive(cb: ReceiveCallback): () => void;
8
+ declare function onConnected(cb: ConnectedCallback): void;
9
+ declare function init(connectionData: SatyaMarkConnectionData, options?: {
10
+ onConnected?: ConnectedCallback;
11
+ }): void;
12
+ declare function sendData(text: string, image_url: string, dataId: string): string | undefined;
13
+ declare function receiveData(data: any): void;
14
+
15
+ declare function process(divRef: HTMLDivElement, dataId: string): Promise<string | undefined>;
16
+
17
+ type StatusOptions = {
18
+ iconSize?: number;
19
+ };
20
+ declare function registerStatus(jobId: string, rootElement: HTMLElement, options?: StatusOptions): void;
21
+
22
+ export { SatyaMarkConnectionData, init, onConnected, onReceive, process, receiveData, registerStatus, sendData };
package/dist/index.js ADDED
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ init: () => init,
34
+ onConnected: () => onConnected,
35
+ onReceive: () => onReceive,
36
+ process: () => process,
37
+ receiveData: () => receiveData,
38
+ registerStatus: () => registerStatus,
39
+ sendData: () => sendData
40
+ });
41
+ module.exports = __toCommonJS(src_exports);
42
+
43
+ // src/satyamark_connect.ts
44
+ var wsUrl = "wss://satyamark.onrender.com";
45
+ var socket = null;
46
+ var storedConnectionData = null;
47
+ var listeners = [];
48
+ var onConnectedCb = null;
49
+ function onReceive(cb) {
50
+ listeners.push(cb);
51
+ return () => {
52
+ const idx = listeners.indexOf(cb);
53
+ if (idx !== -1)
54
+ listeners.splice(idx, 1);
55
+ };
56
+ }
57
+ function onConnected(cb) {
58
+ onConnectedCb = cb;
59
+ }
60
+ function init(connectionData, options) {
61
+ var _a;
62
+ if (socket && socket.readyState == WebSocket.OPEN && storedConnectionData == connectionData) {
63
+ console.log("Already Connected: ", connectionData);
64
+ return;
65
+ }
66
+ socket = new WebSocket(wsUrl);
67
+ onConnectedCb = (_a = options == null ? void 0 : options.onConnected) != null ? _a : onConnectedCb;
68
+ socket.onopen = () => {
69
+ console.log("Connected to server: ", connectionData.user_id);
70
+ safeSend({
71
+ type: "handshake",
72
+ clientId: connectionData.user_id,
73
+ appId: connectionData.app_id
74
+ });
75
+ onConnectedCb == null ? void 0 : onConnectedCb(connectionData);
76
+ };
77
+ socket.onmessage = (event) => {
78
+ receiveData(JSON.parse(event.data));
79
+ };
80
+ socket.onclose = () => {
81
+ console.log("Server connection closed");
82
+ };
83
+ storedConnectionData = connectionData;
84
+ }
85
+ function isSocketOpen() {
86
+ return socket && socket.readyState === WebSocket.OPEN;
87
+ }
88
+ function assert(condition, message) {
89
+ if (!condition)
90
+ throw new Error(message);
91
+ }
92
+ function safeSend(msg) {
93
+ if (!storedConnectionData) {
94
+ console.warn("No connectionData found. Call connect() first.");
95
+ return;
96
+ }
97
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
98
+ console.warn("Socket not ready");
99
+ return;
100
+ }
101
+ socket.send(JSON.stringify(msg));
102
+ }
103
+ function uniqueTimestamp() {
104
+ const now = /* @__PURE__ */ new Date();
105
+ const yyyy = now.getFullYear();
106
+ const MM = String(now.getMonth() + 1).padStart(2, "0");
107
+ const dd = String(now.getDate()).padStart(2, "0");
108
+ const hh = String(now.getHours()).padStart(2, "0");
109
+ const mm = String(now.getMinutes()).padStart(2, "0");
110
+ const ss = String(now.getSeconds()).padStart(2, "0");
111
+ const ms = String(now.getMilliseconds()).padStart(3, "0");
112
+ const micro = String(Math.floor(Math.random() * 1e3)).padStart(3, "0");
113
+ return `${yyyy}${MM}${dd}${hh}${mm}${ss}${ms}${micro}`;
114
+ }
115
+ function sendData(text, image_url, dataId) {
116
+ if (!storedConnectionData) {
117
+ console.log("No connectionData found. Call connect() first.");
118
+ return;
119
+ }
120
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
121
+ console.log("Socket not ready");
122
+ return;
123
+ }
124
+ assert(storedConnectionData, "Call init() before sendData()");
125
+ assert(isSocketOpen(), "WebSocket is not ready");
126
+ assert(dataId, "dataId is required");
127
+ assert(text || image_url, "Provide text or image_url");
128
+ if (text)
129
+ assert(text.trim().length >= 3, "Text must be at least 3 characters");
130
+ const { app_id, user_id } = storedConnectionData;
131
+ const timestamp = uniqueTimestamp();
132
+ const jobId = `${app_id}_${user_id}_${dataId}_${timestamp}`;
133
+ const data = {
134
+ clientId: user_id,
135
+ jobId,
136
+ text,
137
+ image_url
138
+ };
139
+ socket.send(JSON.stringify(data));
140
+ return jobId;
141
+ }
142
+ function receiveData(data) {
143
+ if (!storedConnectionData || data.clientId != storedConnectionData.user_id)
144
+ return;
145
+ for (const cb of Array.from(listeners)) {
146
+ try {
147
+ cb(data);
148
+ } catch (err) {
149
+ console.error("listener error", err);
150
+ }
151
+ }
152
+ }
153
+
154
+ // src/satyamark_process.ts
155
+ var mergeText = (texts) => texts.join(", ");
156
+ var isValidImageUrl = (url) => {
157
+ return new Promise((resolve) => {
158
+ const img = new Image();
159
+ img.onload = () => resolve(true);
160
+ img.onerror = () => resolve(false);
161
+ img.src = url;
162
+ });
163
+ };
164
+ var getFirstValidImage = async (urls) => {
165
+ for (const url of urls) {
166
+ if (await isValidImageUrl(url))
167
+ return url;
168
+ }
169
+ return null;
170
+ };
171
+ var extractFromDiv = (root) => {
172
+ var _a;
173
+ const images = Array.from(root.querySelectorAll("img")).map((img) => img.src);
174
+ const text = [];
175
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
176
+ let node;
177
+ while (node = walker.nextNode()) {
178
+ const trimmed = (_a = node.textContent) == null ? void 0 : _a.trim();
179
+ if (trimmed)
180
+ text.push(trimmed);
181
+ }
182
+ return { text, images };
183
+ };
184
+ async function process(divRef, dataId) {
185
+ if (!divRef) {
186
+ throw new Error("Invalid root element");
187
+ }
188
+ if (!dataId) {
189
+ throw new Error("dataId is required");
190
+ }
191
+ const { text, images } = extractFromDiv(divRef);
192
+ const mergedText = mergeText(text);
193
+ const validImage = await getFirstValidImage(images);
194
+ if (!mergedText && !validImage) {
195
+ throw new Error("No valid text or image found in the element");
196
+ }
197
+ if (mergedText && mergedText.length < 3) {
198
+ throw new Error("Extracted text is too short");
199
+ }
200
+ const jobId = sendData(mergedText, validImage != null ? validImage : "", dataId);
201
+ return jobId;
202
+ }
203
+
204
+ // src/satyamark_status_controller.ts
205
+ var import_verifyable = __toESM(require("./verifyable-AYGD2AEO.png"));
206
+ var import_unverifyable = __toESM(require("./unverifyable-2LSQEVJT.png"));
207
+ var import_insufficient = __toESM(require("./insufficient-FBXL3GLE.png"));
208
+ var import_correct = __toESM(require("./correct-AW5V6TXB.png"));
209
+ var import_incorrect = __toESM(require("./incorrect-KHIGXY2C.png"));
210
+ var import_pending = __toESM(require("./pending-SWS5I5G5.png"));
211
+ var import_ai = __toESM(require("./ai-J46U5IDK.png"));
212
+ var import_nonai = __toESM(require("./nonai-LPEFMQBT.png"));
213
+ var jobMap = {};
214
+ var DEFAULT_ICON_SIZE = 20;
215
+ var satyamark_url = "https://satyamark.vercel.app/";
216
+ var iconMap = {
217
+ verifyable: import_verifyable.default,
218
+ unverifyable: import_unverifyable.default,
219
+ insufficient: import_insufficient.default,
220
+ correct: import_correct.default,
221
+ incorrect: import_incorrect.default,
222
+ pending: import_pending.default,
223
+ ai: import_ai.default,
224
+ nonai: import_nonai.default
225
+ };
226
+ function registerStatus(jobId, rootElement, options = {}) {
227
+ var _a;
228
+ jobMap[jobId] = {
229
+ root: rootElement,
230
+ iconSize: (_a = options.iconSize) != null ? _a : DEFAULT_ICON_SIZE
231
+ };
232
+ updateIcon(jobId, "pending", null);
233
+ }
234
+ function updateIcon(jobId, mark, data) {
235
+ const entry = jobMap[jobId];
236
+ if (!entry)
237
+ return;
238
+ const { root, iconSize } = entry;
239
+ const container = root.querySelector("[data-satyamark-status-container]");
240
+ if (!container)
241
+ return;
242
+ container.style.position = "relative";
243
+ let icon = container.querySelector("img");
244
+ if (!icon) {
245
+ icon = document.createElement("img");
246
+ icon.alt = "status";
247
+ icon.style.objectFit = "contain";
248
+ icon.style.display = "block";
249
+ container.appendChild(icon);
250
+ }
251
+ icon.style.width = iconSize + "px";
252
+ icon.style.height = iconSize + "px";
253
+ icon.src = iconMap[mark] || iconMap["pending"];
254
+ const type = data == null ? void 0 : data.type;
255
+ const isValidType = type === "text" || type === "image";
256
+ const isClickable = !!(data == null ? void 0 : data.dataId) && isValidType && mark !== "pending";
257
+ let tooltip = container.querySelector(".satyamark-tooltip");
258
+ if (!tooltip) {
259
+ tooltip = document.createElement("div");
260
+ tooltip.style.position = "absolute";
261
+ tooltip.style.top = `${icon.offsetTop - 6}px`;
262
+ tooltip.style.left = `${icon.offsetLeft + icon.offsetWidth / 2}px`;
263
+ tooltip.style.transform = "translate(-50%, -100%)";
264
+ tooltip.style.background = "rgba(0,0,0,0.85)";
265
+ tooltip.style.color = "#fff";
266
+ tooltip.style.padding = "4px 8px";
267
+ tooltip.style.borderRadius = "6px";
268
+ tooltip.style.fontSize = "11px";
269
+ tooltip.style.whiteSpace = "nowrap";
270
+ tooltip.style.pointerEvents = "none";
271
+ tooltip.style.opacity = "0";
272
+ tooltip.style.transition = "opacity 0.15s ease";
273
+ container.appendChild(tooltip);
274
+ }
275
+ tooltip.textContent = mark.toUpperCase();
276
+ if (isClickable) {
277
+ icon.style.cursor = "pointer";
278
+ icon.onmouseenter = () => {
279
+ tooltip.style.opacity = "1";
280
+ };
281
+ icon.onmouseleave = () => {
282
+ tooltip.style.opacity = "0";
283
+ };
284
+ icon.onclick = () => {
285
+ window.open(
286
+ `${satyamark_url}/${type}/${data.dataId}`,
287
+ "_blank"
288
+ );
289
+ };
290
+ } else {
291
+ icon.style.cursor = "default";
292
+ icon.onclick = null;
293
+ icon.onmouseenter = null;
294
+ icon.onmouseleave = null;
295
+ tooltip.style.opacity = "0";
296
+ }
297
+ }
298
+ onReceive((data) => {
299
+ var _a, _b;
300
+ if (!(data == null ? void 0 : data.jobId))
301
+ return;
302
+ updateIcon(data.jobId, (_b = (_a = data.mark) == null ? void 0 : _a.toLowerCase()) != null ? _b : "pending", data);
303
+ });
304
+ // Annotate the CommonJS export names for ESM import in node:
305
+ 0 && (module.exports = {
306
+ init,
307
+ onConnected,
308
+ onReceive,
309
+ process,
310
+ receiveData,
311
+ registerStatus,
312
+ sendData
313
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,270 @@
1
+ // src/satyamark_connect.ts
2
+ var wsUrl = "wss://satyamark.onrender.com";
3
+ var socket = null;
4
+ var storedConnectionData = null;
5
+ var listeners = [];
6
+ var onConnectedCb = null;
7
+ function onReceive(cb) {
8
+ listeners.push(cb);
9
+ return () => {
10
+ const idx = listeners.indexOf(cb);
11
+ if (idx !== -1)
12
+ listeners.splice(idx, 1);
13
+ };
14
+ }
15
+ function onConnected(cb) {
16
+ onConnectedCb = cb;
17
+ }
18
+ function init(connectionData, options) {
19
+ var _a;
20
+ if (socket && socket.readyState == WebSocket.OPEN && storedConnectionData == connectionData) {
21
+ console.log("Already Connected: ", connectionData);
22
+ return;
23
+ }
24
+ socket = new WebSocket(wsUrl);
25
+ onConnectedCb = (_a = options == null ? void 0 : options.onConnected) != null ? _a : onConnectedCb;
26
+ socket.onopen = () => {
27
+ console.log("Connected to server: ", connectionData.user_id);
28
+ safeSend({
29
+ type: "handshake",
30
+ clientId: connectionData.user_id,
31
+ appId: connectionData.app_id
32
+ });
33
+ onConnectedCb == null ? void 0 : onConnectedCb(connectionData);
34
+ };
35
+ socket.onmessage = (event) => {
36
+ receiveData(JSON.parse(event.data));
37
+ };
38
+ socket.onclose = () => {
39
+ console.log("Server connection closed");
40
+ };
41
+ storedConnectionData = connectionData;
42
+ }
43
+ function isSocketOpen() {
44
+ return socket && socket.readyState === WebSocket.OPEN;
45
+ }
46
+ function assert(condition, message) {
47
+ if (!condition)
48
+ throw new Error(message);
49
+ }
50
+ function safeSend(msg) {
51
+ if (!storedConnectionData) {
52
+ console.warn("No connectionData found. Call connect() first.");
53
+ return;
54
+ }
55
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
56
+ console.warn("Socket not ready");
57
+ return;
58
+ }
59
+ socket.send(JSON.stringify(msg));
60
+ }
61
+ function uniqueTimestamp() {
62
+ const now = /* @__PURE__ */ new Date();
63
+ const yyyy = now.getFullYear();
64
+ const MM = String(now.getMonth() + 1).padStart(2, "0");
65
+ const dd = String(now.getDate()).padStart(2, "0");
66
+ const hh = String(now.getHours()).padStart(2, "0");
67
+ const mm = String(now.getMinutes()).padStart(2, "0");
68
+ const ss = String(now.getSeconds()).padStart(2, "0");
69
+ const ms = String(now.getMilliseconds()).padStart(3, "0");
70
+ const micro = String(Math.floor(Math.random() * 1e3)).padStart(3, "0");
71
+ return `${yyyy}${MM}${dd}${hh}${mm}${ss}${ms}${micro}`;
72
+ }
73
+ function sendData(text, image_url, dataId) {
74
+ if (!storedConnectionData) {
75
+ console.log("No connectionData found. Call connect() first.");
76
+ return;
77
+ }
78
+ if (!socket || socket.readyState !== WebSocket.OPEN) {
79
+ console.log("Socket not ready");
80
+ return;
81
+ }
82
+ assert(storedConnectionData, "Call init() before sendData()");
83
+ assert(isSocketOpen(), "WebSocket is not ready");
84
+ assert(dataId, "dataId is required");
85
+ assert(text || image_url, "Provide text or image_url");
86
+ if (text)
87
+ assert(text.trim().length >= 3, "Text must be at least 3 characters");
88
+ const { app_id, user_id } = storedConnectionData;
89
+ const timestamp = uniqueTimestamp();
90
+ const jobId = `${app_id}_${user_id}_${dataId}_${timestamp}`;
91
+ const data = {
92
+ clientId: user_id,
93
+ jobId,
94
+ text,
95
+ image_url
96
+ };
97
+ socket.send(JSON.stringify(data));
98
+ return jobId;
99
+ }
100
+ function receiveData(data) {
101
+ if (!storedConnectionData || data.clientId != storedConnectionData.user_id)
102
+ return;
103
+ for (const cb of Array.from(listeners)) {
104
+ try {
105
+ cb(data);
106
+ } catch (err) {
107
+ console.error("listener error", err);
108
+ }
109
+ }
110
+ }
111
+
112
+ // src/satyamark_process.ts
113
+ var mergeText = (texts) => texts.join(", ");
114
+ var isValidImageUrl = (url) => {
115
+ return new Promise((resolve) => {
116
+ const img = new Image();
117
+ img.onload = () => resolve(true);
118
+ img.onerror = () => resolve(false);
119
+ img.src = url;
120
+ });
121
+ };
122
+ var getFirstValidImage = async (urls) => {
123
+ for (const url of urls) {
124
+ if (await isValidImageUrl(url))
125
+ return url;
126
+ }
127
+ return null;
128
+ };
129
+ var extractFromDiv = (root) => {
130
+ var _a;
131
+ const images = Array.from(root.querySelectorAll("img")).map((img) => img.src);
132
+ const text = [];
133
+ const walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT);
134
+ let node;
135
+ while (node = walker.nextNode()) {
136
+ const trimmed = (_a = node.textContent) == null ? void 0 : _a.trim();
137
+ if (trimmed)
138
+ text.push(trimmed);
139
+ }
140
+ return { text, images };
141
+ };
142
+ async function process(divRef, dataId) {
143
+ if (!divRef) {
144
+ throw new Error("Invalid root element");
145
+ }
146
+ if (!dataId) {
147
+ throw new Error("dataId is required");
148
+ }
149
+ const { text, images } = extractFromDiv(divRef);
150
+ const mergedText = mergeText(text);
151
+ const validImage = await getFirstValidImage(images);
152
+ if (!mergedText && !validImage) {
153
+ throw new Error("No valid text or image found in the element");
154
+ }
155
+ if (mergedText && mergedText.length < 3) {
156
+ throw new Error("Extracted text is too short");
157
+ }
158
+ const jobId = sendData(mergedText, validImage != null ? validImage : "", dataId);
159
+ return jobId;
160
+ }
161
+
162
+ // src/satyamark_status_controller.ts
163
+ import verifyableIcon from "./verifyable-AYGD2AEO.png";
164
+ import unverifyableIcon from "./unverifyable-2LSQEVJT.png";
165
+ import insufficientIcon from "./insufficient-FBXL3GLE.png";
166
+ import correctIcon from "./correct-AW5V6TXB.png";
167
+ import incorrectIcon from "./incorrect-KHIGXY2C.png";
168
+ import pendingIcon from "./pending-SWS5I5G5.png";
169
+ import aiIcon from "./ai-J46U5IDK.png";
170
+ import nonaiIcon from "./nonai-LPEFMQBT.png";
171
+ var jobMap = {};
172
+ var DEFAULT_ICON_SIZE = 20;
173
+ var satyamark_url = "https://satyamark.vercel.app/";
174
+ var iconMap = {
175
+ verifyable: verifyableIcon,
176
+ unverifyable: unverifyableIcon,
177
+ insufficient: insufficientIcon,
178
+ correct: correctIcon,
179
+ incorrect: incorrectIcon,
180
+ pending: pendingIcon,
181
+ ai: aiIcon,
182
+ nonai: nonaiIcon
183
+ };
184
+ function registerStatus(jobId, rootElement, options = {}) {
185
+ var _a;
186
+ jobMap[jobId] = {
187
+ root: rootElement,
188
+ iconSize: (_a = options.iconSize) != null ? _a : DEFAULT_ICON_SIZE
189
+ };
190
+ updateIcon(jobId, "pending", null);
191
+ }
192
+ function updateIcon(jobId, mark, data) {
193
+ const entry = jobMap[jobId];
194
+ if (!entry)
195
+ return;
196
+ const { root, iconSize } = entry;
197
+ const container = root.querySelector("[data-satyamark-status-container]");
198
+ if (!container)
199
+ return;
200
+ container.style.position = "relative";
201
+ let icon = container.querySelector("img");
202
+ if (!icon) {
203
+ icon = document.createElement("img");
204
+ icon.alt = "status";
205
+ icon.style.objectFit = "contain";
206
+ icon.style.display = "block";
207
+ container.appendChild(icon);
208
+ }
209
+ icon.style.width = iconSize + "px";
210
+ icon.style.height = iconSize + "px";
211
+ icon.src = iconMap[mark] || iconMap["pending"];
212
+ const type = data == null ? void 0 : data.type;
213
+ const isValidType = type === "text" || type === "image";
214
+ const isClickable = !!(data == null ? void 0 : data.dataId) && isValidType && mark !== "pending";
215
+ let tooltip = container.querySelector(".satyamark-tooltip");
216
+ if (!tooltip) {
217
+ tooltip = document.createElement("div");
218
+ tooltip.style.position = "absolute";
219
+ tooltip.style.top = `${icon.offsetTop - 6}px`;
220
+ tooltip.style.left = `${icon.offsetLeft + icon.offsetWidth / 2}px`;
221
+ tooltip.style.transform = "translate(-50%, -100%)";
222
+ tooltip.style.background = "rgba(0,0,0,0.85)";
223
+ tooltip.style.color = "#fff";
224
+ tooltip.style.padding = "4px 8px";
225
+ tooltip.style.borderRadius = "6px";
226
+ tooltip.style.fontSize = "11px";
227
+ tooltip.style.whiteSpace = "nowrap";
228
+ tooltip.style.pointerEvents = "none";
229
+ tooltip.style.opacity = "0";
230
+ tooltip.style.transition = "opacity 0.15s ease";
231
+ container.appendChild(tooltip);
232
+ }
233
+ tooltip.textContent = mark.toUpperCase();
234
+ if (isClickable) {
235
+ icon.style.cursor = "pointer";
236
+ icon.onmouseenter = () => {
237
+ tooltip.style.opacity = "1";
238
+ };
239
+ icon.onmouseleave = () => {
240
+ tooltip.style.opacity = "0";
241
+ };
242
+ icon.onclick = () => {
243
+ window.open(
244
+ `${satyamark_url}/${type}/${data.dataId}`,
245
+ "_blank"
246
+ );
247
+ };
248
+ } else {
249
+ icon.style.cursor = "default";
250
+ icon.onclick = null;
251
+ icon.onmouseenter = null;
252
+ icon.onmouseleave = null;
253
+ tooltip.style.opacity = "0";
254
+ }
255
+ }
256
+ onReceive((data) => {
257
+ var _a, _b;
258
+ if (!(data == null ? void 0 : data.jobId))
259
+ return;
260
+ updateIcon(data.jobId, (_b = (_a = data.mark) == null ? void 0 : _a.toLowerCase()) != null ? _b : "pending", data);
261
+ });
262
+ export {
263
+ init,
264
+ onConnected,
265
+ onReceive,
266
+ process,
267
+ receiveData,
268
+ registerStatus,
269
+ sendData
270
+ };
Binary file
Binary file
Binary file
package/dist/nonai.png ADDED
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "satyamark-react",
3
+ "version": "0.0.1",
4
+ "description": "Official React SDK for SatyaMark — a lightweight UI library to embed transparent content verification marks, confidence indicators, and trust signals into React applications.",
5
+ "license": "MIT",
6
+ "author": "DK",
7
+ "homepage": "https://satyamark.vercel.app/",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/DhirajKarangale/SatyaMark.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/DhirajKarangale/SatyaMark/issues"
14
+ },
15
+ "keywords": [
16
+ "react",
17
+ "verification",
18
+ "trust",
19
+ "fact-checking",
20
+ "ai-detection",
21
+ "misinformation",
22
+ "sdk",
23
+ "ui-library",
24
+ "open-source",
25
+ "satyamark"
26
+ ],
27
+ "main": "dist/index.cjs.js",
28
+ "module": "dist/index.mjs",
29
+ "types": "dist/index.d.ts",
30
+ "files": [
31
+ "dist",
32
+ "public"
33
+ ],
34
+ "exports": {
35
+ ".": {
36
+ "import": "./dist/index.mjs",
37
+ "require": "./dist/index.cjs.js",
38
+ "types": "./dist/index.d.ts"
39
+ }
40
+ },
41
+ "sideEffects": false,
42
+ "scripts": {
43
+ "clean": "rimraf dist",
44
+ "build": "tsup",
45
+ "build2": "tsup src/index.ts --format esm,cjs --dts",
46
+ "prepare": "npm run clean && npm run build"
47
+ },
48
+ "peerDependencies": {
49
+ "react": ">=16.8"
50
+ },
51
+ "devDependencies": {
52
+ "rimraf": "^5.0.0",
53
+ "tsup": "^6.7.0",
54
+ "typescript": "^5.0.0"
55
+ }
56
+ }
Binary file