@orangecatai/adgen-canvas 0.0.4 → 0.0.5

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/dist/dev/index.js CHANGED
@@ -47849,7 +47849,13 @@ ${f.textContent}`).join("\n\n")}`;
47849
47849
  message: formatToolStartMessage(name, parsedArgs)
47850
47850
  });
47851
47851
  if (name === "generate_image" && onBeforeImageGen) {
47852
- const { allowed, error } = await onBeforeImageGen();
47852
+ let gateResult;
47853
+ try {
47854
+ gateResult = await onBeforeImageGen();
47855
+ } catch {
47856
+ gateResult = { allowed: false, error: "Credit check failed" };
47857
+ }
47858
+ const { allowed, error } = gateResult;
47853
47859
  if (!allowed) {
47854
47860
  const gatedResult = {
47855
47861
  success: false,
@@ -48397,6 +48403,8 @@ var AIChatPanel = React57.forwardRef(
48397
48403
  const [availableFrames, setAvailableFrames] = useState49([]);
48398
48404
  const [attachedFiles, setAttachedFiles] = useState49([]);
48399
48405
  const [webSearchEnabled, setWebSearchEnabled] = useState49(false);
48406
+ const [isSendPending, setIsSendPending] = useState49(false);
48407
+ const [isLoadingSession, setIsLoadingSession] = useState49(false);
48400
48408
  const [isRecording, setIsRecording] = useState49(false);
48401
48409
  const [isTranscribing, setIsTranscribing] = useState49(false);
48402
48410
  const [copiedMsgId, setCopiedMsgId] = useState49(null);
@@ -48408,6 +48416,10 @@ var AIChatPanel = React57.forwardRef(
48408
48416
  const audioChunksRef = useRef49([]);
48409
48417
  const prevPromptRef = useRef49("");
48410
48418
  const voiceUsedRef = useRef49(false);
48419
+ const currentSessionIdRef = useRef49(currentSessionId);
48420
+ useEffect55(() => {
48421
+ currentSessionIdRef.current = currentSessionId;
48422
+ }, [currentSessionId]);
48411
48423
  useEffect55(() => {
48412
48424
  const handler = (e) => {
48413
48425
  if (historyRef.current && !historyRef.current.contains(e.target)) {
@@ -48425,7 +48437,18 @@ var AIChatPanel = React57.forwardRef(
48425
48437
  abortControllerRef.current?.abort();
48426
48438
  }
48427
48439
  }, [isOpen]);
48428
- const currentTitle = messages.length > 0 ? messages[0].content.slice(0, 30) + (messages[0].content.length > 30 ? "\u2026" : "") : "New chat";
48440
+ const currentTitle = (() => {
48441
+ const sessionTitle = sessions.find(
48442
+ (s) => s.id === currentSessionId
48443
+ )?.title;
48444
+ if (sessionTitle) {
48445
+ return sessionTitle;
48446
+ }
48447
+ if (messages.length > 0) {
48448
+ return messages[0].content.slice(0, 30) + (messages[0].content.length > 30 ? "\u2026" : "");
48449
+ }
48450
+ return "New chat";
48451
+ })();
48429
48452
  const saveCurrentSession = useCallback24(() => {
48430
48453
  if (messages.length === 0) {
48431
48454
  return;
@@ -48447,25 +48470,29 @@ var AIChatPanel = React57.forwardRef(
48447
48470
  const newSessionId = genId();
48448
48471
  setMessages([]);
48449
48472
  setCurrentSessionId(newSessionId);
48473
+ setIsLoadingSession(false);
48450
48474
  setHistoryOpen(false);
48451
48475
  setError(null);
48452
48476
  setPrompt("");
48453
48477
  setFrameRef(null);
48454
48478
  setAttachedFiles([]);
48455
48479
  setStatusText("");
48456
- if (onSessionCreate) {
48457
- onSessionCreate({ id: newSessionId, title: "New chat" });
48458
- }
48459
- }, [saveCurrentSession, onSessionCreate]);
48480
+ }, [saveCurrentSession]);
48460
48481
  const handleSwitchSession = useCallback24(
48461
48482
  (session) => {
48462
48483
  saveCurrentSession();
48463
- setMessages(session.messages);
48464
48484
  setCurrentSessionId(session.id);
48465
48485
  setHistoryOpen(false);
48466
48486
  setError(null);
48467
- if (onSessionSwitch) {
48468
- onSessionSwitch(session.id);
48487
+ if (session.messages.length > 0) {
48488
+ setMessages(session.messages);
48489
+ setIsLoadingSession(false);
48490
+ } else {
48491
+ setMessages([]);
48492
+ setIsLoadingSession(true);
48493
+ if (onSessionSwitch) {
48494
+ onSessionSwitch(session.id);
48495
+ }
48469
48496
  }
48470
48497
  },
48471
48498
  [saveCurrentSession, onSessionSwitch]
@@ -48498,7 +48525,10 @@ var AIChatPanel = React57.forwardRef(
48498
48525
  if (!ctx) {
48499
48526
  return;
48500
48527
  }
48501
- const screenshot = await captureFrameScreenshot(excalidrawAPI, frame.id);
48528
+ const screenshot = await captureFrameScreenshot(
48529
+ excalidrawAPI,
48530
+ frame.id
48531
+ );
48502
48532
  setFrameRef({
48503
48533
  frameId: frame.id,
48504
48534
  label: ctx.frameInfo.name,
@@ -48618,7 +48648,10 @@ var AIChatPanel = React57.forwardRef(
48618
48648
  prevPromptRef.current = updated;
48619
48649
  setPrompt(updated);
48620
48650
  } catch (err) {
48621
- setError(err instanceof Error ? err.message : "Transcription failed");
48651
+ voiceUsedRef.current = false;
48652
+ setError(
48653
+ err instanceof Error ? err.message : "Transcription failed"
48654
+ );
48622
48655
  } finally {
48623
48656
  setIsTranscribing(false);
48624
48657
  setStatusText("");
@@ -48642,78 +48675,152 @@ var AIChatPanel = React57.forwardRef(
48642
48675
  }
48643
48676
  );
48644
48677
  }, []);
48645
- const handleSend = useCallback24(async (textOverride) => {
48646
- const text = (textOverride ?? prompt).trim();
48647
- const normalizedKey = resolveOpenRouterApiKey(apiKey);
48648
- if (!text || isLoading) {
48649
- return;
48650
- }
48651
- if (!normalizedKey) {
48652
- setError(
48653
- "No OpenRouter API key. Set VITE_APP_OPENROUTER_API_KEY in .env or pass the key via the `apiKey` prop."
48654
- );
48655
- return;
48656
- }
48657
- const hadVoice = voiceUsedRef.current;
48658
- voiceUsedRef.current = false;
48659
- if (onBeforeSend) {
48660
- const { allowed, error: creditError } = await onBeforeSend({
48661
- hasVoice: hadVoice
48662
- });
48663
- if (!allowed) {
48664
- setError(creditError || "Insufficient credits");
48678
+ const handleSend = useCallback24(
48679
+ async (textOverride) => {
48680
+ const text = (textOverride ?? prompt).trim();
48681
+ const normalizedKey = resolveOpenRouterApiKey(apiKey);
48682
+ if (!text || isLoading || isSendPending) {
48683
+ voiceUsedRef.current = false;
48665
48684
  return;
48666
48685
  }
48667
- }
48668
- const capturedFiles = [...attachedFiles];
48669
- const capturedWebSearch = webSearchEnabled;
48670
- const capturedFrameRef = frameRef;
48671
- const userMsg = {
48672
- id: genId(),
48673
- role: "user",
48674
- content: text,
48675
- timestamp: /* @__PURE__ */ new Date(),
48676
- attachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
48677
- name: f.name,
48678
- type: f.type,
48679
- dataUrl: f.dataUrl
48680
- })) : void 0
48681
- };
48682
- const nextMessages = [...messages, userMsg];
48683
- setMessages(nextMessages);
48684
- setPrompt("");
48685
- prevPromptRef.current = "";
48686
- setAttachedFiles([]);
48687
- setFrameRef(null);
48688
- const controller = new AbortController();
48689
- abortControllerRef.current = controller;
48690
- setIsLoading(true);
48691
- setError(null);
48692
- setStatusText("Thinking\u2026");
48693
- if (!excalidrawAPI) {
48694
- try {
48695
- const reply = await callPlainChatAPI(
48696
- nextMessages,
48697
- normalizedKey,
48698
- controller.signal,
48699
- {
48700
- webSearchEnabled: capturedWebSearch,
48701
- attachments: capturedFiles
48702
- }
48686
+ if (!normalizedKey) {
48687
+ voiceUsedRef.current = false;
48688
+ setError(
48689
+ "No OpenRouter API key. Set VITE_APP_OPENROUTER_API_KEY in .env or pass the key via the `apiKey` prop."
48703
48690
  );
48704
- const assistantMsg = {
48691
+ return;
48692
+ }
48693
+ const capturedFiles = [...attachedFiles];
48694
+ const capturedWebSearch = webSearchEnabled;
48695
+ const capturedFrameRef = frameRef;
48696
+ const userMsg = {
48697
+ id: genId(),
48698
+ role: "user",
48699
+ content: text,
48700
+ timestamp: /* @__PURE__ */ new Date(),
48701
+ attachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
48702
+ name: f.name,
48703
+ type: f.type,
48704
+ dataUrl: f.dataUrl
48705
+ })) : void 0
48706
+ };
48707
+ const isFirstMessage = messages.length === 0;
48708
+ const prevMessages = messages;
48709
+ const nextMessages = [...messages, userMsg];
48710
+ setMessages(nextMessages);
48711
+ setPrompt("");
48712
+ prevPromptRef.current = "";
48713
+ setAttachedFiles([]);
48714
+ setFrameRef(null);
48715
+ setIsSendPending(true);
48716
+ setError(null);
48717
+ const hadVoice = voiceUsedRef.current;
48718
+ voiceUsedRef.current = false;
48719
+ if (onBeforeSend) {
48720
+ const { allowed, error: creditError } = await onBeforeSend({
48721
+ hasVoice: hadVoice
48722
+ });
48723
+ if (!allowed) {
48724
+ setMessages(prevMessages);
48725
+ setError(creditError || "Insufficient credits");
48726
+ setIsSendPending(false);
48727
+ return;
48728
+ }
48729
+ }
48730
+ setIsSendPending(false);
48731
+ const controller = new AbortController();
48732
+ abortControllerRef.current = controller;
48733
+ setIsLoading(true);
48734
+ setStatusText("Thinking\u2026");
48735
+ if (!excalidrawAPI) {
48736
+ try {
48737
+ const reply = await callPlainChatAPI(
48738
+ nextMessages,
48739
+ normalizedKey,
48740
+ controller.signal,
48741
+ {
48742
+ webSearchEnabled: capturedWebSearch,
48743
+ attachments: capturedFiles
48744
+ }
48745
+ );
48746
+ const assistantMsg = {
48747
+ id: genId(),
48748
+ role: "assistant",
48749
+ content: reply,
48750
+ timestamp: /* @__PURE__ */ new Date()
48751
+ };
48752
+ const updatedMessages = [...nextMessages, assistantMsg];
48753
+ setMessages(updatedMessages);
48754
+ if (onAfterSend) {
48755
+ onAfterSend({ hasVoice: hadVoice });
48756
+ }
48757
+ if (isFirstMessage && onSessionCreate) {
48758
+ onSessionCreate({
48759
+ id: currentSessionId,
48760
+ title: text.slice(0, 40)
48761
+ });
48762
+ }
48763
+ if (onMessagesChange) {
48764
+ onMessagesChange(updatedMessages, currentSessionId);
48765
+ }
48766
+ } catch (err) {
48767
+ if (err instanceof Error && err.name !== "AbortError") {
48768
+ setError(err.message);
48769
+ }
48770
+ } finally {
48771
+ abortControllerRef.current = null;
48772
+ setIsLoading(false);
48773
+ setStatusText("");
48774
+ }
48775
+ return;
48776
+ }
48777
+ try {
48778
+ const result = await runAgentLoop({
48779
+ userMessages: nextMessages.map((m) => ({
48780
+ role: m.role,
48781
+ content: m.content
48782
+ })),
48783
+ elementContext: capturedFrameRef?.serialized,
48784
+ frameScreenshot: capturedFrameRef?.screenshot,
48785
+ fileAttachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
48786
+ name: f.name,
48787
+ type: f.type,
48788
+ dataUrl: f.dataUrl,
48789
+ textContent: f.textContent
48790
+ })) : void 0,
48791
+ webSearchEnabled: capturedWebSearch,
48792
+ apiKey: normalizedKey,
48793
+ toolCtx: {
48794
+ excalidrawAPI,
48795
+ geminiApiKey: geminiApiKey || "",
48796
+ signal: controller.signal
48797
+ },
48798
+ onUpdate: (update) => {
48799
+ if (update.type !== "final") {
48800
+ setStatusText(update.message);
48801
+ }
48802
+ },
48803
+ signal: controller.signal,
48804
+ onBeforeImageGen,
48805
+ onAfterImageGen
48806
+ });
48807
+ const assistantMsgAgent = {
48705
48808
  id: genId(),
48706
48809
  role: "assistant",
48707
- content: reply,
48708
- timestamp: /* @__PURE__ */ new Date()
48810
+ content: result.reply,
48811
+ timestamp: /* @__PURE__ */ new Date(),
48812
+ toolActions: result.toolActions.length > 0 ? result.toolActions : void 0
48709
48813
  };
48710
- const updatedMessages = [...nextMessages, assistantMsg];
48711
- setMessages(updatedMessages);
48814
+ const updatedMessagesAgent = [...nextMessages, assistantMsgAgent];
48815
+ setMessages(updatedMessagesAgent);
48712
48816
  if (onAfterSend) {
48713
48817
  onAfterSend({ hasVoice: hadVoice });
48714
48818
  }
48819
+ if (isFirstMessage && onSessionCreate) {
48820
+ onSessionCreate({ id: currentSessionId, title: text.slice(0, 40) });
48821
+ }
48715
48822
  if (onMessagesChange) {
48716
- onMessagesChange(updatedMessages, currentSessionId);
48823
+ onMessagesChange(updatedMessagesAgent, currentSessionId);
48717
48824
  }
48718
48825
  } catch (err) {
48719
48826
  if (err instanceof Error && err.name !== "AbortError") {
@@ -48724,86 +48831,49 @@ var AIChatPanel = React57.forwardRef(
48724
48831
  setIsLoading(false);
48725
48832
  setStatusText("");
48726
48833
  }
48727
- return;
48728
- }
48729
- try {
48730
- const result = await runAgentLoop({
48731
- userMessages: nextMessages.map((m) => ({
48732
- role: m.role,
48733
- content: m.content
48734
- })),
48735
- elementContext: capturedFrameRef?.serialized,
48736
- frameScreenshot: capturedFrameRef?.screenshot,
48737
- fileAttachments: capturedFiles.length > 0 ? capturedFiles.map((f) => ({
48738
- name: f.name,
48739
- type: f.type,
48740
- dataUrl: f.dataUrl,
48741
- textContent: f.textContent
48742
- })) : void 0,
48743
- webSearchEnabled: capturedWebSearch,
48744
- apiKey: normalizedKey,
48745
- toolCtx: {
48746
- excalidrawAPI,
48747
- geminiApiKey: geminiApiKey || "",
48748
- signal: controller.signal
48749
- },
48750
- onUpdate: (update) => {
48751
- if (update.type !== "final") {
48752
- setStatusText(update.message);
48753
- }
48754
- },
48755
- signal: controller.signal,
48756
- onBeforeImageGen,
48757
- onAfterImageGen
48758
- });
48759
- const assistantMsgAgent = {
48760
- id: genId(),
48761
- role: "assistant",
48762
- content: result.reply,
48763
- timestamp: /* @__PURE__ */ new Date(),
48764
- toolActions: result.toolActions.length > 0 ? result.toolActions : void 0
48765
- };
48766
- const updatedMessagesAgent = [...nextMessages, assistantMsgAgent];
48767
- setMessages(updatedMessagesAgent);
48768
- if (onAfterSend) {
48769
- onAfterSend({ hasVoice: hadVoice });
48770
- }
48771
- if (onMessagesChange) {
48772
- onMessagesChange(updatedMessagesAgent, currentSessionId);
48773
- }
48774
- } catch (err) {
48775
- if (err instanceof Error && err.name !== "AbortError") {
48776
- setError(err.message);
48777
- }
48778
- } finally {
48779
- abortControllerRef.current = null;
48780
- setIsLoading(false);
48781
- setStatusText("");
48782
- }
48783
- }, [
48784
- prompt,
48785
- isLoading,
48786
- messages,
48787
- apiKey,
48788
- excalidrawAPI,
48789
- geminiApiKey,
48790
- frameRef,
48791
- attachedFiles,
48792
- webSearchEnabled,
48793
- currentSessionId,
48794
- onBeforeSend,
48795
- onAfterSend,
48796
- onMessagesChange,
48797
- onBeforeImageGen,
48798
- onAfterImageGen
48799
- ]);
48834
+ },
48835
+ [
48836
+ prompt,
48837
+ isLoading,
48838
+ isSendPending,
48839
+ messages,
48840
+ apiKey,
48841
+ excalidrawAPI,
48842
+ geminiApiKey,
48843
+ frameRef,
48844
+ attachedFiles,
48845
+ webSearchEnabled,
48846
+ currentSessionId,
48847
+ onBeforeSend,
48848
+ onAfterSend,
48849
+ onMessagesChange,
48850
+ onSessionCreate,
48851
+ onBeforeImageGen,
48852
+ onAfterImageGen
48853
+ ]
48854
+ );
48800
48855
  useImperativeHandle4(
48801
48856
  ref,
48802
48857
  () => ({
48803
- setMessages,
48858
+ setMessages: (msgs, sessionId) => {
48859
+ const targetSessionId = sessionId ?? currentSessionIdRef.current;
48860
+ setSessions((prev) => {
48861
+ const idx = prev.findIndex((s) => s.id === targetSessionId);
48862
+ if (idx >= 0) {
48863
+ const next = [...prev];
48864
+ next[idx] = { ...next[idx], messages: msgs };
48865
+ return next;
48866
+ }
48867
+ return prev;
48868
+ });
48869
+ if (targetSessionId === currentSessionIdRef.current) {
48870
+ setMessages(msgs);
48871
+ setIsLoadingSession(false);
48872
+ }
48873
+ },
48804
48874
  send: (text) => handleSend(text)
48805
48875
  }),
48806
- [setMessages, handleSend]
48876
+ [handleSend]
48807
48877
  );
48808
48878
  const handleStop = useCallback24(() => {
48809
48879
  abortControllerRef.current?.abort();
@@ -48891,7 +48961,14 @@ var AIChatPanel = React57.forwardRef(
48891
48961
  ] })
48892
48962
  ] })
48893
48963
  ] }),
48894
- /* @__PURE__ */ jsx184("div", { className: "acp-messages-wrap", children: messages.length === 0 && !isLoading ? /* @__PURE__ */ jsxs100("div", { className: "acp-empty", children: [
48964
+ /* @__PURE__ */ jsx184("div", { className: "acp-messages-wrap", children: isLoadingSession ? /* @__PURE__ */ jsx184("div", { className: "acp-empty", children: /* @__PURE__ */ jsxs100("div", { className: "acp-loading-session", children: [
48965
+ /* @__PURE__ */ jsxs100("div", { className: "acp-loading-dots", children: [
48966
+ /* @__PURE__ */ jsx184("span", {}),
48967
+ /* @__PURE__ */ jsx184("span", {}),
48968
+ /* @__PURE__ */ jsx184("span", {})
48969
+ ] }),
48970
+ /* @__PURE__ */ jsx184("span", { className: "acp-status-text", children: "Loading chat\u2026" })
48971
+ ] }) }) : messages.length === 0 && !isLoading && !isSendPending ? /* @__PURE__ */ jsxs100("div", { className: "acp-empty", children: [
48895
48972
  /* @__PURE__ */ jsx184("div", { className: "acp-empty-icon", children: /* @__PURE__ */ jsx184(MessageSquare, { size: 22 }) }),
48896
48973
  /* @__PURE__ */ jsxs100("div", { children: [
48897
48974
  /* @__PURE__ */ jsx184("div", { className: "acp-empty-title", children: "AI Ad Designer" }),
@@ -49130,7 +49207,7 @@ var AIChatPanel = React57.forwardRef(
49130
49207
  {
49131
49208
  className: "acp-send-btn",
49132
49209
  onClick: isLoading ? handleStop : () => handleSend(),
49133
- disabled: !isLoading && !prompt.trim(),
49210
+ disabled: !isLoading && (!prompt.trim() || isSendPending),
49134
49211
  title: isLoading ? "Stop" : "Send",
49135
49212
  children: isLoading ? /* @__PURE__ */ jsx184("span", { className: "acp-stop-square" }) : /* @__PURE__ */ jsx184(ArrowUp3, { size: 16 })
49136
49213
  }