@jiggai/kitchen-plugin-marketing 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.
@@ -453,7 +453,8 @@ var XDriver = class extends BaseDriver {
453
453
  var InstagramDriver = class extends BaseDriver {
454
454
  platform = "instagram";
455
455
  label = "Instagram";
456
- icon = "\u{1F4F7}";
456
+ // Text fallback; UI renders these inside icon circles
457
+ icon = "IG";
457
458
  postizProvider = "instagram";
458
459
  getMaxLength() {
459
460
  return 2200;
@@ -467,7 +468,7 @@ var InstagramDriver = class extends BaseDriver {
467
468
  var FacebookDriver = class extends BaseDriver {
468
469
  platform = "facebook";
469
470
  label = "Facebook";
470
- icon = "\u{1F4D8}";
471
+ icon = "f";
471
472
  postizProvider = "facebook";
472
473
  getMaxLength() {
473
474
  return 63206;
@@ -481,7 +482,7 @@ var FacebookDriver = class extends BaseDriver {
481
482
  var LinkedInDriver = class extends BaseDriver {
482
483
  platform = "linkedin";
483
484
  label = "LinkedIn";
484
- icon = "\u{1F4BC}";
485
+ icon = "in";
485
486
  postizProvider = "linkedin";
486
487
  getMaxLength() {
487
488
  return 3e3;
@@ -495,7 +496,7 @@ var LinkedInDriver = class extends BaseDriver {
495
496
  var TikTokDriver = class extends BaseDriver {
496
497
  platform = "tiktok";
497
498
  label = "TikTok";
498
- icon = "\u{1F3B5}";
499
+ icon = "TT";
499
500
  postizProvider = "tiktok";
500
501
  getMaxLength() {
501
502
  return 2200;
@@ -509,7 +510,7 @@ var TikTokDriver = class extends BaseDriver {
509
510
  var DiscordDriver = class extends BaseDriver {
510
511
  platform = "discord";
511
512
  label = "Discord";
512
- icon = "\u{1F4AC}";
513
+ icon = "DS";
513
514
  postizProvider = "discord";
514
515
  getMaxLength() {
515
516
  return 2e3;
@@ -546,7 +547,7 @@ var DiscordDriver = class extends BaseDriver {
546
547
  var TelegramDriver = class extends BaseDriver {
547
548
  platform = "telegram";
548
549
  label = "Telegram";
549
- icon = "\u2708\uFE0F";
550
+ icon = "TG";
550
551
  postizProvider = "telegram";
551
552
  getMaxLength() {
552
553
  return 4096;
@@ -230,7 +230,11 @@
230
230
  resize: "vertical",
231
231
  fontFamily: "inherit",
232
232
  fontSize: "0.9rem",
233
- outline: "none"
233
+ outline: "none",
234
+ // Hard-force LTR to avoid any inherited RTL / bidi overrides in host app
235
+ direction: "ltr",
236
+ unicodeBidi: "plaintext",
237
+ textAlign: "left"
234
238
  },
235
239
  input: {
236
240
  background: "rgba(255,255,255,0.03)",
@@ -342,6 +346,11 @@
342
346
  const [modalContent, setModalContent] = useState("");
343
347
  const [modalPlatforms, setModalPlatforms] = useState([]);
344
348
  const [modalMediaUrl, setModalMediaUrl] = useState("");
349
+ const [modalShowMedia, setModalShowMedia] = useState(false);
350
+ const [modalUploading, setModalUploading] = useState(false);
351
+ const [modalMediaLibrary, setModalMediaLibrary] = useState([]);
352
+ const [modalSelectedMediaIds, setModalSelectedMediaIds] = useState([]);
353
+ const modalFileInputRef = useRef(null);
345
354
  const [modalSaving, setModalSaving] = useState(false);
346
355
  const [modalPublishing, setModalPublishing] = useState(false);
347
356
  const [modalError, setModalError] = useState(null);
@@ -374,6 +383,51 @@
374
383
  } catch {
375
384
  }
376
385
  }, [teamId, postizHeaders]);
386
+ const loadMedia = useCallback(async () => {
387
+ try {
388
+ const res = await fetch(`${apiBase}/media?team=${encodeURIComponent(teamId)}&limit=200`);
389
+ const json = await res.json();
390
+ setModalMediaLibrary(Array.isArray(json.data) ? json.data : []);
391
+ } catch {
392
+ }
393
+ }, [teamId]);
394
+ const handleModalFileUpload = useCallback(async (files) => {
395
+ if (!files || files.length === 0) return;
396
+ setModalUploading(true);
397
+ setModalError(null);
398
+ try {
399
+ for (const file of Array.from(files)) {
400
+ const base64 = await new Promise((resolve, reject) => {
401
+ const reader = new FileReader();
402
+ reader.onload = () => resolve(reader.result);
403
+ reader.onerror = reject;
404
+ reader.readAsDataURL(file);
405
+ });
406
+ const up = await fetch(`${apiBase}/media?team=${encodeURIComponent(teamId)}`, {
407
+ method: "POST",
408
+ headers: { "content-type": "application/json" },
409
+ body: JSON.stringify({ data: base64, filename: file.name, mimeType: file.type })
410
+ });
411
+ if (!up.ok) {
412
+ const err = await up.json().catch(() => ({}));
413
+ throw new Error(err?.message || `Upload failed (${up.status})`);
414
+ }
415
+ const item = await up.json().catch(() => null);
416
+ if (item?.id) {
417
+ setModalSelectedMediaIds((prev) => prev.includes(item.id) ? prev : [...prev, item.id]);
418
+ }
419
+ }
420
+ await loadMedia();
421
+ } catch (e) {
422
+ setModalError(e?.message || "Upload failed");
423
+ } finally {
424
+ setModalUploading(false);
425
+ if (modalFileInputRef.current) modalFileInputRef.current.value = "";
426
+ }
427
+ }, [teamId, loadMedia]);
428
+ const toggleModalMedia = (id) => {
429
+ setModalSelectedMediaIds((prev) => prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]);
430
+ };
377
431
  useEffect(() => {
378
432
  setLoading(true);
379
433
  Promise.all([loadPosts(), loadDrivers()]).finally(() => setLoading(false));
@@ -393,6 +447,9 @@
393
447
  setModalContent("");
394
448
  setModalPlatforms([]);
395
449
  setModalMediaUrl("");
450
+ setModalShowMedia(false);
451
+ setModalSelectedMediaIds([]);
452
+ setModalMediaLibrary([]);
396
453
  setModalError(null);
397
454
  setModalSuccess(null);
398
455
  setModalOpen(true);
@@ -423,7 +480,8 @@
423
480
  content: modalContent,
424
481
  platforms: modalPlatforms.length > 0 ? modalPlatforms : ["draft"],
425
482
  status: modalDate ? "scheduled" : "draft",
426
- scheduledAt: modalDate || void 0
483
+ scheduledAt: modalDate || void 0,
484
+ mediaIds: modalSelectedMediaIds
427
485
  })
428
486
  });
429
487
  if (!res.ok) throw new Error(`Save failed (${res.status})`);
@@ -451,6 +509,7 @@
451
509
  content: modalContent,
452
510
  platforms: modalPlatforms,
453
511
  scheduledAt: modalDate || void 0,
512
+ // NOTE: Postiz expects publicly reachable URLs; uploaded library media is local-only for now.
454
513
  mediaUrls: modalMediaUrl ? [modalMediaUrl] : void 0
455
514
  })
456
515
  });
@@ -468,7 +527,8 @@
468
527
  content: modalContent,
469
528
  platforms: modalPlatforms,
470
529
  status: modalDate ? "scheduled" : "published",
471
- scheduledAt: modalDate || void 0
530
+ scheduledAt: modalDate || void 0,
531
+ mediaIds: modalSelectedMediaIds
472
532
  })
473
533
  }).catch(() => {
474
534
  });
@@ -793,6 +853,7 @@
793
853
  // Textarea
794
854
  h("textarea", {
795
855
  style: s.textarea,
856
+ dir: "ltr",
796
857
  value: modalContent,
797
858
  onChange: (e) => setModalContent(e.target.value),
798
859
  placeholder: "Write something \u2026",
@@ -803,8 +864,13 @@
803
864
  "div",
804
865
  { style: { display: "flex", gap: "0.5rem", alignItems: "center", flexWrap: "wrap" } },
805
866
  h("button", {
867
+ type: "button",
806
868
  style: { ...s.btnGhost, padding: "0.3rem 0.6rem", fontSize: "0.75rem" },
807
- onClick: () => setModalMediaUrl(modalMediaUrl ? "" : " ")
869
+ onClick: async () => {
870
+ const next = !modalShowMedia;
871
+ setModalShowMedia(next);
872
+ if (next) await loadMedia();
873
+ }
808
874
  }, "\u{1F5BC} Insert Media"),
809
875
  charLimit && h("span", {
810
876
  style: {
@@ -814,14 +880,158 @@
814
880
  }
815
881
  }, `${modalContent.length}/${charLimit}`)
816
882
  ),
817
- // Media URL input
818
- modalMediaUrl !== "" && h("input", {
819
- style: s.input,
820
- type: "url",
821
- value: modalMediaUrl.trim(),
822
- onChange: (e) => setModalMediaUrl(e.target.value),
823
- placeholder: "Paste image or video URL\u2026"
824
- })
883
+ // Media panel (upload + library + URL)
884
+ modalShowMedia && h(
885
+ "div",
886
+ {
887
+ style: {
888
+ background: "rgba(255,255,255,0.02)",
889
+ border: "1px solid var(--ck-border-subtle)",
890
+ borderRadius: "10px",
891
+ padding: "0.75rem"
892
+ }
893
+ },
894
+ h(
895
+ "div",
896
+ { style: { display: "flex", gap: "0.5rem", alignItems: "center", marginBottom: "0.5rem", flexWrap: "wrap" } },
897
+ h("input", {
898
+ ref: modalFileInputRef,
899
+ type: "file",
900
+ accept: "image/*,video/*",
901
+ multiple: true,
902
+ style: { display: "none" },
903
+ onChange: (e) => void handleModalFileUpload(e.target.files)
904
+ }),
905
+ h("button", {
906
+ type: "button",
907
+ style: { ...s.btnGhost, padding: "0.35rem 0.6rem", fontSize: "0.75rem", opacity: modalUploading ? 0.7 : 1 },
908
+ onClick: () => modalFileInputRef.current?.click(),
909
+ disabled: modalUploading
910
+ }, modalUploading ? "\u23F3 Uploading\u2026" : "\u{1F4C1} Upload"),
911
+ h("button", {
912
+ type: "button",
913
+ style: { ...s.btnGhost, padding: "0.35rem 0.6rem", fontSize: "0.75rem" },
914
+ onClick: () => setModalShowMedia(false)
915
+ }, "Done")
916
+ ),
917
+ h("input", {
918
+ style: s.input,
919
+ type: "url",
920
+ value: modalMediaUrl,
921
+ onChange: (e) => setModalMediaUrl(e.target.value),
922
+ placeholder: "Paste a public image/video URL (needed for Postiz)\u2026"
923
+ }),
924
+ // Selected media strip
925
+ modalSelectedMediaIds.length > 0 && h(
926
+ "div",
927
+ { style: { display: "flex", flexWrap: "wrap", gap: "0.5rem", marginTop: "0.65rem" } },
928
+ ...modalSelectedMediaIds.map((id) => {
929
+ const item = modalMediaLibrary.find((m) => m.id === id);
930
+ if (!item) return null;
931
+ return h(
932
+ "div",
933
+ {
934
+ key: id,
935
+ style: {
936
+ position: "relative",
937
+ width: "64px",
938
+ height: "64px",
939
+ borderRadius: "8px",
940
+ overflow: "hidden",
941
+ border: "2px solid rgba(127,90,240,0.55)"
942
+ }
943
+ },
944
+ item.mimeType?.startsWith("video/") ? h("div", {
945
+ style: { width: "100%", height: "100%", background: "rgba(0,0,0,0.35)", display: "flex", alignItems: "center", justifyContent: "center", color: "white" }
946
+ }, "\u{1F3A5}") : h("img", { src: item.thumbnailDataUrl, style: { width: "100%", height: "100%", objectFit: "cover" } }),
947
+ h("button", {
948
+ type: "button",
949
+ onClick: () => toggleModalMedia(id),
950
+ style: {
951
+ position: "absolute",
952
+ top: "2px",
953
+ right: "2px",
954
+ background: "rgba(0,0,0,0.6)",
955
+ border: "none",
956
+ borderRadius: "50%",
957
+ width: "18px",
958
+ height: "18px",
959
+ color: "white",
960
+ fontSize: "0.65rem",
961
+ cursor: "pointer",
962
+ display: "flex",
963
+ alignItems: "center",
964
+ justifyContent: "center"
965
+ }
966
+ }, "\u2715")
967
+ );
968
+ })
969
+ ),
970
+ // Library grid
971
+ h(
972
+ "div",
973
+ { style: { marginTop: "0.65rem" } },
974
+ h("div", { style: { fontSize: "0.75rem", fontWeight: 600, color: "var(--ck-text-secondary)", marginBottom: "0.4rem" } }, "Media Library"),
975
+ modalMediaLibrary.length === 0 ? h("div", { style: { fontSize: "0.75rem", color: "var(--ck-text-tertiary)", padding: "0.5rem 0" } }, "No media yet \u2014 upload something.") : h(
976
+ "div",
977
+ {
978
+ style: {
979
+ display: "grid",
980
+ gridTemplateColumns: "repeat(auto-fill, minmax(86px, 1fr))",
981
+ gap: "0.5rem",
982
+ maxHeight: "220px",
983
+ overflowY: "auto",
984
+ paddingRight: "4px"
985
+ }
986
+ },
987
+ ...modalMediaLibrary.map((item) => {
988
+ const selected = modalSelectedMediaIds.includes(item.id);
989
+ const thumb = item.thumbnailDataUrl;
990
+ return h(
991
+ "div",
992
+ {
993
+ key: item.id,
994
+ onClick: () => toggleModalMedia(item.id),
995
+ style: {
996
+ position: "relative",
997
+ width: "100%",
998
+ paddingTop: "100%",
999
+ borderRadius: "10px",
1000
+ overflow: "hidden",
1001
+ cursor: "pointer",
1002
+ border: selected ? "2px solid rgba(127,90,240,0.75)" : "1px solid var(--ck-border-subtle)",
1003
+ boxShadow: selected ? "0 0 10px rgba(127,90,240,0.25)" : "none",
1004
+ background: "rgba(0,0,0,0.25)"
1005
+ }
1006
+ },
1007
+ item.mimeType?.startsWith("video/") ? h("div", {
1008
+ style: { position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", color: "white", fontSize: "1.4rem" }
1009
+ }, "\u{1F3A5}") : h("img", {
1010
+ src: thumb,
1011
+ style: { position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }
1012
+ }),
1013
+ selected && h("div", {
1014
+ style: {
1015
+ position: "absolute",
1016
+ top: "6px",
1017
+ right: "6px",
1018
+ width: "20px",
1019
+ height: "20px",
1020
+ borderRadius: "50%",
1021
+ background: "rgba(127,90,240,0.9)",
1022
+ display: "flex",
1023
+ alignItems: "center",
1024
+ justifyContent: "center",
1025
+ color: "white",
1026
+ fontSize: "0.75rem",
1027
+ fontWeight: 800
1028
+ }
1029
+ }, "\u2713")
1030
+ );
1031
+ })
1032
+ )
1033
+ )
1034
+ )
825
1035
  ),
826
1036
  // Right — social-post-style preview
827
1037
  h(
@@ -890,14 +1100,26 @@
890
1100
  style: { color: "var(--ck-text-tertiary)", fontSize: "0.85rem", fontStyle: "italic", padding: "1.5rem 0", textAlign: "center" }
891
1101
  }, "Start writing to see a preview")
892
1102
  ),
893
- // Media preview
894
- modalMediaUrl && h("img", {
895
- src: modalMediaUrl,
896
- style: { width: "100%", display: "block" },
897
- onError: (e) => {
898
- e.target.style.display = "none";
899
- }
900
- }),
1103
+ // Media preview (selected library media first, then URL)
1104
+ (modalSelectedMediaIds.length > 0 || modalMediaUrl) && h(
1105
+ "div",
1106
+ null,
1107
+ ...modalSelectedMediaIds.slice(0, 1).map((id) => {
1108
+ const item = modalMediaLibrary.find((m) => m.id === id);
1109
+ if (!item) return null;
1110
+ return item.mimeType?.startsWith("video/") ? h("div", {
1111
+ key: id,
1112
+ style: { background: "rgba(0,0,0,0.4)", padding: "1.25rem", textAlign: "center", color: "var(--ck-text-secondary)" }
1113
+ }, "\u{1F3A5} Video") : h("img", { key: id, src: item.thumbnailDataUrl, style: { width: "100%", display: "block" } });
1114
+ }),
1115
+ modalMediaUrl && h("img", {
1116
+ src: modalMediaUrl,
1117
+ style: { width: "100%", display: "block" },
1118
+ onError: (e) => {
1119
+ e.target.style.display = "none";
1120
+ }
1121
+ })
1122
+ ),
901
1123
  // Engagement bar
902
1124
  h(
903
1125
  "div",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jiggai/kitchen-plugin-marketing",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "Marketing Suite plugin for ClawKitchen",
5
5
  "main": "dist/index.js",
6
6
  "files": [