@kieksme/csp-plugin-chat 0.13.1 → 0.13.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.
package/README.md CHANGED
@@ -8,7 +8,7 @@ For local provider testing, set `CSP_DEMO=true` and `CSP_CHAT_DEMO=false`: porta
8
8
 
9
9
  Langsame lokale Modelle können mit der Runtime-Variable `CSP_CHAT_TIMEOUT_MS` mehr Antwortzeit erhalten (Standard `60000`, Bereich `1000`–`300000` ms).
10
10
 
11
- Send a message with Cmd + Enter on macOS, Ctrl + Enter on other systems, or the send button. The hint displays the system-specific shortcut. Plain Enter inserts a line break. A subtle shortcut hint appears below the text field; blank messages and shortcuts during an active reply do not send.
11
+ Send a message with Cmd + Enter on macOS, Ctrl + Enter on other systems, or the send button. The hint displays the system-specific shortcut. Plain Enter inserts a line break. A subtle shortcut hint appears inside the text field at the bottom right; blank messages and shortcuts during an active reply do not send.
12
12
 
13
13
  The input starts with one text row and automatically grows with wrapped text or line breaks. It shrinks when text is removed or sent and adjusts when its width changes.
14
14
 
@@ -34,4 +34,4 @@ Long histories follow new messages while the visitor reads at the bottom; scroll
34
34
 
35
35
  Both chat modes share a service-chat header and composer. Visitor messages align to the right, assistant and human replies to the left with visible sender labels. Customer theme colours, dark mode and mobile layouts apply throughout. See [chat design](../../docs/chat.md#gestaltung).
36
36
 
37
- Dictation uses an icon-only microphone button with the accessible name and tooltip “Nachricht diktieren”. Its privacy notice stays visible alongside the button, and mobile touch targets remain 44 × 44 px.
37
+ Dictation uses an icon-only microphone button with the accessible name and tooltip “Nachricht diktieren”. The microphone sits directly beside Send in the composer toolbar. All composer buttons stay in one row and share a 44 px height. Stop and cancel use accessible icons during recording. The shortcut sits inside the field at the bottom right, with space reserved below editable text. Its privacy notice stays visible underneath.
package/dist/browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cspPlugin
3
- } from "./chunk-B7EKNOQK.js";
3
+ } from "./chunk-VM2Y3GL7.js";
4
4
 
5
5
  // src/tools.ts
6
6
  import {
@@ -404,7 +404,8 @@ function VoiceInput({
404
404
  setValue,
405
405
  disabled,
406
406
  token,
407
- onBusy
407
+ onBusy,
408
+ children
408
409
  }) {
409
410
  const [enabled, setEnabled] = useState2(false), [state, setState] = useState2("idle"), [error, setError] = useState2("");
410
411
  const recorder = useRef2(null), stream = useRef2(null), timer = useRef2(null), request = useRef2(null);
@@ -573,74 +574,101 @@ function VoiceInput({
573
574
  }
574
575
  }
575
576
  }
576
- if (!enabled) return null;
577
- return /* @__PURE__ */ jsxs2(
577
+ if (!enabled) return children(null, null);
578
+ const controls = state === "idle" ? /* @__PURE__ */ jsx2(
579
+ "button",
580
+ {
581
+ type: "button",
582
+ className: "chat-voice-button chat-microphone",
583
+ "aria-label": "Nachricht diktieren",
584
+ title: "Nachricht diktieren",
585
+ disabled,
586
+ onClick: () => void start(),
587
+ children: /* @__PURE__ */ jsxs2(
588
+ "svg",
589
+ {
590
+ "aria-hidden": "true",
591
+ viewBox: "0 0 24 24",
592
+ width: "16",
593
+ height: "16",
594
+ fill: "none",
595
+ stroke: "currentColor",
596
+ strokeWidth: "2",
597
+ children: [
598
+ /* @__PURE__ */ jsx2("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }),
599
+ /* @__PURE__ */ jsx2("path", { d: "M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8" })
600
+ ]
601
+ }
602
+ )
603
+ }
604
+ ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
605
+ state === "recording" && /* @__PURE__ */ jsx2(
606
+ "button",
607
+ {
608
+ type: "button",
609
+ className: "chat-voice-button",
610
+ "aria-label": "Aufnahme stoppen",
611
+ title: "Aufnahme stoppen",
612
+ onClick: () => recorder.current?.stop(),
613
+ children: /* @__PURE__ */ jsx2(
614
+ "svg",
615
+ {
616
+ "aria-hidden": "true",
617
+ viewBox: "0 0 24 24",
618
+ width: "16",
619
+ height: "16",
620
+ fill: "currentColor",
621
+ children: /* @__PURE__ */ jsx2("rect", { x: "5", y: "5", width: "14", height: "14", rx: "2" })
622
+ }
623
+ )
624
+ }
625
+ ),
626
+ /* @__PURE__ */ jsx2(
627
+ "button",
628
+ {
629
+ type: "button",
630
+ className: "chat-voice-cancel",
631
+ "aria-label": "Abbrechen",
632
+ title: "Abbrechen",
633
+ onClick: () => {
634
+ operation.current++;
635
+ cancelled.current = true;
636
+ request.current?.abort();
637
+ if (recorder.current?.state === "recording")
638
+ recorder.current.stop();
639
+ release();
640
+ idle();
641
+ },
642
+ children: /* @__PURE__ */ jsx2(
643
+ "svg",
644
+ {
645
+ "aria-hidden": "true",
646
+ viewBox: "0 0 24 24",
647
+ width: "16",
648
+ height: "16",
649
+ fill: "none",
650
+ stroke: "currentColor",
651
+ strokeWidth: "2",
652
+ children: /* @__PURE__ */ jsx2("path", { d: "m6 6 12 12M18 6 6 18" })
653
+ }
654
+ )
655
+ }
656
+ )
657
+ ] });
658
+ const details = /* @__PURE__ */ jsxs2(
578
659
  "div",
579
660
  {
580
661
  className: "chat-voice",
581
662
  "data-state": state,
582
663
  "data-recording": state === "recording",
583
664
  children: [
584
- state === "idle" ? /* @__PURE__ */ jsx2(
585
- "button",
586
- {
587
- type: "button",
588
- className: "chat-voice-button chat-microphone",
589
- "aria-label": "Nachricht diktieren",
590
- title: "Nachricht diktieren",
591
- disabled,
592
- onClick: () => void start(),
593
- children: /* @__PURE__ */ jsxs2(
594
- "svg",
595
- {
596
- "aria-hidden": "true",
597
- viewBox: "0 0 24 24",
598
- width: "16",
599
- height: "16",
600
- fill: "none",
601
- stroke: "currentColor",
602
- strokeWidth: "2",
603
- children: [
604
- /* @__PURE__ */ jsx2("rect", { x: "9", y: "2", width: "6", height: "12", rx: "3" }),
605
- /* @__PURE__ */ jsx2("path", { d: "M5 10v2a7 7 0 0 0 14 0v-2M12 19v3M8 22h8" })
606
- ]
607
- }
608
- )
609
- }
610
- ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
611
- /* @__PURE__ */ jsx2("span", { role: "status", className: "chat-voice-status", children: state === "recording" ? "\u25CF Aufnahme l\xE4uft \xB7 maximal 60 Sekunden" : state === "starting" ? "Mikrofon wird gestartet \u2026" : "Sprache wird in Text umgewandelt \u2026" }),
612
- state === "recording" && /* @__PURE__ */ jsx2(
613
- "button",
614
- {
615
- type: "button",
616
- className: "chat-voice-button",
617
- onClick: () => recorder.current?.stop(),
618
- children: "Aufnahme stoppen"
619
- }
620
- ),
621
- /* @__PURE__ */ jsx2(
622
- "button",
623
- {
624
- type: "button",
625
- className: "chat-voice-cancel",
626
- onClick: () => {
627
- operation.current++;
628
- cancelled.current = true;
629
- request.current?.abort();
630
- if (recorder.current?.state === "recording")
631
- recorder.current.stop();
632
- release();
633
- idle();
634
- },
635
- children: "Abbrechen"
636
- }
637
- )
638
- ] }),
665
+ state !== "idle" && /* @__PURE__ */ jsx2("span", { role: "status", className: "chat-voice-status", children: state === "recording" ? "\u25CF Aufnahme l\xE4uft \xB7 maximal 60 Sekunden" : state === "starting" ? "Mikrofon wird gestartet \u2026" : "Sprache wird in Text umgewandelt \u2026" }),
639
666
  /* @__PURE__ */ jsx2("p", { className: "chat-voice-notice", children: "Die Aufnahme wird zur Texterkennung an OpenAI gesendet. Pr\xFCfen Sie den Text vor dem Senden." }),
640
667
  error && /* @__PURE__ */ jsx2("p", { role: "alert", className: "text-danger dark:text-warning mt-2", children: error })
641
668
  ]
642
669
  }
643
670
  );
671
+ return children(controls, details);
644
672
  }
645
673
 
646
674
  // src/support-chat.tsx
@@ -926,48 +954,61 @@ function SupportChat(ctx) {
926
954
  children: anonymous ? "Chat neu verbinden" : ctx.config.chatSupport?.auth === "session" ? "Portal-Anmeldung erneuern" : "Mit Microsoft anmelden"
927
955
  }
928
956
  ),
929
- ready && /* @__PURE__ */ jsxs3("div", { className: "chat-composer", children: [
930
- /* @__PURE__ */ jsxs3("form", { onSubmit: submit, className: "chat-form flex gap-2.5 items-end", children: [
931
- /* @__PURE__ */ jsx3("label", { htmlFor: "support-chat-input", className: "sr-only", children: "Ihre Nachricht" }),
932
- /* @__PURE__ */ jsx3(
933
- "textarea",
934
- {
935
- id: "support-chat-input",
936
- value: input,
937
- rows: 2,
938
- maxLength: 4e3,
939
- disabled: current?.status === "closed",
940
- className: "flex-1 min-w-0 max-h-[180px] overflow-auto resize-none border border-line rounded-card bg-card p-3",
941
- onChange: (e) => setInput(e.target.value),
942
- onKeyDown: (e) => sendShortcut(e, blocked || !input.trim()),
943
- "aria-keyshortcuts": "Control+Enter Meta+Enter",
944
- "aria-describedby": "support-chat-shortcut",
945
- placeholder: "Wie k\xF6nnen wir Ihnen helfen?"
946
- }
947
- ),
948
- /* @__PURE__ */ jsx3(
949
- "button",
950
- {
951
- type: "submit",
952
- className: "chat-send",
953
- disabled: blocked || !input.trim(),
954
- children: "Senden"
955
- }
956
- )
957
- ] }),
958
- /* @__PURE__ */ jsx3(Shortcut, { id: "support-chat-shortcut" }),
959
- /* @__PURE__ */ jsx3(
960
- VoiceInput,
961
- {
962
- ctx,
963
- value: input,
964
- setValue: setInput,
965
- disabled: current?.status === "closed",
966
- token: auth?.token,
967
- onBusy: setVoiceBusy
968
- }
969
- )
970
- ] }),
957
+ ready && /* @__PURE__ */ jsx3("div", { className: "chat-composer", children: /* @__PURE__ */ jsx3(
958
+ VoiceInput,
959
+ {
960
+ ctx,
961
+ value: input,
962
+ setValue: setInput,
963
+ disabled: current?.status === "closed",
964
+ token: auth?.token,
965
+ onBusy: setVoiceBusy,
966
+ children: (controls, details) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
967
+ /* @__PURE__ */ jsxs3(
968
+ "form",
969
+ {
970
+ onSubmit: submit,
971
+ className: "chat-form flex gap-2.5 items-end",
972
+ children: [
973
+ /* @__PURE__ */ jsx3("label", { htmlFor: "support-chat-input", className: "sr-only", children: "Ihre Nachricht" }),
974
+ /* @__PURE__ */ jsxs3("div", { className: "chat-compose-field", children: [
975
+ /* @__PURE__ */ jsx3(
976
+ "textarea",
977
+ {
978
+ id: "support-chat-input",
979
+ value: input,
980
+ rows: 1,
981
+ maxLength: 4e3,
982
+ disabled: current?.status === "closed",
983
+ className: "flex-1 min-w-0 max-h-[180px] overflow-auto resize-none border border-line rounded-card bg-card p-3",
984
+ onChange: (e) => setInput(e.target.value),
985
+ onKeyDown: (e) => sendShortcut(e, blocked || !input.trim()),
986
+ "aria-keyshortcuts": "Control+Enter Meta+Enter",
987
+ "aria-describedby": "support-chat-shortcut",
988
+ placeholder: "Wie k\xF6nnen wir Ihnen helfen?"
989
+ }
990
+ ),
991
+ /* @__PURE__ */ jsx3(Shortcut, { id: "support-chat-shortcut" })
992
+ ] }),
993
+ /* @__PURE__ */ jsx3("div", { className: "chat-compose-toolbar", children: /* @__PURE__ */ jsxs3("div", { className: "chat-compose-actions", children: [
994
+ controls,
995
+ /* @__PURE__ */ jsx3(
996
+ "button",
997
+ {
998
+ type: "submit",
999
+ className: "chat-send",
1000
+ disabled: blocked || !input.trim(),
1001
+ children: "Senden"
1002
+ }
1003
+ )
1004
+ ] }) })
1005
+ ]
1006
+ }
1007
+ ),
1008
+ details
1009
+ ] })
1010
+ }
1011
+ ) }),
971
1012
  ready && (!current || current.status === "bot") && /* @__PURE__ */ jsxs3(
972
1013
  "button",
973
1014
  {
@@ -1062,7 +1103,7 @@ function SupportChat(ctx) {
1062
1103
  }
1063
1104
 
1064
1105
  // src/browser.tsx
1065
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1106
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1066
1107
  function resizeInput(field) {
1067
1108
  if (!field) return;
1068
1109
  field.style.height = "auto";
@@ -1246,56 +1287,67 @@ function LegacyChat(ctx) {
1246
1287
  }
1247
1288
  ),
1248
1289
  /* @__PURE__ */ jsx4(NewMessages, { visible: scroll.unread, follow: scroll.follow }),
1249
- /* @__PURE__ */ jsxs4("div", { className: "chat-composer", children: [
1250
- /* @__PURE__ */ jsxs4("form", { className: "chat-form flex gap-2.5 items-end", onSubmit: submit, children: [
1251
- /* @__PURE__ */ jsx4("label", { className: "sr-only", htmlFor: "chat-input", children: "Ihre Frage" }),
1252
- /* @__PURE__ */ jsxs4("div", { className: "relative flex-1 min-w-0", children: [
1253
- /* @__PURE__ */ jsx4(
1254
- "textarea",
1290
+ /* @__PURE__ */ jsx4("div", { className: "chat-composer", children: /* @__PURE__ */ jsx4(
1291
+ VoiceInput,
1292
+ {
1293
+ ctx,
1294
+ value: input,
1295
+ setValue: setInput,
1296
+ disabled: resolved,
1297
+ onBusy: setVoiceBusy,
1298
+ children: (controls, details) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1299
+ /* @__PURE__ */ jsxs4(
1300
+ "form",
1255
1301
  {
1256
- id: "chat-input",
1257
- ref: inputField,
1258
- rows: 1,
1259
- autoFocus: true,
1260
- className: "search bg-card border border-line rounded-card pt-3.5 pb-7 px-4.5 text-ink w-full block max-h-[180px] overflow-auto",
1261
- value: input,
1262
- disabled: resolved,
1263
- maxLength: 4e3,
1264
- onChange: (e) => setInput(e.target.value),
1265
- onKeyDown: (e) => sendShortcut(
1266
- e,
1267
- busy || voiceBusy || resolved || !input.trim()
1268
- ),
1269
- "aria-keyshortcuts": "Control+Enter Meta+Enter",
1270
- "aria-describedby": "chat-shortcut",
1271
- placeholder: "Ihre Frage an unser Team \u2026",
1272
- required: true
1302
+ className: "chat-form flex gap-2.5 items-end",
1303
+ onSubmit: submit,
1304
+ children: [
1305
+ /* @__PURE__ */ jsx4("label", { className: "sr-only", htmlFor: "chat-input", children: "Ihre Frage" }),
1306
+ /* @__PURE__ */ jsxs4("div", { className: "chat-compose-field", children: [
1307
+ /* @__PURE__ */ jsx4(
1308
+ "textarea",
1309
+ {
1310
+ id: "chat-input",
1311
+ ref: inputField,
1312
+ rows: 1,
1313
+ autoFocus: true,
1314
+ className: "search bg-card border border-line rounded-card pt-3.5 pb-7 px-4.5 text-ink w-full block max-h-[180px] overflow-auto",
1315
+ value: input,
1316
+ disabled: resolved,
1317
+ maxLength: 4e3,
1318
+ onChange: (e) => setInput(e.target.value),
1319
+ onKeyDown: (e) => sendShortcut(
1320
+ e,
1321
+ busy || voiceBusy || resolved || !input.trim()
1322
+ ),
1323
+ "aria-keyshortcuts": "Control+Enter Meta+Enter",
1324
+ "aria-describedby": "chat-shortcut",
1325
+ placeholder: "Ihre Frage an unser Team \u2026",
1326
+ required: true
1327
+ }
1328
+ ),
1329
+ /* @__PURE__ */ jsx4(Shortcut, { id: "chat-shortcut" })
1330
+ ] }),
1331
+ /* @__PURE__ */ jsx4("div", { className: "chat-compose-toolbar", children: /* @__PURE__ */ jsxs4("div", { className: "chat-compose-actions", children: [
1332
+ controls,
1333
+ /* @__PURE__ */ jsx4(
1334
+ "button",
1335
+ {
1336
+ type: "submit",
1337
+ disabled: busy || voiceBusy || resolved || !input.trim(),
1338
+ className: "chat-send",
1339
+ "aria-label": "Frage senden",
1340
+ children: "Senden"
1341
+ }
1342
+ )
1343
+ ] }) })
1344
+ ]
1273
1345
  }
1274
1346
  ),
1275
- /* @__PURE__ */ jsx4(Shortcut, { id: "chat-shortcut" })
1276
- ] }),
1277
- /* @__PURE__ */ jsx4(
1278
- "button",
1279
- {
1280
- type: "submit",
1281
- disabled: busy || voiceBusy || resolved || !input.trim(),
1282
- className: "chat-send",
1283
- "aria-label": "Frage senden",
1284
- children: "Senden"
1285
- }
1286
- )
1287
- ] }),
1288
- /* @__PURE__ */ jsx4(
1289
- VoiceInput,
1290
- {
1291
- ctx,
1292
- value: input,
1293
- setValue: setInput,
1294
- disabled: resolved,
1295
- onBusy: setVoiceBusy
1296
- }
1297
- )
1298
- ] }),
1347
+ details
1348
+ ] })
1349
+ }
1350
+ ) }),
1299
1351
  messages.length > 0 && !resolved && /* @__PURE__ */ jsx4(
1300
1352
  "button",
1301
1353
  {
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var cspPlugin = {
3
3
  id: "chat",
4
- sdkVersion: "^0.13.1",
4
+ sdkVersion: "^0.13.3",
5
5
  browser: "./browser",
6
6
  server: "./server"
7
7
  };
package/dist/server.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cspPlugin
3
- } from "./chunk-B7EKNOQK.js";
3
+ } from "./chunk-VM2Y3GL7.js";
4
4
 
5
5
  // src/server.ts
6
6
  import { z as z2 } from "zod";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kieksme/csp-plugin-chat",
3
- "version": "0.13.1",
3
+ "version": "0.13.3",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "files": [
@@ -23,7 +23,7 @@
23
23
  },
24
24
  "dependencies": {
25
25
  "@azure/msal-browser": "^5.25.0",
26
- "@kieksme/csp-sdk": "^0.13.1",
26
+ "@kieksme/csp-sdk": "^0.13.3",
27
27
  "openai": "^6.3.0",
28
28
  "zod": "^3.25.76"
29
29
  },
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "cspPlugin": {
37
37
  "id": "chat",
38
- "sdkVersion": "^0.13.1",
38
+ "sdkVersion": "^0.13.3",
39
39
  "browser": "./browser",
40
40
  "server": "./server"
41
41
  },