@resenty/widget 0.0.0
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/LICENSE +21 -0
- package/README.md +32 -0
- package/dist/chunks/chat-CJ6R7eoG.js +592 -0
- package/dist/chunks/chat-CZS20945.js +54622 -0
- package/dist/chunks/core-Cpyk9a29.js +214 -0
- package/dist/chunks/index-Df7cNTmq.js +348 -0
- package/dist/chunks/query-common-D_G8O-M_.js +7754 -0
- package/dist/chunks/react-dom-B4CdVWWC.js +1176 -0
- package/dist/chunks/react-root-BBU2MuNT.js +5 -0
- package/dist/chunks/react-root-ChAKSfvh.js +18956 -0
- package/dist/chunks/rolldown-runtime-B_fQOXul.js +11 -0
- package/dist/chunks/schemas-B2StEWw-.js +4326 -0
- package/dist/chunks/ui-common-DfZ7eP4u.js +2372 -0
- package/dist/chunks/ui-f9sPMhJV.js +135 -0
- package/dist/chunks/vendor-CAJw9nfJ.js +68985 -0
- package/dist/resenty.js +676 -0
- package/dist/styles/resenty.css +2 -0
- package/package.json +94 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Resenty
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# @resenty/widget
|
|
2
|
+
|
|
3
|
+
Resenty's customer chat widget, distributed via CDN.
|
|
4
|
+
|
|
5
|
+
## Quick install
|
|
6
|
+
|
|
7
|
+
Paste this `<script>` block into your site's `<head>`:
|
|
8
|
+
|
|
9
|
+
```html
|
|
10
|
+
<script>
|
|
11
|
+
(function (w, d, s) {
|
|
12
|
+
w.Resenty =
|
|
13
|
+
w.Resenty ||
|
|
14
|
+
function () {
|
|
15
|
+
(w.Resenty.q = w.Resenty.q || []).push(arguments);
|
|
16
|
+
};
|
|
17
|
+
var f = d.getElementsByTagName(s)[0],
|
|
18
|
+
j = d.createElement(s);
|
|
19
|
+
j.async = true;
|
|
20
|
+
j.src = 'https://cdn.resenty.com/widget/v1/resenty.js';
|
|
21
|
+
f.parentNode.insertBefore(j, f);
|
|
22
|
+
})(window, document, 'script');
|
|
23
|
+
|
|
24
|
+
window.resentyOptions = { appId: '<your_widget_id>' };
|
|
25
|
+
</script>
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
The widget loads asynchronously from `cdn.resenty.com` (which proxies this package via jsDelivr).
|
|
29
|
+
|
|
30
|
+
## License
|
|
31
|
+
|
|
32
|
+
MIT
|
|
@@ -0,0 +1,592 @@
|
|
|
1
|
+
import { n as __toESM } from "./rolldown-runtime-B_fQOXul.js";
|
|
2
|
+
import { A as _enum, C as e$2, D as r$2, E as r$3, O as r$1, S as o$1, T as e$1, _ as e$5, a as useEditorState, b as e$4, c as index_default, d as Markdown, f as MarkdownManager, g as o$3, h as r, i as useEditor, j as object, l as index_default$2, m as e$3, n as EditorContext, o as renderToReactElement, p as index_default$1, r as useCurrentEditor, s as index_default$3, t as EditorContent, u as Placeholder, v as o$2, w as e, x as o, y as t } from "./vendor-CAJw9nfJ.js";
|
|
3
|
+
import { _ as useMutation, a as createHotkeyHandler, b as require_jsx_runtime, c as createRouter, d as createRoute, f as useNavigate, h as createMemoryHistory, i as widgetThreadsGetThreadsOptions, l as Outlet, n as widgetMessagesGetMessagesQueryKey, o as formatForDisplay, r as widgetMessagesSendMessageMutation, s as RouterProvider, t as widgetMessagesGetMessagesOptions, u as createRootRoute, v as useQuery, x as require_react } from "./query-common-D_G8O-M_.js";
|
|
4
|
+
import { a as Toggle, c as cn, d as useLauncher, f as useFrame, i as Kbd, l as LogoIcon, m as useCore, n as TooltipPopup, o as ToggleGroup, r as TooltipProvider, s as Button, t as Tooltip, u as BaseLayout } from "./ui-common-DfZ7eP4u.js";
|
|
5
|
+
var import_react = /* @__PURE__ */ __toESM(require_react(), 1);
|
|
6
|
+
const ChatPanelContext = (0, import_react.createContext)({
|
|
7
|
+
isOpen: !1,
|
|
8
|
+
onOpenChange: () => {}
|
|
9
|
+
}), useChatPanelContext = () => {
|
|
10
|
+
let d = (0, import_react.useContext)(ChatPanelContext);
|
|
11
|
+
if (!d) throw Error("Chat panel context is unavailable.");
|
|
12
|
+
return d;
|
|
13
|
+
};
|
|
14
|
+
var import_jsx_runtime = /* @__PURE__ */ __toESM(require_jsx_runtime(), 1);
|
|
15
|
+
const rootRoute = createRootRoute({
|
|
16
|
+
notFoundComponent: () => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { children: "This is the notFoundComponent configured on root route" }) }),
|
|
17
|
+
component: () => {
|
|
18
|
+
let { isOpen: d, onOpenChange: I } = useChatPanelContext();
|
|
19
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BaseLayout, {
|
|
20
|
+
isOpen: d,
|
|
21
|
+
onOpenChange: I,
|
|
22
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
23
|
+
className: "isolate flex h-full flex-col overflow-hidden",
|
|
24
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
25
|
+
className: "relative isolate h-full overflow-auto",
|
|
26
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Outlet, {})
|
|
27
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
28
|
+
className: "shrink-0",
|
|
29
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
30
|
+
className: "flex items-center justify-center border-t border-gray-200 bg-gray-100 p-2",
|
|
31
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", {
|
|
32
|
+
className: "rounded-lg px-2 py-1 text-xs leading-none font-medium text-gray-600 transition-all duration-300 ease-in hover:bg-gray-100/70 hover:text-gray-900",
|
|
33
|
+
href: "https://resenty.com/",
|
|
34
|
+
title: "Powered by Resenty",
|
|
35
|
+
target: "_blank",
|
|
36
|
+
rel: "noopener noreferrer",
|
|
37
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", {
|
|
38
|
+
className: "flex items-center gap-1",
|
|
39
|
+
children: [
|
|
40
|
+
"Powered by",
|
|
41
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LogoIcon, { className: "size-3 shrink-0" }),
|
|
42
|
+
"Resenty"
|
|
43
|
+
]
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
})]
|
|
48
|
+
})
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}), createTiptapExtensions = (d = {}) => {
|
|
52
|
+
let I = [
|
|
53
|
+
index_default.configure({}),
|
|
54
|
+
index_default$1,
|
|
55
|
+
Markdown,
|
|
56
|
+
index_default$3
|
|
57
|
+
];
|
|
58
|
+
d.placeholder && I.push(Placeholder.configure({ placeholder: d.placeholder }));
|
|
59
|
+
let L = I.filter((I) => !d.extensions?.some((d) => d.name === I.name));
|
|
60
|
+
return d.extensions ? [...L, ...d.extensions] : L;
|
|
61
|
+
};
|
|
62
|
+
var markdownExtensions = [
|
|
63
|
+
index_default.configure(),
|
|
64
|
+
index_default$2,
|
|
65
|
+
index_default$1
|
|
66
|
+
], contentRendererExtensions = [
|
|
67
|
+
index_default.configure(),
|
|
68
|
+
index_default$2,
|
|
69
|
+
Markdown,
|
|
70
|
+
index_default$1,
|
|
71
|
+
index_default$3
|
|
72
|
+
];
|
|
73
|
+
new MarkdownManager({ extensions: markdownExtensions });
|
|
74
|
+
const renderTiptapContent = (d) => renderToReactElement({
|
|
75
|
+
content: d,
|
|
76
|
+
extensions: contentRendererExtensions
|
|
77
|
+
}), TiptapProvider = ({ children: d, extensions: I, placeholder: L,...R }) => {
|
|
78
|
+
let z = import_react.useMemo(() => createTiptapExtensions({
|
|
79
|
+
placeholder: L,
|
|
80
|
+
extensions: I
|
|
81
|
+
}), [I, L]), B = useEditor({
|
|
82
|
+
content: "",
|
|
83
|
+
...R,
|
|
84
|
+
extensions: z
|
|
85
|
+
}), V = import_react.useMemo(() => ({ editor: B }), [B]);
|
|
86
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditorContext.Provider, {
|
|
87
|
+
value: V,
|
|
88
|
+
children: d
|
|
89
|
+
});
|
|
90
|
+
}, TiptapContentRenderer = ({ content: d }) => renderTiptapContent(d), useFrameHotkey = (d, I, { enabled: L = !0, eventType: R = "keydown", capture: z = !0, target: B = "document", preventDefault: V = !0, stopPropagation: H = !0, platform: U } = {}) => {
|
|
91
|
+
let W = useFrame(), G = (0, import_react.useRef)(I);
|
|
92
|
+
(0, import_react.useEffect)(() => {
|
|
93
|
+
G.current = I;
|
|
94
|
+
}, [I]);
|
|
95
|
+
let K = (0, import_react.useMemo)(() => createHotkeyHandler(d, (d, I) => {
|
|
96
|
+
G.current(d, I);
|
|
97
|
+
}, {
|
|
98
|
+
preventDefault: V,
|
|
99
|
+
stopPropagation: H,
|
|
100
|
+
platform: U
|
|
101
|
+
}), [
|
|
102
|
+
d,
|
|
103
|
+
V,
|
|
104
|
+
H,
|
|
105
|
+
U
|
|
106
|
+
]);
|
|
107
|
+
(0, import_react.useEffect)(() => {
|
|
108
|
+
if (!L) return;
|
|
109
|
+
let d = resolveTargets(B, W.document, W.window);
|
|
110
|
+
if (d.length !== 0) {
|
|
111
|
+
for (let I of d) I.addEventListener(R, K, z);
|
|
112
|
+
return () => {
|
|
113
|
+
for (let I of d) I.removeEventListener(R, K, z);
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}, [
|
|
117
|
+
L,
|
|
118
|
+
W.document,
|
|
119
|
+
W.window,
|
|
120
|
+
R,
|
|
121
|
+
K,
|
|
122
|
+
z,
|
|
123
|
+
B
|
|
124
|
+
]);
|
|
125
|
+
};
|
|
126
|
+
var isTargetRef = (d) => !!(d && typeof d == "object" && "current" in d), resolveTargets = (d, I, L) => !d || d === "both" ? [I, L].filter(Boolean) : d === "document" ? I ? [I] : [] : d === "window" ? L ? [L] : [] : isTargetRef(d) ? d.current ? [d.current] : [] : [d];
|
|
127
|
+
const formatDateTime = (d, I) => {
|
|
128
|
+
let L = 60 * 1e3, R = 60 * L, z = 24 * R;
|
|
129
|
+
30 * z;
|
|
130
|
+
let B = d.getTime() - I.getTime(), V = Math.abs(B);
|
|
131
|
+
if (V < L) return "just now";
|
|
132
|
+
if (V < 2592e6) {
|
|
133
|
+
let d = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
134
|
+
return V < R ? d.format(Math.round(B / L), "minute") : V < z ? d.format(Math.round(B / R), "hour") : d.format(Math.round(B / z), "day");
|
|
135
|
+
}
|
|
136
|
+
let H = d.getFullYear() !== I.getFullYear();
|
|
137
|
+
return `on ${new Intl.DateTimeFormat(void 0, {
|
|
138
|
+
month: "short",
|
|
139
|
+
day: "numeric",
|
|
140
|
+
...H ? { year: "numeric" } : {}
|
|
141
|
+
}).format(d)}`;
|
|
142
|
+
};
|
|
143
|
+
var productSearchSchema = object({ type: _enum([
|
|
144
|
+
"ask",
|
|
145
|
+
"bug",
|
|
146
|
+
"feature"
|
|
147
|
+
]).optional() }), chatEditorProps = { attributes: { class: cn([
|
|
148
|
+
"prose max-h-32 min-h-14 [&.ProseMirror-focused]:outline-none",
|
|
149
|
+
"[&>p.is-editor-empty:first-child::before]:text-gray-400",
|
|
150
|
+
"[&>p.is-editor-empty:first-child::before]:content-[attr(data-placeholder)]",
|
|
151
|
+
"[&>p.is-editor-empty:first-child::before]:float-left",
|
|
152
|
+
"[&>p.is-editor-empty:first-child::before]:h-0",
|
|
153
|
+
"[&>p.is-editor-empty:first-child::before]:pointer-events-none"
|
|
154
|
+
]) } };
|
|
155
|
+
const ChatRoute = createRoute({
|
|
156
|
+
getParentRoute: () => rootRoute,
|
|
157
|
+
path: "/chat/{-$threadID}",
|
|
158
|
+
validateSearch: productSearchSchema,
|
|
159
|
+
component: () => {
|
|
160
|
+
let { threadID: d } = ChatRoute.useParams();
|
|
161
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TiptapProvider, {
|
|
162
|
+
editorProps: chatEditorProps,
|
|
163
|
+
placeholder: "Type your message here...",
|
|
164
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatContent, { threadId: d })
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
var ChatContent = (d) => {
|
|
169
|
+
let [I, L] = (0, import_react.useState)(d.threadId), { queryClient: V } = useCore(), { close: H, isExpanded: U, toggleExpanded: G } = useLauncher(), K = (0, import_react.useRef)(null), { editor: q } = useCurrentEditor(), J = useEditorState({
|
|
170
|
+
editor: q,
|
|
171
|
+
selector: (d) => ({
|
|
172
|
+
isEmpty: d.editor?.isEmpty,
|
|
173
|
+
content: d.editor?.getJSON() ?? void 0
|
|
174
|
+
})
|
|
175
|
+
}), { mutateAsync: Y } = useMutation({
|
|
176
|
+
...widgetMessagesSendMessageMutation(),
|
|
177
|
+
onSuccess: async (d) => {
|
|
178
|
+
await V.invalidateQueries({ queryKey: widgetMessagesGetMessagesQueryKey({ path: { threadId: d.thread_id ?? "" } }) });
|
|
179
|
+
}
|
|
180
|
+
}), { data: X } = useQuery({
|
|
181
|
+
...widgetMessagesGetMessagesOptions({ path: { threadId: I ?? "" } }),
|
|
182
|
+
refetchOnWindowFocus: !0,
|
|
183
|
+
enabled: !!I
|
|
184
|
+
}), Z = async () => {
|
|
185
|
+
if (J?.isEmpty) return;
|
|
186
|
+
let d = await Y({ body: {
|
|
187
|
+
content: J?.content,
|
|
188
|
+
thread_id: I
|
|
189
|
+
} });
|
|
190
|
+
d.thread_id && L(d.thread_id), q?.chain().setContent("").run();
|
|
191
|
+
}, Q = useNavigate();
|
|
192
|
+
useFrameHotkey("Mod+Enter", () => Z(), {
|
|
193
|
+
target: K,
|
|
194
|
+
enabled: !J?.isEmpty
|
|
195
|
+
});
|
|
196
|
+
let $ = (0, import_react.useMemo)(() => [...X?.entries ?? []].sort((d, I) => new Date(I.created_at).getTime() - new Date(d.created_at).getTime()), [X]);
|
|
197
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
198
|
+
className: "flex h-full min-h-0 flex-col overflow-hidden",
|
|
199
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
200
|
+
className: "sticky top-0 flex shrink-0 items-center justify-between border-b border-gray-200 bg-white px-2 py-1",
|
|
201
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
202
|
+
className: "flex items-center gap-2",
|
|
203
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
204
|
+
variant: "ghost",
|
|
205
|
+
size: "icon",
|
|
206
|
+
className: "flex size-10 items-center justify-center rounded-full text-zinc-600",
|
|
207
|
+
onClick: () => Q({ to: "/" }),
|
|
208
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(r$1, {
|
|
209
|
+
className: "size-5",
|
|
210
|
+
weight: "bold"
|
|
211
|
+
})
|
|
212
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
213
|
+
className: "truncate text-sm text-gray-700",
|
|
214
|
+
children: X?.thread.title
|
|
215
|
+
})]
|
|
216
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
217
|
+
className: "flex items-center gap-1",
|
|
218
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
219
|
+
variant: "ghost",
|
|
220
|
+
size: "icon",
|
|
221
|
+
className: "flex size-10 items-center justify-center rounded-full text-zinc-600",
|
|
222
|
+
onClick: G,
|
|
223
|
+
"aria-label": U ? "Collapse chat" : "Expand chat",
|
|
224
|
+
children: U ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(r$2, {
|
|
225
|
+
className: "size-5",
|
|
226
|
+
weight: "bold"
|
|
227
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(r$3, {
|
|
228
|
+
className: "size-5",
|
|
229
|
+
weight: "bold"
|
|
230
|
+
})
|
|
231
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
232
|
+
variant: "ghost",
|
|
233
|
+
size: "icon",
|
|
234
|
+
className: "flex size-10 items-center justify-center rounded-full text-zinc-600",
|
|
235
|
+
onClick: H,
|
|
236
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$3, {
|
|
237
|
+
className: "size-5",
|
|
238
|
+
weight: "bold"
|
|
239
|
+
})
|
|
240
|
+
})]
|
|
241
|
+
})]
|
|
242
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
243
|
+
className: "flex min-h-0 flex-1 flex-col",
|
|
244
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
245
|
+
className: "flex flex-1 flex-col justify-between overflow-hidden",
|
|
246
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
247
|
+
className: cn("flex min-h-40 flex-col-reverse overflow-y-auto"),
|
|
248
|
+
children: $?.map((d, I) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MessageEntry, {
|
|
249
|
+
message: d,
|
|
250
|
+
className: cn("border-t border-gray-200", I === $?.length - 1 && "border-t-0")
|
|
251
|
+
}, d.id))
|
|
252
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
253
|
+
className: "flex flex-col gap-4",
|
|
254
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
255
|
+
className: "m-2 flex h-full flex-col gap-2 rounded-md border border-gray-200",
|
|
256
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EditorContent, {
|
|
257
|
+
editor: q,
|
|
258
|
+
ref: K,
|
|
259
|
+
tabIndex: 0,
|
|
260
|
+
className: "overflow-x-hidden overflow-y-auto p-4"
|
|
261
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TiptapToolbar, { onSubmit: () => void Z() })]
|
|
262
|
+
})
|
|
263
|
+
})]
|
|
264
|
+
})
|
|
265
|
+
})]
|
|
266
|
+
});
|
|
267
|
+
}, TiptapToolbar = ({ onSubmit: d }) => {
|
|
268
|
+
let { editor: I } = useCurrentEditor(), L = useEditorState({
|
|
269
|
+
editor: I,
|
|
270
|
+
selector: (d) => ({
|
|
271
|
+
isEmpty: d.editor?.isEmpty,
|
|
272
|
+
isBoldActive: d.editor?.isActive("bold"),
|
|
273
|
+
isItalicActive: d.editor?.isActive("italic"),
|
|
274
|
+
isUnderlineActive: d.editor?.isActive("underline"),
|
|
275
|
+
codeBlockActive: d.editor?.isActive("codeBlock"),
|
|
276
|
+
canToggleCodeBlock: d.editor?.can().chain().toggleCodeBlock().run(),
|
|
277
|
+
canToggleBold: d.editor?.can().chain().toggleBold().run(),
|
|
278
|
+
canToggleItalic: d.editor?.can().chain().toggleItalic().run(),
|
|
279
|
+
canToggleUnderline: d.editor?.can().toggleUnderline()
|
|
280
|
+
})
|
|
281
|
+
});
|
|
282
|
+
return I ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
283
|
+
className: "flex justify-between p-2",
|
|
284
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
285
|
+
className: "flex items-center gap-1",
|
|
286
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipProvider, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(ToggleGroup, {
|
|
287
|
+
size: "sm",
|
|
288
|
+
multiple: !0,
|
|
289
|
+
value: [
|
|
290
|
+
L?.isBoldActive ? "bold" : void 0,
|
|
291
|
+
L?.isItalicActive ? "italic" : void 0,
|
|
292
|
+
L?.isUnderlineActive ? "underline" : void 0,
|
|
293
|
+
L?.codeBlockActive ? "codeBlock" : void 0
|
|
294
|
+
],
|
|
295
|
+
children: [
|
|
296
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
|
|
297
|
+
"aria-label": "Toggle bold",
|
|
298
|
+
value: "bold",
|
|
299
|
+
pressed: L?.isBoldActive,
|
|
300
|
+
onClick: () => I.chain().focus().toggleBold().run(),
|
|
301
|
+
disabled: !L?.canToggleBold,
|
|
302
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(o$2, {
|
|
303
|
+
className: "size-4",
|
|
304
|
+
weight: "bold"
|
|
305
|
+
})
|
|
306
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPopup, { children: "Bold" })] }),
|
|
307
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
|
|
308
|
+
"aria-label": "Toggle italic",
|
|
309
|
+
value: "italic",
|
|
310
|
+
pressed: L?.isItalicActive,
|
|
311
|
+
onClick: () => I.chain().focus().toggleItalic().run(),
|
|
312
|
+
disabled: !L?.canToggleItalic,
|
|
313
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$5, {
|
|
314
|
+
className: "size-4",
|
|
315
|
+
weight: "bold"
|
|
316
|
+
})
|
|
317
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPopup, { children: "Italic" })] }),
|
|
318
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
|
|
319
|
+
"aria-label": "Toggle underline",
|
|
320
|
+
value: "underline",
|
|
321
|
+
pressed: L?.isUnderlineActive,
|
|
322
|
+
onClick: () => I.chain().focus().toggleUnderline().run(),
|
|
323
|
+
disabled: !L?.canToggleUnderline,
|
|
324
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(o$3, {
|
|
325
|
+
className: "size-4",
|
|
326
|
+
weight: "bold"
|
|
327
|
+
})
|
|
328
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPopup, { children: "Underline" })] }),
|
|
329
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Tooltip, { children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Toggle, {
|
|
330
|
+
"aria-label": "Toggle code block",
|
|
331
|
+
value: "codeBlock",
|
|
332
|
+
pressed: L?.codeBlockActive,
|
|
333
|
+
onClick: () => I.chain().focus().toggleCodeBlock().run(),
|
|
334
|
+
disabled: !L?.canToggleCodeBlock,
|
|
335
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$4, {
|
|
336
|
+
className: "size-4",
|
|
337
|
+
weight: "bold"
|
|
338
|
+
})
|
|
339
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TooltipPopup, { children: "Code Block" })] })
|
|
340
|
+
]
|
|
341
|
+
}) })
|
|
342
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(Button, {
|
|
343
|
+
type: "button",
|
|
344
|
+
disabled: L?.isEmpty,
|
|
345
|
+
onClick: d,
|
|
346
|
+
children: ["Send ", /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Kbd, {
|
|
347
|
+
className: "text-sm text-white",
|
|
348
|
+
children: formatForDisplay("Mod+Enter")
|
|
349
|
+
})]
|
|
350
|
+
})]
|
|
351
|
+
}) : null;
|
|
352
|
+
}, MessageEntry = ({ message: d, className: I }) => {
|
|
353
|
+
let [, L] = (0, import_react.useState)(0), R = () => {
|
|
354
|
+
L((d) => d + 1);
|
|
355
|
+
};
|
|
356
|
+
(0, import_react.useEffect)(() => {
|
|
357
|
+
let d = setInterval(R, 6e4);
|
|
358
|
+
return () => clearInterval(d);
|
|
359
|
+
}, [d.created_at]);
|
|
360
|
+
let z = d.sender, B, V = null;
|
|
361
|
+
if (z?.kind === "agent") B = z.name ?? "Support", V = z.image;
|
|
362
|
+
else if (z?.kind === "customer") {
|
|
363
|
+
let d = z.name || z.email;
|
|
364
|
+
B = d ? `${d} (You)` : "You";
|
|
365
|
+
} else B = "System";
|
|
366
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
367
|
+
className: cn("flex justify-between gap-2 px-4 py-4", I),
|
|
368
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
369
|
+
className: "flex w-full flex-col gap-2",
|
|
370
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
371
|
+
className: "flex items-center gap-2",
|
|
372
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
373
|
+
className: "flex size-8 shrink-0 items-center justify-center overflow-hidden rounded-md bg-gray-100 text-gray-500",
|
|
374
|
+
children: V ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", {
|
|
375
|
+
src: V,
|
|
376
|
+
alt: B,
|
|
377
|
+
className: "size-full object-cover"
|
|
378
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(r, {
|
|
379
|
+
className: "size-5",
|
|
380
|
+
weight: "duotone"
|
|
381
|
+
})
|
|
382
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
383
|
+
className: "flex w-full items-center gap-2",
|
|
384
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
385
|
+
className: "font-medium text-gray-800",
|
|
386
|
+
children: B
|
|
387
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
388
|
+
className: "mt-[2px] text-xs text-gray-500",
|
|
389
|
+
children: formatDateTime(new Date(d.created_at), /* @__PURE__ */ new Date())
|
|
390
|
+
})]
|
|
391
|
+
})]
|
|
392
|
+
}), d.content && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
393
|
+
className: "prose ml-10 max-w-2/3 rounded-lg bg-gray-50 p-2 text-sm wrap-break-word text-gray-700",
|
|
394
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TiptapContentRenderer, { content: d.content })
|
|
395
|
+
})]
|
|
396
|
+
})
|
|
397
|
+
}, d.id);
|
|
398
|
+
}, ACTIONS = [
|
|
399
|
+
{
|
|
400
|
+
label: "Request a feature",
|
|
401
|
+
description: "What feature would you like to see in our product?",
|
|
402
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(t, { weight: "duotone" }),
|
|
403
|
+
type: "feature"
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
label: "Report a bug",
|
|
407
|
+
description: "Found a bug? Let us know so we can fix it.",
|
|
408
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$1, { weight: "duotone" }),
|
|
409
|
+
type: "bug"
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
label: "Ask a question",
|
|
413
|
+
description: "Ask us anything, we're here to help.",
|
|
414
|
+
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(o$1, { weight: "duotone" }),
|
|
415
|
+
type: "ask"
|
|
416
|
+
}
|
|
417
|
+
], Home = () => {
|
|
418
|
+
let { close: d } = useLauncher(), I = HomeRoute.useNavigate(), { data: R } = useQuery({ ...widgetThreadsGetThreadsOptions() });
|
|
419
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
420
|
+
className: "isolate flex flex-col justify-between",
|
|
421
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
422
|
+
className: "flex h-20 items-center justify-between px-4 py-8",
|
|
423
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
424
|
+
className: "flex size-10 items-center justify-center rounded-full bg-purple-500 text-white",
|
|
425
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(o$1, {
|
|
426
|
+
className: "size-5",
|
|
427
|
+
weight: "duotone"
|
|
428
|
+
})
|
|
429
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Button, {
|
|
430
|
+
variant: "ghost",
|
|
431
|
+
size: "icon",
|
|
432
|
+
className: "flex size-10 items-center justify-center rounded-full text-zinc-600",
|
|
433
|
+
onClick: d,
|
|
434
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$3, {
|
|
435
|
+
className: "size-5",
|
|
436
|
+
weight: "bold"
|
|
437
|
+
})
|
|
438
|
+
})]
|
|
439
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
440
|
+
className: "isolate flex flex-col gap-4 overflow-y-auto",
|
|
441
|
+
children: [
|
|
442
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
443
|
+
className: "flex flex-col gap-2 px-4",
|
|
444
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
445
|
+
className: "text-2xl font-bold tracking-wide",
|
|
446
|
+
children: "Hello 👋"
|
|
447
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
448
|
+
className: "text-gray-500",
|
|
449
|
+
children: "How can we help you today?"
|
|
450
|
+
})]
|
|
451
|
+
}),
|
|
452
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
453
|
+
className: "mt-6 mb-2 flex w-full flex-col gap-4 px-4 py-2",
|
|
454
|
+
children: ACTIONS.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
455
|
+
onClick: async (L) => {
|
|
456
|
+
L.preventDefault(), await I({
|
|
457
|
+
to: "/chat/{-$threadID}",
|
|
458
|
+
search: { type: d.type }
|
|
459
|
+
});
|
|
460
|
+
},
|
|
461
|
+
type: "button",
|
|
462
|
+
className: cn("flex items-start justify-start p-4 shadow-[0px_0px_1px_1px_rgba(17,17,26,0.1)]", "w-full rounded-md bg-white transition-colors duration-200 hover:bg-gray-50/80"),
|
|
463
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
464
|
+
className: "flex w-full items-center justify-between gap-4",
|
|
465
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
466
|
+
className: "flex gap-4",
|
|
467
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
468
|
+
className: "[&>svg]:size-5 [&>svg]:text-gray-500",
|
|
469
|
+
children: d.icon
|
|
470
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
471
|
+
className: "flex flex-col text-left",
|
|
472
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
473
|
+
className: "text-sm text-gray-700",
|
|
474
|
+
children: d.label.trim()
|
|
475
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", {
|
|
476
|
+
className: "text-xs text-wrap whitespace-normal text-gray-500",
|
|
477
|
+
children: d.description
|
|
478
|
+
})]
|
|
479
|
+
})]
|
|
480
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
481
|
+
className: "hidden size-6 items-center justify-center rounded bg-gray-100 text-gray-600 @sm:flex",
|
|
482
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e$2, {
|
|
483
|
+
className: "size-4",
|
|
484
|
+
weight: "bold"
|
|
485
|
+
})
|
|
486
|
+
})]
|
|
487
|
+
})
|
|
488
|
+
}, d.type))
|
|
489
|
+
}),
|
|
490
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
491
|
+
className: "flex max-h-[calc(1/3*100dvh-1px)] overflow-y-auto border-t border-gray-200",
|
|
492
|
+
children: R && R.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
493
|
+
className: "flex w-full flex-1 flex-col divide-y divide-gray-200",
|
|
494
|
+
children: R.map((d) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ThreadItem, { thread: d }, d.id))
|
|
495
|
+
})
|
|
496
|
+
})
|
|
497
|
+
]
|
|
498
|
+
})]
|
|
499
|
+
});
|
|
500
|
+
}, ThreadItem = ({ thread: d }) => {
|
|
501
|
+
let I = HomeRoute.useNavigate();
|
|
502
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
503
|
+
className: cn(["flex justify-between gap-2 px-4 py-4", "w-full bg-white transition-colors duration-200 hover:bg-gray-50/80"]),
|
|
504
|
+
onClick: () => {
|
|
505
|
+
I({
|
|
506
|
+
to: "/chat/{-$threadID}",
|
|
507
|
+
params: { threadID: d.id }
|
|
508
|
+
});
|
|
509
|
+
},
|
|
510
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
511
|
+
className: "flex w-full flex-col gap-2",
|
|
512
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
513
|
+
className: "flex items-center gap-2",
|
|
514
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
515
|
+
className: "flex size-8 shrink-0 items-center justify-center rounded-md bg-gray-100 text-gray-500",
|
|
516
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(r, {
|
|
517
|
+
className: "size-5",
|
|
518
|
+
weight: "duotone"
|
|
519
|
+
})
|
|
520
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", {
|
|
521
|
+
className: "flex w-full items-center gap-2",
|
|
522
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
523
|
+
className: "text-sm text-gray-800",
|
|
524
|
+
children: d.title.trim()
|
|
525
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
526
|
+
className: "mt-[2px] text-xs text-gray-500",
|
|
527
|
+
children: formatDateTime(new Date(d.created_at), /* @__PURE__ */ new Date())
|
|
528
|
+
})]
|
|
529
|
+
})]
|
|
530
|
+
})
|
|
531
|
+
})
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
const HomeRoute = createRoute({
|
|
535
|
+
getParentRoute: () => rootRoute,
|
|
536
|
+
path: "/",
|
|
537
|
+
component: Home
|
|
538
|
+
}), router = createRouter({
|
|
539
|
+
routeTree: rootRoute.addChildren([HomeRoute, ChatRoute]),
|
|
540
|
+
defaultPreload: "render",
|
|
541
|
+
history: createMemoryHistory({ initialEntries: ["/"] }),
|
|
542
|
+
defaultStaleTime: 5e3,
|
|
543
|
+
scrollRestoration: !0,
|
|
544
|
+
isServer: !1
|
|
545
|
+
}), ChatTrigger = ({ isOpen: d, onToggle: I }) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", {
|
|
546
|
+
className: "resenty-trigger-wrapper",
|
|
547
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
548
|
+
type: "button",
|
|
549
|
+
"aria-label": d ? "Close chat" : "Open chat",
|
|
550
|
+
onClick: I,
|
|
551
|
+
className: "resenty-trigger-button",
|
|
552
|
+
"data-open": d,
|
|
553
|
+
children: d ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(e, {
|
|
554
|
+
size: 24,
|
|
555
|
+
weight: "bold"
|
|
556
|
+
}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(o, {
|
|
557
|
+
size: 24,
|
|
558
|
+
weight: "fill"
|
|
559
|
+
})
|
|
560
|
+
})
|
|
561
|
+
}), useCoreEvents = (d, I, L) => {
|
|
562
|
+
(0, import_react.useEffect)(() => (d.on(I, L), () => {
|
|
563
|
+
d.off(I, L);
|
|
564
|
+
}), [
|
|
565
|
+
d,
|
|
566
|
+
I,
|
|
567
|
+
L
|
|
568
|
+
]);
|
|
569
|
+
}, Chat = () => {
|
|
570
|
+
let { events: d, options: I } = useCore(), [L, R] = (0, import_react.useState)(!1);
|
|
571
|
+
return useCoreEvents(d, "chat:toggle", () => {
|
|
572
|
+
R((d) => !d);
|
|
573
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react.Suspense, {
|
|
574
|
+
fallback: null,
|
|
575
|
+
children: [I?.appearance?.chat?.showTrigger ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatTrigger, {
|
|
576
|
+
isOpen: L,
|
|
577
|
+
onToggle: () => {
|
|
578
|
+
R((d) => !d);
|
|
579
|
+
}
|
|
580
|
+
}) : null, L ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, {
|
|
581
|
+
fallback: null,
|
|
582
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ChatPanelContext.Provider, {
|
|
583
|
+
value: {
|
|
584
|
+
isOpen: L,
|
|
585
|
+
onOpenChange: R
|
|
586
|
+
},
|
|
587
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(RouterProvider, { router })
|
|
588
|
+
})
|
|
589
|
+
}) : null]
|
|
590
|
+
});
|
|
591
|
+
};
|
|
592
|
+
export { Chat };
|