@streamoid/chat-components 0.2.11 → 0.2.13

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.
Files changed (2) hide show
  1. package/dist/index.js +92 -47
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -401,7 +401,9 @@ import {
401
401
  X,
402
402
  Plus,
403
403
  Pencil,
404
- Trash2
404
+ Trash2,
405
+ ChevronLeft,
406
+ ChevronRight
405
407
  } from "lucide-react";
406
408
  import { Fragment, jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
407
409
  function DynamicForm(props) {
@@ -495,6 +497,7 @@ function DynamicForm(props) {
495
497
  const [newTodoInputs, setNewTodoInputs] = useState({});
496
498
  const [fileUploadState, setFileUploadState] = useState({});
497
499
  const [mediaPreview, setMediaPreview] = useState(null);
500
+ const [carouselIndices, setCarouselIndices] = useState({});
498
501
  const scrollRef = useRef(null);
499
502
  const [canScrollUp, setCanScrollUp] = useState(false);
500
503
  const [canScrollDown, setCanScrollDown] = useState(false);
@@ -836,7 +839,7 @@ function DynamicForm(props) {
836
839
  }
837
840
  );
838
841
  case "approval":
839
- return /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 justify-end", children: [
842
+ return /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap gap-2 justify-end", children: [
840
843
  /* @__PURE__ */ jsxs2(
841
844
  Button,
842
845
  {
@@ -844,7 +847,7 @@ function DynamicForm(props) {
844
847
  variant: "destructive",
845
848
  size: "sm",
846
849
  onClick: () => handleApproval(field.id, false),
847
- className: "bg-destructive/10 text-destructive hover:bg-destructive/20 border-transparent shadow-none h-8 text-xs",
850
+ className: "bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-red-500/15 dark:text-red-400 dark:hover:bg-red-500/25 border-transparent shadow-none h-8 text-xs",
848
851
  disabled: isLoading,
849
852
  children: [
850
853
  /* @__PURE__ */ jsx10(AlertCircle, { className: "w-3 h-3 mr-1.5" }),
@@ -879,42 +882,83 @@ function DynamicForm(props) {
879
882
  const feedbackPlaceholder = field.feedback_placeholder ?? "Describe what needs to be modified.";
880
883
  const approveLabel = field.approve_label ?? "Approve";
881
884
  const rejectLabel = field.reject_label ?? "Needs Modification";
885
+ const carouselIdx = carouselIndices[field.id] ?? 0;
886
+ const hasMultipleMedia = mediaUrls.length > 1;
882
887
  return /* @__PURE__ */ jsxs2("div", { className: "space-y-3", children: [
883
- mediaUrls.length > 0 && /* @__PURE__ */ jsx10("div", { className: "grid gap-2 sm:grid-cols-2", children: mediaUrls.map((url, idx) => {
884
- const mediaType = field.media_type && field.media_type !== "mixed" ? field.media_type : inferMediaTypeFromUrl(url);
885
- return /* @__PURE__ */ jsx10(
886
- "div",
887
- {
888
- className: "rounded-md border border-border overflow-hidden bg-muted/20",
889
- children: /* @__PURE__ */ jsx10(
890
- "button",
891
- {
892
- type: "button",
893
- onClick: () => setMediaPreview({ url, type: mediaType }),
894
- className: "w-full text-left cursor-zoom-in",
895
- title: "Click to view full media",
896
- children: mediaType === "video" ? /* @__PURE__ */ jsx10(
897
- "video",
898
- {
899
- src: url,
900
- controls: true,
901
- className: "w-full h-44 object-contain bg-black"
902
- }
903
- ) : /* @__PURE__ */ jsx10(
904
- "img",
905
- {
906
- src: url,
907
- alt: `Generated media ${idx + 1}`,
908
- className: "w-full h-44 object-contain bg-black",
909
- loading: "lazy"
910
- }
911
- )
912
- }
913
- )
914
- },
915
- `${field.id}-${idx}`
916
- );
917
- }) }),
888
+ mediaUrls.length > 0 && /* @__PURE__ */ jsxs2("div", { className: "relative rounded-md border border-border overflow-hidden bg-muted/20", children: [
889
+ (() => {
890
+ const url = mediaUrls[carouselIdx] ?? mediaUrls[0];
891
+ const mediaType = field.media_type && field.media_type !== "mixed" ? field.media_type : inferMediaTypeFromUrl(url);
892
+ return /* @__PURE__ */ jsx10(
893
+ "button",
894
+ {
895
+ type: "button",
896
+ onClick: () => setMediaPreview({ url, type: mediaType }),
897
+ className: "w-full text-left cursor-zoom-in",
898
+ title: "Click to view full media",
899
+ children: mediaType === "video" ? /* @__PURE__ */ jsx10(
900
+ "video",
901
+ {
902
+ src: url,
903
+ controls: true,
904
+ className: "w-full h-52 object-contain bg-black"
905
+ }
906
+ ) : /* @__PURE__ */ jsx10(
907
+ "img",
908
+ {
909
+ src: url,
910
+ alt: `Generated media ${carouselIdx + 1}`,
911
+ className: "w-full h-52 object-contain bg-black",
912
+ loading: "lazy"
913
+ }
914
+ )
915
+ }
916
+ );
917
+ })(),
918
+ hasMultipleMedia && /* @__PURE__ */ jsxs2(Fragment, { children: [
919
+ /* @__PURE__ */ jsx10(
920
+ "button",
921
+ {
922
+ type: "button",
923
+ onClick: () => setCarouselIndices((prev) => ({
924
+ ...prev,
925
+ [field.id]: (carouselIdx - 1 + mediaUrls.length) % mediaUrls.length
926
+ })),
927
+ className: "absolute left-1.5 top-1/2 -translate-y-1/2 w-7 h-7 rounded-full bg-black/50 hover:bg-black/70 text-white flex items-center justify-center transition-colors backdrop-blur-sm",
928
+ title: "Previous",
929
+ children: /* @__PURE__ */ jsx10(ChevronLeft, { className: "w-4 h-4" })
930
+ }
931
+ ),
932
+ /* @__PURE__ */ jsx10(
933
+ "button",
934
+ {
935
+ type: "button",
936
+ onClick: () => setCarouselIndices((prev) => ({
937
+ ...prev,
938
+ [field.id]: (carouselIdx + 1) % mediaUrls.length
939
+ })),
940
+ className: "absolute right-1.5 top-1/2 -translate-y-1/2 w-7 h-7 rounded-full bg-black/50 hover:bg-black/70 text-white flex items-center justify-center transition-colors backdrop-blur-sm",
941
+ title: "Next",
942
+ children: /* @__PURE__ */ jsx10(ChevronRight, { className: "w-4 h-4" })
943
+ }
944
+ ),
945
+ /* @__PURE__ */ jsx10("div", { className: "absolute bottom-2 left-1/2 -translate-x-1/2 flex items-center gap-1.5 px-2 py-1 rounded-full bg-black/50 backdrop-blur-sm", children: mediaUrls.map((_, idx) => /* @__PURE__ */ jsx10(
946
+ "button",
947
+ {
948
+ type: "button",
949
+ onClick: () => setCarouselIndices((prev) => ({ ...prev, [field.id]: idx })),
950
+ className: `w-1.5 h-1.5 rounded-full transition-all ${idx === carouselIdx ? "bg-white w-3" : "bg-white/50 hover:bg-white/75"}`,
951
+ title: `Go to media ${idx + 1}`
952
+ },
953
+ idx
954
+ )) }),
955
+ /* @__PURE__ */ jsxs2("div", { className: "absolute top-2 right-2 px-2 py-0.5 rounded-full bg-black/50 backdrop-blur-sm text-white text-[10px] font-medium", children: [
956
+ carouselIdx + 1,
957
+ " / ",
958
+ mediaUrls.length
959
+ ] })
960
+ ] })
961
+ ] }),
918
962
  /* @__PURE__ */ jsxs2("div", { className: "space-y-1", children: [
919
963
  /* @__PURE__ */ jsx10(Label2, { className: "text-xs font-medium", children: feedbackLabel }),
920
964
  /* @__PURE__ */ jsx10(
@@ -932,7 +976,7 @@ function DynamicForm(props) {
932
976
  }
933
977
  )
934
978
  ] }),
935
- /* @__PURE__ */ jsxs2("div", { className: "flex gap-2 justify-end", children: [
979
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap gap-2 justify-end", children: [
936
980
  /* @__PURE__ */ jsxs2(
937
981
  Button,
938
982
  {
@@ -940,7 +984,7 @@ function DynamicForm(props) {
940
984
  variant: "destructive",
941
985
  size: "sm",
942
986
  onClick: () => handleMediaApprovalDecision(field, "needs_modification"),
943
- className: "bg-destructive/10 text-destructive hover:bg-destructive/20 border-transparent shadow-none h-8 text-xs",
987
+ className: "bg-destructive/10 text-destructive hover:bg-destructive/20 dark:bg-red-500/15 dark:text-red-400 dark:hover:bg-red-500/25 border-transparent shadow-none h-8 text-xs",
944
988
  disabled: isLoading,
945
989
  children: [
946
990
  /* @__PURE__ */ jsx10(AlertCircle, { className: "w-3 h-3 mr-1.5" }),
@@ -1023,7 +1067,7 @@ function DynamicForm(props) {
1023
1067
  children: item.content
1024
1068
  }
1025
1069
  ),
1026
- /* @__PURE__ */ jsx10("div", { className: "flex items-center gap-0.5 flex-shrink-0 opacity-0 group-hover:opacity-100 transition-opacity", children: editingTodoId !== item.id && /* @__PURE__ */ jsxs2(Fragment, { children: [
1070
+ /* @__PURE__ */ jsx10("div", { className: "flex items-center gap-0.5 flex-shrink-0 opacity-100 sm:opacity-0 sm:group-hover:opacity-100 transition-opacity", children: editingTodoId !== item.id && /* @__PURE__ */ jsxs2(Fragment, { children: [
1027
1071
  /* @__PURE__ */ jsx10(
1028
1072
  "button",
1029
1073
  {
@@ -1102,9 +1146,10 @@ function DynamicForm(props) {
1102
1146
  /* @__PURE__ */ jsxs2("div", { className: "flex flex-col items-center justify-center pt-5 pb-6", children: [
1103
1147
  /* @__PURE__ */ jsx10(Upload, { className: "w-8 h-8 mb-2 text-muted-foreground group-hover:text-primary transition-colors" }),
1104
1148
  /* @__PURE__ */ jsxs2("p", { className: "mb-1 text-sm text-muted-foreground", children: [
1105
- /* @__PURE__ */ jsx10("span", { className: "font-semibold group-hover:text-foreground transition-colors", children: "Click to upload" }),
1149
+ /* @__PURE__ */ jsx10("span", { className: "font-semibold group-hover:text-foreground transition-colors sm:hidden", children: "Tap to upload" }),
1150
+ /* @__PURE__ */ jsx10("span", { className: "font-semibold group-hover:text-foreground transition-colors hidden sm:inline", children: "Click to upload" }),
1106
1151
  " ",
1107
- "or drag and drop"
1152
+ /* @__PURE__ */ jsx10("span", { className: "hidden sm:inline", children: "or drag and drop" })
1108
1153
  ] }),
1109
1154
  field.accept && /* @__PURE__ */ jsxs2("p", { className: "text-xs text-muted-foreground/70", children: [
1110
1155
  "Accepted: ",
@@ -1197,8 +1242,8 @@ function DynamicForm(props) {
1197
1242
  }
1198
1243
  ) });
1199
1244
  }
1200
- return /* @__PURE__ */ jsxs2(Card, { className: "w-[60%] max-w-2xl max-h-[45vh] flex flex-col border border-border bg-card shadow-sm animate-in fade-in duration-300", children: [
1201
- /* @__PURE__ */ jsxs2(CardHeader, { className: "pb-2 pt-4 px-5 flex-shrink-0", children: [
1245
+ return /* @__PURE__ */ jsxs2(Card, { className: "w-full lg:w-[75%] max-w-2xl max-h-[60vh] lg:max-h-[45vh] flex flex-col border border-border bg-card shadow-sm animate-in fade-in duration-300", children: [
1246
+ /* @__PURE__ */ jsxs2(CardHeader, { className: "pb-2 pt-4 px-3 sm:px-5 flex-shrink-0", children: [
1202
1247
  /* @__PURE__ */ jsx10(CardTitle, { className: "text-sm font-semibold tracking-tight", children: title }),
1203
1248
  description && /* @__PURE__ */ jsx10(CardDescription, { className: "text-xs text-muted-foreground leading-snug", children: description })
1204
1249
  ] }),
@@ -1208,7 +1253,7 @@ function DynamicForm(props) {
1208
1253
  "div",
1209
1254
  {
1210
1255
  ref: scrollRef,
1211
- className: "px-5 overflow-y-auto flex-1 min-h-0 dynamic-form-scroll",
1256
+ className: "px-3 sm:px-5 overflow-y-auto flex-1 min-h-0 dynamic-form-scroll",
1212
1257
  children: /* @__PURE__ */ jsxs2("div", { className: "space-y-3 pb-2", children: [
1213
1258
  fields.map((field) => {
1214
1259
  const statusText = getFieldStatusText(field);
@@ -1235,7 +1280,7 @@ function DynamicForm(props) {
1235
1280
  }
1236
1281
  ),
1237
1282
  /* @__PURE__ */ jsx10("div", { className: `scroll-fade-bottom ${!canScrollDown ? "fade-hidden" : ""}` }),
1238
- !(fields.length === 1 && fields[0]?.type === "media_approval") && /* @__PURE__ */ jsxs2("div", { className: "flex justify-end gap-2 px-5 py-2 flex-shrink-0", children: [
1283
+ !(fields.length === 1 && fields[0]?.type === "media_approval") && /* @__PURE__ */ jsxs2("div", { className: "flex justify-end gap-2 px-3 sm:px-5 py-2 flex-shrink-0", children: [
1239
1284
  cancelText && /* @__PURE__ */ jsx10(
1240
1285
  Button,
1241
1286
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamoid/chat-components",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "Shared chat UI components for the Streamoid chat host — DynamicForm and other cross-service components",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",