@consilioweb/payload-support 0.8.2 → 0.9.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 (50) hide show
  1. package/dist/components/RichTextEditor/index.cjs +233 -0
  2. package/dist/components/RichTextEditor/index.js +232 -0
  3. package/dist/components/TicketConversation/components/CodeBlock.cjs +24 -7
  4. package/dist/components/TicketConversation/components/CodeBlock.js +23 -9
  5. package/dist/index.cjs +19 -1
  6. package/dist/index.js +19 -1
  7. package/dist/views/BillingView/client.cjs +260 -103
  8. package/dist/views/BillingView/client.js +259 -103
  9. package/dist/views/ChatView/client.cjs +184 -137
  10. package/dist/views/ChatView/client.js +180 -137
  11. package/dist/views/CrmView/client.cjs +270 -122
  12. package/dist/views/CrmView/client.js +266 -122
  13. package/dist/views/EmailTrackingView/client.cjs +80 -69
  14. package/dist/views/EmailTrackingView/client.js +80 -70
  15. package/dist/views/ImportConversationView/client.cjs +127 -94
  16. package/dist/views/ImportConversationView/client.js +123 -94
  17. package/dist/views/LogsView/client.cjs +56 -58
  18. package/dist/views/LogsView/client.js +52 -58
  19. package/dist/views/NewTicketView/client.cjs +39 -55
  20. package/dist/views/NewTicketView/client.js +38 -55
  21. package/dist/views/PendingEmailsView/client.cjs +399 -102
  22. package/dist/views/PendingEmailsView/client.js +396 -103
  23. package/dist/views/SupportDashboardView/client.cjs +276 -137
  24. package/dist/views/SupportDashboardView/client.js +275 -137
  25. package/dist/views/TicketDetailView/client.cjs +487 -204
  26. package/dist/views/TicketDetailView/client.js +486 -204
  27. package/dist/views/TicketInboxView/client.cjs +62 -65
  28. package/dist/views/TicketInboxView/client.js +62 -66
  29. package/dist/views/TicketingSettingsView/client.cjs +10 -8
  30. package/dist/views/TicketingSettingsView/client.js +10 -8
  31. package/dist/views/TimeDashboardView/client.cjs +70 -59
  32. package/dist/views/TimeDashboardView/client.js +69 -59
  33. package/package.json +7 -3
  34. package/src/components/RichTextEditor/index.tsx +261 -0
  35. package/src/components/TicketConversation/components/CodeBlock.tsx +53 -14
  36. package/src/plugin.ts +2 -0
  37. package/src/utils/emailTemplate.ts +37 -0
  38. package/src/views/BillingView/client.tsx +362 -69
  39. package/src/views/ChatView/client.tsx +225 -140
  40. package/src/views/CrmView/client.tsx +447 -189
  41. package/src/views/EmailTrackingView/client.tsx +111 -71
  42. package/src/views/ImportConversationView/client.tsx +255 -70
  43. package/src/views/LogsView/client.tsx +85 -50
  44. package/src/views/NewTicketView/client.tsx +37 -53
  45. package/src/views/PendingEmailsView/client.tsx +512 -92
  46. package/src/views/SupportDashboardView/client.tsx +294 -134
  47. package/src/views/TicketDetailView/client.tsx +486 -213
  48. package/src/views/TicketInboxView/client.tsx +52 -61
  49. package/src/views/TicketingSettingsView/client.tsx +10 -9
  50. package/src/views/TimeDashboardView/client.tsx +184 -69
@@ -0,0 +1,233 @@
1
+ 'use strict';
2
+
3
+ var jsxRuntime = require('react/jsx-runtime');
4
+ var react = require('react');
5
+
6
+ const RichTextEditor = react.forwardRef(function RichTextEditor2({
7
+ initialValue = "",
8
+ onChange,
9
+ placeholder = "\xC9crivez votre message...",
10
+ minHeight = 150,
11
+ onFileUpload,
12
+ borderColor = "#000",
13
+ focusBorderColor = "#00E5FF"
14
+ }, ref) {
15
+ const editorRef = react.useRef(null);
16
+ const fileInputRef = react.useRef(null);
17
+ const [focused, setFocused] = react.useState(false);
18
+ const [isEmpty, setIsEmpty] = react.useState(!initialValue);
19
+ react.useEffect(() => {
20
+ if (editorRef.current && initialValue) {
21
+ editorRef.current.innerHTML = initialValue;
22
+ const text = editorRef.current.innerText?.trim() || "";
23
+ setIsEmpty(!text && !editorRef.current.querySelector("img"));
24
+ }
25
+ }, []);
26
+ const emitChange = react.useCallback(() => {
27
+ if (!editorRef.current) return;
28
+ const html = editorRef.current.innerHTML;
29
+ const text = editorRef.current.innerText?.trim() || "";
30
+ const empty = !text && !editorRef.current.querySelector("img");
31
+ setIsEmpty(empty);
32
+ onChange(empty ? "" : html, text);
33
+ }, [onChange]);
34
+ const exec = react.useCallback((command, value) => {
35
+ document.execCommand(command, false, value);
36
+ editorRef.current?.focus();
37
+ setTimeout(emitChange, 0);
38
+ }, [emitChange]);
39
+ react.useImperativeHandle(ref, () => ({
40
+ clear: () => {
41
+ if (editorRef.current) {
42
+ editorRef.current.innerHTML = "";
43
+ setIsEmpty(true);
44
+ onChange("", "");
45
+ }
46
+ },
47
+ setContent: (html) => {
48
+ if (editorRef.current) {
49
+ editorRef.current.innerHTML = html;
50
+ const text = editorRef.current.innerText?.trim() || "";
51
+ setIsEmpty(!text && !editorRef.current.querySelector("img"));
52
+ onChange(html, text);
53
+ }
54
+ },
55
+ getHtml: () => editorRef.current?.innerHTML || "",
56
+ getPlainText: () => editorRef.current?.innerText?.trim() || "",
57
+ focus: () => editorRef.current?.focus()
58
+ }));
59
+ const handleInsertLink = react.useCallback(() => {
60
+ const sel = window.getSelection();
61
+ if (!sel || sel.isCollapsed) {
62
+ const url = prompt("URL du lien :");
63
+ if (!url) return;
64
+ const text = prompt("Texte du lien :", url) || url;
65
+ const safeUrl = url.replace(/"/g, """);
66
+ const safeText = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
67
+ document.execCommand("insertHTML", false, `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${safeText}</a>`);
68
+ editorRef.current?.focus();
69
+ setTimeout(emitChange, 0);
70
+ } else {
71
+ const url = prompt("URL du lien :");
72
+ if (url) exec("createLink", url);
73
+ }
74
+ }, [exec, emitChange]);
75
+ const handleImageClick = react.useCallback(() => {
76
+ if (onFileUpload) {
77
+ fileInputRef.current?.click();
78
+ } else {
79
+ const url = prompt("URL de l'image :");
80
+ if (url) {
81
+ const safeUrl = url.replace(/"/g, "&quot;");
82
+ document.execCommand("insertHTML", false, `<img src="${safeUrl}" alt="Image" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
83
+ editorRef.current?.focus();
84
+ setTimeout(emitChange, 0);
85
+ }
86
+ }
87
+ }, [onFileUpload, emitChange]);
88
+ const handleFileChange = react.useCallback(async (e) => {
89
+ if (!e.target.files || !onFileUpload) return;
90
+ for (const file of Array.from(e.target.files)) {
91
+ if (!file.type.startsWith("image/")) continue;
92
+ const url = await onFileUpload(file);
93
+ if (url) {
94
+ const safeName = file.name.replace(/"/g, "&quot;");
95
+ document.execCommand("insertHTML", false, `<img src="${url}" alt="${safeName}" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
96
+ editorRef.current?.focus();
97
+ setTimeout(emitChange, 0);
98
+ }
99
+ }
100
+ if (fileInputRef.current) fileInputRef.current.value = "";
101
+ }, [onFileUpload, emitChange]);
102
+ const handlePaste = react.useCallback(async (e) => {
103
+ if (!onFileUpload) return;
104
+ const items = e.clipboardData?.items;
105
+ if (!items) return;
106
+ for (const item of Array.from(items)) {
107
+ if (item.type.startsWith("image/")) {
108
+ e.preventDefault();
109
+ const file = item.getAsFile();
110
+ if (file) {
111
+ const url = await onFileUpload(file);
112
+ if (url) {
113
+ document.execCommand("insertHTML", false, `<img src="${url}" alt="Image coll\xE9e" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
114
+ editorRef.current?.focus();
115
+ setTimeout(emitChange, 0);
116
+ }
117
+ }
118
+ return;
119
+ }
120
+ }
121
+ }, [onFileUpload, emitChange]);
122
+ const btn = {
123
+ border: "none",
124
+ background: "transparent",
125
+ cursor: "pointer",
126
+ padding: "8px 10px",
127
+ fontSize: "13px",
128
+ fontWeight: 700,
129
+ color: "#555",
130
+ borderRadius: "5px",
131
+ lineHeight: 1,
132
+ minHeight: "44px",
133
+ minWidth: "44px",
134
+ display: "inline-flex",
135
+ alignItems: "center",
136
+ justifyContent: "center"
137
+ };
138
+ const sep = {
139
+ width: "1px",
140
+ height: "18px",
141
+ background: "#d1d5db",
142
+ margin: "0 4px",
143
+ alignSelf: "center"
144
+ };
145
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: `3px solid ${focused ? focusBorderColor : borderColor}`, borderRadius: "12px", overflow: "hidden", transition: "border-color 0.15s", background: "#fff" }, children: [
146
+ /* @__PURE__ */ jsxRuntime.jsx("style", { children: `
147
+ .rte-toolbar button:hover { background: #e5e7eb !important; }
148
+ .rte-editor blockquote { border-left: 4px solid #00E5FF; margin: 8px 0; padding: 8px 16px; background: #f0f9ff; border-radius: 0 6px 6px 0; }
149
+ .rte-editor img { max-width: 100%; height: auto; border-radius: 8px; margin: 8px 0; }
150
+ .rte-editor a { color: #00838f; text-decoration: underline; }
151
+ .rte-editor ul, .rte-editor ol { margin: 8px 0; padding-left: 24px; }
152
+ .rte-editor li { margin: 2px 0; }
153
+ .rte-editor p { margin: 0 0 6px 0; }
154
+ ` }),
155
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "rte-toolbar", style: { display: "flex", alignItems: "center", gap: "2px", padding: "6px 10px", borderBottom: "2px solid #e5e7eb", backgroundColor: "#f9fafb", flexWrap: "wrap" }, children: [
156
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
157
+ e.preventDefault();
158
+ exec("bold");
159
+ }, title: "Gras (Ctrl+B)", children: /* @__PURE__ */ jsxRuntime.jsx("strong", { children: "B" }) }),
160
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
161
+ e.preventDefault();
162
+ exec("italic");
163
+ }, title: "Italique (Ctrl+I)", children: /* @__PURE__ */ jsxRuntime.jsx("em", { style: { fontStyle: "italic" }, children: "I" }) }),
164
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
165
+ e.preventDefault();
166
+ exec("underline");
167
+ }, title: "Soulign\xE9 (Ctrl+U)", children: /* @__PURE__ */ jsxRuntime.jsx("span", { style: { textDecoration: "underline" }, children: "S" }) }),
168
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
169
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "16px" }, onMouseDown: (e) => {
170
+ e.preventDefault();
171
+ exec("formatBlock", "blockquote");
172
+ }, title: "Citation", children: "\u201C\u201D" }),
173
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
174
+ e.preventDefault();
175
+ exec("insertUnorderedList");
176
+ }, title: "Liste \xE0 puces", children: "\u2022 Liste" }),
177
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
178
+ e.preventDefault();
179
+ exec("insertOrderedList");
180
+ }, title: "Liste num\xE9rot\xE9e", children: "1. Liste" }),
181
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: sep }),
182
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
183
+ e.preventDefault();
184
+ handleInsertLink();
185
+ }, title: "Ins\xE9rer un lien", children: "\u{1F517}" }),
186
+ /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
187
+ e.preventDefault();
188
+ handleImageClick();
189
+ }, title: "Ins\xE9rer une image", children: "\u{1F5BC}\uFE0F" })
190
+ ] }),
191
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative" }, children: [
192
+ isEmpty && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, padding: "14px 16px", color: "#9ca3af", fontSize: "14px", pointerEvents: "none", userSelect: "none" }, children: placeholder }),
193
+ /* @__PURE__ */ jsxRuntime.jsx(
194
+ "div",
195
+ {
196
+ ref: editorRef,
197
+ className: "rte-editor",
198
+ contentEditable: true,
199
+ suppressContentEditableWarning: true,
200
+ onInput: emitChange,
201
+ onFocus: () => setFocused(true),
202
+ onBlur: () => {
203
+ setFocused(false);
204
+ emitChange();
205
+ },
206
+ onPaste: handlePaste,
207
+ style: {
208
+ minHeight: `${minHeight}px`,
209
+ padding: "14px 16px",
210
+ fontSize: "14px",
211
+ lineHeight: 1.6,
212
+ color: "#1f2937",
213
+ outline: "none",
214
+ overflowY: "auto"
215
+ }
216
+ }
217
+ )
218
+ ] }),
219
+ onFileUpload && /* @__PURE__ */ jsxRuntime.jsx(
220
+ "input",
221
+ {
222
+ ref: fileInputRef,
223
+ type: "file",
224
+ accept: "image/*",
225
+ multiple: true,
226
+ onChange: handleFileChange,
227
+ style: { display: "none" }
228
+ }
229
+ )
230
+ ] });
231
+ });
232
+
233
+ exports.RichTextEditor = RichTextEditor;
@@ -0,0 +1,232 @@
1
+ "use client";
2
+ import { jsxs, jsx } from 'react/jsx-runtime';
3
+ import { forwardRef, useRef, useState, useEffect, useCallback, useImperativeHandle } from 'react';
4
+
5
+ const RichTextEditor = forwardRef(function RichTextEditor2({
6
+ initialValue = "",
7
+ onChange,
8
+ placeholder = "\xC9crivez votre message...",
9
+ minHeight = 150,
10
+ onFileUpload,
11
+ borderColor = "#000",
12
+ focusBorderColor = "#00E5FF"
13
+ }, ref) {
14
+ const editorRef = useRef(null);
15
+ const fileInputRef = useRef(null);
16
+ const [focused, setFocused] = useState(false);
17
+ const [isEmpty, setIsEmpty] = useState(!initialValue);
18
+ useEffect(() => {
19
+ if (editorRef.current && initialValue) {
20
+ editorRef.current.innerHTML = initialValue;
21
+ const text = editorRef.current.innerText?.trim() || "";
22
+ setIsEmpty(!text && !editorRef.current.querySelector("img"));
23
+ }
24
+ }, []);
25
+ const emitChange = useCallback(() => {
26
+ if (!editorRef.current) return;
27
+ const html = editorRef.current.innerHTML;
28
+ const text = editorRef.current.innerText?.trim() || "";
29
+ const empty = !text && !editorRef.current.querySelector("img");
30
+ setIsEmpty(empty);
31
+ onChange(empty ? "" : html, text);
32
+ }, [onChange]);
33
+ const exec = useCallback((command, value) => {
34
+ document.execCommand(command, false, value);
35
+ editorRef.current?.focus();
36
+ setTimeout(emitChange, 0);
37
+ }, [emitChange]);
38
+ useImperativeHandle(ref, () => ({
39
+ clear: () => {
40
+ if (editorRef.current) {
41
+ editorRef.current.innerHTML = "";
42
+ setIsEmpty(true);
43
+ onChange("", "");
44
+ }
45
+ },
46
+ setContent: (html) => {
47
+ if (editorRef.current) {
48
+ editorRef.current.innerHTML = html;
49
+ const text = editorRef.current.innerText?.trim() || "";
50
+ setIsEmpty(!text && !editorRef.current.querySelector("img"));
51
+ onChange(html, text);
52
+ }
53
+ },
54
+ getHtml: () => editorRef.current?.innerHTML || "",
55
+ getPlainText: () => editorRef.current?.innerText?.trim() || "",
56
+ focus: () => editorRef.current?.focus()
57
+ }));
58
+ const handleInsertLink = useCallback(() => {
59
+ const sel = window.getSelection();
60
+ if (!sel || sel.isCollapsed) {
61
+ const url = prompt("URL du lien :");
62
+ if (!url) return;
63
+ const text = prompt("Texte du lien :", url) || url;
64
+ const safeUrl = url.replace(/"/g, "&quot;");
65
+ const safeText = text.replace(/</g, "&lt;").replace(/>/g, "&gt;");
66
+ document.execCommand("insertHTML", false, `<a href="${safeUrl}" target="_blank" rel="noopener noreferrer">${safeText}</a>`);
67
+ editorRef.current?.focus();
68
+ setTimeout(emitChange, 0);
69
+ } else {
70
+ const url = prompt("URL du lien :");
71
+ if (url) exec("createLink", url);
72
+ }
73
+ }, [exec, emitChange]);
74
+ const handleImageClick = useCallback(() => {
75
+ if (onFileUpload) {
76
+ fileInputRef.current?.click();
77
+ } else {
78
+ const url = prompt("URL de l'image :");
79
+ if (url) {
80
+ const safeUrl = url.replace(/"/g, "&quot;");
81
+ document.execCommand("insertHTML", false, `<img src="${safeUrl}" alt="Image" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
82
+ editorRef.current?.focus();
83
+ setTimeout(emitChange, 0);
84
+ }
85
+ }
86
+ }, [onFileUpload, emitChange]);
87
+ const handleFileChange = useCallback(async (e) => {
88
+ if (!e.target.files || !onFileUpload) return;
89
+ for (const file of Array.from(e.target.files)) {
90
+ if (!file.type.startsWith("image/")) continue;
91
+ const url = await onFileUpload(file);
92
+ if (url) {
93
+ const safeName = file.name.replace(/"/g, "&quot;");
94
+ document.execCommand("insertHTML", false, `<img src="${url}" alt="${safeName}" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
95
+ editorRef.current?.focus();
96
+ setTimeout(emitChange, 0);
97
+ }
98
+ }
99
+ if (fileInputRef.current) fileInputRef.current.value = "";
100
+ }, [onFileUpload, emitChange]);
101
+ const handlePaste = useCallback(async (e) => {
102
+ if (!onFileUpload) return;
103
+ const items = e.clipboardData?.items;
104
+ if (!items) return;
105
+ for (const item of Array.from(items)) {
106
+ if (item.type.startsWith("image/")) {
107
+ e.preventDefault();
108
+ const file = item.getAsFile();
109
+ if (file) {
110
+ const url = await onFileUpload(file);
111
+ if (url) {
112
+ document.execCommand("insertHTML", false, `<img src="${url}" alt="Image coll\xE9e" style="max-width:100%;height:auto;border-radius:8px;margin:8px 0;" />`);
113
+ editorRef.current?.focus();
114
+ setTimeout(emitChange, 0);
115
+ }
116
+ }
117
+ return;
118
+ }
119
+ }
120
+ }, [onFileUpload, emitChange]);
121
+ const btn = {
122
+ border: "none",
123
+ background: "transparent",
124
+ cursor: "pointer",
125
+ padding: "8px 10px",
126
+ fontSize: "13px",
127
+ fontWeight: 700,
128
+ color: "#555",
129
+ borderRadius: "5px",
130
+ lineHeight: 1,
131
+ minHeight: "44px",
132
+ minWidth: "44px",
133
+ display: "inline-flex",
134
+ alignItems: "center",
135
+ justifyContent: "center"
136
+ };
137
+ const sep = {
138
+ width: "1px",
139
+ height: "18px",
140
+ background: "#d1d5db",
141
+ margin: "0 4px",
142
+ alignSelf: "center"
143
+ };
144
+ return /* @__PURE__ */ jsxs("div", { style: { border: `3px solid ${focused ? focusBorderColor : borderColor}`, borderRadius: "12px", overflow: "hidden", transition: "border-color 0.15s", background: "#fff" }, children: [
145
+ /* @__PURE__ */ jsx("style", { children: `
146
+ .rte-toolbar button:hover { background: #e5e7eb !important; }
147
+ .rte-editor blockquote { border-left: 4px solid #00E5FF; margin: 8px 0; padding: 8px 16px; background: #f0f9ff; border-radius: 0 6px 6px 0; }
148
+ .rte-editor img { max-width: 100%; height: auto; border-radius: 8px; margin: 8px 0; }
149
+ .rte-editor a { color: #00838f; text-decoration: underline; }
150
+ .rte-editor ul, .rte-editor ol { margin: 8px 0; padding-left: 24px; }
151
+ .rte-editor li { margin: 2px 0; }
152
+ .rte-editor p { margin: 0 0 6px 0; }
153
+ ` }),
154
+ /* @__PURE__ */ jsxs("div", { className: "rte-toolbar", style: { display: "flex", alignItems: "center", gap: "2px", padding: "6px 10px", borderBottom: "2px solid #e5e7eb", backgroundColor: "#f9fafb", flexWrap: "wrap" }, children: [
155
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
156
+ e.preventDefault();
157
+ exec("bold");
158
+ }, title: "Gras (Ctrl+B)", children: /* @__PURE__ */ jsx("strong", { children: "B" }) }),
159
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
160
+ e.preventDefault();
161
+ exec("italic");
162
+ }, title: "Italique (Ctrl+I)", children: /* @__PURE__ */ jsx("em", { style: { fontStyle: "italic" }, children: "I" }) }),
163
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
164
+ e.preventDefault();
165
+ exec("underline");
166
+ }, title: "Soulign\xE9 (Ctrl+U)", children: /* @__PURE__ */ jsx("span", { style: { textDecoration: "underline" }, children: "S" }) }),
167
+ /* @__PURE__ */ jsx("span", { style: sep }),
168
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "16px" }, onMouseDown: (e) => {
169
+ e.preventDefault();
170
+ exec("formatBlock", "blockquote");
171
+ }, title: "Citation", children: "\u201C\u201D" }),
172
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
173
+ e.preventDefault();
174
+ exec("insertUnorderedList");
175
+ }, title: "Liste \xE0 puces", children: "\u2022 Liste" }),
176
+ /* @__PURE__ */ jsx("button", { type: "button", style: { ...btn, fontSize: "12px" }, onMouseDown: (e) => {
177
+ e.preventDefault();
178
+ exec("insertOrderedList");
179
+ }, title: "Liste num\xE9rot\xE9e", children: "1. Liste" }),
180
+ /* @__PURE__ */ jsx("span", { style: sep }),
181
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
182
+ e.preventDefault();
183
+ handleInsertLink();
184
+ }, title: "Ins\xE9rer un lien", children: "\u{1F517}" }),
185
+ /* @__PURE__ */ jsx("button", { type: "button", style: btn, onMouseDown: (e) => {
186
+ e.preventDefault();
187
+ handleImageClick();
188
+ }, title: "Ins\xE9rer une image", children: "\u{1F5BC}\uFE0F" })
189
+ ] }),
190
+ /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
191
+ isEmpty && /* @__PURE__ */ jsx("div", { style: { position: "absolute", top: 0, left: 0, right: 0, padding: "14px 16px", color: "#9ca3af", fontSize: "14px", pointerEvents: "none", userSelect: "none" }, children: placeholder }),
192
+ /* @__PURE__ */ jsx(
193
+ "div",
194
+ {
195
+ ref: editorRef,
196
+ className: "rte-editor",
197
+ contentEditable: true,
198
+ suppressContentEditableWarning: true,
199
+ onInput: emitChange,
200
+ onFocus: () => setFocused(true),
201
+ onBlur: () => {
202
+ setFocused(false);
203
+ emitChange();
204
+ },
205
+ onPaste: handlePaste,
206
+ style: {
207
+ minHeight: `${minHeight}px`,
208
+ padding: "14px 16px",
209
+ fontSize: "14px",
210
+ lineHeight: 1.6,
211
+ color: "#1f2937",
212
+ outline: "none",
213
+ overflowY: "auto"
214
+ }
215
+ }
216
+ )
217
+ ] }),
218
+ onFileUpload && /* @__PURE__ */ jsx(
219
+ "input",
220
+ {
221
+ ref: fileInputRef,
222
+ type: "file",
223
+ accept: "image/*",
224
+ multiple: true,
225
+ onChange: handleFileChange,
226
+ style: { display: "none" }
227
+ }
228
+ )
229
+ ] });
230
+ });
231
+
232
+ export { RichTextEditor };
@@ -129,11 +129,12 @@ function SingleCodeBlock({ lang, code }) {
129
129
  ] })
130
130
  ] });
131
131
  }
132
- function CodeBlockRenderer({ text }) {
132
+ function hasCodeBlocks(text) {
133
+ return /```[\s\S]*?```/.test(text);
134
+ }
135
+ function MessageWithCodeBlocks({ text, style }) {
133
136
  const parts = text.split(/(```[\s\S]*?```)/g);
134
- const hasCodeBlock = parts.some((p) => p.startsWith("```"));
135
- if (!hasCodeBlock) return null;
136
- return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: parts.map((part, i) => {
137
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: parts.map((part, i) => {
137
138
  if (part.startsWith("```")) {
138
139
  const match = part.match(/^```(\w*)\n?([\s\S]*?)```$/);
139
140
  if (match) {
@@ -142,13 +143,29 @@ function CodeBlockRenderer({ text }) {
142
143
  return /* @__PURE__ */ jsxRuntime.jsx(SingleCodeBlock, { lang, code }, i);
143
144
  }
144
145
  }
145
- return null;
146
+ if (!part) return null;
147
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { style: { whiteSpace: "pre-wrap" }, children: part }, i);
146
148
  }) });
147
149
  }
150
+ function htmlToText(html) {
151
+ return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|tr|pre)>/gi, "\n").replace(/<\/?(ul|ol|table|tbody|thead)[^>]*>/gi, "").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\n{3,}/g, "\n\n").trim();
152
+ }
153
+ function MessageWithCodeBlocksHtml({ html, style }) {
154
+ const text = htmlToText(html);
155
+ if (!hasCodeBlocks(text)) return null;
156
+ return /* @__PURE__ */ jsxRuntime.jsx(MessageWithCodeBlocks, { text, style });
157
+ }
158
+ function CodeBlockRenderer({ text }) {
159
+ if (!hasCodeBlocks(text)) return null;
160
+ return /* @__PURE__ */ jsxRuntime.jsx(MessageWithCodeBlocks, { text });
161
+ }
148
162
  function CodeBlockRendererHtml({ html }) {
149
- const stripped = html.replace(/<[^>]+>/g, "");
150
- return /* @__PURE__ */ jsxRuntime.jsx(CodeBlockRenderer, { text: stripped });
163
+ const text = htmlToText(html);
164
+ return /* @__PURE__ */ jsxRuntime.jsx(CodeBlockRenderer, { text });
151
165
  }
152
166
 
153
167
  exports.CodeBlockRenderer = CodeBlockRenderer;
154
168
  exports.CodeBlockRendererHtml = CodeBlockRendererHtml;
169
+ exports.MessageWithCodeBlocks = MessageWithCodeBlocks;
170
+ exports.MessageWithCodeBlocksHtml = MessageWithCodeBlocksHtml;
171
+ exports.hasCodeBlocks = hasCodeBlocks;
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, Fragment, jsxs } from 'react/jsx-runtime';
2
+ import { jsx, jsxs } from 'react/jsx-runtime';
3
3
  import { useState } from 'react';
4
4
 
5
5
  const LANG_CONFIG = {
@@ -128,11 +128,12 @@ function SingleCodeBlock({ lang, code }) {
128
128
  ] })
129
129
  ] });
130
130
  }
131
- function CodeBlockRenderer({ text }) {
131
+ function hasCodeBlocks(text) {
132
+ return /```[\s\S]*?```/.test(text);
133
+ }
134
+ function MessageWithCodeBlocks({ text, style }) {
132
135
  const parts = text.split(/(```[\s\S]*?```)/g);
133
- const hasCodeBlock = parts.some((p) => p.startsWith("```"));
134
- if (!hasCodeBlock) return null;
135
- return /* @__PURE__ */ jsx(Fragment, { children: parts.map((part, i) => {
136
+ return /* @__PURE__ */ jsx("div", { style, children: parts.map((part, i) => {
136
137
  if (part.startsWith("```")) {
137
138
  const match = part.match(/^```(\w*)\n?([\s\S]*?)```$/);
138
139
  if (match) {
@@ -141,12 +142,25 @@ function CodeBlockRenderer({ text }) {
141
142
  return /* @__PURE__ */ jsx(SingleCodeBlock, { lang, code }, i);
142
143
  }
143
144
  }
144
- return null;
145
+ if (!part) return null;
146
+ return /* @__PURE__ */ jsx("span", { style: { whiteSpace: "pre-wrap" }, children: part }, i);
145
147
  }) });
146
148
  }
149
+ function htmlToText(html) {
150
+ return html.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div|li|h[1-6]|tr|pre)>/gi, "\n").replace(/<\/?(ul|ol|table|tbody|thead)[^>]*>/gi, "").replace(/<[^>]+>/g, "").replace(/&nbsp;/g, " ").replace(/&amp;/g, "&").replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&quot;/g, '"').replace(/&#39;/g, "'").replace(/\n{3,}/g, "\n\n").trim();
151
+ }
152
+ function MessageWithCodeBlocksHtml({ html, style }) {
153
+ const text = htmlToText(html);
154
+ if (!hasCodeBlocks(text)) return null;
155
+ return /* @__PURE__ */ jsx(MessageWithCodeBlocks, { text, style });
156
+ }
157
+ function CodeBlockRenderer({ text }) {
158
+ if (!hasCodeBlocks(text)) return null;
159
+ return /* @__PURE__ */ jsx(MessageWithCodeBlocks, { text });
160
+ }
147
161
  function CodeBlockRendererHtml({ html }) {
148
- const stripped = html.replace(/<[^>]+>/g, "");
149
- return /* @__PURE__ */ jsx(CodeBlockRenderer, { text: stripped });
162
+ const text = htmlToText(html);
163
+ return /* @__PURE__ */ jsx(CodeBlockRenderer, { text });
150
164
  }
151
165
 
152
- export { CodeBlockRenderer, CodeBlockRendererHtml };
166
+ export { CodeBlockRenderer, CodeBlockRendererHtml, MessageWithCodeBlocks, MessageWithCodeBlocksHtml, hasCodeBlocks };
package/dist/index.cjs CHANGED
@@ -1454,7 +1454,23 @@ function emailRichContent(html, config) {
1454
1454
  return `<${tag}${cleanAttrs} style="${style}">`;
1455
1455
  });
1456
1456
  }
1457
+ function convertFencedCodeBlocks(input) {
1458
+ if (!input.includes("```")) return input;
1459
+ const normalized = input.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>\s*<(p|div)[^>]*>/gi, "\n").replace(/<(p|div)[^>]*>/gi, "").replace(/<\/(p|div)>/gi, "\n");
1460
+ return normalized.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
1461
+ const cleanCode = escapeHtml((code || "").replace(/\n$/, ""));
1462
+ const label = lang ? lang.trim() : "code";
1463
+ return `<div style="margin: 16px 0; border: 1px solid #1f2937; border-radius: 8px; overflow: hidden; background: #0f172a;">
1464
+ <div style="padding: 6px 14px; background: #1e293b; border-bottom: 1px solid #334155; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">${escapeHtml(label)}</div>
1465
+ <pre style="margin: 0; padding: 14px 16px; overflow-x: auto; background: #0f172a; color: #e2e8f0; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 13px; line-height: 1.6; white-space: pre;"><code style="background: transparent; padding: 0; color: inherit; font-family: inherit; font-size: inherit;">${cleanCode}</code></pre>
1466
+ </div>`;
1467
+ }).split(/(<div style="margin: 16px 0[\s\S]*?<\/div>)/g).map((chunk) => {
1468
+ if (chunk.startsWith('<div style="margin: 16px 0')) return chunk;
1469
+ return chunk.split("\n").map((line) => line.trim() ? `<p>${line}</p>` : "").join("");
1470
+ }).join("");
1471
+ }
1457
1472
  let result = html.replace(/src="\/([^"]+)"/g, `src="${baseUrl}/$1"`);
1473
+ result = convertFencedCodeBlocks(result);
1458
1474
  result = styleTag(result, "blockquote", `border-left: 4px solid ${c.brandColor}; margin: 16px 0; padding: 12px 20px; background: #f0f9fa; border-radius: 0 8px 8px 0;`);
1459
1475
  result = styleTag(result, "img", "max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; display: block;");
1460
1476
  result = styleTag(result, "a", `color: ${c.brandColor}; text-decoration: underline; font-weight: 600;`);
@@ -8074,7 +8090,9 @@ function supportPlugin(config) {
8074
8090
  "support-new-ticket": viewConfig(`${viewsBase}#NewTicketView`, `${bp}/new-ticket`),
8075
8091
  "support-settings": viewConfig(`${viewsBase}#TicketingSettingsView`, `${bp}/settings`),
8076
8092
  "support-logs": viewConfig(`${viewsBase}#LogsView`, `${bp}/logs`),
8077
- "support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`)
8093
+ "support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`),
8094
+ "support-billing": viewConfig(`${viewsBase}#BillingView`, `${bp}/billing`),
8095
+ "support-import": viewConfig(`${viewsBase}#ImportConversationView`, `/import-conversation`)
8078
8096
  };
8079
8097
  if (features.chat) {
8080
8098
  supportViews["support-chat"] = viewConfig(`${viewsBase}#ChatView`, `${bp}/chat`);
package/dist/index.js CHANGED
@@ -1448,7 +1448,23 @@ function emailRichContent(html, config) {
1448
1448
  return `<${tag}${cleanAttrs} style="${style}">`;
1449
1449
  });
1450
1450
  }
1451
+ function convertFencedCodeBlocks(input) {
1452
+ if (!input.includes("```")) return input;
1453
+ const normalized = input.replace(/<br\s*\/?>/gi, "\n").replace(/<\/(p|div)>\s*<(p|div)[^>]*>/gi, "\n").replace(/<(p|div)[^>]*>/gi, "").replace(/<\/(p|div)>/gi, "\n");
1454
+ return normalized.replace(/```(\w*)\n?([\s\S]*?)```/g, (_match, lang, code) => {
1455
+ const cleanCode = escapeHtml((code || "").replace(/\n$/, ""));
1456
+ const label = lang ? lang.trim() : "code";
1457
+ return `<div style="margin: 16px 0; border: 1px solid #1f2937; border-radius: 8px; overflow: hidden; background: #0f172a;">
1458
+ <div style="padding: 6px 14px; background: #1e293b; border-bottom: 1px solid #334155; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 11px; font-weight: 700; color: #94a3b8; text-transform: uppercase; letter-spacing: 0.05em;">${escapeHtml(label)}</div>
1459
+ <pre style="margin: 0; padding: 14px 16px; overflow-x: auto; background: #0f172a; color: #e2e8f0; font-family: 'SF Mono', 'Fira Code', Consolas, monospace; font-size: 13px; line-height: 1.6; white-space: pre;"><code style="background: transparent; padding: 0; color: inherit; font-family: inherit; font-size: inherit;">${cleanCode}</code></pre>
1460
+ </div>`;
1461
+ }).split(/(<div style="margin: 16px 0[\s\S]*?<\/div>)/g).map((chunk) => {
1462
+ if (chunk.startsWith('<div style="margin: 16px 0')) return chunk;
1463
+ return chunk.split("\n").map((line) => line.trim() ? `<p>${line}</p>` : "").join("");
1464
+ }).join("");
1465
+ }
1451
1466
  let result = html.replace(/src="\/([^"]+)"/g, `src="${baseUrl}/$1"`);
1467
+ result = convertFencedCodeBlocks(result);
1452
1468
  result = styleTag(result, "blockquote", `border-left: 4px solid ${c.brandColor}; margin: 16px 0; padding: 12px 20px; background: #f0f9fa; border-radius: 0 8px 8px 0;`);
1453
1469
  result = styleTag(result, "img", "max-width: 100%; height: auto; border-radius: 8px; margin: 12px 0; display: block;");
1454
1470
  result = styleTag(result, "a", `color: ${c.brandColor}; text-decoration: underline; font-weight: 600;`);
@@ -8068,7 +8084,9 @@ function supportPlugin(config) {
8068
8084
  "support-new-ticket": viewConfig(`${viewsBase}#NewTicketView`, `${bp}/new-ticket`),
8069
8085
  "support-settings": viewConfig(`${viewsBase}#TicketingSettingsView`, `${bp}/settings`),
8070
8086
  "support-logs": viewConfig(`${viewsBase}#LogsView`, `${bp}/logs`),
8071
- "support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`)
8087
+ "support-crm": viewConfig(`${viewsBase}#CrmView`, `${bp}/crm`),
8088
+ "support-billing": viewConfig(`${viewsBase}#BillingView`, `${bp}/billing`),
8089
+ "support-import": viewConfig(`${viewsBase}#ImportConversationView`, `/import-conversation`)
8072
8090
  };
8073
8091
  if (features.chat) {
8074
8092
  supportViews["support-chat"] = viewConfig(`${viewsBase}#ChatView`, `${bp}/chat`);