@schlenkr/quassel 0.5.1 → 0.5.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/chat.css CHANGED
@@ -43,6 +43,9 @@
43
43
  .qsl\:absolute {
44
44
  position: absolute;
45
45
  }
46
+ .qsl\:fixed {
47
+ position: fixed;
48
+ }
46
49
  .qsl\:relative {
47
50
  position: relative;
48
51
  }
@@ -55,6 +58,9 @@
55
58
  .qsl\:top-1 {
56
59
  top: var(--qsl-spacing);
57
60
  }
61
+ .qsl\:top-1\/2 {
62
+ top: calc(1 / 2 * 100%);
63
+ }
58
64
  .qsl\:right-1 {
59
65
  right: var(--qsl-spacing);
60
66
  }
@@ -64,6 +70,9 @@
64
70
  .qsl\:bottom-\[calc\(var\(--qsl-composer-height\,0px\)_\+_12px\)\] {
65
71
  bottom: calc(var(--qsl-composer-height,0px) + 12px);
66
72
  }
73
+ .qsl\:left-1\/2 {
74
+ left: calc(1 / 2 * 100%);
75
+ }
67
76
  .qsl\:isolate {
68
77
  isolation: isolate;
69
78
  }
@@ -79,6 +88,12 @@
79
88
  .qsl\:z-\[110\] {
80
89
  z-index: 110;
81
90
  }
91
+ .qsl\:z-\[120\] {
92
+ z-index: 120;
93
+ }
94
+ .qsl\:z-\[121\] {
95
+ z-index: 121;
96
+ }
82
97
  .qsl\:-mx-\[16px\] {
83
98
  margin-inline: calc(16px * -1);
84
99
  }
@@ -198,6 +213,9 @@
198
213
  .qsl\:max-h-\[240px\] {
199
214
  max-height: 240px;
200
215
  }
216
+ .qsl\:max-h-\[calc\(100vh-6rem\)\] {
217
+ max-height: calc(100vh - 6rem);
218
+ }
201
219
  .qsl\:max-h-\[calc\(var\(--qsl-panel-height\,100dvh\)_\/_2\)\] {
202
220
  max-height: calc(var(--qsl-panel-height,100dvh) / 2);
203
221
  }
@@ -225,6 +243,9 @@
225
243
  .qsl\:w-\[190px\] {
226
244
  width: 190px;
227
245
  }
246
+ .qsl\:w-\[320px\] {
247
+ width: 320px;
248
+ }
228
249
  .qsl\:w-\[calc\(100\%_-_2_\*_var\(--qsl-chat-horizontal-padding\,24px\)\)\] {
229
250
  width: calc(100% - 2 * var(--qsl-chat-horizontal-padding,24px));
230
251
  }
@@ -246,6 +267,9 @@
246
267
  .qsl\:max-w-\[86\%\] {
247
268
  max-width: 86%;
248
269
  }
270
+ .qsl\:max-w-\[94vw\] {
271
+ max-width: 94vw;
272
+ }
249
273
  .qsl\:max-w-\[220px\] {
250
274
  max-width: 220px;
251
275
  }
@@ -294,6 +318,14 @@
294
318
  .qsl\:origin-\(--transform-origin\) {
295
319
  transform-origin: var(--transform-origin);
296
320
  }
321
+ .qsl\:-translate-x-1\/2 {
322
+ --tw-translate-x: calc(calc(1 / 2 * 100%) * -1);
323
+ translate: var(--tw-translate-x) var(--tw-translate-y);
324
+ }
325
+ .qsl\:-translate-y-1\/2 {
326
+ --tw-translate-y: calc(calc(1 / 2 * 100%) * -1);
327
+ translate: var(--tw-translate-x) var(--tw-translate-y);
328
+ }
297
329
  .qsl\:rotate-90 {
298
330
  rotate: 90deg;
299
331
  }
@@ -309,6 +341,9 @@
309
341
  .qsl\:cursor-pointer {
310
342
  cursor: pointer;
311
343
  }
344
+ .qsl\:cursor-zoom-in {
345
+ cursor: zoom-in;
346
+ }
312
347
  .qsl\:resize-none {
313
348
  resize: none;
314
349
  }
@@ -515,6 +550,12 @@
515
550
  background-color: color-mix(in oklab, var(--qsl-color-black) 15%, transparent);
516
551
  }
517
552
  }
553
+ .qsl\:bg-black\/75 {
554
+ background-color: var(--qsl-color-black);
555
+ @supports (color: color-mix(in lab, red, red)) {
556
+ background-color: color-mix(in oklab, var(--qsl-color-black) 75%, transparent);
557
+ }
558
+ }
518
559
  .qsl\:bg-card {
519
560
  background-color: var(--qsl-card);
520
561
  }
@@ -822,6 +863,9 @@
822
863
  --tw-numeric-spacing: tabular-nums;
823
864
  font-variant-numeric: var(--tw-ordinal,) var(--tw-slashed-zero,) var(--tw-numeric-figure,) var(--tw-numeric-spacing,) var(--tw-numeric-fraction,);
824
865
  }
866
+ .qsl\:underline {
867
+ text-decoration-line: underline;
868
+ }
825
869
  .qsl\:\[text-underline-offset\:2px\] {
826
870
  text-underline-offset: 2px;
827
871
  }
@@ -857,6 +901,10 @@
857
901
  --tw-shadow: var(--qsl-bar-shadow);
858
902
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
859
903
  }
904
+ .qsl\:shadow-lg {
905
+ --tw-shadow: 0 10px 15px -3px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 4px 6px -4px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
906
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
907
+ }
860
908
  .qsl\:shadow-md {
861
909
  --tw-shadow: 0 4px 6px -1px var(--tw-shadow-color, rgb(0 0 0 / 0.1)), 0 2px 4px -2px var(--tw-shadow-color, rgb(0 0 0 / 0.1));
862
910
  box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
@@ -1858,6 +1906,21 @@
1858
1906
  display: none;
1859
1907
  }
1860
1908
  }
1909
+ @property --tw-translate-x {
1910
+ syntax: "*";
1911
+ inherits: false;
1912
+ initial-value: 0;
1913
+ }
1914
+ @property --tw-translate-y {
1915
+ syntax: "*";
1916
+ inherits: false;
1917
+ initial-value: 0;
1918
+ }
1919
+ @property --tw-translate-z {
1920
+ syntax: "*";
1921
+ inherits: false;
1922
+ initial-value: 0;
1923
+ }
1861
1924
  @property --tw-space-y-reverse {
1862
1925
  syntax: "*";
1863
1926
  inherits: false;
@@ -1974,21 +2037,6 @@
1974
2037
  syntax: "*";
1975
2038
  inherits: false;
1976
2039
  }
1977
- @property --tw-translate-x {
1978
- syntax: "*";
1979
- inherits: false;
1980
- initial-value: 0;
1981
- }
1982
- @property --tw-translate-y {
1983
- syntax: "*";
1984
- inherits: false;
1985
- initial-value: 0;
1986
- }
1987
- @property --tw-translate-z {
1988
- syntax: "*";
1989
- inherits: false;
1990
- initial-value: 0;
1991
- }
1992
2040
  @keyframes enter {
1993
2041
  from {
1994
2042
  opacity: var(--tw-enter-opacity,1);
@@ -2029,6 +2077,9 @@
2029
2077
  --qsl-base: 13px;
2030
2078
  }
2031
2079
  *, ::before, ::after, ::backdrop {
2080
+ --tw-translate-x: 0;
2081
+ --tw-translate-y: 0;
2082
+ --tw-translate-z: 0;
2032
2083
  --tw-space-y-reverse: 0;
2033
2084
  --tw-border-style: solid;
2034
2085
  --tw-leading: initial;
@@ -2055,9 +2106,6 @@
2055
2106
  --tw-ring-offset-shadow: 0 0 #0000;
2056
2107
  --tw-outline-style: solid;
2057
2108
  --tw-duration: initial;
2058
- --tw-translate-x: 0;
2059
- --tw-translate-y: 0;
2060
- --tw-translate-z: 0;
2061
2109
  --tw-animation-delay: 0s;
2062
2110
  --tw-animation-direction: normal;
2063
2111
  --tw-animation-duration: initial;
package/index.js CHANGED
@@ -72,6 +72,8 @@ var englishTexts = {
72
72
  attachFiles: "Attach files",
73
73
  attachFilesHint: "Attach files (up to {count} files, {size} in total)",
74
74
  removeAttachment: "Remove {name}",
75
+ enlargeImage: "Enlarge {name}",
76
+ downloadAttachment: "Download",
75
77
  attachmentPreparing: "Preparing ...",
76
78
  attachmentsPreparing: "Preparing attachments ...",
77
79
  attachmentReadFailed: "The file could not be read. Remove it and add it again.",
@@ -139,6 +141,8 @@ var germanTexts = {
139
141
  attachFiles: "Dateien anh\xE4ngen",
140
142
  attachFilesHint: "Dateien anh\xE4ngen (bis zu {count} Dateien, insgesamt {size})",
141
143
  removeAttachment: "{name} entfernen",
144
+ enlargeImage: "{name} vergr\xF6\xDFern",
145
+ downloadAttachment: "Herunterladen",
142
146
  attachmentPreparing: "Wird vorbereitet ...",
143
147
  attachmentsPreparing: "Anh\xE4nge werden vorbereitet ...",
144
148
  attachmentReadFailed: "Die Datei konnte nicht gelesen werden. Den Anhang entfernen und erneut hinzuf\xFCgen.",
@@ -163,6 +167,7 @@ var cn = createCn({ prefix: "qsl", extend: { theme: { text: ["meta", "trace", "t
163
167
  // packages/quassel/src/ChatMessages.tsx
164
168
  import { useCallback as useCallback2, useContext as useContext3, useEffect as useEffect3, useLayoutEffect, useMemo as useMemo3, useRef as useRef3, useState as useState4 } from "react";
165
169
  import { createPortal } from "react-dom";
170
+ import { Dialog } from "@base-ui/react/dialog";
166
171
 
167
172
  // packages/quassel/src/chat-scroll.ts
168
173
  var atEnd = (viewport) => viewport.scrollHeight - viewport.clientHeight - viewport.scrollTop <= 24;
@@ -1760,7 +1765,7 @@ function Bubble({
1760
1765
  if (message.role === "system") {
1761
1766
  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: [
1762
1767
  /* @__PURE__ */ jsx13(Markdown, { text: message.text }),
1763
- /* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message })
1768
+ /* @__PURE__ */ jsx13(MessageAttachments, { message, texts })
1764
1769
  ] });
1765
1770
  }
1766
1771
  if (!message.text.trim() && !message.attachments?.length) return null;
@@ -1775,7 +1780,7 @@ function Bubble({
1775
1780
  useBubble && message.bubble && "qsl:rounded-full qsl:border qsl:border-white/30 qsl:bg-black/15 qsl:px-[7px] qsl:py-px qsl:tracking-[0.02em]"
1776
1781
  ), "data-chat": "sender", children: sender }),
1777
1782
  /* @__PURE__ */ jsx13(Markdown, { text: message.text, streaming: message.role === "assistant" && !message.closed }),
1778
- /* @__PURE__ */ jsx13(MessageAttachments, { label: texts.attachments, message }),
1783
+ /* @__PURE__ */ jsx13(MessageAttachments, { message, texts }),
1779
1784
  message.steered && /* @__PURE__ */ jsx13("small", { className: "qsl:mt-1 qsl:block qsl:text-label qsl:leading-[1.45] qsl:opacity-70", "data-chat": "steered", children: texts.steered }),
1780
1785
  /* @__PURE__ */ jsx13(MessageActions, { message, options: messageActions, texts })
1781
1786
  ] });
@@ -1826,16 +1831,56 @@ function ActionContent({
1826
1831
  }
1827
1832
  );
1828
1833
  }
1829
- function MessageAttachments({ message, label }) {
1834
+ function MessageAttachments({ message, texts }) {
1835
+ const [enlarged, setEnlarged] = useState4();
1830
1836
  if (!message.attachments?.length) return null;
1831
- return /* @__PURE__ */ jsx13("ul", { className: "qsl:mt-2.5 qsl:flex qsl:flex-wrap qsl:gap-2", "aria-label": label, children: message.attachments.map((attachment, index) => /* @__PURE__ */ jsxs6("li", { className: "qsl:relative qsl:flex qsl:w-[190px] qsl:max-w-full qsl:min-w-0 qsl:flex-col qsl:overflow-hidden qsl:rounded-lg qsl:border qsl:border-border qsl:text-sm", children: [
1832
- attachment.mediaType.startsWith("image/") && /* @__PURE__ */ jsx13("a", { href: attachmentDownloadUrl(attachment.url), download: attachment.name, children: /* @__PURE__ */ jsx13("img", { className: "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondary qsl:object-contain", src: attachment.url, alt: attachment.name, loading: "lazy" }) }),
1833
- attachment.mediaType.startsWith("video/") && /* @__PURE__ */ jsx13("video", { className: "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondary qsl:object-contain", src: attachment.url, controls: true, preload: "metadata", "aria-label": attachment.name }),
1834
- /* @__PURE__ */ jsxs6("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:[overflow-wrap:anywhere]", children: [
1835
- /* @__PURE__ */ jsx13("a", { className: "qsl:font-medium", href: attachmentDownloadUrl(attachment.url), download: attachment.name, children: attachment.name }),
1836
- /* @__PURE__ */ jsx13("small", { children: formatAttachmentSize(attachment.size) })
1837
- ] })
1838
- ] }, `${attachment.url}-${index}`)) });
1837
+ return /* @__PURE__ */ jsxs6(Fragment2, { children: [
1838
+ /* @__PURE__ */ jsx13("ul", { className: "qsl:mt-2.5 qsl:flex qsl:flex-wrap qsl:gap-2", "aria-label": texts.attachments, children: message.attachments.map((attachment, index) => {
1839
+ const isImage = attachment.mediaType.startsWith("image/");
1840
+ return /* @__PURE__ */ jsxs6("li", { className: cn("qsl:relative qsl:flex qsl:max-w-full qsl:min-w-0 qsl:flex-col qsl:overflow-hidden qsl:rounded-lg qsl:border qsl:border-border qsl:text-sm", isImage ? "qsl:w-[320px]" : "qsl:w-[190px]"), children: [
1841
+ isImage && /* @__PURE__ */ jsx13(
1842
+ "button",
1843
+ {
1844
+ type: "button",
1845
+ className: "qsl:block qsl:w-full qsl:cursor-zoom-in qsl:bg-secondary",
1846
+ "aria-label": fillText(texts.enlargeImage ?? defaultTexts.enlargeImage, { name: attachment.name }),
1847
+ onClick: () => setEnlarged(attachment),
1848
+ "data-chat": "attachment-image",
1849
+ children: /* @__PURE__ */ jsx13("img", { className: "qsl:mx-auto qsl:block qsl:max-h-[240px] qsl:max-w-full qsl:object-contain", src: attachment.url, alt: attachment.name, loading: "lazy" })
1850
+ }
1851
+ ),
1852
+ attachment.mediaType.startsWith("video/") && /* @__PURE__ */ jsx13("video", { className: "qsl:block qsl:h-[112px] qsl:w-full qsl:bg-secondary qsl:object-contain", src: attachment.url, controls: true, preload: "metadata", "aria-label": attachment.name }),
1853
+ /* @__PURE__ */ jsxs6("div", { className: "qsl:flex qsl:flex-col qsl:gap-1 qsl:p-2 qsl:[overflow-wrap:anywhere]", children: [
1854
+ /* @__PURE__ */ jsx13("a", { className: "qsl:font-medium", href: attachmentDownloadUrl(attachment.url), download: attachment.name, children: attachment.name }),
1855
+ /* @__PURE__ */ jsx13("small", { children: formatAttachmentSize(attachment.size) })
1856
+ ] })
1857
+ ] }, `${attachment.url}-${index}`);
1858
+ }) }),
1859
+ /* @__PURE__ */ jsx13(ImageViewer, { attachment: enlarged, onClose: () => setEnlarged(void 0), texts })
1860
+ ] });
1861
+ }
1862
+ function ImageViewer({ attachment, onClose, texts }) {
1863
+ const portalContainer = useQuasselPortalContainer();
1864
+ return /* @__PURE__ */ jsx13(Dialog.Root, { open: attachment !== void 0, onOpenChange: (open) => !open && onClose(), children: /* @__PURE__ */ jsxs6(Dialog.Portal, { container: portalContainer, children: [
1865
+ /* @__PURE__ */ jsx13(Dialog.Backdrop, { className: "qsl:fixed qsl:inset-0 qsl:z-[120] qsl:bg-black/75", "data-quassel": "" }),
1866
+ /* @__PURE__ */ jsx13(
1867
+ Dialog.Popup,
1868
+ {
1869
+ className: "qsl:fixed qsl:top-1/2 qsl:left-1/2 qsl:z-[121] qsl:flex qsl:max-w-[94vw] qsl:-translate-x-1/2 qsl:-translate-y-1/2 qsl:flex-col qsl:items-center qsl:gap-2 qsl:outline-none",
1870
+ "aria-label": attachment?.name,
1871
+ "data-quassel": "",
1872
+ "data-chat": "image-viewer",
1873
+ children: attachment && /* @__PURE__ */ jsxs6(Fragment2, { children: [
1874
+ /* @__PURE__ */ jsx13("img", { className: "qsl:block qsl:max-h-[calc(100vh-6rem)] qsl:max-w-full qsl:rounded-md qsl:bg-background qsl:object-contain qsl:shadow-lg", src: attachment.url, alt: attachment.name }),
1875
+ /* @__PURE__ */ jsxs6("div", { className: "qsl:flex qsl:items-center qsl:gap-4 qsl:text-sm qsl:text-white", children: [
1876
+ /* @__PURE__ */ jsx13("span", { className: "qsl:[overflow-wrap:anywhere]", children: attachment.name }),
1877
+ /* @__PURE__ */ jsx13("a", { className: "qsl:underline", href: attachmentDownloadUrl(attachment.url), download: attachment.name, children: texts.downloadAttachment ?? defaultTexts.downloadAttachment }),
1878
+ /* @__PURE__ */ jsx13(Dialog.Close, { className: "qsl:cursor-pointer qsl:underline", children: texts.close })
1879
+ ] })
1880
+ ] })
1881
+ }
1882
+ )
1883
+ ] }) });
1839
1884
  }
1840
1885
 
1841
1886
  // packages/quassel/src/ChatPanel.tsx