@tmls-ai/support 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +53 -2
  2. package/package.json +1 -1
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,7 +120,9 @@ 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();
@@ -123,6 +135,9 @@ function SupportWidget(props) {
123
135
  scale: 0.75,
124
136
  width: root.clientWidth || window.innerWidth,
125
137
  height: root.clientHeight || window.innerHeight,
138
+ // Keep scrolled containers (chat log, lists) at their on-screen scroll
139
+ // position instead of snapping back to the top in the clone.
140
+ features: { restoreScrollPosition: true },
126
141
  filter: (node) => !(node instanceof HTMLElement && node.dataset.tmlsSupportRoot === "true")
127
142
  });
128
143
  setShotPreview(URL.createObjectURL(blob));
@@ -166,14 +181,28 @@ function SupportWidget(props) {
166
181
  }, [api]);
167
182
  const openThread = useCallback(async (id) => {
168
183
  setActiveId(id);
169
- setMessages(await api.listMessages(id));
184
+ setActiveConvo(null);
185
+ setMessages([]);
186
+ setShotUrls({});
170
187
  setView("thread");
188
+ const detail = await api.getConversation(id);
189
+ if (!detail) return;
190
+ setActiveConvo(detail);
191
+ setMessages(detail.messages);
192
+ for (const att of detail.attachments) {
193
+ const url = await api.getScreenshot(att.r2_key);
194
+ if (url) setShotUrls((prev) => ({ ...prev, [att.r2_key]: url }));
195
+ }
171
196
  }, [api]);
172
197
  const sendReply = useCallback(async () => {
173
198
  if (!reply.trim() || !activeId) return;
174
199
  await api.reply(activeId, reply.trim());
175
200
  setReply("");
176
- setMessages(await api.listMessages(activeId));
201
+ const detail = await api.getConversation(activeId);
202
+ if (detail) {
203
+ setActiveConvo(detail);
204
+ setMessages(detail.messages);
205
+ }
177
206
  }, [api, reply, activeId]);
178
207
  const chip = (active) => ({
179
208
  padding: "6px 12px",
@@ -260,6 +289,15 @@ function SupportWidget(props) {
260
289
  ] }, t.id))
261
290
  ] }),
262
291
  view === "thread" && /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: [
292
+ 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: [
293
+ /* @__PURE__ */ jsxs("span", { style: { fontSize: 13, fontWeight: 600 }, children: [
294
+ "#",
295
+ activeConvo.ticket_number
296
+ ] }),
297
+ /* @__PURE__ */ jsx("span", { style: metaPill("#c8c8d0"), children: activeConvo.type }),
298
+ /* @__PURE__ */ jsx("span", { style: metaPill(SEV_COLOR[activeConvo.severity]), children: activeConvo.severity }),
299
+ /* @__PURE__ */ jsx("span", { style: metaPill(activeConvo.status === "resolved" ? "#30d158" : accent), children: activeConvo.status })
300
+ ] }),
263
301
  messages.map((m) => /* @__PURE__ */ jsx("div", { style: {
264
302
  alignSelf: m.author === "user" ? "flex-end" : "flex-start",
265
303
  maxWidth: "85%",
@@ -269,6 +307,7 @@ function SupportWidget(props) {
269
307
  borderRadius: 12,
270
308
  fontSize: 13
271
309
  }, children: m.body }, m.id)),
310
+ 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)),
272
311
  /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
273
312
  /* @__PURE__ */ jsx(
274
313
  "input",
@@ -327,6 +366,18 @@ var primary = (accent) => ({
327
366
  fontWeight: 600,
328
367
  cursor: "pointer"
329
368
  });
369
+ var SEV_COLOR = { blocking: "#ff453a", annoying: "#ff9f0a", minor: "#8e8e93" };
370
+ var metaPill = (color) => ({
371
+ padding: "1px 8px",
372
+ borderRadius: 100,
373
+ fontSize: 10.5,
374
+ fontWeight: 700,
375
+ textTransform: "uppercase",
376
+ letterSpacing: "0.03em",
377
+ color,
378
+ border: `1px solid ${color}55`,
379
+ background: "transparent"
380
+ });
330
381
 
331
382
  // src/index.ts
332
383
  function mountSupportWidget(props) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmls-ai/support",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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",