@open-mercato/ui 0.6.2-develop.3446.1.bd060c6017 → 0.6.2-develop.3467.1.2a1818709d

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.
@@ -1,3 +1,3 @@
1
1
  Generated lucide registry with 135 icons -> /home/runner/work/open-mercato/open-mercato/packages/ui/src/backend/icons/lucideRegistry.generated.tsx
2
- [build:ui] found 351 entry points
2
+ [build:ui] found 353 entry points
3
3
  [build:ui] built successfully
package/dist/ai/AiChat.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3
3
  import * as React from "react";
4
4
  import {
5
5
  Activity,
@@ -16,12 +16,14 @@ import {
16
16
  CircleDashed,
17
17
  Copy,
18
18
  Database,
19
+ Download,
19
20
  FileQuestion,
20
21
  GitBranch,
21
22
  Handshake,
22
23
  Lightbulb,
23
24
  ListChecks,
24
25
  Loader2,
26
+ Maximize2,
25
27
  Paperclip,
26
28
  Plus,
27
29
  Search,
@@ -38,6 +40,13 @@ import { useT } from "@open-mercato/shared/lib/i18n/context";
38
40
  import { cn } from "@open-mercato/shared/lib/utils";
39
41
  import { Alert, AlertDescription, AlertTitle } from "../primitives/alert.js";
40
42
  import { Button } from "../primitives/button.js";
43
+ import {
44
+ Dialog,
45
+ DialogContent,
46
+ DialogDescription,
47
+ DialogHeader,
48
+ DialogTitle
49
+ } from "../primitives/dialog.js";
41
50
  import { IconButton } from "../primitives/icon-button.js";
42
51
  import { Label } from "../primitives/label.js";
43
52
  import { Textarea } from "../primitives/textarea.js";
@@ -391,6 +400,124 @@ function ReasoningPanel({ text, streaming }) {
391
400
  }
392
401
  );
393
402
  }
403
+ function getAttachmentImageUrl(file) {
404
+ if (file.id) return `/api/attachments/image/${encodeURIComponent(file.id)}`;
405
+ return file.previewUrl ?? null;
406
+ }
407
+ function getAttachmentDownloadUrl(file) {
408
+ if (file.id) return `/api/attachments/file/${encodeURIComponent(file.id)}?download=1`;
409
+ return file.previewUrl ?? null;
410
+ }
411
+ function MessageFileAttachment({ file }) {
412
+ const t = useT();
413
+ const [zoomOpen, setZoomOpen] = React.useState(false);
414
+ const imageUrl = file.previewUrl ? getAttachmentImageUrl(file) : null;
415
+ const downloadUrl = getAttachmentDownloadUrl(file);
416
+ const downloadLabel = t("ai_assistant.chat.downloadFile", "Download {name}").replace(
417
+ "{name}",
418
+ file.name
419
+ );
420
+ const zoomLabel = t("ai_assistant.chat.zoomImage", "Open {name} preview").replace(
421
+ "{name}",
422
+ file.name
423
+ );
424
+ if (!file.previewUrl) {
425
+ return /* @__PURE__ */ jsxs("span", { className: "inline-flex max-w-full items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs", children: [
426
+ /* @__PURE__ */ jsx(Paperclip, { className: "size-3 shrink-0", "aria-hidden": true }),
427
+ /* @__PURE__ */ jsx("span", { className: "max-w-[180px] truncate", children: file.name }),
428
+ downloadUrl ? /* @__PURE__ */ jsx(
429
+ IconButton,
430
+ {
431
+ asChild: true,
432
+ variant: "ghost",
433
+ size: "xs",
434
+ className: "-mr-1 size-5 shrink-0",
435
+ children: /* @__PURE__ */ jsx("a", { href: downloadUrl, download: file.name, "aria-label": downloadLabel, children: /* @__PURE__ */ jsx(Download, { className: "size-3", "aria-hidden": true }) })
436
+ }
437
+ ) : null
438
+ ] });
439
+ }
440
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
441
+ /* @__PURE__ */ jsxs("div", { className: "group/file relative max-w-full", children: [
442
+ /* @__PURE__ */ jsxs(
443
+ "button",
444
+ {
445
+ type: "button",
446
+ className: "block max-w-full rounded-md border border-border bg-muted/30 focus-visible:outline-none focus-visible:shadow-focus",
447
+ onClick: () => setZoomOpen(true),
448
+ "aria-label": zoomLabel,
449
+ "data-ai-chat-image-preview": "",
450
+ children: [
451
+ /* @__PURE__ */ jsx(
452
+ "img",
453
+ {
454
+ src: file.previewUrl,
455
+ alt: file.name,
456
+ className: "max-h-32 max-w-[200px] rounded-md object-cover"
457
+ }
458
+ ),
459
+ /* @__PURE__ */ jsx("span", { className: "pointer-events-none absolute right-1 top-1 inline-flex size-6 items-center justify-center rounded-md bg-background/90 text-muted-foreground opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100", children: /* @__PURE__ */ jsx(Maximize2, { className: "size-3.5", "aria-hidden": true }) })
460
+ ]
461
+ }
462
+ ),
463
+ downloadUrl ? /* @__PURE__ */ jsx(
464
+ IconButton,
465
+ {
466
+ asChild: true,
467
+ variant: "white",
468
+ size: "xs",
469
+ className: "absolute bottom-1 right-1 opacity-0 shadow-xs transition-opacity group-hover/file:opacity-100 group-focus-within/file:opacity-100",
470
+ children: /* @__PURE__ */ jsx(
471
+ "a",
472
+ {
473
+ href: downloadUrl,
474
+ download: file.name,
475
+ "aria-label": downloadLabel,
476
+ onClick: (event) => event.stopPropagation(),
477
+ "data-ai-chat-file-download": "",
478
+ children: /* @__PURE__ */ jsx(Download, { className: "size-3.5", "aria-hidden": true })
479
+ }
480
+ )
481
+ }
482
+ ) : null
483
+ ] }),
484
+ /* @__PURE__ */ jsx(Dialog, { open: zoomOpen, onOpenChange: setZoomOpen, children: /* @__PURE__ */ jsxs(DialogContent, { className: "gap-3 p-4 sm:max-w-[min(96vw,960px)]", children: [
485
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "min-w-0 pr-16", children: [
486
+ /* @__PURE__ */ jsx(DialogTitle, { className: "truncate text-sm", children: file.name }),
487
+ /* @__PURE__ */ jsx(DialogDescription, { className: "sr-only", children: t("ai_assistant.chat.imagePreviewDialogDescription", "Image preview") })
488
+ ] }),
489
+ /* @__PURE__ */ jsxs("div", { className: "relative flex min-h-0 items-center justify-center overflow-auto rounded-md bg-muted/30 p-2", children: [
490
+ /* @__PURE__ */ jsx(
491
+ "img",
492
+ {
493
+ src: imageUrl ?? file.previewUrl,
494
+ alt: file.name,
495
+ className: "max-h-[75vh] max-w-full object-contain"
496
+ }
497
+ ),
498
+ downloadUrl ? /* @__PURE__ */ jsx(
499
+ IconButton,
500
+ {
501
+ asChild: true,
502
+ variant: "white",
503
+ size: "sm",
504
+ className: "absolute bottom-3 right-3 shadow-xs",
505
+ children: /* @__PURE__ */ jsx(
506
+ "a",
507
+ {
508
+ href: downloadUrl,
509
+ download: file.name,
510
+ "aria-label": downloadLabel,
511
+ "data-ai-chat-file-download": "",
512
+ children: /* @__PURE__ */ jsx(Download, { className: "size-4", "aria-hidden": true })
513
+ }
514
+ )
515
+ }
516
+ ) : null
517
+ ] })
518
+ ] }) })
519
+ ] });
520
+ }
394
521
  function MessageRow({
395
522
  message,
396
523
  registry,
@@ -468,27 +595,7 @@ function MessageRow({
468
595
  }
469
596
  ) : null
470
597
  ] }),
471
- message.files && message.files.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 py-1", children: message.files.map(
472
- (file, i) => file.previewUrl ? /* @__PURE__ */ jsx(
473
- "img",
474
- {
475
- src: file.previewUrl,
476
- alt: file.name,
477
- className: "max-h-32 max-w-[200px] rounded-md border border-border object-cover"
478
- },
479
- i
480
- ) : /* @__PURE__ */ jsxs(
481
- "span",
482
- {
483
- className: "inline-flex items-center gap-1 rounded-full border border-border bg-muted px-2 py-0.5 text-xs",
484
- children: [
485
- /* @__PURE__ */ jsx(Paperclip, { className: "size-3", "aria-hidden": true }),
486
- file.name
487
- ]
488
- },
489
- i
490
- )
491
- ) }) : null,
598
+ message.files && message.files.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-2 py-1", children: message.files.map((file, i) => /* @__PURE__ */ jsx(MessageFileAttachment, { file }, `${file.id ?? file.name}:${i}`)) }) : null,
492
599
  isAssistant && message.reasoning ? /* @__PURE__ */ jsx(
493
600
  ReasoningPanel,
494
601
  {
@@ -867,6 +974,7 @@ function AiChat({
867
974
  const isImage = entry.file.type.startsWith("image/");
868
975
  const fallback = isImage ? URL.createObjectURL(entry.file) : void 0;
869
976
  return {
977
+ id: entry.attachmentId,
870
978
  name: entry.file.name,
871
979
  type: entry.file.type,
872
980
  previewUrl: isImage ? entry.previewDataUrl ?? fallback : void 0