backpack-viewer 0.7.1 → 0.7.3
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.
|
@@ -1,357 +1,199 @@
|
|
|
1
|
-
const
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
position: "top-right",
|
|
11
|
-
onClick: () => toggleSharePanel(viewer),
|
|
12
|
-
});
|
|
1
|
+
const E = "https://app.backpackontology.com", x = `${E}/.well-known/oauth-authorization-server`, _ = new Uint8Array([66, 80, 65, 75]), S = 1;
|
|
2
|
+
let m = null;
|
|
3
|
+
function I(e) {
|
|
4
|
+
e.registerTaskbarIcon({
|
|
5
|
+
label: "Share",
|
|
6
|
+
iconText: "↗",
|
|
7
|
+
position: "bottom-right",
|
|
8
|
+
onClick: () => N(e)
|
|
9
|
+
});
|
|
13
10
|
}
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
11
|
+
function N(e) {
|
|
12
|
+
if (m && m.isVisible()) {
|
|
13
|
+
m.setVisible(!1);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const t = e.getGraphName();
|
|
17
|
+
if (!t) return;
|
|
18
|
+
const n = document.createElement("div");
|
|
19
|
+
n.className = "share-panel-body", m ? (m.element.replaceChildren(), m.element.appendChild(n), m.setTitle(`Share "${t}"`), m.setVisible(!0), m.bringToFront()) : (n.textContent = "Loading...", m = e.mountPanel(n, {
|
|
20
|
+
title: `Share "${t}"`,
|
|
21
|
+
defaultPosition: { left: window.innerWidth - 420, top: 80 },
|
|
22
|
+
showFullscreenButton: !1,
|
|
23
|
+
onClose: () => {
|
|
24
|
+
m = null;
|
|
18
25
|
}
|
|
19
|
-
|
|
20
|
-
if (!graphName)
|
|
21
|
-
return;
|
|
22
|
-
const body = document.createElement("div");
|
|
23
|
-
body.className = "share-panel-body";
|
|
24
|
-
if (panel) {
|
|
25
|
-
panel.element.replaceChildren();
|
|
26
|
-
panel.element.appendChild(body);
|
|
27
|
-
panel.setTitle(`Share "${graphName}"`);
|
|
28
|
-
panel.setVisible(true);
|
|
29
|
-
panel.bringToFront();
|
|
30
|
-
}
|
|
31
|
-
else {
|
|
32
|
-
body.textContent = "Loading...";
|
|
33
|
-
panel = viewer.mountPanel(body, {
|
|
34
|
-
title: `Share "${graphName}"`,
|
|
35
|
-
defaultPosition: { left: window.innerWidth - 420, top: 80 },
|
|
36
|
-
showFullscreenButton: false,
|
|
37
|
-
onClose: () => { panel = null; },
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
renderShareForm(viewer, body);
|
|
26
|
+
})), w(e, n);
|
|
41
27
|
}
|
|
42
|
-
async function
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (!token) {
|
|
46
|
-
renderUpsell(viewer, container);
|
|
47
|
-
return;
|
|
48
|
-
}
|
|
49
|
-
renderForm(viewer, container, token);
|
|
28
|
+
async function w(e, t) {
|
|
29
|
+
const n = await e.settings.get("relay_token");
|
|
30
|
+
t.replaceChildren(), n ? v(e, t, n) : A(e, t);
|
|
50
31
|
}
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
cta.textContent = "Sign in to share";
|
|
64
|
-
cta.addEventListener("click", () => startOAuthFlow(viewer, container));
|
|
65
|
-
wrapper.appendChild(cta);
|
|
66
|
-
const tokenLink = document.createElement("button");
|
|
67
|
-
tokenLink.className = "share-token-link";
|
|
68
|
-
tokenLink.textContent = "Or paste an API token";
|
|
69
|
-
tokenLink.addEventListener("click", () => renderTokenInput(viewer, container));
|
|
70
|
-
wrapper.appendChild(tokenLink);
|
|
71
|
-
const trust = document.createElement("p");
|
|
72
|
-
trust.className = "share-trust";
|
|
73
|
-
trust.textContent =
|
|
74
|
-
"Free account. Your graph is encrypted before upload — we can't read it.";
|
|
75
|
-
wrapper.appendChild(trust);
|
|
76
|
-
container.replaceChildren(wrapper);
|
|
32
|
+
function A(e, t) {
|
|
33
|
+
const n = document.createElement("div");
|
|
34
|
+
n.className = "share-upsell", n.innerHTML = `
|
|
35
|
+
<h4>Share this graph with anyone</h4>
|
|
36
|
+
<p>Encrypt your graph and get a shareable link. Recipients open it in their browser — no install needed. Your data stays encrypted on our servers.</p>
|
|
37
|
+
`;
|
|
38
|
+
const a = document.createElement("button");
|
|
39
|
+
a.className = "share-cta-btn", a.textContent = "Sign in to share", a.addEventListener("click", () => T(e, t)), n.appendChild(a);
|
|
40
|
+
const o = document.createElement("button");
|
|
41
|
+
o.className = "share-token-link", o.textContent = "Or paste an API token", o.addEventListener("click", () => P(e, t)), n.appendChild(o);
|
|
42
|
+
const i = document.createElement("p");
|
|
43
|
+
i.className = "share-trust", i.textContent = "Free account. Your graph is encrypted before upload — we can't read it.", n.appendChild(i), t.replaceChildren(n);
|
|
77
44
|
}
|
|
78
|
-
function
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
saveBtn.addEventListener("click", async () => {
|
|
95
|
-
const val = input.value.trim();
|
|
96
|
-
if (!val)
|
|
97
|
-
return;
|
|
98
|
-
await viewer.settings.set("relay_token", val);
|
|
99
|
-
renderShareForm(viewer, container);
|
|
100
|
-
});
|
|
101
|
-
row.appendChild(saveBtn);
|
|
102
|
-
const backBtn = document.createElement("button");
|
|
103
|
-
backBtn.className = "share-btn-secondary";
|
|
104
|
-
backBtn.textContent = "Back";
|
|
105
|
-
backBtn.addEventListener("click", () => renderShareForm(viewer, container));
|
|
106
|
-
row.appendChild(backBtn);
|
|
107
|
-
wrapper.appendChild(row);
|
|
108
|
-
container.replaceChildren(wrapper);
|
|
45
|
+
function P(e, t) {
|
|
46
|
+
const n = document.createElement("div");
|
|
47
|
+
n.className = "share-token-input";
|
|
48
|
+
const a = document.createElement("p");
|
|
49
|
+
a.textContent = "Paste your API token from Backpack App settings:", n.appendChild(a);
|
|
50
|
+
const o = document.createElement("input");
|
|
51
|
+
o.type = "password", o.placeholder = "Token", o.className = "share-input", n.appendChild(o);
|
|
52
|
+
const i = document.createElement("div");
|
|
53
|
+
i.className = "share-btn-row";
|
|
54
|
+
const d = document.createElement("button");
|
|
55
|
+
d.className = "share-btn-primary", d.textContent = "Save", d.addEventListener("click", async () => {
|
|
56
|
+
const c = o.value.trim();
|
|
57
|
+
c && (await e.settings.set("relay_token", c), w(e, t));
|
|
58
|
+
}), i.appendChild(d);
|
|
59
|
+
const l = document.createElement("button");
|
|
60
|
+
l.className = "share-btn-secondary", l.textContent = "Back", l.addEventListener("click", () => w(e, t)), i.appendChild(l), n.appendChild(i), t.replaceChildren(n);
|
|
109
61
|
}
|
|
110
|
-
async function
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
authUrl.searchParams.set("code_challenge_method", "S256");
|
|
133
|
-
authUrl.searchParams.set("state", state);
|
|
134
|
-
if (!authUrl.searchParams.has("scope")) {
|
|
135
|
-
authUrl.searchParams.set("scope", "openid email profile offline_access");
|
|
136
|
-
}
|
|
137
|
-
// Store state for the callback
|
|
138
|
-
sessionStorage.setItem("share_oauth_state", state);
|
|
139
|
-
sessionStorage.setItem("share_oauth_verifier", codeVerifier);
|
|
140
|
-
sessionStorage.setItem("share_oauth_token_endpoint", meta.token_endpoint);
|
|
141
|
-
sessionStorage.setItem("share_oauth_client_id", client.client_id);
|
|
142
|
-
sessionStorage.setItem("share_oauth_redirect_uri", redirectUri);
|
|
143
|
-
// Open popup
|
|
144
|
-
const popup = window.open(authUrl.toString(), "backpack-share-auth", "width=500,height=700");
|
|
145
|
-
// Listen for the callback
|
|
146
|
-
const handler = async (event) => {
|
|
147
|
-
if (event.data?.type !== "backpack-oauth-callback")
|
|
148
|
-
return;
|
|
149
|
-
window.removeEventListener("message", handler);
|
|
150
|
-
const { code, returnedState } = event.data;
|
|
151
|
-
if (returnedState !== state)
|
|
152
|
-
return;
|
|
153
|
-
// Exchange code for token
|
|
154
|
-
const tokenRes = await viewer.fetch(meta.token_endpoint, {
|
|
155
|
-
method: "POST",
|
|
156
|
-
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
157
|
-
body: new URLSearchParams({
|
|
158
|
-
grant_type: "authorization_code",
|
|
159
|
-
code,
|
|
160
|
-
redirect_uri: redirectUri,
|
|
161
|
-
client_id: client.client_id,
|
|
162
|
-
code_verifier: codeVerifier,
|
|
163
|
-
}).toString(),
|
|
164
|
-
});
|
|
165
|
-
const tokenData = (await tokenRes.json());
|
|
166
|
-
await viewer.settings.set("relay_token", tokenData.access_token);
|
|
167
|
-
renderShareForm(viewer, container);
|
|
168
|
-
};
|
|
169
|
-
window.addEventListener("message", handler);
|
|
170
|
-
// Fallback: if popup is blocked, show a link
|
|
171
|
-
if (!popup || popup.closed) {
|
|
172
|
-
const msg = document.createElement("p");
|
|
173
|
-
msg.className = "share-error";
|
|
174
|
-
msg.textContent = "Popup blocked. ";
|
|
175
|
-
const link = document.createElement("a");
|
|
176
|
-
link.href = authUrl.toString();
|
|
177
|
-
link.target = "_blank";
|
|
178
|
-
link.textContent = "Click here to sign in";
|
|
179
|
-
msg.appendChild(link);
|
|
180
|
-
container.appendChild(msg);
|
|
181
|
-
}
|
|
62
|
+
async function T(e, t) {
|
|
63
|
+
try {
|
|
64
|
+
const a = await (await fetch(x)).json(), i = await (await fetch(a.registration_endpoint, { method: "POST" })).json(), d = B(), l = await $(d), c = window.location.origin + "/oauth/callback", r = crypto.randomUUID(), s = new URL(a.authorization_endpoint);
|
|
65
|
+
s.searchParams.set("client_id", i.client_id), s.searchParams.set("redirect_uri", c), s.searchParams.set("response_type", "code"), s.searchParams.set("code_challenge", l), s.searchParams.set("code_challenge_method", "S256"), s.searchParams.set("state", r), s.searchParams.has("scope") || s.searchParams.set("scope", "openid email profile offline_access"), sessionStorage.setItem("share_oauth_state", r);
|
|
66
|
+
const h = window.open(s.toString(), "backpack-share-auth", "width=500,height=700"), g = async (p) => {
|
|
67
|
+
var f;
|
|
68
|
+
if (((f = p.data) == null ? void 0 : f.type) !== "backpack-oauth-callback") return;
|
|
69
|
+
window.removeEventListener("message", g);
|
|
70
|
+
const { code: u, returnedState: k } = p.data;
|
|
71
|
+
if (k !== r) return;
|
|
72
|
+
const C = await (await fetch(a.token_endpoint, {
|
|
73
|
+
method: "POST",
|
|
74
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
75
|
+
body: new URLSearchParams({ grant_type: "authorization_code", code: u, redirect_uri: c, client_id: i.client_id, code_verifier: d }).toString()
|
|
76
|
+
})).json();
|
|
77
|
+
await e.settings.set("relay_token", C.access_token), w(e, t);
|
|
78
|
+
};
|
|
79
|
+
if (window.addEventListener("message", g), !h || h.closed) {
|
|
80
|
+
const p = document.createElement("p");
|
|
81
|
+
p.className = "share-error", p.textContent = "Popup blocked. ";
|
|
82
|
+
const u = document.createElement("a");
|
|
83
|
+
u.href = s.toString(), u.target = "_blank", u.textContent = "Click here to sign in", p.appendChild(u), t.appendChild(p);
|
|
182
84
|
}
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
85
|
+
} catch (n) {
|
|
86
|
+
const a = document.createElement("p");
|
|
87
|
+
a.className = "share-error", a.textContent = `Auth failed: ${n.message}`, t.appendChild(a);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function v(e, t, n) {
|
|
91
|
+
const a = document.createElement("div");
|
|
92
|
+
a.className = "share-form";
|
|
93
|
+
const o = document.createElement("label");
|
|
94
|
+
o.className = "share-toggle-row";
|
|
95
|
+
const i = document.createElement("input");
|
|
96
|
+
i.type = "checkbox", i.checked = !0, o.appendChild(i);
|
|
97
|
+
const d = document.createElement("span");
|
|
98
|
+
d.textContent = "Encrypt (recommended)", o.appendChild(d), a.appendChild(o);
|
|
99
|
+
const l = document.createElement("div");
|
|
100
|
+
l.className = "share-pass-row";
|
|
101
|
+
const c = document.createElement("input");
|
|
102
|
+
c.type = "password", c.placeholder = "Passphrase (optional)", c.className = "share-input", l.appendChild(c), a.appendChild(l);
|
|
103
|
+
const r = document.createElement("button");
|
|
104
|
+
r.className = "share-btn-primary", r.textContent = "Share", r.addEventListener("click", async () => {
|
|
105
|
+
r.disabled = !0, r.textContent = "Encrypting...";
|
|
106
|
+
try {
|
|
107
|
+
await L(e, t, n, i.checked, c.value.trim());
|
|
108
|
+
} catch (g) {
|
|
109
|
+
r.disabled = !1, r.textContent = "Share";
|
|
110
|
+
const p = document.createElement("p");
|
|
111
|
+
p.className = "share-error", p.textContent = g.message, a.appendChild(p);
|
|
188
112
|
}
|
|
113
|
+
}), a.appendChild(r);
|
|
114
|
+
const s = document.createElement("p");
|
|
115
|
+
s.className = "share-note", s.textContent = "Recipients open the link in their browser. No install needed.", a.appendChild(s);
|
|
116
|
+
const h = document.createElement("button");
|
|
117
|
+
h.className = "share-token-link", h.textContent = "Sign out", h.addEventListener("click", async () => {
|
|
118
|
+
await e.settings.remove("relay_token"), w(e, t);
|
|
119
|
+
}), a.appendChild(h), t.replaceChildren(a);
|
|
189
120
|
}
|
|
190
|
-
function
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
shareBtn.textContent = "Share";
|
|
217
|
-
shareBtn.addEventListener("click", async () => {
|
|
218
|
-
shareBtn.disabled = true;
|
|
219
|
-
shareBtn.textContent = "Encrypting...";
|
|
220
|
-
try {
|
|
221
|
-
await doShare(viewer, container, token, encryptCheckbox.checked, passInput.value.trim());
|
|
222
|
-
}
|
|
223
|
-
catch (err) {
|
|
224
|
-
shareBtn.disabled = false;
|
|
225
|
-
shareBtn.textContent = "Share";
|
|
226
|
-
const errMsg = document.createElement("p");
|
|
227
|
-
errMsg.className = "share-error";
|
|
228
|
-
errMsg.textContent = err.message;
|
|
229
|
-
wrapper.appendChild(errMsg);
|
|
230
|
-
}
|
|
231
|
-
});
|
|
232
|
-
wrapper.appendChild(shareBtn);
|
|
233
|
-
const note = document.createElement("p");
|
|
234
|
-
note.className = "share-note";
|
|
235
|
-
note.textContent = "Recipients open the link in their browser. No install needed.";
|
|
236
|
-
wrapper.appendChild(note);
|
|
237
|
-
// Logout link
|
|
238
|
-
const logoutBtn = document.createElement("button");
|
|
239
|
-
logoutBtn.className = "share-token-link";
|
|
240
|
-
logoutBtn.textContent = "Sign out";
|
|
241
|
-
logoutBtn.addEventListener("click", async () => {
|
|
242
|
-
await viewer.settings.remove("relay_token");
|
|
243
|
-
renderShareForm(viewer, container);
|
|
244
|
-
});
|
|
245
|
-
wrapper.appendChild(logoutBtn);
|
|
246
|
-
container.replaceChildren(wrapper);
|
|
121
|
+
async function L(e, t, n, a, o) {
|
|
122
|
+
const i = e.getGraph(), d = e.getGraphName();
|
|
123
|
+
if (!i || !d) throw new Error("No graph loaded");
|
|
124
|
+
const l = new TextEncoder().encode(JSON.stringify(i));
|
|
125
|
+
let c, r, s = "";
|
|
126
|
+
if (a) {
|
|
127
|
+
const y = await import("../index-B-LFGJqd.js"), C = await y.generateX25519Identity(), f = await y.identityToRecipient(C), b = new y.Encrypter();
|
|
128
|
+
b.addRecipient(f), c = await b.encrypt(l), r = "age-v1", s = btoa(C).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
129
|
+
} else
|
|
130
|
+
c = l, r = "plaintext";
|
|
131
|
+
const h = await U(d, c, r), g = {
|
|
132
|
+
"Content-Type": "application/octet-stream",
|
|
133
|
+
Authorization: `Bearer ${n}`
|
|
134
|
+
};
|
|
135
|
+
o && (g["X-Passphrase"] = o);
|
|
136
|
+
const p = await fetch(`${E}/v1/share`, {
|
|
137
|
+
method: "POST",
|
|
138
|
+
headers: g,
|
|
139
|
+
body: h
|
|
140
|
+
});
|
|
141
|
+
if (!p.ok) {
|
|
142
|
+
const y = await p.text().catch(() => "");
|
|
143
|
+
throw p.status === 401 ? (await e.settings.remove("relay_token"), w(e, t), new Error("Session expired. Please sign in again.")) : new Error(`Upload failed: ${y}`);
|
|
144
|
+
}
|
|
145
|
+
const u = await p.json(), k = s ? `${u.url}#k=${s}` : u.url;
|
|
146
|
+
R(t, k, a, u.expires_at);
|
|
247
147
|
}
|
|
248
|
-
async function
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
fragmentKey = encodeKeyForFragment(keyPair.secretKey);
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
payload = plaintext;
|
|
266
|
-
format = "plaintext";
|
|
267
|
-
}
|
|
268
|
-
// Build BPAK envelope
|
|
269
|
-
const { createEnvelope } = await import("backpack-ontology");
|
|
270
|
-
const envelope = await createEnvelope(graphName, payload, format, 1);
|
|
271
|
-
// Upload to relay
|
|
272
|
-
const headers = {
|
|
273
|
-
"Content-Type": "application/octet-stream",
|
|
274
|
-
Authorization: `Bearer ${token}`,
|
|
275
|
-
};
|
|
276
|
-
if (passphrase) {
|
|
277
|
-
headers["X-Passphrase"] = passphrase;
|
|
278
|
-
}
|
|
279
|
-
const res = await viewer.fetch(`${RELAY_URL}/v1/share`, {
|
|
280
|
-
method: "POST",
|
|
281
|
-
headers,
|
|
282
|
-
body: envelope,
|
|
283
|
-
});
|
|
284
|
-
if (!res.ok) {
|
|
285
|
-
const body = await res.text().catch(() => "");
|
|
286
|
-
if (res.status === 401) {
|
|
287
|
-
// Token expired — clear and show upsell
|
|
288
|
-
await viewer.settings.remove("relay_token");
|
|
289
|
-
renderShareForm(viewer, container);
|
|
290
|
-
throw new Error("Session expired. Please sign in again.");
|
|
291
|
-
}
|
|
292
|
-
throw new Error(`Upload failed: ${body}`);
|
|
293
|
-
}
|
|
294
|
-
const result = (await res.json());
|
|
295
|
-
const shareLink = fragmentKey
|
|
296
|
-
? `${result.url}#k=${fragmentKey}`
|
|
297
|
-
: result.url;
|
|
298
|
-
renderSuccess(container, shareLink, encrypted, result.expires_at);
|
|
148
|
+
async function U(e, t, n) {
|
|
149
|
+
const a = new ArrayBuffer(t.byteLength);
|
|
150
|
+
new Uint8Array(a).set(t);
|
|
151
|
+
const o = await crypto.subtle.digest("SHA-256", a), i = "sha256:" + Array.from(new Uint8Array(o)).map((h) => h.toString(16).padStart(2, "0")).join(""), d = JSON.stringify({
|
|
152
|
+
format: n,
|
|
153
|
+
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
154
|
+
backpack_name: e,
|
|
155
|
+
graph_count: 1,
|
|
156
|
+
checksum: i
|
|
157
|
+
}), l = new TextEncoder().encode(d), c = new ArrayBuffer(4);
|
|
158
|
+
new DataView(c).setUint32(0, l.length, !1);
|
|
159
|
+
const r = new Uint8Array(9 + l.length + t.length);
|
|
160
|
+
let s = 0;
|
|
161
|
+
return r.set(_, s), s += 4, r[s] = S, s += 1, r.set(new Uint8Array(c), s), s += 4, r.set(l, s), s += l.length, r.set(t, s), r;
|
|
299
162
|
}
|
|
300
|
-
function
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
wrapper.appendChild(linkRow);
|
|
324
|
-
if (encrypted) {
|
|
325
|
-
const note = document.createElement("p");
|
|
326
|
-
note.className = "share-note";
|
|
327
|
-
note.textContent =
|
|
328
|
-
"The decryption key is in the link. Anyone with the full link can view this graph. The server cannot read your data.";
|
|
329
|
-
wrapper.appendChild(note);
|
|
330
|
-
}
|
|
331
|
-
if (expiresAt) {
|
|
332
|
-
const exp = document.createElement("p");
|
|
333
|
-
exp.className = "share-note";
|
|
334
|
-
exp.textContent = `Expires: ${new Date(expiresAt).toLocaleDateString()}`;
|
|
335
|
-
wrapper.appendChild(exp);
|
|
336
|
-
}
|
|
337
|
-
container.replaceChildren(wrapper);
|
|
163
|
+
function R(e, t, n, a) {
|
|
164
|
+
const o = document.createElement("div");
|
|
165
|
+
o.className = "share-success";
|
|
166
|
+
const i = document.createElement("h4");
|
|
167
|
+
i.textContent = "Shared!", o.appendChild(i);
|
|
168
|
+
const d = document.createElement("div");
|
|
169
|
+
d.className = "share-link-row";
|
|
170
|
+
const l = document.createElement("input");
|
|
171
|
+
l.type = "text", l.readOnly = !0, l.value = t, l.className = "share-link-input", d.appendChild(l);
|
|
172
|
+
const c = document.createElement("button");
|
|
173
|
+
if (c.className = "share-btn-primary", c.textContent = "Copy", c.addEventListener("click", () => {
|
|
174
|
+
navigator.clipboard.writeText(t), c.textContent = "Copied!", setTimeout(() => {
|
|
175
|
+
c.textContent = "Copy";
|
|
176
|
+
}, 2e3);
|
|
177
|
+
}), d.appendChild(c), o.appendChild(d), n) {
|
|
178
|
+
const r = document.createElement("p");
|
|
179
|
+
r.className = "share-note", r.textContent = "The decryption key is in the link. Anyone with the full link can view. The server cannot read your data.", o.appendChild(r);
|
|
180
|
+
}
|
|
181
|
+
if (a) {
|
|
182
|
+
const r = document.createElement("p");
|
|
183
|
+
r.className = "share-note", r.textContent = `Expires: ${new Date(a).toLocaleDateString()}`, o.appendChild(r);
|
|
184
|
+
}
|
|
185
|
+
e.replaceChildren(o);
|
|
338
186
|
}
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
crypto.getRandomValues(arr);
|
|
343
|
-
return btoa(String.fromCharCode(...arr))
|
|
344
|
-
.replace(/\+/g, "-")
|
|
345
|
-
.replace(/\//g, "_")
|
|
346
|
-
.replace(/=+$/, "");
|
|
187
|
+
function B() {
|
|
188
|
+
const e = new Uint8Array(32);
|
|
189
|
+
return crypto.getRandomValues(e), btoa(String.fromCharCode(...e)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
347
190
|
}
|
|
348
|
-
async function
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
return btoa(String.fromCharCode(...new Uint8Array(digest)))
|
|
354
|
-
.replace(/\+/g, "-")
|
|
355
|
-
.replace(/\//g, "_")
|
|
356
|
-
.replace(/=+$/, "");
|
|
191
|
+
async function $(e) {
|
|
192
|
+
const t = new TextEncoder().encode(e), n = new ArrayBuffer(t.byteLength);
|
|
193
|
+
new Uint8Array(n).set(t);
|
|
194
|
+
const a = await crypto.subtle.digest("SHA-256", n);
|
|
195
|
+
return btoa(String.fromCharCode(...new Uint8Array(a))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
357
196
|
}
|
|
197
|
+
export {
|
|
198
|
+
I as activate
|
|
199
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { defineConfig } from "vite";
|
|
2
|
+
import { resolve } from "path";
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
build: {
|
|
5
|
+
lib: {
|
|
6
|
+
entry: resolve(__dirname, "src/index.ts"),
|
|
7
|
+
formats: ["es"],
|
|
8
|
+
fileName: () => "src/index.js",
|
|
9
|
+
},
|
|
10
|
+
outDir: resolve(__dirname, "../../dist/extensions/share"),
|
|
11
|
+
emptyOutDir: false,
|
|
12
|
+
rollupOptions: {
|
|
13
|
+
// Don't externalize anything — bundle all deps into one file
|
|
14
|
+
external: [],
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "backpack-viewer",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Web-based graph visualizer for backpack-ontology — Canvas 2D, force-directed layout, live reload",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "Noah Irzinger",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"scripts": {
|
|
16
16
|
"dev": "vite",
|
|
17
|
-
"build": "rm -rf dist && tsc && tsc -p tsconfig.extensions.json && cp src/style.css dist/ && node scripts/build-extensions.js && vite build",
|
|
17
|
+
"build": "rm -rf dist && tsc && tsc -p tsconfig.extensions.json && cp src/style.css dist/ && node scripts/build-extensions.js && node scripts/bundle-extensions.js && vite build",
|
|
18
18
|
"preview": "vite preview",
|
|
19
19
|
"serve": "node bin/serve.js",
|
|
20
20
|
"prepare": "npm run build",
|