@tmls-ai/support 0.1.3 → 0.1.5

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 +40 -1
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -106,6 +106,16 @@ var SEVERITIES = [
106
106
  { id: "annoying", label: "Annoying" },
107
107
  { id: "minor", label: "Minor" }
108
108
  ];
109
+ var deeplinkTicket = (() => {
110
+ if (typeof window === "undefined") return null;
111
+ const params = new URLSearchParams(window.location.search);
112
+ const tn = params.get("support");
113
+ if (!tn) return null;
114
+ params.delete("support");
115
+ const qs = params.toString();
116
+ window.history.replaceState({}, "", window.location.pathname + (qs ? `?${qs}` : "") + window.location.hash);
117
+ return tn;
118
+ })();
109
119
  function SupportWidget(props) {
110
120
  const { productId, apiUrl, getToken, accent = "#0a84ff", getContext, appVersion } = props;
111
121
  const api = useMemo(() => new SupportApi(apiUrl, getToken), [apiUrl, getToken]);
@@ -116,6 +126,7 @@ function SupportWidget(props) {
116
126
  const [message, setMessage] = useState("");
117
127
  const [withShot, setWithShot] = useState(true);
118
128
  const [shotPreview, setShotPreview] = useState(null);
129
+ const [capturing, setCapturing] = useState(false);
119
130
  const [sending, setSending] = useState(false);
120
131
  const [lastTicket, setLastTicket] = useState(null);
121
132
  const [threads, setThreads] = useState([]);
@@ -128,9 +139,15 @@ function SupportWidget(props) {
128
139
  installErrorCapture();
129
140
  }, []);
130
141
  const captureShot = useCallback(async () => {
142
+ setCapturing(true);
143
+ await new Promise((resolve) => {
144
+ const ric = window.requestIdleCallback;
145
+ if (ric) ric(() => resolve(), { timeout: 600 });
146
+ else setTimeout(resolve, 150);
147
+ });
131
148
  try {
132
149
  const root = document.documentElement;
133
- const blob = await domToBlob(root, {
150
+ const shoot = () => domToBlob(root, {
134
151
  quality: 0.8,
135
152
  scale: 0.75,
136
153
  width: root.clientWidth || window.innerWidth,
@@ -140,9 +157,16 @@ function SupportWidget(props) {
140
157
  features: { restoreScrollPosition: true },
141
158
  filter: (node) => !(node instanceof HTMLElement && node.dataset.tmlsSupportRoot === "true")
142
159
  });
160
+ let blob = await shoot();
161
+ if (blob.size < 1024) {
162
+ await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r())));
163
+ blob = await shoot();
164
+ }
143
165
  setShotPreview(URL.createObjectURL(blob));
144
166
  captureShot._blob = blob;
145
167
  } catch {
168
+ } finally {
169
+ setCapturing(false);
146
170
  }
147
171
  }, []);
148
172
  const submit = useCallback(async () => {
@@ -150,6 +174,7 @@ function SupportWidget(props) {
150
174
  setSending(true);
151
175
  try {
152
176
  let attachmentKeys = [];
177
+ if (withShot && !captureShot._blob) await captureShot();
153
178
  const blob = withShot ? captureShot._blob : void 0;
154
179
  if (blob) {
155
180
  try {
@@ -204,6 +229,18 @@ function SupportWidget(props) {
204
229
  setMessages(detail.messages);
205
230
  }
206
231
  }, [api, reply, activeId]);
232
+ useEffect(() => {
233
+ if (!deeplinkTicket) return;
234
+ const tn = deeplinkTicket;
235
+ (async () => {
236
+ setOpen(true);
237
+ const convos = await api.listConversations();
238
+ setThreads(convos);
239
+ const match = convos.find((c) => String(c.ticket_number) === tn);
240
+ if (match) await openThread(match.id);
241
+ else setView("threads");
242
+ })();
243
+ }, [api, openThread]);
207
244
  const chip = (active) => ({
208
245
  padding: "6px 12px",
209
246
  borderRadius: 999,
@@ -259,6 +296,7 @@ function SupportWidget(props) {
259
296
  } }),
260
297
  "Attach a screenshot"
261
298
  ] }),
299
+ withShot && capturing && !shotPreview && /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: "#8a8a92", padding: "10px 0", textAlign: "center" }, children: "Capturing screenshot\u2026" }),
262
300
  shotPreview && /* @__PURE__ */ jsx("img", { src: shotPreview, alt: "", style: { width: "100%", borderRadius: 8, border: "0.5px solid #3a3a42" } }),
263
301
  /* @__PURE__ */ jsx("button", { onClick: submit, disabled: !message.trim() || sending, style: { ...primary(accent), opacity: !message.trim() || sending ? 0.5 : 1 }, children: sending ? "Sending\u2026" : "Send report" })
264
302
  ] }),
@@ -328,6 +366,7 @@ function SupportWidget(props) {
328
366
  "button",
329
367
  {
330
368
  onClick: () => {
369
+ if (open) deeplinkTicket = null;
331
370
  setOpen((o) => !o);
332
371
  if (!open && withShot) captureShot();
333
372
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tmls-ai/support",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
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",