vibezcheck 0.2.0 → 0.3.0

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.
@@ -383,7 +383,7 @@ var VibezSessionProvider = ({
383
383
  };
384
384
 
385
385
  // src/react/hooks.ts
386
- import { useContext } from "react";
386
+ import { useContext, useState as useState2, useCallback as useCallback2, useRef } from "react";
387
387
  function useVibezSession() {
388
388
  const context = useContext(VibezSessionContext);
389
389
  if (!context) {
@@ -394,9 +394,196 @@ function useVibezSession() {
394
394
  return context;
395
395
  }
396
396
  var useVibez = useVibezSession;
397
+ function useVibezChat(options = {}) {
398
+ const {
399
+ api = "/api/chat",
400
+ model = "gpt-4o-mini",
401
+ customer = "anonymous",
402
+ body = {},
403
+ initialMessages = [],
404
+ onFinish,
405
+ onError
406
+ } = options;
407
+ const session = useContext(VibezSessionContext);
408
+ const [messages, setMessages] = useState2(initialMessages);
409
+ const [input, setInput] = useState2("");
410
+ const [isLoading, setIsLoading] = useState2(false);
411
+ const abortControllerRef = useRef(null);
412
+ const handleInputChange = useCallback2(
413
+ (e) => {
414
+ setInput(e.target.value);
415
+ },
416
+ []
417
+ );
418
+ const append = useCallback2(
419
+ async (userMessage) => {
420
+ const updatedMessages = [...messages, userMessage];
421
+ setMessages(updatedMessages);
422
+ setIsLoading(true);
423
+ const assistantId = "msg_" + Math.random().toString(36).substring(2, 9);
424
+ let assistantContent = "";
425
+ let detectedReasoning = "";
426
+ let detectedUsage = {
427
+ promptTokens: 0,
428
+ completionTokens: 0,
429
+ totalTokens: 0,
430
+ reasoningTokens: 0
431
+ };
432
+ setMessages((prev) => [
433
+ ...prev,
434
+ {
435
+ id: assistantId,
436
+ role: "assistant",
437
+ content: "",
438
+ createdAt: /* @__PURE__ */ new Date()
439
+ }
440
+ ]);
441
+ try {
442
+ abortControllerRef.current = new AbortController();
443
+ const res = await fetch(api, {
444
+ method: "POST",
445
+ headers: { "Content-Type": "application/json" },
446
+ body: JSON.stringify({
447
+ messages: updatedMessages.map((m) => ({
448
+ role: m.role,
449
+ content: m.content
450
+ })),
451
+ model,
452
+ customer,
453
+ ...body
454
+ }),
455
+ signal: abortControllerRef.current.signal
456
+ });
457
+ if (!res.ok) {
458
+ const errData = await res.json().catch(() => ({}));
459
+ throw new Error(errData.error || `HTTP error! status: ${res.status}`);
460
+ }
461
+ const reader = res.body?.getReader();
462
+ if (!reader) throw new Error("No stream body available");
463
+ const decoder = new TextDecoder();
464
+ let buffer = "";
465
+ while (true) {
466
+ const { done, value } = await reader.read();
467
+ if (done) break;
468
+ buffer += decoder.decode(value, { stream: true });
469
+ const lines = buffer.split("\n");
470
+ buffer = lines.pop() || "";
471
+ for (const line of lines) {
472
+ if (!line.trim()) continue;
473
+ if (line.startsWith("0:")) {
474
+ try {
475
+ assistantContent += JSON.parse(line.substring(2));
476
+ } catch {
477
+ assistantContent += line.substring(2).replace(/^"|"$/g, "");
478
+ }
479
+ } else if (line.startsWith("d:")) {
480
+ try {
481
+ const data = JSON.parse(line.substring(2));
482
+ if (data.usage) {
483
+ detectedUsage = {
484
+ promptTokens: data.usage.promptTokens ?? 0,
485
+ completionTokens: data.usage.completionTokens ?? 0,
486
+ totalTokens: (data.usage.promptTokens ?? 0) + (data.usage.completionTokens ?? 0),
487
+ reasoningTokens: data.usage.reasoningTokens ?? 0
488
+ };
489
+ }
490
+ } catch {
491
+ }
492
+ } else if (line.startsWith("e:") || line.startsWith("g:")) {
493
+ try {
494
+ detectedReasoning += JSON.parse(line.substring(2));
495
+ } catch {
496
+ detectedReasoning += line.substring(2);
497
+ }
498
+ } else if (!line.startsWith("f:") && !line.startsWith("2:")) {
499
+ assistantContent += line;
500
+ }
501
+ setMessages(
502
+ (prev) => prev.map(
503
+ (msg) => msg.id === assistantId ? {
504
+ ...msg,
505
+ content: assistantContent,
506
+ annotations: detectedReasoning ? [{ type: "reasoning", reasoning: detectedReasoning }] : void 0
507
+ } : msg
508
+ )
509
+ );
510
+ }
511
+ }
512
+ if (detectedUsage.promptTokens === 0 && detectedUsage.completionTokens === 0) {
513
+ const promptWords = updatedMessages.reduce(
514
+ (acc, m) => acc + m.content.split(/\s+/).length,
515
+ 0
516
+ );
517
+ const outputWords = assistantContent.split(/\s+/).length;
518
+ detectedUsage = {
519
+ promptTokens: Math.ceil(promptWords * 1.3),
520
+ completionTokens: Math.ceil(outputWords * 1.3),
521
+ totalTokens: Math.ceil((promptWords + outputWords) * 1.3),
522
+ reasoningTokens: detectedReasoning ? Math.ceil(detectedReasoning.split(/\s+/).length * 1.3) : 0
523
+ };
524
+ }
525
+ if (session) {
526
+ session.recordTurn({
527
+ model,
528
+ usage: detectedUsage
529
+ });
530
+ }
531
+ const finalMsg = {
532
+ id: assistantId,
533
+ role: "assistant",
534
+ content: assistantContent,
535
+ annotations: detectedReasoning ? [{ type: "reasoning", reasoning: detectedReasoning }] : void 0,
536
+ createdAt: /* @__PURE__ */ new Date()
537
+ };
538
+ if (onFinish) {
539
+ onFinish(finalMsg, detectedUsage);
540
+ }
541
+ } catch (err) {
542
+ if (err.name === "AbortError") return;
543
+ console.error("[useVibezChat Error]", err);
544
+ if (onError) onError(err);
545
+ } finally {
546
+ setIsLoading(false);
547
+ }
548
+ },
549
+ [api, model, customer, body, messages, session, onFinish, onError]
550
+ );
551
+ const handleSubmit = useCallback2(
552
+ (e) => {
553
+ if (e) e.preventDefault();
554
+ if (!input.trim() || isLoading) return;
555
+ const userMsg = {
556
+ id: "msg_" + Math.random().toString(36).substring(2, 9),
557
+ role: "user",
558
+ content: input.trim(),
559
+ createdAt: /* @__PURE__ */ new Date()
560
+ };
561
+ setInput("");
562
+ append(userMsg);
563
+ },
564
+ [input, isLoading, append]
565
+ );
566
+ const stop = useCallback2(() => {
567
+ if (abortControllerRef.current) {
568
+ abortControllerRef.current.abort();
569
+ setIsLoading(false);
570
+ }
571
+ }, []);
572
+ return {
573
+ messages,
574
+ setMessages,
575
+ input,
576
+ setInput,
577
+ handleInputChange,
578
+ handleSubmit,
579
+ isLoading,
580
+ stop,
581
+ append
582
+ };
583
+ }
397
584
 
398
585
  // src/react/widget.tsx
399
- import { useState as useState2 } from "react";
586
+ import { useState as useState3 } from "react";
400
587
  import { jsx as jsx2, jsxs } from "react/jsx-runtime";
401
588
  var VibezSessionWidget = ({
402
589
  position = "bottom-right",
@@ -408,7 +595,7 @@ var VibezSessionWidget = ({
408
595
  style
409
596
  }) => {
410
597
  const { sessionUsage, sessionCost, turnCount, byModel } = useVibezSession();
411
- const [expanded, setExpanded] = useState2(false);
598
+ const [expanded, setExpanded] = useState3(false);
412
599
  const hasUsage = sessionUsage.totalTokens > 0;
413
600
  const isDark = theme === "dark" || theme === "auto" && typeof window !== "undefined" && window.matchMedia?.("(prefers-color-scheme: dark)").matches;
414
601
  const colors = {
@@ -631,12 +818,168 @@ var VibezSessionBadge = ({
631
818
  }
632
819
  );
633
820
  };
821
+
822
+ // src/react/billing-modal.tsx
823
+ import { useState as useState4 } from "react";
824
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
825
+ var CreditCardIcon = () => /* @__PURE__ */ jsxs3("svg", { className: "h-5 w-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: [
826
+ /* @__PURE__ */ jsx4("rect", { x: "2", y: "5", width: "20", height: "14", rx: "2", strokeWidth: "2" }),
827
+ /* @__PURE__ */ jsx4("line", { x1: "2", y1: "10", x2: "22", y2: "10", strokeWidth: "2" })
828
+ ] });
829
+ var ShieldCheckIcon = () => /* @__PURE__ */ jsx4("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: "2", d: "M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" }) });
830
+ var ZapIcon = () => /* @__PURE__ */ jsx4("svg", { className: "h-3.5 w-3.5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("polygon", { points: "13 2 3 14 12 14 11 22 21 10 12 10 13 2", strokeWidth: "2", strokeLinejoin: "round" }) });
831
+ var CloseIcon = () => /* @__PURE__ */ jsxs3("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: [
832
+ /* @__PURE__ */ jsx4("line", { x1: "18", y1: "6", x2: "6", y2: "18", strokeWidth: "2", strokeLinecap: "round" }),
833
+ /* @__PURE__ */ jsx4("line", { x1: "6", y1: "6", x2: "18", y2: "18", strokeWidth: "2", strokeLinecap: "round" })
834
+ ] });
835
+ var CheckIcon = () => /* @__PURE__ */ jsx4("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx4("polyline", { points: "20 6 9 17 4 12", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) });
836
+ var ArrowRightIcon = () => /* @__PURE__ */ jsxs3("svg", { className: "h-4 w-4", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: [
837
+ /* @__PURE__ */ jsx4("line", { x1: "5", y1: "12", x2: "19", y2: "12", strokeWidth: "2", strokeLinecap: "round" }),
838
+ /* @__PURE__ */ jsx4("polyline", { points: "12 5 19 12 12 19", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" })
839
+ ] });
840
+ var VibezBillingModal = ({
841
+ notice,
842
+ isOpen: explicitIsOpen,
843
+ onClose,
844
+ onSuccess,
845
+ theme = "light",
846
+ testMode = true
847
+ }) => {
848
+ const [loading, setLoading] = useState4(false);
849
+ const [successState, setSuccessState] = useState4(false);
850
+ const [selectedPlan, setSelectedPlan] = useState4("starter");
851
+ const isVisible = explicitIsOpen !== void 0 ? explicitIsOpen : Boolean(notice);
852
+ if (!isVisible) return null;
853
+ const handleSimulatePayment = async () => {
854
+ setLoading(true);
855
+ if (notice?.checkoutUrl) {
856
+ window.location.href = notice.checkoutUrl;
857
+ return;
858
+ }
859
+ await new Promise((r) => setTimeout(r, 1200));
860
+ setLoading(false);
861
+ setSuccessState(true);
862
+ setTimeout(() => {
863
+ setSuccessState(false);
864
+ if (onSuccess) onSuccess();
865
+ if (onClose) onClose();
866
+ }, 1500);
867
+ };
868
+ const isLight = theme === "light";
869
+ return /* @__PURE__ */ jsx4("div", { className: "fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/60 backdrop-blur-sm animate-in fade-in duration-200", children: /* @__PURE__ */ jsxs3(
870
+ "div",
871
+ {
872
+ className: `relative w-full max-w-md rounded-2xl shadow-2xl border transition-all overflow-hidden ${isLight ? "bg-white border-slate-200 text-slate-900" : "bg-slate-900 border-slate-800 text-white"}`,
873
+ children: [
874
+ /* @__PURE__ */ jsx4("div", { className: "h-1.5 w-full bg-gradient-to-r from-indigo-500 via-purple-500 to-emerald-500" }),
875
+ onClose && /* @__PURE__ */ jsx4(
876
+ "button",
877
+ {
878
+ onClick: onClose,
879
+ className: "absolute top-3.5 right-3.5 p-1 rounded-lg text-slate-400 hover:text-slate-700 hover:bg-slate-100 transition",
880
+ children: /* @__PURE__ */ jsx4(CloseIcon, {})
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsxs3("div", { className: "p-6 space-y-5", children: [
884
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3", children: [
885
+ /* @__PURE__ */ jsx4("div", { className: "h-10 w-10 rounded-xl bg-indigo-600 flex items-center justify-center text-white shadow-md shadow-indigo-500/20 shrink-0", children: /* @__PURE__ */ jsx4(CreditCardIcon, {}) }),
886
+ /* @__PURE__ */ jsxs3("div", { children: [
887
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", children: [
888
+ /* @__PURE__ */ jsx4("h3", { className: "font-bold text-base tracking-tight", children: notice?.status === "limit_reached" ? "Token Limit Reached" : "Add Billing Credits" }),
889
+ testMode && /* @__PURE__ */ jsx4("span", { className: "text-[10px] uppercase font-bold px-2 py-0.5 rounded-full bg-amber-50 text-amber-700 border border-amber-200", children: "Stripe Sandbox" })
890
+ ] }),
891
+ /* @__PURE__ */ jsx4("p", { className: "text-xs text-slate-500 mt-0.5", children: notice?.message || "Top up your account to continue streaming AI tokens." })
892
+ ] })
893
+ ] }),
894
+ (notice?.tokensUsed || notice?.costUSD) && /* @__PURE__ */ jsxs3(
895
+ "div",
896
+ {
897
+ className: `p-3 rounded-xl border flex items-center justify-between text-xs ${isLight ? "bg-slate-50 border-slate-200" : "bg-slate-800 border-slate-700"}`,
898
+ children: [
899
+ /* @__PURE__ */ jsxs3("div", { children: [
900
+ /* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Tokens Metered:" }),
901
+ " ",
902
+ /* @__PURE__ */ jsx4("strong", { className: "text-slate-800 font-semibold", children: notice.tokensUsed?.toLocaleString() || 0 })
903
+ ] }),
904
+ /* @__PURE__ */ jsxs3("div", { children: [
905
+ /* @__PURE__ */ jsx4("span", { className: "text-slate-400", children: "Cost:" }),
906
+ " ",
907
+ /* @__PURE__ */ jsxs3("strong", { className: "text-indigo-600 font-semibold", children: [
908
+ "$",
909
+ notice.costUSD?.toFixed(4) || "0.0000",
910
+ " USD"
911
+ ] })
912
+ ] })
913
+ ]
914
+ }
915
+ ),
916
+ /* @__PURE__ */ jsxs3("div", { className: "space-y-2", children: [
917
+ /* @__PURE__ */ jsx4("label", { className: "text-xs font-semibold text-slate-500 uppercase tracking-wider", children: "Select Top-Up Option" }),
918
+ /* @__PURE__ */ jsxs3("div", { className: "grid grid-cols-2 gap-2.5", children: [
919
+ /* @__PURE__ */ jsxs3(
920
+ "button",
921
+ {
922
+ type: "button",
923
+ onClick: () => setSelectedPlan("starter"),
924
+ className: `p-3 rounded-xl border text-left transition relative ${selectedPlan === "starter" ? "border-indigo-600 bg-indigo-50/60 ring-2 ring-indigo-500/20" : isLight ? "border-slate-200 bg-white hover:border-slate-300" : "border-slate-800 bg-slate-800/50"}`,
925
+ children: [
926
+ /* @__PURE__ */ jsx4("div", { className: "text-xs font-bold text-slate-900", children: "$5.00 Top-Up" }),
927
+ /* @__PURE__ */ jsx4("div", { className: "text-[11px] text-slate-500 mt-0.5", children: "500k AI Tokens" })
928
+ ]
929
+ }
930
+ ),
931
+ /* @__PURE__ */ jsxs3(
932
+ "button",
933
+ {
934
+ type: "button",
935
+ onClick: () => setSelectedPlan("pro"),
936
+ className: `p-3 rounded-xl border text-left transition relative ${selectedPlan === "pro" ? "border-indigo-600 bg-indigo-50/60 ring-2 ring-indigo-500/20" : isLight ? "border-slate-200 bg-white hover:border-slate-300" : "border-slate-800 bg-slate-800/50"}`,
937
+ children: [
938
+ /* @__PURE__ */ jsx4("div", { className: "text-xs font-bold text-slate-900", children: "$20.00 Pro" }),
939
+ /* @__PURE__ */ jsx4("div", { className: "text-[11px] text-slate-500 mt-0.5", children: "2.5M AI Tokens" })
940
+ ]
941
+ }
942
+ )
943
+ ] })
944
+ ] }),
945
+ /* @__PURE__ */ jsx4(
946
+ "button",
947
+ {
948
+ onClick: handleSimulatePayment,
949
+ disabled: loading || successState,
950
+ className: "w-full py-2.5 px-4 rounded-xl bg-indigo-600 hover:bg-indigo-500 disabled:opacity-50 text-white text-sm font-semibold flex items-center justify-center gap-2 shadow-md shadow-indigo-500/20 transition",
951
+ children: loading ? /* @__PURE__ */ jsx4("span", { children: "Processing Stripe Sandbox Checkout..." }) : successState ? /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5 text-emerald-300 font-semibold", children: [
952
+ /* @__PURE__ */ jsx4(CheckIcon, {}),
953
+ " Credits Added Successfully!"
954
+ ] }) : /* @__PURE__ */ jsxs3("span", { className: "flex items-center gap-1.5", children: [
955
+ /* @__PURE__ */ jsx4("span", { children: "Checkout with Stripe" }),
956
+ /* @__PURE__ */ jsx4(ArrowRightIcon, {})
957
+ ] })
958
+ }
959
+ ),
960
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-center gap-4 text-[11px] text-slate-400 pt-1 border-t border-slate-100", children: [
961
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
962
+ /* @__PURE__ */ jsx4(ShieldCheckIcon, {}),
963
+ /* @__PURE__ */ jsx4("span", { children: "Stripe v2 Meter Events" })
964
+ ] }),
965
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
966
+ /* @__PURE__ */ jsx4(ZapIcon, {}),
967
+ /* @__PURE__ */ jsx4("span", { children: "Zero Latency Telemetry" })
968
+ ] })
969
+ ] })
970
+ ] })
971
+ ]
972
+ }
973
+ ) });
974
+ };
634
975
  export {
976
+ VibezBillingModal,
635
977
  VibezSessionBadge,
636
978
  VibezSessionContext,
637
979
  VibezSessionProvider,
638
980
  VibezSessionWidget,
639
981
  useVibez,
982
+ useVibezChat,
640
983
  useVibezSession
641
984
  };
642
985
  //# sourceMappingURL=index.mjs.map