mindcache 3.6.0 → 3.7.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.
package/dist/index.mjs CHANGED
@@ -4,9 +4,11 @@ import * as decoding from 'lib0/decoding';
4
4
  import * as Y from 'yjs';
5
5
  import { IndexeddbPersistence } from 'y-indexeddb';
6
6
  import diff from 'fast-diff';
7
- import { tool } from 'ai';
7
+ import { tool, streamText, stepCountIs } from 'ai';
8
8
  import { z } from 'zod';
9
- import { useState, useRef, useEffect } from 'react';
9
+ import React2, { createContext, useState, useRef, useEffect, useContext, useCallback } from 'react';
10
+ import { createOpenAI } from '@ai-sdk/openai';
11
+ import { jsx, jsxs } from 'react/jsx-runtime';
10
12
 
11
13
  var __defProp = Object.defineProperty;
12
14
  var __getOwnPropNames = Object.getOwnPropertyNames;
@@ -3405,10 +3407,1017 @@ function useMindCache(options) {
3405
3407
  error
3406
3408
  };
3407
3409
  }
3410
+ function createModel(provider, model, apiKey) {
3411
+ switch (provider) {
3412
+ case "openai": {
3413
+ const openai = createOpenAI({ apiKey });
3414
+ return openai(model);
3415
+ }
3416
+ case "anthropic":
3417
+ throw new Error("Anthropic provider not yet implemented. Use modelProvider for custom providers.");
3418
+ default:
3419
+ throw new Error(`Unknown provider: ${provider}. Use modelProvider for custom providers.`);
3420
+ }
3421
+ }
3422
+ var MindCacheContext = createContext(null);
3423
+ function useMindCacheContext() {
3424
+ const context = useContext(MindCacheContext);
3425
+ if (!context) {
3426
+ throw new Error("useMindCacheContext must be used within a MindCacheProvider");
3427
+ }
3428
+ return context;
3429
+ }
3430
+ function MindCacheProvider({
3431
+ mindcache: mcOptions,
3432
+ sync: syncConfig,
3433
+ ai: aiConfig = {},
3434
+ children
3435
+ }) {
3436
+ const [mindcache2, setMindcache] = useState(null);
3437
+ const [isLoaded, setIsLoaded] = useState(false);
3438
+ const [error, setError] = useState(null);
3439
+ const [hasApiKey, setHasApiKey] = useState(false);
3440
+ const [lastSyncAt, setLastSyncAt] = useState(null);
3441
+ const [isSyncing, setIsSyncing] = useState(false);
3442
+ const initRef = useRef(false);
3443
+ const resolvedAiConfig = {
3444
+ provider: "openai",
3445
+ model: "gpt-4o",
3446
+ keyStorage: "localStorage",
3447
+ storageKey: "ai_api_key",
3448
+ ...aiConfig
3449
+ };
3450
+ useEffect(() => {
3451
+ if (initRef.current) {
3452
+ return;
3453
+ }
3454
+ initRef.current = true;
3455
+ const init = async () => {
3456
+ try {
3457
+ const options = mcOptions || {
3458
+ indexedDB: {
3459
+ dbName: "mindcache_local_first",
3460
+ storeName: "mindcache_store",
3461
+ debounceMs: 1e3
3462
+ }
3463
+ };
3464
+ const mc = new MindCache(options);
3465
+ await mc.waitForSync();
3466
+ setMindcache(mc);
3467
+ setIsLoaded(true);
3468
+ if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
3469
+ const stored = localStorage.getItem(resolvedAiConfig.storageKey || "openai_api_key");
3470
+ setHasApiKey(!!stored);
3471
+ } else if (resolvedAiConfig.apiKey) {
3472
+ setHasApiKey(true);
3473
+ }
3474
+ } catch (err) {
3475
+ setError(err instanceof Error ? err : new Error(String(err)));
3476
+ setIsLoaded(true);
3477
+ }
3478
+ };
3479
+ init();
3480
+ return () => {
3481
+ if (mindcache2) {
3482
+ mindcache2.disconnect();
3483
+ }
3484
+ };
3485
+ }, []);
3486
+ const getApiKey = () => {
3487
+ if (resolvedAiConfig.apiKey) {
3488
+ return resolvedAiConfig.apiKey;
3489
+ }
3490
+ if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
3491
+ return localStorage.getItem(resolvedAiConfig.storageKey || "openai_api_key");
3492
+ }
3493
+ return null;
3494
+ };
3495
+ const setApiKey = (key) => {
3496
+ if (resolvedAiConfig.keyStorage === "localStorage" && typeof window !== "undefined") {
3497
+ localStorage.setItem(resolvedAiConfig.storageKey || "openai_api_key", key);
3498
+ setHasApiKey(true);
3499
+ }
3500
+ };
3501
+ const getModel = () => {
3502
+ const apiKey = getApiKey();
3503
+ if (!apiKey) {
3504
+ throw new Error("API key not configured. Call setApiKey() first or configure ai.apiKey.");
3505
+ }
3506
+ if (resolvedAiConfig.modelProvider) {
3507
+ return resolvedAiConfig.modelProvider(apiKey);
3508
+ }
3509
+ const provider = resolvedAiConfig.provider || "openai";
3510
+ const model = resolvedAiConfig.model || "gpt-4o";
3511
+ return createModel(provider, model, apiKey);
3512
+ };
3513
+ const syncToGitStore = async () => {
3514
+ if (!mindcache2 || !syncConfig?.gitstore) {
3515
+ return;
3516
+ }
3517
+ setIsSyncing(true);
3518
+ try {
3519
+ let gitStoreModule;
3520
+ try {
3521
+ gitStoreModule = await Function('return import("@mindcache/gitstore")')();
3522
+ } catch {
3523
+ throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
3524
+ }
3525
+ const { GitStore, MindCacheSync } = gitStoreModule;
3526
+ const token = typeof syncConfig.gitstore.token === "function" ? await syncConfig.gitstore.token() : syncConfig.gitstore.token;
3527
+ const gitStore = new GitStore({
3528
+ owner: syncConfig.gitstore.owner,
3529
+ repo: syncConfig.gitstore.repo,
3530
+ tokenProvider: async () => token
3531
+ });
3532
+ const sync = new MindCacheSync(gitStore, mindcache2, {
3533
+ filePath: syncConfig.gitstore.path || "mindcache.md"
3534
+ });
3535
+ await sync.save({ message: "Auto-sync from MindCache" });
3536
+ setLastSyncAt(/* @__PURE__ */ new Date());
3537
+ } catch (err) {
3538
+ console.error("[MindCacheProvider] Sync error:", err);
3539
+ throw err;
3540
+ } finally {
3541
+ setIsSyncing(false);
3542
+ }
3543
+ };
3544
+ const value = {
3545
+ mindcache: mindcache2,
3546
+ isLoaded,
3547
+ error,
3548
+ aiConfig: resolvedAiConfig,
3549
+ syncConfig,
3550
+ getApiKey,
3551
+ setApiKey,
3552
+ hasApiKey,
3553
+ getModel,
3554
+ syncToGitStore,
3555
+ lastSyncAt,
3556
+ isSyncing
3557
+ };
3558
+ return /* @__PURE__ */ jsx(MindCacheContext.Provider, { value, children });
3559
+ }
3560
+ function generateId() {
3561
+ return Math.random().toString(36).substring(2, 15) + Date.now().toString(36);
3562
+ }
3563
+ function useClientChat(options = {}) {
3564
+ const context = useMindCacheContext();
3565
+ const mc = options.mindcache || context.mindcache;
3566
+ const [messages, setMessages] = useState(options.initialMessages || []);
3567
+ const [status, setStatus] = useState("idle");
3568
+ const [error, setError] = useState(null);
3569
+ const [streamingContent, setStreamingContent] = useState("");
3570
+ const abortControllerRef = useRef(null);
3571
+ const {
3572
+ systemPrompt,
3573
+ onMindCacheChange,
3574
+ onFinish,
3575
+ onError,
3576
+ maxToolCalls = 5
3577
+ } = options;
3578
+ const apiKey = context.getApiKey();
3579
+ const addMessage = useCallback((msg) => {
3580
+ const newMessage = {
3581
+ ...msg,
3582
+ id: generateId(),
3583
+ createdAt: /* @__PURE__ */ new Date()
3584
+ };
3585
+ setMessages((prev) => [...prev, newMessage]);
3586
+ return newMessage;
3587
+ }, []);
3588
+ const clearMessages = useCallback(() => {
3589
+ setMessages([]);
3590
+ setError(null);
3591
+ setStreamingContent("");
3592
+ }, []);
3593
+ const stop = useCallback(() => {
3594
+ abortControllerRef.current?.abort();
3595
+ setStatus("idle");
3596
+ }, []);
3597
+ const sendMessage = useCallback(async (content) => {
3598
+ if (!mc) {
3599
+ const err = new Error("MindCache not initialized");
3600
+ setError(err);
3601
+ onError?.(err);
3602
+ return;
3603
+ }
3604
+ if (!apiKey) {
3605
+ const err = new Error("API key not configured. Please set your API key.");
3606
+ setError(err);
3607
+ onError?.(err);
3608
+ return;
3609
+ }
3610
+ abortControllerRef.current?.abort();
3611
+ abortControllerRef.current = new AbortController();
3612
+ const userMessage = addMessage({ role: "user", content });
3613
+ setStatus("loading");
3614
+ setError(null);
3615
+ setStreamingContent("");
3616
+ try {
3617
+ const model = context.getModel();
3618
+ const finalSystemPrompt = systemPrompt || mc.get_system_prompt();
3619
+ const tools = mc.create_vercel_ai_tools();
3620
+ const apiMessages = messages.concat(userMessage).map((m) => ({
3621
+ role: m.role,
3622
+ content: m.content
3623
+ }));
3624
+ setStatus("streaming");
3625
+ const parts = [];
3626
+ let accumulatedText = "";
3627
+ const result = await streamText({
3628
+ model,
3629
+ system: finalSystemPrompt,
3630
+ messages: apiMessages,
3631
+ tools,
3632
+ stopWhen: stepCountIs(maxToolCalls),
3633
+ abortSignal: abortControllerRef.current.signal,
3634
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
3635
+ onStepFinish: async (step) => {
3636
+ if (step.toolCalls && step.toolCalls.length > 0) {
3637
+ for (const toolCall of step.toolCalls) {
3638
+ const toolName = toolCall.toolName;
3639
+ const args = toolCall.args || toolCall.input || {};
3640
+ parts.push({
3641
+ type: "tool-call",
3642
+ toolCallId: toolCall.toolCallId,
3643
+ toolName,
3644
+ args
3645
+ });
3646
+ if (typeof toolName === "string" && (toolName.startsWith("write_") || toolName === "create_key")) {
3647
+ const value = args.value;
3648
+ const result2 = mc.executeToolCall(toolName, value);
3649
+ parts.push({
3650
+ type: "tool-result",
3651
+ toolCallId: toolCall.toolCallId,
3652
+ toolName,
3653
+ result: result2
3654
+ });
3655
+ onMindCacheChange?.();
3656
+ }
3657
+ }
3658
+ }
3659
+ if (step.text) {
3660
+ accumulatedText += step.text;
3661
+ }
3662
+ }
3663
+ });
3664
+ for await (const chunk of result.textStream) {
3665
+ accumulatedText += chunk;
3666
+ setStreamingContent(accumulatedText);
3667
+ }
3668
+ if (accumulatedText) {
3669
+ parts.unshift({ type: "text", text: accumulatedText });
3670
+ }
3671
+ const assistantMessage = {
3672
+ id: generateId(),
3673
+ role: "assistant",
3674
+ content: accumulatedText,
3675
+ parts: parts.length > 0 ? parts : void 0,
3676
+ createdAt: /* @__PURE__ */ new Date()
3677
+ };
3678
+ setMessages((prev) => [...prev, assistantMessage]);
3679
+ setStreamingContent("");
3680
+ setStatus("idle");
3681
+ onFinish?.(assistantMessage);
3682
+ } catch (err) {
3683
+ if (err.name === "AbortError") {
3684
+ if (streamingContent) {
3685
+ const partialMessage = {
3686
+ id: generateId(),
3687
+ role: "assistant",
3688
+ content: streamingContent + " [stopped]",
3689
+ createdAt: /* @__PURE__ */ new Date()
3690
+ };
3691
+ setMessages((prev) => [...prev, partialMessage]);
3692
+ }
3693
+ setStreamingContent("");
3694
+ setStatus("idle");
3695
+ return;
3696
+ }
3697
+ const error2 = err instanceof Error ? err : new Error(String(err));
3698
+ setError(error2);
3699
+ setStatus("error");
3700
+ setStreamingContent("");
3701
+ onError?.(error2);
3702
+ }
3703
+ }, [
3704
+ mc,
3705
+ apiKey,
3706
+ context,
3707
+ messages,
3708
+ systemPrompt,
3709
+ maxToolCalls,
3710
+ addMessage,
3711
+ onMindCacheChange,
3712
+ onFinish,
3713
+ onError,
3714
+ streamingContent
3715
+ ]);
3716
+ useEffect(() => {
3717
+ return () => {
3718
+ abortControllerRef.current?.abort();
3719
+ };
3720
+ }, []);
3721
+ return {
3722
+ messages,
3723
+ status,
3724
+ error,
3725
+ sendMessage,
3726
+ clearMessages,
3727
+ isLoading: status === "loading" || status === "streaming",
3728
+ addMessage,
3729
+ stop,
3730
+ streamingContent
3731
+ };
3732
+ }
3733
+ var defaultTheme = {
3734
+ background: "#000",
3735
+ userBubble: "#1a1a2e",
3736
+ assistantBubble: "#0d0d0d",
3737
+ textColor: "#22c55e",
3738
+ secondaryTextColor: "#6b7280",
3739
+ borderColor: "#333",
3740
+ primaryColor: "#22c55e",
3741
+ fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif'
3742
+ };
3743
+ function DefaultMessage({
3744
+ message,
3745
+ theme
3746
+ }) {
3747
+ const isUser = message.role === "user";
3748
+ return /* @__PURE__ */ jsx(
3749
+ "div",
3750
+ {
3751
+ style: {
3752
+ display: "flex",
3753
+ justifyContent: isUser ? "flex-end" : "flex-start",
3754
+ marginBottom: "12px"
3755
+ },
3756
+ children: /* @__PURE__ */ jsxs(
3757
+ "div",
3758
+ {
3759
+ style: {
3760
+ maxWidth: "80%",
3761
+ padding: "12px 16px",
3762
+ borderRadius: "12px",
3763
+ backgroundColor: isUser ? theme.userBubble : theme.assistantBubble,
3764
+ border: `1px solid ${theme.borderColor}`
3765
+ },
3766
+ children: [
3767
+ /* @__PURE__ */ jsx(
3768
+ "div",
3769
+ {
3770
+ style: {
3771
+ fontSize: "10px",
3772
+ color: theme.secondaryTextColor,
3773
+ marginBottom: "4px",
3774
+ textTransform: "uppercase"
3775
+ },
3776
+ children: isUser ? "You" : "Assistant"
3777
+ }
3778
+ ),
3779
+ /* @__PURE__ */ jsx(
3780
+ "div",
3781
+ {
3782
+ style: {
3783
+ color: theme.textColor,
3784
+ whiteSpace: "pre-wrap",
3785
+ wordBreak: "break-word",
3786
+ lineHeight: 1.5
3787
+ },
3788
+ children: message.content
3789
+ }
3790
+ )
3791
+ ]
3792
+ }
3793
+ )
3794
+ }
3795
+ );
3796
+ }
3797
+ function ApiKeyInput({
3798
+ theme,
3799
+ onSubmit
3800
+ }) {
3801
+ const [key, setKey] = useState("");
3802
+ const handleSubmit = (e) => {
3803
+ e.preventDefault();
3804
+ if (key.trim()) {
3805
+ onSubmit(key.trim());
3806
+ }
3807
+ };
3808
+ return /* @__PURE__ */ jsxs(
3809
+ "div",
3810
+ {
3811
+ style: {
3812
+ display: "flex",
3813
+ flexDirection: "column",
3814
+ alignItems: "center",
3815
+ justifyContent: "center",
3816
+ height: "100%",
3817
+ padding: "20px"
3818
+ },
3819
+ children: [
3820
+ /* @__PURE__ */ jsx(
3821
+ "div",
3822
+ {
3823
+ style: {
3824
+ fontSize: "14px",
3825
+ color: theme.textColor,
3826
+ marginBottom: "16px",
3827
+ textAlign: "center"
3828
+ },
3829
+ children: "Enter your API key to start chatting"
3830
+ }
3831
+ ),
3832
+ /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit, style: { width: "100%", maxWidth: "400px" }, children: [
3833
+ /* @__PURE__ */ jsx(
3834
+ "input",
3835
+ {
3836
+ type: "password",
3837
+ value: key,
3838
+ onChange: (e) => setKey(e.target.value),
3839
+ placeholder: "sk-...",
3840
+ style: {
3841
+ width: "100%",
3842
+ padding: "12px",
3843
+ backgroundColor: theme.assistantBubble,
3844
+ border: `1px solid ${theme.borderColor}`,
3845
+ borderRadius: "8px",
3846
+ color: theme.textColor,
3847
+ fontFamily: theme.fontFamily,
3848
+ fontSize: "14px",
3849
+ marginBottom: "12px"
3850
+ }
3851
+ }
3852
+ ),
3853
+ /* @__PURE__ */ jsx(
3854
+ "button",
3855
+ {
3856
+ type: "submit",
3857
+ disabled: !key.trim(),
3858
+ style: {
3859
+ width: "100%",
3860
+ padding: "12px",
3861
+ backgroundColor: theme.primaryColor,
3862
+ border: "none",
3863
+ borderRadius: "8px",
3864
+ color: "#000",
3865
+ fontFamily: theme.fontFamily,
3866
+ fontSize: "14px",
3867
+ fontWeight: "bold",
3868
+ cursor: key.trim() ? "pointer" : "not-allowed",
3869
+ opacity: key.trim() ? 1 : 0.5
3870
+ },
3871
+ children: "Save & Start"
3872
+ }
3873
+ )
3874
+ ] }),
3875
+ /* @__PURE__ */ jsx(
3876
+ "div",
3877
+ {
3878
+ style: {
3879
+ fontSize: "11px",
3880
+ color: theme.secondaryTextColor,
3881
+ marginTop: "16px",
3882
+ textAlign: "center"
3883
+ },
3884
+ children: "Your key is stored locally and never sent to our servers."
3885
+ }
3886
+ )
3887
+ ]
3888
+ }
3889
+ );
3890
+ }
3891
+ function MindCacheChat({
3892
+ theme: customTheme,
3893
+ placeholder = "Type a message...",
3894
+ welcomeMessage = "Hello! I'm ready to help you.",
3895
+ showApiKeyInput = true,
3896
+ className,
3897
+ style,
3898
+ renderMessage,
3899
+ header,
3900
+ footer,
3901
+ initialMessages,
3902
+ ...chatOptions
3903
+ }) {
3904
+ const context = useMindCacheContext();
3905
+ const theme = { ...defaultTheme, ...customTheme };
3906
+ const messagesEndRef = useRef(null);
3907
+ const inputRef = useRef(null);
3908
+ const [inputValue, setInputValue] = useState("");
3909
+ const defaultInitialMessages = welcomeMessage ? [
3910
+ {
3911
+ id: "welcome",
3912
+ role: "assistant",
3913
+ content: welcomeMessage,
3914
+ createdAt: /* @__PURE__ */ new Date()
3915
+ }
3916
+ ] : [];
3917
+ const {
3918
+ messages,
3919
+ sendMessage,
3920
+ isLoading,
3921
+ error,
3922
+ streamingContent,
3923
+ stop
3924
+ } = useClientChat({
3925
+ ...chatOptions,
3926
+ initialMessages: initialMessages || defaultInitialMessages,
3927
+ mindcache: context.mindcache || void 0
3928
+ });
3929
+ useEffect(() => {
3930
+ messagesEndRef.current?.scrollIntoView({ behavior: "smooth" });
3931
+ }, [messages]);
3932
+ const handleSubmit = async (e) => {
3933
+ e?.preventDefault();
3934
+ if (!inputValue.trim() || isLoading) {
3935
+ return;
3936
+ }
3937
+ const message = inputValue.trim();
3938
+ setInputValue("");
3939
+ await sendMessage(message);
3940
+ };
3941
+ const handleKeyDown = (e) => {
3942
+ if (e.key === "Enter" && !e.shiftKey) {
3943
+ e.preventDefault();
3944
+ handleSubmit();
3945
+ }
3946
+ };
3947
+ if (showApiKeyInput && !context.hasApiKey && context.aiConfig.keyStorage !== "memory") {
3948
+ return /* @__PURE__ */ jsxs(
3949
+ "div",
3950
+ {
3951
+ className,
3952
+ style: {
3953
+ display: "flex",
3954
+ flexDirection: "column",
3955
+ height: "100%",
3956
+ backgroundColor: theme.background,
3957
+ fontFamily: theme.fontFamily,
3958
+ ...style
3959
+ },
3960
+ children: [
3961
+ header,
3962
+ /* @__PURE__ */ jsx(
3963
+ ApiKeyInput,
3964
+ {
3965
+ theme,
3966
+ onSubmit: (key) => context.setApiKey(key)
3967
+ }
3968
+ ),
3969
+ footer
3970
+ ]
3971
+ }
3972
+ );
3973
+ }
3974
+ if (!context.isLoaded) {
3975
+ return /* @__PURE__ */ jsx(
3976
+ "div",
3977
+ {
3978
+ className,
3979
+ style: {
3980
+ display: "flex",
3981
+ alignItems: "center",
3982
+ justifyContent: "center",
3983
+ height: "100%",
3984
+ backgroundColor: theme.background,
3985
+ color: theme.textColor,
3986
+ fontFamily: theme.fontFamily,
3987
+ ...style
3988
+ },
3989
+ children: "Loading..."
3990
+ }
3991
+ );
3992
+ }
3993
+ return /* @__PURE__ */ jsxs(
3994
+ "div",
3995
+ {
3996
+ className,
3997
+ style: {
3998
+ display: "flex",
3999
+ flexDirection: "column",
4000
+ height: "100%",
4001
+ backgroundColor: theme.background,
4002
+ fontFamily: theme.fontFamily,
4003
+ ...style
4004
+ },
4005
+ children: [
4006
+ header,
4007
+ /* @__PURE__ */ jsxs(
4008
+ "div",
4009
+ {
4010
+ style: {
4011
+ flex: 1,
4012
+ overflowY: "auto",
4013
+ padding: "16px"
4014
+ },
4015
+ children: [
4016
+ messages.map((message) => renderMessage ? /* @__PURE__ */ jsx(React2.Fragment, { children: renderMessage(message) }, message.id) : /* @__PURE__ */ jsx(DefaultMessage, { message, theme }, message.id)),
4017
+ streamingContent && /* @__PURE__ */ jsx(
4018
+ "div",
4019
+ {
4020
+ style: {
4021
+ display: "flex",
4022
+ justifyContent: "flex-start",
4023
+ marginBottom: "12px"
4024
+ },
4025
+ children: /* @__PURE__ */ jsxs(
4026
+ "div",
4027
+ {
4028
+ style: {
4029
+ maxWidth: "80%",
4030
+ padding: "12px 16px",
4031
+ borderRadius: "12px",
4032
+ backgroundColor: theme.assistantBubble,
4033
+ border: `1px solid ${theme.borderColor}`
4034
+ },
4035
+ children: [
4036
+ /* @__PURE__ */ jsx(
4037
+ "div",
4038
+ {
4039
+ style: {
4040
+ fontSize: "10px",
4041
+ color: theme.secondaryTextColor,
4042
+ marginBottom: "4px",
4043
+ textTransform: "uppercase"
4044
+ },
4045
+ children: "Assistant"
4046
+ }
4047
+ ),
4048
+ /* @__PURE__ */ jsxs(
4049
+ "div",
4050
+ {
4051
+ style: {
4052
+ color: theme.textColor,
4053
+ whiteSpace: "pre-wrap",
4054
+ wordBreak: "break-word",
4055
+ lineHeight: 1.5
4056
+ },
4057
+ children: [
4058
+ streamingContent,
4059
+ /* @__PURE__ */ jsx("span", { style: { opacity: 0.5, animation: "blink 1s infinite" }, children: "\u258A" })
4060
+ ]
4061
+ }
4062
+ )
4063
+ ]
4064
+ }
4065
+ )
4066
+ }
4067
+ ),
4068
+ isLoading && !streamingContent && /* @__PURE__ */ jsx(
4069
+ "div",
4070
+ {
4071
+ style: {
4072
+ display: "flex",
4073
+ justifyContent: "flex-start",
4074
+ marginBottom: "12px"
4075
+ },
4076
+ children: /* @__PURE__ */ jsx(
4077
+ "div",
4078
+ {
4079
+ style: {
4080
+ padding: "12px 16px",
4081
+ borderRadius: "12px",
4082
+ backgroundColor: theme.assistantBubble,
4083
+ border: `1px solid ${theme.borderColor}`,
4084
+ color: theme.secondaryTextColor
4085
+ },
4086
+ children: "Thinking..."
4087
+ }
4088
+ )
4089
+ }
4090
+ ),
4091
+ error && /* @__PURE__ */ jsxs(
4092
+ "div",
4093
+ {
4094
+ style: {
4095
+ padding: "12px",
4096
+ marginBottom: "12px",
4097
+ borderRadius: "8px",
4098
+ backgroundColor: "rgba(239, 68, 68, 0.1)",
4099
+ border: "1px solid rgba(239, 68, 68, 0.3)",
4100
+ color: "#ef4444",
4101
+ fontSize: "13px"
4102
+ },
4103
+ children: [
4104
+ "Error: ",
4105
+ error.message
4106
+ ]
4107
+ }
4108
+ ),
4109
+ /* @__PURE__ */ jsx("div", { ref: messagesEndRef })
4110
+ ]
4111
+ }
4112
+ ),
4113
+ /* @__PURE__ */ jsx(
4114
+ "form",
4115
+ {
4116
+ onSubmit: handleSubmit,
4117
+ style: {
4118
+ padding: "12px 16px",
4119
+ borderTop: `1px solid ${theme.borderColor}`
4120
+ },
4121
+ children: /* @__PURE__ */ jsxs(
4122
+ "div",
4123
+ {
4124
+ style: {
4125
+ display: "flex",
4126
+ gap: "8px",
4127
+ alignItems: "flex-end"
4128
+ },
4129
+ children: [
4130
+ /* @__PURE__ */ jsx(
4131
+ "textarea",
4132
+ {
4133
+ ref: inputRef,
4134
+ value: inputValue,
4135
+ onChange: (e) => {
4136
+ setInputValue(e.target.value);
4137
+ e.target.style.height = "auto";
4138
+ e.target.style.height = Math.min(e.target.scrollHeight, 120) + "px";
4139
+ },
4140
+ onKeyDown: handleKeyDown,
4141
+ placeholder: isLoading ? "Waiting for response..." : placeholder,
4142
+ disabled: isLoading,
4143
+ rows: 1,
4144
+ style: {
4145
+ flex: 1,
4146
+ padding: "12px",
4147
+ backgroundColor: theme.assistantBubble,
4148
+ border: `1px solid ${theme.borderColor}`,
4149
+ borderRadius: "8px",
4150
+ color: theme.textColor,
4151
+ fontFamily: theme.fontFamily,
4152
+ fontSize: "16px",
4153
+ // Prevents iOS zoom on focus
4154
+ resize: "none",
4155
+ minHeight: "44px",
4156
+ // Apple touch target minimum
4157
+ maxHeight: "120px",
4158
+ outline: "none",
4159
+ WebkitAppearance: "none"
4160
+ // Remove iOS styling
4161
+ }
4162
+ }
4163
+ ),
4164
+ isLoading ? /* @__PURE__ */ jsx(
4165
+ "button",
4166
+ {
4167
+ type: "button",
4168
+ onClick: stop,
4169
+ style: {
4170
+ padding: "12px 20px",
4171
+ minWidth: "44px",
4172
+ // Touch target
4173
+ minHeight: "44px",
4174
+ // Touch target
4175
+ backgroundColor: "#ef4444",
4176
+ border: "none",
4177
+ borderRadius: "8px",
4178
+ color: "#fff",
4179
+ fontFamily: theme.fontFamily,
4180
+ fontSize: "16px",
4181
+ fontWeight: "bold",
4182
+ cursor: "pointer",
4183
+ transition: "opacity 0.2s",
4184
+ WebkitTapHighlightColor: "transparent",
4185
+ touchAction: "manipulation"
4186
+ // Faster touch response
4187
+ },
4188
+ children: "Stop"
4189
+ }
4190
+ ) : /* @__PURE__ */ jsx(
4191
+ "button",
4192
+ {
4193
+ type: "submit",
4194
+ disabled: !inputValue.trim(),
4195
+ style: {
4196
+ padding: "12px 20px",
4197
+ minWidth: "44px",
4198
+ // Touch target
4199
+ minHeight: "44px",
4200
+ // Touch target
4201
+ backgroundColor: theme.primaryColor,
4202
+ border: "none",
4203
+ borderRadius: "8px",
4204
+ color: "#000",
4205
+ fontFamily: theme.fontFamily,
4206
+ fontSize: "16px",
4207
+ fontWeight: "bold",
4208
+ cursor: !inputValue.trim() ? "not-allowed" : "pointer",
4209
+ opacity: !inputValue.trim() ? 0.5 : 1,
4210
+ transition: "opacity 0.2s",
4211
+ WebkitTapHighlightColor: "transparent",
4212
+ touchAction: "manipulation"
4213
+ // Faster touch response
4214
+ },
4215
+ children: "Send"
4216
+ }
4217
+ )
4218
+ ]
4219
+ }
4220
+ )
4221
+ }
4222
+ ),
4223
+ footer
4224
+ ]
4225
+ }
4226
+ );
4227
+ }
4228
+ function useLocalFirstSync(options) {
4229
+ const {
4230
+ mindcache: mindcache2,
4231
+ gitstore,
4232
+ server,
4233
+ autoSyncInterval = 0,
4234
+ saveDebounceMs = 5e3,
4235
+ loadOnMount = true,
4236
+ mergeOnLoad = true
4237
+ } = options;
4238
+ const [status, setStatus] = useState("idle");
4239
+ const [error, setError] = useState(null);
4240
+ const [lastSyncAt, setLastSyncAt] = useState(null);
4241
+ const [hasLocalChanges, setHasLocalChanges] = useState(false);
4242
+ const saveTimeoutRef = useRef(null);
4243
+ const syncIntervalRef = useRef(null);
4244
+ const mountedRef = useRef(true);
4245
+ const getToken = useCallback(async () => {
4246
+ if (!gitstore) {
4247
+ throw new Error("GitStore not configured");
4248
+ }
4249
+ return typeof gitstore.token === "function" ? await gitstore.token() : gitstore.token;
4250
+ }, [gitstore]);
4251
+ const load = useCallback(async () => {
4252
+ if (!mindcache2) {
4253
+ return;
4254
+ }
4255
+ setStatus("loading");
4256
+ setError(null);
4257
+ try {
4258
+ if (gitstore) {
4259
+ let gitStoreModule;
4260
+ try {
4261
+ gitStoreModule = await Function('return import("@mindcache/gitstore")')();
4262
+ } catch {
4263
+ throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
4264
+ }
4265
+ const { GitStore, MindCacheSync } = gitStoreModule;
4266
+ const token = await getToken();
4267
+ const store = new GitStore({
4268
+ owner: gitstore.owner,
4269
+ repo: gitstore.repo,
4270
+ branch: gitstore.branch,
4271
+ tokenProvider: async () => token
4272
+ });
4273
+ const sync2 = new MindCacheSync(store, mindcache2, {
4274
+ filePath: gitstore.path || "mindcache.md"
4275
+ });
4276
+ await sync2.load({ merge: mergeOnLoad });
4277
+ } else if (server) {
4278
+ const response = await fetch(server.url, {
4279
+ headers: server.authToken ? { Authorization: `Bearer ${server.authToken}` } : {}
4280
+ });
4281
+ if (response.ok) {
4282
+ const markdown = await response.text();
4283
+ mindcache2.fromMarkdown(markdown, mergeOnLoad);
4284
+ }
4285
+ }
4286
+ if (mountedRef.current) {
4287
+ setLastSyncAt(/* @__PURE__ */ new Date());
4288
+ setStatus("idle");
4289
+ }
4290
+ } catch (err) {
4291
+ if (mountedRef.current) {
4292
+ const error2 = err instanceof Error ? err : new Error(String(err));
4293
+ setError(error2);
4294
+ setStatus("error");
4295
+ }
4296
+ throw err;
4297
+ }
4298
+ }, [mindcache2, gitstore, server, getToken, mergeOnLoad]);
4299
+ const save = useCallback(async (message) => {
4300
+ if (!mindcache2) {
4301
+ return;
4302
+ }
4303
+ setStatus("saving");
4304
+ setError(null);
4305
+ try {
4306
+ if (gitstore) {
4307
+ let gitStoreModule;
4308
+ try {
4309
+ gitStoreModule = await Function('return import("@mindcache/gitstore")')();
4310
+ } catch {
4311
+ throw new Error("@mindcache/gitstore is not installed. Run: npm install @mindcache/gitstore");
4312
+ }
4313
+ const { GitStore, MindCacheSync } = gitStoreModule;
4314
+ const token = await getToken();
4315
+ const store = new GitStore({
4316
+ owner: gitstore.owner,
4317
+ repo: gitstore.repo,
4318
+ branch: gitstore.branch,
4319
+ tokenProvider: async () => token
4320
+ });
4321
+ const sync2 = new MindCacheSync(store, mindcache2, {
4322
+ filePath: gitstore.path || "mindcache.md"
4323
+ });
4324
+ await sync2.save({ message: message || "MindCache sync" });
4325
+ } else if (server) {
4326
+ await fetch(server.url, {
4327
+ method: "POST",
4328
+ headers: {
4329
+ "Content-Type": "text/plain",
4330
+ ...server.authToken ? { Authorization: `Bearer ${server.authToken}` } : {}
4331
+ },
4332
+ body: mindcache2.toMarkdown()
4333
+ });
4334
+ }
4335
+ if (mountedRef.current) {
4336
+ setLastSyncAt(/* @__PURE__ */ new Date());
4337
+ setHasLocalChanges(false);
4338
+ setStatus("idle");
4339
+ }
4340
+ } catch (err) {
4341
+ if (mountedRef.current) {
4342
+ const error2 = err instanceof Error ? err : new Error(String(err));
4343
+ setError(error2);
4344
+ setStatus("error");
4345
+ }
4346
+ throw err;
4347
+ }
4348
+ }, [mindcache2, gitstore, server, getToken]);
4349
+ const sync = useCallback(async () => {
4350
+ setStatus("syncing");
4351
+ try {
4352
+ await load();
4353
+ if (hasLocalChanges) {
4354
+ await save();
4355
+ }
4356
+ } catch (err) {
4357
+ }
4358
+ }, [load, save, hasLocalChanges]);
4359
+ const markSaved = useCallback(() => {
4360
+ setHasLocalChanges(false);
4361
+ }, []);
4362
+ useEffect(() => {
4363
+ if (!mindcache2 || !gitstore || saveDebounceMs <= 0) {
4364
+ return;
4365
+ }
4366
+ const unsubscribe = mindcache2.subscribeToAll(() => {
4367
+ setHasLocalChanges(true);
4368
+ if (saveTimeoutRef.current) {
4369
+ clearTimeout(saveTimeoutRef.current);
4370
+ }
4371
+ saveTimeoutRef.current = setTimeout(() => {
4372
+ save("Auto-save").catch(console.error);
4373
+ }, saveDebounceMs);
4374
+ });
4375
+ return () => {
4376
+ unsubscribe();
4377
+ if (saveTimeoutRef.current) {
4378
+ clearTimeout(saveTimeoutRef.current);
4379
+ }
4380
+ };
4381
+ }, [mindcache2, gitstore, saveDebounceMs, save]);
4382
+ useEffect(() => {
4383
+ if (!mindcache2 || autoSyncInterval <= 0) {
4384
+ return;
4385
+ }
4386
+ syncIntervalRef.current = setInterval(() => {
4387
+ sync().catch(console.error);
4388
+ }, autoSyncInterval);
4389
+ return () => {
4390
+ if (syncIntervalRef.current) {
4391
+ clearInterval(syncIntervalRef.current);
4392
+ }
4393
+ };
4394
+ }, [mindcache2, autoSyncInterval, sync]);
4395
+ useEffect(() => {
4396
+ if (loadOnMount && mindcache2 && (gitstore || server)) {
4397
+ load().catch(console.error);
4398
+ }
4399
+ }, [mindcache2, gitstore, server, loadOnMount]);
4400
+ useEffect(() => {
4401
+ mountedRef.current = true;
4402
+ return () => {
4403
+ mountedRef.current = false;
4404
+ };
4405
+ }, []);
4406
+ return {
4407
+ status,
4408
+ error,
4409
+ lastSyncAt,
4410
+ hasLocalChanges,
4411
+ load,
4412
+ save,
4413
+ sync,
4414
+ markSaved
4415
+ };
4416
+ }
3408
4417
 
3409
4418
  // src/index.ts
3410
4419
  var mindcache = new MindCache();
3411
4420
 
3412
- export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, OAuthClient, SchemaParser, SystemTagHelpers, createOAuthClient, mindcache, useMindCache };
4421
+ export { CloudAdapter, DEFAULT_KEY_ATTRIBUTES, IndexedDBAdapter, MindCache, MindCacheChat, MindCacheProvider, OAuthClient, SchemaParser, SystemTagHelpers, createOAuthClient, mindcache, useClientChat, useLocalFirstSync, useMindCache, useMindCacheContext };
3413
4422
  //# sourceMappingURL=index.mjs.map
3414
4423
  //# sourceMappingURL=index.mjs.map