backpack-viewer 0.7.7 → 0.7.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/serve.js +1 -1
- package/dist/app/assets/index-Dz__sU13.js +12 -0
- package/dist/app/assets/{index-D5fMGKMe.js → index-F9MHq10h.js} +2 -2
- package/dist/app/index.html +1 -1
- package/dist/extensions/share/index-B8_hkT8R.js +6277 -0
- package/dist/extensions/share/src/index.js +280 -157
- package/dist/extensions/share/style.css +59 -7
- package/dist/server-api-routes.js +71 -12
- package/package.json +1 -1
- package/dist/app/assets/index-D-H7agBH.js +0 -12
- package/dist/extensions/share/index-B-LFGJqd.js +0 -4827
|
@@ -1,200 +1,323 @@
|
|
|
1
|
-
const
|
|
1
|
+
const L = "https://app.backpackontology.com";
|
|
2
|
+
let f = L, T = `${f}/.well-known/oauth-authorization-server`;
|
|
3
|
+
const P = new Uint8Array([66, 80, 65, 75]), R = 1;
|
|
2
4
|
let m = null;
|
|
3
|
-
function
|
|
4
|
-
e.
|
|
5
|
+
async function M(t) {
|
|
6
|
+
const e = await t.settings.get("relay_url");
|
|
7
|
+
e && (f = e, T = `${f}/.well-known/oauth-authorization-server`), t.registerTaskbarIcon({
|
|
5
8
|
label: "Share",
|
|
6
9
|
iconText: "↗",
|
|
7
10
|
position: "bottom-center",
|
|
8
|
-
onClick: () =>
|
|
11
|
+
onClick: () => $(t)
|
|
9
12
|
});
|
|
10
13
|
}
|
|
11
|
-
function
|
|
14
|
+
function $(t) {
|
|
12
15
|
if (m && m.isVisible()) {
|
|
13
16
|
m.setVisible(!1);
|
|
14
17
|
return;
|
|
15
18
|
}
|
|
16
|
-
const
|
|
17
|
-
if (!
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
title: `Share "${
|
|
19
|
+
const e = t.getGraphName();
|
|
20
|
+
if (!e) return;
|
|
21
|
+
const a = document.createElement("div");
|
|
22
|
+
a.className = "share-panel-body", m ? (m.element.replaceChildren(), m.element.appendChild(a), m.setTitle(`Share "${e}"`), m.setVisible(!0), m.bringToFront()) : (a.textContent = "Loading...", m = t.mountPanel(a, {
|
|
23
|
+
title: `Share "${e}"`,
|
|
21
24
|
defaultPosition: { left: Math.max(100, (window.innerWidth - 380) / 2), top: Math.max(80, (window.innerHeight - 400) / 2) },
|
|
22
25
|
persistKey: "share-v2",
|
|
23
26
|
showFullscreenButton: !1,
|
|
24
27
|
onClose: () => {
|
|
25
28
|
m = null;
|
|
26
29
|
}
|
|
27
|
-
})),
|
|
30
|
+
})), C(t, a);
|
|
31
|
+
}
|
|
32
|
+
async function C(t, e) {
|
|
33
|
+
const a = await t.settings.get("relay_token");
|
|
34
|
+
e.replaceChildren(), a ? D(t, e, a) : I(t, e);
|
|
35
|
+
}
|
|
36
|
+
function I(t, e) {
|
|
37
|
+
const a = document.createElement("div");
|
|
38
|
+
a.className = "share-upsell";
|
|
39
|
+
const n = document.createElement("h4");
|
|
40
|
+
n.textContent = "Share this graph with anyone", a.appendChild(n);
|
|
41
|
+
const s = document.createElement("p");
|
|
42
|
+
s.textContent = "Encrypt your graph and get a shareable link. Recipients open it in their browser — no install needed.", a.appendChild(s);
|
|
43
|
+
const o = document.createElement("button");
|
|
44
|
+
o.className = "share-cta-btn", o.textContent = "Sign in to share", o.addEventListener("click", () => Y(t, e)), a.appendChild(o);
|
|
45
|
+
const r = document.createElement("button");
|
|
46
|
+
r.className = "share-token-link", r.textContent = "Or paste an API token", r.addEventListener("click", () => B(t, e)), a.appendChild(r);
|
|
47
|
+
const c = document.createElement("p");
|
|
48
|
+
c.className = "share-trust", c.textContent = "Free account. Your graph is encrypted before upload — we can’t read it.", a.appendChild(c), e.replaceChildren(a);
|
|
49
|
+
}
|
|
50
|
+
function B(t, e) {
|
|
51
|
+
const a = document.createElement("div");
|
|
52
|
+
a.className = "share-token-input";
|
|
53
|
+
const n = document.createElement("p");
|
|
54
|
+
n.textContent = "Paste your API token from your account settings:", a.appendChild(n);
|
|
55
|
+
const s = document.createElement("input");
|
|
56
|
+
s.type = "password", s.placeholder = "Token", s.className = "share-input", a.appendChild(s);
|
|
57
|
+
const o = document.createElement("div");
|
|
58
|
+
o.className = "share-btn-row";
|
|
59
|
+
const r = document.createElement("button");
|
|
60
|
+
r.className = "share-btn-primary", r.textContent = "Save", r.addEventListener("click", async () => {
|
|
61
|
+
const i = s.value.trim();
|
|
62
|
+
i && (await t.settings.set("relay_token", i), C(t, e));
|
|
63
|
+
}), o.appendChild(r);
|
|
64
|
+
const c = document.createElement("button");
|
|
65
|
+
c.className = "share-btn-secondary", c.textContent = "Back", c.addEventListener("click", () => C(t, e)), o.appendChild(c), a.appendChild(o), e.replaceChildren(a);
|
|
66
|
+
}
|
|
67
|
+
async function O(t) {
|
|
68
|
+
try {
|
|
69
|
+
const e = await fetch(`${f}/api/graphs`, {
|
|
70
|
+
headers: { Authorization: `Bearer ${t}` }
|
|
71
|
+
});
|
|
72
|
+
if (e.status === 401) return { graphs: [], error: "unauthorized" };
|
|
73
|
+
if (!e.ok) return { graphs: [], error: `status ${e.status}` };
|
|
74
|
+
const a = await e.json();
|
|
75
|
+
return { graphs: Array.isArray(a) ? a : [] };
|
|
76
|
+
} catch (e) {
|
|
77
|
+
return { graphs: [], error: e.message };
|
|
78
|
+
}
|
|
28
79
|
}
|
|
29
|
-
async function
|
|
30
|
-
const n =
|
|
31
|
-
|
|
80
|
+
async function D(t, e, a) {
|
|
81
|
+
const n = t.getGraphName(), s = document.createElement("div");
|
|
82
|
+
s.className = "share-loading", s.textContent = "Checking account…", e.replaceChildren(s);
|
|
83
|
+
const o = await O(a);
|
|
84
|
+
if (o.error === "unauthorized") {
|
|
85
|
+
await t.settings.remove("relay_token"), C(t, e);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
e.replaceChildren();
|
|
89
|
+
const r = o.graphs.find((c) => c.name === n);
|
|
90
|
+
r ? F(t, e, a, r) : V(t, e, a);
|
|
91
|
+
}
|
|
92
|
+
function F(t, e, a, n) {
|
|
93
|
+
const s = document.createElement("div");
|
|
94
|
+
s.className = "share-synced";
|
|
95
|
+
const o = document.createElement("h4");
|
|
96
|
+
if (o.textContent = "Synced to your account", s.appendChild(o), n.syncedAt) {
|
|
97
|
+
const d = document.createElement("p");
|
|
98
|
+
d.className = "share-note", d.textContent = `Last synced: ${new Date(n.syncedAt).toLocaleString()}`, s.appendChild(d);
|
|
99
|
+
}
|
|
100
|
+
const r = document.createElement("p");
|
|
101
|
+
r.className = "share-note", r.textContent = n.encrypted ? "Encrypted — server cannot read your data." : "Public — stored unencrypted.", s.appendChild(r);
|
|
102
|
+
const c = document.createElement("button");
|
|
103
|
+
c.className = "share-cta-btn", c.textContent = "Update & Share", c.addEventListener("click", async () => {
|
|
104
|
+
c.disabled = !0, c.textContent = n.encrypted ? "Encrypting…" : "Syncing…";
|
|
105
|
+
try {
|
|
106
|
+
await x(t, e, a, n.encrypted);
|
|
107
|
+
} catch (d) {
|
|
108
|
+
c.disabled = !1, c.textContent = "Update & Share", S(s), b(s, d.message);
|
|
109
|
+
}
|
|
110
|
+
}), s.appendChild(c);
|
|
111
|
+
const i = document.createElement("a");
|
|
112
|
+
i.href = f, i.target = "_blank", i.rel = "noopener", i.className = "share-token-link", i.textContent = "Open dashboard", s.appendChild(i), _(t, e, s);
|
|
32
113
|
}
|
|
33
|
-
function
|
|
114
|
+
function V(t, e, a) {
|
|
34
115
|
const n = document.createElement("div");
|
|
35
|
-
n.className = "share-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
`;
|
|
39
|
-
const a = document.createElement("button");
|
|
40
|
-
a.className = "share-cta-btn", a.textContent = "Sign in to share", a.addEventListener("click", () => v(e, t)), n.appendChild(a);
|
|
116
|
+
n.className = "share-form";
|
|
117
|
+
const s = document.createElement("p");
|
|
118
|
+
s.className = "share-description", s.textContent = "Your graph will be encrypted and synced to your Backpack account. You’ll get a shareable link.", n.appendChild(s);
|
|
41
119
|
const o = document.createElement("button");
|
|
42
|
-
o.className = "share-
|
|
120
|
+
o.className = "share-cta-btn", o.textContent = "Sync & Share", o.addEventListener("click", async () => {
|
|
121
|
+
o.disabled = !0, o.textContent = "Encrypting…";
|
|
122
|
+
try {
|
|
123
|
+
await x(t, e, a, !0);
|
|
124
|
+
} catch (c) {
|
|
125
|
+
o.disabled = !1, o.textContent = "Sync & Share";
|
|
126
|
+
const i = c.message;
|
|
127
|
+
if (i.includes("encrypted") && i.includes("limit")) {
|
|
128
|
+
z(t, e, a);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
S(n), b(n, i);
|
|
132
|
+
}
|
|
133
|
+
}), n.appendChild(o);
|
|
134
|
+
const r = document.createElement("p");
|
|
135
|
+
r.className = "share-trust", r.textContent = "Your first encrypted graph is free. Data is encrypted before upload — we can’t read it.", n.appendChild(r), _(t, e, n);
|
|
136
|
+
}
|
|
137
|
+
function z(t, e, a) {
|
|
138
|
+
const n = document.createElement("div");
|
|
139
|
+
n.className = "share-quota";
|
|
140
|
+
const s = document.createElement("h4");
|
|
141
|
+
s.textContent = "Encrypted limit reached", n.appendChild(s);
|
|
142
|
+
const o = document.createElement("p");
|
|
143
|
+
o.className = "share-description", o.textContent = "Your free account includes one encrypted graph, which is already in use.", n.appendChild(o);
|
|
144
|
+
const r = document.createElement("a");
|
|
145
|
+
r.href = `${f}/settings`, r.target = "_blank", r.rel = "noopener", r.className = "share-cta-btn share-btn-link", r.textContent = "Upgrade for unlimited encryption", n.appendChild(r);
|
|
146
|
+
const c = document.createElement("div");
|
|
147
|
+
c.className = "share-divider", n.appendChild(c);
|
|
43
148
|
const i = document.createElement("p");
|
|
44
|
-
i.className = "share-
|
|
149
|
+
i.className = "share-description", i.textContent = "Or share as a public graph:", n.appendChild(i);
|
|
150
|
+
const d = document.createElement("p");
|
|
151
|
+
d.className = "share-warning", d.textContent = "Your graph data will be stored unencrypted and visible to the server and anyone with the link.", n.appendChild(d);
|
|
152
|
+
const l = document.createElement("label");
|
|
153
|
+
l.className = "share-toggle-row";
|
|
154
|
+
const u = document.createElement("input");
|
|
155
|
+
u.type = "checkbox", l.appendChild(u);
|
|
156
|
+
const p = document.createElement("span");
|
|
157
|
+
p.textContent = "I understand this graph will not be encrypted", l.appendChild(p), n.appendChild(l);
|
|
158
|
+
const h = document.createElement("button");
|
|
159
|
+
h.className = "share-btn-secondary", h.textContent = "Share as public graph", h.disabled = !0, u.addEventListener("change", () => {
|
|
160
|
+
h.disabled = !u.checked;
|
|
161
|
+
}), h.addEventListener("click", async () => {
|
|
162
|
+
h.disabled = !0, h.textContent = "Syncing…";
|
|
163
|
+
try {
|
|
164
|
+
await x(t, e, a, !1, "public");
|
|
165
|
+
} catch (w) {
|
|
166
|
+
h.disabled = !1, h.textContent = "Share as public graph", S(n), b(n, w.message);
|
|
167
|
+
}
|
|
168
|
+
}), n.appendChild(h), _(t, e, n);
|
|
45
169
|
}
|
|
46
|
-
function
|
|
170
|
+
function _(t, e, a) {
|
|
47
171
|
const n = document.createElement("div");
|
|
48
|
-
n.className = "share-
|
|
172
|
+
n.className = "share-footer";
|
|
173
|
+
const s = document.createElement("button");
|
|
174
|
+
s.className = "share-token-link", s.textContent = "Sign out", s.addEventListener("click", async () => {
|
|
175
|
+
await t.settings.remove("relay_token"), C(t, e);
|
|
176
|
+
}), n.appendChild(s), a.appendChild(n), e.replaceChildren(a);
|
|
177
|
+
}
|
|
178
|
+
function S(t) {
|
|
179
|
+
for (const e of t.querySelectorAll(".share-error")) e.remove();
|
|
180
|
+
}
|
|
181
|
+
function b(t, e) {
|
|
49
182
|
const a = document.createElement("p");
|
|
50
|
-
a.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const i = document.createElement("div");
|
|
54
|
-
i.className = "share-btn-row";
|
|
55
|
-
const d = document.createElement("button");
|
|
56
|
-
d.className = "share-btn-primary", d.textContent = "Save", d.addEventListener("click", async () => {
|
|
57
|
-
const c = o.value.trim();
|
|
58
|
-
c && (await e.settings.set("relay_token", c), w(e, t));
|
|
59
|
-
}), i.appendChild(d);
|
|
60
|
-
const l = document.createElement("button");
|
|
61
|
-
l.className = "share-btn-secondary", l.textContent = "Back", l.addEventListener("click", () => w(e, t)), i.appendChild(l), n.appendChild(i), t.replaceChildren(n);
|
|
62
|
-
}
|
|
63
|
-
async function v(e, t) {
|
|
183
|
+
a.className = "share-error", a.textContent = e, t.appendChild(a);
|
|
184
|
+
}
|
|
185
|
+
async function Y(t, e) {
|
|
64
186
|
try {
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
const
|
|
68
|
-
var
|
|
69
|
-
if (((
|
|
70
|
-
window.removeEventListener("message",
|
|
71
|
-
const { code:
|
|
72
|
-
if (
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
187
|
+
const n = await (await fetch(T)).json(), o = await (await fetch(n.registration_endpoint, { method: "POST" })).json(), r = K(), c = await H(r), i = window.location.origin + "/oauth/callback", d = crypto.randomUUID(), l = new URL(n.authorization_endpoint);
|
|
188
|
+
l.searchParams.set("client_id", o.client_id), l.searchParams.set("redirect_uri", i), l.searchParams.set("response_type", "code"), l.searchParams.set("code_challenge", c), l.searchParams.set("code_challenge_method", "S256"), l.searchParams.set("state", d), l.searchParams.has("scope") || l.searchParams.set("scope", "openid email profile offline_access"), sessionStorage.setItem("share_oauth_state", d), sessionStorage.setItem("share_oauth_token_endpoint", n.token_endpoint), sessionStorage.setItem("share_oauth_client_id", o.client_id), sessionStorage.setItem("share_oauth_code_verifier", r), sessionStorage.setItem("share_oauth_redirect_uri", i);
|
|
189
|
+
const u = window.open(l.toString(), "backpack-share-auth", "width=500,height=700"), p = async (h) => {
|
|
190
|
+
var k;
|
|
191
|
+
if (((k = h.data) == null ? void 0 : k.type) !== "backpack-oauth-callback") return;
|
|
192
|
+
window.removeEventListener("message", p);
|
|
193
|
+
const { code: w, returnedState: E } = h.data;
|
|
194
|
+
if (E === d) {
|
|
195
|
+
G();
|
|
196
|
+
try {
|
|
197
|
+
const g = await (await fetch(n.token_endpoint, {
|
|
198
|
+
method: "POST",
|
|
199
|
+
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
200
|
+
body: new URLSearchParams({ grant_type: "authorization_code", code: w, redirect_uri: i, client_id: o.client_id, code_verifier: r }).toString()
|
|
201
|
+
})).json();
|
|
202
|
+
if (!g.access_token) {
|
|
203
|
+
b(e, "Token exchange failed: no access token returned.");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
await t.settings.set("relay_token", g.access_token), C(t, e);
|
|
207
|
+
} catch (y) {
|
|
208
|
+
b(e, `Token exchange failed: ${y.message}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
79
211
|
};
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const u = document.createElement("a");
|
|
84
|
-
u.href = s.toString(), u.target = "_blank", u.textContent = "Click here to sign in", p.appendChild(u), t.appendChild(p);
|
|
85
|
-
}
|
|
86
|
-
} catch (n) {
|
|
87
|
-
const a = document.createElement("p");
|
|
88
|
-
a.className = "share-error", a.textContent = `Auth failed: ${n.message}`, t.appendChild(a);
|
|
212
|
+
window.addEventListener("message", p), (!u || u.closed) && (window.location.href = l.toString());
|
|
213
|
+
} catch (a) {
|
|
214
|
+
b(e, `Auth failed: ${a.message}`);
|
|
89
215
|
}
|
|
90
216
|
}
|
|
91
|
-
function
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
217
|
+
async function x(t, e, a, n, s = "private") {
|
|
218
|
+
const o = t.getGraph(), r = t.getGraphName();
|
|
219
|
+
if (!o || !r) throw new Error("No graph loaded");
|
|
220
|
+
const c = new TextEncoder().encode(JSON.stringify(o));
|
|
221
|
+
let i, d, l = "";
|
|
222
|
+
if (n) {
|
|
223
|
+
const y = await import("../index-B8_hkT8R.js"), g = await y.generateX25519Identity(), U = await y.identityToRecipient(g), N = new y.Encrypter();
|
|
224
|
+
N.addRecipient(U), i = await N.encrypt(c), d = "age-v1", l = btoa(g).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
225
|
+
} else
|
|
226
|
+
i = c, d = "plaintext";
|
|
227
|
+
const u = await j(r, i, d, o), p = `${f}/api/graphs/${encodeURIComponent(r)}/sync${s === "public" ? "?visibility=public" : ""}`, h = await v(a, p, {
|
|
228
|
+
method: "PUT",
|
|
229
|
+
headers: { "Content-Type": "application/octet-stream" },
|
|
230
|
+
body: u
|
|
231
|
+
});
|
|
232
|
+
if (!h.ok) {
|
|
233
|
+
if (h.status === 401)
|
|
234
|
+
throw await t.settings.remove("relay_token"), C(t, e), new Error("Session expired. Please sign in again.");
|
|
235
|
+
let y = `Sync failed (${h.status})`;
|
|
107
236
|
try {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const p = document.createElement("p");
|
|
112
|
-
p.className = "share-error", p.textContent = g.message, a.appendChild(p);
|
|
237
|
+
const g = await h.json();
|
|
238
|
+
g.error && (y = g.error);
|
|
239
|
+
} catch {
|
|
113
240
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
h.className = "share-token-link", h.textContent = "Sign out", h.addEventListener("click", async () => {
|
|
119
|
-
await e.settings.remove("relay_token"), w(e, t);
|
|
120
|
-
}), a.appendChild(h), t.replaceChildren(a);
|
|
121
|
-
}
|
|
122
|
-
async function L(e, t, n, a, o) {
|
|
123
|
-
const i = e.getGraph(), d = e.getGraphName();
|
|
124
|
-
if (!i || !d) throw new Error("No graph loaded");
|
|
125
|
-
const l = new TextEncoder().encode(JSON.stringify(i));
|
|
126
|
-
let c, r, s = "";
|
|
127
|
-
if (a) {
|
|
128
|
-
const y = await import("../index-B-LFGJqd.js"), C = await y.generateX25519Identity(), f = await y.identityToRecipient(C), b = new y.Encrypter();
|
|
129
|
-
b.addRecipient(f), c = await b.encrypt(l), r = "age-v1", s = btoa(C).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
130
|
-
} else
|
|
131
|
-
c = l, r = "plaintext";
|
|
132
|
-
const h = await U(d, c, r), g = {
|
|
133
|
-
"Content-Type": "application/octet-stream",
|
|
134
|
-
Authorization: `Bearer ${n}`
|
|
135
|
-
};
|
|
136
|
-
o && (g["X-Passphrase"] = o);
|
|
137
|
-
const p = await fetch(`${E}/v1/share`, {
|
|
138
|
-
method: "POST",
|
|
139
|
-
headers: g,
|
|
140
|
-
body: h
|
|
241
|
+
throw new Error(y);
|
|
242
|
+
}
|
|
243
|
+
const w = await v(a, `${f}/api/graphs/${encodeURIComponent(r)}/share`, {
|
|
244
|
+
method: "POST"
|
|
141
245
|
});
|
|
142
|
-
if (!
|
|
143
|
-
|
|
144
|
-
|
|
246
|
+
if (!w.ok) {
|
|
247
|
+
A(e, null, n, void 0);
|
|
248
|
+
return;
|
|
145
249
|
}
|
|
146
|
-
const
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
async function
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
250
|
+
const E = await w.json(), k = l ? `${E.url}#k=${l}` : E.url;
|
|
251
|
+
A(e, k, n, E.expires_at);
|
|
252
|
+
}
|
|
253
|
+
async function v(t, e, a = {}) {
|
|
254
|
+
const n = new Headers(a.headers);
|
|
255
|
+
return n.set("Authorization", `Bearer ${t}`), fetch(e, { ...a, headers: n });
|
|
256
|
+
}
|
|
257
|
+
async function j(t, e, a, n) {
|
|
258
|
+
const s = new Uint8Array(e).buffer, o = await crypto.subtle.digest("SHA-256", s), r = "sha256:" + Array.from(new Uint8Array(o)).map((h) => h.toString(16).padStart(2, "0")).join(""), c = /* @__PURE__ */ new Set();
|
|
259
|
+
for (const h of n.nodes) c.add(h.type);
|
|
260
|
+
const i = JSON.stringify({
|
|
261
|
+
format: a,
|
|
154
262
|
created_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
155
|
-
backpack_name:
|
|
263
|
+
backpack_name: t,
|
|
156
264
|
graph_count: 1,
|
|
157
|
-
checksum:
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
265
|
+
checksum: r,
|
|
266
|
+
node_count: n.nodes.length,
|
|
267
|
+
edge_count: n.edges.length,
|
|
268
|
+
node_types: Array.from(c)
|
|
269
|
+
}), d = new TextEncoder().encode(i), l = new ArrayBuffer(4);
|
|
270
|
+
new DataView(l).setUint32(0, d.length, !1);
|
|
271
|
+
const u = new Uint8Array(9 + d.length + e.length);
|
|
272
|
+
let p = 0;
|
|
273
|
+
return u.set(P, p), p += 4, u[p] = R, p += 1, u.set(new Uint8Array(l), p), p += 4, u.set(d, p), p += d.length, u.set(e, p), u;
|
|
274
|
+
}
|
|
275
|
+
function A(t, e, a, n) {
|
|
276
|
+
const s = document.createElement("div");
|
|
277
|
+
s.className = "share-success";
|
|
278
|
+
const o = document.createElement("h4");
|
|
279
|
+
if (o.textContent = e ? "Synced & shared!" : "Synced!", s.appendChild(o), e) {
|
|
280
|
+
const r = document.createElement("div");
|
|
281
|
+
r.className = "share-link-row";
|
|
282
|
+
const c = document.createElement("input");
|
|
283
|
+
c.type = "text", c.readOnly = !0, c.value = e, c.className = "share-link-input", r.appendChild(c);
|
|
284
|
+
const i = document.createElement("button");
|
|
285
|
+
i.className = "share-btn-primary", i.textContent = "Copy", i.addEventListener("click", () => {
|
|
286
|
+
navigator.clipboard.writeText(e).then(() => {
|
|
287
|
+
i.textContent = "Copied!", setTimeout(() => {
|
|
288
|
+
i.textContent = "Copy";
|
|
289
|
+
}, 2e3);
|
|
290
|
+
}).catch(() => {
|
|
291
|
+
i.textContent = "Failed", setTimeout(() => {
|
|
292
|
+
i.textContent = "Copy";
|
|
293
|
+
}, 2e3);
|
|
294
|
+
});
|
|
295
|
+
}), r.appendChild(i), s.appendChild(r);
|
|
181
296
|
}
|
|
182
297
|
if (a) {
|
|
183
298
|
const r = document.createElement("p");
|
|
184
|
-
r.className = "share-note", r.textContent =
|
|
299
|
+
r.className = "share-note", r.textContent = "The decryption key is embedded in the link. Share the complete link — if the #k= part is removed, recipients won’t be able to decrypt. The server cannot read your data.", s.appendChild(r);
|
|
300
|
+
} else {
|
|
301
|
+
const r = document.createElement("p");
|
|
302
|
+
r.className = "share-note", r.textContent = "This graph is stored unencrypted. Anyone with the link can view it.", s.appendChild(r);
|
|
185
303
|
}
|
|
186
|
-
|
|
304
|
+
if (n) {
|
|
305
|
+
const r = document.createElement("p");
|
|
306
|
+
r.className = "share-note", r.textContent = `Expires: ${new Date(n).toLocaleDateString()}`, s.appendChild(r);
|
|
307
|
+
}
|
|
308
|
+
t.replaceChildren(s);
|
|
309
|
+
}
|
|
310
|
+
function G() {
|
|
311
|
+
sessionStorage.removeItem("share_oauth_state"), sessionStorage.removeItem("share_oauth_token_endpoint"), sessionStorage.removeItem("share_oauth_client_id"), sessionStorage.removeItem("share_oauth_code_verifier"), sessionStorage.removeItem("share_oauth_redirect_uri");
|
|
187
312
|
}
|
|
188
|
-
function
|
|
189
|
-
const
|
|
190
|
-
return crypto.getRandomValues(
|
|
313
|
+
function K() {
|
|
314
|
+
const t = new Uint8Array(32);
|
|
315
|
+
return crypto.getRandomValues(t), btoa(String.fromCharCode(...t)).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
191
316
|
}
|
|
192
|
-
async function
|
|
193
|
-
const
|
|
194
|
-
new Uint8Array(n).
|
|
195
|
-
const a = await crypto.subtle.digest("SHA-256", n);
|
|
196
|
-
return btoa(String.fromCharCode(...new Uint8Array(a))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
317
|
+
async function H(t) {
|
|
318
|
+
const e = new TextEncoder().encode(t), a = new Uint8Array(e).buffer, n = await crypto.subtle.digest("SHA-256", a);
|
|
319
|
+
return btoa(String.fromCharCode(...new Uint8Array(n))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
197
320
|
}
|
|
198
321
|
export {
|
|
199
|
-
|
|
322
|
+
M as activate
|
|
200
323
|
};
|
|
@@ -5,12 +5,16 @@
|
|
|
5
5
|
min-width: 300px;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
-
.share-upsell h4
|
|
8
|
+
.share-upsell h4,
|
|
9
|
+
.share-synced h4,
|
|
10
|
+
.share-quota h4,
|
|
11
|
+
.share-success h4 {
|
|
9
12
|
margin: 0 0 8px;
|
|
10
13
|
font-size: 15px;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
.share-upsell p
|
|
16
|
+
.share-upsell p,
|
|
17
|
+
.share-description {
|
|
14
18
|
margin: 0 0 12px;
|
|
15
19
|
color: var(--text-muted);
|
|
16
20
|
line-height: 1.5;
|
|
@@ -28,12 +32,20 @@
|
|
|
28
32
|
font-weight: 600;
|
|
29
33
|
cursor: pointer;
|
|
30
34
|
margin-bottom: 8px;
|
|
35
|
+
text-align: center;
|
|
36
|
+
text-decoration: none;
|
|
37
|
+
box-sizing: border-box;
|
|
31
38
|
}
|
|
32
39
|
|
|
33
40
|
.share-cta-btn:hover {
|
|
34
41
|
opacity: 0.9;
|
|
35
42
|
}
|
|
36
43
|
|
|
44
|
+
.share-cta-btn:disabled {
|
|
45
|
+
opacity: 0.5;
|
|
46
|
+
cursor: default;
|
|
47
|
+
}
|
|
48
|
+
|
|
37
49
|
.share-token-link {
|
|
38
50
|
background: none;
|
|
39
51
|
border: none;
|
|
@@ -68,6 +80,10 @@
|
|
|
68
80
|
margin-top: 8px;
|
|
69
81
|
}
|
|
70
82
|
|
|
83
|
+
.share-btn-row-stack {
|
|
84
|
+
flex-direction: column;
|
|
85
|
+
}
|
|
86
|
+
|
|
71
87
|
.share-btn-primary {
|
|
72
88
|
padding: 8px 16px;
|
|
73
89
|
background: var(--accent);
|
|
@@ -91,6 +107,18 @@
|
|
|
91
107
|
border-radius: 4px;
|
|
92
108
|
font-size: 13px;
|
|
93
109
|
cursor: pointer;
|
|
110
|
+
text-align: center;
|
|
111
|
+
text-decoration: none;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.share-btn-secondary:disabled {
|
|
115
|
+
opacity: 0.5;
|
|
116
|
+
cursor: default;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.share-btn-link {
|
|
120
|
+
display: block;
|
|
121
|
+
text-decoration: none;
|
|
94
122
|
}
|
|
95
123
|
|
|
96
124
|
.share-toggle-row {
|
|
@@ -101,10 +129,6 @@
|
|
|
101
129
|
cursor: pointer;
|
|
102
130
|
}
|
|
103
131
|
|
|
104
|
-
.share-pass-row {
|
|
105
|
-
margin-bottom: 12px;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
132
|
.share-form {
|
|
109
133
|
display: flex;
|
|
110
134
|
flex-direction: column;
|
|
@@ -123,8 +147,14 @@
|
|
|
123
147
|
margin-top: 8px;
|
|
124
148
|
}
|
|
125
149
|
|
|
150
|
+
.share-warning {
|
|
151
|
+
color: var(--warning, #d69e2e);
|
|
152
|
+
font-size: 12px;
|
|
153
|
+
margin: 0 0 8px;
|
|
154
|
+
line-height: 1.4;
|
|
155
|
+
}
|
|
156
|
+
|
|
126
157
|
.share-success h4 {
|
|
127
|
-
margin: 0 0 12px;
|
|
128
158
|
color: var(--text);
|
|
129
159
|
}
|
|
130
160
|
|
|
@@ -149,3 +179,25 @@
|
|
|
149
179
|
margin: 0 0 8px;
|
|
150
180
|
color: var(--text-muted);
|
|
151
181
|
}
|
|
182
|
+
|
|
183
|
+
.share-loading {
|
|
184
|
+
color: var(--text-muted);
|
|
185
|
+
font-size: 13px;
|
|
186
|
+
padding: 8px 0;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
.share-footer {
|
|
190
|
+
margin-top: 16px;
|
|
191
|
+
padding-top: 8px;
|
|
192
|
+
border-top: 1px solid var(--border);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.share-divider {
|
|
196
|
+
height: 1px;
|
|
197
|
+
background: var(--border);
|
|
198
|
+
margin: 16px 0;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.share-hidden {
|
|
202
|
+
display: none;
|
|
203
|
+
}
|