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 +220 -0
- package/dist/ai-J46U5IDK.png +0 -0
- package/dist/ai.png +0 -0
- package/dist/correct-AW5V6TXB.png +0 -0
- package/dist/correct.png +0 -0
- package/dist/incorrect-KHIGXY2C.png +0 -0
- package/dist/incorrect.png +0 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.js +313 -0
- package/dist/index.mjs +270 -0
- package/dist/insufficient-FBXL3GLE.png +0 -0
- package/dist/insufficient.png +0 -0
- package/dist/nonai-LPEFMQBT.png +0 -0
- package/dist/nonai.png +0 -0
- package/dist/pending-SWS5I5G5.png +0 -0
- package/dist/pending.png +0 -0
- package/dist/uncertain.png +0 -0
- package/dist/unverifyable-2LSQEVJT.png +0 -0
- package/dist/unverifyable.png +0 -0
- package/dist/verifyable-AYGD2AEO.png +0 -0
- package/dist/verifyable.png +0 -0
- package/package.json +56 -0
- package/public/Logo.png +0 -0
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
|
package/dist/correct.png
ADDED
|
Binary file
|
|
Binary file
|
|
Binary file
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
package/dist/pending.png
ADDED
|
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
|
+
}
|
package/public/Logo.png
ADDED
|
Binary file
|