@shane_donnelly/dsi-internal-react-utils 0.2.1 → 1.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/README.md CHANGED
@@ -30,6 +30,25 @@ Wrapper de simplification pour `keycloak-js`. `KeycloakProvider` singleton à la
30
30
 
31
31
  [Documentation complète du module Keycloak](docs/keycloak.md)
32
32
 
33
+ ### Chatbot UI
34
+
35
+ Ensemble de composants React pour créer des interfaces de type chatbot modernes (style ChatGPT / Claude / Gemini). Entièrement indépendant de la logique métier — gère uniquement l'affichage.
36
+
37
+ Composants disponibles :
38
+
39
+ | Composant | Description |
40
+ |-----------|-------------|
41
+ | `MarkdownRenderer` | Rendu markdown avec bouton copier sur les blocs de code |
42
+ | `UserMessage` | Bulle de message utilisateur avec slots typés optionnels |
43
+ | `BotMessage` | Message bot full-width, extensible via registry |
44
+ | `SessionHistory` | Affichage d'une conversation complète |
45
+ | `InputBar` | Barre d'input avec bouton "+", bouton d'action configurable et slots |
46
+ | `SessionContainer` | Composant tout-en-un : SessionHistory + InputBar |
47
+
48
+ Système de thème global via `setTheme("light" | "dark" | ColorPalette)`.
49
+
50
+ [Documentation complète du module Chatbot](docs/chatbot.md)
51
+
33
52
  ---
34
53
 
35
54
  ## Exemple complet avec React Router v7 (framework mode)
@@ -0,0 +1,255 @@
1
+ import { t as e } from "./MarkdownRenderer-vU2aoyaG.js";
2
+ import { useTheme as t } from "./chatbot/theme/useTheme.js";
3
+ import { t as n } from "./iconBase-CDbPVA4E.js";
4
+ import { n as r, t as i } from "./bs-CmZftXMO.js";
5
+ import { useEffect as a, useState as o } from "react";
6
+ import { Fragment as s, jsx as c, jsxs as l } from "react/jsx-runtime";
7
+ //#region node_modules/react-icons/tb/index.mjs
8
+ function u(e) {
9
+ return n({
10
+ tag: "svg",
11
+ attr: {
12
+ viewBox: "0 0 24 24",
13
+ fill: "none",
14
+ stroke: "currentColor",
15
+ strokeWidth: "2",
16
+ strokeLinecap: "round",
17
+ strokeLinejoin: "round"
18
+ },
19
+ child: [
20
+ {
21
+ tag: "path",
22
+ attr: { d: "M3 12a9 9 0 1 0 18 0a9 9 0 0 0 -18 0" },
23
+ child: []
24
+ },
25
+ {
26
+ tag: "path",
27
+ attr: { d: "M3.6 9h16.8" },
28
+ child: []
29
+ },
30
+ {
31
+ tag: "path",
32
+ attr: { d: "M3.6 15h16.8" },
33
+ child: []
34
+ },
35
+ {
36
+ tag: "path",
37
+ attr: { d: "M11.5 3a17 17 0 0 0 0 18" },
38
+ child: []
39
+ },
40
+ {
41
+ tag: "path",
42
+ attr: { d: "M12.5 3a17 17 0 0 1 0 18" },
43
+ child: []
44
+ }
45
+ ]
46
+ })(e);
47
+ }
48
+ function d(e) {
49
+ return n({
50
+ tag: "svg",
51
+ attr: {
52
+ viewBox: "0 0 24 24",
53
+ fill: "none",
54
+ stroke: "currentColor",
55
+ strokeWidth: "2",
56
+ strokeLinecap: "round",
57
+ strokeLinejoin: "round"
58
+ },
59
+ child: [{
60
+ tag: "path",
61
+ attr: { d: "M6 9l6 6l6 -6" },
62
+ child: []
63
+ }]
64
+ })(e);
65
+ }
66
+ //#endregion
67
+ //#region lib/chatbot/BotMessage/styles.module.css
68
+ var f = {
69
+ "dsi-bot-msg": "_dsi-bot-msg_9iqvz_2",
70
+ "dsi-bot-loading-dot": "_dsi-bot-loading-dot_9iqvz_15",
71
+ "dsi-bot-pulse": "_dsi-bot-pulse_9iqvz_1",
72
+ "dsi-bot-msg-footer": "_dsi-bot-msg-footer_9iqvz_28",
73
+ "dsi-bot-copy-btn": "_dsi-bot-copy-btn_9iqvz_39",
74
+ "dsi-bot-msg-time": "_dsi-bot-msg-time_9iqvz_62",
75
+ "dsi-bot-reasoning": "_dsi-bot-reasoning_9iqvz_75",
76
+ "dsi-bot-tool": "_dsi-bot-tool_9iqvz_76",
77
+ "dsi-bot-search": "_dsi-bot-search_9iqvz_77",
78
+ "dsi-bot-collapsible-header": "_dsi-bot-collapsible-header_9iqvz_84",
79
+ "dsi-bot-collapsible-header-inner": "_dsi-bot-collapsible-header-inner_9iqvz_99",
80
+ "dsi-bot-element-title": "_dsi-bot-element-title_9iqvz_108",
81
+ "dsi-bot-collapse-chevron": "_dsi-bot-collapse-chevron_9iqvz_115",
82
+ "dsi-bot-collapsible-body": "_dsi-bot-collapsible-body_9iqvz_126",
83
+ "dsi-bot-collapsible-content": "_dsi-bot-collapsible-content_9iqvz_132",
84
+ "dsi-bot-search-icon": "_dsi-bot-search-icon_9iqvz_140",
85
+ "dsi-bot-spinner": "_dsi-bot-spinner_9iqvz_146",
86
+ "dsi-spin": "_dsi-spin_9iqvz_1"
87
+ };
88
+ //#endregion
89
+ //#region lib/chatbot/BotMessage/index.tsx
90
+ function p({ markdownText: t }) {
91
+ return t ? /* @__PURE__ */ c(e, { text: t }) : null;
92
+ }
93
+ function m() {
94
+ return /* @__PURE__ */ c("svg", {
95
+ className: f["dsi-bot-loading-dot"],
96
+ width: "10",
97
+ height: "10",
98
+ viewBox: "0 0 10 10",
99
+ "aria-hidden": "true",
100
+ children: /* @__PURE__ */ c("circle", {
101
+ cx: "5",
102
+ cy: "5",
103
+ r: "4",
104
+ fill: "currentColor"
105
+ })
106
+ });
107
+ }
108
+ function h({ text: e }) {
109
+ let [t, n] = o(!1);
110
+ return /* @__PURE__ */ c("button", {
111
+ className: f["dsi-bot-copy-btn"],
112
+ onClick: () => {
113
+ navigator.clipboard.writeText(e).then(() => {
114
+ n(!0), setTimeout(() => n(!1), 2e3);
115
+ });
116
+ },
117
+ "aria-label": "Copier le message",
118
+ type: "button",
119
+ children: c(t ? i : r, {})
120
+ });
121
+ }
122
+ function g({ defaultOpen: e, closeWhen: t = !1, header: n, content: r, className: i, style: s }) {
123
+ let [u, p] = o(e);
124
+ return a(() => {
125
+ t && p(!1);
126
+ }, [t]), /* @__PURE__ */ l("div", {
127
+ className: i,
128
+ style: s,
129
+ children: [/* @__PURE__ */ l("div", {
130
+ className: f["dsi-bot-collapsible-header"],
131
+ onClick: () => p((e) => !e),
132
+ role: "button",
133
+ tabIndex: 0,
134
+ onKeyDown: (e) => {
135
+ (e.key === "Enter" || e.key === " ") && p((e) => !e);
136
+ },
137
+ "aria-expanded": u,
138
+ children: [/* @__PURE__ */ c("div", {
139
+ className: f["dsi-bot-collapsible-header-inner"],
140
+ children: n
141
+ }), /* @__PURE__ */ c(d, {
142
+ className: f["dsi-bot-collapse-chevron"],
143
+ style: { transform: u ? "rotate(0deg)" : "rotate(-90deg)" },
144
+ "aria-hidden": "true"
145
+ })]
146
+ }), u && r && /* @__PURE__ */ c("div", {
147
+ className: f["dsi-bot-collapsible-body"],
148
+ children: r
149
+ })]
150
+ });
151
+ }
152
+ function _({ title: e, content: n, isLoading: r = !1, _isLastElement: i = !1 }) {
153
+ let a = t();
154
+ return /* @__PURE__ */ c(g, {
155
+ defaultOpen: r || i,
156
+ closeWhen: !r && !i,
157
+ className: f["dsi-bot-reasoning"],
158
+ style: {
159
+ "--dsi-reasoning-border": a.reasoningBorder,
160
+ "--dsi-reasoning-text": a.reasoningText
161
+ },
162
+ header: /* @__PURE__ */ c("span", {
163
+ className: f["dsi-bot-element-title"],
164
+ children: e || "Raisonnement"
165
+ }),
166
+ content: n ? /* @__PURE__ */ c("p", {
167
+ className: f["dsi-bot-collapsible-content"],
168
+ children: n
169
+ }) : null
170
+ });
171
+ }
172
+ function v({ title: e, content: n, isLoading: r = !1, _isLastElement: i = !1 }) {
173
+ let a = t();
174
+ return /* @__PURE__ */ c(g, {
175
+ defaultOpen: r || i,
176
+ closeWhen: !r && !i,
177
+ className: f["dsi-bot-tool"],
178
+ style: {
179
+ "--dsi-tool-border": a.toolBorder,
180
+ "--dsi-text-secondary": a.textSecondary,
181
+ "--dsi-text-primary": a.textPrimary
182
+ },
183
+ header: /* @__PURE__ */ l(s, { children: [r && /* @__PURE__ */ c("span", { className: f["dsi-bot-spinner"] }), /* @__PURE__ */ c("span", {
184
+ className: f["dsi-bot-element-title"],
185
+ children: e || "Outil"
186
+ })] }),
187
+ content: n ? /* @__PURE__ */ c("p", {
188
+ className: f["dsi-bot-collapsible-content"],
189
+ children: n
190
+ }) : null
191
+ });
192
+ }
193
+ function y({ title: e, content: n, isLoading: r = !1, _isLastElement: i = !1 }) {
194
+ let a = t();
195
+ return /* @__PURE__ */ c(g, {
196
+ defaultOpen: r || i,
197
+ closeWhen: !r && !i,
198
+ className: f["dsi-bot-search"],
199
+ style: {
200
+ "--dsi-tool-border": a.toolBorder,
201
+ "--dsi-text-secondary": a.textSecondary,
202
+ "--dsi-text-primary": a.textPrimary
203
+ },
204
+ header: /* @__PURE__ */ l(s, { children: [
205
+ /* @__PURE__ */ c(u, { className: f["dsi-bot-search-icon"] }),
206
+ r && /* @__PURE__ */ c("span", { className: f["dsi-bot-spinner"] }),
207
+ /* @__PURE__ */ c("span", {
208
+ className: f["dsi-bot-element-title"],
209
+ children: e || "Recherche"
210
+ })
211
+ ] }),
212
+ content: n ? /* @__PURE__ */ c("p", {
213
+ className: f["dsi-bot-collapsible-content"],
214
+ children: n
215
+ }) : null
216
+ });
217
+ }
218
+ var b = {
219
+ text: p,
220
+ reasoning: _,
221
+ tool: v,
222
+ search: y
223
+ };
224
+ function x({ message: e, registry: n = b }) {
225
+ let r = t(), i = [...e.elements].sort((e, t) => e.index - t.index), a = [...i].reverse().find((e) => e.type === "text")?.markdownText ?? "";
226
+ return /* @__PURE__ */ l("div", {
227
+ className: f["dsi-bot-msg"],
228
+ style: {
229
+ "--dsi-text-primary": r.textPrimary,
230
+ "--dsi-text-secondary": r.textSecondary,
231
+ "--dsi-border": r.border
232
+ },
233
+ children: [
234
+ e.isLoading && /* @__PURE__ */ c(m, {}),
235
+ i.map((e, t) => {
236
+ let r = n[e.type];
237
+ if (!r) return null;
238
+ let a = t === i.length - 1;
239
+ return /* @__PURE__ */ c(r, {
240
+ ...e,
241
+ _isLastElement: a
242
+ }, `${e.type}-${e.index}-${t}`);
243
+ }),
244
+ /* @__PURE__ */ l("div", {
245
+ className: f["dsi-bot-msg-footer"],
246
+ children: [!e.isLoading && /* @__PURE__ */ c(h, { text: a }), !e.isLoading && e.timeOrDateToDisplay && /* @__PURE__ */ c("span", {
247
+ className: f["dsi-bot-msg-time"],
248
+ children: e.timeOrDateToDisplay
249
+ })]
250
+ })
251
+ ]
252
+ });
253
+ }
254
+ //#endregion
255
+ export { b as n, x as t };
@@ -0,0 +1,201 @@
1
+ import { useTheme as e } from "./chatbot/theme/useTheme.js";
2
+ import { t } from "./iconBase-CDbPVA4E.js";
3
+ import { useEffect as n, useRef as r, useState as i } from "react";
4
+ import { jsx as a, jsxs as o } from "react/jsx-runtime";
5
+ //#region node_modules/react-icons/fi/index.mjs
6
+ function s(e) {
7
+ return t({
8
+ tag: "svg",
9
+ attr: {
10
+ viewBox: "0 0 24 24",
11
+ fill: "none",
12
+ stroke: "currentColor",
13
+ strokeWidth: "2",
14
+ strokeLinecap: "round",
15
+ strokeLinejoin: "round"
16
+ },
17
+ child: [{
18
+ tag: "line",
19
+ attr: {
20
+ x1: "22",
21
+ y1: "2",
22
+ x2: "11",
23
+ y2: "13"
24
+ },
25
+ child: []
26
+ }, {
27
+ tag: "polygon",
28
+ attr: { points: "22 2 15 22 11 13 2 9 22 2" },
29
+ child: []
30
+ }]
31
+ })(e);
32
+ }
33
+ function c(e) {
34
+ return t({
35
+ tag: "svg",
36
+ attr: {
37
+ viewBox: "0 0 24 24",
38
+ fill: "none",
39
+ stroke: "currentColor",
40
+ strokeWidth: "2",
41
+ strokeLinecap: "round",
42
+ strokeLinejoin: "round"
43
+ },
44
+ child: [{
45
+ tag: "line",
46
+ attr: {
47
+ x1: "12",
48
+ y1: "5",
49
+ x2: "12",
50
+ y2: "19"
51
+ },
52
+ child: []
53
+ }, {
54
+ tag: "line",
55
+ attr: {
56
+ x1: "5",
57
+ y1: "12",
58
+ x2: "19",
59
+ y2: "12"
60
+ },
61
+ child: []
62
+ }]
63
+ })(e);
64
+ }
65
+ //#endregion
66
+ //#region node_modules/react-icons/fa/index.mjs
67
+ function l(e) {
68
+ return t({
69
+ tag: "svg",
70
+ attr: { viewBox: "0 0 448 512" },
71
+ child: [{
72
+ tag: "path",
73
+ attr: { d: "M400 32H48C21.5 32 0 53.5 0 80v352c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48V80c0-26.5-21.5-48-48-48z" },
74
+ child: []
75
+ }]
76
+ })(e);
77
+ }
78
+ //#endregion
79
+ //#region node_modules/react-icons/ri/index.mjs
80
+ function u(e) {
81
+ return t({
82
+ tag: "svg",
83
+ attr: {
84
+ viewBox: "0 0 24 24",
85
+ fill: "currentColor"
86
+ },
87
+ child: [{
88
+ tag: "path",
89
+ attr: { d: "M5 7H7V17H5V7ZM1 10H3V14H1V10ZM9 2H11V20H9V2ZM13 4H15V22H13V4ZM17 7H19V17H17V7ZM21 10H23V14H21V10Z" },
90
+ child: []
91
+ }]
92
+ })(e);
93
+ }
94
+ //#endregion
95
+ //#region lib/chatbot/InputBar/styles.module.css
96
+ var d = {
97
+ "dsi-inputbar": "_dsi-inputbar_1ngbl_2",
98
+ "dsi-inputbar-upper-slot": "_dsi-inputbar-upper-slot_1ngbl_12",
99
+ "dsi-inputbar-plus-slot": "_dsi-inputbar-plus-slot_1ngbl_23",
100
+ "dsi-inputbar-row": "_dsi-inputbar-row_1ngbl_38",
101
+ "dsi-inputbar-row--with-upper": "_dsi-inputbar-row--with-upper_1ngbl_55",
102
+ "dsi-inputbar-textarea": "_dsi-inputbar-textarea_1ngbl_60",
103
+ "dsi-inputbar-plus-btn": "_dsi-inputbar-plus-btn_1ngbl_81",
104
+ "dsi-inputbar-plus-btn--open": "_dsi-inputbar-plus-btn--open_1ngbl_100",
105
+ "dsi-inputbar-action-btn": "_dsi-inputbar-action-btn_1ngbl_105"
106
+ };
107
+ //#endregion
108
+ //#region lib/chatbot/InputBar/index.tsx
109
+ function f({ type: e }) {
110
+ switch (e) {
111
+ case "send": return /* @__PURE__ */ a(s, {});
112
+ case "stop": return /* @__PURE__ */ a(l, {});
113
+ case "vocal": return /* @__PURE__ */ a(u, {});
114
+ default: return null;
115
+ }
116
+ }
117
+ function p({ value: t, onChange: s, placeholder: l = "Envoyer un message…", actionButtonType: u = "send", actionButtonEnabled: p = !0, onActionButtonClick: m, plusSlot: h, upperSlot: g, messageHistory: _ }) {
118
+ let v = e(), [y, b] = i(!1), x = r(null), S = r(null), C = r(null), w = r(-1);
119
+ n(() => {
120
+ let e = x.current;
121
+ e && (e.style.height = "auto", e.style.height = `${e.scrollHeight}px`);
122
+ }, [t]), n(() => {
123
+ if (!y) return;
124
+ let e = (e) => {
125
+ !S.current?.contains(e.target) && !C.current?.contains(e.target) && b(!1);
126
+ };
127
+ return document.addEventListener("mousedown", e), () => document.removeEventListener("mousedown", e);
128
+ }, [y]);
129
+ let T = {
130
+ "--dsi-border": v.border,
131
+ "--dsi-input-bg": v.inputBg,
132
+ "--dsi-accent": v.accent,
133
+ "--dsi-text-primary": v.textPrimary,
134
+ "--dsi-text-secondary": v.textSecondary,
135
+ "--dsi-button-bg": v.buttonBg,
136
+ "--dsi-button-hover-bg": v.buttonHoverBg,
137
+ "--dsi-action-btn-bg": v.actionButtonBg,
138
+ "--dsi-action-btn-hover-bg": v.actionButtonHoverBg,
139
+ "--dsi-action-btn-icon": v.actionButtonIcon
140
+ };
141
+ return /* @__PURE__ */ o("div", {
142
+ className: d["dsi-inputbar"],
143
+ style: T,
144
+ children: [
145
+ y && h && /* @__PURE__ */ a("div", {
146
+ className: d["dsi-inputbar-plus-slot"],
147
+ ref: S,
148
+ children: h
149
+ }),
150
+ g && /* @__PURE__ */ a("div", {
151
+ className: d["dsi-inputbar-upper-slot"],
152
+ children: g
153
+ }),
154
+ /* @__PURE__ */ o("div", {
155
+ className: `${d["dsi-inputbar-row"]} ${g ? d["dsi-inputbar-row--with-upper"] : ""}`,
156
+ children: [
157
+ /* @__PURE__ */ a("button", {
158
+ ref: C,
159
+ className: `${d["dsi-inputbar-plus-btn"]} ${y ? d["dsi-inputbar-plus-btn--open"] : ""}`,
160
+ onClick: () => b((e) => !e),
161
+ "aria-label": "Options",
162
+ type: "button",
163
+ children: /* @__PURE__ */ a(c, {})
164
+ }),
165
+ /* @__PURE__ */ a("textarea", {
166
+ ref: x,
167
+ className: d["dsi-inputbar-textarea"],
168
+ value: t,
169
+ onChange: (e) => {
170
+ w.current = -1, s(e.target.value);
171
+ },
172
+ placeholder: l,
173
+ rows: 1,
174
+ onKeyDown: (e) => {
175
+ let n = _ ?? [];
176
+ if (e.key === "ArrowUp" && !e.shiftKey) {
177
+ t === "" && w.current === -1 && n.length > 0 ? (w.current = n.length - 1, s(n[w.current]), e.preventDefault()) : w.current > 0 ? (w.current--, s(n[w.current]), e.preventDefault()) : w.current === 0 && e.preventDefault();
178
+ return;
179
+ }
180
+ if (e.key === "ArrowDown" && w.current !== -1) {
181
+ w.current < n.length - 1 ? (w.current++, s(n[w.current])) : (w.current = -1, s("")), e.preventDefault();
182
+ return;
183
+ }
184
+ e.key === "Enter" && !e.shiftKey && (e.preventDefault(), w.current = -1, p && u !== "none" && m?.(u));
185
+ }
186
+ }),
187
+ u !== "none" && /* @__PURE__ */ a("button", {
188
+ className: d["dsi-inputbar-action-btn"],
189
+ disabled: !p,
190
+ onClick: () => m?.(u),
191
+ "aria-label": u,
192
+ type: "button",
193
+ children: /* @__PURE__ */ a(f, { type: u })
194
+ })
195
+ ]
196
+ })
197
+ ]
198
+ });
199
+ }
200
+ //#endregion
201
+ export { p as t };