@tmls-ai/support 0.1.0 → 0.1.2
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/index.js +59 -4
- package/package.json +26 -7
package/dist/index.js
CHANGED
|
@@ -75,6 +75,16 @@ var SupportApi = class {
|
|
|
75
75
|
const res = await fetch(`${this.apiUrl}/v1/conversations/${conversationId}/messages`, { headers: await this.auth() });
|
|
76
76
|
return res.ok ? res.json() : [];
|
|
77
77
|
}
|
|
78
|
+
/** Full thread: meta (type/severity/status) + messages + attachments. */
|
|
79
|
+
async getConversation(conversationId) {
|
|
80
|
+
const res = await fetch(`${this.apiUrl}/v1/conversations/${conversationId}`, { headers: await this.auth() });
|
|
81
|
+
return res.ok ? res.json() : null;
|
|
82
|
+
}
|
|
83
|
+
/** Fetch a screenshot the user owns; returns an object URL (auth header can't ride an <img src>). */
|
|
84
|
+
async getScreenshot(r2Key) {
|
|
85
|
+
const res = await fetch(`${this.apiUrl}/v1/screenshots/${r2Key}`, { headers: await this.auth() });
|
|
86
|
+
return res.ok ? URL.createObjectURL(await res.blob()) : null;
|
|
87
|
+
}
|
|
78
88
|
async reply(conversationId, body) {
|
|
79
89
|
await fetch(`${this.apiUrl}/v1/conversations/${conversationId}/messages`, {
|
|
80
90
|
method: "POST",
|
|
@@ -110,14 +120,23 @@ function SupportWidget(props) {
|
|
|
110
120
|
const [lastTicket, setLastTicket] = useState(null);
|
|
111
121
|
const [threads, setThreads] = useState([]);
|
|
112
122
|
const [activeId, setActiveId] = useState(null);
|
|
123
|
+
const [activeConvo, setActiveConvo] = useState(null);
|
|
113
124
|
const [messages, setMessages] = useState([]);
|
|
125
|
+
const [shotUrls, setShotUrls] = useState({});
|
|
114
126
|
const [reply, setReply] = useState("");
|
|
115
127
|
useEffect(() => {
|
|
116
128
|
installErrorCapture();
|
|
117
129
|
}, []);
|
|
118
130
|
const captureShot = useCallback(async () => {
|
|
119
131
|
try {
|
|
120
|
-
const
|
|
132
|
+
const root = document.documentElement;
|
|
133
|
+
const blob = await domToBlob(root, {
|
|
134
|
+
quality: 0.8,
|
|
135
|
+
scale: 0.75,
|
|
136
|
+
width: root.clientWidth || window.innerWidth,
|
|
137
|
+
height: root.clientHeight || window.innerHeight,
|
|
138
|
+
filter: (node) => !(node instanceof HTMLElement && node.dataset.tmlsSupportRoot === "true")
|
|
139
|
+
});
|
|
121
140
|
setShotPreview(URL.createObjectURL(blob));
|
|
122
141
|
captureShot._blob = blob;
|
|
123
142
|
} catch {
|
|
@@ -159,14 +178,28 @@ function SupportWidget(props) {
|
|
|
159
178
|
}, [api]);
|
|
160
179
|
const openThread = useCallback(async (id) => {
|
|
161
180
|
setActiveId(id);
|
|
162
|
-
|
|
181
|
+
setActiveConvo(null);
|
|
182
|
+
setMessages([]);
|
|
183
|
+
setShotUrls({});
|
|
163
184
|
setView("thread");
|
|
185
|
+
const detail = await api.getConversation(id);
|
|
186
|
+
if (!detail) return;
|
|
187
|
+
setActiveConvo(detail);
|
|
188
|
+
setMessages(detail.messages);
|
|
189
|
+
for (const att of detail.attachments) {
|
|
190
|
+
const url = await api.getScreenshot(att.r2_key);
|
|
191
|
+
if (url) setShotUrls((prev) => ({ ...prev, [att.r2_key]: url }));
|
|
192
|
+
}
|
|
164
193
|
}, [api]);
|
|
165
194
|
const sendReply = useCallback(async () => {
|
|
166
195
|
if (!reply.trim() || !activeId) return;
|
|
167
196
|
await api.reply(activeId, reply.trim());
|
|
168
197
|
setReply("");
|
|
169
|
-
|
|
198
|
+
const detail = await api.getConversation(activeId);
|
|
199
|
+
if (detail) {
|
|
200
|
+
setActiveConvo(detail);
|
|
201
|
+
setMessages(detail.messages);
|
|
202
|
+
}
|
|
170
203
|
}, [api, reply, activeId]);
|
|
171
204
|
const chip = (active) => ({
|
|
172
205
|
padding: "6px 12px",
|
|
@@ -178,7 +211,7 @@ function SupportWidget(props) {
|
|
|
178
211
|
background: active ? accent : "transparent",
|
|
179
212
|
color: active ? "#fff" : "#c8c8d0"
|
|
180
213
|
});
|
|
181
|
-
return /* @__PURE__ */ jsxs("div", { style: { position: "fixed", bottom: 20, right: 20, zIndex: 2147483e3, fontFamily: "system-ui, sans-serif" }, children: [
|
|
214
|
+
return /* @__PURE__ */ jsxs("div", { "data-tmls-support-root": "true", style: { position: "fixed", bottom: 20, right: 20, zIndex: 2147483e3, fontFamily: "system-ui, sans-serif" }, children: [
|
|
182
215
|
open && /* @__PURE__ */ jsxs("div", { style: {
|
|
183
216
|
position: "absolute",
|
|
184
217
|
bottom: 60,
|
|
@@ -253,6 +286,15 @@ function SupportWidget(props) {
|
|
|
253
286
|
] }, t.id))
|
|
254
287
|
] }),
|
|
255
288
|
view === "thread" && /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
|
|
289
|
+
activeConvo && /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap", paddingBottom: 8, borderBottom: "0.5px solid rgba(255,255,255,0.08)" }, children: [
|
|
290
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: 13, fontWeight: 600 }, children: [
|
|
291
|
+
"#",
|
|
292
|
+
activeConvo.ticket_number
|
|
293
|
+
] }),
|
|
294
|
+
/* @__PURE__ */ jsx("span", { style: metaPill("#c8c8d0"), children: activeConvo.type }),
|
|
295
|
+
/* @__PURE__ */ jsx("span", { style: metaPill(SEV_COLOR[activeConvo.severity]), children: activeConvo.severity }),
|
|
296
|
+
/* @__PURE__ */ jsx("span", { style: metaPill(activeConvo.status === "resolved" ? "#30d158" : accent), children: activeConvo.status })
|
|
297
|
+
] }),
|
|
256
298
|
messages.map((m) => /* @__PURE__ */ jsx("div", { style: {
|
|
257
299
|
alignSelf: m.author === "user" ? "flex-end" : "flex-start",
|
|
258
300
|
maxWidth: "85%",
|
|
@@ -262,6 +304,7 @@ function SupportWidget(props) {
|
|
|
262
304
|
borderRadius: 12,
|
|
263
305
|
fontSize: 13
|
|
264
306
|
}, children: m.body }, m.id)),
|
|
307
|
+
activeConvo?.attachments.map((att) => shotUrls[att.r2_key] ? /* @__PURE__ */ jsx("a", { href: shotUrls[att.r2_key], target: "_blank", rel: "noreferrer", style: { display: "block" }, children: /* @__PURE__ */ jsx("img", { src: shotUrls[att.r2_key], alt: "Attached screenshot", style: { width: "100%", borderRadius: 8, border: "0.5px solid #3a3a42" } }) }, att.id) : /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: "#8a8a92" }, children: "Loading screenshot\u2026" }, att.id)),
|
|
265
308
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
266
309
|
/* @__PURE__ */ jsx(
|
|
267
310
|
"input",
|
|
@@ -320,6 +363,18 @@ var primary = (accent) => ({
|
|
|
320
363
|
fontWeight: 600,
|
|
321
364
|
cursor: "pointer"
|
|
322
365
|
});
|
|
366
|
+
var SEV_COLOR = { blocking: "#ff453a", annoying: "#ff9f0a", minor: "#8e8e93" };
|
|
367
|
+
var metaPill = (color) => ({
|
|
368
|
+
padding: "1px 8px",
|
|
369
|
+
borderRadius: 100,
|
|
370
|
+
fontSize: 10.5,
|
|
371
|
+
fontWeight: 700,
|
|
372
|
+
textTransform: "uppercase",
|
|
373
|
+
letterSpacing: "0.03em",
|
|
374
|
+
color,
|
|
375
|
+
border: `1px solid ${color}55`,
|
|
376
|
+
background: "transparent"
|
|
377
|
+
});
|
|
323
378
|
|
|
324
379
|
// src/index.ts
|
|
325
380
|
function mountSupportWidget(props) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tmls-ai/support",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Embeddable Timeless support widget — bottom-right report overlay (type / severity / screenshot) + ticket thread. Auto-captures context, talks to tmls-support-api.",
|
|
5
5
|
"license": "UNLICENSED",
|
|
6
6
|
"type": "module",
|
|
@@ -8,12 +8,31 @@
|
|
|
8
8
|
"main": "./dist/index.js",
|
|
9
9
|
"module": "./dist/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
|
-
"exports": {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"import": "./dist/index.js"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"files": [
|
|
18
|
+
"dist"
|
|
19
|
+
],
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/tmls-ai/tmls-support-widget.git"
|
|
23
|
+
},
|
|
24
|
+
"keywords": [
|
|
25
|
+
"support",
|
|
26
|
+
"widget",
|
|
27
|
+
"timeless",
|
|
28
|
+
"tmls"
|
|
29
|
+
],
|
|
30
|
+
"publishConfig": {
|
|
31
|
+
"access": "public"
|
|
32
|
+
},
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18"
|
|
35
|
+
},
|
|
17
36
|
"scripts": {
|
|
18
37
|
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
19
38
|
"typecheck": "tsc --noEmit",
|