@kieksme/csp-plugin-chat 0.13.0 → 0.13.2

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
@@ -33,3 +33,5 @@ The assistant asks one short clarifying question for vague requests (for example
33
33
  Long histories follow new messages while the visitor reads at the bottom; scrolling up preserves the reading position and offers **Neue Nachrichten ↓**. Real OpenAI providers expose editable dictation through the server-only transcription API. With a support transport, **Ich möchte mit einer echten Person sprechen** pauses the bot immediately and queues a persistent, directed support request. See [chat behaviour and APIs](../../docs/chat.md) and [runtime options](../../docs/configuration.md#spracheingabe-und-supportanfragen).
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
+
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. Its privacy notice stays visible underneath, and mobile touch targets remain 44 × 44 px.
package/dist/browser.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  cspPlugin
3
- } from "./chunk-W5TFART7.js";
3
+ } from "./chunk-OGWLCI3O.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,68 +574,75 @@ function VoiceInput({
573
574
  }
574
575
  }
575
576
  }
576
- if (!enabled) return null;
577
- return /* @__PURE__ */ jsxs2("div", { className: "chat-voice", "data-recording": state === "recording", children: [
578
- state === "idle" ? /* @__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(
579
606
  "button",
580
607
  {
581
608
  type: "button",
582
609
  className: "chat-voice-button",
583
- disabled,
584
- onClick: () => void start(),
585
- children: [
586
- /* @__PURE__ */ jsxs2(
587
- "svg",
588
- {
589
- "aria-hidden": "true",
590
- viewBox: "0 0 24 24",
591
- width: "16",
592
- height: "16",
593
- className: "inline mr-2",
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
- "Nachricht diktieren"
604
- ]
610
+ onClick: () => recorder.current?.stop(),
611
+ children: "Aufnahme stoppen"
605
612
  }
606
- ) : /* @__PURE__ */ jsxs2(Fragment, { children: [
607
- /* @__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" }),
608
- state === "recording" && /* @__PURE__ */ jsx2(
609
- "button",
610
- {
611
- type: "button",
612
- className: "chat-voice-button",
613
- onClick: () => recorder.current?.stop(),
614
- children: "Aufnahme stoppen"
615
- }
616
- ),
617
- /* @__PURE__ */ jsx2(
618
- "button",
619
- {
620
- type: "button",
621
- className: "chat-voice-cancel",
622
- onClick: () => {
623
- operation.current++;
624
- cancelled.current = true;
625
- request.current?.abort();
626
- if (recorder.current?.state === "recording")
627
- recorder.current.stop();
628
- release();
629
- idle();
630
- },
631
- children: "Abbrechen"
632
- }
633
- )
634
- ] }),
635
- /* @__PURE__ */ jsx2("p", { className: "chat-voice-notice", children: "Die Aufnahme wird zur Texterkennung an OpenAI gesendet. Pr\xFCfen Sie den Text vor dem Senden." }),
636
- error && /* @__PURE__ */ jsx2("p", { role: "alert", className: "text-danger dark:text-warning mt-2", children: error })
613
+ ),
614
+ /* @__PURE__ */ jsx2(
615
+ "button",
616
+ {
617
+ type: "button",
618
+ className: "chat-voice-cancel",
619
+ onClick: () => {
620
+ operation.current++;
621
+ cancelled.current = true;
622
+ request.current?.abort();
623
+ if (recorder.current?.state === "recording")
624
+ recorder.current.stop();
625
+ release();
626
+ idle();
627
+ },
628
+ children: "Abbrechen"
629
+ }
630
+ )
637
631
  ] });
632
+ const details = /* @__PURE__ */ jsxs2(
633
+ "div",
634
+ {
635
+ className: "chat-voice",
636
+ "data-state": state,
637
+ "data-recording": state === "recording",
638
+ children: [
639
+ 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" }),
640
+ /* @__PURE__ */ jsx2("p", { className: "chat-voice-notice", children: "Die Aufnahme wird zur Texterkennung an OpenAI gesendet. Pr\xFCfen Sie den Text vor dem Senden." }),
641
+ error && /* @__PURE__ */ jsx2("p", { role: "alert", className: "text-danger dark:text-warning mt-2", children: error })
642
+ ]
643
+ }
644
+ );
645
+ return children(controls, details);
638
646
  }
639
647
 
640
648
  // src/support-chat.tsx
@@ -920,48 +928,61 @@ function SupportChat(ctx) {
920
928
  children: anonymous ? "Chat neu verbinden" : ctx.config.chatSupport?.auth === "session" ? "Portal-Anmeldung erneuern" : "Mit Microsoft anmelden"
921
929
  }
922
930
  ),
923
- ready && /* @__PURE__ */ jsxs3("div", { className: "chat-composer", children: [
924
- /* @__PURE__ */ jsxs3("form", { onSubmit: submit, className: "chat-form flex gap-2.5 items-end", children: [
925
- /* @__PURE__ */ jsx3("label", { htmlFor: "support-chat-input", className: "sr-only", children: "Ihre Nachricht" }),
926
- /* @__PURE__ */ jsx3(
927
- "textarea",
928
- {
929
- id: "support-chat-input",
930
- value: input,
931
- rows: 2,
932
- maxLength: 4e3,
933
- disabled: current?.status === "closed",
934
- className: "flex-1 min-w-0 max-h-[180px] overflow-auto resize-none border border-line rounded-card bg-card p-3",
935
- onChange: (e) => setInput(e.target.value),
936
- onKeyDown: (e) => sendShortcut(e, blocked || !input.trim()),
937
- "aria-keyshortcuts": "Control+Enter Meta+Enter",
938
- "aria-describedby": "support-chat-shortcut",
939
- placeholder: "Wie k\xF6nnen wir Ihnen helfen?"
940
- }
941
- ),
942
- /* @__PURE__ */ jsx3(
943
- "button",
944
- {
945
- type: "submit",
946
- className: "chat-send",
947
- disabled: blocked || !input.trim(),
948
- children: "Senden"
949
- }
950
- )
951
- ] }),
952
- /* @__PURE__ */ jsx3(Shortcut, { id: "support-chat-shortcut" }),
953
- /* @__PURE__ */ jsx3(
954
- VoiceInput,
955
- {
956
- ctx,
957
- value: input,
958
- setValue: setInput,
959
- disabled: current?.status === "closed",
960
- token: auth?.token,
961
- onBusy: setVoiceBusy
962
- }
963
- )
964
- ] }),
931
+ ready && /* @__PURE__ */ jsx3("div", { className: "chat-composer", children: /* @__PURE__ */ jsx3(
932
+ VoiceInput,
933
+ {
934
+ ctx,
935
+ value: input,
936
+ setValue: setInput,
937
+ disabled: current?.status === "closed",
938
+ token: auth?.token,
939
+ onBusy: setVoiceBusy,
940
+ children: (controls, details) => /* @__PURE__ */ jsxs3(Fragment2, { children: [
941
+ /* @__PURE__ */ jsxs3(
942
+ "form",
943
+ {
944
+ onSubmit: submit,
945
+ className: "chat-form flex gap-2.5 items-end",
946
+ children: [
947
+ /* @__PURE__ */ jsx3("label", { htmlFor: "support-chat-input", className: "sr-only", children: "Ihre Nachricht" }),
948
+ /* @__PURE__ */ jsx3(
949
+ "textarea",
950
+ {
951
+ id: "support-chat-input",
952
+ value: input,
953
+ rows: 2,
954
+ maxLength: 4e3,
955
+ disabled: current?.status === "closed",
956
+ className: "flex-1 min-w-0 max-h-[180px] overflow-auto resize-none border border-line rounded-card bg-card p-3",
957
+ onChange: (e) => setInput(e.target.value),
958
+ onKeyDown: (e) => sendShortcut(e, blocked || !input.trim()),
959
+ "aria-keyshortcuts": "Control+Enter Meta+Enter",
960
+ "aria-describedby": "support-chat-shortcut",
961
+ placeholder: "Wie k\xF6nnen wir Ihnen helfen?"
962
+ }
963
+ ),
964
+ /* @__PURE__ */ jsxs3("div", { className: "chat-compose-toolbar", children: [
965
+ /* @__PURE__ */ jsx3(Shortcut, { id: "support-chat-shortcut" }),
966
+ /* @__PURE__ */ jsxs3("div", { className: "chat-compose-actions", children: [
967
+ controls,
968
+ /* @__PURE__ */ jsx3(
969
+ "button",
970
+ {
971
+ type: "submit",
972
+ className: "chat-send",
973
+ disabled: blocked || !input.trim(),
974
+ children: "Senden"
975
+ }
976
+ )
977
+ ] })
978
+ ] })
979
+ ]
980
+ }
981
+ ),
982
+ details
983
+ ] })
984
+ }
985
+ ) }),
965
986
  ready && (!current || current.status === "bot") && /* @__PURE__ */ jsxs3(
966
987
  "button",
967
988
  {
@@ -1056,7 +1077,7 @@ function SupportChat(ctx) {
1056
1077
  }
1057
1078
 
1058
1079
  // src/browser.tsx
1059
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1080
+ import { Fragment as Fragment3, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
1060
1081
  function resizeInput(field) {
1061
1082
  if (!field) return;
1062
1083
  field.style.height = "auto";
@@ -1240,56 +1261,67 @@ function LegacyChat(ctx) {
1240
1261
  }
1241
1262
  ),
1242
1263
  /* @__PURE__ */ jsx4(NewMessages, { visible: scroll.unread, follow: scroll.follow }),
1243
- /* @__PURE__ */ jsxs4("div", { className: "chat-composer", children: [
1244
- /* @__PURE__ */ jsxs4("form", { className: "chat-form flex gap-2.5 items-end", onSubmit: submit, children: [
1245
- /* @__PURE__ */ jsx4("label", { className: "sr-only", htmlFor: "chat-input", children: "Ihre Frage" }),
1246
- /* @__PURE__ */ jsxs4("div", { className: "relative flex-1 min-w-0", children: [
1247
- /* @__PURE__ */ jsx4(
1248
- "textarea",
1264
+ /* @__PURE__ */ jsx4("div", { className: "chat-composer", children: /* @__PURE__ */ jsx4(
1265
+ VoiceInput,
1266
+ {
1267
+ ctx,
1268
+ value: input,
1269
+ setValue: setInput,
1270
+ disabled: resolved,
1271
+ onBusy: setVoiceBusy,
1272
+ children: (controls, details) => /* @__PURE__ */ jsxs4(Fragment3, { children: [
1273
+ /* @__PURE__ */ jsxs4(
1274
+ "form",
1249
1275
  {
1250
- id: "chat-input",
1251
- ref: inputField,
1252
- rows: 1,
1253
- autoFocus: true,
1254
- 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",
1255
- value: input,
1256
- disabled: resolved,
1257
- maxLength: 4e3,
1258
- onChange: (e) => setInput(e.target.value),
1259
- onKeyDown: (e) => sendShortcut(
1260
- e,
1261
- busy || voiceBusy || resolved || !input.trim()
1262
- ),
1263
- "aria-keyshortcuts": "Control+Enter Meta+Enter",
1264
- "aria-describedby": "chat-shortcut",
1265
- placeholder: "Ihre Frage an unser Team \u2026",
1266
- required: true
1276
+ className: "chat-form flex gap-2.5 items-end",
1277
+ onSubmit: submit,
1278
+ children: [
1279
+ /* @__PURE__ */ jsx4("label", { className: "sr-only", htmlFor: "chat-input", children: "Ihre Frage" }),
1280
+ /* @__PURE__ */ jsx4(
1281
+ "textarea",
1282
+ {
1283
+ id: "chat-input",
1284
+ ref: inputField,
1285
+ rows: 1,
1286
+ autoFocus: true,
1287
+ 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",
1288
+ value: input,
1289
+ disabled: resolved,
1290
+ maxLength: 4e3,
1291
+ onChange: (e) => setInput(e.target.value),
1292
+ onKeyDown: (e) => sendShortcut(
1293
+ e,
1294
+ busy || voiceBusy || resolved || !input.trim()
1295
+ ),
1296
+ "aria-keyshortcuts": "Control+Enter Meta+Enter",
1297
+ "aria-describedby": "chat-shortcut",
1298
+ placeholder: "Ihre Frage an unser Team \u2026",
1299
+ required: true
1300
+ }
1301
+ ),
1302
+ /* @__PURE__ */ jsxs4("div", { className: "chat-compose-toolbar", children: [
1303
+ /* @__PURE__ */ jsx4(Shortcut, { id: "chat-shortcut" }),
1304
+ /* @__PURE__ */ jsxs4("div", { className: "chat-compose-actions", children: [
1305
+ controls,
1306
+ /* @__PURE__ */ jsx4(
1307
+ "button",
1308
+ {
1309
+ type: "submit",
1310
+ disabled: busy || voiceBusy || resolved || !input.trim(),
1311
+ className: "chat-send",
1312
+ "aria-label": "Frage senden",
1313
+ children: "Senden"
1314
+ }
1315
+ )
1316
+ ] })
1317
+ ] })
1318
+ ]
1267
1319
  }
1268
1320
  ),
1269
- /* @__PURE__ */ jsx4(Shortcut, { id: "chat-shortcut" })
1270
- ] }),
1271
- /* @__PURE__ */ jsx4(
1272
- "button",
1273
- {
1274
- type: "submit",
1275
- disabled: busy || voiceBusy || resolved || !input.trim(),
1276
- className: "chat-send",
1277
- "aria-label": "Frage senden",
1278
- children: "Senden"
1279
- }
1280
- )
1281
- ] }),
1282
- /* @__PURE__ */ jsx4(
1283
- VoiceInput,
1284
- {
1285
- ctx,
1286
- value: input,
1287
- setValue: setInput,
1288
- disabled: resolved,
1289
- onBusy: setVoiceBusy
1290
- }
1291
- )
1292
- ] }),
1321
+ details
1322
+ ] })
1323
+ }
1324
+ ) }),
1293
1325
  messages.length > 0 && !resolved && /* @__PURE__ */ jsx4(
1294
1326
  "button",
1295
1327
  {
@@ -1,7 +1,7 @@
1
1
  // package.json
2
2
  var cspPlugin = {
3
3
  id: "chat",
4
- sdkVersion: "^0.13.0",
4
+ sdkVersion: "^0.13.2",
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-W5TFART7.js";
3
+ } from "./chunk-OGWLCI3O.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.0",
3
+ "version": "0.13.2",
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.0",
26
+ "@kieksme/csp-sdk": "^0.13.2",
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.0",
38
+ "sdkVersion": "^0.13.2",
39
39
  "browser": "./browser",
40
40
  "server": "./server"
41
41
  },