quassel 0.3.0 → 0.4.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/index.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  chatAttachmentMediaType,
6
6
  compactToolLine,
7
7
  prettyJson
8
- } from "./chunk-IGCFQJZB.js";
8
+ } from "./chunk-3VEL4HEO.js";
9
9
 
10
10
  // packages/quassel/src/types.ts
11
11
  function stepState(message) {
@@ -21,68 +21,67 @@ function stepState(message) {
21
21
  // packages/quassel/src/texts.ts
22
22
  var fillText = (text, values) => text.replace(/\{(\w+)\}/g, (match, name) => name in values ? String(values[name]) : match);
23
23
  var defaultTexts = {
24
- working: "Arbeitet ...",
25
- toolRunning: "l\xE4uft ...",
26
- toolStillRunning: "Werkzeug l\xE4uft ...",
24
+ working: "Working ...",
25
+ toolRunning: "running ...",
26
+ toolStillRunning: "Tool running ...",
27
27
  toolElapsedSeconds: "{seconds} s",
28
- currentTool: "Werkzeug l\xE4uft",
29
- thinkingChip: "Denken",
30
- toolChip: "Werkzeug",
31
- stepGroupOne: "1 Schritt",
32
- stepGroupMany: "{count} Schritte",
33
- stepGroupCollapse: "Einklappen",
34
- thinkingTitle: "Thinking-Trace",
35
- toolTitle: "Tool-Call",
36
- argumentsLabel: "Argumente",
37
- resultLabel: "Ergebnis",
38
- close: "Schlie\xDFen",
39
- jumpToEnd: "Zum Ende springen",
40
- send: "Senden",
41
- sendIntoRun: "In den laufenden Turn einspeisen",
42
- stop: "Arbeit stoppen",
43
- placeholder: "Nachricht schreiben ...",
44
- steeringPlaceholder: "Dazwischenfunken ...",
45
- steered: "In den laufenden Turn eingespeist",
46
- detailModeTitle: "Detailgrad der Schritte",
47
- detailModeOff: "nur Antworten",
48
- detailModeCurrent: "aktuell",
49
- detailModeIcons: "Symbole",
50
- detailModeChips: "kompakt",
51
- detailModeGrouped: "gruppiert",
52
- detailModeCompact: "einzeilig",
53
- detailModeFull: "alles",
54
- timestamps: "Zeitstempel",
55
- showTimestamps: "Zeitstempel anzeigen",
56
- hideTimestamps: "Zeitstempel ausblenden",
57
- copyMessage: "Nachricht kopieren",
58
- editMessage: "Nachricht bearbeiten",
59
- retryMessage: "Antwort erneut anfordern",
60
- copyCode: "Code kopieren",
61
- copied: "Kopiert",
62
- pendingAction: "wartet auf Eingabe",
63
- dismissAction: "Verwerfen",
64
- actionDismissed: "verworfen",
65
- actionFailed: "Aktion fehlgeschlagen",
66
- clipboardUnavailable: "Die Zwischenablage ist nicht verf\xFCgbar",
67
- transcript: "Chatverlauf",
68
- attachments: "Anh\xE4nge",
69
- attachFiles: "Dateien anh\xE4ngen",
70
- attachFilesHint: "Dateien anh\xE4ngen (bis zu {count} Dateien, insgesamt {size})",
71
- removeAttachment: "{name} entfernen",
72
- attachmentPreparing: "Wird vorbereitet ...",
73
- attachmentsPreparing: "Anh\xE4nge werden vorbereitet ...",
74
- attachmentReadFailed: "Datei konnte nicht gelesen werden. Entferne sie und f\xFCge sie erneut hinzu.",
75
- attachmentsWhileBusy: "Warte, bis die Eingabe wieder bereit ist, und f\xFCge die Dateien dann erneut hinzu.",
76
- tooManyAttachments: "H\xF6chstens {count} Anh\xE4nge pro Nachricht. Entferne zuerst einen Anhang.",
77
- attachmentsTooLarge: "Anh\xE4nge d\xFCrfen zusammen h\xF6chstens {size} gro\xDF sein. W\xE4hle kleinere Dateien.",
78
- attachmentUnsupported: "Dieser Chat unterst\xFCtzt {name} nicht als {kind}. Entferne den Anhang.",
79
- attachmentUnsupportedByModel: "Das Modell {model} unterst\xFCtzt {name} nicht als {kind}. W\xE4hle ein passendes Modell oder entferne den Anhang.",
80
- attachmentKindImage: "Bild",
81
- attachmentKindVideo: "Video",
28
+ currentTool: "Tool running",
29
+ thinkingChip: "Thinking",
30
+ toolChip: "Tool",
31
+ stepGroupOne: "1 step",
32
+ stepGroupMany: "{count} steps",
33
+ stepGroupCollapse: "Collapse",
34
+ thinkingTitle: "Thinking trace",
35
+ toolTitle: "Tool call",
36
+ argumentsLabel: "Arguments",
37
+ resultLabel: "Result",
38
+ close: "Close",
39
+ jumpToEnd: "Jump to end",
40
+ send: "Send",
41
+ sendIntoRun: "Feed into the running turn",
42
+ stop: "Stop work",
43
+ placeholder: "Write a message ...",
44
+ steeringPlaceholder: "Interject ...",
45
+ steered: "Fed into the running turn",
46
+ detailModeTitle: "Step detail level",
47
+ detailModeOff: "answers only",
48
+ detailModeCurrent: "current",
49
+ detailModeIcons: "icons",
50
+ detailModeChips: "compact",
51
+ detailModeGrouped: "grouped",
52
+ detailModeCompact: "single line",
53
+ detailModeFull: "everything",
54
+ showTimestamps: "Show timestamps",
55
+ hideTimestamps: "Hide timestamps",
56
+ copyMessage: "Copy message",
57
+ editMessage: "Edit message",
58
+ retryMessage: "Request answer again",
59
+ copyCode: "Copy code",
60
+ copied: "Copied",
61
+ pendingAction: "waiting for input",
62
+ dismissAction: "Dismiss",
63
+ actionDismissed: "dismissed",
64
+ actionFailed: "Action failed",
65
+ clipboardUnavailable: "The clipboard is not available",
66
+ transcript: "Chat history",
67
+ attachments: "Attachments",
68
+ attachFiles: "Attach files",
69
+ attachFilesHint: "Attach files (up to {count} files, {size} in total)",
70
+ removeAttachment: "Remove {name}",
71
+ attachmentPreparing: "Preparing ...",
72
+ attachmentsPreparing: "Preparing attachments ...",
73
+ attachmentReadFailed: "The file could not be read. Remove it and add it again.",
74
+ attachmentsWhileBusy: "Wait until the input is ready again, then add the files again.",
75
+ tooManyAttachments: "At most {count} attachments per message. Remove an attachment first.",
76
+ attachmentsTooLarge: "Attachments may be at most {size} in total. Choose smaller files.",
77
+ attachmentUnsupported: "This chat does not support {name} as {kind}. Remove the attachment.",
78
+ attachmentUnsupportedByModel: "The model {model} does not support {name} as {kind}. Choose a suitable model or remove the attachment.",
79
+ attachmentKindImage: "image",
80
+ attachmentKindVideo: "video",
82
81
  attachmentKindPdf: "PDF",
83
- notSent: "Nicht gesendet:",
84
- notSentAttachments: "({count} Anh\xE4nge)",
85
- insertUnsent: "Nicht gesendete Eingabe einf\xFCgen"
82
+ notSent: "Not sent:",
83
+ notSentAttachments: "({count} attachments)",
84
+ insertUnsent: "Insert unsent input"
86
85
  };
87
86
 
88
87
  // packages/quassel/src/ui/cn.ts
@@ -461,7 +460,7 @@ var components = {
461
460
  a: ({ href, children }) => /* @__PURE__ */ jsx8(MarkdownLink, { href, children }),
462
461
  pre: ({ children }) => /* @__PURE__ */ jsx8(CodeBlock, { children }),
463
462
  code: ({ children, className }) => /* @__PURE__ */ jsx8("code", { className: cn(className, "qsl:rounded-sm qsl:bg-secondary qsl:px-1 qsl:py-0.5 qsl:font-mono qsl:text-sm qsl:[overflow-wrap:anywhere] qsl:in-data-[tone=on-color]:bg-white/20 qsl:in-data-[tone=on-color]:text-inherit", "qsl:[pre>&]:bg-transparent qsl:[pre>&]:p-0"), children }),
464
- // Ohne max-content schrumpft die Tabelle auf den Container und stapelt Buchstaben.
463
+ // Without max-content the table shrinks to the container and stacks letters.
465
464
  table: ({ children }) => /* @__PURE__ */ jsx8("div", { className: cn(scrollbar, scrollCover, "qsl:my-2 qsl:overflow-x-auto"), children: /* @__PURE__ */ jsx8("table", { className: "qsl:w-max qsl:min-w-full qsl:border-collapse qsl:text-sm", children }) }),
466
465
  thead: ({ children }) => /* @__PURE__ */ jsx8("thead", { children }),
467
466
  tbody: ({ children }) => /* @__PURE__ */ jsx8("tbody", { children }),
@@ -495,7 +494,7 @@ function Markdown({ text, streaming = false }) {
495
494
  );
496
495
  }
497
496
  function markdownPlainText(text) {
498
- return text.split("\n").filter((line) => !/^\s*```\w*\s*$/.test(line) && !isTableDivider(line)).map((line) => (line.trim().startsWith("|") ? splitTableRow(line).join(", ") : line).replace(/^(#{1,6})\s+/, "").replace(/^\s*>\s?/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/!?\[([^\]]*)\]\(([^)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*]+)\*|`([^`]+)`/g, (_, link, _href, fett, fettUnterstrich, kursiv, code) => link ?? fett ?? fettUnterstrich ?? kursiv ?? code).trim()).filter((line) => line !== "").join("\n");
497
+ return text.split("\n").filter((line) => !/^\s*```\w*\s*$/.test(line) && !isTableDivider(line)).map((line) => (line.trim().startsWith("|") ? splitTableRow(line).join(", ") : line).replace(/^(#{1,6})\s+/, "").replace(/^\s*>\s?/, "").replace(/^\s*([-*+]|\d+[.)])\s+/, "").replace(/!?\[([^\]]*)\]\(([^)]+)\)|\*\*([^*]+)\*\*|__([^_]+)__|\*([^*]+)\*|`([^`]+)`/g, (_, link, _href, bold, boldUnderscore, italic, code) => link ?? bold ?? boldUnderscore ?? italic ?? code).trim()).filter((line) => line !== "").join("\n");
499
498
  }
500
499
  function splitTableRow(line) {
501
500
  let row = line.trim();
@@ -605,159 +604,159 @@ function PendingActionCard({
605
604
  // packages/quassel/src/WorkingScenes.tsx
606
605
  import { useEffect as useEffect2, useState as useState2 } from "react";
607
606
  import { jsx as jsx11, jsxs as jsxs4 } from "react/jsx-runtime";
608
- var SPUR = 13;
609
- var GRUNDTAKT_MS = 90;
610
- var SZENENWECHSEL_MS = 2600;
611
- function bahn(figuren, breite = SPUR) {
612
- return Array.from({ length: breite }, (_, x) => figuren[x] ?? " ");
607
+ var TRACK = 13;
608
+ var BASE_TICK_MS = 90;
609
+ var SCENE_CHANGE_MS = 2600;
610
+ function track(figures, width = TRACK) {
611
+ return Array.from({ length: width }, (_, x) => figures[x] ?? " ");
613
612
  }
614
- var SZENEN = [
613
+ var SCENES = [
615
614
  {
616
- id: "denken",
617
- takt: 2,
618
- zeichne: (schritt) => {
619
- const takt = schritt % 10;
620
- if (takt >= 8) return "\u{1F929}\u{1F4A1}";
621
- return "\u{1F914}" + "\u{1F4AD}".repeat(Math.min(takt, 3));
615
+ id: "thinking",
616
+ tick: 2,
617
+ draw: (step) => {
618
+ const tick = step % 10;
619
+ if (tick >= 8) return "\u{1F929}\u{1F4A1}";
620
+ return "\u{1F914}" + "\u{1F4AD}".repeat(Math.min(tick, 3));
622
621
  }
623
622
  },
624
623
  {
625
- // Fliegt nach rechts und zieht ihren Schweif hinterher.
626
- id: "rakete",
627
- takt: 2,
628
- laufend: true,
629
- zeichne: (schritt) => {
630
- const x = schritt % SPUR;
631
- return bahn({ [x - 2]: "\xB7", [x - 1]: "\u{1F4A8}", [x]: "\u{1F680}" });
624
+ // Flies to the right, dragging its tail behind.
625
+ id: "rocket",
626
+ tick: 2,
627
+ moving: true,
628
+ draw: (step) => {
629
+ const x = step % TRACK;
630
+ return track({ [x - 2]: "\xB7", [x - 1]: "\u{1F4A8}", [x]: "\u{1F680}" });
632
631
  }
633
632
  },
634
633
  {
635
- // Die Punkte bleiben stehen und werden einzeln gefressen.
634
+ // The dots stay put and are eaten one by one.
636
635
  id: "pacman",
637
- takt: 2,
638
- laufend: true,
639
- zeichne: (schritt) => {
640
- const x = schritt % SPUR;
641
- const figuren = {};
642
- for (let i = x + 1; i < SPUR; i++) {
643
- figuren[i] = "\xB7";
636
+ tick: 2,
637
+ moving: true,
638
+ draw: (step) => {
639
+ const x = step % TRACK;
640
+ const figures = {};
641
+ for (let i = x + 1; i < TRACK; i++) {
642
+ figures[i] = "\xB7";
644
643
  }
645
- figuren[x] = schritt % 2 === 0 ? "\u{1F7E1}" : "\u{1F315}";
646
- return bahn(figuren);
644
+ figures[x] = step % 2 === 0 ? "\u{1F7E1}" : "\u{1F315}";
645
+ return track(figures);
647
646
  }
648
647
  },
649
648
  {
650
- // Aus einem Samen wird ein Baum, dann von vorn.
651
- id: "wachsen",
649
+ // A seed becomes a tree, then start over.
650
+ id: "growing",
652
651
  compact: true,
653
- takt: 2,
654
- zeichne: (schritt) => {
655
- const stufen = ["\u{1F331}", "\u{1F331}", "\u{1F33F}", "\u{1F33F}", "\u{1F343}", "\u{1F333}", "\u{1F333}", "\u{1F333}"];
656
- return stufen[schritt % stufen.length];
652
+ tick: 2,
653
+ draw: (step) => {
654
+ const stages = ["\u{1F331}", "\u{1F331}", "\u{1F33F}", "\u{1F33F}", "\u{1F343}", "\u{1F333}", "\u{1F333}", "\u{1F333}"];
655
+ return stages[step % stages.length];
657
656
  }
658
657
  },
659
658
  {
660
- // Das Ei bekommt Risse, das Kueken schluepft.
661
- id: "schluepfen",
659
+ // The egg cracks, the chick hatches.
660
+ id: "hatching",
662
661
  compact: true,
663
- takt: 2,
664
- zeichne: (schritt) => {
665
- const takt = schritt % 12;
666
- if (takt < 4) return "\u{1F95A}";
667
- if (takt < 6) return "\u{1F95A}*";
668
- if (takt < 9) return "\u{1F423}";
662
+ tick: 2,
663
+ draw: (step) => {
664
+ const tick = step % 12;
665
+ if (tick < 4) return "\u{1F95A}";
666
+ if (tick < 6) return "\u{1F95A}*";
667
+ if (tick < 9) return "\u{1F423}";
669
668
  return "\u{1F425}";
670
669
  }
671
670
  },
672
671
  {
673
- // Morse-artiges Signal, das sich zu einem OK aufloest.
672
+ // Morse-like signal that resolves into an OK.
674
673
  id: "signal",
675
- takt: 2,
676
- zeichne: (schritt) => {
677
- const takt = schritt % 14;
678
- if (takt >= 11) return "\u{1F4E1} OK";
679
- const muster = ["\xB7", "\xB7\xB7", "\xB7-", "-\xB7", "\xB7\xB7-", "-\xB7\xB7"];
680
- return "\u{1F4E1} " + muster[takt % muster.length];
674
+ tick: 2,
675
+ draw: (step) => {
676
+ const tick = step % 14;
677
+ if (tick >= 11) return "\u{1F4E1} OK";
678
+ const pattern = ["\xB7", "\xB7\xB7", "\xB7-", "-\xB7", "\xB7\xB7-", "-\xB7\xB7"];
679
+ return "\u{1F4E1} " + pattern[tick % pattern.length];
681
680
  }
682
681
  },
683
682
  {
684
- // Sanduhr: der Sand laeuft durch, dann wird gedreht.
685
- id: "sanduhr",
686
- takt: 2,
687
- zeichne: (schritt) => {
688
- const takt = schritt % 12;
689
- const koerner = "\xB7".repeat(Math.max(0, 5 - Math.floor(takt / 2)));
690
- return (takt < 10 ? "\u23F3" : "\u231B") + " " + koerner;
683
+ // Hourglass: the sand runs through, then it is flipped.
684
+ id: "hourglass",
685
+ tick: 2,
686
+ draw: (step) => {
687
+ const tick = step % 12;
688
+ const grains = "\xB7".repeat(Math.max(0, 5 - Math.floor(tick / 2)));
689
+ return (tick < 10 ? "\u23F3" : "\u231B") + " " + grains;
691
690
  }
692
691
  },
693
692
  {
694
- // Ei, Kueken, Henne - und die legt das naechste Ei.
695
- id: "huhn-kreislauf",
693
+ // Egg, chick, hen - and she lays the next egg.
694
+ id: "chicken-cycle",
696
695
  compact: true,
697
- takt: 3,
698
- zeichne: (schritt) => {
699
- const takt = schritt % 16;
700
- if (takt < 3) return "\u{1F95A}";
701
- if (takt < 5) return "\u{1F95A}*";
702
- if (takt < 8) return "\u{1F423}";
703
- if (takt < 11) return "\u{1F424}";
704
- if (takt < 14) return "\u{1F414}";
696
+ tick: 3,
697
+ draw: (step) => {
698
+ const tick = step % 16;
699
+ if (tick < 3) return "\u{1F95A}";
700
+ if (tick < 5) return "\u{1F95A}*";
701
+ if (tick < 8) return "\u{1F423}";
702
+ if (tick < 11) return "\u{1F424}";
703
+ if (tick < 14) return "\u{1F414}";
705
704
  return "\u{1F414}\u{1F95A}";
706
705
  }
707
706
  },
708
707
  {
709
- // Hut, Funken - und das Kaninchen tuermt.
710
- id: "zauberhut",
708
+ // Hat, sparks - and the rabbit bolts.
709
+ id: "magic-hat",
711
710
  compact: true,
712
- takt: 3,
713
- zeichne: (schritt) => {
714
- const takt = schritt % 14;
715
- if (takt < 4) return "\u{1F3A9}";
716
- if (takt < 7) return "\u{1F3A9}*";
717
- if (takt < 11) return "\u{1F3A9}\u{1F407}";
711
+ tick: 3,
712
+ draw: (step) => {
713
+ const tick = step % 14;
714
+ if (tick < 4) return "\u{1F3A9}";
715
+ if (tick < 7) return "\u{1F3A9}*";
716
+ if (tick < 11) return "\u{1F3A9}\u{1F407}";
718
717
  return "\u{1F407}\u{1F4A8}";
719
718
  }
720
719
  },
721
720
  {
722
- // Rot, Gelb, Gruen - und ab.
723
- id: "ampel",
721
+ // Red, yellow, green - and off.
722
+ id: "traffic-light",
724
723
  compact: true,
725
- takt: 3,
726
- zeichne: (schritt) => {
727
- const takt = schritt % 12;
728
- if (takt < 4) return "\u{1F534}";
729
- if (takt < 6) return "\u{1F7E1}";
730
- if (takt < 8) return "\u{1F7E2}";
724
+ tick: 3,
725
+ draw: (step) => {
726
+ const tick = step % 12;
727
+ if (tick < 4) return "\u{1F534}";
728
+ if (tick < 6) return "\u{1F7E1}";
729
+ if (tick < 8) return "\u{1F7E2}";
731
730
  return "\u{1F697}\u{1F4A8}";
732
731
  }
733
732
  },
734
733
  {
735
- // Klingeln, verschlafen, losrennen.
736
- id: "wecker",
734
+ // Ringing, oversleeping, running off.
735
+ id: "alarm-clock",
737
736
  compact: true,
738
- takt: 2,
739
- zeichne: (schritt) => {
740
- const takt = schritt % 14;
741
- if (takt < 5) return takt % 2 === 0 ? "\u23F0" : "\u{1F514}";
742
- if (takt < 8) return "\u{1F634}";
743
- if (takt < 10) return "\u{1F633}";
737
+ tick: 2,
738
+ draw: (step) => {
739
+ const tick = step % 14;
740
+ if (tick < 5) return tick % 2 === 0 ? "\u23F0" : "\u{1F514}";
741
+ if (tick < 8) return "\u{1F634}";
742
+ if (tick < 10) return "\u{1F633}";
744
743
  return "\u{1F3C3}\u{1F4A8}";
745
744
  }
746
745
  },
747
746
  {
748
- // Schnecke kriecht zur Flagge - und feiert.
749
- id: "schnecke",
750
- takt: 4,
751
- zeichne: (schritt) => {
752
- const takt = schritt % 8;
753
- if (takt < 5) return bahn({ [takt]: "\u{1F40C}", 4: "\u{1F3C1}" }, 5);
754
- return bahn({ 4: "\u{1F389}" }, 5);
747
+ // A snail crawls to the flag - and celebrates.
748
+ id: "snail",
749
+ tick: 4,
750
+ draw: (step) => {
751
+ const tick = step % 8;
752
+ if (tick < 5) return track({ [tick]: "\u{1F40C}", 4: "\u{1F3C1}" }, 5);
753
+ return track({ 4: "\u{1F389}" }, 5);
755
754
  }
756
755
  }
757
756
  ];
758
- var COMPACT_SCENES = SZENEN.filter((scene) => scene.compact);
759
- function WorkingScenes({ label = "Arbeitet ...", compact = false }) {
760
- const scenes = compact ? COMPACT_SCENES : SZENEN;
757
+ var COMPACT_SCENES = SCENES.filter((scene) => scene.compact);
758
+ function WorkingScenes({ label = "Working ...", compact = false }) {
759
+ const scenes = compact ? COMPACT_SCENES : SCENES;
761
760
  const [sceneIndex, setSceneIndex] = useState2(() => Math.floor(Math.random() * scenes.length));
762
761
  const [offset, setOffset] = useState2(() => Math.floor(Math.random() * 20));
763
762
  const [frameTick, setFrameTick] = useState2(0);
@@ -776,15 +775,15 @@ function WorkingScenes({ label = "Arbeitet ...", compact = false }) {
776
775
  setSceneIndex((previous) => (previous + 1 + Math.floor(Math.random() * (scenes.length - 1))) % scenes.length);
777
776
  setOffset(Math.floor(Math.random() * 20));
778
777
  setFrameTick(0);
779
- }, SZENENWECHSEL_MS);
778
+ }, SCENE_CHANGE_MS);
780
779
  return () => window.clearInterval(timer);
781
780
  }, [reducedMotion, scenes]);
782
781
  useEffect2(() => {
783
782
  if (reducedMotion) return;
784
- const timer = window.setInterval(() => setFrameTick((value) => value + 1), GRUNDTAKT_MS);
783
+ const timer = window.setInterval(() => setFrameTick((value) => value + 1), BASE_TICK_MS);
785
784
  return () => window.clearInterval(timer);
786
785
  }, [reducedMotion]);
787
- const frame = scene.zeichne(Math.floor(frameTick / scene.takt) + (scene.laufend ? offset : 0));
786
+ const frame = scene.draw(Math.floor(frameTick / scene.tick) + (scene.moving ? offset : 0));
788
787
  return /* @__PURE__ */ jsxs4("span", { "aria-label": label, className: cn("qsl:flex qsl:items-center qsl:gap-2.5 qsl:text-[13px]", compact ? "qsl:text-inherit" : "qsl:mt-4 qsl:text-foreground"), "data-chat": "working", role: "status", children: [
789
788
  !compact && /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: "qsl:text-muted-foreground qsl:opacity-70", children: ">" }),
790
789
  /* @__PURE__ */ jsx11("span", { "aria-hidden": "true", className: cn("qsl:inline-block qsl:flex-none qsl:font-mono qsl:[font-variant-ligatures:none]", compact ? "qsl:w-[2.4em] qsl:text-center" : "qsl:w-[15rem]"), children: /* @__PURE__ */ jsx11("span", { className: "qsl:tracking-[0.02em] qsl:whitespace-pre qsl:motion-reduce:opacity-60", children: typeof frame === "string" ? frame : frame.map((cell, index) => /* @__PURE__ */ jsx11("span", { className: "qsl:inline-block qsl:w-[1.35em] qsl:text-center", children: cell }, index)) }) }, scene.id)
@@ -900,7 +899,7 @@ function formatElapsed(ms, texts) {
900
899
  const seconds = String(total % 60).padStart(2, "0");
901
900
  return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${seconds}` : `${minutes}:${seconds}`;
902
901
  }
903
- function createTicker(intervalMs = tickMs) {
902
+ function createTicker(intervalMs2 = tickMs) {
904
903
  const listeners = /* @__PURE__ */ new Set();
905
904
  let now = Date.now();
906
905
  let timer;
@@ -912,7 +911,7 @@ function createTicker(intervalMs = tickMs) {
912
911
  timer = setInterval(() => {
913
912
  now = Date.now();
914
913
  listeners.forEach((notify) => notify());
915
- }, intervalMs);
914
+ }, intervalMs2);
916
915
  }
917
916
  return () => {
918
917
  listeners.delete(listener);
@@ -924,7 +923,7 @@ function createTicker(intervalMs = tickMs) {
924
923
  },
925
924
  now() {
926
925
  const current = Date.now();
927
- if (timer === void 0 && (current - now >= intervalMs || current < now)) now = current;
926
+ if (timer === void 0 && (current - now >= intervalMs2 || current < now)) now = current;
928
927
  return now;
929
928
  },
930
929
  active: () => timer !== void 0
@@ -1006,12 +1005,12 @@ import { createContext as createContext4 } from "react";
1006
1005
  var ChatActionDock = createContext4(void 0);
1007
1006
 
1008
1007
  // packages/quassel/src/announce.ts
1009
- var abstandMs = 400;
1010
- var hoechstensWartend = 5;
1008
+ var intervalMs = 400;
1009
+ var maxQueued = 5;
1011
1010
  var region;
1012
- var wartend = [];
1013
- var laeuft = false;
1014
- function ansageRegion() {
1011
+ var queued = [];
1012
+ var running = false;
1013
+ function liveRegion() {
1015
1014
  if (region?.isConnected) {
1016
1015
  return region;
1017
1016
  }
@@ -1034,33 +1033,33 @@ function ansageRegion() {
1034
1033
  region = element;
1035
1034
  return element;
1036
1035
  }
1037
- function naechste() {
1038
- if (wartend.length === 0) {
1039
- laeuft = false;
1036
+ function next() {
1037
+ if (queued.length === 0) {
1038
+ running = false;
1040
1039
  return;
1041
1040
  }
1042
1041
  if (document.visibilityState === "hidden") {
1043
- document.addEventListener("visibilitychange", naechste, { once: true });
1042
+ document.addEventListener("visibilitychange", next, { once: true });
1044
1043
  return;
1045
1044
  }
1046
- const [text, ...rest] = wartend;
1047
- wartend = rest;
1048
- const ziel = ansageRegion();
1049
- ziel.textContent = ziel.textContent === text ? `${text}\xA0` : text;
1050
- window.setTimeout(naechste, abstandMs);
1045
+ const [text, ...rest] = queued;
1046
+ queued = rest;
1047
+ const target = liveRegion();
1048
+ target.textContent = target.textContent === text ? `${text}\xA0` : text;
1049
+ window.setTimeout(next, intervalMs);
1051
1050
  }
1052
1051
  function announce(text) {
1053
- const inhalt = text.trim();
1054
- if (!inhalt || typeof document === "undefined") {
1052
+ const content = text.trim();
1053
+ if (!content || typeof document === "undefined") {
1055
1054
  return;
1056
1055
  }
1057
- wartend = [...wartend, inhalt].slice(-hoechstensWartend);
1058
- if (laeuft) {
1056
+ queued = [...queued, content].slice(-maxQueued);
1057
+ if (running) {
1059
1058
  return;
1060
1059
  }
1061
- laeuft = true;
1062
- ansageRegion();
1063
- window.setTimeout(naechste, abstandMs);
1060
+ running = true;
1061
+ liveRegion();
1062
+ window.setTimeout(next, intervalMs);
1064
1063
  }
1065
1064
 
1066
1065
  // packages/quassel/src/ChatMessages.tsx
@@ -1083,12 +1082,12 @@ var chipClasses = cn(
1083
1082
  );
1084
1083
  var chipInteractiveClasses = "qsl:cursor-pointer qsl:hover:border-border qsl:hover:bg-secondary qsl:hover:text-foreground qsl:focus-visible:border-border qsl:focus-visible:bg-secondary qsl:focus-visible:text-foreground qsl:focus-visible:outline-none qsl:data-[state=done]:hover:opacity-100 qsl:data-[state=done]:focus-visible:opacity-100";
1085
1084
  var collapseAtBottomFrom = 11;
1086
- var hoechstensAnsagenAufEinmal = 2;
1085
+ var maxAnnouncementsAtOnce = 2;
1087
1086
  var bubbleBodyClasses = "qsl:relative qsl:max-w-[86%] qsl:px-3 qsl:py-2 qsl:leading-[var(--qsl-chat-line-height,1.625)] qsl:[overflow-wrap:anywhere] qsl:@max-[620px]/chat:max-w-full";
1088
- function istSchritt(message) {
1087
+ function isStep(message) {
1089
1088
  return !!message && (message.role === "thinking" || message.role === "tool");
1090
1089
  }
1091
- function istOffeneAktion(message) {
1090
+ function isOpenAction(message) {
1092
1091
  return message.role === "action" && message.action !== void 0 && message.action.status === void 0;
1093
1092
  }
1094
1093
  function defaultArgumentsText(tool) {
@@ -1098,7 +1097,7 @@ function ChatMessages({
1098
1097
  messages,
1099
1098
  owner,
1100
1099
  detailMode = "current",
1101
- running = false,
1100
+ running: running2 = false,
1102
1101
  working,
1103
1102
  stepsExpandable = true,
1104
1103
  texts,
@@ -1123,9 +1122,9 @@ function ChatMessages({
1123
1122
  messageActions,
1124
1123
  toolElapsedThreshold = 3e3
1125
1124
  }) {
1126
- const alleTexte = { ...defaultTexts, ...texts };
1125
+ const allTexts = { ...defaultTexts, ...texts };
1127
1126
  const { Button: Button2 } = useQuasselComponents();
1128
- const scrollBereich = useRef3(null);
1127
+ const scrollArea = useRef3(null);
1129
1128
  const threadRef = useRef3(null);
1130
1129
  const scrollbarDragging = useRef3(false);
1131
1130
  const touchY = useRef3(void 0);
@@ -1140,23 +1139,23 @@ function ChatMessages({
1140
1139
  const timer = window.setInterval(() => setNow(Date.now()), 15e3);
1141
1140
  return () => window.clearInterval(timer);
1142
1141
  }, [showTimestamps, timestampOptions?.format]);
1143
- const angesagt = useRef3(new Set(messages.map((message) => message.key)));
1142
+ const announced = useRef3(new Set(messages.map((message) => message.key)));
1144
1143
  useEffect3(() => {
1145
- const faellig = messages.filter((message, index) => !angesagt.current.has(message.key) && (message.role === "action" ? istOffeneAktion(message) : message.role === "assistant" && message.text.trim() !== "" && (message.closed === true || !running && index === messages.length - 1)));
1146
- if (faellig.length === 0) {
1144
+ const due = messages.filter((message, index) => !announced.current.has(message.key) && (message.role === "action" ? isOpenAction(message) : message.role === "assistant" && message.text.trim() !== "" && (message.closed === true || !running2 && index === messages.length - 1)));
1145
+ if (due.length === 0) {
1147
1146
  return;
1148
1147
  }
1149
- angesagt.current = /* @__PURE__ */ new Set([...angesagt.current, ...faellig.map((message) => message.key)]);
1150
- if (announce2 === false || faellig.length > hoechstensAnsagenAufEinmal) {
1148
+ announced.current = /* @__PURE__ */ new Set([...announced.current, ...due.map((message) => message.key)]);
1149
+ if (announce2 === false || due.length > maxAnnouncementsAtOnce) {
1151
1150
  return;
1152
1151
  }
1153
- faellig.map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
1154
- }, [messages, running, announce2]);
1152
+ due.map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
1153
+ }, [messages, running2, announce2]);
1155
1154
  const updateJumpVisibility = useCallback2((viewport) => {
1156
1155
  if (viewport.clientHeight > 0) setShowJumpToEnd(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > jumpToEndThreshold);
1157
1156
  }, [jumpToEndThreshold]);
1158
1157
  useLayoutEffect(() => sendScope?.subscribe(() => {
1159
- const viewport = scrollBereich.current;
1158
+ const viewport = scrollArea.current;
1160
1159
  if (!viewport) return;
1161
1160
  scroll.jump(viewport);
1162
1161
  updateJumpVisibility(viewport);
@@ -1169,16 +1168,16 @@ function ChatMessages({
1169
1168
  }
1170
1169
  scroll.pause(viewport);
1171
1170
  };
1172
- const pruefeEnde = useCallback2(() => {
1173
- const bereich = scrollBereich.current;
1174
- if (bereich) {
1175
- scroll.scroll(bereich, scrollbarDragging.current);
1176
- updateJumpVisibility(bereich);
1171
+ const checkEnd = useCallback2(() => {
1172
+ const area = scrollArea.current;
1173
+ if (area) {
1174
+ scroll.scroll(area, scrollbarDragging.current);
1175
+ updateJumpVisibility(area);
1177
1176
  }
1178
1177
  }, [scroll, updateJumpVisibility]);
1179
1178
  useEffect3(() => {
1180
1179
  const finishDrag = () => {
1181
- if (scrollbarDragging.current && scrollBereich.current) scroll.scroll(scrollBereich.current, true);
1180
+ if (scrollbarDragging.current && scrollArea.current) scroll.scroll(scrollArea.current, true);
1182
1181
  scrollbarDragging.current = false;
1183
1182
  };
1184
1183
  window.addEventListener("pointerup", finishDrag);
@@ -1189,27 +1188,27 @@ function ChatMessages({
1189
1188
  };
1190
1189
  }, [scroll]);
1191
1190
  useLayoutEffect(() => {
1192
- const bereich = scrollBereich.current;
1193
- if (!bereich) return;
1191
+ const area = scrollArea.current;
1192
+ if (!area) return;
1194
1193
  const align = () => {
1195
- scroll.layout(bereich, scrollbarDragging.current);
1196
- updateJumpVisibility(bereich);
1194
+ scroll.layout(area, scrollbarDragging.current);
1195
+ updateJumpVisibility(area);
1197
1196
  };
1198
1197
  align();
1199
- const beobachter = new ResizeObserver(align);
1200
- beobachter.observe(bereich);
1201
- if (threadRef.current) beobachter.observe(threadRef.current, { box: "border-box" });
1202
- return () => beobachter.disconnect();
1198
+ const observer = new ResizeObserver(align);
1199
+ observer.observe(area);
1200
+ if (threadRef.current) observer.observe(threadRef.current, { box: "border-box" });
1201
+ return () => observer.disconnect();
1203
1202
  });
1204
1203
  const visible = useMemo3(
1205
1204
  () => {
1206
- const letzterFremder = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
1205
+ const lastNonUser = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
1207
1206
  return messages.filter((message, index) => {
1208
- if (dock !== void 0 && istOffeneAktion(message)) {
1207
+ if (dock !== void 0 && isOpenAction(message)) {
1209
1208
  return false;
1210
1209
  }
1211
- if (detailMode === "current" && istSchritt(message)) {
1212
- return running && index === letzterFremder && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
1210
+ if (detailMode === "current" && isStep(message)) {
1211
+ return running2 && index === lastNonUser && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
1213
1212
  }
1214
1213
  if ((message.role === "thinking" || message.role === "assistant") && message.text.trim() === "" && !message.attachments?.length) {
1215
1214
  return false;
@@ -1220,54 +1219,54 @@ function ChatMessages({
1220
1219
  return true;
1221
1220
  });
1222
1221
  },
1223
- [messages, detailMode, running, dock]
1222
+ [messages, detailMode, running2, dock]
1224
1223
  );
1225
- const offeneAktionen = dock ? messages.filter(istOffeneAktion) : [];
1226
- const mitZeit = (inhalt, key, at, dicht = false) => showTimestamps ? /* @__PURE__ */ jsxs6("div", { className: cn("qsl:flex qsl:items-baseline qsl:gap-2", dicht ? denseStepClasses : stepClasses), children: [
1224
+ const openActions = dock ? messages.filter(isOpenAction) : [];
1225
+ const withTime = (content, key, at, dense = false) => showTimestamps ? /* @__PURE__ */ jsxs6("div", { className: cn("qsl:flex qsl:items-baseline qsl:gap-2", dense ? denseStepClasses : stepClasses), children: [
1227
1226
  /* @__PURE__ */ jsx13("time", { dateTime: messageDate(at)?.toISOString(), className: cn(
1228
1227
  "qsl:flex-none qsl:text-meta qsl:tabular-nums qsl:text-muted-foreground qsl:opacity-85",
1229
1228
  timestampOptions?.format && timestampOptions.format !== "time" ? "qsl:w-[clamp(60px,20%,120px)]" : "qsl:w-[34px]"
1230
1229
  ), children: messageDate(at) ? timestampLabel(messageDate(at), timestampOptions, now) : "" }),
1231
- /* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children: inhalt })
1232
- ] }, `zeit-${key}`) : inhalt;
1233
- const chipModus = detailMode === "current" || detailMode === "chips" || detailMode === "icons";
1234
- const elapsedAfter = running && toolElapsedThreshold !== false ? toolElapsedThreshold : void 0;
1235
- const gruppenModus = detailMode === "grouped";
1236
- const bloecke = [];
1230
+ /* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children: content })
1231
+ ] }, `time-${key}`) : content;
1232
+ const chipMode = detailMode === "current" || detailMode === "chips" || detailMode === "icons";
1233
+ const elapsedAfter = running2 && toolElapsedThreshold !== false ? toolElapsedThreshold : void 0;
1234
+ const groupMode = detailMode === "grouped";
1235
+ const blocks = [];
1237
1236
  let lastDay;
1238
- let gruppe = [];
1239
- const schliesseGruppe = () => {
1240
- if (gruppe.length > 0) {
1241
- const laufenderSchritt = running ? [...gruppe].reverse().find((message) => ["running", "thinking"].includes(stepState(message))) : void 0;
1242
- bloecke.push(mitZeit(
1243
- gruppenModus ? /* @__PURE__ */ jsx13(
1237
+ let group = [];
1238
+ const closeGroup = () => {
1239
+ if (group.length > 0) {
1240
+ const runningStep = running2 ? [...group].reverse().find((message) => ["running", "thinking"].includes(stepState(message))) : void 0;
1241
+ blocks.push(withTime(
1242
+ groupMode ? /* @__PURE__ */ jsx13(
1244
1243
  StepGroup,
1245
1244
  {
1246
1245
  elapsedAfter,
1247
- expandierbar: stepsExpandable,
1248
- laufenderSchritt,
1249
- messages: gruppe,
1250
- texts: alleTexte,
1246
+ expandable: stepsExpandable,
1247
+ runningStep,
1248
+ messages: group,
1249
+ texts: allTexts,
1251
1250
  toolArgumentsText
1252
1251
  },
1253
- gruppe[0].key
1252
+ group[0].key
1254
1253
  ) : /* @__PURE__ */ jsx13(
1255
1254
  StepRow,
1256
1255
  {
1257
1256
  elapsedAfter,
1258
- expandierbar: stepsExpandable,
1259
- messages: gruppe,
1260
- mitText: detailMode !== "icons",
1261
- texts: alleTexte,
1257
+ expandable: stepsExpandable,
1258
+ messages: group,
1259
+ withText: detailMode !== "icons",
1260
+ texts: allTexts,
1262
1261
  toolArgumentsText,
1263
- toolLabel: detailMode === "current" && !stepsExpandable ? alleTexte.currentTool : void 0
1262
+ toolLabel: detailMode === "current" && !stepsExpandable ? allTexts.currentTool : void 0
1264
1263
  },
1265
- gruppe[0].key
1264
+ group[0].key
1266
1265
  ),
1267
- gruppe[0].key,
1268
- gruppe[0].at
1266
+ group[0].key,
1267
+ group[0].at
1269
1268
  ));
1270
- gruppe = [];
1269
+ group = [];
1271
1270
  }
1272
1271
  };
1273
1272
  visible.forEach((message, index) => {
@@ -1275,38 +1274,38 @@ function ChatMessages({
1275
1274
  if (date) {
1276
1275
  const day = timestampDay(date, timestampOptions);
1277
1276
  if (day.key !== lastDay) {
1278
- schliesseGruppe();
1279
- bloecke.push(/* @__PURE__ */ jsx13("div", { className: cn(stepClasses, "qsl:text-center qsl:text-xs qsl:text-muted-foreground"), "data-chat": "day-separator", "data-day": day.key, children: day.label }, `day-${message.key}`));
1277
+ closeGroup();
1278
+ blocks.push(/* @__PURE__ */ jsx13("div", { className: cn(stepClasses, "qsl:text-center qsl:text-xs qsl:text-muted-foreground"), "data-chat": "day-separator", "data-day": day.key, children: day.label }, `day-${message.key}`));
1280
1279
  lastDay = day.key;
1281
1280
  }
1282
1281
  }
1283
- const eigeneDarstellung = message.role === "tool" && message.tool && detailMode !== "current" ? renderTool?.(message.tool) : void 0;
1284
- if (eigeneDarstellung !== void 0) {
1285
- schliesseGruppe();
1286
- bloecke.push(mitZeit(
1287
- /* @__PURE__ */ jsx13("div", { className: stepClasses, children: eigeneDarstellung }, message.key),
1282
+ const customRendering = message.role === "tool" && message.tool && detailMode !== "current" ? renderTool?.(message.tool) : void 0;
1283
+ if (customRendering !== void 0) {
1284
+ closeGroup();
1285
+ blocks.push(withTime(
1286
+ /* @__PURE__ */ jsx13("div", { className: stepClasses, children: customRendering }, message.key),
1288
1287
  message.key,
1289
1288
  message.at
1290
1289
  ));
1291
1290
  return;
1292
1291
  }
1293
- if ((chipModus || gruppenModus) && istSchritt(message)) {
1294
- gruppe = [...gruppe, message];
1292
+ if ((chipMode || groupMode) && isStep(message)) {
1293
+ group = [...group, message];
1295
1294
  return;
1296
1295
  }
1297
- schliesseGruppe();
1298
- const dicht = istSchritt(message) && istSchritt(visible[index - 1]);
1299
- bloecke.push(mitZeit(
1296
+ closeGroup();
1297
+ const dense = isStep(message) && isStep(visible[index - 1]);
1298
+ blocks.push(withTime(
1300
1299
  /* @__PURE__ */ jsx13(
1301
1300
  Bubble,
1302
1301
  {
1303
1302
  detailMode,
1304
- dicht,
1303
+ dense,
1305
1304
  elapsedAfter,
1306
- expandierbar: stepsExpandable,
1305
+ expandable: stepsExpandable,
1307
1306
  message,
1308
1307
  plain: owner != null && message.sender === owner,
1309
- texts: alleTexte,
1308
+ texts: allTexts,
1310
1309
  toolArgumentsText,
1311
1310
  renderAction,
1312
1311
  onDismissAction,
@@ -1317,11 +1316,11 @@ function ChatMessages({
1317
1316
  ),
1318
1317
  message.key,
1319
1318
  message.at,
1320
- dicht
1319
+ dense
1321
1320
  ));
1322
1321
  });
1323
- schliesseGruppe();
1324
- const verlauf = /* @__PURE__ */ jsxs6("div", { className: cn("qsl:@container/chat qsl:relative qsl:flex qsl:min-h-0 qsl:flex-1 qsl:flex-col", className), "data-quassel": "", style: {
1322
+ closeGroup();
1323
+ const transcript = /* @__PURE__ */ jsxs6("div", { className: cn("qsl:@container/chat qsl:relative qsl:flex qsl:min-h-0 qsl:flex-1 qsl:flex-col", className), "data-quassel": "", style: {
1325
1324
  ...appearanceStyle(appearance),
1326
1325
  "--qsl-chat-content-max-width": typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth,
1327
1326
  "--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
@@ -1329,7 +1328,7 @@ function ChatMessages({
1329
1328
  /* @__PURE__ */ jsx13(
1330
1329
  "div",
1331
1330
  {
1332
- "aria-label": alleTexte.transcript,
1331
+ "aria-label": allTexts.transcript,
1333
1332
  className: cn(
1334
1333
  "qsl:min-h-0 qsl:flex-1 qsl:cursor-default qsl:overflow-y-auto qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground",
1335
1334
  "qsl:[--qsl-fade-edge:calc(100%_-_var(--qsl-composer-height,0px))]",
@@ -1339,7 +1338,7 @@ function ChatMessages({
1339
1338
  scrollerClassName
1340
1339
  ),
1341
1340
  "data-quassel-transcript": "",
1342
- onScroll: pruefeEnde,
1341
+ onScroll: checkEnd,
1343
1342
  onWheel: (event) => {
1344
1343
  if (!event.ctrlKey && event.deltaY < 0) pauseUp(event.currentTarget, event.target);
1345
1344
  },
@@ -1355,20 +1354,20 @@ function ChatMessages({
1355
1354
  touchY.current = event.touches[0]?.clientY;
1356
1355
  },
1357
1356
  onTouchMove: (event) => {
1358
- const next = event.touches[0]?.clientY;
1359
- if (next !== void 0 && touchY.current !== void 0 && next > touchY.current) pauseUp(event.currentTarget, event.target);
1360
- touchY.current = next;
1357
+ const next2 = event.touches[0]?.clientY;
1358
+ if (next2 !== void 0 && touchY.current !== void 0 && next2 > touchY.current) pauseUp(event.currentTarget, event.target);
1359
+ touchY.current = next2;
1361
1360
  },
1362
1361
  role: "region",
1363
1362
  tabIndex: 0,
1364
1363
  ref: (element) => {
1365
- scrollBereich.current = element;
1364
+ scrollArea.current = element;
1366
1365
  scrollerRef?.(element);
1367
1366
  },
1368
- children: visible.length === 0 && !running ? emptyState ?? null : /* @__PURE__ */ jsxs6(
1367
+ children: visible.length === 0 && !running2 ? emptyState ?? null : /* @__PURE__ */ jsxs6(
1369
1368
  "div",
1370
1369
  {
1371
- "aria-busy": running || void 0,
1370
+ "aria-busy": running2 || void 0,
1372
1371
  className: cn(
1373
1372
  "qsl:mx-auto qsl:w-[calc(100%_-_2_*_var(--qsl-chat-horizontal-padding,24px))] qsl:max-w-[var(--qsl-chat-content-max-width,none)] qsl:pt-2",
1374
1373
  "qsl:pb-[calc(var(--qsl-composer-height,0px)_+_max(3.25em,40px))]",
@@ -1376,52 +1375,52 @@ function ChatMessages({
1376
1375
  ),
1377
1376
  ref: threadRef,
1378
1377
  children: [
1379
- bloecke,
1380
- running && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: alleTexte.working }))
1378
+ blocks,
1379
+ running2 && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: allTexts.working }))
1381
1380
  ]
1382
1381
  }
1383
1382
  )
1384
1383
  }
1385
1384
  ),
1386
- showJumpToEnd && /* @__PURE__ */ jsx13("div", { className: "qsl:pointer-events-none qsl:absolute qsl:inset-x-0 qsl:bottom-[calc(var(--qsl-composer-height,0px)_+_12px)] qsl:z-10 qsl:flex qsl:justify-center", children: /* @__PURE__ */ jsx13(Button2, { "aria-label": alleTexte.jumpToEnd, className: "qsl:pointer-events-auto qsl:rounded-full qsl:border qsl:border-primary-foreground/20 qsl:shadow-md", size: "icon", title: alleTexte.jumpToEnd, variant: "default", onClick: () => {
1387
- if (scrollBereich.current) {
1388
- scroll.jump(scrollBereich.current);
1389
- updateJumpVisibility(scrollBereich.current);
1385
+ showJumpToEnd && /* @__PURE__ */ jsx13("div", { className: "qsl:pointer-events-none qsl:absolute qsl:inset-x-0 qsl:bottom-[calc(var(--qsl-composer-height,0px)_+_12px)] qsl:z-10 qsl:flex qsl:justify-center", children: /* @__PURE__ */ jsx13(Button2, { "aria-label": allTexts.jumpToEnd, className: "qsl:pointer-events-auto qsl:rounded-full qsl:border qsl:border-primary-foreground/20 qsl:shadow-md", size: "icon", title: allTexts.jumpToEnd, variant: "default", onClick: () => {
1386
+ if (scrollArea.current) {
1387
+ scroll.jump(scrollArea.current);
1388
+ updateJumpVisibility(scrollArea.current);
1390
1389
  }
1391
1390
  }, children: /* @__PURE__ */ jsx13(ChevronDownIcon, {}) }) }),
1392
- dock && offeneAktionen.length > 0 && createPortal(
1393
- /* @__PURE__ */ jsx13("div", { className: "qsl:flex qsl:flex-col qsl:gap-[var(--qsl-chat-dense-message-gap,8px)] qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground", style: appearanceStyle(appearance), children: offeneAktionen.map((message) => /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts: alleTexte }) }, message.key)) }),
1391
+ dock && openActions.length > 0 && createPortal(
1392
+ /* @__PURE__ */ jsx13("div", { className: "qsl:flex qsl:flex-col qsl:gap-[var(--qsl-chat-dense-message-gap,8px)] qsl:text-[length:var(--qsl-chat-font-size,13px)] qsl:text-foreground", style: appearanceStyle(appearance), children: openActions.map((message) => /* @__PURE__ */ jsx13("div", { children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts: allTexts }) }, message.key)) }),
1394
1393
  dock
1395
1394
  )
1396
1395
  ] });
1397
- return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: alleTexte, children: verlauf }) });
1396
+ return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: allTexts, children: transcript }) });
1398
1397
  }
1399
1398
  function StepRow({
1400
1399
  messages,
1401
- mitText,
1402
- expandierbar,
1400
+ withText,
1401
+ expandable,
1403
1402
  elapsedAfter,
1404
1403
  texts,
1405
1404
  toolArgumentsText,
1406
1405
  toolLabel
1407
1406
  }) {
1408
1407
  const [detail, setDetail] = useState4();
1409
- const offen = expandierbar && detail && messages.find((message) => message.key === detail.key);
1408
+ const open = expandable && detail && messages.find((message) => message.key === detail.key);
1410
1409
  return /* @__PURE__ */ jsxs6("div", { className: cn(stepClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-x-1.5 qsl:gap-y-1 qsl:pl-6"), "data-step": "row", children: [
1411
1410
  messages.map((message) => {
1412
1411
  const thinking = message.role === "thinking";
1413
1412
  const tool = message.tool;
1414
1413
  const state = stepState(message);
1415
- const running = state === "running" || state === "thinking";
1414
+ const running2 = state === "running" || state === "thinking";
1416
1415
  const label = thinking ? texts.thinkingChip : toolLabel ?? (tool?.name || message.text || texts.toolChip);
1417
1416
  const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1418
1417
  thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { size: 11 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? "qsl:text-destructive" : void 0, size: 11 }),
1419
- mitText && /* @__PURE__ */ jsx13("span", { className: "qsl:min-w-0 qsl:self-baseline qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap", children: label }),
1420
- mitText && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, className: "qsl:flex-none qsl:self-baseline", message, texts }),
1421
- mitText && /* @__PURE__ */ jsx13("span", { className: "qsl:inline-flex qsl:w-2.5 qsl:flex-none qsl:items-center qsl:justify-center qsl:text-success", children: state === "done" && /* @__PURE__ */ jsx13(CheckIcon4, { size: 10 }) })
1418
+ withText && /* @__PURE__ */ jsx13("span", { className: "qsl:min-w-0 qsl:self-baseline qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap", children: label }),
1419
+ withText && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, className: "qsl:flex-none qsl:self-baseline", message, texts }),
1420
+ withText && /* @__PURE__ */ jsx13("span", { className: "qsl:inline-flex qsl:w-2.5 qsl:flex-none qsl:items-center qsl:justify-center qsl:text-success", children: state === "done" && /* @__PURE__ */ jsx13(CheckIcon4, { size: 10 }) })
1422
1421
  ] });
1423
- const classes = cn(chipClasses, !mitText && "qsl:p-1", running && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
1424
- if (!expandierbar) {
1422
+ const classes = cn(chipClasses, !withText && "qsl:p-1", running2 && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
1423
+ if (!expandable) {
1425
1424
  return /* @__PURE__ */ jsx13("span", { className: classes, "data-kind": thinking ? "thinking" : "tool", "data-state": state, "data-step": "chip", title: label, children: content }, message.key);
1426
1425
  }
1427
1426
  return /* @__PURE__ */ jsx13(
@@ -1440,10 +1439,10 @@ function StepRow({
1440
1439
  message.key
1441
1440
  );
1442
1441
  }),
1443
- offen && detail && /* @__PURE__ */ jsx13(
1442
+ open && detail && /* @__PURE__ */ jsx13(
1444
1443
  StepPopover,
1445
1444
  {
1446
- message: offen,
1445
+ message: open,
1447
1446
  position: detail.position,
1448
1447
  texts,
1449
1448
  toolArgumentsText,
@@ -1454,58 +1453,58 @@ function StepRow({
1454
1453
  }
1455
1454
  function StepGroup({
1456
1455
  messages,
1457
- laufenderSchritt,
1456
+ runningStep,
1458
1457
  elapsedAfter,
1459
- expandierbar,
1458
+ expandable,
1460
1459
  texts,
1461
1460
  toolArgumentsText
1462
1461
  }) {
1463
- const [offen, setOffen] = useState4(false);
1464
- const kopf = useRef3(null);
1465
- const vonUntenEingeklappt = useRef3(false);
1462
+ const [open, setOpen] = useState4(false);
1463
+ const header = useRef3(null);
1464
+ const collapsedFromBottom = useRef3(false);
1466
1465
  useLayoutEffect(() => {
1467
- if (!offen && vonUntenEingeklappt.current) {
1468
- vonUntenEingeklappt.current = false;
1469
- kopf.current?.scrollIntoView({ block: "nearest" });
1466
+ if (!open && collapsedFromBottom.current) {
1467
+ collapsedFromBottom.current = false;
1468
+ header.current?.scrollIntoView({ block: "nearest" });
1470
1469
  }
1471
- }, [offen]);
1472
- const aktiv = laufenderSchritt !== void 0;
1473
- const fehler = messages.some((message) => message.tool?.isError);
1474
- const anzahl = messages.length === 1 ? texts.stepGroupOne : texts.stepGroupMany.replace("{count}", String(messages.length));
1475
- const laufend = laufenderSchritt && (laufenderSchritt.role === "thinking" ? texts.thinkingChip : laufenderSchritt.tool?.name || laufenderSchritt.text || texts.toolChip);
1470
+ }, [open]);
1471
+ const active = runningStep !== void 0;
1472
+ const failed = messages.some((message) => message.tool?.isError);
1473
+ const count = messages.length === 1 ? texts.stepGroupOne : texts.stepGroupMany.replace("{count}", String(messages.length));
1474
+ const runningLabel = runningStep && (runningStep.role === "thinking" ? texts.thinkingChip : runningStep.tool?.name || runningStep.text || texts.toolChip);
1476
1475
  return /* @__PURE__ */ jsxs6("div", { className: stepClasses, "data-step": "group", children: [
1477
1476
  /* @__PURE__ */ jsxs6(
1478
1477
  "button",
1479
1478
  {
1480
- "aria-expanded": offen,
1481
- className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1", aktiv && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"),
1482
- onClick: () => setOffen((wert) => !wert),
1483
- ref: kopf,
1479
+ "aria-expanded": open,
1480
+ className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"),
1481
+ onClick: () => setOpen((value) => !value),
1482
+ ref: header,
1484
1483
  type: "button",
1485
1484
  children: [
1486
- /* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform", offen && "qsl:rotate-90"), size: 12 }),
1487
- /* @__PURE__ */ jsx13(LayersIcon, { className: fehler ? traceErrorIconClasses : traceIconClasses, size: 12 }),
1485
+ /* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform", open && "qsl:rotate-90"), size: 12 }),
1486
+ /* @__PURE__ */ jsx13(LayersIcon, { className: failed ? traceErrorIconClasses : traceIconClasses, size: 12 }),
1488
1487
  /* @__PURE__ */ jsxs6("span", { className: cn(traceLineClasses, "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap"), children: [
1489
- anzahl,
1490
- laufend && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
1491
- laufend,
1488
+ count,
1489
+ runningLabel && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
1490
+ runningLabel,
1492
1491
  " ",
1493
1492
  texts.toolRunning
1494
1493
  ] }),
1495
- laufenderSchritt && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: laufenderSchritt, texts })
1494
+ runningStep && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: runningStep, texts })
1496
1495
  ] })
1497
1496
  ]
1498
1497
  }
1499
1498
  ),
1500
- offen && /* @__PURE__ */ jsx13("div", { className: "qsl:mt-0.5 qsl:flex qsl:flex-col qsl:gap-0.5 qsl:pl-3", children: messages.map((message) => /* @__PURE__ */ jsx13(TraceLine, { className: "qsl:pl-3", elapsedAfter, expandierbar, message, texts, toolArgumentsText }, message.key)) }),
1501
- offen && messages.length >= collapseAtBottomFrom && /* @__PURE__ */ jsxs6(
1499
+ open && /* @__PURE__ */ jsx13("div", { className: "qsl:mt-0.5 qsl:flex qsl:flex-col qsl:gap-0.5 qsl:pl-3", children: messages.map((message) => /* @__PURE__ */ jsx13(TraceLine, { className: "qsl:pl-3", elapsedAfter, expandable, message, texts, toolArgumentsText }, message.key)) }),
1500
+ open && messages.length >= collapseAtBottomFrom && /* @__PURE__ */ jsxs6(
1502
1501
  "button",
1503
1502
  {
1504
1503
  className: cn(traceClasses, traceButtonClasses, "qsl:mt-0.5 qsl:items-center qsl:pl-1"),
1505
1504
  "data-step": "group-collapse",
1506
1505
  onClick: () => {
1507
- vonUntenEingeklappt.current = true;
1508
- setOffen(false);
1506
+ collapsedFromBottom.current = true;
1507
+ setOpen(false);
1509
1508
  },
1510
1509
  type: "button",
1511
1510
  children: [
@@ -1520,7 +1519,7 @@ function TraceLine({
1520
1519
  message,
1521
1520
  className,
1522
1521
  elapsedAfter,
1523
- expandierbar,
1522
+ expandable,
1524
1523
  texts,
1525
1524
  toolArgumentsText
1526
1525
  }) {
@@ -1528,18 +1527,18 @@ function TraceLine({
1528
1527
  const thinking = message.role === "thinking";
1529
1528
  const tool = message.tool;
1530
1529
  const classes = cn(traceClasses, className);
1531
- const zeile = cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap", "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap");
1532
- const inhalt = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1530
+ const line = cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap", "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap");
1531
+ const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1533
1532
  thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 }),
1534
- /* @__PURE__ */ jsxs6("span", { className: zeile, children: [
1533
+ /* @__PURE__ */ jsxs6("span", { className: line, children: [
1535
1534
  message.text,
1536
1535
  tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
1537
1536
  /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
1538
1537
  ] })
1539
1538
  ] });
1540
1539
  const marks = { "data-compact": "true", "data-kind": thinking ? "thinking" : "tool", "data-step": "line" };
1541
- if (!expandierbar) {
1542
- return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children: inhalt });
1540
+ if (!expandable) {
1541
+ return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children: content });
1543
1542
  }
1544
1543
  return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1545
1544
  /* @__PURE__ */ jsx13(
@@ -1551,7 +1550,7 @@ function TraceLine({
1551
1550
  ...marks,
1552
1551
  onClick: (event) => setDetailPosition({ x: event.clientX, y: event.clientY }),
1553
1552
  type: "button",
1554
- children: inhalt
1553
+ children: content
1555
1554
  }
1556
1555
  ),
1557
1556
  detailPosition && /* @__PURE__ */ jsx13(
@@ -1579,9 +1578,9 @@ function Bubble({
1579
1578
  message,
1580
1579
  plain,
1581
1580
  detailMode,
1582
- dicht,
1581
+ dense,
1583
1582
  elapsedAfter,
1584
- expandierbar,
1583
+ expandable,
1585
1584
  texts,
1586
1585
  toolArgumentsText,
1587
1586
  renderAction,
@@ -1589,23 +1588,23 @@ function Bubble({
1589
1588
  bubbleOptions,
1590
1589
  messageActions
1591
1590
  }) {
1592
- const schritt = dicht ? denseStepClasses : stepClasses;
1591
+ const stepSpacing = dense ? denseStepClasses : stepClasses;
1593
1592
  if (message.role === "thinking" || message.role === "tool" && message.tool) {
1594
1593
  if (detailMode === "compact") {
1595
- return /* @__PURE__ */ jsx13(TraceLine, { className: schritt, elapsedAfter, expandierbar, message, texts, toolArgumentsText });
1594
+ return /* @__PURE__ */ jsx13(TraceLine, { className: stepSpacing, elapsedAfter, expandable, message, texts, toolArgumentsText });
1596
1595
  }
1597
1596
  const thinking = message.role === "thinking";
1598
1597
  const tool = message.tool;
1599
1598
  const icon = thinking ? /* @__PURE__ */ jsx13(SparklesIcon2, { className: traceIconClasses, size: 12 }) : /* @__PURE__ */ jsx13(WrenchIcon2, { className: tool?.isError ? traceErrorIconClasses : traceIconClasses, size: 12 });
1600
- const zeile = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1599
+ const line = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1601
1600
  message.text,
1602
1601
  tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
1603
1602
  /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
1604
1603
  ] });
1605
- return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, schritt), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
1604
+ return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, stepSpacing), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
1606
1605
  icon,
1607
1606
  /* @__PURE__ */ jsxs6("div", { className: "qsl:min-w-0 qsl:flex-1", children: [
1608
- /* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children: zeile }),
1607
+ /* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children: line }),
1609
1608
  tool && /* @__PURE__ */ jsxs6("div", { className: "qsl:mt-0.5 qsl:border-l-2 qsl:border-border-soft qsl:pl-2", children: [
1610
1609
  /* @__PURE__ */ jsx13("pre", { className: "qsl:mb-0.5 qsl:max-h-[208px] qsl:overflow-auto qsl:font-mono qsl:text-meta qsl:leading-[1.375] qsl:break-all qsl:whitespace-pre-wrap qsl:opacity-80", children: toolArgumentsText(tool) }),
1611
1610
  tool.result !== void 0 && /* @__PURE__ */ jsx13("pre", { className: cn(
@@ -1617,10 +1616,10 @@ function Bubble({
1617
1616
  ] });
1618
1617
  }
1619
1618
  if (message.role === "action" && message.action) {
1620
- return /* @__PURE__ */ jsx13("div", { className: schritt, children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts }) });
1619
+ return /* @__PURE__ */ jsx13("div", { className: stepSpacing, children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts }) });
1621
1620
  }
1622
1621
  if (message.role === "system") {
1623
- return /* @__PURE__ */ jsxs6("div", { className: cn(schritt, "qsl:border-l-2 qsl:border-destructive/50 qsl:pl-3 qsl:text-sm qsl:text-muted-foreground"), "data-message": "system", children: [
1622
+ return /* @__PURE__ */ jsxs6("div", { className: cn(stepSpacing, "qsl:border-l-2 qsl:border-destructive/50 qsl:pl-3 qsl:text-sm qsl:text-muted-foreground"), "data-message": "system", children: [
1624
1623
  /* @__PURE__ */ jsx13(Markdown, { text: message.text }),
1625
1624
  /* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message })
1626
1625
  ] });
@@ -1642,12 +1641,12 @@ function Bubble({
1642
1641
  /* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
1643
1642
  ] });
1644
1643
  const cursor = message.role === "assistant" && message.textCursor ? JSON.stringify(message.textCursor) : void 0;
1645
- if (!useBubble) return /* @__PURE__ */ jsx13("div", { "data-chat-text-cursor": cursor, className: cn(schritt, "qsl:group qsl:relative qsl:w-full qsl:leading-[var(--qsl-chat-line-height,1.625)] qsl:text-foreground"), "data-message": "answer", children: content });
1644
+ if (!useBubble) return /* @__PURE__ */ jsx13("div", { "data-chat-text-cursor": cursor, className: cn(stepSpacing, "qsl:group qsl:relative qsl:w-full qsl:leading-[var(--qsl-chat-line-height,1.625)] qsl:text-foreground"), "data-message": "answer", children: content });
1646
1645
  return /* @__PURE__ */ jsx13(
1647
1646
  "div",
1648
1647
  {
1649
1648
  "data-chat-text-cursor": cursor,
1650
- className: cn(schritt, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
1649
+ className: cn(stepSpacing, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
1651
1650
  "data-message": message.role === "user" && !message.bubble && variant === "default" ? "user" : "bubble",
1652
1651
  "data-side": side,
1653
1652
  children: /* @__PURE__ */ jsx13(
@@ -1706,8 +1705,8 @@ function ChatPanel({
1706
1705
  appearance,
1707
1706
  scrollOnSend = false
1708
1707
  }) {
1709
- const wurzel = useRef4(null);
1710
- const eingabe = useRef4(null);
1708
+ const root = useRef4(null);
1709
+ const composerRef = useRef4(null);
1711
1710
  const [dock, setDock] = useState5(null);
1712
1711
  const hasComposer = composer !== void 0;
1713
1712
  const scrollOnSendRef = useRef4(scrollOnSend);
@@ -1716,23 +1715,23 @@ function ChatPanel({
1716
1715
  scrollOnSendRef.current = scrollOnSend;
1717
1716
  }, [scrollOnSend]);
1718
1717
  useEffect4(() => {
1719
- const element = eingabe.current;
1720
- const rahmen = wurzel.current;
1721
- if (!element || !rahmen) {
1718
+ const element = composerRef.current;
1719
+ const frame = root.current;
1720
+ if (!element || !frame) {
1722
1721
  return;
1723
1722
  }
1724
- const messen = () => {
1725
- rahmen.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
1726
- rahmen.style.setProperty("--qsl-panel-height", `${rahmen.offsetHeight}px`);
1723
+ const measure = () => {
1724
+ frame.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
1725
+ frame.style.setProperty("--qsl-panel-height", `${frame.offsetHeight}px`);
1727
1726
  };
1728
- const beobachter = new ResizeObserver(messen);
1729
- beobachter.observe(element);
1730
- beobachter.observe(rahmen);
1731
- messen();
1727
+ const observer = new ResizeObserver(measure);
1728
+ observer.observe(element);
1729
+ observer.observe(frame);
1730
+ measure();
1732
1731
  return () => {
1733
- beobachter.disconnect();
1734
- rahmen.style.removeProperty("--qsl-composer-height");
1735
- rahmen.style.removeProperty("--qsl-panel-height");
1732
+ observer.disconnect();
1733
+ frame.style.removeProperty("--qsl-composer-height");
1734
+ frame.style.removeProperty("--qsl-panel-height");
1736
1735
  };
1737
1736
  }, [hasComposer]);
1738
1737
  return /* @__PURE__ */ jsx14(ChatSendContext.Provider, { value: sendScope, children: /* @__PURE__ */ jsx14(ChatActionDock.Provider, { value: hasComposer ? dock : void 0, children: /* @__PURE__ */ jsxs7(
@@ -1747,7 +1746,7 @@ function ChatPanel({
1747
1746
  "--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
1748
1747
  },
1749
1748
  ref: (element) => {
1750
- wurzel.current = element;
1749
+ root.current = element;
1751
1750
  if (typeof nodeRef === "function") {
1752
1751
  nodeRef(element);
1753
1752
  } else if (nodeRef) {
@@ -1756,7 +1755,7 @@ function ChatPanel({
1756
1755
  },
1757
1756
  children: [
1758
1757
  children,
1759
- hasComposer && /* @__PURE__ */ jsxs7("div", { className: "qsl:absolute qsl:inset-x-0 qsl:bottom-0 qsl:z-[5] qsl:px-[var(--qsl-chat-horizontal-padding,24px)] qsl:pb-3.5 qsl:*:mx-auto qsl:*:max-w-[var(--qsl-chat-content-max-width,none)]", "data-chat": "composer", ref: eingabe, children: [
1758
+ hasComposer && /* @__PURE__ */ jsxs7("div", { className: "qsl:absolute qsl:inset-x-0 qsl:bottom-0 qsl:z-[5] qsl:px-[var(--qsl-chat-horizontal-padding,24px)] qsl:pb-3.5 qsl:*:mx-auto qsl:*:max-w-[var(--qsl-chat-content-max-width,none)]", "data-chat": "composer", ref: composerRef, children: [
1760
1759
  /* @__PURE__ */ jsx14("div", { children: /* @__PURE__ */ jsx14(
1761
1760
  "div",
1762
1761
  {
@@ -1834,7 +1833,7 @@ var attachmentMediaClasses = "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondar
1834
1833
  function ChatInputToolbar({
1835
1834
  onSend,
1836
1835
  onStop,
1837
- running = false,
1836
+ running: running2 = false,
1838
1837
  disabled = false,
1839
1838
  sendDisabled = false,
1840
1839
  layout = "card",
@@ -1859,7 +1858,7 @@ function ChatInputToolbar({
1859
1858
  onAutoFocusSettled
1860
1859
  }) {
1861
1860
  const sendScope = useContext4(ChatSendContext);
1862
- const alleTexte = { ...defaultTexts, ...texts };
1861
+ const allTexts = { ...defaultTexts, ...texts };
1863
1862
  const { Button: Button2, StopButton: StopButton2, Toggle: Toggle2 } = useQuasselComponents();
1864
1863
  const [draft, setDraft] = useState7(initialValue);
1865
1864
  const [sending, setSending] = useState7(false);
@@ -1883,11 +1882,11 @@ function ChatInputToolbar({
1883
1882
  const addFiles = (files) => {
1884
1883
  if (!files.length) return;
1885
1884
  if (disabled || pendingSend.current) {
1886
- setAttachmentError(alleTexte.attachmentsWhileBusy);
1885
+ setAttachmentError(allTexts.attachmentsWhileBusy);
1887
1886
  return;
1888
1887
  }
1889
1888
  try {
1890
- validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, alleTexte);
1889
+ validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, allTexts);
1891
1890
  } catch (error) {
1892
1891
  setAttachmentError(error instanceof Error ? error.message : String(error));
1893
1892
  return;
@@ -1901,17 +1900,17 @@ function ChatInputToolbar({
1901
1900
  () => updateAttachments((current) => current.map((item) => item.id === entry.id ? {
1902
1901
  ...entry,
1903
1902
  status: "error",
1904
- error: alleTexte.attachmentReadFailed
1903
+ error: allTexts.attachmentReadFailed
1905
1904
  } : item))
1906
1905
  );
1907
1906
  }
1908
1907
  };
1909
1908
  const rootRef = useRef6(null);
1910
- const eingabeRef = useRef6(null);
1909
+ const textareaRef = useRef6(null);
1911
1910
  const autoFocusSettled = useRef6(false);
1912
1911
  useEffect5(() => {
1913
1912
  if (!autoFocus || autoFocusSettled.current) return;
1914
- const input2 = eingabeRef.current;
1913
+ const input2 = textareaRef.current;
1915
1914
  if (!input2) return;
1916
1915
  let frame = 0;
1917
1916
  const settle = () => {
@@ -1960,7 +1959,7 @@ function ChatInputToolbar({
1960
1959
  disabled: disabled || sending,
1961
1960
  onFiles: addFiles,
1962
1961
  onPasteText: (text) => {
1963
- const input2 = eingabeRef.current;
1962
+ const input2 = textareaRef.current;
1964
1963
  if (!input2 || disabled || pendingSend.current) return;
1965
1964
  const start = input2.selectionStart;
1966
1965
  const end = input2.selectionEnd;
@@ -1997,11 +1996,11 @@ function ChatInputToolbar({
1997
1996
  insert: (text) => {
1998
1997
  pendingFocus.current = true;
1999
1998
  changeDraft(text);
2000
- const element = eingabeRef.current;
1999
+ const element = textareaRef.current;
2001
2000
  element?.focus();
2002
2001
  if (element?.value === text) element.setSelectionRange(text.length, text.length);
2003
2002
  },
2004
- focus: () => eingabeRef.current?.focus(),
2003
+ focus: () => textareaRef.current?.focus(),
2005
2004
  submit: (onSubmit, options) => send(onSubmit, options?.allowEmpty),
2006
2005
  reset: () => {
2007
2006
  generation.current += 1;
@@ -2024,31 +2023,31 @@ function ChatInputToolbar({
2024
2023
  return;
2025
2024
  }
2026
2025
  pendingFocus.current = false;
2027
- const element = eingabeRef.current;
2026
+ const element = textareaRef.current;
2028
2027
  if (element) {
2029
2028
  element.focus();
2030
2029
  element.setSelectionRange(element.value.length, element.value.length);
2031
2030
  }
2032
2031
  }, [draft, sending]);
2033
2032
  const applyAutoGrow = useCallback3(() => {
2034
- const element = eingabeRef.current;
2033
+ const element = textareaRef.current;
2035
2034
  if (!element || inputMaxRows === void 0) {
2036
2035
  return;
2037
2036
  }
2038
- const stil = getComputedStyle(element);
2039
- const zeilenhoehe = Number.parseFloat(stil.lineHeight) || Number.parseFloat(stil.fontSize) * 1.4;
2040
- const polster = Number.parseFloat(stil.paddingTop) + Number.parseFloat(stil.paddingBottom);
2041
- const maximum = zeilenhoehe * Math.max(inputMaxRows, inputRows) + polster;
2037
+ const style = getComputedStyle(element);
2038
+ const lineHeight = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.4;
2039
+ const padding = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom);
2040
+ const maximum = lineHeight * Math.max(inputMaxRows, inputRows) + padding;
2042
2041
  element.style.height = "auto";
2043
- const inhalt = element.scrollHeight;
2044
- element.style.height = `${Math.min(inhalt, maximum)}px`;
2045
- element.style.overflowY = inhalt > maximum ? "auto" : "hidden";
2042
+ const contentHeight = element.scrollHeight;
2043
+ element.style.height = `${Math.min(contentHeight, maximum)}px`;
2044
+ element.style.overflowY = contentHeight > maximum ? "auto" : "hidden";
2046
2045
  }, [inputMaxRows, inputRows]);
2047
2046
  useEffect5(() => {
2048
2047
  applyAutoGrow();
2049
2048
  }, [draft, applyAutoGrow]);
2050
2049
  useEffect5(() => {
2051
- const element = eingabeRef.current;
2050
+ const element = textareaRef.current;
2052
2051
  if (!element || inputMaxRows === void 0) {
2053
2052
  return;
2054
2053
  }
@@ -2069,9 +2068,9 @@ function ChatInputToolbar({
2069
2068
  }
2070
2069
  const update = () => setCompact(element.clientWidth < COMPACT_WIDTH_PX);
2071
2070
  update();
2072
- const beobachter = new ResizeObserver(update);
2073
- beobachter.observe(element);
2074
- return () => beobachter.disconnect();
2071
+ const observer = new ResizeObserver(update);
2072
+ observer.observe(element);
2073
+ return () => observer.disconnect();
2075
2074
  }, []);
2076
2075
  const hasAttachments = attachments.length > 0;
2077
2076
  useEffect5(() => {
@@ -2080,7 +2079,7 @@ function ChatInputToolbar({
2080
2079
  const capabilityError = hasAttachments && attachmentCapabilitiesError ? attachmentCapabilitiesError : attachmentCapabilities ? attachmentCapabilityError(attachments.map((entry) => ({
2081
2080
  name: entry.file.name,
2082
2081
  mediaType: attachmentMediaType(entry.file)
2083
- })), attachmentCapabilities, alleTexte) : void 0;
2082
+ })), attachmentCapabilities, allTexts) : void 0;
2084
2083
  const hasContent = draft.trim().length > 0 || attachments.length > 0;
2085
2084
  const preparing = attachments.some((entry) => entry.status === "reading");
2086
2085
  const attachmentBlocked = attachments.some((entry) => entry.status !== "ready");
@@ -2099,7 +2098,7 @@ function ChatInputToolbar({
2099
2098
  const sentRevision = draftRevision.current;
2100
2099
  const sentDraft = draft;
2101
2100
  const focusOrigin = document.activeElement;
2102
- const startedFocused = focusOrigin === eingabeRef.current || rootRef.current?.contains(focusOrigin);
2101
+ const startedFocused = focusOrigin === textareaRef.current || rootRef.current?.contains(focusOrigin);
2103
2102
  let leftComposer = false;
2104
2103
  const observeFocus = (event) => {
2105
2104
  const target = event.target;
@@ -2130,7 +2129,7 @@ function ChatInputToolbar({
2130
2129
  document.removeEventListener("pointerdown", observeFocus);
2131
2130
  if (generation.current === sentGeneration) {
2132
2131
  pendingSend.current = false;
2133
- pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== eingabeRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
2132
+ pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== textareaRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
2134
2133
  setSending(false);
2135
2134
  }
2136
2135
  }
@@ -2141,28 +2140,28 @@ function ChatInputToolbar({
2141
2140
  if (!event.repeat) void send();
2142
2141
  }
2143
2142
  };
2144
- const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "qsl:flex qsl:max-h-[240px] qsl:flex-wrap qsl:gap-2 qsl:overflow-y-auto qsl:px-3 qsl:pt-2.5 qsl:in-data-[tone=overseer-details]:max-h-40 qsl:in-data-[tone=overseer-details]:overflow-y-auto", "aria-label": alleTexte.attachments, children: attachments.map((entry) => /* @__PURE__ */ jsxs8("li", { className: attachmentItemClasses, children: [
2143
+ const attachmentPreviews = attachments.length > 0 && /* @__PURE__ */ jsx15("ul", { className: "qsl:flex qsl:max-h-[240px] qsl:flex-wrap qsl:gap-2 qsl:overflow-y-auto qsl:px-3 qsl:pt-2.5 qsl:in-data-[tone=overseer-details]:max-h-40 qsl:in-data-[tone=overseer-details]:overflow-y-auto", "aria-label": allTexts.attachments, children: attachments.map((entry) => /* @__PURE__ */ jsxs8("li", { className: attachmentItemClasses, children: [
2145
2144
  entry.status === "ready" && entry.input.mediaType.startsWith("image/") && /* @__PURE__ */ jsx15("img", { className: attachmentMediaClasses, src: `data:${entry.input.mediaType};base64,${entry.input.data}`, alt: entry.file.name }),
2146
2145
  entry.status === "ready" && entry.input.mediaType.startsWith("video/") && /* @__PURE__ */ jsx15("video", { className: attachmentMediaClasses, controls: true, preload: "metadata", "aria-label": entry.file.name, src: `data:${entry.input.mediaType};base64,${entry.input.data}` }),
2147
2146
  /* @__PURE__ */ jsxs8("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:pr-8 qsl:[overflow-wrap:anywhere]", children: [
2148
2147
  /* @__PURE__ */ jsx15("span", { className: "qsl:font-medium", title: entry.file.name, children: entry.file.name }),
2149
2148
  /* @__PURE__ */ jsx15("small", { children: formatAttachmentSize(entry.file.size) }),
2150
- entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: alleTexte.attachmentPreparing }),
2149
+ entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: allTexts.attachmentPreparing }),
2151
2150
  entry.status === "error" && /* @__PURE__ */ jsx15("small", { role: "alert", children: entry.error })
2152
2151
  ] }),
2153
2152
  /* @__PURE__ */ jsx15(
2154
2153
  Button2,
2155
2154
  {
2156
- "aria-label": fillText(alleTexte.removeAttachment, { name: entry.file.name }),
2155
+ "aria-label": fillText(allTexts.removeAttachment, { name: entry.file.name }),
2157
2156
  className: "qsl:absolute qsl:top-1 qsl:right-1",
2158
2157
  disabled: disabled || sending,
2159
2158
  onClick: () => {
2160
2159
  updateAttachments((current) => current.filter((item) => item.id !== entry.id));
2161
2160
  setAttachmentError(void 0);
2162
- eingabeRef.current?.focus();
2161
+ textareaRef.current?.focus();
2163
2162
  },
2164
2163
  size: "icon-sm",
2165
- title: fillText(alleTexte.removeAttachment, { name: entry.file.name }),
2164
+ title: fillText(allTexts.removeAttachment, { name: entry.file.name }),
2166
2165
  variant: "outline",
2167
2166
  children: /* @__PURE__ */ jsx15(XIcon3, {})
2168
2167
  }
@@ -2174,16 +2173,16 @@ function ChatInputToolbar({
2174
2173
  sendError && /* @__PURE__ */ jsx15("p", { className: errorClasses, role: "alert", children: sendError }),
2175
2174
  failedSubmissions.map((failed) => /* @__PURE__ */ jsxs8("div", { className: cn(errorClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-2"), role: "status", children: [
2176
2175
  /* @__PURE__ */ jsxs8("span", { children: [
2177
- alleTexte.notSent,
2176
+ allTexts.notSent,
2178
2177
  " ",
2179
2178
  failed.draft.slice(0, 100),
2180
2179
  failed.draft.length > 100 ? " ..." : "",
2181
- failed.attachments.length > 0 ? ` ${fillText(alleTexte.notSentAttachments, { count: failed.attachments.length })}` : "",
2180
+ failed.attachments.length > 0 ? ` ${fillText(allTexts.notSentAttachments, { count: failed.attachments.length })}` : "",
2182
2181
  " "
2183
2182
  ] }),
2184
2183
  /* @__PURE__ */ jsx15(Button2, { disabled: disabled || sending, size: "sm", variant: "outline", onClick: () => {
2185
2184
  try {
2186
- validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), alleTexte);
2185
+ validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), allTexts);
2187
2186
  } catch (error) {
2188
2187
  setAttachmentError(error instanceof Error ? error.message : String(error));
2189
2188
  return;
@@ -2194,22 +2193,22 @@ function ChatInputToolbar({
2194
2193
  setFailedSubmissions((current) => current.filter((entry) => entry.id !== failed.id));
2195
2194
  setAttachmentError(void 0);
2196
2195
  pendingFocus.current = true;
2197
- }, children: alleTexte.insertUnsent })
2196
+ }, children: allTexts.insertUnsent })
2198
2197
  ] }, failed.id))
2199
2198
  ] });
2200
2199
  const sendButtons = /* @__PURE__ */ jsxs8(Fragment3, { children: [
2201
- running && onStop && !hasContent && /* @__PURE__ */ jsx15(StopButton2, { className: "qsl:flex-none", label: alleTexte.stop, onClick: onStop, size: "icon-sm" }),
2202
- (!running || !onStop || hasContent) && /* @__PURE__ */ jsx15(
2200
+ running2 && onStop && !hasContent && /* @__PURE__ */ jsx15(StopButton2, { className: "qsl:flex-none", label: allTexts.stop, onClick: onStop, size: "icon-sm" }),
2201
+ (!running2 || !onStop || hasContent) && /* @__PURE__ */ jsx15(
2203
2202
  Button2,
2204
2203
  {
2205
- "aria-label": alleTexte.send,
2204
+ "aria-label": allTexts.send,
2206
2205
  className: "qsl:flex-none",
2207
2206
  disabled: disabled || sendDisabled || sending || attachmentBlocked || !!capabilityError || !hasContent,
2208
2207
  onClick: () => {
2209
2208
  void send();
2210
2209
  },
2211
2210
  size: "icon-sm",
2212
- title: running ? alleTexte.sendIntoRun : alleTexte.send,
2211
+ title: running2 ? allTexts.sendIntoRun : allTexts.send,
2213
2212
  children: /* @__PURE__ */ jsx15(SendIcon, { size: 14 })
2214
2213
  }
2215
2214
  )
@@ -2219,16 +2218,16 @@ function ChatInputToolbar({
2219
2218
  attachmentsEnabled && /* @__PURE__ */ jsx15(
2220
2219
  Button2,
2221
2220
  {
2222
- "aria-label": alleTexte.attachFiles,
2221
+ "aria-label": allTexts.attachFiles,
2223
2222
  disabled: disabled || sending,
2224
2223
  onClick: () => fileInput.inputRef.current?.click(),
2225
2224
  size: "icon-sm",
2226
- title: fillText(alleTexte.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
2225
+ title: fillText(allTexts.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
2227
2226
  variant: "ghost",
2228
2227
  children: /* @__PURE__ */ jsx15(PaperclipIcon, {})
2229
2228
  }
2230
2229
  ),
2231
- preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: alleTexte.attachmentsPreparing }),
2230
+ preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: allTexts.attachmentsPreparing }),
2232
2231
  actions?.map((action, index) => action.active === void 0 ? /* @__PURE__ */ jsxs8(Button2, { disabled: action.disabled, onClick: action.onClick, size: "sm", title: action.title ?? action.label, variant: "ghost", children: [
2233
2232
  action.icon,
2234
2233
  action.label && /* @__PURE__ */ jsx15("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
@@ -2253,13 +2252,13 @@ function ChatInputToolbar({
2253
2252
  {
2254
2253
  "aria-controls": inputAriaControls,
2255
2254
  className: textareaClasses,
2256
- "aria-label": alleTexte.placeholder,
2255
+ "aria-label": allTexts.placeholder,
2257
2256
  disabled: !toolbar && disabled,
2258
2257
  readOnly: toolbar && disabled,
2259
2258
  onChange: (event) => changeDraft(event.target.value),
2260
2259
  onKeyDown,
2261
- placeholder: running ? alleTexte.steeringPlaceholder : alleTexte.placeholder,
2262
- ref: eingabeRef,
2260
+ placeholder: running2 ? allTexts.steeringPlaceholder : allTexts.placeholder,
2261
+ ref: textareaRef,
2263
2262
  rows: inputRows,
2264
2263
  value: draft
2265
2264
  }
@@ -2271,14 +2270,14 @@ function ChatInputToolbar({
2271
2270
  "data-compact": compact,
2272
2271
  "data-layout": layout,
2273
2272
  "data-quassel": "",
2274
- "data-working": running || sending,
2273
+ "data-working": running2 || sending,
2275
2274
  ref: rootRef,
2276
2275
  ...attachmentsEnabled ? fileInput.dropProps : {},
2277
2276
  children: [
2278
2277
  attachmentsEnabled && /* @__PURE__ */ jsx15(
2279
2278
  "input",
2280
2279
  {
2281
- "aria-label": alleTexte.attachFiles,
2280
+ "aria-label": allTexts.attachFiles,
2282
2281
  hidden: true,
2283
2282
  multiple: true,
2284
2283
  type: "file",
@@ -2306,8 +2305,8 @@ function ChatInputToolbar({
2306
2305
  }
2307
2306
 
2308
2307
  // packages/quassel/src/DetailModeSwitch.tsx
2309
- import { SparklesIcon as SparklesIcon3 } from "lucide-react";
2310
- import { jsx as jsx16, jsxs as jsxs9 } from "react/jsx-runtime";
2308
+ import { CircleDotIcon, EyeOffIcon, ListCollapseIcon, ListIcon, ShapesIcon, TagsIcon, TextAlignJustifyIcon } from "lucide-react";
2309
+ import { jsx as jsx16 } from "react/jsx-runtime";
2311
2310
  var DETAIL_MODES = ["off", "current", "icons", "chips", "grouped", "compact", "full"];
2312
2311
  var TEXT_KEYS = {
2313
2312
  off: "detailModeOff",
@@ -2318,57 +2317,50 @@ var TEXT_KEYS = {
2318
2317
  compact: "detailModeCompact",
2319
2318
  full: "detailModeFull"
2320
2319
  };
2320
+ var ICONS = {
2321
+ off: EyeOffIcon,
2322
+ current: CircleDotIcon,
2323
+ icons: ShapesIcon,
2324
+ chips: TagsIcon,
2325
+ grouped: ListCollapseIcon,
2326
+ compact: ListIcon,
2327
+ full: TextAlignJustifyIcon
2328
+ };
2321
2329
  var detailModeLabel = (mode, texts) => ({ ...defaultTexts, ...texts })[TEXT_KEYS[mode]];
2322
2330
  function DetailModeSwitch({
2323
2331
  mode,
2324
2332
  onChange,
2325
2333
  modes = DETAIL_MODES,
2326
- collapsible = true,
2327
2334
  texts,
2328
2335
  className
2329
2336
  }) {
2330
- const alleTexte = { ...defaultTexts, ...texts };
2337
+ const allTexts = { ...defaultTexts, ...texts };
2331
2338
  const { Button: Button2 } = useQuasselComponents();
2332
- const aktuell = modes.indexOf(mode);
2333
- const naechster = modes[(aktuell < 0 ? 0 : aktuell + 1) % modes.length];
2334
- return /* @__PURE__ */ jsxs9(
2335
- Button2,
2336
- {
2337
- "aria-label": `${alleTexte.detailModeTitle}: ${detailModeLabel(mode, texts)}`,
2338
- className,
2339
- onClick: () => onChange(naechster),
2340
- size: "sm",
2341
- title: `${alleTexte.detailModeTitle}: ${detailModeLabel(mode, texts)}`,
2342
- variant: "ghost",
2343
- children: [
2344
- /* @__PURE__ */ jsx16(SparklesIcon3, {}),
2345
- /* @__PURE__ */ jsx16("span", { className: collapsible ? "qsl:in-data-[compact=true]:hidden" : void 0, children: detailModeLabel(mode, texts) })
2346
- ]
2347
- }
2348
- );
2339
+ const current = modes.indexOf(mode);
2340
+ const next2 = modes[(current < 0 ? 0 : current + 1) % modes.length];
2341
+ const Icon = ICONS[mode];
2342
+ const title = `${allTexts.detailModeTitle}: ${detailModeLabel(mode, texts)}`;
2343
+ return /* @__PURE__ */ jsx16(Button2, { "aria-label": title, className, onClick: () => onChange(next2), size: "icon-sm", title, variant: "ghost", children: /* @__PURE__ */ jsx16(Icon, {}) });
2349
2344
  }
2350
2345
 
2351
2346
  // packages/quassel/src/TimestampSwitch.tsx
2352
- import { ClockIcon } from "lucide-react";
2353
- import { jsx as jsx17, jsxs as jsxs10 } from "react/jsx-runtime";
2354
- function TimestampSwitch({ showTimestamps, onChange, collapsible = true, texts, className }) {
2347
+ import { ClockFadingIcon, ClockIcon } from "lucide-react";
2348
+ import { jsx as jsx17 } from "react/jsx-runtime";
2349
+ function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
2355
2350
  const labels = { ...defaultTexts, ...texts };
2356
2351
  const { Button: Button2 } = useQuasselComponents();
2357
2352
  const title = showTimestamps ? labels.hideTimestamps : labels.showTimestamps;
2358
- return /* @__PURE__ */ jsxs10(
2353
+ return /* @__PURE__ */ jsx17(
2359
2354
  Button2,
2360
2355
  {
2361
2356
  "aria-label": title,
2362
2357
  "aria-pressed": showTimestamps,
2363
2358
  className,
2364
2359
  onClick: () => onChange(!showTimestamps),
2365
- size: "sm",
2360
+ size: "icon-sm",
2366
2361
  title,
2367
2362
  variant: "ghost",
2368
- children: [
2369
- /* @__PURE__ */ jsx17(ClockIcon, {}),
2370
- /* @__PURE__ */ jsx17("span", { className: collapsible ? "qsl:in-data-[compact=true]:hidden" : void 0, children: labels.timestamps })
2371
- ]
2363
+ children: showTimestamps ? /* @__PURE__ */ jsx17(ClockIcon, {}) : /* @__PURE__ */ jsx17(ClockFadingIcon, {})
2372
2364
  }
2373
2365
  );
2374
2366
  }