@burdenoff/microfe-vibecontrols 2026.531.12 → 2026.531.13
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/dist/components/sessions/SessionShareDialog.js +63 -65
- package/dist/components/sessions/SessionShareDialog.js.map +1 -1
- package/dist/components/vibedeck/VibeDeckShareDialog.js +144 -132
- package/dist/components/vibedeck/VibeDeckShareDialog.js.map +1 -1
- package/dist/utils/shareUrl.js +41 -0
- package/dist/utils/shareUrl.js.map +1 -0
- package/package.json +1 -1
|
@@ -1,39 +1,40 @@
|
|
|
1
1
|
import { useCreateSessionShareLinkMutation as e, useGetSessionShareLinksQuery as t, useGetSessionSharesQuery as n, useRevokeSessionShareLinkMutation as r, useRevokeSessionShareMutation as i, useShareSessionMutation as a } from "../../generated/wspace-operations.js";
|
|
2
|
-
import { useTr as
|
|
3
|
-
import { SessionSharePermission as
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { useTr as ee } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { SessionSharePermission as o, ShareStatus as s } from "../../generated/wspace-types.js";
|
|
4
|
+
import { copyTextToClipboard as te, normalizeShareUrl as c } from "../../utils/shareUrl.js";
|
|
5
|
+
import { useEffect as ne, useRef as re, useState as l } from "react";
|
|
6
|
+
import { Check as ie, Clock as u, Copy as d, Eye as f, Gamepad2 as p, Link2 as m, Lock as ae, RefreshCw as h, Share2 as oe, Trash2 as g, Users as _, X as v } from "lucide-react";
|
|
6
7
|
import { Fragment as y, jsx as b, jsxs as x } from "react/jsx-runtime";
|
|
7
8
|
//#region src/components/sessions/SessionShareDialog.tsx
|
|
8
9
|
var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
9
|
-
let T =
|
|
10
|
+
let T = ee(), E = re(null), [D, O] = l("users"), [se, k] = l(null), [ce, A] = l(null), [j, M] = l(""), [N, le] = l(o.View), [P, F] = l(""), [I, ue] = l(24), [L, R] = l(""), [z, B] = l(o.View), [V, H] = l(1), [U, W] = l(""), [G, K] = l(null), { data: de, refetch: q } = n({
|
|
10
11
|
variables: { sessionId: S.id },
|
|
11
12
|
skip: !C
|
|
12
|
-
}), { data:
|
|
13
|
+
}), { data: fe, refetch: J } = t({
|
|
13
14
|
variables: { sessionId: S.id },
|
|
14
15
|
skip: !C
|
|
15
|
-
}), [
|
|
16
|
-
|
|
16
|
+
}), [pe, { loading: Y }] = a(), [me, { loading: X }] = e(), [he, { loading: ge }] = i(), [_e, { loading: ve }] = r(), Z = de?.sessionShares?.filter((e) => e.status === s.Active) || [], Q = fe?.sessionShareLinks?.filter((e) => e.status === s.Active) || [];
|
|
17
|
+
ne(() => {
|
|
17
18
|
let e = E.current;
|
|
18
19
|
e && (C ? e.showModal() : e.close());
|
|
19
20
|
}, [C]);
|
|
20
|
-
let
|
|
21
|
-
if (e.preventDefault(),
|
|
21
|
+
let ye = async (e) => {
|
|
22
|
+
if (e.preventDefault(), j.trim()) try {
|
|
22
23
|
let e = new Date(Date.now() + I * 60 * 60 * 1e3).toISOString();
|
|
23
|
-
await
|
|
24
|
+
await pe({ variables: { input: {
|
|
24
25
|
sessionId: S.id,
|
|
25
|
-
sharedWithEmail:
|
|
26
|
-
permission:
|
|
26
|
+
sharedWithEmail: j.trim(),
|
|
27
|
+
permission: N,
|
|
27
28
|
expiresAt: e,
|
|
28
29
|
message: P.trim() || void 0
|
|
29
|
-
} } }),
|
|
30
|
+
} } }), M(""), F(""), q();
|
|
30
31
|
} catch (e) {
|
|
31
32
|
console.error("Failed to share session:", e);
|
|
32
33
|
}
|
|
33
|
-
},
|
|
34
|
+
}, be = async (e) => {
|
|
34
35
|
e.preventDefault();
|
|
35
36
|
try {
|
|
36
|
-
await
|
|
37
|
+
await me({ variables: { input: {
|
|
37
38
|
sessionId: S.id,
|
|
38
39
|
name: L.trim() || void 0,
|
|
39
40
|
permission: z,
|
|
@@ -44,26 +45,23 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
44
45
|
} catch (e) {
|
|
45
46
|
console.error("Failed to create share link:", e);
|
|
46
47
|
}
|
|
47
|
-
},
|
|
48
|
+
}, xe = async (e) => {
|
|
48
49
|
try {
|
|
49
|
-
await
|
|
50
|
+
await he({ variables: { id: e } }), q();
|
|
50
51
|
} catch (e) {
|
|
51
52
|
console.error("Failed to revoke share:", e);
|
|
52
53
|
}
|
|
53
|
-
},
|
|
54
|
+
}, Se = async (e) => {
|
|
54
55
|
try {
|
|
55
|
-
await
|
|
56
|
+
await _e({ variables: { id: e } }), J();
|
|
56
57
|
} catch (e) {
|
|
57
58
|
console.error("Failed to revoke link:", e);
|
|
58
59
|
}
|
|
59
|
-
},
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t), t.select(), document.execCommand("copy"), document.body.removeChild(t);
|
|
65
|
-
}
|
|
66
|
-
k(!0), setTimeout(() => k(!1), 2e3);
|
|
60
|
+
}, Ce = async (e, t) => {
|
|
61
|
+
let n = await te(c(t)).then(() => !0, (e) => (console.warn("Failed to copy session share link:", e), !1));
|
|
62
|
+
k(n ? e : null), A(n ? null : e), setTimeout(() => {
|
|
63
|
+
k((t) => t === e ? null : t), A((t) => t === e ? null : t);
|
|
64
|
+
}, 2e3);
|
|
67
65
|
}, $ = (e) => {
|
|
68
66
|
let t = new Date(e), n = /* @__PURE__ */ new Date(), r = t.getTime() - n.getTime(), i = Math.floor(r / (1e3 * 60 * 60)), a = Math.floor(r % (1e3 * 60 * 60) / (1e3 * 60));
|
|
69
67
|
return r < 0 ? "Expired" : i > 24 ? `${Math.floor(i / 24)}d` : i > 0 ? `${i}h ${a}m` : `${a}m`;
|
|
@@ -81,7 +79,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
81
79
|
className: "flex items-center gap-3",
|
|
82
80
|
children: [/* @__PURE__ */ b("div", {
|
|
83
81
|
className: "p-2 bg-action-primary-bg/10 rounded-lg",
|
|
84
|
-
children: /* @__PURE__ */ b(
|
|
82
|
+
children: /* @__PURE__ */ b(oe, { className: "size-5 text-action-primary-bg" })
|
|
85
83
|
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("h2", {
|
|
86
84
|
className: "text-lg font-semibold text-text-primary",
|
|
87
85
|
children: T("vibecontrols.sessions.share.title", "Share Session")
|
|
@@ -93,7 +91,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
93
91
|
type: "button",
|
|
94
92
|
onClick: w,
|
|
95
93
|
className: "p-1.5 rounded hover:bg-bg-sunken transition-colors",
|
|
96
|
-
children: /* @__PURE__ */ b(
|
|
94
|
+
children: /* @__PURE__ */ b(v, { className: "size-5 text-text-secondary" })
|
|
97
95
|
})]
|
|
98
96
|
}),
|
|
99
97
|
/* @__PURE__ */ x("div", {
|
|
@@ -103,7 +101,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
103
101
|
onClick: () => O("users"),
|
|
104
102
|
className: `flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${D === "users" ? "text-action-primary-bg border-b-2 border-action-primary-bg" : "text-text-secondary hover:text-text-primary"}`,
|
|
105
103
|
children: [
|
|
106
|
-
/* @__PURE__ */ b(
|
|
104
|
+
/* @__PURE__ */ b(_, { className: "size-4" }),
|
|
107
105
|
T("vibecontrols.sessions.share.shareWithUser", "Share with User"),
|
|
108
106
|
Z.length > 0 && /* @__PURE__ */ b("span", {
|
|
109
107
|
className: "ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full",
|
|
@@ -115,7 +113,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
115
113
|
onClick: () => O("links"),
|
|
116
114
|
className: `flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${D === "links" ? "text-action-primary-bg border-b-2 border-action-primary-bg" : "text-text-secondary hover:text-text-primary"}`,
|
|
117
115
|
children: [
|
|
118
|
-
/* @__PURE__ */ b(
|
|
116
|
+
/* @__PURE__ */ b(m, { className: "size-4" }),
|
|
119
117
|
T("vibecontrols.sessions.share.createLink", "Create Link"),
|
|
120
118
|
Q.length > 0 && /* @__PURE__ */ b("span", {
|
|
121
119
|
className: "ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full",
|
|
@@ -129,7 +127,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
129
127
|
children: D === "users" ? /* @__PURE__ */ x("div", {
|
|
130
128
|
className: "space-y-6",
|
|
131
129
|
children: [/* @__PURE__ */ x("form", {
|
|
132
|
-
onSubmit:
|
|
130
|
+
onSubmit: ye,
|
|
133
131
|
className: "space-y-4",
|
|
134
132
|
children: [
|
|
135
133
|
/* @__PURE__ */ x("div", { children: [
|
|
@@ -140,8 +138,8 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
140
138
|
/* @__PURE__ */ b("input", {
|
|
141
139
|
"aria-label": "teammate@company.com",
|
|
142
140
|
type: "email",
|
|
143
|
-
value:
|
|
144
|
-
onChange: (e) =>
|
|
141
|
+
value: j,
|
|
142
|
+
onChange: (e) => M(e.target.value),
|
|
145
143
|
placeholder: "teammate@company.com",
|
|
146
144
|
required: !0,
|
|
147
145
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
@@ -157,14 +155,14 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
157
155
|
className: "block text-sm font-medium text-text-primary mb-1",
|
|
158
156
|
children: T("vibecontrols.sessions.share.permission", "Permission")
|
|
159
157
|
}), /* @__PURE__ */ x("select", {
|
|
160
|
-
value:
|
|
161
|
-
onChange: (e) =>
|
|
158
|
+
value: N,
|
|
159
|
+
onChange: (e) => le(e.target.value),
|
|
162
160
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
163
161
|
children: [/* @__PURE__ */ b("option", {
|
|
164
|
-
value:
|
|
162
|
+
value: o.View,
|
|
165
163
|
children: T("vibecontrols.sessions.share.viewOnly", "View Only")
|
|
166
164
|
}), /* @__PURE__ */ b("option", {
|
|
167
|
-
value:
|
|
165
|
+
value: o.Interact,
|
|
168
166
|
children: T("vibecontrols.sessions.share.canControl", "Can Control")
|
|
169
167
|
})]
|
|
170
168
|
})] }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -172,7 +170,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
172
170
|
children: T("vibecontrols.sessions.share.expiresIn", "Expires In")
|
|
173
171
|
}), /* @__PURE__ */ x("select", {
|
|
174
172
|
value: I,
|
|
175
|
-
onChange: (e) =>
|
|
173
|
+
onChange: (e) => ue(Number(e.target.value)),
|
|
176
174
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
177
175
|
children: [
|
|
178
176
|
/* @__PURE__ */ b("option", {
|
|
@@ -215,17 +213,17 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
215
213
|
})] }),
|
|
216
214
|
/* @__PURE__ */ b("div", {
|
|
217
215
|
className: "flex items-center gap-2 p-3 bg-bg-sunken rounded-lg text-sm",
|
|
218
|
-
children:
|
|
216
|
+
children: N === o.View ? /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(f, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ b("span", {
|
|
219
217
|
className: "text-text-secondary",
|
|
220
218
|
children: T("vibecontrols.sessions.share.viewOnlyHint", "User can watch the terminal but cannot type")
|
|
221
|
-
})] }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(
|
|
219
|
+
})] }) : /* @__PURE__ */ x(y, { children: [/* @__PURE__ */ b(p, { className: "size-4 text-text-secondary" }), /* @__PURE__ */ b("span", {
|
|
222
220
|
className: "text-text-secondary",
|
|
223
221
|
children: T("vibecontrols.sessions.share.controlHint", "User can request control to type (turn-based)")
|
|
224
222
|
})] })
|
|
225
223
|
}),
|
|
226
224
|
/* @__PURE__ */ b("button", {
|
|
227
225
|
type: "submit",
|
|
228
|
-
disabled: Y || !
|
|
226
|
+
disabled: Y || !j.trim(),
|
|
229
227
|
className: "w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50",
|
|
230
228
|
children: Y ? T("vibecontrols.sessions.share.sharing", "Sharing...") : T("vibecontrols.sessions.share.shareSession", "Share Session")
|
|
231
229
|
})
|
|
@@ -241,7 +239,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
241
239
|
className: "flex items-center gap-3",
|
|
242
240
|
children: [/* @__PURE__ */ b("div", {
|
|
243
241
|
className: "size-8 rounded-full bg-action-primary-bg/20 flex items-center justify-center",
|
|
244
|
-
children: /* @__PURE__ */ b(
|
|
242
|
+
children: /* @__PURE__ */ b(_, { className: "size-4 text-action-primary-bg" })
|
|
245
243
|
}), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ x("p", {
|
|
246
244
|
className: "text-sm font-medium text-text-primary",
|
|
247
245
|
children: [
|
|
@@ -252,29 +250,29 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
252
250
|
}), /* @__PURE__ */ x("div", {
|
|
253
251
|
className: "flex items-center gap-2 text-xs text-text-secondary",
|
|
254
252
|
children: [
|
|
255
|
-
e.permission ===
|
|
253
|
+
e.permission === o.View ? /* @__PURE__ */ b(f, { className: "size-3" }) : /* @__PURE__ */ b(p, { className: "size-3" }),
|
|
256
254
|
/* @__PURE__ */ b("span", { children: e.permission }),
|
|
257
255
|
e.expiresAt && /* @__PURE__ */ x(y, { children: [
|
|
258
256
|
/* @__PURE__ */ b("span", { children: "·" }),
|
|
259
|
-
/* @__PURE__ */ b(
|
|
257
|
+
/* @__PURE__ */ b(u, { className: "size-3" }),
|
|
260
258
|
/* @__PURE__ */ b("span", { children: $(e.expiresAt) })
|
|
261
259
|
] })
|
|
262
260
|
]
|
|
263
261
|
})] })]
|
|
264
262
|
}), /* @__PURE__ */ b("button", {
|
|
265
263
|
type: "button",
|
|
266
|
-
onClick: () =>
|
|
267
|
-
disabled:
|
|
264
|
+
onClick: () => xe(e.id),
|
|
265
|
+
disabled: ge,
|
|
268
266
|
className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors",
|
|
269
267
|
title: "Revoke access",
|
|
270
|
-
children: /* @__PURE__ */ b(
|
|
268
|
+
children: /* @__PURE__ */ b(g, { className: "size-4" })
|
|
271
269
|
})]
|
|
272
270
|
}, e.id))
|
|
273
271
|
})] })]
|
|
274
272
|
}) : /* @__PURE__ */ x("div", {
|
|
275
273
|
className: "space-y-6",
|
|
276
274
|
children: [/* @__PURE__ */ x("form", {
|
|
277
|
-
onSubmit:
|
|
275
|
+
onSubmit: be,
|
|
278
276
|
className: "space-y-4",
|
|
279
277
|
children: [
|
|
280
278
|
/* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -298,10 +296,10 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
298
296
|
onChange: (e) => B(e.target.value),
|
|
299
297
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg",
|
|
300
298
|
children: [/* @__PURE__ */ b("option", {
|
|
301
|
-
value:
|
|
299
|
+
value: o.View,
|
|
302
300
|
children: T("vibecontrols.sessions.share.viewOnly", "View Only")
|
|
303
301
|
}), /* @__PURE__ */ b("option", {
|
|
304
|
-
value:
|
|
302
|
+
value: o.Interact,
|
|
305
303
|
children: T("vibecontrols.sessions.share.canControl", "Can Control")
|
|
306
304
|
})]
|
|
307
305
|
})] }), /* @__PURE__ */ x("div", { children: [/* @__PURE__ */ b("label", {
|
|
@@ -380,46 +378,46 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
380
378
|
children: [/* @__PURE__ */ x("div", {
|
|
381
379
|
className: "flex items-center gap-2",
|
|
382
380
|
children: [
|
|
383
|
-
/* @__PURE__ */ b(
|
|
381
|
+
/* @__PURE__ */ b(m, { className: "size-4 text-action-primary-bg" }),
|
|
384
382
|
/* @__PURE__ */ b("span", {
|
|
385
383
|
className: "text-sm font-medium text-text-primary",
|
|
386
384
|
children: e.name || "Unnamed Link"
|
|
387
385
|
}),
|
|
388
|
-
e.hasPassword && /* @__PURE__ */ b(
|
|
386
|
+
e.hasPassword && /* @__PURE__ */ b(ae, { className: "size-3 text-text-secondary" })
|
|
389
387
|
]
|
|
390
388
|
}), /* @__PURE__ */ x("div", {
|
|
391
389
|
className: "flex items-center gap-1",
|
|
392
390
|
children: [/* @__PURE__ */ b("button", {
|
|
393
391
|
type: "button",
|
|
394
|
-
onClick: () =>
|
|
392
|
+
onClick: () => Ce(e.id, e.shareUrl),
|
|
395
393
|
className: "p-1.5 text-text-secondary hover:text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors",
|
|
396
394
|
title: "Copy link",
|
|
397
|
-
children:
|
|
395
|
+
children: se === e.id ? /* @__PURE__ */ b(ie, { className: "size-4 text-status-success-text" }) : ce === e.id ? /* @__PURE__ */ b(v, { className: "size-4 text-status-error-text" }) : /* @__PURE__ */ b(d, { className: "size-4" })
|
|
398
396
|
}), /* @__PURE__ */ b("button", {
|
|
399
397
|
type: "button",
|
|
400
|
-
onClick: () =>
|
|
401
|
-
disabled:
|
|
398
|
+
onClick: () => Se(e.id),
|
|
399
|
+
disabled: ve,
|
|
402
400
|
className: "p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors",
|
|
403
401
|
title: "Revoke link",
|
|
404
|
-
children: /* @__PURE__ */ b(
|
|
402
|
+
children: /* @__PURE__ */ b(g, { className: "size-4" })
|
|
405
403
|
})]
|
|
406
404
|
})]
|
|
407
405
|
}),
|
|
408
406
|
/* @__PURE__ */ x("div", {
|
|
409
407
|
className: "flex items-center gap-3 text-xs text-text-secondary",
|
|
410
408
|
children: [
|
|
411
|
-
e.permission ===
|
|
409
|
+
e.permission === o.View ? /* @__PURE__ */ x("span", {
|
|
412
410
|
className: "flex items-center gap-1",
|
|
413
|
-
children: [/* @__PURE__ */ b(
|
|
411
|
+
children: [/* @__PURE__ */ b(f, { className: "size-3" }), " View"]
|
|
414
412
|
}) : /* @__PURE__ */ x("span", {
|
|
415
413
|
className: "flex items-center gap-1",
|
|
416
|
-
children: [/* @__PURE__ */ b(
|
|
414
|
+
children: [/* @__PURE__ */ b(p, { className: "size-3" }), " Control"]
|
|
417
415
|
}),
|
|
418
416
|
/* @__PURE__ */ b("span", { children: "·" }),
|
|
419
417
|
/* @__PURE__ */ x("span", {
|
|
420
418
|
className: "flex items-center gap-1",
|
|
421
419
|
children: [
|
|
422
|
-
/* @__PURE__ */ b(
|
|
420
|
+
/* @__PURE__ */ b(u, { className: "size-3" }),
|
|
423
421
|
" ",
|
|
424
422
|
$(e.expiresAt)
|
|
425
423
|
]
|
|
@@ -428,7 +426,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
428
426
|
/* @__PURE__ */ x("span", {
|
|
429
427
|
className: "flex items-center gap-1",
|
|
430
428
|
children: [
|
|
431
|
-
/* @__PURE__ */ b(
|
|
429
|
+
/* @__PURE__ */ b(h, { className: "size-3" }),
|
|
432
430
|
" ",
|
|
433
431
|
e.useCount,
|
|
434
432
|
e.maxUses ? `/${e.maxUses}` : "",
|
|
@@ -442,7 +440,7 @@ var S = ({ session: S, isOpen: C, onClose: w }) => {
|
|
|
442
440
|
children: /* @__PURE__ */ b("input", {
|
|
443
441
|
"aria-label": "control",
|
|
444
442
|
type: "text",
|
|
445
|
-
value: e.shareUrl,
|
|
443
|
+
value: c(e.shareUrl),
|
|
446
444
|
readOnly: !0,
|
|
447
445
|
className: "flex-1 px-2 py-1 text-xs font-mono bg-bg-surface border border-border-default rounded text-text-secondary truncate"
|
|
448
446
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionShareDialog.js","names":[],"sources":["../../../src/components/sessions/SessionShareDialog.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useRef } from 'react';\nimport {\n X,\n Share2,\n Link2,\n Users,\n Copy,\n Check,\n Eye,\n Gamepad2,\n Clock,\n Lock,\n Trash2,\n RefreshCw,\n} from 'lucide-react';\nimport type { Session } from './types';\nimport {\n useShareSessionMutation,\n useCreateSessionShareLinkMutation,\n useRevokeSessionShareMutation,\n useRevokeSessionShareLinkMutation,\n useGetSessionSharesQuery,\n useGetSessionShareLinksQuery,\n type SessionShareFieldsFragment,\n type SessionShareLinkFieldsFragment,\n} from '../../generated/wspace-operations';\nimport { SessionSharePermission, ShareStatus } from '../../generated/wspace-types';\nimport { useTr } from '../../shared/hooks/useTr';\n\ntype TabType = 'users' | 'links';\n\ninterface SessionShareDialogProps {\n session: Session;\n isOpen: boolean;\n onClose: () => void;\n}\n\n/**\n * SessionShareDialog - Modal for sharing terminal sessions\n * Supports sharing with specific users or creating shareable links\n */\nexport const SessionShareDialog: FC<SessionShareDialogProps> = ({ session, isOpen, onClose }) => {\n const tr = useTr();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const [activeTab, setActiveTab] = useState<TabType>('users');\n const [copied, setCopied] = useState(false);\n\n // Share with user form state\n const [email, setEmail] = useState('');\n const [permission, setPermission] = useState<SessionSharePermission>(SessionSharePermission.View);\n const [message, setMessage] = useState('');\n const [expiresInHours, setExpiresInHours] = useState(24);\n\n // Link form state\n const [linkName, setLinkName] = useState('');\n const [linkPermission, setLinkPermission] = useState<SessionSharePermission>(\n SessionSharePermission.View\n );\n const [linkExpiresInHours, setLinkExpiresInHours] = useState(1);\n const [linkPassword, setLinkPassword] = useState('');\n const [maxUses, setMaxUses] = useState<number | null>(null);\n\n // Queries for existing shares\n const { data: sharesData, refetch: refetchShares } = useGetSessionSharesQuery({\n variables: { sessionId: session.id },\n skip: !isOpen,\n });\n\n const { data: linksData, refetch: refetchLinks } = useGetSessionShareLinksQuery({\n variables: { sessionId: session.id },\n skip: !isOpen,\n });\n\n // Mutations\n const [shareSession, { loading: sharingUser }] = useShareSessionMutation();\n const [createLink, { loading: creatingLink }] = useCreateSessionShareLinkMutation();\n const [revokeShare, { loading: revokingShare }] = useRevokeSessionShareMutation();\n const [revokeLink, { loading: revokingLink }] = useRevokeSessionShareLinkMutation();\n\n const existingShares: SessionShareFieldsFragment[] =\n sharesData?.sessionShares?.filter(\n (s: SessionShareFieldsFragment) => s.status === ShareStatus.Active\n ) || [];\n const existingLinks: SessionShareLinkFieldsFragment[] =\n linksData?.sessionShareLinks?.filter(\n (l: SessionShareLinkFieldsFragment) => l.status === ShareStatus.Active\n ) || [];\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n if (isOpen) {\n dialog.showModal();\n } else {\n dialog.close();\n }\n }, [isOpen]);\n\n const handleShareWithUser = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!email.trim()) return;\n\n try {\n const expiresAt = new Date(Date.now() + expiresInHours * 60 * 60 * 1000).toISOString();\n await shareSession({\n variables: {\n input: {\n sessionId: session.id,\n sharedWithEmail: email.trim(),\n permission,\n expiresAt,\n message: message.trim() || undefined,\n },\n },\n });\n setEmail('');\n setMessage('');\n refetchShares();\n } catch (error) {\n console.error('Failed to share session:', error);\n }\n };\n\n const handleCreateLink = async (e: React.FormEvent) => {\n e.preventDefault();\n\n try {\n await createLink({\n variables: {\n input: {\n sessionId: session.id,\n name: linkName.trim() || undefined,\n permission: linkPermission,\n expiresInHours: linkExpiresInHours,\n maxUses: maxUses || undefined,\n password: linkPassword.trim() || undefined,\n },\n },\n });\n setLinkName('');\n setLinkPassword('');\n setMaxUses(null);\n refetchLinks();\n } catch (error) {\n console.error('Failed to create share link:', error);\n }\n };\n\n const handleRevokeShare = async (shareId: string) => {\n try {\n await revokeShare({ variables: { id: shareId } });\n refetchShares();\n } catch (error) {\n console.error('Failed to revoke share:', error);\n }\n };\n\n const handleRevokeLink = async (linkId: string) => {\n try {\n await revokeLink({ variables: { id: linkId } });\n refetchLinks();\n } catch (error) {\n console.error('Failed to revoke link:', error);\n }\n };\n\n const copyToClipboard = async (text: string) => {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n // Fallback for non-secure contexts (HTTP dev/staging)\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n textarea.select();\n document.execCommand('copy');\n document.body.removeChild(textarea);\n }\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n };\n\n const formatExpiry = (expiresAt: string) => {\n const expiry = new Date(expiresAt);\n const now = new Date();\n const diffMs = expiry.getTime() - now.getTime();\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffMins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));\n\n if (diffMs < 0) return 'Expired';\n if (diffHours > 24) return `${Math.floor(diffHours / 24)}d`;\n if (diffHours > 0) return `${diffHours}h ${diffMins}m`;\n return `${diffMins}m`;\n };\n\n if (!isOpen) return null;\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto bg-bg-overlay/0 p-0 backdrop:bg-bg-overlay\"\n onClose={onClose}\n >\n <div className=\"w-full max-w-xl bg-bg-surface rounded-lg shadow-xl max-h-[85vh] flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-lg\">\n <Share2 className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.sessions.share.title', 'Share Session')}\n </h2>\n <p className=\"text-sm text-text-secondary truncate max-w-[300px]\">{session.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex border-b border-border-default\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('users')}\n className={`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${\n activeTab === 'users'\n ? 'text-action-primary-bg border-b-2 border-action-primary-bg'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n <Users className=\"size-4\" />\n {tr('vibecontrols.sessions.share.shareWithUser', 'Share with User')}\n {existingShares.length > 0 && (\n <span className=\"ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full\">\n {existingShares.length}\n </span>\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('links')}\n className={`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${\n activeTab === 'links'\n ? 'text-action-primary-bg border-b-2 border-action-primary-bg'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n <Link2 className=\"size-4\" />\n {tr('vibecontrols.sessions.share.createLink', 'Create Link')}\n {existingLinks.length > 0 && (\n <span className=\"ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full\">\n {existingLinks.length}\n </span>\n )}\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {activeTab === 'users' ? (\n <div className=\"space-y-6\">\n {/* Share form */}\n <form onSubmit={handleShareWithUser} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.emailAddress', 'Email Address')}\n </label>\n <input\n aria-label=\"teammate@company.com\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"teammate@company.com\"\n required\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.mustBeWorkspaceMember',\n 'Must be an existing workspace member'\n )}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.permission', 'Permission')}\n </label>\n <select\n value={permission}\n onChange={(e) => setPermission(e.target.value as SessionSharePermission)}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={SessionSharePermission.View}>\n {tr('vibecontrols.sessions.share.viewOnly', 'View Only')}\n </option>\n <option value={SessionSharePermission.Interact}>\n {tr('vibecontrols.sessions.share.canControl', 'Can Control')}\n </option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.expiresIn', 'Expires In')}\n </label>\n <select\n value={expiresInHours}\n onChange={(e) => setExpiresInHours(Number(e.target.value))}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={1}>1 hour</option>\n <option value={4}>4 hours</option>\n <option value={8}>8 hours</option>\n <option value={24}>24 hours</option>\n <option value={72}>3 days</option>\n <option value={168}>1 week</option>\n </select>\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.messageOptional', 'Message (optional)')}\n </label>\n <textarea\n aria-label=\"Let me show you this issue...\"\n value={message}\n onChange={(e) => setMessage(e.target.value)}\n placeholder=\"Let me show you this issue...\"\n rows={2}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none\"\n />\n </div>\n\n <div className=\"flex items-center gap-2 p-3 bg-bg-sunken rounded-lg text-sm\">\n {permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-4 text-text-secondary\" />\n <span className=\"text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.viewOnlyHint',\n 'User can watch the terminal but cannot type'\n )}\n </span>\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-4 text-text-secondary\" />\n <span className=\"text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.controlHint',\n 'User can request control to type (turn-based)'\n )}\n </span>\n </>\n )}\n </div>\n\n <button\n type=\"submit\"\n disabled={sharingUser || !email.trim()}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {sharingUser\n ? tr('vibecontrols.sessions.share.sharing', 'Sharing...')\n : tr('vibecontrols.sessions.share.shareSession', 'Share Session')}\n </button>\n </form>\n\n {/* Existing shares */}\n {existingShares.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">\n {tr('vibecontrols.sessions.share.currentlySharedWith', 'Currently Shared With')}\n </h3>\n <div className=\"space-y-2\">\n {existingShares.map((share) => (\n <div\n key={share.id}\n className=\"flex items-center justify-between p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-full bg-action-primary-bg/20 flex items-center justify-center\">\n <Users className=\"size-4 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n User: {share.sharedWithId.slice(0, 8)}...\n </p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n {share.permission === SessionSharePermission.View ? (\n <Eye className=\"size-3\" />\n ) : (\n <Gamepad2 className=\"size-3\" />\n )}\n <span>{share.permission}</span>\n {share.expiresAt && (\n <>\n <span>·</span>\n <Clock className=\"size-3\" />\n <span>{formatExpiry(share.expiresAt)}</span>\n </>\n )}\n </div>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => handleRevokeShare(share.id)}\n disabled={revokingShare}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors\"\n title=\"Revoke access\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {/* Create link form */}\n <form onSubmit={handleCreateLink} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.linkNameOptional', 'Link Name (optional)')}\n </label>\n <input\n aria-label=\"Debug session link\"\n type=\"text\"\n value={linkName}\n onChange={(e) => setLinkName(e.target.value)}\n placeholder=\"Debug session link\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.permission', 'Permission')}\n </label>\n <select\n value={linkPermission}\n onChange={(e) => setLinkPermission(e.target.value as SessionSharePermission)}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={SessionSharePermission.View}>\n {tr('vibecontrols.sessions.share.viewOnly', 'View Only')}\n </option>\n <option value={SessionSharePermission.Interact}>\n {tr('vibecontrols.sessions.share.canControl', 'Can Control')}\n </option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.expiresIn', 'Expires In')}\n </label>\n <select\n value={linkExpiresInHours}\n onChange={(e) => setLinkExpiresInHours(Number(e.target.value))}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={1}>1 hour</option>\n <option value={4}>4 hours</option>\n <option value={8}>8 hours</option>\n <option value={24}>24 hours</option>\n <option value={72}>3 days</option>\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.passwordOptional', 'Password (optional)')}\n </label>\n <input\n aria-label=\"••••••••\"\n type=\"password\"\n value={linkPassword}\n onChange={(e) => setLinkPassword(e.target.value)}\n placeholder=\"••••••••\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.maxUses', 'Max Uses')}\n </label>\n <input\n aria-label=\"Unlimited\"\n type=\"number\"\n value={maxUses ?? ''}\n onChange={(e) => setMaxUses(e.target.value ? Number(e.target.value) : null)}\n placeholder=\"Unlimited\"\n min={1}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n\n <button\n type=\"submit\"\n disabled={creatingLink}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creatingLink\n ? tr('vibecontrols.sessions.share.creating', 'Creating...')\n : tr('vibecontrols.sessions.share.createShareLink', 'Create Share Link')}\n </button>\n </form>\n\n {/* Existing links */}\n {existingLinks.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">\n {tr('vibecontrols.sessions.share.activeLinks', 'Active Links')}\n </h3>\n <div className=\"space-y-2\">\n {existingLinks.map((link) => (\n <div key={link.id} className=\"p-3 bg-bg-sunken rounded-lg space-y-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Link2 className=\"size-4 text-action-primary-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {link.name || 'Unnamed Link'}\n </span>\n {link.hasPassword && <Lock className=\"size-3 text-text-secondary\" />}\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => copyToClipboard(link.shareUrl)}\n className=\"p-1.5 text-text-secondary hover:text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors\"\n title=\"Copy link\"\n >\n {copied ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => handleRevokeLink(link.id)}\n disabled={revokingLink}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors\"\n title=\"Revoke link\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n {link.permission === SessionSharePermission.View ? (\n <span className=\"flex items-center gap-1\">\n <Eye className=\"size-3\" /> View\n </span>\n ) : (\n <span className=\"flex items-center gap-1\">\n <Gamepad2 className=\"size-3\" /> Control\n </span>\n )}\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" /> {formatExpiry(link.expiresAt)}\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <RefreshCw className=\"size-3\" /> {link.useCount}\n {link.maxUses ? `/${link.maxUses}` : ''} uses\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={link.shareUrl}\n readOnly\n className=\"flex-1 px-2 py-1 text-xs font-mono bg-bg-surface border border-border-default rounded text-text-secondary truncate\"\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"px-6 py-4 bg-bg-sunken border-t border-border-default rounded-b-lg\">\n <p className=\"text-xs text-text-secondary text-center\">\n {tr(\n 'vibecontrols.sessions.share.footerNote',\n 'Shared users must be logged in to access the session'\n )}\n </p>\n </div>\n </div>\n </dialog>\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SessionShareDialog.js","names":[],"sources":["../../../src/components/sessions/SessionShareDialog.tsx"],"sourcesContent":["import type { FC } from 'react';\nimport { useState, useEffect, useRef } from 'react';\nimport {\n X,\n Share2,\n Link2,\n Users,\n Copy,\n Check,\n Eye,\n Gamepad2,\n Clock,\n Lock,\n Trash2,\n RefreshCw,\n} from 'lucide-react';\nimport type { Session } from './types';\nimport {\n useShareSessionMutation,\n useCreateSessionShareLinkMutation,\n useRevokeSessionShareMutation,\n useRevokeSessionShareLinkMutation,\n useGetSessionSharesQuery,\n useGetSessionShareLinksQuery,\n type SessionShareFieldsFragment,\n type SessionShareLinkFieldsFragment,\n} from '../../generated/wspace-operations';\nimport { SessionSharePermission, ShareStatus } from '../../generated/wspace-types';\nimport { useTr } from '../../shared/hooks/useTr';\nimport { copyTextToClipboard, normalizeShareUrl } from '../../utils/shareUrl';\n\ntype TabType = 'users' | 'links';\n\ninterface SessionShareDialogProps {\n session: Session;\n isOpen: boolean;\n onClose: () => void;\n}\n\n/**\n * SessionShareDialog - Modal for sharing terminal sessions\n * Supports sharing with specific users or creating shareable links\n */\nexport const SessionShareDialog: FC<SessionShareDialogProps> = ({ session, isOpen, onClose }) => {\n const tr = useTr();\n const dialogRef = useRef<HTMLDialogElement>(null);\n const [activeTab, setActiveTab] = useState<TabType>('users');\n const [copied, setCopied] = useState<string | null>(null);\n const [copyFailed, setCopyFailed] = useState<string | null>(null);\n\n // Share with user form state\n const [email, setEmail] = useState('');\n const [permission, setPermission] = useState<SessionSharePermission>(SessionSharePermission.View);\n const [message, setMessage] = useState('');\n const [expiresInHours, setExpiresInHours] = useState(24);\n\n // Link form state\n const [linkName, setLinkName] = useState('');\n const [linkPermission, setLinkPermission] = useState<SessionSharePermission>(\n SessionSharePermission.View\n );\n const [linkExpiresInHours, setLinkExpiresInHours] = useState(1);\n const [linkPassword, setLinkPassword] = useState('');\n const [maxUses, setMaxUses] = useState<number | null>(null);\n\n // Queries for existing shares\n const { data: sharesData, refetch: refetchShares } = useGetSessionSharesQuery({\n variables: { sessionId: session.id },\n skip: !isOpen,\n });\n\n const { data: linksData, refetch: refetchLinks } = useGetSessionShareLinksQuery({\n variables: { sessionId: session.id },\n skip: !isOpen,\n });\n\n // Mutations\n const [shareSession, { loading: sharingUser }] = useShareSessionMutation();\n const [createLink, { loading: creatingLink }] = useCreateSessionShareLinkMutation();\n const [revokeShare, { loading: revokingShare }] = useRevokeSessionShareMutation();\n const [revokeLink, { loading: revokingLink }] = useRevokeSessionShareLinkMutation();\n\n const existingShares: SessionShareFieldsFragment[] =\n sharesData?.sessionShares?.filter(\n (s: SessionShareFieldsFragment) => s.status === ShareStatus.Active\n ) || [];\n const existingLinks: SessionShareLinkFieldsFragment[] =\n linksData?.sessionShareLinks?.filter(\n (l: SessionShareLinkFieldsFragment) => l.status === ShareStatus.Active\n ) || [];\n\n useEffect(() => {\n const dialog = dialogRef.current;\n if (!dialog) return;\n\n if (isOpen) {\n dialog.showModal();\n } else {\n dialog.close();\n }\n }, [isOpen]);\n\n const handleShareWithUser = async (e: React.FormEvent) => {\n e.preventDefault();\n if (!email.trim()) return;\n\n try {\n const expiresAt = new Date(Date.now() + expiresInHours * 60 * 60 * 1000).toISOString();\n await shareSession({\n variables: {\n input: {\n sessionId: session.id,\n sharedWithEmail: email.trim(),\n permission,\n expiresAt,\n message: message.trim() || undefined,\n },\n },\n });\n setEmail('');\n setMessage('');\n refetchShares();\n } catch (error) {\n console.error('Failed to share session:', error);\n }\n };\n\n const handleCreateLink = async (e: React.FormEvent) => {\n e.preventDefault();\n\n try {\n await createLink({\n variables: {\n input: {\n sessionId: session.id,\n name: linkName.trim() || undefined,\n permission: linkPermission,\n expiresInHours: linkExpiresInHours,\n maxUses: maxUses || undefined,\n password: linkPassword.trim() || undefined,\n },\n },\n });\n setLinkName('');\n setLinkPassword('');\n setMaxUses(null);\n refetchLinks();\n } catch (error) {\n console.error('Failed to create share link:', error);\n }\n };\n\n const handleRevokeShare = async (shareId: string) => {\n try {\n await revokeShare({ variables: { id: shareId } });\n refetchShares();\n } catch (error) {\n console.error('Failed to revoke share:', error);\n }\n };\n\n const handleRevokeLink = async (linkId: string) => {\n try {\n await revokeLink({ variables: { id: linkId } });\n refetchLinks();\n } catch (error) {\n console.error('Failed to revoke link:', error);\n }\n };\n\n const copyToClipboard = async (id: string, text: string) => {\n const shareUrl = normalizeShareUrl(text);\n const ok = await copyTextToClipboard(shareUrl).then(\n () => true,\n (error) => {\n console.warn('Failed to copy session share link:', error);\n return false;\n }\n );\n setCopied(ok ? id : null);\n setCopyFailed(ok ? null : id);\n setTimeout(() => {\n setCopied((current) => (current === id ? null : current));\n setCopyFailed((current) => (current === id ? null : current));\n }, 2000);\n };\n\n const formatExpiry = (expiresAt: string) => {\n const expiry = new Date(expiresAt);\n const now = new Date();\n const diffMs = expiry.getTime() - now.getTime();\n const diffHours = Math.floor(diffMs / (1000 * 60 * 60));\n const diffMins = Math.floor((diffMs % (1000 * 60 * 60)) / (1000 * 60));\n\n if (diffMs < 0) return 'Expired';\n if (diffHours > 24) return `${Math.floor(diffHours / 24)}d`;\n if (diffHours > 0) return `${diffHours}h ${diffMins}m`;\n return `${diffMins}m`;\n };\n\n if (!isOpen) return null;\n\n return (\n <dialog\n ref={dialogRef}\n className=\"fixed inset-0 z-50 m-auto bg-bg-overlay/0 p-0 backdrop:bg-bg-overlay\"\n onClose={onClose}\n >\n <div className=\"w-full max-w-xl bg-bg-surface rounded-lg shadow-xl max-h-[85vh] flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div className=\"flex items-center gap-3\">\n <div className=\"p-2 bg-action-primary-bg/10 rounded-lg\">\n <Share2 className=\"size-5 text-action-primary-bg\" />\n </div>\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">\n {tr('vibecontrols.sessions.share.title', 'Share Session')}\n </h2>\n <p className=\"text-sm text-text-secondary truncate max-w-[300px]\">{session.name}</p>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded hover:bg-bg-sunken transition-colors\"\n >\n <X className=\"size-5 text-text-secondary\" />\n </button>\n </div>\n\n {/* Tabs */}\n <div className=\"flex border-b border-border-default\">\n <button\n type=\"button\"\n onClick={() => setActiveTab('users')}\n className={`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${\n activeTab === 'users'\n ? 'text-action-primary-bg border-b-2 border-action-primary-bg'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n <Users className=\"size-4\" />\n {tr('vibecontrols.sessions.share.shareWithUser', 'Share with User')}\n {existingShares.length > 0 && (\n <span className=\"ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full\">\n {existingShares.length}\n </span>\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => setActiveTab('links')}\n className={`flex items-center gap-2 px-6 py-3 text-sm font-medium transition-colors ${\n activeTab === 'links'\n ? 'text-action-primary-bg border-b-2 border-action-primary-bg'\n : 'text-text-secondary hover:text-text-primary'\n }`}\n >\n <Link2 className=\"size-4\" />\n {tr('vibecontrols.sessions.share.createLink', 'Create Link')}\n {existingLinks.length > 0 && (\n <span className=\"ml-1 px-1.5 py-0.5 text-xs bg-bg-sunken rounded-full\">\n {existingLinks.length}\n </span>\n )}\n </button>\n </div>\n\n {/* Content */}\n <div className=\"flex-1 overflow-y-auto p-6\">\n {activeTab === 'users' ? (\n <div className=\"space-y-6\">\n {/* Share form */}\n <form onSubmit={handleShareWithUser} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.emailAddress', 'Email Address')}\n </label>\n <input\n aria-label=\"teammate@company.com\"\n type=\"email\"\n value={email}\n onChange={(e) => setEmail(e.target.value)}\n placeholder=\"teammate@company.com\"\n required\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n <p className=\"mt-1 text-xs text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.mustBeWorkspaceMember',\n 'Must be an existing workspace member'\n )}\n </p>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.permission', 'Permission')}\n </label>\n <select\n value={permission}\n onChange={(e) => setPermission(e.target.value as SessionSharePermission)}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={SessionSharePermission.View}>\n {tr('vibecontrols.sessions.share.viewOnly', 'View Only')}\n </option>\n <option value={SessionSharePermission.Interact}>\n {tr('vibecontrols.sessions.share.canControl', 'Can Control')}\n </option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.expiresIn', 'Expires In')}\n </label>\n <select\n value={expiresInHours}\n onChange={(e) => setExpiresInHours(Number(e.target.value))}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={1}>1 hour</option>\n <option value={4}>4 hours</option>\n <option value={8}>8 hours</option>\n <option value={24}>24 hours</option>\n <option value={72}>3 days</option>\n <option value={168}>1 week</option>\n </select>\n </div>\n </div>\n\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.messageOptional', 'Message (optional)')}\n </label>\n <textarea\n aria-label=\"Let me show you this issue...\"\n value={message}\n onChange={(e) => setMessage(e.target.value)}\n placeholder=\"Let me show you this issue...\"\n rows={2}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg resize-none\"\n />\n </div>\n\n <div className=\"flex items-center gap-2 p-3 bg-bg-sunken rounded-lg text-sm\">\n {permission === SessionSharePermission.View ? (\n <>\n <Eye className=\"size-4 text-text-secondary\" />\n <span className=\"text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.viewOnlyHint',\n 'User can watch the terminal but cannot type'\n )}\n </span>\n </>\n ) : (\n <>\n <Gamepad2 className=\"size-4 text-text-secondary\" />\n <span className=\"text-text-secondary\">\n {tr(\n 'vibecontrols.sessions.share.controlHint',\n 'User can request control to type (turn-based)'\n )}\n </span>\n </>\n )}\n </div>\n\n <button\n type=\"submit\"\n disabled={sharingUser || !email.trim()}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {sharingUser\n ? tr('vibecontrols.sessions.share.sharing', 'Sharing...')\n : tr('vibecontrols.sessions.share.shareSession', 'Share Session')}\n </button>\n </form>\n\n {/* Existing shares */}\n {existingShares.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">\n {tr('vibecontrols.sessions.share.currentlySharedWith', 'Currently Shared With')}\n </h3>\n <div className=\"space-y-2\">\n {existingShares.map((share) => (\n <div\n key={share.id}\n className=\"flex items-center justify-between p-3 bg-bg-sunken rounded-lg\"\n >\n <div className=\"flex items-center gap-3\">\n <div className=\"size-8 rounded-full bg-action-primary-bg/20 flex items-center justify-center\">\n <Users className=\"size-4 text-action-primary-bg\" />\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n User: {share.sharedWithId.slice(0, 8)}...\n </p>\n <div className=\"flex items-center gap-2 text-xs text-text-secondary\">\n {share.permission === SessionSharePermission.View ? (\n <Eye className=\"size-3\" />\n ) : (\n <Gamepad2 className=\"size-3\" />\n )}\n <span>{share.permission}</span>\n {share.expiresAt && (\n <>\n <span>·</span>\n <Clock className=\"size-3\" />\n <span>{formatExpiry(share.expiresAt)}</span>\n </>\n )}\n </div>\n </div>\n </div>\n <button\n type=\"button\"\n onClick={() => handleRevokeShare(share.id)}\n disabled={revokingShare}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors\"\n title=\"Revoke access\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n ) : (\n <div className=\"space-y-6\">\n {/* Create link form */}\n <form onSubmit={handleCreateLink} className=\"space-y-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.linkNameOptional', 'Link Name (optional)')}\n </label>\n <input\n aria-label=\"Debug session link\"\n type=\"text\"\n value={linkName}\n onChange={(e) => setLinkName(e.target.value)}\n placeholder=\"Debug session link\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.permission', 'Permission')}\n </label>\n <select\n value={linkPermission}\n onChange={(e) => setLinkPermission(e.target.value as SessionSharePermission)}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={SessionSharePermission.View}>\n {tr('vibecontrols.sessions.share.viewOnly', 'View Only')}\n </option>\n <option value={SessionSharePermission.Interact}>\n {tr('vibecontrols.sessions.share.canControl', 'Can Control')}\n </option>\n </select>\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.expiresIn', 'Expires In')}\n </label>\n <select\n value={linkExpiresInHours}\n onChange={(e) => setLinkExpiresInHours(Number(e.target.value))}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n >\n <option value={1}>1 hour</option>\n <option value={4}>4 hours</option>\n <option value={8}>8 hours</option>\n <option value={24}>24 hours</option>\n <option value={72}>3 days</option>\n </select>\n </div>\n </div>\n\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.passwordOptional', 'Password (optional)')}\n </label>\n <input\n aria-label=\"••••••••\"\n type=\"password\"\n value={linkPassword}\n onChange={(e) => setLinkPassword(e.target.value)}\n placeholder=\"••••••••\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n <div>\n <label className=\"block text-sm font-medium text-text-primary mb-1\">\n {tr('vibecontrols.sessions.share.maxUses', 'Max Uses')}\n </label>\n <input\n aria-label=\"Unlimited\"\n type=\"number\"\n value={maxUses ?? ''}\n onChange={(e) => setMaxUses(e.target.value ? Number(e.target.value) : null)}\n placeholder=\"Unlimited\"\n min={1}\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-md text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n\n <button\n type=\"submit\"\n disabled={creatingLink}\n className=\"w-full px-4 py-2 text-sm font-medium text-action-primary-text bg-action-primary-bg rounded-md hover:bg-action-primary-bg-hover transition-colors disabled:opacity-50\"\n >\n {creatingLink\n ? tr('vibecontrols.sessions.share.creating', 'Creating...')\n : tr('vibecontrols.sessions.share.createShareLink', 'Create Share Link')}\n </button>\n </form>\n\n {/* Existing links */}\n {existingLinks.length > 0 && (\n <div>\n <h3 className=\"text-sm font-medium text-text-primary mb-3\">\n {tr('vibecontrols.sessions.share.activeLinks', 'Active Links')}\n </h3>\n <div className=\"space-y-2\">\n {existingLinks.map((link) => (\n <div key={link.id} className=\"p-3 bg-bg-sunken rounded-lg space-y-2\">\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n <Link2 className=\"size-4 text-action-primary-bg\" />\n <span className=\"text-sm font-medium text-text-primary\">\n {link.name || 'Unnamed Link'}\n </span>\n {link.hasPassword && <Lock className=\"size-3 text-text-secondary\" />}\n </div>\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n onClick={() => copyToClipboard(link.id, link.shareUrl)}\n className=\"p-1.5 text-text-secondary hover:text-action-primary-bg hover:bg-action-primary-bg/10 rounded transition-colors\"\n title=\"Copy link\"\n >\n {copied === link.id ? (\n <Check className=\"size-4 text-status-success-text\" />\n ) : copyFailed === link.id ? (\n <X className=\"size-4 text-status-error-text\" />\n ) : (\n <Copy className=\"size-4\" />\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => handleRevokeLink(link.id)}\n disabled={revokingLink}\n className=\"p-1.5 text-text-secondary hover:text-status-error-text hover:bg-status-error-bg/10 rounded transition-colors\"\n title=\"Revoke link\"\n >\n <Trash2 className=\"size-4\" />\n </button>\n </div>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-text-secondary\">\n {link.permission === SessionSharePermission.View ? (\n <span className=\"flex items-center gap-1\">\n <Eye className=\"size-3\" /> View\n </span>\n ) : (\n <span className=\"flex items-center gap-1\">\n <Gamepad2 className=\"size-3\" /> Control\n </span>\n )}\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <Clock className=\"size-3\" /> {formatExpiry(link.expiresAt)}\n </span>\n <span>·</span>\n <span className=\"flex items-center gap-1\">\n <RefreshCw className=\"size-3\" /> {link.useCount}\n {link.maxUses ? `/${link.maxUses}` : ''} uses\n </span>\n </div>\n <div className=\"flex items-center gap-2\">\n <input\n aria-label=\"control\"\n type=\"text\"\n value={normalizeShareUrl(link.shareUrl)}\n readOnly\n className=\"flex-1 px-2 py-1 text-xs font-mono bg-bg-surface border border-border-default rounded text-text-secondary truncate\"\n />\n </div>\n </div>\n ))}\n </div>\n </div>\n )}\n </div>\n )}\n </div>\n\n {/* Footer */}\n <div className=\"px-6 py-4 bg-bg-sunken border-t border-border-default rounded-b-lg\">\n <p className=\"text-xs text-text-secondary text-center\">\n {tr(\n 'vibecontrols.sessions.share.footerNote',\n 'Shared users must be logged in to access the session'\n )}\n </p>\n </div>\n </div>\n </dialog>\n );\n};\n"],"mappings":"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"}
|
|
@@ -1,140 +1,146 @@
|
|
|
1
1
|
import { useCreateVibeDeckShareLinkMutation as e, useListVibeDeckShareLinksQuery as t, useRevokeVibeDeckShareLinkMutation as n } from "../../generated/wspace-operations.js";
|
|
2
|
-
import { useTr as
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
2
|
+
import { useTr as ee } from "../../shared/hooks/useTr.js";
|
|
3
|
+
import { buildEmbedUrl as r, copyTextToClipboard as i, escapeHtmlAttribute as a, normalizeShareUrl as o } from "../../utils/shareUrl.js";
|
|
4
|
+
import { useCallback as s, useState as c } from "react";
|
|
5
|
+
import { Check as l, Code as u, Copy as d, Eye as f, Globe as p, Link2 as m, Loader2 as h, Lock as g, Play as _, Shield as v, Trash2 as te, Users as ne, X as y } from "lucide-react";
|
|
6
|
+
import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
6
7
|
//#region src/components/vibedeck/VibeDeckShareDialog.tsx
|
|
7
|
-
var
|
|
8
|
+
var C = [
|
|
8
9
|
{
|
|
9
10
|
value: "SIGNED_LINK",
|
|
10
11
|
label: "Anyone with the link",
|
|
11
12
|
description: "Anyone who has the link can access, no login required",
|
|
12
|
-
icon:
|
|
13
|
+
icon: m
|
|
13
14
|
},
|
|
14
15
|
{
|
|
15
16
|
value: "PUBLIC",
|
|
16
17
|
label: "Public",
|
|
17
18
|
description: "Anyone can discover and access this deck",
|
|
18
|
-
icon:
|
|
19
|
+
icon: p
|
|
19
20
|
},
|
|
20
21
|
{
|
|
21
22
|
value: "RESTRICTED",
|
|
22
23
|
label: "Restricted",
|
|
23
24
|
description: "Only people with permission can access (requires login)",
|
|
24
|
-
icon:
|
|
25
|
+
icon: g
|
|
25
26
|
}
|
|
26
|
-
],
|
|
27
|
+
], w = [{
|
|
27
28
|
value: "VIEW",
|
|
28
29
|
label: "Viewer",
|
|
29
30
|
description: "Can view the deck",
|
|
30
|
-
icon:
|
|
31
|
+
icon: f
|
|
31
32
|
}, {
|
|
32
33
|
value: "EXECUTE",
|
|
33
34
|
label: "Can execute",
|
|
34
35
|
description: "Can view and execute buttons",
|
|
35
|
-
icon:
|
|
36
|
-
}],
|
|
37
|
-
let
|
|
36
|
+
icon: _
|
|
37
|
+
}], T = ({ vibeDeckId: T, vibeDeckName: E, onClose: D }) => {
|
|
38
|
+
let O = ee(), [k, A] = c(null), [re, j] = c(null), [M, N] = c(null), [P, F] = c(null), [I, L] = c("SIGNED_LINK"), [R, z] = c("EXECUTE"), [B, V] = c(""), [H, U] = c(""), [W, G] = c(!1), [K, q] = c(""), { data: J, loading: Y, refetch: ie } = t({ variables: { vibeDeckId: T } }), [X, { loading: Z }] = e({ refetchQueries: ["ListVibeDeckShareLinks"] }), [Q] = n({ refetchQueries: ["ListVibeDeckShareLinks"] }), $ = J?.vibeDeckShareLinks || [], ae = s(async () => {
|
|
38
39
|
try {
|
|
39
|
-
await
|
|
40
|
-
vibeDeckId:
|
|
41
|
-
name:
|
|
42
|
-
visibility:
|
|
43
|
-
permission:
|
|
44
|
-
expiresInHours:
|
|
45
|
-
password:
|
|
46
|
-
allowIframe:
|
|
47
|
-
} } }),
|
|
40
|
+
await X({ variables: { input: {
|
|
41
|
+
vibeDeckId: T,
|
|
42
|
+
name: K || null,
|
|
43
|
+
visibility: I,
|
|
44
|
+
permission: R,
|
|
45
|
+
expiresInHours: B ? parseInt(B, 10) : null,
|
|
46
|
+
password: H || null,
|
|
47
|
+
allowIframe: W
|
|
48
|
+
} } }), q(""), U(""), V(""), G(!1);
|
|
48
49
|
} catch {}
|
|
49
50
|
}, [
|
|
51
|
+
X,
|
|
52
|
+
T,
|
|
50
53
|
K,
|
|
51
|
-
w,
|
|
52
|
-
H,
|
|
53
|
-
M,
|
|
54
|
-
P,
|
|
55
54
|
I,
|
|
56
55
|
R,
|
|
57
|
-
B
|
|
58
|
-
|
|
56
|
+
B,
|
|
57
|
+
H,
|
|
58
|
+
W
|
|
59
|
+
]), oe = s(async (e) => {
|
|
59
60
|
try {
|
|
60
|
-
await
|
|
61
|
+
await Q({ variables: { id: e } });
|
|
61
62
|
} catch {}
|
|
62
|
-
}, [
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}, []),
|
|
63
|
+
}, [Q]), se = s(async (e, t) => {
|
|
64
|
+
let n = await i(o(t)).then(() => !0, (e) => (console.warn("Failed to copy VibeDeck share link:", e), !1));
|
|
65
|
+
A(n ? e : null), N(n ? null : e), setTimeout(() => {
|
|
66
|
+
A(null), N(null);
|
|
67
|
+
}, 2e3);
|
|
68
|
+
}, []), ce = s(async (e, t) => {
|
|
69
|
+
let n = await i(`<iframe src="${a(r(t))}" width="100%" height="600" frameborder="0" allow="clipboard-write" style="border-radius: 8px; border: 1px solid var(--color-border-subtle);"></iframe>`).then(() => !0, (e) => (console.warn("Failed to copy VibeDeck embed code:", e), !1));
|
|
70
|
+
j(n ? e : null), F(n ? null : e), setTimeout(() => {
|
|
71
|
+
j(null), F(null);
|
|
72
|
+
}, 2e3);
|
|
73
|
+
}, []), le = (e) => {
|
|
68
74
|
switch (e) {
|
|
69
|
-
case "PUBLIC": return /* @__PURE__ */
|
|
75
|
+
case "PUBLIC": return /* @__PURE__ */ x(p, {
|
|
70
76
|
size: 14,
|
|
71
77
|
className: "text-status-success-text"
|
|
72
78
|
});
|
|
73
|
-
case "RESTRICTED": return /* @__PURE__ */
|
|
79
|
+
case "RESTRICTED": return /* @__PURE__ */ x(g, {
|
|
74
80
|
size: 14,
|
|
75
81
|
className: "text-status-warning-text"
|
|
76
82
|
});
|
|
77
|
-
default: return /* @__PURE__ */
|
|
83
|
+
default: return /* @__PURE__ */ x(m, {
|
|
78
84
|
size: 14,
|
|
79
85
|
className: "text-status-info-text"
|
|
80
86
|
});
|
|
81
87
|
}
|
|
82
88
|
};
|
|
83
|
-
return /* @__PURE__ */
|
|
89
|
+
return /* @__PURE__ */ x("div", {
|
|
84
90
|
className: "fixed inset-0 z-50 flex items-center justify-center bg-bg-elevated/60 backdrop-blur-sm",
|
|
85
|
-
children: /* @__PURE__ */
|
|
91
|
+
children: /* @__PURE__ */ S("div", {
|
|
86
92
|
className: "bg-bg-surface border border-border-default rounded-xl shadow-2xl w-full max-w-lg mx-4 max-h-[85vh] flex flex-col",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ S("div", {
|
|
88
94
|
className: "flex items-center justify-between px-6 py-4 border-b border-border-default",
|
|
89
|
-
children: [/* @__PURE__ */
|
|
95
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ S("h2", {
|
|
90
96
|
className: "text-lg font-semibold text-text-primary",
|
|
91
97
|
children: [
|
|
92
98
|
"Share \"",
|
|
93
|
-
|
|
99
|
+
E,
|
|
94
100
|
"\""
|
|
95
101
|
]
|
|
96
|
-
}), /* @__PURE__ */
|
|
102
|
+
}), /* @__PURE__ */ x("p", {
|
|
97
103
|
className: "text-sm text-text-secondary mt-0.5",
|
|
98
|
-
children:
|
|
99
|
-
})] }), /* @__PURE__ */
|
|
104
|
+
children: O("vibecontrols.vibedeck.share.manageAccess", "Manage access to this VibeDeck")
|
|
105
|
+
})] }), /* @__PURE__ */ x("button", {
|
|
100
106
|
type: "button",
|
|
101
|
-
onClick:
|
|
107
|
+
onClick: D,
|
|
102
108
|
className: "p-1.5 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors",
|
|
103
|
-
children: /* @__PURE__ */
|
|
109
|
+
children: /* @__PURE__ */ x(y, { size: 18 })
|
|
104
110
|
})]
|
|
105
|
-
}), /* @__PURE__ */
|
|
111
|
+
}), /* @__PURE__ */ S("div", {
|
|
106
112
|
className: "overflow-y-auto flex-1 px-6 py-4 space-y-6",
|
|
107
|
-
children: [/* @__PURE__ */
|
|
113
|
+
children: [/* @__PURE__ */ S("div", {
|
|
108
114
|
className: "space-y-4",
|
|
109
115
|
children: [
|
|
110
|
-
/* @__PURE__ */
|
|
116
|
+
/* @__PURE__ */ S("h3", {
|
|
111
117
|
className: "text-sm font-medium text-text-primary flex items-center gap-2",
|
|
112
|
-
children: [/* @__PURE__ */
|
|
118
|
+
children: [/* @__PURE__ */ x(m, { size: 14 }), O("vibecontrols.vibedeck.share.createShareLink", "Create share link")]
|
|
113
119
|
}),
|
|
114
|
-
/* @__PURE__ */
|
|
120
|
+
/* @__PURE__ */ x("input", {
|
|
115
121
|
"aria-label": "Link name (optional)",
|
|
116
122
|
type: "text",
|
|
117
|
-
value:
|
|
118
|
-
onChange: (e) =>
|
|
123
|
+
value: K,
|
|
124
|
+
onChange: (e) => q(e.target.value),
|
|
119
125
|
placeholder: "Link name (optional)",
|
|
120
126
|
className: "w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
121
127
|
}),
|
|
122
|
-
/* @__PURE__ */
|
|
128
|
+
/* @__PURE__ */ S("div", {
|
|
123
129
|
className: "space-y-2",
|
|
124
|
-
children: [/* @__PURE__ */
|
|
130
|
+
children: [/* @__PURE__ */ x("label", {
|
|
125
131
|
htmlFor: "vibedecksharedialog-37",
|
|
126
132
|
className: "text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
127
133
|
children: "Access"
|
|
128
|
-
}), /* @__PURE__ */
|
|
134
|
+
}), /* @__PURE__ */ x("div", {
|
|
129
135
|
className: "grid grid-cols-3 gap-2",
|
|
130
|
-
children:
|
|
136
|
+
children: C.map((e) => {
|
|
131
137
|
let t = e.icon;
|
|
132
|
-
return /* @__PURE__ */
|
|
138
|
+
return /* @__PURE__ */ S("button", {
|
|
133
139
|
type: "button",
|
|
134
|
-
onClick: () =>
|
|
135
|
-
className: `flex flex-col items-center gap-1.5 p-3 rounded-lg border-2 text-xs font-medium transition-all ${
|
|
140
|
+
onClick: () => L(e.value),
|
|
141
|
+
className: `flex flex-col items-center gap-1.5 p-3 rounded-lg border-2 text-xs font-medium transition-all ${I === e.value ? "border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg" : "border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary"}`,
|
|
136
142
|
title: e.description,
|
|
137
|
-
children: [/* @__PURE__ */
|
|
143
|
+
children: [/* @__PURE__ */ x(t, { size: 16 }), /* @__PURE__ */ x("span", {
|
|
138
144
|
className: "text-center leading-tight",
|
|
139
145
|
children: e.label
|
|
140
146
|
})]
|
|
@@ -142,180 +148,186 @@ var S = [
|
|
|
142
148
|
})
|
|
143
149
|
})]
|
|
144
150
|
}),
|
|
145
|
-
/* @__PURE__ */
|
|
151
|
+
/* @__PURE__ */ S("div", {
|
|
146
152
|
className: "space-y-2",
|
|
147
|
-
children: [/* @__PURE__ */
|
|
153
|
+
children: [/* @__PURE__ */ x("label", {
|
|
148
154
|
htmlFor: "vibedecksharedialog-37",
|
|
149
155
|
className: "text-xs font-medium text-text-secondary uppercase tracking-wider",
|
|
150
156
|
children: "Permission"
|
|
151
|
-
}), /* @__PURE__ */
|
|
157
|
+
}), /* @__PURE__ */ x("div", {
|
|
152
158
|
className: "grid grid-cols-2 gap-2",
|
|
153
|
-
children:
|
|
159
|
+
children: w.map((e) => {
|
|
154
160
|
let t = e.icon;
|
|
155
|
-
return /* @__PURE__ */
|
|
161
|
+
return /* @__PURE__ */ S("button", {
|
|
156
162
|
type: "button",
|
|
157
|
-
onClick: () =>
|
|
158
|
-
className: `flex items-center gap-2 p-3 rounded-lg border-2 text-sm font-medium transition-all ${
|
|
159
|
-
children: [/* @__PURE__ */
|
|
163
|
+
onClick: () => z(e.value),
|
|
164
|
+
className: `flex items-center gap-2 p-3 rounded-lg border-2 text-sm font-medium transition-all ${R === e.value ? "border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg" : "border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary"}`,
|
|
165
|
+
children: [/* @__PURE__ */ x(t, { size: 14 }), e.label]
|
|
160
166
|
}, e.value);
|
|
161
167
|
})
|
|
162
168
|
})]
|
|
163
169
|
}),
|
|
164
|
-
/* @__PURE__ */
|
|
170
|
+
/* @__PURE__ */ S("div", {
|
|
165
171
|
className: "grid grid-cols-2 gap-3",
|
|
166
|
-
children: [/* @__PURE__ */
|
|
172
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
167
173
|
htmlFor: "vibedecksharedialog-37",
|
|
168
174
|
className: "text-xs font-medium text-text-secondary",
|
|
169
175
|
children: "Expires in (hours)"
|
|
170
|
-
}), /* @__PURE__ */
|
|
176
|
+
}), /* @__PURE__ */ x("input", {
|
|
171
177
|
id: "vibedecksharedialog-37",
|
|
172
178
|
"aria-label": "Never",
|
|
173
179
|
type: "number",
|
|
174
180
|
min: "1",
|
|
175
|
-
value:
|
|
176
|
-
onChange: (e) =>
|
|
181
|
+
value: B,
|
|
182
|
+
onChange: (e) => V(e.target.value),
|
|
177
183
|
placeholder: "Never",
|
|
178
184
|
className: "w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
179
|
-
})] }), /* @__PURE__ */
|
|
185
|
+
})] }), /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("label", {
|
|
180
186
|
htmlFor: "vibedecksharedialog-36",
|
|
181
187
|
className: "text-xs font-medium text-text-secondary",
|
|
182
188
|
children: "Password (optional)"
|
|
183
|
-
}), /* @__PURE__ */
|
|
189
|
+
}), /* @__PURE__ */ x("input", {
|
|
184
190
|
id: "vibedecksharedialog-36",
|
|
185
191
|
"aria-label": "No password",
|
|
186
192
|
type: "password",
|
|
187
|
-
value:
|
|
188
|
-
onChange: (e) =>
|
|
193
|
+
value: H,
|
|
194
|
+
onChange: (e) => U(e.target.value),
|
|
189
195
|
placeholder: "No password",
|
|
190
196
|
className: "w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg"
|
|
191
197
|
})] })]
|
|
192
198
|
}),
|
|
193
|
-
/* @__PURE__ */
|
|
199
|
+
/* @__PURE__ */ S("label", {
|
|
194
200
|
className: "flex items-center gap-3 cursor-pointer",
|
|
195
|
-
children: [/* @__PURE__ */
|
|
201
|
+
children: [/* @__PURE__ */ x("input", {
|
|
196
202
|
"aria-label": "control",
|
|
197
203
|
type: "checkbox",
|
|
198
|
-
checked:
|
|
199
|
-
onChange: (e) =>
|
|
204
|
+
checked: W,
|
|
205
|
+
onChange: (e) => G(e.target.checked),
|
|
200
206
|
className: "size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg"
|
|
201
|
-
}), /* @__PURE__ */
|
|
207
|
+
}), /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("span", {
|
|
202
208
|
className: "text-sm text-text-primary",
|
|
203
209
|
children: "Allow iframe embedding"
|
|
204
|
-
}), /* @__PURE__ */
|
|
210
|
+
}), /* @__PURE__ */ x("p", {
|
|
205
211
|
className: "text-xs text-text-tertiary",
|
|
206
212
|
children: "Enable embedding this deck in other websites"
|
|
207
213
|
})] })]
|
|
208
214
|
}),
|
|
209
|
-
/* @__PURE__ */
|
|
215
|
+
/* @__PURE__ */ S("button", {
|
|
210
216
|
type: "button",
|
|
211
|
-
onClick:
|
|
212
|
-
disabled:
|
|
217
|
+
onClick: ae,
|
|
218
|
+
disabled: Z,
|
|
213
219
|
className: "w-full px-4 py-2.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors flex items-center justify-center gap-2",
|
|
214
|
-
children: [
|
|
220
|
+
children: [Z ? /* @__PURE__ */ x(h, {
|
|
215
221
|
size: 14,
|
|
216
222
|
className: "animate-spin"
|
|
217
|
-
}) : /* @__PURE__ */
|
|
223
|
+
}) : /* @__PURE__ */ x(m, { size: 14 }), O("vibecontrols.vibedeck.share.createLink", "Create Link")]
|
|
218
224
|
})
|
|
219
225
|
]
|
|
220
|
-
}),
|
|
226
|
+
}), Y ? /* @__PURE__ */ x("div", {
|
|
221
227
|
className: "flex items-center justify-center py-6",
|
|
222
|
-
children: /* @__PURE__ */
|
|
228
|
+
children: /* @__PURE__ */ x(h, {
|
|
223
229
|
size: 20,
|
|
224
230
|
className: "animate-spin text-text-tertiary"
|
|
225
231
|
})
|
|
226
|
-
}) :
|
|
232
|
+
}) : $.length > 0 ? /* @__PURE__ */ S("div", {
|
|
227
233
|
className: "space-y-3",
|
|
228
|
-
children: [/* @__PURE__ */
|
|
234
|
+
children: [/* @__PURE__ */ S("h3", {
|
|
229
235
|
className: "text-sm font-medium text-text-primary flex items-center gap-2",
|
|
230
236
|
children: [
|
|
231
|
-
/* @__PURE__ */
|
|
237
|
+
/* @__PURE__ */ x(v, { size: 14 }),
|
|
232
238
|
"Active links (",
|
|
233
|
-
|
|
239
|
+
$.length,
|
|
234
240
|
")"
|
|
235
241
|
]
|
|
236
|
-
}),
|
|
242
|
+
}), $.map((e) => /* @__PURE__ */ S("div", {
|
|
237
243
|
className: `rounded-lg border p-3 space-y-2 ${e.status === "ACTIVE" ? "border-border-default bg-bg-secondary" : "border-border-default/50 bg-bg-secondary/50 opacity-60"}`,
|
|
238
|
-
children: [/* @__PURE__ */
|
|
244
|
+
children: [/* @__PURE__ */ S("div", {
|
|
239
245
|
className: "flex items-center justify-between",
|
|
240
|
-
children: [/* @__PURE__ */
|
|
246
|
+
children: [/* @__PURE__ */ S("div", {
|
|
241
247
|
className: "flex items-center gap-2",
|
|
242
248
|
children: [
|
|
243
|
-
|
|
244
|
-
/* @__PURE__ */
|
|
249
|
+
le(e.visibility),
|
|
250
|
+
/* @__PURE__ */ x("span", {
|
|
245
251
|
className: "text-sm font-medium text-text-primary",
|
|
246
252
|
children: e.name || "Unnamed link"
|
|
247
253
|
}),
|
|
248
|
-
/* @__PURE__ */
|
|
254
|
+
/* @__PURE__ */ x("span", {
|
|
249
255
|
className: `text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${e.status === "ACTIVE" ? "bg-status-success-bg/20 text-status-success-text" : e.status === "REVOKED" ? "bg-status-error-bg/20 text-status-error-text" : "bg-status-warning-bg/20 text-status-warning-text"}`,
|
|
250
256
|
children: e.status
|
|
251
257
|
})
|
|
252
258
|
]
|
|
253
|
-
}), /* @__PURE__ */
|
|
259
|
+
}), /* @__PURE__ */ x("div", {
|
|
254
260
|
className: "flex items-center gap-1",
|
|
255
|
-
children: e.status === "ACTIVE" && /* @__PURE__ */
|
|
256
|
-
/* @__PURE__ */
|
|
261
|
+
children: e.status === "ACTIVE" && /* @__PURE__ */ S(b, { children: [
|
|
262
|
+
/* @__PURE__ */ x("button", {
|
|
257
263
|
type: "button",
|
|
258
|
-
onClick: () =>
|
|
264
|
+
onClick: () => se(e.id, e.shareUrl),
|
|
259
265
|
className: "p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors",
|
|
260
266
|
title: "Copy link",
|
|
261
|
-
children:
|
|
267
|
+
children: k === e.id ? /* @__PURE__ */ x(l, {
|
|
262
268
|
size: 14,
|
|
263
269
|
className: "text-status-success-text"
|
|
264
|
-
}) : /* @__PURE__ */
|
|
270
|
+
}) : M === e.id ? /* @__PURE__ */ x(y, {
|
|
271
|
+
size: 14,
|
|
272
|
+
className: "text-status-error-text"
|
|
273
|
+
}) : /* @__PURE__ */ x(d, { size: 14 })
|
|
265
274
|
}),
|
|
266
|
-
e.allowIframe && /* @__PURE__ */
|
|
275
|
+
e.allowIframe && /* @__PURE__ */ x("button", {
|
|
267
276
|
type: "button",
|
|
268
|
-
onClick: () =>
|
|
277
|
+
onClick: () => ce(e.id, e.shareUrl),
|
|
269
278
|
className: "p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors",
|
|
270
279
|
title: "Copy embed code",
|
|
271
|
-
children:
|
|
280
|
+
children: re === e.id ? /* @__PURE__ */ x(l, {
|
|
272
281
|
size: 14,
|
|
273
282
|
className: "text-status-success-text"
|
|
274
|
-
}) : /* @__PURE__ */
|
|
283
|
+
}) : P === e.id ? /* @__PURE__ */ x(y, {
|
|
284
|
+
size: 14,
|
|
285
|
+
className: "text-status-error-text"
|
|
286
|
+
}) : /* @__PURE__ */ x(u, { size: 14 })
|
|
275
287
|
}),
|
|
276
|
-
/* @__PURE__ */
|
|
288
|
+
/* @__PURE__ */ x("button", {
|
|
277
289
|
type: "button",
|
|
278
|
-
onClick: () =>
|
|
290
|
+
onClick: () => oe(e.id),
|
|
279
291
|
className: "p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-status-error-text transition-colors",
|
|
280
292
|
title: "Revoke link",
|
|
281
|
-
children: /* @__PURE__ */
|
|
293
|
+
children: /* @__PURE__ */ x(te, { size: 14 })
|
|
282
294
|
})
|
|
283
295
|
] })
|
|
284
296
|
})]
|
|
285
|
-
}), /* @__PURE__ */
|
|
297
|
+
}), /* @__PURE__ */ S("div", {
|
|
286
298
|
className: "flex items-center gap-3 text-xs text-text-tertiary",
|
|
287
299
|
children: [
|
|
288
|
-
/* @__PURE__ */
|
|
300
|
+
/* @__PURE__ */ S("span", {
|
|
289
301
|
className: "flex items-center gap-1",
|
|
290
|
-
children: [e.permission === "EXECUTE" ? /* @__PURE__ */
|
|
302
|
+
children: [e.permission === "EXECUTE" ? /* @__PURE__ */ x(_, { size: 10 }) : /* @__PURE__ */ x(f, { size: 10 }), e.permission === "EXECUTE" ? "Can execute" : "View only"]
|
|
291
303
|
}),
|
|
292
|
-
e.hasPassword && /* @__PURE__ */
|
|
304
|
+
e.hasPassword && /* @__PURE__ */ S("span", {
|
|
293
305
|
className: "flex items-center gap-1",
|
|
294
|
-
children: [/* @__PURE__ */
|
|
306
|
+
children: [/* @__PURE__ */ x(g, { size: 10 }), "Password"]
|
|
295
307
|
}),
|
|
296
|
-
e.allowIframe && /* @__PURE__ */
|
|
308
|
+
e.allowIframe && /* @__PURE__ */ S("span", {
|
|
297
309
|
className: "flex items-center gap-1",
|
|
298
|
-
children: [/* @__PURE__ */
|
|
310
|
+
children: [/* @__PURE__ */ x(u, { size: 10 }), "Embeddable"]
|
|
299
311
|
}),
|
|
300
|
-
/* @__PURE__ */
|
|
301
|
-
e.expiresAt && /* @__PURE__ */
|
|
312
|
+
/* @__PURE__ */ S("span", { children: [e.useCount, " uses"] }),
|
|
313
|
+
e.expiresAt && /* @__PURE__ */ S("span", { children: ["Expires ", new Date(e.expiresAt).toLocaleDateString()] })
|
|
302
314
|
]
|
|
303
315
|
})]
|
|
304
316
|
}, e.id))]
|
|
305
|
-
}) : /* @__PURE__ */
|
|
317
|
+
}) : /* @__PURE__ */ S("div", {
|
|
306
318
|
className: "text-center py-6",
|
|
307
319
|
children: [
|
|
308
|
-
/* @__PURE__ */
|
|
320
|
+
/* @__PURE__ */ x(ne, {
|
|
309
321
|
size: 24,
|
|
310
322
|
className: "mx-auto text-text-tertiary mb-2"
|
|
311
323
|
}),
|
|
312
|
-
/* @__PURE__ */
|
|
324
|
+
/* @__PURE__ */ x("p", {
|
|
313
325
|
className: "text-sm text-text-secondary",
|
|
314
|
-
children:
|
|
326
|
+
children: O("vibecontrols.vibedeck.share.noLinks", "No share links yet")
|
|
315
327
|
}),
|
|
316
|
-
/* @__PURE__ */
|
|
328
|
+
/* @__PURE__ */ x("p", {
|
|
317
329
|
className: "text-xs text-text-tertiary mt-1",
|
|
318
|
-
children:
|
|
330
|
+
children: O("vibecontrols.vibedeck.share.createLinkHint", "Create a link above to share this VibeDeck")
|
|
319
331
|
})
|
|
320
332
|
]
|
|
321
333
|
})]
|
|
@@ -324,6 +336,6 @@ var S = [
|
|
|
324
336
|
});
|
|
325
337
|
};
|
|
326
338
|
//#endregion
|
|
327
|
-
export {
|
|
339
|
+
export { T as VibeDeckShareDialog };
|
|
328
340
|
|
|
329
341
|
//# sourceMappingURL=VibeDeckShareDialog.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"VibeDeckShareDialog.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckShareDialog.tsx"],"sourcesContent":["import { type FC, useState, useCallback } from 'react';\nimport {\n X,\n Link2,\n Copy,\n Check,\n Trash2,\n Globe,\n Lock,\n Users,\n Code,\n Eye,\n Play,\n Shield,\n Loader2,\n} from 'lucide-react';\nimport { type VibeDeckShareLink } from '@/generated/wspace-types';\nimport {\n useListVibeDeckShareLinksQuery,\n useCreateVibeDeckShareLinkMutation,\n useRevokeVibeDeckShareLinkMutation,\n} from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\n\ninterface VibeDeckShareDialogProps {\n vibeDeckId: string;\n vibeDeckName: string;\n onClose: () => void;\n}\n\nconst VISIBILITY_OPTIONS = [\n {\n value: 'SIGNED_LINK' as const,\n label: 'Anyone with the link',\n description: 'Anyone who has the link can access, no login required',\n icon: Link2,\n },\n {\n value: 'PUBLIC' as const,\n label: 'Public',\n description: 'Anyone can discover and access this deck',\n icon: Globe,\n },\n {\n value: 'RESTRICTED' as const,\n label: 'Restricted',\n description: 'Only people with permission can access (requires login)',\n icon: Lock,\n },\n];\n\nconst PERMISSION_OPTIONS = [\n {\n value: 'VIEW' as const,\n label: 'Viewer',\n description: 'Can view the deck',\n icon: Eye,\n },\n {\n value: 'EXECUTE' as const,\n label: 'Can execute',\n description: 'Can view and execute buttons',\n icon: Play,\n },\n];\n\nexport const VibeDeckShareDialog: FC<VibeDeckShareDialogProps> = ({\n vibeDeckId,\n vibeDeckName,\n onClose,\n}) => {\n const tr = useTr();\n const [copiedId, setCopiedId] = useState<string | null>(null);\n const [copiedEmbed, setCopiedEmbed] = useState<string | null>(null);\n\n // New link form state\n const [visibility, setVisibility] = useState<'SIGNED_LINK' | 'PUBLIC' | 'RESTRICTED'>(\n 'SIGNED_LINK'\n );\n const [permission, setPermission] = useState<'VIEW' | 'EXECUTE'>('EXECUTE');\n const [expiresInHours, setExpiresInHours] = useState<string>('');\n const [password, setPassword] = useState('');\n const [allowIframe, setAllowIframe] = useState(false);\n const [linkName, setLinkName] = useState('');\n\n const { data, loading, refetch } = useListVibeDeckShareLinksQuery({\n variables: { vibeDeckId },\n });\n\n const [createShareLink, { loading: creating }] = useCreateVibeDeckShareLinkMutation({\n refetchQueries: ['ListVibeDeckShareLinks'],\n });\n\n const [revokeShareLink] = useRevokeVibeDeckShareLinkMutation({\n refetchQueries: ['ListVibeDeckShareLinks'],\n });\n\n const shareLinks = data?.vibeDeckShareLinks || [];\n\n const handleCreate = useCallback(async () => {\n try {\n await createShareLink({\n variables: {\n input: {\n vibeDeckId,\n name: linkName || null,\n visibility,\n permission,\n expiresInHours: expiresInHours ? parseInt(expiresInHours, 10) : null,\n password: password || null,\n allowIframe,\n },\n },\n });\n // Reset form\n setLinkName('');\n setPassword('');\n setExpiresInHours('');\n setAllowIframe(false);\n } catch {\n // Error handled by Apollo\n }\n }, [\n createShareLink,\n vibeDeckId,\n linkName,\n visibility,\n permission,\n expiresInHours,\n password,\n allowIframe,\n ]);\n\n const handleRevoke = useCallback(\n async (id: string) => {\n try {\n await revokeShareLink({ variables: { id } });\n } catch {\n // Error handled by Apollo\n }\n },\n [revokeShareLink]\n );\n\n const handleCopyUrl = useCallback((id: string, url: string) => {\n navigator.clipboard.writeText(url);\n setCopiedId(id);\n setTimeout(() => setCopiedId(null), 2000);\n }, []);\n\n const handleCopyEmbed = useCallback((id: string, url: string) => {\n const embedCode = `<iframe src=\"${url}?embed=true\" width=\"100%\" height=\"600\" frameborder=\"0\" allow=\"clipboard-write\" style=\"border-radius: 8px; border: 1px solid var(--color-border-subtle);\"></iframe>`;\n navigator.clipboard.writeText(embedCode);\n setCopiedEmbed(id);\n setTimeout(() => setCopiedEmbed(null), 2000);\n }, []);\n\n const visibilityIcon = (v: string) => {\n switch (v) {\n case 'PUBLIC':\n return <Globe size={14} className=\"text-status-success-text\" />;\n case 'RESTRICTED':\n return <Lock size={14} className=\"text-status-warning-text\" />;\n default:\n return <Link2 size={14} className=\"text-status-info-text\" />;\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-elevated/60 backdrop-blur-sm\">\n <div className=\"bg-bg-surface border border-border-default rounded-xl shadow-2xl w-full max-w-lg mx-4 max-h-[85vh] flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Share \"{vibeDeckName}\"</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr('vibecontrols.vibedeck.share.manageAccess', 'Manage access to this VibeDeck')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n >\n <X size={18} />\n </button>\n </div>\n\n <div className=\"overflow-y-auto flex-1 px-6 py-4 space-y-6\">\n {/* Create new link section */}\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Link2 size={14} />\n {tr('vibecontrols.vibedeck.share.createShareLink', 'Create share link')}\n </h3>\n\n {/* Link name (optional) */}\n <input\n aria-label=\"Link name (optional)\"\n type=\"text\"\n value={linkName}\n onChange={(e) => setLinkName(e.target.value)}\n placeholder=\"Link name (optional)\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n\n {/* Visibility selector */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\"\n >\n Access\n </label>\n <div className=\"grid grid-cols-3 gap-2\">\n {VISIBILITY_OPTIONS.map((opt) => {\n const Icon = opt.icon;\n const isSelected = visibility === opt.value;\n return (\n <button\n type=\"button\"\n key={opt.value}\n onClick={() => setVisibility(opt.value)}\n className={`flex flex-col items-center gap-1.5 p-3 rounded-lg border-2 text-xs font-medium transition-all ${\n isSelected\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary'\n }`}\n title={opt.description}\n >\n <Icon size={16} />\n <span className=\"text-center leading-tight\">{opt.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Permission selector */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\"\n >\n Permission\n </label>\n <div className=\"grid grid-cols-2 gap-2\">\n {PERMISSION_OPTIONS.map((opt) => {\n const Icon = opt.icon;\n const isSelected = permission === opt.value;\n return (\n <button\n type=\"button\"\n key={opt.value}\n onClick={() => setPermission(opt.value)}\n className={`flex items-center gap-2 p-3 rounded-lg border-2 text-sm font-medium transition-all ${\n isSelected\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary'\n }`}\n >\n <Icon size={14} />\n {opt.label}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Options row */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary\"\n >\n Expires in (hours)\n </label>\n <input\n id=\"vibedecksharedialog-37\"\n aria-label=\"Never\"\n type=\"number\"\n min=\"1\"\n value={expiresInHours}\n onChange={(e) => setExpiresInHours(e.target.value)}\n placeholder=\"Never\"\n className=\"w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n <div>\n <label\n htmlFor=\"vibedecksharedialog-36\"\n className=\"text-xs font-medium text-text-secondary\"\n >\n Password (optional)\n </label>\n <input\n id=\"vibedecksharedialog-36\"\n aria-label=\"No password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"No password\"\n className=\"w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n\n {/* Allow iframe toggle */}\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={allowIframe}\n onChange={(e) => setAllowIframe(e.target.checked)}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n <div>\n <span className=\"text-sm text-text-primary\">Allow iframe embedding</span>\n <p className=\"text-xs text-text-tertiary\">\n Enable embedding this deck in other websites\n </p>\n </div>\n </label>\n\n {/* Create button */}\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={creating}\n className=\"w-full px-4 py-2.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors flex items-center justify-center gap-2\"\n >\n {creating ? <Loader2 size={14} className=\"animate-spin\" /> : <Link2 size={14} />}\n {tr('vibecontrols.vibedeck.share.createLink', 'Create Link')}\n </button>\n </div>\n\n {/* Existing links */}\n {loading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Loader2 size={20} className=\"animate-spin text-text-tertiary\" />\n </div>\n ) : shareLinks.length > 0 ? (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Shield size={14} />\n Active links ({shareLinks.length})\n </h3>\n {shareLinks.map((link: VibeDeckShareLink) => (\n <div\n key={link.id}\n className={`rounded-lg border p-3 space-y-2 ${\n link.status === 'ACTIVE'\n ? 'border-border-default bg-bg-secondary'\n : 'border-border-default/50 bg-bg-secondary/50 opacity-60'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {visibilityIcon(link.visibility)}\n <span className=\"text-sm font-medium text-text-primary\">\n {link.name || 'Unnamed link'}\n </span>\n <span\n className={`text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${\n link.status === 'ACTIVE'\n ? 'bg-status-success-bg/20 text-status-success-text'\n : link.status === 'REVOKED'\n ? 'bg-status-error-bg/20 text-status-error-text'\n : 'bg-status-warning-bg/20 text-status-warning-text'\n }`}\n >\n {link.status}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n {link.status === 'ACTIVE' && (\n <>\n <button\n type=\"button\"\n onClick={() => handleCopyUrl(link.id, link.shareUrl)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Copy link\"\n >\n {copiedId === link.id ? (\n <Check size={14} className=\"text-status-success-text\" />\n ) : (\n <Copy size={14} />\n )}\n </button>\n {link.allowIframe && (\n <button\n type=\"button\"\n onClick={() => handleCopyEmbed(link.id, link.shareUrl)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Copy embed code\"\n >\n {copiedEmbed === link.id ? (\n <Check size={14} className=\"text-status-success-text\" />\n ) : (\n <Code size={14} />\n )}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => handleRevoke(link.id)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-status-error-text transition-colors\"\n title=\"Revoke link\"\n >\n <Trash2 size={14} />\n </button>\n </>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-text-tertiary\">\n <span className=\"flex items-center gap-1\">\n {link.permission === 'EXECUTE' ? <Play size={10} /> : <Eye size={10} />}\n {link.permission === 'EXECUTE' ? 'Can execute' : 'View only'}\n </span>\n {link.hasPassword && (\n <span className=\"flex items-center gap-1\">\n <Lock size={10} />\n Password\n </span>\n )}\n {link.allowIframe && (\n <span className=\"flex items-center gap-1\">\n <Code size={10} />\n Embeddable\n </span>\n )}\n <span>{link.useCount} uses</span>\n {link.expiresAt && (\n <span>Expires {new Date(link.expiresAt).toLocaleDateString()}</span>\n )}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"text-center py-6\">\n <Users size={24} className=\"mx-auto text-text-tertiary mb-2\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('vibecontrols.vibedeck.share.noLinks', 'No share links yet')}\n </p>\n <p className=\"text-xs text-text-tertiary mt-1\">\n {tr(\n 'vibecontrols.vibedeck.share.createLinkHint',\n 'Create a link above to share this VibeDeck'\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":";;;;;;AA8BA,IAAM,IAAqB;CACzB;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,MAAM;EACP;CACD;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,MAAM;EACP;CACD;EACE,OAAO;EACP,OAAO;EACP,aAAa;EACb,MAAM;EACP;CACF,EAEK,IAAqB,CACzB;CACE,OAAO;CACP,OAAO;CACP,aAAa;CACb,MAAM;CACP,EACD;CACE,OAAO;CACP,OAAO;CACP,aAAa;CACb,MAAM;CACP,CACF,EAEY,KAAqD,EAChE,eACA,iBACA,iBACI;CACJ,IAAM,IAAK,GAAO,EACZ,CAAC,GAAU,KAAe,EAAwB,KAAK,EACvD,CAAC,GAAa,KAAkB,EAAwB,KAAK,EAG7D,CAAC,GAAY,KAAiB,EAClC,cACD,EACK,CAAC,GAAY,KAAiB,EAA6B,UAAU,EACrE,CAAC,GAAgB,KAAqB,EAAiB,GAAG,EAC1D,CAAC,GAAU,KAAe,EAAS,GAAG,EACtC,CAAC,GAAa,KAAkB,EAAS,GAAM,EAC/C,CAAC,GAAU,KAAe,EAAS,GAAG,EAEtC,EAAE,SAAM,YAAS,gBAAY,EAA+B,EAChE,WAAW,EAAE,eAAY,EAC1B,CAAC,EAEI,CAAC,GAAiB,EAAE,SAAS,OAAc,EAAmC,EAClF,gBAAgB,CAAC,yBAAyB,EAC3C,CAAC,EAEI,CAAC,KAAmB,EAAmC,EAC3D,gBAAgB,CAAC,yBAAyB,EAC3C,CAAC,EAEI,IAAa,GAAM,sBAAsB,EAAE,EAE3C,IAAe,EAAY,YAAY;AAC3C,MAAI;AAkBF,GAjBA,MAAM,EAAgB,EACpB,WAAW,EACT,OAAO;IACL;IACA,MAAM,KAAY;IAClB;IACA;IACA,gBAAgB,IAAiB,SAAS,GAAgB,GAAG,GAAG;IAChE,UAAU,KAAY;IACtB;IACD,EACF,EACF,CAAC,EAEF,EAAY,GAAG,EACf,EAAY,GAAG,EACf,EAAkB,GAAG,EACrB,EAAe,GAAM;UACf;IAGP;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAEI,IAAe,EACnB,OAAO,MAAe;AACpB,MAAI;AACF,SAAM,EAAgB,EAAE,WAAW,EAAE,OAAI,EAAE,CAAC;UACtC;IAIV,CAAC,EAAgB,CAClB,EAEK,IAAgB,GAAa,GAAY,MAAgB;AAG7D,EAFA,UAAU,UAAU,UAAU,EAAI,EAClC,EAAY,EAAG,EACf,iBAAiB,EAAY,KAAK,EAAE,IAAK;IACxC,EAAE,CAAC,EAEA,IAAkB,GAAa,GAAY,MAAgB;EAC/D,IAAM,IAAY,gBAAgB,EAAI;AAGtC,EAFA,UAAU,UAAU,UAAU,EAAU,EACxC,EAAe,EAAG,EAClB,iBAAiB,EAAe,KAAK,EAAE,IAAK;IAC3C,EAAE,CAAC,EAEA,MAAkB,MAAc;AACpC,UAAQ,GAAR;GACE,KAAK,SACH,QAAO,kBAAC,GAAD;IAAO,MAAM;IAAI,WAAU;IAA6B,CAAA;GACjE,KAAK,aACH,QAAO,kBAAC,GAAD;IAAM,MAAM;IAAI,WAAU;IAA6B,CAAA;GAChE,QACE,QAAO,kBAAC,GAAD;IAAO,MAAM;IAAI,WAAU;IAA0B,CAAA;;;AAIlE,QACE,kBAAC,OAAD;EAAK,WAAU;YACb,kBAAC,OAAD;GAAK,WAAU;aAAf,CAEE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;KAAI,WAAU;eAAd;MAAwD;MAAQ;MAAa;MAAM;QACnF,kBAAC,KAAD;KAAG,WAAU;eACV,EAAG,4CAA4C,iCAAiC;KAC/E,CAAA,CACA,EAAA,CAAA,EACN,kBAAC,UAAD;KACE,MAAK;KACL,SAAS;KACT,WAAU;eAEV,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;KACR,CAAA,CACL;OAEN,kBAAC,OAAD;IAAK,WAAU;cAAf,CAEE,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACE,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAClB,EAAG,+CAA+C,oBAAoB,CACpE;;MAGL,kBAAC,SAAD;OACE,cAAW;OACX,MAAK;OACL,OAAO;OACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;OAC5C,aAAY;OACZ,WAAU;OACV,CAAA;MAGF,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAmB,KAAK,MAAQ;SAC/B,IAAM,IAAO,EAAI;AAEjB,gBACE,kBAAC,UAAD;UACE,MAAK;UAEL,eAAe,EAAc,EAAI,MAAM;UACvC,WAAW,iGANI,MAAe,EAAI,QAQ5B,4EACA;UAEN,OAAO,EAAI;oBATb,CAWE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAClB,kBAAC,QAAD;WAAM,WAAU;qBAA6B,EAAI;WAAa,CAAA,CACvD;YAXF,EAAI,MAWF;UAEX;QACE,CAAA,CACF;;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAmB,KAAK,MAAQ;SAC/B,IAAM,IAAO,EAAI;AAEjB,gBACE,kBAAC,UAAD;UACE,MAAK;UAEL,eAAe,EAAc,EAAI,MAAM;UACvC,WAAW,sFANI,MAAe,EAAI,QAQ5B,4EACA;oBAPR,CAUE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EACjB,EAAI,MACE;YAVF,EAAI,MAUF;UAEX;QACE,CAAA,CACF;;MAGN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,cAAW;QACX,MAAK;QACL,KAAI;QACJ,OAAO;QACP,WAAW,MAAM,EAAkB,EAAE,OAAO,MAAM;QAClD,aAAY;QACZ,WAAU;QACV,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;QACE,SAAQ;QACR,WAAU;kBACX;QAEO,CAAA,EACR,kBAAC,SAAD;QACE,IAAG;QACH,cAAW;QACX,MAAK;QACL,OAAO;QACP,WAAW,MAAM,EAAY,EAAE,OAAO,MAAM;QAC5C,aAAY;QACZ,WAAU;QACV,CAAA,CACE,EAAA,CAAA,CACF;;MAGN,kBAAC,SAAD;OAAO,WAAU;iBAAjB,CACE,kBAAC,SAAD;QACE,cAAW;QACX,MAAK;QACL,SAAS;QACT,WAAW,MAAM,EAAe,EAAE,OAAO,QAAQ;QACjD,WAAU;QACV,CAAA,EACF,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,QAAD;QAAM,WAAU;kBAA4B;QAA6B,CAAA,EACzE,kBAAC,KAAD;QAAG,WAAU;kBAA6B;QAEtC,CAAA,CACA,EAAA,CAAA,CACA;;MAGR,kBAAC,UAAD;OACE,MAAK;OACL,SAAS;OACT,UAAU;OACV,WAAU;iBAJZ,CAMG,IAAW,kBAAC,GAAD;QAAS,MAAM;QAAI,WAAU;QAAiB,CAAA,GAAG,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA,EAC/E,EAAG,0CAA0C,cAAc,CACrD;;MACL;QAGL,IACC,kBAAC,OAAD;KAAK,WAAU;eACb,kBAAC,GAAD;MAAS,MAAM;MAAI,WAAU;MAAoC,CAAA;KAC7D,CAAA,GACJ,EAAW,SAAS,IACtB,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAAd;OACE,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;;OACL,EAAW;OAAO;OAC9B;SACJ,EAAW,KAAK,MACf,kBAAC,OAAD;MAEE,WAAW,mCACT,EAAK,WAAW,WACZ,0CACA;gBALR,CAQE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf;SACG,GAAe,EAAK,WAAW;SAChC,kBAAC,QAAD;UAAM,WAAU;oBACb,EAAK,QAAQ;UACT,CAAA;SACP,kBAAC,QAAD;UACE,WAAW,yDACT,EAAK,WAAW,WACZ,qDACA,EAAK,WAAW,YACd,iDACA;oBAGP,EAAK;UACD,CAAA;SACH;WACN,kBAAC,OAAD;QAAK,WAAU;kBACZ,EAAK,WAAW,YACf,kBAAA,GAAA,EAAA,UAAA;SACE,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAc,EAAK,IAAI,EAAK,SAAS;UACpD,WAAU;UACV,OAAM;oBAEL,MAAa,EAAK,KACjB,kBAAC,GAAD;WAAO,MAAM;WAAI,WAAU;WAA6B,CAAA,GAExD,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;UAEb,CAAA;SACR,EAAK,eACJ,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAgB,EAAK,IAAI,EAAK,SAAS;UACtD,WAAU;UACV,OAAM;oBAEL,MAAgB,EAAK,KACpB,kBAAC,GAAD;WAAO,MAAM;WAAI,WAAU;WAA6B,CAAA,GAExD,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;UAEb,CAAA;SAEX,kBAAC,UAAD;UACE,MAAK;UACL,eAAe,EAAa,EAAK,GAAG;UACpC,WAAU;UACV,OAAM;oBAEN,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;UACb,CAAA;SACR,EAAA,CAAA;QAED,CAAA,CACF;UACN,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACG,EAAK,eAAe,YAAY,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,GAAG,kBAAC,GAAD,EAAK,MAAM,IAAM,CAAA,EACtE,EAAK,eAAe,YAAY,gBAAgB,YAC5C;;QACN,EAAK,eACJ,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAAA,WAEb;;QAER,EAAK,eACJ,kBAAC,QAAD;SAAM,WAAU;mBAAhB,CACE,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA,EAAA,aAEb;;QAET,kBAAC,QAAD,EAAA,UAAA,CAAO,EAAK,UAAS,QAAY,EAAA,CAAA;QAChC,EAAK,aACJ,kBAAC,QAAD,EAAA,UAAA,CAAM,YAAS,IAAI,KAAK,EAAK,UAAU,CAAC,oBAAoB,CAAQ,EAAA,CAAA;QAElE;SACF;QAxFC,EAAK,GAwFN,CACN,CACE;SAEN,kBAAC,OAAD;KAAK,WAAU;eAAf;MACE,kBAAC,GAAD;OAAO,MAAM;OAAI,WAAU;OAAoC,CAAA;MAC/D,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAG,uCAAuC,qBAAqB;OAC9D,CAAA;MACJ,kBAAC,KAAD;OAAG,WAAU;iBACV,EACC,8CACA,6CACD;OACC,CAAA;MACA;OAEJ;MACF;;EACF,CAAA"}
|
|
1
|
+
{"version":3,"file":"VibeDeckShareDialog.js","names":[],"sources":["../../../src/components/vibedeck/VibeDeckShareDialog.tsx"],"sourcesContent":["import { type FC, useState, useCallback } from 'react';\nimport {\n X,\n Link2,\n Copy,\n Check,\n Trash2,\n Globe,\n Lock,\n Users,\n Code,\n Eye,\n Play,\n Shield,\n Loader2,\n} from 'lucide-react';\nimport { type VibeDeckShareLink } from '@/generated/wspace-types';\nimport {\n useListVibeDeckShareLinksQuery,\n useCreateVibeDeckShareLinkMutation,\n useRevokeVibeDeckShareLinkMutation,\n} from '@/generated/wspace-operations';\nimport { useTr } from '../../shared/hooks/useTr';\nimport {\n buildEmbedUrl,\n copyTextToClipboard,\n escapeHtmlAttribute,\n normalizeShareUrl,\n} from '../../utils/shareUrl';\n\ninterface VibeDeckShareDialogProps {\n vibeDeckId: string;\n vibeDeckName: string;\n onClose: () => void;\n}\n\nconst VISIBILITY_OPTIONS = [\n {\n value: 'SIGNED_LINK' as const,\n label: 'Anyone with the link',\n description: 'Anyone who has the link can access, no login required',\n icon: Link2,\n },\n {\n value: 'PUBLIC' as const,\n label: 'Public',\n description: 'Anyone can discover and access this deck',\n icon: Globe,\n },\n {\n value: 'RESTRICTED' as const,\n label: 'Restricted',\n description: 'Only people with permission can access (requires login)',\n icon: Lock,\n },\n];\n\nconst PERMISSION_OPTIONS = [\n {\n value: 'VIEW' as const,\n label: 'Viewer',\n description: 'Can view the deck',\n icon: Eye,\n },\n {\n value: 'EXECUTE' as const,\n label: 'Can execute',\n description: 'Can view and execute buttons',\n icon: Play,\n },\n];\n\nexport const VibeDeckShareDialog: FC<VibeDeckShareDialogProps> = ({\n vibeDeckId,\n vibeDeckName,\n onClose,\n}) => {\n const tr = useTr();\n const [copiedId, setCopiedId] = useState<string | null>(null);\n const [copiedEmbed, setCopiedEmbed] = useState<string | null>(null);\n const [copyFailedId, setCopyFailedId] = useState<string | null>(null);\n const [copyFailedEmbed, setCopyFailedEmbed] = useState<string | null>(null);\n\n // New link form state\n const [visibility, setVisibility] = useState<'SIGNED_LINK' | 'PUBLIC' | 'RESTRICTED'>(\n 'SIGNED_LINK'\n );\n const [permission, setPermission] = useState<'VIEW' | 'EXECUTE'>('EXECUTE');\n const [expiresInHours, setExpiresInHours] = useState<string>('');\n const [password, setPassword] = useState('');\n const [allowIframe, setAllowIframe] = useState(false);\n const [linkName, setLinkName] = useState('');\n\n const { data, loading, refetch } = useListVibeDeckShareLinksQuery({\n variables: { vibeDeckId },\n });\n\n const [createShareLink, { loading: creating }] = useCreateVibeDeckShareLinkMutation({\n refetchQueries: ['ListVibeDeckShareLinks'],\n });\n\n const [revokeShareLink] = useRevokeVibeDeckShareLinkMutation({\n refetchQueries: ['ListVibeDeckShareLinks'],\n });\n\n const shareLinks = data?.vibeDeckShareLinks || [];\n\n const handleCreate = useCallback(async () => {\n try {\n await createShareLink({\n variables: {\n input: {\n vibeDeckId,\n name: linkName || null,\n visibility,\n permission,\n expiresInHours: expiresInHours ? parseInt(expiresInHours, 10) : null,\n password: password || null,\n allowIframe,\n },\n },\n });\n // Reset form\n setLinkName('');\n setPassword('');\n setExpiresInHours('');\n setAllowIframe(false);\n } catch {\n // Error handled by Apollo\n }\n }, [\n createShareLink,\n vibeDeckId,\n linkName,\n visibility,\n permission,\n expiresInHours,\n password,\n allowIframe,\n ]);\n\n const handleRevoke = useCallback(\n async (id: string) => {\n try {\n await revokeShareLink({ variables: { id } });\n } catch {\n // Error handled by Apollo\n }\n },\n [revokeShareLink]\n );\n\n const handleCopyUrl = useCallback(async (id: string, url: string) => {\n const ok = await copyTextToClipboard(normalizeShareUrl(url)).then(\n () => true,\n (error) => {\n console.warn('Failed to copy VibeDeck share link:', error);\n return false;\n }\n );\n setCopiedId(ok ? id : null);\n setCopyFailedId(ok ? null : id);\n setTimeout(() => {\n setCopiedId(null);\n setCopyFailedId(null);\n }, 2000);\n }, []);\n\n const handleCopyEmbed = useCallback(async (id: string, url: string) => {\n const embedCode = `<iframe src=\"${escapeHtmlAttribute(buildEmbedUrl(url))}\" width=\"100%\" height=\"600\" frameborder=\"0\" allow=\"clipboard-write\" style=\"border-radius: 8px; border: 1px solid var(--color-border-subtle);\"></iframe>`;\n const ok = await copyTextToClipboard(embedCode).then(\n () => true,\n (error) => {\n console.warn('Failed to copy VibeDeck embed code:', error);\n return false;\n }\n );\n setCopiedEmbed(ok ? id : null);\n setCopyFailedEmbed(ok ? null : id);\n setTimeout(() => {\n setCopiedEmbed(null);\n setCopyFailedEmbed(null);\n }, 2000);\n }, []);\n\n const visibilityIcon = (v: string) => {\n switch (v) {\n case 'PUBLIC':\n return <Globe size={14} className=\"text-status-success-text\" />;\n case 'RESTRICTED':\n return <Lock size={14} className=\"text-status-warning-text\" />;\n default:\n return <Link2 size={14} className=\"text-status-info-text\" />;\n }\n };\n\n return (\n <div className=\"fixed inset-0 z-50 flex items-center justify-center bg-bg-elevated/60 backdrop-blur-sm\">\n <div className=\"bg-bg-surface border border-border-default rounded-xl shadow-2xl w-full max-w-lg mx-4 max-h-[85vh] flex flex-col\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-6 py-4 border-b border-border-default\">\n <div>\n <h2 className=\"text-lg font-semibold text-text-primary\">Share \"{vibeDeckName}\"</h2>\n <p className=\"text-sm text-text-secondary mt-0.5\">\n {tr('vibecontrols.vibedeck.share.manageAccess', 'Manage access to this VibeDeck')}\n </p>\n </div>\n <button\n type=\"button\"\n onClick={onClose}\n className=\"p-1.5 rounded-lg hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n >\n <X size={18} />\n </button>\n </div>\n\n <div className=\"overflow-y-auto flex-1 px-6 py-4 space-y-6\">\n {/* Create new link section */}\n <div className=\"space-y-4\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Link2 size={14} />\n {tr('vibecontrols.vibedeck.share.createShareLink', 'Create share link')}\n </h3>\n\n {/* Link name (optional) */}\n <input\n aria-label=\"Link name (optional)\"\n type=\"text\"\n value={linkName}\n onChange={(e) => setLinkName(e.target.value)}\n placeholder=\"Link name (optional)\"\n className=\"w-full px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n\n {/* Visibility selector */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\"\n >\n Access\n </label>\n <div className=\"grid grid-cols-3 gap-2\">\n {VISIBILITY_OPTIONS.map((opt) => {\n const Icon = opt.icon;\n const isSelected = visibility === opt.value;\n return (\n <button\n type=\"button\"\n key={opt.value}\n onClick={() => setVisibility(opt.value)}\n className={`flex flex-col items-center gap-1.5 p-3 rounded-lg border-2 text-xs font-medium transition-all ${\n isSelected\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary'\n }`}\n title={opt.description}\n >\n <Icon size={16} />\n <span className=\"text-center leading-tight\">{opt.label}</span>\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Permission selector */}\n <div className=\"space-y-2\">\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary uppercase tracking-wider\"\n >\n Permission\n </label>\n <div className=\"grid grid-cols-2 gap-2\">\n {PERMISSION_OPTIONS.map((opt) => {\n const Icon = opt.icon;\n const isSelected = permission === opt.value;\n return (\n <button\n type=\"button\"\n key={opt.value}\n onClick={() => setPermission(opt.value)}\n className={`flex items-center gap-2 p-3 rounded-lg border-2 text-sm font-medium transition-all ${\n isSelected\n ? 'border-action-primary-bg bg-action-primary-bg/10 text-action-primary-bg'\n : 'border-border-default text-text-secondary hover:border-border-default hover:bg-bg-secondary'\n }`}\n >\n <Icon size={14} />\n {opt.label}\n </button>\n );\n })}\n </div>\n </div>\n\n {/* Options row */}\n <div className=\"grid grid-cols-2 gap-3\">\n <div>\n <label\n htmlFor=\"vibedecksharedialog-37\"\n className=\"text-xs font-medium text-text-secondary\"\n >\n Expires in (hours)\n </label>\n <input\n id=\"vibedecksharedialog-37\"\n aria-label=\"Never\"\n type=\"number\"\n min=\"1\"\n value={expiresInHours}\n onChange={(e) => setExpiresInHours(e.target.value)}\n placeholder=\"Never\"\n className=\"w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n <div>\n <label\n htmlFor=\"vibedecksharedialog-36\"\n className=\"text-xs font-medium text-text-secondary\"\n >\n Password (optional)\n </label>\n <input\n id=\"vibedecksharedialog-36\"\n aria-label=\"No password\"\n type=\"password\"\n value={password}\n onChange={(e) => setPassword(e.target.value)}\n placeholder=\"No password\"\n className=\"w-full mt-1 px-3 py-2 text-sm bg-bg-sunken border border-border-default rounded-lg text-text-primary placeholder:text-text-placeholder focus:outline-none focus:ring-2 focus:ring-action-primary-bg\"\n />\n </div>\n </div>\n\n {/* Allow iframe toggle */}\n <label className=\"flex items-center gap-3 cursor-pointer\">\n <input\n aria-label=\"control\"\n type=\"checkbox\"\n checked={allowIframe}\n onChange={(e) => setAllowIframe(e.target.checked)}\n className=\"size-4 rounded border-border-default text-action-primary-bg focus:ring-action-primary-bg\"\n />\n <div>\n <span className=\"text-sm text-text-primary\">Allow iframe embedding</span>\n <p className=\"text-xs text-text-tertiary\">\n Enable embedding this deck in other websites\n </p>\n </div>\n </label>\n\n {/* Create button */}\n <button\n type=\"button\"\n onClick={handleCreate}\n disabled={creating}\n className=\"w-full px-4 py-2.5 rounded-lg bg-action-primary-bg text-action-primary-text text-sm font-medium hover:bg-action-primary-bg-hover disabled:opacity-50 transition-colors flex items-center justify-center gap-2\"\n >\n {creating ? <Loader2 size={14} className=\"animate-spin\" /> : <Link2 size={14} />}\n {tr('vibecontrols.vibedeck.share.createLink', 'Create Link')}\n </button>\n </div>\n\n {/* Existing links */}\n {loading ? (\n <div className=\"flex items-center justify-center py-6\">\n <Loader2 size={20} className=\"animate-spin text-text-tertiary\" />\n </div>\n ) : shareLinks.length > 0 ? (\n <div className=\"space-y-3\">\n <h3 className=\"text-sm font-medium text-text-primary flex items-center gap-2\">\n <Shield size={14} />\n Active links ({shareLinks.length})\n </h3>\n {shareLinks.map((link: VibeDeckShareLink) => (\n <div\n key={link.id}\n className={`rounded-lg border p-3 space-y-2 ${\n link.status === 'ACTIVE'\n ? 'border-border-default bg-bg-secondary'\n : 'border-border-default/50 bg-bg-secondary/50 opacity-60'\n }`}\n >\n <div className=\"flex items-center justify-between\">\n <div className=\"flex items-center gap-2\">\n {visibilityIcon(link.visibility)}\n <span className=\"text-sm font-medium text-text-primary\">\n {link.name || 'Unnamed link'}\n </span>\n <span\n className={`text-[10px] font-bold uppercase px-1.5 py-0.5 rounded ${\n link.status === 'ACTIVE'\n ? 'bg-status-success-bg/20 text-status-success-text'\n : link.status === 'REVOKED'\n ? 'bg-status-error-bg/20 text-status-error-text'\n : 'bg-status-warning-bg/20 text-status-warning-text'\n }`}\n >\n {link.status}\n </span>\n </div>\n <div className=\"flex items-center gap-1\">\n {link.status === 'ACTIVE' && (\n <>\n <button\n type=\"button\"\n onClick={() => handleCopyUrl(link.id, link.shareUrl)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Copy link\"\n >\n {copiedId === link.id ? (\n <Check size={14} className=\"text-status-success-text\" />\n ) : copyFailedId === link.id ? (\n <X size={14} className=\"text-status-error-text\" />\n ) : (\n <Copy size={14} />\n )}\n </button>\n {link.allowIframe && (\n <button\n type=\"button\"\n onClick={() => handleCopyEmbed(link.id, link.shareUrl)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-text-primary transition-colors\"\n title=\"Copy embed code\"\n >\n {copiedEmbed === link.id ? (\n <Check size={14} className=\"text-status-success-text\" />\n ) : copyFailedEmbed === link.id ? (\n <X size={14} className=\"text-status-error-text\" />\n ) : (\n <Code size={14} />\n )}\n </button>\n )}\n <button\n type=\"button\"\n onClick={() => handleRevoke(link.id)}\n className=\"p-1.5 rounded hover:bg-bg-tertiary text-text-tertiary hover:text-status-error-text transition-colors\"\n title=\"Revoke link\"\n >\n <Trash2 size={14} />\n </button>\n </>\n )}\n </div>\n </div>\n <div className=\"flex items-center gap-3 text-xs text-text-tertiary\">\n <span className=\"flex items-center gap-1\">\n {link.permission === 'EXECUTE' ? <Play size={10} /> : <Eye size={10} />}\n {link.permission === 'EXECUTE' ? 'Can execute' : 'View only'}\n </span>\n {link.hasPassword && (\n <span className=\"flex items-center gap-1\">\n <Lock size={10} />\n Password\n </span>\n )}\n {link.allowIframe && (\n <span className=\"flex items-center gap-1\">\n <Code size={10} />\n Embeddable\n </span>\n )}\n <span>{link.useCount} uses</span>\n {link.expiresAt && (\n <span>Expires {new Date(link.expiresAt).toLocaleDateString()}</span>\n )}\n </div>\n </div>\n ))}\n </div>\n ) : (\n <div className=\"text-center py-6\">\n <Users size={24} className=\"mx-auto text-text-tertiary mb-2\" />\n <p className=\"text-sm text-text-secondary\">\n {tr('vibecontrols.vibedeck.share.noLinks', 'No share links yet')}\n </p>\n <p className=\"text-xs text-text-tertiary mt-1\">\n {tr(\n 'vibecontrols.vibedeck.share.createLinkHint',\n 'Create a link above to share this VibeDeck'\n )}\n </p>\n </div>\n )}\n </div>\n </div>\n </div>\n );\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
//#region src/utils/shareUrl.ts
|
|
2
|
+
function e(e) {
|
|
3
|
+
if (typeof window > "u") return e;
|
|
4
|
+
try {
|
|
5
|
+
let t = new URL(e, window.location.origin);
|
|
6
|
+
if ((t.protocol === "http:" || t.protocol === "https:") && t.pathname.startsWith("/vibecontrols/")) return `${window.location.origin}${t.pathname}${t.search}${t.hash}`;
|
|
7
|
+
} catch {
|
|
8
|
+
return e;
|
|
9
|
+
}
|
|
10
|
+
return e;
|
|
11
|
+
}
|
|
12
|
+
function t(t) {
|
|
13
|
+
let n = e(t);
|
|
14
|
+
try {
|
|
15
|
+
let e = typeof window > "u" ? new URL(n) : new URL(n, window.location.origin);
|
|
16
|
+
return e.protocol !== "http:" && e.protocol !== "https:" ? n : (e.searchParams.set("embed", "true"), e.toString());
|
|
17
|
+
} catch {
|
|
18
|
+
let e = n.indexOf("#"), t = e >= 0 ? n.slice(0, e) : n, r = e >= 0 ? n.slice(e) : "";
|
|
19
|
+
return `${t}${t.includes("?") ? "&" : "?"}embed=true${r}`;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
function n(e) {
|
|
23
|
+
return e.replace(/&/g, "&").replace(/"/g, """).replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">");
|
|
24
|
+
}
|
|
25
|
+
async function r(e) {
|
|
26
|
+
try {
|
|
27
|
+
await navigator.clipboard.writeText(e);
|
|
28
|
+
} catch {
|
|
29
|
+
let t = document.createElement("textarea");
|
|
30
|
+
t.value = e, t.style.position = "fixed", t.style.opacity = "0", document.body.appendChild(t);
|
|
31
|
+
try {
|
|
32
|
+
if (t.select(), !document.execCommand("copy")) throw Error("Clipboard copy failed");
|
|
33
|
+
} finally {
|
|
34
|
+
document.body.removeChild(t);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { t as buildEmbedUrl, r as copyTextToClipboard, n as escapeHtmlAttribute, e as normalizeShareUrl };
|
|
40
|
+
|
|
41
|
+
//# sourceMappingURL=shareUrl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shareUrl.js","names":[],"sources":["../../src/utils/shareUrl.ts"],"sourcesContent":["export function normalizeShareUrl(url: string): string {\n if (typeof window === 'undefined') return url;\n try {\n const parsed = new URL(url, window.location.origin);\n if (\n (parsed.protocol === 'http:' || parsed.protocol === 'https:') &&\n parsed.pathname.startsWith('/vibecontrols/')\n ) {\n return `${window.location.origin}${parsed.pathname}${parsed.search}${parsed.hash}`;\n }\n } catch {\n return url;\n }\n return url;\n}\n\nexport function buildEmbedUrl(url: string): string {\n const normalized = normalizeShareUrl(url);\n try {\n const parsed =\n typeof window === 'undefined'\n ? new URL(normalized)\n : new URL(normalized, window.location.origin);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n return normalized;\n }\n parsed.searchParams.set('embed', 'true');\n return parsed.toString();\n } catch {\n const hashIndex = normalized.indexOf('#');\n const base = hashIndex >= 0 ? normalized.slice(0, hashIndex) : normalized;\n const hash = hashIndex >= 0 ? normalized.slice(hashIndex) : '';\n const separator = base.includes('?') ? '&' : '?';\n return `${base}${separator}embed=true${hash}`;\n }\n}\n\nexport function escapeHtmlAttribute(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/\"/g, '"')\n .replace(/'/g, ''')\n .replace(/</g, '<')\n .replace(/>/g, '>');\n}\n\nexport async function copyTextToClipboard(text: string): Promise<void> {\n try {\n await navigator.clipboard.writeText(text);\n } catch {\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n try {\n textarea.select();\n if (!document.execCommand('copy')) {\n throw new Error('Clipboard copy failed');\n }\n } finally {\n document.body.removeChild(textarea);\n }\n }\n}\n"],"mappings":";AAAA,SAAgB,EAAkB,GAAqB;AACrD,KAAI,OAAO,SAAW,IAAa,QAAO;AAC1C,KAAI;EACF,IAAM,IAAS,IAAI,IAAI,GAAK,OAAO,SAAS,OAAO;AACnD,OACG,EAAO,aAAa,WAAW,EAAO,aAAa,aACpD,EAAO,SAAS,WAAW,iBAAiB,CAE5C,QAAO,GAAG,OAAO,SAAS,SAAS,EAAO,WAAW,EAAO,SAAS,EAAO;SAExE;AACN,SAAO;;AAET,QAAO;;AAGT,SAAgB,EAAc,GAAqB;CACjD,IAAM,IAAa,EAAkB,EAAI;AACzC,KAAI;EACF,IAAM,IACJ,OAAO,SAAW,MACd,IAAI,IAAI,EAAW,GACnB,IAAI,IAAI,GAAY,OAAO,SAAS,OAAO;AAKjD,SAJI,EAAO,aAAa,WAAW,EAAO,aAAa,WAC9C,KAET,EAAO,aAAa,IAAI,SAAS,OAAO,EACjC,EAAO,UAAU;SAClB;EACN,IAAM,IAAY,EAAW,QAAQ,IAAI,EACnC,IAAO,KAAa,IAAI,EAAW,MAAM,GAAG,EAAU,GAAG,GACzD,IAAO,KAAa,IAAI,EAAW,MAAM,EAAU,GAAG;AAE5D,SAAO,GAAG,IADQ,EAAK,SAAS,IAAI,GAAG,MAAM,IAClB,YAAY;;;AAI3C,SAAgB,EAAoB,GAAuB;AACzD,QAAO,EACJ,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,SAAS,CACvB,QAAQ,MAAM,QAAQ,CACtB,QAAQ,MAAM,OAAO,CACrB,QAAQ,MAAM,OAAO;;AAG1B,eAAsB,EAAoB,GAA6B;AACrE,KAAI;AACF,QAAM,UAAU,UAAU,UAAU,EAAK;SACnC;EACN,IAAM,IAAW,SAAS,cAAc,WAAW;AAInD,EAHA,EAAS,QAAQ,GACjB,EAAS,MAAM,WAAW,SAC1B,EAAS,MAAM,UAAU,KACzB,SAAS,KAAK,YAAY,EAAS;AACnC,MAAI;AAEF,OADA,EAAS,QAAQ,EACb,CAAC,SAAS,YAAY,OAAO,CAC/B,OAAU,MAAM,wBAAwB;YAElC;AACR,YAAS,KAAK,YAAY,EAAS"}
|