quassel 0.3.0 → 0.4.1

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,9 +1097,10 @@ 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,
1103
+ groupsExpandable = true,
1104
1104
  texts,
1105
1105
  toolArgumentsText = defaultArgumentsText,
1106
1106
  renderTool,
@@ -1123,9 +1123,9 @@ function ChatMessages({
1123
1123
  messageActions,
1124
1124
  toolElapsedThreshold = 3e3
1125
1125
  }) {
1126
- const alleTexte = { ...defaultTexts, ...texts };
1126
+ const allTexts = { ...defaultTexts, ...texts };
1127
1127
  const { Button: Button2 } = useQuasselComponents();
1128
- const scrollBereich = useRef3(null);
1128
+ const scrollArea = useRef3(null);
1129
1129
  const threadRef = useRef3(null);
1130
1130
  const scrollbarDragging = useRef3(false);
1131
1131
  const touchY = useRef3(void 0);
@@ -1140,23 +1140,23 @@ function ChatMessages({
1140
1140
  const timer = window.setInterval(() => setNow(Date.now()), 15e3);
1141
1141
  return () => window.clearInterval(timer);
1142
1142
  }, [showTimestamps, timestampOptions?.format]);
1143
- const angesagt = useRef3(new Set(messages.map((message) => message.key)));
1143
+ const announced = useRef3(new Set(messages.map((message) => message.key)));
1144
1144
  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) {
1145
+ 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)));
1146
+ if (due.length === 0) {
1147
1147
  return;
1148
1148
  }
1149
- angesagt.current = /* @__PURE__ */ new Set([...angesagt.current, ...faellig.map((message) => message.key)]);
1150
- if (announce2 === false || faellig.length > hoechstensAnsagenAufEinmal) {
1149
+ announced.current = /* @__PURE__ */ new Set([...announced.current, ...due.map((message) => message.key)]);
1150
+ if (announce2 === false || due.length > maxAnnouncementsAtOnce) {
1151
1151
  return;
1152
1152
  }
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]);
1153
+ due.map((message) => announce2 ? announce2({ kind: message.role === "action" ? "action" : "reply", message }) : markdownPlainText(message.text)).forEach((text) => text && announce(text));
1154
+ }, [messages, running2, announce2]);
1155
1155
  const updateJumpVisibility = useCallback2((viewport) => {
1156
1156
  if (viewport.clientHeight > 0) setShowJumpToEnd(viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop > jumpToEndThreshold);
1157
1157
  }, [jumpToEndThreshold]);
1158
1158
  useLayoutEffect(() => sendScope?.subscribe(() => {
1159
- const viewport = scrollBereich.current;
1159
+ const viewport = scrollArea.current;
1160
1160
  if (!viewport) return;
1161
1161
  scroll.jump(viewport);
1162
1162
  updateJumpVisibility(viewport);
@@ -1169,16 +1169,16 @@ function ChatMessages({
1169
1169
  }
1170
1170
  scroll.pause(viewport);
1171
1171
  };
1172
- const pruefeEnde = useCallback2(() => {
1173
- const bereich = scrollBereich.current;
1174
- if (bereich) {
1175
- scroll.scroll(bereich, scrollbarDragging.current);
1176
- updateJumpVisibility(bereich);
1172
+ const checkEnd = useCallback2(() => {
1173
+ const area = scrollArea.current;
1174
+ if (area) {
1175
+ scroll.scroll(area, scrollbarDragging.current);
1176
+ updateJumpVisibility(area);
1177
1177
  }
1178
1178
  }, [scroll, updateJumpVisibility]);
1179
1179
  useEffect3(() => {
1180
1180
  const finishDrag = () => {
1181
- if (scrollbarDragging.current && scrollBereich.current) scroll.scroll(scrollBereich.current, true);
1181
+ if (scrollbarDragging.current && scrollArea.current) scroll.scroll(scrollArea.current, true);
1182
1182
  scrollbarDragging.current = false;
1183
1183
  };
1184
1184
  window.addEventListener("pointerup", finishDrag);
@@ -1189,27 +1189,27 @@ function ChatMessages({
1189
1189
  };
1190
1190
  }, [scroll]);
1191
1191
  useLayoutEffect(() => {
1192
- const bereich = scrollBereich.current;
1193
- if (!bereich) return;
1192
+ const area = scrollArea.current;
1193
+ if (!area) return;
1194
1194
  const align = () => {
1195
- scroll.layout(bereich, scrollbarDragging.current);
1196
- updateJumpVisibility(bereich);
1195
+ scroll.layout(area, scrollbarDragging.current);
1196
+ updateJumpVisibility(area);
1197
1197
  };
1198
1198
  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();
1199
+ const observer = new ResizeObserver(align);
1200
+ observer.observe(area);
1201
+ if (threadRef.current) observer.observe(threadRef.current, { box: "border-box" });
1202
+ return () => observer.disconnect();
1203
1203
  });
1204
1204
  const visible = useMemo3(
1205
1205
  () => {
1206
- const letzterFremder = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
1206
+ const lastNonUser = messages.reduce((last, message, index) => message.role === "user" ? last : index, -1);
1207
1207
  return messages.filter((message, index) => {
1208
- if (dock !== void 0 && istOffeneAktion(message)) {
1208
+ if (dock !== void 0 && isOpenAction(message)) {
1209
1209
  return false;
1210
1210
  }
1211
- if (detailMode === "current" && istSchritt(message)) {
1212
- return running && index === letzterFremder && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
1211
+ if (detailMode === "current" && isStep(message)) {
1212
+ return running2 && index === lastNonUser && (message.role === "thinking" ? !message.closed : stepState(message) === "running");
1213
1213
  }
1214
1214
  if ((message.role === "thinking" || message.role === "assistant") && message.text.trim() === "" && !message.attachments?.length) {
1215
1215
  return false;
@@ -1220,54 +1220,55 @@ function ChatMessages({
1220
1220
  return true;
1221
1221
  });
1222
1222
  },
1223
- [messages, detailMode, running, dock]
1223
+ [messages, detailMode, running2, dock]
1224
1224
  );
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: [
1225
+ const openActions = dock ? messages.filter(isOpenAction) : [];
1226
+ 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
1227
  /* @__PURE__ */ jsx13("time", { dateTime: messageDate(at)?.toISOString(), className: cn(
1228
1228
  "qsl:flex-none qsl:text-meta qsl:tabular-nums qsl:text-muted-foreground qsl:opacity-85",
1229
1229
  timestampOptions?.format && timestampOptions.format !== "time" ? "qsl:w-[clamp(60px,20%,120px)]" : "qsl:w-[34px]"
1230
1230
  ), 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 = [];
1231
+ /* @__PURE__ */ jsx13("div", { className: "qsl:min-w-0 qsl:flex-1 qsl:[&>*:first-child]:mt-0", children: content })
1232
+ ] }, `time-${key}`) : content;
1233
+ const chipMode = detailMode === "current" || detailMode === "chips" || detailMode === "icons";
1234
+ const elapsedAfter = running2 && toolElapsedThreshold !== false ? toolElapsedThreshold : void 0;
1235
+ const groupMode = detailMode === "grouped";
1236
+ const blocks = [];
1237
1237
  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(
1238
+ let group = [];
1239
+ const closeGroup = () => {
1240
+ if (group.length > 0) {
1241
+ const runningStep = running2 ? [...group].reverse().find((message) => ["running", "thinking"].includes(stepState(message))) : void 0;
1242
+ blocks.push(withTime(
1243
+ groupMode ? /* @__PURE__ */ jsx13(
1244
1244
  StepGroup,
1245
1245
  {
1246
1246
  elapsedAfter,
1247
- expandierbar: stepsExpandable,
1248
- laufenderSchritt,
1249
- messages: gruppe,
1250
- texts: alleTexte,
1247
+ expandable: stepsExpandable,
1248
+ groupExpandable: groupsExpandable,
1249
+ runningStep,
1250
+ messages: group,
1251
+ texts: allTexts,
1251
1252
  toolArgumentsText
1252
1253
  },
1253
- gruppe[0].key
1254
+ group[0].key
1254
1255
  ) : /* @__PURE__ */ jsx13(
1255
1256
  StepRow,
1256
1257
  {
1257
1258
  elapsedAfter,
1258
- expandierbar: stepsExpandable,
1259
- messages: gruppe,
1260
- mitText: detailMode !== "icons",
1261
- texts: alleTexte,
1259
+ expandable: stepsExpandable,
1260
+ messages: group,
1261
+ withText: detailMode !== "icons",
1262
+ texts: allTexts,
1262
1263
  toolArgumentsText,
1263
- toolLabel: detailMode === "current" && !stepsExpandable ? alleTexte.currentTool : void 0
1264
+ toolLabel: detailMode === "current" && !stepsExpandable ? allTexts.currentTool : void 0
1264
1265
  },
1265
- gruppe[0].key
1266
+ group[0].key
1266
1267
  ),
1267
- gruppe[0].key,
1268
- gruppe[0].at
1268
+ group[0].key,
1269
+ group[0].at
1269
1270
  ));
1270
- gruppe = [];
1271
+ group = [];
1271
1272
  }
1272
1273
  };
1273
1274
  visible.forEach((message, index) => {
@@ -1275,38 +1276,38 @@ function ChatMessages({
1275
1276
  if (date) {
1276
1277
  const day = timestampDay(date, timestampOptions);
1277
1278
  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}`));
1279
+ closeGroup();
1280
+ 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
1281
  lastDay = day.key;
1281
1282
  }
1282
1283
  }
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),
1284
+ const customRendering = message.role === "tool" && message.tool && detailMode !== "current" ? renderTool?.(message.tool) : void 0;
1285
+ if (customRendering !== void 0) {
1286
+ closeGroup();
1287
+ blocks.push(withTime(
1288
+ /* @__PURE__ */ jsx13("div", { className: stepClasses, children: customRendering }, message.key),
1288
1289
  message.key,
1289
1290
  message.at
1290
1291
  ));
1291
1292
  return;
1292
1293
  }
1293
- if ((chipModus || gruppenModus) && istSchritt(message)) {
1294
- gruppe = [...gruppe, message];
1294
+ if ((chipMode || groupMode) && isStep(message)) {
1295
+ group = [...group, message];
1295
1296
  return;
1296
1297
  }
1297
- schliesseGruppe();
1298
- const dicht = istSchritt(message) && istSchritt(visible[index - 1]);
1299
- bloecke.push(mitZeit(
1298
+ closeGroup();
1299
+ const dense = isStep(message) && isStep(visible[index - 1]);
1300
+ blocks.push(withTime(
1300
1301
  /* @__PURE__ */ jsx13(
1301
1302
  Bubble,
1302
1303
  {
1303
1304
  detailMode,
1304
- dicht,
1305
+ dense,
1305
1306
  elapsedAfter,
1306
- expandierbar: stepsExpandable,
1307
+ expandable: stepsExpandable,
1307
1308
  message,
1308
1309
  plain: owner != null && message.sender === owner,
1309
- texts: alleTexte,
1310
+ texts: allTexts,
1310
1311
  toolArgumentsText,
1311
1312
  renderAction,
1312
1313
  onDismissAction,
@@ -1317,11 +1318,11 @@ function ChatMessages({
1317
1318
  ),
1318
1319
  message.key,
1319
1320
  message.at,
1320
- dicht
1321
+ dense
1321
1322
  ));
1322
1323
  });
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: {
1324
+ closeGroup();
1325
+ 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
1326
  ...appearanceStyle(appearance),
1326
1327
  "--qsl-chat-content-max-width": typeof maxWidth === "number" ? `${maxWidth}px` : maxWidth,
1327
1328
  "--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
@@ -1329,7 +1330,7 @@ function ChatMessages({
1329
1330
  /* @__PURE__ */ jsx13(
1330
1331
  "div",
1331
1332
  {
1332
- "aria-label": alleTexte.transcript,
1333
+ "aria-label": allTexts.transcript,
1333
1334
  className: cn(
1334
1335
  "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
1336
  "qsl:[--qsl-fade-edge:calc(100%_-_var(--qsl-composer-height,0px))]",
@@ -1339,7 +1340,7 @@ function ChatMessages({
1339
1340
  scrollerClassName
1340
1341
  ),
1341
1342
  "data-quassel-transcript": "",
1342
- onScroll: pruefeEnde,
1343
+ onScroll: checkEnd,
1343
1344
  onWheel: (event) => {
1344
1345
  if (!event.ctrlKey && event.deltaY < 0) pauseUp(event.currentTarget, event.target);
1345
1346
  },
@@ -1355,20 +1356,20 @@ function ChatMessages({
1355
1356
  touchY.current = event.touches[0]?.clientY;
1356
1357
  },
1357
1358
  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;
1359
+ const next2 = event.touches[0]?.clientY;
1360
+ if (next2 !== void 0 && touchY.current !== void 0 && next2 > touchY.current) pauseUp(event.currentTarget, event.target);
1361
+ touchY.current = next2;
1361
1362
  },
1362
1363
  role: "region",
1363
1364
  tabIndex: 0,
1364
1365
  ref: (element) => {
1365
- scrollBereich.current = element;
1366
+ scrollArea.current = element;
1366
1367
  scrollerRef?.(element);
1367
1368
  },
1368
- children: visible.length === 0 && !running ? emptyState ?? null : /* @__PURE__ */ jsxs6(
1369
+ children: visible.length === 0 && !running2 ? emptyState ?? null : /* @__PURE__ */ jsxs6(
1369
1370
  "div",
1370
1371
  {
1371
- "aria-busy": running || void 0,
1372
+ "aria-busy": running2 || void 0,
1372
1373
  className: cn(
1373
1374
  "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
1375
  "qsl:pb-[calc(var(--qsl-composer-height,0px)_+_max(3.25em,40px))]",
@@ -1376,52 +1377,52 @@ function ChatMessages({
1376
1377
  ),
1377
1378
  ref: threadRef,
1378
1379
  children: [
1379
- bloecke,
1380
- running && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: alleTexte.working }))
1380
+ blocks,
1381
+ running2 && (working ?? /* @__PURE__ */ jsx13(WorkingScenes, { label: allTexts.working }))
1381
1382
  ]
1382
1383
  }
1383
1384
  )
1384
1385
  }
1385
1386
  ),
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);
1387
+ 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: () => {
1388
+ if (scrollArea.current) {
1389
+ scroll.jump(scrollArea.current);
1390
+ updateJumpVisibility(scrollArea.current);
1390
1391
  }
1391
1392
  }, 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)) }),
1393
+ dock && openActions.length > 0 && createPortal(
1394
+ /* @__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
1395
  dock
1395
1396
  )
1396
1397
  ] });
1397
- return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: alleTexte, children: verlauf }) });
1398
+ return /* @__PURE__ */ jsx13(MarkdownLinks, { onLinkClick, children: /* @__PURE__ */ jsx13(MarkdownCodeBlocks, { options: codeBlockOptions, texts: allTexts, children: transcript }) });
1398
1399
  }
1399
1400
  function StepRow({
1400
1401
  messages,
1401
- mitText,
1402
- expandierbar,
1402
+ withText,
1403
+ expandable,
1403
1404
  elapsedAfter,
1404
1405
  texts,
1405
1406
  toolArgumentsText,
1406
1407
  toolLabel
1407
1408
  }) {
1408
1409
  const [detail, setDetail] = useState4();
1409
- const offen = expandierbar && detail && messages.find((message) => message.key === detail.key);
1410
+ const open = expandable && detail && messages.find((message) => message.key === detail.key);
1410
1411
  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
1412
  messages.map((message) => {
1412
1413
  const thinking = message.role === "thinking";
1413
1414
  const tool = message.tool;
1414
1415
  const state = stepState(message);
1415
- const running = state === "running" || state === "thinking";
1416
+ const running2 = state === "running" || state === "thinking";
1416
1417
  const label = thinking ? texts.thinkingChip : toolLabel ?? (tool?.name || message.text || texts.toolChip);
1417
1418
  const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1418
1419
  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 }) })
1420
+ withText && /* @__PURE__ */ jsx13("span", { className: "qsl:min-w-0 qsl:self-baseline qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap", children: label }),
1421
+ withText && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, className: "qsl:flex-none qsl:self-baseline", message, texts }),
1422
+ 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
1423
  ] });
1423
- const classes = cn(chipClasses, !mitText && "qsl:p-1", running && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
1424
- if (!expandierbar) {
1424
+ const classes = cn(chipClasses, !withText && "qsl:p-1", running2 && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none");
1425
+ if (!expandable) {
1425
1426
  return /* @__PURE__ */ jsx13("span", { className: classes, "data-kind": thinking ? "thinking" : "tool", "data-state": state, "data-step": "chip", title: label, children: content }, message.key);
1426
1427
  }
1427
1428
  return /* @__PURE__ */ jsx13(
@@ -1440,10 +1441,10 @@ function StepRow({
1440
1441
  message.key
1441
1442
  );
1442
1443
  }),
1443
- offen && detail && /* @__PURE__ */ jsx13(
1444
+ open && detail && /* @__PURE__ */ jsx13(
1444
1445
  StepPopover,
1445
1446
  {
1446
- message: offen,
1447
+ message: open,
1447
1448
  position: detail.position,
1448
1449
  texts,
1449
1450
  toolArgumentsText,
@@ -1454,58 +1455,65 @@ function StepRow({
1454
1455
  }
1455
1456
  function StepGroup({
1456
1457
  messages,
1457
- laufenderSchritt,
1458
+ runningStep,
1458
1459
  elapsedAfter,
1459
- expandierbar,
1460
+ expandable,
1461
+ groupExpandable,
1460
1462
  texts,
1461
1463
  toolArgumentsText
1462
1464
  }) {
1463
- const [offen, setOffen] = useState4(false);
1464
- const kopf = useRef3(null);
1465
- const vonUntenEingeklappt = useRef3(false);
1465
+ const [open, setOpen] = useState4(false);
1466
+ const header = useRef3(null);
1467
+ const collapsedFromBottom = useRef3(false);
1466
1468
  useLayoutEffect(() => {
1467
- if (!offen && vonUntenEingeklappt.current) {
1468
- vonUntenEingeklappt.current = false;
1469
- kopf.current?.scrollIntoView({ block: "nearest" });
1469
+ if (!open && collapsedFromBottom.current) {
1470
+ collapsedFromBottom.current = false;
1471
+ header.current?.scrollIntoView({ block: "nearest" });
1470
1472
  }
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);
1473
+ }, [open]);
1474
+ const active = runningStep !== void 0;
1475
+ const failed = messages.some((message) => message.tool?.isError);
1476
+ const count = messages.length === 1 ? texts.stepGroupOne : texts.stepGroupMany.replace("{count}", String(messages.length));
1477
+ const runningLabel = runningStep && (runningStep.role === "thinking" ? texts.thinkingChip : runningStep.tool?.name || runningStep.text || texts.toolChip);
1478
+ const summary = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1479
+ /* @__PURE__ */ jsx13(LayersIcon, { className: failed ? traceErrorIconClasses : traceIconClasses, size: 12 }),
1480
+ /* @__PURE__ */ jsxs6("span", { className: cn(traceLineClasses, "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap"), children: [
1481
+ count,
1482
+ runningLabel && /* @__PURE__ */ jsxs6("span", { className: traceRunningClasses, children: [
1483
+ runningLabel,
1484
+ " ",
1485
+ texts.toolRunning
1486
+ ] }),
1487
+ runningStep && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: runningStep, texts })
1488
+ ] })
1489
+ ] });
1490
+ if (!groupExpandable) {
1491
+ return /* @__PURE__ */ jsx13("div", { className: stepClasses, "data-step": "group", children: /* @__PURE__ */ jsx13("div", { className: cn(traceClasses, "qsl:items-center qsl:py-1 qsl:pr-2 qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"), children: summary }) });
1492
+ }
1476
1493
  return /* @__PURE__ */ jsxs6("div", { className: stepClasses, "data-step": "group", children: [
1477
1494
  /* @__PURE__ */ jsxs6(
1478
1495
  "button",
1479
1496
  {
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,
1497
+ "aria-expanded": open,
1498
+ className: cn(traceClasses, traceButtonClasses, "qsl:items-center qsl:pl-1", active && "qsl:animate-fade-pulse qsl:motion-reduce:animate-none"),
1499
+ onClick: () => setOpen((value) => !value),
1500
+ ref: header,
1484
1501
  type: "button",
1485
1502
  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 }),
1488
- /* @__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,
1492
- " ",
1493
- texts.toolRunning
1494
- ] }),
1495
- laufenderSchritt && /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message: laufenderSchritt, texts })
1496
- ] })
1503
+ /* @__PURE__ */ jsx13(ChevronRightIcon, { className: cn("qsl:flex-none qsl:opacity-60 qsl:transition-transform", open && "qsl:rotate-90"), size: 12 }),
1504
+ summary
1497
1505
  ]
1498
1506
  }
1499
1507
  ),
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(
1508
+ 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)) }),
1509
+ open && messages.length >= collapseAtBottomFrom && /* @__PURE__ */ jsxs6(
1502
1510
  "button",
1503
1511
  {
1504
1512
  className: cn(traceClasses, traceButtonClasses, "qsl:mt-0.5 qsl:items-center qsl:pl-1"),
1505
1513
  "data-step": "group-collapse",
1506
1514
  onClick: () => {
1507
- vonUntenEingeklappt.current = true;
1508
- setOffen(false);
1515
+ collapsedFromBottom.current = true;
1516
+ setOpen(false);
1509
1517
  },
1510
1518
  type: "button",
1511
1519
  children: [
@@ -1520,7 +1528,7 @@ function TraceLine({
1520
1528
  message,
1521
1529
  className,
1522
1530
  elapsedAfter,
1523
- expandierbar,
1531
+ expandable,
1524
1532
  texts,
1525
1533
  toolArgumentsText
1526
1534
  }) {
@@ -1528,18 +1536,18 @@ function TraceLine({
1528
1536
  const thinking = message.role === "thinking";
1529
1537
  const tool = message.tool;
1530
1538
  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: [
1539
+ const line = cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap", "qsl:overflow-hidden qsl:text-ellipsis qsl:whitespace-nowrap");
1540
+ const content = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1533
1541
  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: [
1542
+ /* @__PURE__ */ jsxs6("span", { className: line, children: [
1535
1543
  message.text,
1536
1544
  tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
1537
1545
  /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
1538
1546
  ] })
1539
1547
  ] });
1540
1548
  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 });
1549
+ if (!expandable) {
1550
+ return /* @__PURE__ */ jsx13("div", { className: classes, ...marks, children: content });
1543
1551
  }
1544
1552
  return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1545
1553
  /* @__PURE__ */ jsx13(
@@ -1551,7 +1559,7 @@ function TraceLine({
1551
1559
  ...marks,
1552
1560
  onClick: (event) => setDetailPosition({ x: event.clientX, y: event.clientY }),
1553
1561
  type: "button",
1554
- children: inhalt
1562
+ children: content
1555
1563
  }
1556
1564
  ),
1557
1565
  detailPosition && /* @__PURE__ */ jsx13(
@@ -1579,9 +1587,9 @@ function Bubble({
1579
1587
  message,
1580
1588
  plain,
1581
1589
  detailMode,
1582
- dicht,
1590
+ dense,
1583
1591
  elapsedAfter,
1584
- expandierbar,
1592
+ expandable,
1585
1593
  texts,
1586
1594
  toolArgumentsText,
1587
1595
  renderAction,
@@ -1589,23 +1597,23 @@ function Bubble({
1589
1597
  bubbleOptions,
1590
1598
  messageActions
1591
1599
  }) {
1592
- const schritt = dicht ? denseStepClasses : stepClasses;
1600
+ const stepSpacing = dense ? denseStepClasses : stepClasses;
1593
1601
  if (message.role === "thinking" || message.role === "tool" && message.tool) {
1594
1602
  if (detailMode === "compact") {
1595
- return /* @__PURE__ */ jsx13(TraceLine, { className: schritt, elapsedAfter, expandierbar, message, texts, toolArgumentsText });
1603
+ return /* @__PURE__ */ jsx13(TraceLine, { className: stepSpacing, elapsedAfter, expandable, message, texts, toolArgumentsText });
1596
1604
  }
1597
1605
  const thinking = message.role === "thinking";
1598
1606
  const tool = message.tool;
1599
1607
  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: [
1608
+ const line = /* @__PURE__ */ jsxs6(Fragment2, { children: [
1601
1609
  message.text,
1602
1610
  tool && tool.result === void 0 && /* @__PURE__ */ jsx13("span", { className: traceRunningClasses, children: texts.toolRunning }),
1603
1611
  /* @__PURE__ */ jsx13(ToolElapsed, { after: elapsedAfter, message, texts })
1604
1612
  ] });
1605
- return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, schritt), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
1613
+ return /* @__PURE__ */ jsxs6("div", { className: cn(traceClasses, stepSpacing), "data-kind": thinking ? "thinking" : "tool", "data-step": "detail", children: [
1606
1614
  icon,
1607
1615
  /* @__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 }),
1616
+ /* @__PURE__ */ jsx13("div", { className: cn(traceLineClasses, thinking && "qsl:font-sans qsl:italic qsl:whitespace-pre-wrap"), children: line }),
1609
1617
  tool && /* @__PURE__ */ jsxs6("div", { className: "qsl:mt-0.5 qsl:border-l-2 qsl:border-border-soft qsl:pl-2", children: [
1610
1618
  /* @__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
1619
  tool.result !== void 0 && /* @__PURE__ */ jsx13("pre", { className: cn(
@@ -1617,10 +1625,10 @@ function Bubble({
1617
1625
  ] });
1618
1626
  }
1619
1627
  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 }) });
1628
+ return /* @__PURE__ */ jsx13("div", { className: stepSpacing, children: /* @__PURE__ */ jsx13(ActionContent, { action: message.action, onDismissAction, renderAction, text: message.text, texts }) });
1621
1629
  }
1622
1630
  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: [
1631
+ 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
1632
  /* @__PURE__ */ jsx13(Markdown, { text: message.text }),
1625
1633
  /* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message })
1626
1634
  ] });
@@ -1642,12 +1650,12 @@ function Bubble({
1642
1650
  /* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
1643
1651
  ] });
1644
1652
  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 });
1653
+ 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
1654
  return /* @__PURE__ */ jsx13(
1647
1655
  "div",
1648
1656
  {
1649
1657
  "data-chat-text-cursor": cursor,
1650
- className: cn(schritt, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
1658
+ className: cn(stepSpacing, "qsl:flex", side === "end" ? "qsl:justify-end" : "qsl:justify-start"),
1651
1659
  "data-message": message.role === "user" && !message.bubble && variant === "default" ? "user" : "bubble",
1652
1660
  "data-side": side,
1653
1661
  children: /* @__PURE__ */ jsx13(
@@ -1706,8 +1714,8 @@ function ChatPanel({
1706
1714
  appearance,
1707
1715
  scrollOnSend = false
1708
1716
  }) {
1709
- const wurzel = useRef4(null);
1710
- const eingabe = useRef4(null);
1717
+ const root = useRef4(null);
1718
+ const composerRef = useRef4(null);
1711
1719
  const [dock, setDock] = useState5(null);
1712
1720
  const hasComposer = composer !== void 0;
1713
1721
  const scrollOnSendRef = useRef4(scrollOnSend);
@@ -1716,23 +1724,23 @@ function ChatPanel({
1716
1724
  scrollOnSendRef.current = scrollOnSend;
1717
1725
  }, [scrollOnSend]);
1718
1726
  useEffect4(() => {
1719
- const element = eingabe.current;
1720
- const rahmen = wurzel.current;
1721
- if (!element || !rahmen) {
1727
+ const element = composerRef.current;
1728
+ const frame = root.current;
1729
+ if (!element || !frame) {
1722
1730
  return;
1723
1731
  }
1724
- const messen = () => {
1725
- rahmen.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
1726
- rahmen.style.setProperty("--qsl-panel-height", `${rahmen.offsetHeight}px`);
1732
+ const measure = () => {
1733
+ frame.style.setProperty("--qsl-composer-height", `${element.offsetHeight}px`);
1734
+ frame.style.setProperty("--qsl-panel-height", `${frame.offsetHeight}px`);
1727
1735
  };
1728
- const beobachter = new ResizeObserver(messen);
1729
- beobachter.observe(element);
1730
- beobachter.observe(rahmen);
1731
- messen();
1736
+ const observer = new ResizeObserver(measure);
1737
+ observer.observe(element);
1738
+ observer.observe(frame);
1739
+ measure();
1732
1740
  return () => {
1733
- beobachter.disconnect();
1734
- rahmen.style.removeProperty("--qsl-composer-height");
1735
- rahmen.style.removeProperty("--qsl-panel-height");
1741
+ observer.disconnect();
1742
+ frame.style.removeProperty("--qsl-composer-height");
1743
+ frame.style.removeProperty("--qsl-panel-height");
1736
1744
  };
1737
1745
  }, [hasComposer]);
1738
1746
  return /* @__PURE__ */ jsx14(ChatSendContext.Provider, { value: sendScope, children: /* @__PURE__ */ jsx14(ChatActionDock.Provider, { value: hasComposer ? dock : void 0, children: /* @__PURE__ */ jsxs7(
@@ -1747,7 +1755,7 @@ function ChatPanel({
1747
1755
  "--qsl-chat-horizontal-padding": typeof horizontalPadding === "number" ? `${horizontalPadding}px` : horizontalPadding
1748
1756
  },
1749
1757
  ref: (element) => {
1750
- wurzel.current = element;
1758
+ root.current = element;
1751
1759
  if (typeof nodeRef === "function") {
1752
1760
  nodeRef(element);
1753
1761
  } else if (nodeRef) {
@@ -1756,7 +1764,7 @@ function ChatPanel({
1756
1764
  },
1757
1765
  children: [
1758
1766
  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: [
1767
+ 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
1768
  /* @__PURE__ */ jsx14("div", { children: /* @__PURE__ */ jsx14(
1761
1769
  "div",
1762
1770
  {
@@ -1834,7 +1842,7 @@ var attachmentMediaClasses = "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondar
1834
1842
  function ChatInputToolbar({
1835
1843
  onSend,
1836
1844
  onStop,
1837
- running = false,
1845
+ running: running2 = false,
1838
1846
  disabled = false,
1839
1847
  sendDisabled = false,
1840
1848
  layout = "card",
@@ -1859,7 +1867,7 @@ function ChatInputToolbar({
1859
1867
  onAutoFocusSettled
1860
1868
  }) {
1861
1869
  const sendScope = useContext4(ChatSendContext);
1862
- const alleTexte = { ...defaultTexts, ...texts };
1870
+ const allTexts = { ...defaultTexts, ...texts };
1863
1871
  const { Button: Button2, StopButton: StopButton2, Toggle: Toggle2 } = useQuasselComponents();
1864
1872
  const [draft, setDraft] = useState7(initialValue);
1865
1873
  const [sending, setSending] = useState7(false);
@@ -1883,11 +1891,11 @@ function ChatInputToolbar({
1883
1891
  const addFiles = (files) => {
1884
1892
  if (!files.length) return;
1885
1893
  if (disabled || pendingSend.current) {
1886
- setAttachmentError(alleTexte.attachmentsWhileBusy);
1894
+ setAttachmentError(allTexts.attachmentsWhileBusy);
1887
1895
  return;
1888
1896
  }
1889
1897
  try {
1890
- validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, alleTexte);
1898
+ validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), files, allTexts);
1891
1899
  } catch (error) {
1892
1900
  setAttachmentError(error instanceof Error ? error.message : String(error));
1893
1901
  return;
@@ -1901,17 +1909,17 @@ function ChatInputToolbar({
1901
1909
  () => updateAttachments((current) => current.map((item) => item.id === entry.id ? {
1902
1910
  ...entry,
1903
1911
  status: "error",
1904
- error: alleTexte.attachmentReadFailed
1912
+ error: allTexts.attachmentReadFailed
1905
1913
  } : item))
1906
1914
  );
1907
1915
  }
1908
1916
  };
1909
1917
  const rootRef = useRef6(null);
1910
- const eingabeRef = useRef6(null);
1918
+ const textareaRef = useRef6(null);
1911
1919
  const autoFocusSettled = useRef6(false);
1912
1920
  useEffect5(() => {
1913
1921
  if (!autoFocus || autoFocusSettled.current) return;
1914
- const input2 = eingabeRef.current;
1922
+ const input2 = textareaRef.current;
1915
1923
  if (!input2) return;
1916
1924
  let frame = 0;
1917
1925
  const settle = () => {
@@ -1960,7 +1968,7 @@ function ChatInputToolbar({
1960
1968
  disabled: disabled || sending,
1961
1969
  onFiles: addFiles,
1962
1970
  onPasteText: (text) => {
1963
- const input2 = eingabeRef.current;
1971
+ const input2 = textareaRef.current;
1964
1972
  if (!input2 || disabled || pendingSend.current) return;
1965
1973
  const start = input2.selectionStart;
1966
1974
  const end = input2.selectionEnd;
@@ -1997,11 +2005,11 @@ function ChatInputToolbar({
1997
2005
  insert: (text) => {
1998
2006
  pendingFocus.current = true;
1999
2007
  changeDraft(text);
2000
- const element = eingabeRef.current;
2008
+ const element = textareaRef.current;
2001
2009
  element?.focus();
2002
2010
  if (element?.value === text) element.setSelectionRange(text.length, text.length);
2003
2011
  },
2004
- focus: () => eingabeRef.current?.focus(),
2012
+ focus: () => textareaRef.current?.focus(),
2005
2013
  submit: (onSubmit, options) => send(onSubmit, options?.allowEmpty),
2006
2014
  reset: () => {
2007
2015
  generation.current += 1;
@@ -2024,31 +2032,31 @@ function ChatInputToolbar({
2024
2032
  return;
2025
2033
  }
2026
2034
  pendingFocus.current = false;
2027
- const element = eingabeRef.current;
2035
+ const element = textareaRef.current;
2028
2036
  if (element) {
2029
2037
  element.focus();
2030
2038
  element.setSelectionRange(element.value.length, element.value.length);
2031
2039
  }
2032
2040
  }, [draft, sending]);
2033
2041
  const applyAutoGrow = useCallback3(() => {
2034
- const element = eingabeRef.current;
2042
+ const element = textareaRef.current;
2035
2043
  if (!element || inputMaxRows === void 0) {
2036
2044
  return;
2037
2045
  }
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;
2046
+ const style = getComputedStyle(element);
2047
+ const lineHeight = Number.parseFloat(style.lineHeight) || Number.parseFloat(style.fontSize) * 1.4;
2048
+ const padding = Number.parseFloat(style.paddingTop) + Number.parseFloat(style.paddingBottom);
2049
+ const maximum = lineHeight * Math.max(inputMaxRows, inputRows) + padding;
2042
2050
  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";
2051
+ const contentHeight = element.scrollHeight;
2052
+ element.style.height = `${Math.min(contentHeight, maximum)}px`;
2053
+ element.style.overflowY = contentHeight > maximum ? "auto" : "hidden";
2046
2054
  }, [inputMaxRows, inputRows]);
2047
2055
  useEffect5(() => {
2048
2056
  applyAutoGrow();
2049
2057
  }, [draft, applyAutoGrow]);
2050
2058
  useEffect5(() => {
2051
- const element = eingabeRef.current;
2059
+ const element = textareaRef.current;
2052
2060
  if (!element || inputMaxRows === void 0) {
2053
2061
  return;
2054
2062
  }
@@ -2069,9 +2077,9 @@ function ChatInputToolbar({
2069
2077
  }
2070
2078
  const update = () => setCompact(element.clientWidth < COMPACT_WIDTH_PX);
2071
2079
  update();
2072
- const beobachter = new ResizeObserver(update);
2073
- beobachter.observe(element);
2074
- return () => beobachter.disconnect();
2080
+ const observer = new ResizeObserver(update);
2081
+ observer.observe(element);
2082
+ return () => observer.disconnect();
2075
2083
  }, []);
2076
2084
  const hasAttachments = attachments.length > 0;
2077
2085
  useEffect5(() => {
@@ -2080,7 +2088,7 @@ function ChatInputToolbar({
2080
2088
  const capabilityError = hasAttachments && attachmentCapabilitiesError ? attachmentCapabilitiesError : attachmentCapabilities ? attachmentCapabilityError(attachments.map((entry) => ({
2081
2089
  name: entry.file.name,
2082
2090
  mediaType: attachmentMediaType(entry.file)
2083
- })), attachmentCapabilities, alleTexte) : void 0;
2091
+ })), attachmentCapabilities, allTexts) : void 0;
2084
2092
  const hasContent = draft.trim().length > 0 || attachments.length > 0;
2085
2093
  const preparing = attachments.some((entry) => entry.status === "reading");
2086
2094
  const attachmentBlocked = attachments.some((entry) => entry.status !== "ready");
@@ -2099,7 +2107,7 @@ function ChatInputToolbar({
2099
2107
  const sentRevision = draftRevision.current;
2100
2108
  const sentDraft = draft;
2101
2109
  const focusOrigin = document.activeElement;
2102
- const startedFocused = focusOrigin === eingabeRef.current || rootRef.current?.contains(focusOrigin);
2110
+ const startedFocused = focusOrigin === textareaRef.current || rootRef.current?.contains(focusOrigin);
2103
2111
  let leftComposer = false;
2104
2112
  const observeFocus = (event) => {
2105
2113
  const target = event.target;
@@ -2130,7 +2138,7 @@ function ChatInputToolbar({
2130
2138
  document.removeEventListener("pointerdown", observeFocus);
2131
2139
  if (generation.current === sentGeneration) {
2132
2140
  pendingSend.current = false;
2133
- pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== eingabeRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
2141
+ pendingFocus.current = !!startedFocused && !leftComposer && document.hasFocus() && (!toolbar || focusOrigin !== textareaRef.current) && (document.activeElement === focusOrigin || !toolbar && document.activeElement === document.body);
2134
2142
  setSending(false);
2135
2143
  }
2136
2144
  }
@@ -2141,28 +2149,28 @@ function ChatInputToolbar({
2141
2149
  if (!event.repeat) void send();
2142
2150
  }
2143
2151
  };
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: [
2152
+ 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
2153
  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
2154
  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
2155
  /* @__PURE__ */ jsxs8("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:pr-8 qsl:[overflow-wrap:anywhere]", children: [
2148
2156
  /* @__PURE__ */ jsx15("span", { className: "qsl:font-medium", title: entry.file.name, children: entry.file.name }),
2149
2157
  /* @__PURE__ */ jsx15("small", { children: formatAttachmentSize(entry.file.size) }),
2150
- entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: alleTexte.attachmentPreparing }),
2158
+ entry.status === "reading" && /* @__PURE__ */ jsx15("small", { role: "status", children: allTexts.attachmentPreparing }),
2151
2159
  entry.status === "error" && /* @__PURE__ */ jsx15("small", { role: "alert", children: entry.error })
2152
2160
  ] }),
2153
2161
  /* @__PURE__ */ jsx15(
2154
2162
  Button2,
2155
2163
  {
2156
- "aria-label": fillText(alleTexte.removeAttachment, { name: entry.file.name }),
2164
+ "aria-label": fillText(allTexts.removeAttachment, { name: entry.file.name }),
2157
2165
  className: "qsl:absolute qsl:top-1 qsl:right-1",
2158
2166
  disabled: disabled || sending,
2159
2167
  onClick: () => {
2160
2168
  updateAttachments((current) => current.filter((item) => item.id !== entry.id));
2161
2169
  setAttachmentError(void 0);
2162
- eingabeRef.current?.focus();
2170
+ textareaRef.current?.focus();
2163
2171
  },
2164
2172
  size: "icon-sm",
2165
- title: fillText(alleTexte.removeAttachment, { name: entry.file.name }),
2173
+ title: fillText(allTexts.removeAttachment, { name: entry.file.name }),
2166
2174
  variant: "outline",
2167
2175
  children: /* @__PURE__ */ jsx15(XIcon3, {})
2168
2176
  }
@@ -2174,16 +2182,16 @@ function ChatInputToolbar({
2174
2182
  sendError && /* @__PURE__ */ jsx15("p", { className: errorClasses, role: "alert", children: sendError }),
2175
2183
  failedSubmissions.map((failed) => /* @__PURE__ */ jsxs8("div", { className: cn(errorClasses, "qsl:flex qsl:flex-wrap qsl:items-center qsl:gap-2"), role: "status", children: [
2176
2184
  /* @__PURE__ */ jsxs8("span", { children: [
2177
- alleTexte.notSent,
2185
+ allTexts.notSent,
2178
2186
  " ",
2179
2187
  failed.draft.slice(0, 100),
2180
2188
  failed.draft.length > 100 ? " ..." : "",
2181
- failed.attachments.length > 0 ? ` ${fillText(alleTexte.notSentAttachments, { count: failed.attachments.length })}` : "",
2189
+ failed.attachments.length > 0 ? ` ${fillText(allTexts.notSentAttachments, { count: failed.attachments.length })}` : "",
2182
2190
  " "
2183
2191
  ] }),
2184
2192
  /* @__PURE__ */ jsx15(Button2, { disabled: disabled || sending, size: "sm", variant: "outline", onClick: () => {
2185
2193
  try {
2186
- validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), alleTexte);
2194
+ validateAttachmentSelection(attachmentsRef.current.map((entry) => entry.file), failed.attachments.map((entry) => entry.file), allTexts);
2187
2195
  } catch (error) {
2188
2196
  setAttachmentError(error instanceof Error ? error.message : String(error));
2189
2197
  return;
@@ -2194,22 +2202,22 @@ function ChatInputToolbar({
2194
2202
  setFailedSubmissions((current) => current.filter((entry) => entry.id !== failed.id));
2195
2203
  setAttachmentError(void 0);
2196
2204
  pendingFocus.current = true;
2197
- }, children: alleTexte.insertUnsent })
2205
+ }, children: allTexts.insertUnsent })
2198
2206
  ] }, failed.id))
2199
2207
  ] });
2200
2208
  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(
2209
+ running2 && onStop && !hasContent && /* @__PURE__ */ jsx15(StopButton2, { className: "qsl:flex-none", label: allTexts.stop, onClick: onStop, size: "icon-sm" }),
2210
+ (!running2 || !onStop || hasContent) && /* @__PURE__ */ jsx15(
2203
2211
  Button2,
2204
2212
  {
2205
- "aria-label": alleTexte.send,
2213
+ "aria-label": allTexts.send,
2206
2214
  className: "qsl:flex-none",
2207
2215
  disabled: disabled || sendDisabled || sending || attachmentBlocked || !!capabilityError || !hasContent,
2208
2216
  onClick: () => {
2209
2217
  void send();
2210
2218
  },
2211
2219
  size: "icon-sm",
2212
- title: running ? alleTexte.sendIntoRun : alleTexte.send,
2220
+ title: running2 ? allTexts.sendIntoRun : allTexts.send,
2213
2221
  children: /* @__PURE__ */ jsx15(SendIcon, { size: 14 })
2214
2222
  }
2215
2223
  )
@@ -2219,16 +2227,16 @@ function ChatInputToolbar({
2219
2227
  attachmentsEnabled && /* @__PURE__ */ jsx15(
2220
2228
  Button2,
2221
2229
  {
2222
- "aria-label": alleTexte.attachFiles,
2230
+ "aria-label": allTexts.attachFiles,
2223
2231
  disabled: disabled || sending,
2224
2232
  onClick: () => fileInput.inputRef.current?.click(),
2225
2233
  size: "icon-sm",
2226
- title: fillText(alleTexte.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
2234
+ title: fillText(allTexts.attachFilesHint, { count: MAX_CHAT_ATTACHMENTS, size: maxAttachmentSizeLabel }),
2227
2235
  variant: "ghost",
2228
2236
  children: /* @__PURE__ */ jsx15(PaperclipIcon, {})
2229
2237
  }
2230
2238
  ),
2231
- preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: alleTexte.attachmentsPreparing }),
2239
+ preparing && /* @__PURE__ */ jsx15("span", { className: cn("qsl:text-xs qsl:text-muted-foreground", inline && "qsl:sr-only"), role: "status", children: allTexts.attachmentsPreparing }),
2232
2240
  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
2241
  action.icon,
2234
2242
  action.label && /* @__PURE__ */ jsx15("span", { className: action.icon ? "qsl:in-data-[compact=true]:hidden" : void 0, children: action.label })
@@ -2253,13 +2261,13 @@ function ChatInputToolbar({
2253
2261
  {
2254
2262
  "aria-controls": inputAriaControls,
2255
2263
  className: textareaClasses,
2256
- "aria-label": alleTexte.placeholder,
2264
+ "aria-label": allTexts.placeholder,
2257
2265
  disabled: !toolbar && disabled,
2258
2266
  readOnly: toolbar && disabled,
2259
2267
  onChange: (event) => changeDraft(event.target.value),
2260
2268
  onKeyDown,
2261
- placeholder: running ? alleTexte.steeringPlaceholder : alleTexte.placeholder,
2262
- ref: eingabeRef,
2269
+ placeholder: running2 ? allTexts.steeringPlaceholder : allTexts.placeholder,
2270
+ ref: textareaRef,
2263
2271
  rows: inputRows,
2264
2272
  value: draft
2265
2273
  }
@@ -2271,14 +2279,14 @@ function ChatInputToolbar({
2271
2279
  "data-compact": compact,
2272
2280
  "data-layout": layout,
2273
2281
  "data-quassel": "",
2274
- "data-working": running || sending,
2282
+ "data-working": running2 || sending,
2275
2283
  ref: rootRef,
2276
2284
  ...attachmentsEnabled ? fileInput.dropProps : {},
2277
2285
  children: [
2278
2286
  attachmentsEnabled && /* @__PURE__ */ jsx15(
2279
2287
  "input",
2280
2288
  {
2281
- "aria-label": alleTexte.attachFiles,
2289
+ "aria-label": allTexts.attachFiles,
2282
2290
  hidden: true,
2283
2291
  multiple: true,
2284
2292
  type: "file",
@@ -2306,8 +2314,8 @@ function ChatInputToolbar({
2306
2314
  }
2307
2315
 
2308
2316
  // 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";
2317
+ import { CircleDotIcon, EyeOffIcon, ListCollapseIcon, ListIcon, ShapesIcon, TagsIcon, TextAlignJustifyIcon } from "lucide-react";
2318
+ import { jsx as jsx16 } from "react/jsx-runtime";
2311
2319
  var DETAIL_MODES = ["off", "current", "icons", "chips", "grouped", "compact", "full"];
2312
2320
  var TEXT_KEYS = {
2313
2321
  off: "detailModeOff",
@@ -2318,57 +2326,50 @@ var TEXT_KEYS = {
2318
2326
  compact: "detailModeCompact",
2319
2327
  full: "detailModeFull"
2320
2328
  };
2329
+ var ICONS = {
2330
+ off: EyeOffIcon,
2331
+ current: CircleDotIcon,
2332
+ icons: ShapesIcon,
2333
+ chips: TagsIcon,
2334
+ grouped: ListCollapseIcon,
2335
+ compact: ListIcon,
2336
+ full: TextAlignJustifyIcon
2337
+ };
2321
2338
  var detailModeLabel = (mode, texts) => ({ ...defaultTexts, ...texts })[TEXT_KEYS[mode]];
2322
2339
  function DetailModeSwitch({
2323
2340
  mode,
2324
2341
  onChange,
2325
2342
  modes = DETAIL_MODES,
2326
- collapsible = true,
2327
2343
  texts,
2328
2344
  className
2329
2345
  }) {
2330
- const alleTexte = { ...defaultTexts, ...texts };
2346
+ const allTexts = { ...defaultTexts, ...texts };
2331
2347
  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
- );
2348
+ const current = modes.indexOf(mode);
2349
+ const next2 = modes[(current < 0 ? 0 : current + 1) % modes.length];
2350
+ const Icon = ICONS[mode];
2351
+ const title = `${allTexts.detailModeTitle}: ${detailModeLabel(mode, texts)}`;
2352
+ return /* @__PURE__ */ jsx16(Button2, { "aria-label": title, className, onClick: () => onChange(next2), size: "icon-sm", title, variant: "ghost", children: /* @__PURE__ */ jsx16(Icon, {}) });
2349
2353
  }
2350
2354
 
2351
2355
  // 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 }) {
2356
+ import { ClockFadingIcon, ClockIcon } from "lucide-react";
2357
+ import { jsx as jsx17 } from "react/jsx-runtime";
2358
+ function TimestampSwitch({ showTimestamps, onChange, texts, className }) {
2355
2359
  const labels = { ...defaultTexts, ...texts };
2356
2360
  const { Button: Button2 } = useQuasselComponents();
2357
2361
  const title = showTimestamps ? labels.hideTimestamps : labels.showTimestamps;
2358
- return /* @__PURE__ */ jsxs10(
2362
+ return /* @__PURE__ */ jsx17(
2359
2363
  Button2,
2360
2364
  {
2361
2365
  "aria-label": title,
2362
2366
  "aria-pressed": showTimestamps,
2363
2367
  className,
2364
2368
  onClick: () => onChange(!showTimestamps),
2365
- size: "sm",
2369
+ size: "icon-sm",
2366
2370
  title,
2367
2371
  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
- ]
2372
+ children: showTimestamps ? /* @__PURE__ */ jsx17(ClockIcon, {}) : /* @__PURE__ */ jsx17(ClockFadingIcon, {})
2372
2373
  }
2373
2374
  );
2374
2375
  }