@frankieai/react 0.2.0 → 0.2.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.
- package/dist/frankie.css +1 -1
- package/dist/index.cjs +206 -178
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +202 -174
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/chat/AgentContext.tsx +22 -0
- package/src/components/chat/ChatPanel.tsx +9 -7
- package/src/components/chat/Composer.tsx +6 -5
- package/src/components/chat/FrankieChatPage.tsx +2 -3
- package/src/components/chat/MessageBubble.tsx +7 -5
- package/src/lib/client.ts +1 -1
- package/src/styles/frankie.css +61 -0
- package/src/widget.test.tsx +29 -0
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { createContext as createContext2, useContext as useContext2, useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
5
5
|
|
|
6
6
|
// src/lib/client.ts
|
|
7
|
-
var FRANKIE_CLOUD_URL = "https://frankie-data-
|
|
7
|
+
var FRANKIE_CLOUD_URL = "https://frankie-data-665333762719.us-central1.run.app";
|
|
8
8
|
var FrankieApiError = class extends Error {
|
|
9
9
|
constructor(message, status, code) {
|
|
10
10
|
super(message);
|
|
@@ -659,16 +659,51 @@ var ScrollArea = forwardRef(
|
|
|
659
659
|
}
|
|
660
660
|
);
|
|
661
661
|
|
|
662
|
+
// src/components/chat/AgentContext.tsx
|
|
663
|
+
import { ArrowUpRight, Sparkles } from "lucide-react";
|
|
664
|
+
import { jsx as jsx6, jsxs } from "react/jsx-runtime";
|
|
665
|
+
function AgentContext() {
|
|
666
|
+
const { loading, configError } = useFrankie();
|
|
667
|
+
const { loadedSkills, busy, restoring, error } = useFrankieChat();
|
|
668
|
+
const status = configError || error ? "Unavailable" : loading || restoring ? "Connecting" : busy && loadedSkills === null ? "Checking" : loadedSkills?.length ? "Active" : loadedSkills ? "Tools only" : "Ready";
|
|
669
|
+
return /* @__PURE__ */ jsxs("div", { className: "frankie-agent-context-wrap", children: [
|
|
670
|
+
/* @__PURE__ */ jsxs("details", { className: "frankie-agent-context", children: [
|
|
671
|
+
/* @__PURE__ */ jsxs("summary", { "aria-label": "Agent context", children: [
|
|
672
|
+
/* @__PURE__ */ jsx6(Sparkles, { size: 17, "aria-hidden": "true" }),
|
|
673
|
+
/* @__PURE__ */ jsx6("span", { children: "Agent context" }),
|
|
674
|
+
/* @__PURE__ */ jsx6("span", { className: "frankie-context-count", children: loadedSkills === null ? "Not checked" : `${loadedSkills.length} ${loadedSkills.length === 1 ? "skill" : "skills"}` }),
|
|
675
|
+
/* @__PURE__ */ jsxs("span", { className: "frankie-context-status", "data-active": status === "Active", children: [
|
|
676
|
+
/* @__PURE__ */ jsx6("i", { "aria-hidden": "true" }),
|
|
677
|
+
status
|
|
678
|
+
] }),
|
|
679
|
+
/* @__PURE__ */ jsx6(ArrowUpRight, { size: 15, "aria-hidden": "true" })
|
|
680
|
+
] }),
|
|
681
|
+
/* @__PURE__ */ jsxs("div", { className: "frankie-context-detail", children: [
|
|
682
|
+
/* @__PURE__ */ jsx6("strong", { children: loadedSkills === null ? "Skills are checked when you send a message" : loadedSkills.length ? `${loadedSkills.length} ${loadedSkills.length === 1 ? "skill" : "skills"} loaded` : "No skills loaded for this turn" }),
|
|
683
|
+
loadedSkills?.length ? /* @__PURE__ */ jsx6("ul", { children: loadedSkills.map((skill) => /* @__PURE__ */ jsxs("li", { children: [
|
|
684
|
+
/* @__PURE__ */ jsx6("span", { children: skill.name }),
|
|
685
|
+
/* @__PURE__ */ jsxs("small", { children: [
|
|
686
|
+
"v",
|
|
687
|
+
skill.revision
|
|
688
|
+
] })
|
|
689
|
+
] }, skill.id)) }) : /* @__PURE__ */ jsx6("p", { children: "Frankie can use the tools approved for this workspace. Drafts and skills without available tools are not loaded." }),
|
|
690
|
+
/* @__PURE__ */ jsx6("p", { children: "Published skills and updates are checked on every message. Only approved, available tools can run." })
|
|
691
|
+
] })
|
|
692
|
+
] }),
|
|
693
|
+
/* @__PURE__ */ jsx6("span", { className: "frankie-sr-only", role: "status", children: loadedSkills === null ? status : `${loadedSkills.length} ${loadedSkills.length === 1 ? "skill" : "skills"} loaded. ${status}.` })
|
|
694
|
+
] });
|
|
695
|
+
}
|
|
696
|
+
|
|
662
697
|
// src/components/chat/Composer.tsx
|
|
663
|
-
import { ArrowUp, Paperclip, Square, X } from "lucide-react";
|
|
698
|
+
import { ArrowUp, Paperclip, Plus, Square, X } from "lucide-react";
|
|
664
699
|
import { useCallback as useCallback2, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
665
700
|
|
|
666
701
|
// src/components/ui/textarea.tsx
|
|
667
702
|
import { forwardRef as forwardRef2 } from "react";
|
|
668
|
-
import { jsx as
|
|
703
|
+
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
669
704
|
var Textarea = forwardRef2(
|
|
670
705
|
function Textarea2({ className, ...props }, ref) {
|
|
671
|
-
return /* @__PURE__ */
|
|
706
|
+
return /* @__PURE__ */ jsx7(
|
|
672
707
|
"textarea",
|
|
673
708
|
{
|
|
674
709
|
ref,
|
|
@@ -683,7 +718,7 @@ var Textarea = forwardRef2(
|
|
|
683
718
|
);
|
|
684
719
|
|
|
685
720
|
// src/components/chat/Composer.tsx
|
|
686
|
-
import { Fragment, jsx as
|
|
721
|
+
import { Fragment, jsx as jsx8, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
687
722
|
var MAX_ROWS_PX = 140;
|
|
688
723
|
function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker }) {
|
|
689
724
|
const [value, setValue] = useState3("");
|
|
@@ -745,46 +780,46 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
745
780
|
});
|
|
746
781
|
}, [busy, disabled, files, modelPicker?.value, onSend, value]);
|
|
747
782
|
const onKeyDown = (event) => {
|
|
748
|
-
if (event.key === "Enter" && !event.shiftKey) {
|
|
783
|
+
if (event.key === "Enter" && !event.shiftKey && !event.nativeEvent.isComposing) {
|
|
749
784
|
event.preventDefault();
|
|
750
785
|
submit();
|
|
751
786
|
}
|
|
752
787
|
};
|
|
753
788
|
const nothingToSend = value.trim().length === 0 && files.length === 0;
|
|
754
|
-
return /* @__PURE__ */
|
|
789
|
+
return /* @__PURE__ */ jsxs2(
|
|
755
790
|
"form",
|
|
756
791
|
{
|
|
757
|
-
className: "flex flex-col gap-2",
|
|
792
|
+
className: "frankie-composer flex flex-col gap-2",
|
|
758
793
|
onSubmit: (event) => {
|
|
759
794
|
event.preventDefault();
|
|
760
795
|
submit();
|
|
761
796
|
},
|
|
762
797
|
children: [
|
|
763
|
-
files.length > 0 ? /* @__PURE__ */
|
|
798
|
+
files.length > 0 ? /* @__PURE__ */ jsx8("div", { className: "flex flex-wrap gap-2", children: files.map((file) => /* @__PURE__ */ jsxs2(
|
|
764
799
|
"span",
|
|
765
800
|
{
|
|
766
801
|
className: "inline-flex max-w-[14rem] items-center gap-1.5 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs",
|
|
767
802
|
children: [
|
|
768
|
-
/* @__PURE__ */
|
|
769
|
-
/* @__PURE__ */
|
|
770
|
-
/* @__PURE__ */
|
|
803
|
+
/* @__PURE__ */ jsx8(Paperclip, { className: "size-3 shrink-0 opacity-70" }),
|
|
804
|
+
/* @__PURE__ */ jsx8("span", { className: "truncate", children: file.name }),
|
|
805
|
+
/* @__PURE__ */ jsx8(
|
|
771
806
|
"button",
|
|
772
807
|
{
|
|
773
808
|
type: "button",
|
|
774
809
|
onClick: () => removeFile(file.id),
|
|
775
810
|
"aria-label": `Remove ${file.name}`,
|
|
776
811
|
className: "frankie-focus shrink-0 opacity-60 hover:opacity-100",
|
|
777
|
-
children: /* @__PURE__ */
|
|
812
|
+
children: /* @__PURE__ */ jsx8(X, { className: "size-3" })
|
|
778
813
|
}
|
|
779
814
|
)
|
|
780
815
|
]
|
|
781
816
|
},
|
|
782
817
|
file.id
|
|
783
818
|
)) }) : null,
|
|
784
|
-
fileError ? /* @__PURE__ */
|
|
785
|
-
/* @__PURE__ */
|
|
786
|
-
canAttach ? /* @__PURE__ */
|
|
787
|
-
/* @__PURE__ */
|
|
819
|
+
fileError ? /* @__PURE__ */ jsx8("p", { className: "text-xs text-red-600 dark:text-red-400", children: fileError }) : null,
|
|
820
|
+
/* @__PURE__ */ jsxs2("div", { className: "frankie-composer-controls flex items-end gap-2", children: [
|
|
821
|
+
canAttach ? /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
822
|
+
/* @__PURE__ */ jsx8(
|
|
788
823
|
"input",
|
|
789
824
|
{
|
|
790
825
|
ref: fileInputRef,
|
|
@@ -798,7 +833,7 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
798
833
|
}
|
|
799
834
|
}
|
|
800
835
|
),
|
|
801
|
-
/* @__PURE__ */
|
|
836
|
+
/* @__PURE__ */ jsx8(
|
|
802
837
|
Button,
|
|
803
838
|
{
|
|
804
839
|
type: "button",
|
|
@@ -808,11 +843,11 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
808
843
|
onClick: () => fileInputRef.current?.click(),
|
|
809
844
|
"aria-label": "Attach files",
|
|
810
845
|
title: "Attach files",
|
|
811
|
-
children: /* @__PURE__ */
|
|
846
|
+
children: /* @__PURE__ */ jsx8(Plus, {})
|
|
812
847
|
}
|
|
813
848
|
)
|
|
814
849
|
] }) : null,
|
|
815
|
-
/* @__PURE__ */
|
|
850
|
+
/* @__PURE__ */ jsx8(
|
|
816
851
|
Textarea,
|
|
817
852
|
{
|
|
818
853
|
ref: textareaRef,
|
|
@@ -826,7 +861,7 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
826
861
|
"aria-label": "Message"
|
|
827
862
|
}
|
|
828
863
|
),
|
|
829
|
-
models.length > 0 ? /* @__PURE__ */
|
|
864
|
+
models.length > 0 ? /* @__PURE__ */ jsx8(
|
|
830
865
|
"select",
|
|
831
866
|
{
|
|
832
867
|
value: modelPicker?.value ?? models[0]?.id,
|
|
@@ -834,10 +869,10 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
834
869
|
disabled,
|
|
835
870
|
"aria-label": "Model",
|
|
836
871
|
className: "frankie-focus h-9 max-w-[8rem] rounded-[var(--frankie-radius)] border border-border bg-background px-2 text-sm",
|
|
837
|
-
children: models.map((model) => /* @__PURE__ */
|
|
872
|
+
children: models.map((model) => /* @__PURE__ */ jsx8("option", { value: model.id, children: model.label }, model.id))
|
|
838
873
|
}
|
|
839
874
|
) : null,
|
|
840
|
-
busy ? /* @__PURE__ */
|
|
875
|
+
busy ? /* @__PURE__ */ jsx8(Button, { type: "button", size: "icon", variant: "outline", onClick: onStop, "aria-label": "Stop", children: /* @__PURE__ */ jsx8(Square, {}) }) : /* @__PURE__ */ jsx8(Button, { type: "submit", size: "icon", disabled: disabled || nothingToSend, "aria-label": "Send", children: /* @__PURE__ */ jsx8(ArrowUp, {}) })
|
|
841
876
|
] })
|
|
842
877
|
]
|
|
843
878
|
}
|
|
@@ -867,9 +902,9 @@ import { useState as useState4 } from "react";
|
|
|
867
902
|
|
|
868
903
|
// src/components/chat/Markdown.tsx
|
|
869
904
|
import { Fragment as Fragment2 } from "react";
|
|
870
|
-
import { jsx as
|
|
905
|
+
import { jsx as jsx9 } from "react/jsx-runtime";
|
|
871
906
|
function Markdown({ text, className }) {
|
|
872
|
-
return /* @__PURE__ */
|
|
907
|
+
return /* @__PURE__ */ jsx9("div", { className: cn("frankie-prose", className), children: renderBlocks(text) });
|
|
873
908
|
}
|
|
874
909
|
function renderBlocks(text) {
|
|
875
910
|
const lines = text.split("\n");
|
|
@@ -879,13 +914,13 @@ function renderBlocks(text) {
|
|
|
879
914
|
let code = null;
|
|
880
915
|
const flushParagraph = () => {
|
|
881
916
|
if (paragraph.length === 0) return;
|
|
882
|
-
blocks.push(/* @__PURE__ */
|
|
917
|
+
blocks.push(/* @__PURE__ */ jsx9("p", { children: renderInline(paragraph.join(" ")) }, `p${blocks.length}`));
|
|
883
918
|
paragraph = [];
|
|
884
919
|
};
|
|
885
920
|
const flushList = () => {
|
|
886
921
|
if (!list) return;
|
|
887
|
-
const items = list.items.map((item, index) => /* @__PURE__ */
|
|
888
|
-
blocks.push(list.ordered ? /* @__PURE__ */
|
|
922
|
+
const items = list.items.map((item, index) => /* @__PURE__ */ jsx9("li", { children: renderInline(item) }, index));
|
|
923
|
+
blocks.push(list.ordered ? /* @__PURE__ */ jsx9("ol", { children: items }, `l${blocks.length}`) : /* @__PURE__ */ jsx9("ul", { children: items }, `l${blocks.length}`));
|
|
889
924
|
list = null;
|
|
890
925
|
};
|
|
891
926
|
for (const line of lines) {
|
|
@@ -893,7 +928,7 @@ function renderBlocks(text) {
|
|
|
893
928
|
if (fence) {
|
|
894
929
|
if (code) {
|
|
895
930
|
blocks.push(
|
|
896
|
-
/* @__PURE__ */
|
|
931
|
+
/* @__PURE__ */ jsx9("pre", { children: /* @__PURE__ */ jsx9("code", { children: code.lines.join("\n") }) }, `c${blocks.length}`)
|
|
897
932
|
);
|
|
898
933
|
code = null;
|
|
899
934
|
} else {
|
|
@@ -928,7 +963,7 @@ function renderBlocks(text) {
|
|
|
928
963
|
if (heading) {
|
|
929
964
|
flushParagraph();
|
|
930
965
|
flushList();
|
|
931
|
-
blocks.push(/* @__PURE__ */
|
|
966
|
+
blocks.push(/* @__PURE__ */ jsx9("p", { className: "font-semibold", children: renderInline(heading[1] ?? "") }, `h${blocks.length}`));
|
|
932
967
|
continue;
|
|
933
968
|
}
|
|
934
969
|
flushList();
|
|
@@ -936,7 +971,7 @@ function renderBlocks(text) {
|
|
|
936
971
|
}
|
|
937
972
|
if (code) {
|
|
938
973
|
blocks.push(
|
|
939
|
-
/* @__PURE__ */
|
|
974
|
+
/* @__PURE__ */ jsx9("pre", { children: /* @__PURE__ */ jsx9("code", { children: code.lines.join("\n") }) }, `c${blocks.length}`)
|
|
940
975
|
);
|
|
941
976
|
}
|
|
942
977
|
flushParagraph();
|
|
@@ -951,20 +986,20 @@ function renderInline(text) {
|
|
|
951
986
|
const token = match[0];
|
|
952
987
|
const start = match.index;
|
|
953
988
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
954
|
-
nodes.push(/* @__PURE__ */
|
|
989
|
+
nodes.push(/* @__PURE__ */ jsx9(Fragment2, { children: renderToken(token) }, start));
|
|
955
990
|
lastIndex = start + token.length;
|
|
956
991
|
}
|
|
957
992
|
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
958
993
|
return nodes;
|
|
959
994
|
}
|
|
960
995
|
function renderToken(token) {
|
|
961
|
-
if (token.startsWith("**")) return /* @__PURE__ */
|
|
962
|
-
if (token.startsWith("`")) return /* @__PURE__ */
|
|
996
|
+
if (token.startsWith("**")) return /* @__PURE__ */ jsx9("strong", { children: token.slice(2, -2) });
|
|
997
|
+
if (token.startsWith("`")) return /* @__PURE__ */ jsx9("code", { children: token.slice(1, -1) });
|
|
963
998
|
const link = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(token);
|
|
964
999
|
const href = link ? link[2] ?? "" : token;
|
|
965
1000
|
const label = link ? link[1] ?? "" : token;
|
|
966
1001
|
if (!/^(https?:\/\/|\/)/.test(href)) return token;
|
|
967
|
-
return /* @__PURE__ */
|
|
1002
|
+
return /* @__PURE__ */ jsx9("a", { href, target: "_blank", rel: "noopener noreferrer nofollow", children: label });
|
|
968
1003
|
}
|
|
969
1004
|
|
|
970
1005
|
// src/components/chat/ToolActivityList.tsx
|
|
@@ -972,7 +1007,7 @@ import { Check, ChevronDown, Loader2, ShieldAlert, X as X2 } from "lucide-react"
|
|
|
972
1007
|
|
|
973
1008
|
// src/components/ui/badge.tsx
|
|
974
1009
|
import { cva as cva2 } from "class-variance-authority";
|
|
975
|
-
import { jsx as
|
|
1010
|
+
import { jsx as jsx10 } from "react/jsx-runtime";
|
|
976
1011
|
var badgeVariants = cva2(
|
|
977
1012
|
"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[0.6875rem] font-medium",
|
|
978
1013
|
{
|
|
@@ -992,44 +1027,44 @@ function Badge({
|
|
|
992
1027
|
variant,
|
|
993
1028
|
...props
|
|
994
1029
|
}) {
|
|
995
|
-
return /* @__PURE__ */
|
|
1030
|
+
return /* @__PURE__ */ jsx10("span", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
996
1031
|
}
|
|
997
1032
|
|
|
998
1033
|
// src/components/chat/ToolActivityList.tsx
|
|
999
|
-
import { jsx as
|
|
1034
|
+
import { jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
1000
1035
|
function ToolActivityList({ tools, renderTool }) {
|
|
1001
1036
|
if (tools.length === 0) return null;
|
|
1002
1037
|
const running = tools.some((tool) => tool.status === "running");
|
|
1003
|
-
return /* @__PURE__ */
|
|
1004
|
-
/* @__PURE__ */
|
|
1005
|
-
/* @__PURE__ */
|
|
1006
|
-
/* @__PURE__ */
|
|
1007
|
-
/* @__PURE__ */
|
|
1038
|
+
return /* @__PURE__ */ jsxs3("details", { className: "frankie-tool-activity mt-2", open: running, children: [
|
|
1039
|
+
/* @__PURE__ */ jsxs3("summary", { className: "inline-flex min-h-7 cursor-pointer list-none items-center gap-1.5 rounded-md px-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1040
|
+
/* @__PURE__ */ jsx11(ChevronDown, { className: "frankie-tool-chevron size-3.5 transition-transform", "aria-hidden": "true" }),
|
|
1041
|
+
/* @__PURE__ */ jsx11("span", { children: running ? "Working" : "Activity" }),
|
|
1042
|
+
/* @__PURE__ */ jsxs3("span", { className: "text-[11px]", children: [
|
|
1008
1043
|
"\xB7 ",
|
|
1009
1044
|
tools.length,
|
|
1010
1045
|
" tool call",
|
|
1011
1046
|
tools.length === 1 ? "" : "s"
|
|
1012
1047
|
] })
|
|
1013
1048
|
] }),
|
|
1014
|
-
/* @__PURE__ */
|
|
1049
|
+
/* @__PURE__ */ jsx11("ul", { className: "mt-1 flex flex-col gap-1 border-l border-border pl-2", children: tools.map((tool) => /* @__PURE__ */ jsx11("li", { children: renderTool ? renderTool(tool) : /* @__PURE__ */ jsx11(ToolActivityRow, { tool }) }, tool.id)) })
|
|
1015
1050
|
] });
|
|
1016
1051
|
}
|
|
1017
1052
|
function ToolActivityRow({ tool }) {
|
|
1018
|
-
return /* @__PURE__ */
|
|
1019
|
-
/* @__PURE__ */
|
|
1020
|
-
/* @__PURE__ */
|
|
1053
|
+
return /* @__PURE__ */ jsxs3(Badge, { variant: badgeVariant(tool.status), title: tool.detail ?? tool.name, children: [
|
|
1054
|
+
/* @__PURE__ */ jsx11(ToolIcon, { status: tool.status }),
|
|
1055
|
+
/* @__PURE__ */ jsx11("span", { className: "truncate", children: tool.label })
|
|
1021
1056
|
] });
|
|
1022
1057
|
}
|
|
1023
1058
|
function ToolIcon({ status }) {
|
|
1024
1059
|
switch (status) {
|
|
1025
1060
|
case "running":
|
|
1026
|
-
return /* @__PURE__ */
|
|
1061
|
+
return /* @__PURE__ */ jsx11(Loader2, { className: "size-3 animate-spin", "aria-hidden": "true" });
|
|
1027
1062
|
case "done":
|
|
1028
|
-
return /* @__PURE__ */
|
|
1063
|
+
return /* @__PURE__ */ jsx11(Check, { className: "size-3", "aria-hidden": "true" });
|
|
1029
1064
|
case "denied":
|
|
1030
|
-
return /* @__PURE__ */
|
|
1065
|
+
return /* @__PURE__ */ jsx11(ShieldAlert, { className: "size-3", "aria-hidden": "true" });
|
|
1031
1066
|
default:
|
|
1032
|
-
return /* @__PURE__ */
|
|
1067
|
+
return /* @__PURE__ */ jsx11(X2, { className: "size-3", "aria-hidden": "true" });
|
|
1033
1068
|
}
|
|
1034
1069
|
}
|
|
1035
1070
|
function badgeVariant(status) {
|
|
@@ -1040,7 +1075,7 @@ function badgeVariant(status) {
|
|
|
1040
1075
|
}
|
|
1041
1076
|
|
|
1042
1077
|
// src/components/chat/MessageBubble.tsx
|
|
1043
|
-
import { jsx as
|
|
1078
|
+
import { jsx as jsx12, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1044
1079
|
function MessageBubble({
|
|
1045
1080
|
message,
|
|
1046
1081
|
assistantName,
|
|
@@ -1049,33 +1084,33 @@ function MessageBubble({
|
|
|
1049
1084
|
renderTool
|
|
1050
1085
|
}) {
|
|
1051
1086
|
if (message.role === "user") {
|
|
1052
|
-
return /* @__PURE__ */
|
|
1053
|
-
message.attachments?.length ? /* @__PURE__ */
|
|
1087
|
+
return /* @__PURE__ */ jsxs4("div", { className: "frankie-user-message flex flex-col items-end gap-1", children: [
|
|
1088
|
+
message.attachments?.length ? /* @__PURE__ */ jsx12("div", { className: "flex max-w-[85%] flex-wrap justify-end gap-1", children: message.attachments.map((file, index) => /* @__PURE__ */ jsxs4(
|
|
1054
1089
|
"span",
|
|
1055
1090
|
{
|
|
1056
1091
|
className: "inline-flex max-w-[12rem] items-center gap-1 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs",
|
|
1057
1092
|
children: [
|
|
1058
|
-
/* @__PURE__ */
|
|
1059
|
-
/* @__PURE__ */
|
|
1093
|
+
/* @__PURE__ */ jsx12(Paperclip2, { className: "size-3 shrink-0 opacity-70" }),
|
|
1094
|
+
/* @__PURE__ */ jsx12("span", { className: "truncate", children: file.name })
|
|
1060
1095
|
]
|
|
1061
1096
|
},
|
|
1062
1097
|
`${file.name}-${index}`
|
|
1063
1098
|
)) }) : null,
|
|
1064
|
-
message.text ? /* @__PURE__ */
|
|
1065
|
-
/* @__PURE__ */
|
|
1066
|
-
/* @__PURE__ */
|
|
1099
|
+
message.text ? /* @__PURE__ */ jsxs4("div", { className: "flex max-w-[85%] items-end gap-1", children: [
|
|
1100
|
+
/* @__PURE__ */ jsx12("div", { className: "frankie-user-bubble rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground", children: message.text }),
|
|
1101
|
+
/* @__PURE__ */ jsx12(MessageCopyButton, { text: message.text })
|
|
1067
1102
|
] }) : null
|
|
1068
1103
|
] });
|
|
1069
1104
|
}
|
|
1070
1105
|
const empty = message.text.length === 0 && message.tools.length === 0;
|
|
1071
|
-
return /* @__PURE__ */
|
|
1072
|
-
/* @__PURE__ */
|
|
1073
|
-
/* @__PURE__ */
|
|
1074
|
-
message.text ? /* @__PURE__ */
|
|
1106
|
+
return /* @__PURE__ */ jsxs4("div", { className: "frankie-assistant-message flex gap-2", children: [
|
|
1107
|
+
/* @__PURE__ */ jsxs4("div", { className: "frankie-assistant-actions mt-0.5 flex shrink-0 flex-col items-center gap-1", children: [
|
|
1108
|
+
/* @__PURE__ */ jsx12(Avatar, { ...avatarUrl ? { src: avatarUrl } : {}, name: assistantName }),
|
|
1109
|
+
message.text ? /* @__PURE__ */ jsx12(MessageCopyButton, { text: message.text }) : null
|
|
1075
1110
|
] }),
|
|
1076
|
-
/* @__PURE__ */
|
|
1077
|
-
message.status === "error" ? /* @__PURE__ */
|
|
1078
|
-
showToolActivity ? /* @__PURE__ */
|
|
1111
|
+
/* @__PURE__ */ jsxs4("div", { className: "frankie-assistant-content min-w-0 max-w-[85%]", children: [
|
|
1112
|
+
message.status === "error" ? /* @__PURE__ */ jsx12("p", { className: "rounded-[var(--frankie-radius)] bg-red-500/10 px-3 py-2 text-sm text-red-600 dark:text-red-400", children: message.error }) : empty && message.status === "streaming" ? /* @__PURE__ */ jsx12(TypingDots, {}) : /* @__PURE__ */ jsx12(Markdown, { text: message.text, className: "text-sm" }),
|
|
1113
|
+
showToolActivity ? /* @__PURE__ */ jsx12(ToolActivityList, { tools: message.tools, ...renderTool ? { renderTool } : {} }) : null
|
|
1079
1114
|
] })
|
|
1080
1115
|
] });
|
|
1081
1116
|
}
|
|
@@ -1089,28 +1124,31 @@ function MessageCopyButton({ text }) {
|
|
|
1089
1124
|
} catch {
|
|
1090
1125
|
}
|
|
1091
1126
|
};
|
|
1092
|
-
return /* @__PURE__ */
|
|
1127
|
+
return /* @__PURE__ */ jsx12("button", { type: "button", onClick: () => void copy(), className: "frankie-message-copy inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-transparent text-muted-foreground transition hover:border-border hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary", "aria-label": copied ? "Copied" : "Copy message", title: copied ? "Copied" : "Copy message", children: copied ? /* @__PURE__ */ jsx12(Check2, { className: "size-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx12(Copy, { className: "size-3.5", "aria-hidden": "true" }) });
|
|
1093
1128
|
}
|
|
1094
1129
|
function TypingDots() {
|
|
1095
|
-
return /* @__PURE__ */
|
|
1096
|
-
"span",
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1130
|
+
return /* @__PURE__ */ jsxs4("span", { className: "frankie-thinking inline-flex items-center gap-1 py-2", role: "status", "aria-label": "Thinking", children: [
|
|
1131
|
+
/* @__PURE__ */ jsx12("span", { className: "frankie-thinking-label", children: "Thinking" }),
|
|
1132
|
+
[0, 1, 2].map((index) => /* @__PURE__ */ jsx12(
|
|
1133
|
+
"span",
|
|
1134
|
+
{
|
|
1135
|
+
className: cn("size-1.5 animate-pulse rounded-full bg-muted-foreground"),
|
|
1136
|
+
style: { animationDelay: `${index * 150}ms` }
|
|
1137
|
+
},
|
|
1138
|
+
index
|
|
1139
|
+
))
|
|
1140
|
+
] });
|
|
1103
1141
|
}
|
|
1104
1142
|
|
|
1105
1143
|
// src/components/chat/SuggestedPrompts.tsx
|
|
1106
|
-
import { jsx as
|
|
1144
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1107
1145
|
function SuggestedPrompts({
|
|
1108
1146
|
prompts,
|
|
1109
1147
|
onPick,
|
|
1110
1148
|
disabled
|
|
1111
1149
|
}) {
|
|
1112
1150
|
if (prompts.length === 0) return null;
|
|
1113
|
-
return /* @__PURE__ */
|
|
1151
|
+
return /* @__PURE__ */ jsx13("div", { className: "flex flex-wrap gap-2", children: prompts.map((prompt) => /* @__PURE__ */ jsx13(
|
|
1114
1152
|
Button,
|
|
1115
1153
|
{
|
|
1116
1154
|
type: "button",
|
|
@@ -1125,7 +1163,7 @@ function SuggestedPrompts({
|
|
|
1125
1163
|
}
|
|
1126
1164
|
|
|
1127
1165
|
// src/components/chat/ChatPanel.tsx
|
|
1128
|
-
import { jsx as
|
|
1166
|
+
import { jsx as jsx14, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1129
1167
|
function ChatPanel({
|
|
1130
1168
|
className,
|
|
1131
1169
|
header,
|
|
@@ -1140,7 +1178,7 @@ function ChatPanel({
|
|
|
1140
1178
|
}) {
|
|
1141
1179
|
const { config, configError, loading } = useFrankie();
|
|
1142
1180
|
const theme = useFrankieTheme();
|
|
1143
|
-
const { messages, busy, restoring,
|
|
1181
|
+
const { messages, busy, restoring, error, send, stop, reset } = useFrankieChat();
|
|
1144
1182
|
const scrollRef = useRef4(null);
|
|
1145
1183
|
const features = config?.features;
|
|
1146
1184
|
const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];
|
|
@@ -1153,11 +1191,12 @@ function ChatPanel({
|
|
|
1153
1191
|
}, [messages]);
|
|
1154
1192
|
const name = config?.branding.name ?? "Assistant";
|
|
1155
1193
|
const mode = resolveMode(theme?.mode);
|
|
1156
|
-
return /* @__PURE__ */
|
|
1194
|
+
return /* @__PURE__ */ jsxs5(
|
|
1157
1195
|
Card,
|
|
1158
1196
|
{
|
|
1159
1197
|
className: cn(
|
|
1160
|
-
"frankie-root",
|
|
1198
|
+
"frankie-root frankie-chat-panel",
|
|
1199
|
+
fill && "frankie-chat-panel-fill",
|
|
1161
1200
|
fill ? "h-full w-full rounded-none border-0 shadow-none" : "h-[32rem] w-[22rem] max-w-[calc(100vw-2rem)]",
|
|
1162
1201
|
mode === "dark" && "dark",
|
|
1163
1202
|
className
|
|
@@ -1165,24 +1204,25 @@ function ChatPanel({
|
|
|
1165
1204
|
style: themeToCssVars(theme),
|
|
1166
1205
|
"data-frankie-mode": mode,
|
|
1167
1206
|
children: [
|
|
1168
|
-
header === false ? null : header !== void 0 ? header : /* @__PURE__ */
|
|
1169
|
-
/* @__PURE__ */
|
|
1170
|
-
/* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
config?.branding.tagline ? /* @__PURE__ */
|
|
1207
|
+
header === false ? null : header !== void 0 ? header : /* @__PURE__ */ jsxs5(CardHeader, { children: [
|
|
1208
|
+
/* @__PURE__ */ jsx14(Avatar, { ...config?.branding.avatarUrl ? { src: config.branding.avatarUrl } : {}, name }),
|
|
1209
|
+
/* @__PURE__ */ jsxs5("div", { className: "min-w-0 flex-1", children: [
|
|
1210
|
+
/* @__PURE__ */ jsx14(CardTitle, { children: name }),
|
|
1211
|
+
config?.branding.tagline ? /* @__PURE__ */ jsx14(CardDescription, { children: config.branding.tagline }) : null
|
|
1173
1212
|
] }),
|
|
1174
|
-
/* @__PURE__ */
|
|
1175
|
-
onClose ? /* @__PURE__ */
|
|
1213
|
+
/* @__PURE__ */ jsx14(Button, { size: "icon", variant: "ghost", onClick: reset, "aria-label": "Start a new conversation", title: "New conversation", children: /* @__PURE__ */ jsx14(RotateCcw, {}) }),
|
|
1214
|
+
onClose ? /* @__PURE__ */ jsx14(Button, { size: "icon", variant: "ghost", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx14(X3, {}) }) : null
|
|
1176
1215
|
] }),
|
|
1177
|
-
/* @__PURE__ */
|
|
1178
|
-
|
|
1179
|
-
/* @__PURE__ */
|
|
1180
|
-
/* @__PURE__ */
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
/* @__PURE__ */
|
|
1184
|
-
|
|
1185
|
-
|
|
1216
|
+
/* @__PURE__ */ jsx14(AgentContext, {}),
|
|
1217
|
+
/* @__PURE__ */ jsx14(CardContent, { className: "frankie-conversation-content", children: /* @__PURE__ */ jsx14(ScrollArea, { ref: scrollRef, className: "frankie-transcript flex h-full flex-col gap-4 p-4", "aria-live": "polite", children: configError ? /* @__PURE__ */ jsxs5("div", { className: "frankie-config-error", role: "alert", children: [
|
|
1218
|
+
/* @__PURE__ */ jsx14("strong", { children: "Frankie couldn\u2019t start" }),
|
|
1219
|
+
/* @__PURE__ */ jsx14("p", { children: configError }),
|
|
1220
|
+
/* @__PURE__ */ jsx14("p", { children: "Refresh this page after checking the workspace\u2019s allowed origin and sign-in settings." })
|
|
1221
|
+
] }) : loading ? /* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: "Starting Frankie\u2026" }) : messages.length === 0 ? emptyState ?? /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-4", children: [
|
|
1222
|
+
/* @__PURE__ */ jsx14("p", { className: "text-sm text-muted-foreground", children: config?.greeting }),
|
|
1223
|
+
/* @__PURE__ */ jsx14(SuggestedPrompts, { prompts: config?.suggestedPrompts ?? [], onPick: send, disabled: busy })
|
|
1224
|
+
] }) : /* @__PURE__ */ jsx14("div", { className: "frankie-message-list flex flex-col gap-4", children: messages.map(
|
|
1225
|
+
(message) => renderMessage ? /* @__PURE__ */ jsx14("div", { children: renderMessage(message) }, message.id) : /* @__PURE__ */ jsx14(
|
|
1186
1226
|
MessageBubble,
|
|
1187
1227
|
{
|
|
1188
1228
|
message,
|
|
@@ -1194,19 +1234,9 @@ function ChatPanel({
|
|
|
1194
1234
|
message.id
|
|
1195
1235
|
)
|
|
1196
1236
|
) }) }) }),
|
|
1197
|
-
/* @__PURE__ */
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
loadedSkills.length ? /* @__PURE__ */ jsx13("ul", { children: loadedSkills.map((skill) => /* @__PURE__ */ jsxs4("li", { children: [
|
|
1201
|
-
skill.name,
|
|
1202
|
-
/* @__PURE__ */ jsxs4("small", { children: [
|
|
1203
|
-
"v",
|
|
1204
|
-
skill.revision
|
|
1205
|
-
] })
|
|
1206
|
-
] }, skill.id)) }) : /* @__PURE__ */ jsx13("p", { children: "Your assistant can still answer using its available tools." })
|
|
1207
|
-
] }) : null,
|
|
1208
|
-
error ? /* @__PURE__ */ jsx13("p", { className: "w-full text-xs text-red-600 dark:text-red-400", children: error }) : null,
|
|
1209
|
-
/* @__PURE__ */ jsx13("div", { className: "w-full", children: /* @__PURE__ */ jsx13(
|
|
1237
|
+
/* @__PURE__ */ jsxs5(CardFooter, { className: "frankie-composer-footer flex flex-col gap-2", children: [
|
|
1238
|
+
error ? /* @__PURE__ */ jsx14("p", { className: "w-full text-xs text-red-600 dark:text-red-400", children: error }) : null,
|
|
1239
|
+
/* @__PURE__ */ jsx14("div", { className: "w-full", children: /* @__PURE__ */ jsx14(
|
|
1210
1240
|
Composer,
|
|
1211
1241
|
{
|
|
1212
1242
|
onSend: send,
|
|
@@ -1227,7 +1257,7 @@ function ChatPanel({
|
|
|
1227
1257
|
}
|
|
1228
1258
|
|
|
1229
1259
|
// src/components/chat/FrankieWidget.tsx
|
|
1230
|
-
import { jsx as
|
|
1260
|
+
import { jsx as jsx15, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1231
1261
|
function FrankieWidget({
|
|
1232
1262
|
position = "bottom-right",
|
|
1233
1263
|
offset = 24,
|
|
@@ -1257,15 +1287,15 @@ function FrankieWidget({
|
|
|
1257
1287
|
const label = config?.branding.launcherLabel ?? "Need a hand?";
|
|
1258
1288
|
const mode = resolveMode(theme?.mode);
|
|
1259
1289
|
const side = position === "bottom-right" ? { right: offset } : { left: offset };
|
|
1260
|
-
return /* @__PURE__ */
|
|
1290
|
+
return /* @__PURE__ */ jsx15(
|
|
1261
1291
|
"div",
|
|
1262
1292
|
{
|
|
1263
1293
|
className: cn("frankie-root frankie-widget", mode === "dark" && "dark"),
|
|
1264
1294
|
style: { ...themeToCssVars(theme), position: "fixed", bottom: offset, ...side, zIndex, pointerEvents: "none" },
|
|
1265
1295
|
"data-frankie-mode": mode,
|
|
1266
|
-
children: /* @__PURE__ */
|
|
1267
|
-
open ? /* @__PURE__ */
|
|
1268
|
-
/* @__PURE__ */
|
|
1296
|
+
children: /* @__PURE__ */ jsxs6("div", { className: cn("flex flex-col gap-3", position === "bottom-left" ? "items-start" : "items-end"), children: [
|
|
1297
|
+
open ? /* @__PURE__ */ jsx15("div", { style: { pointerEvents: "auto", maxHeight: "calc(100dvh - 100px)", overflow: "hidden" }, children: /* @__PURE__ */ jsx15(ChatPanel, { ...panelProps, className: cn("frankie-widget-panel", panelProps.className), onClose: () => setOpen(false), autoFocus: true }) }) : null,
|
|
1298
|
+
/* @__PURE__ */ jsx15("div", { style: { pointerEvents: "auto" }, children: renderLauncher ? renderLauncher({ open, toggle: () => setOpen(!open), label }) : /* @__PURE__ */ jsx15(
|
|
1269
1299
|
Button,
|
|
1270
1300
|
{
|
|
1271
1301
|
size: "icon",
|
|
@@ -1274,7 +1304,7 @@ function FrankieWidget({
|
|
|
1274
1304
|
"aria-expanded": open,
|
|
1275
1305
|
"aria-label": open ? "Close the assistant" : label,
|
|
1276
1306
|
title: label,
|
|
1277
|
-
children: open ? /* @__PURE__ */
|
|
1307
|
+
children: open ? /* @__PURE__ */ jsx15(X4, { className: "size-5" }) : /* @__PURE__ */ jsx15(MessageCircle, { className: "size-5" })
|
|
1278
1308
|
}
|
|
1279
1309
|
) })
|
|
1280
1310
|
] })
|
|
@@ -1283,45 +1313,45 @@ function FrankieWidget({
|
|
|
1283
1313
|
}
|
|
1284
1314
|
|
|
1285
1315
|
// src/components/chat/FrankieInline.tsx
|
|
1286
|
-
import { jsx as
|
|
1316
|
+
import { jsx as jsx16 } from "react/jsx-runtime";
|
|
1287
1317
|
function FrankieInline({ className, style, ...panelProps }) {
|
|
1288
1318
|
const theme = useFrankieTheme();
|
|
1289
1319
|
const mode = resolveMode(theme?.mode);
|
|
1290
|
-
return /* @__PURE__ */
|
|
1320
|
+
return /* @__PURE__ */ jsx16(
|
|
1291
1321
|
"div",
|
|
1292
1322
|
{
|
|
1293
1323
|
className: cn("frankie-root frankie-inline", mode === "dark" && "dark", className),
|
|
1294
1324
|
style: { ...themeToCssVars(theme), display: "flex", height: "100%", width: "100%", ...style },
|
|
1295
1325
|
"data-frankie-mode": mode,
|
|
1296
|
-
children: /* @__PURE__ */
|
|
1326
|
+
children: /* @__PURE__ */ jsx16(ChatPanel, { ...panelProps, fill: true })
|
|
1297
1327
|
}
|
|
1298
1328
|
);
|
|
1299
1329
|
}
|
|
1300
1330
|
|
|
1301
1331
|
// src/components/chat/FrankieChatPage.tsx
|
|
1302
1332
|
import { useCallback as useCallback3, useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "react";
|
|
1303
|
-
import { History, Menu, Plus, Settings, X as X5 } from "lucide-react";
|
|
1304
|
-
import { Fragment as Fragment3, jsx as
|
|
1333
|
+
import { History, Menu, Plus as Plus2, Settings, X as X5 } from "lucide-react";
|
|
1334
|
+
import { Fragment as Fragment3, jsx as jsx17, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1305
1335
|
function PageEmptyState() {
|
|
1306
1336
|
const { config } = useFrankie();
|
|
1307
1337
|
const { send, busy, restoring } = useFrankieChat();
|
|
1308
1338
|
const name = config?.branding.name ?? "Frankie";
|
|
1309
|
-
return /* @__PURE__ */
|
|
1310
|
-
/* @__PURE__ */
|
|
1311
|
-
/* @__PURE__ */
|
|
1312
|
-
/* @__PURE__ */
|
|
1313
|
-
/* @__PURE__ */
|
|
1314
|
-
/* @__PURE__ */
|
|
1339
|
+
return /* @__PURE__ */ jsxs7("section", { className: "frankie-page-empty", "aria-label": "Start a conversation", children: [
|
|
1340
|
+
/* @__PURE__ */ jsx17("span", { className: "frankie-page-avatar", "aria-hidden": true, children: name.trim().slice(0, 1).toUpperCase() }),
|
|
1341
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1342
|
+
/* @__PURE__ */ jsx17("p", { className: "frankie-page-kicker", children: "Your product assistant" }),
|
|
1343
|
+
/* @__PURE__ */ jsx17("h1", { children: "How can I help?" }),
|
|
1344
|
+
/* @__PURE__ */ jsx17("p", { children: config?.greeting ?? "Ask a question or choose a task to get started." })
|
|
1315
1345
|
] }),
|
|
1316
|
-
/* @__PURE__ */
|
|
1317
|
-
/* @__PURE__ */
|
|
1318
|
-
/* @__PURE__ */
|
|
1346
|
+
/* @__PURE__ */ jsx17("div", { className: "frankie-page-prompts", children: config?.suggestedPrompts?.slice(0, 3).map((prompt) => /* @__PURE__ */ jsxs7("button", { type: "button", disabled: busy || restoring, onClick: () => void send(prompt), children: [
|
|
1347
|
+
/* @__PURE__ */ jsx17("strong", { children: prompt }),
|
|
1348
|
+
/* @__PURE__ */ jsx17("b", { "aria-hidden": true, children: "\u2197" })
|
|
1319
1349
|
] }, prompt)) })
|
|
1320
1350
|
] });
|
|
1321
1351
|
}
|
|
1322
1352
|
function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, ...panel }) {
|
|
1323
1353
|
const { client, config } = useFrankie();
|
|
1324
|
-
const { busy } = useFrankieChat();
|
|
1354
|
+
const { busy, messages } = useFrankieChat();
|
|
1325
1355
|
const theme = useFrankieTheme();
|
|
1326
1356
|
const [conversations, setConversations] = useState7([]);
|
|
1327
1357
|
const [historyError, setHistoryError] = useState7("");
|
|
@@ -1331,7 +1361,6 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, ...pa
|
|
|
1331
1361
|
const dialog = useRef5(null);
|
|
1332
1362
|
const menu = useRef5(null);
|
|
1333
1363
|
const requestVersion = useRef5(0);
|
|
1334
|
-
const name = config?.branding.name ?? "Frankie";
|
|
1335
1364
|
const refresh = useCallback3(async () => {
|
|
1336
1365
|
if (!showHistory || !config || config.allowAnonymous) {
|
|
1337
1366
|
setHistoryError("");
|
|
@@ -1373,77 +1402,76 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, ...pa
|
|
|
1373
1402
|
close();
|
|
1374
1403
|
}
|
|
1375
1404
|
};
|
|
1376
|
-
const history = /* @__PURE__ */
|
|
1377
|
-
/* @__PURE__ */
|
|
1378
|
-
/* @__PURE__ */
|
|
1379
|
-
/* @__PURE__ */
|
|
1405
|
+
const history = /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1406
|
+
/* @__PURE__ */ jsxs7("div", { className: "frankie-history-heading", children: [
|
|
1407
|
+
/* @__PURE__ */ jsx17("strong", { children: "Conversations" }),
|
|
1408
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: "frankie-mobile-only", "aria-label": "Close conversations", onClick: close, children: /* @__PURE__ */ jsx17(X5, { size: 18 }) })
|
|
1380
1409
|
] }),
|
|
1381
|
-
/* @__PURE__ */
|
|
1410
|
+
/* @__PURE__ */ jsxs7("button", { type: "button", className: "frankie-new-chat", disabled: busy, onClick: () => {
|
|
1382
1411
|
onNew();
|
|
1383
1412
|
close();
|
|
1384
1413
|
}, children: [
|
|
1385
|
-
/* @__PURE__ */
|
|
1414
|
+
/* @__PURE__ */ jsx17(Plus2, { size: 17 }),
|
|
1386
1415
|
"New chat"
|
|
1387
1416
|
] }),
|
|
1388
|
-
/* @__PURE__ */
|
|
1389
|
-
/* @__PURE__ */
|
|
1390
|
-
/* @__PURE__ */
|
|
1417
|
+
/* @__PURE__ */ jsxs7("label", { className: "frankie-history-search", children: [
|
|
1418
|
+
/* @__PURE__ */ jsx17("span", { children: "Search conversations" }),
|
|
1419
|
+
/* @__PURE__ */ jsx17("input", { type: "search", placeholder: "Search chats\u2026", value: query, onChange: (event) => setQuery(event.target.value) })
|
|
1391
1420
|
] }),
|
|
1392
|
-
/* @__PURE__ */
|
|
1393
|
-
/* @__PURE__ */
|
|
1394
|
-
/* @__PURE__ */
|
|
1421
|
+
/* @__PURE__ */ jsx17("nav", { className: "frankie-history-list", "aria-label": "Conversations", children: conversations.filter((item) => item.title.toLowerCase().includes(query.toLowerCase())).map((item) => /* @__PURE__ */ jsxs7("button", { type: "button", "aria-current": item.id === currentId ? "page" : void 0, disabled: busy, onClick: () => select(item.id), children: [
|
|
1422
|
+
/* @__PURE__ */ jsx17("span", { children: item.title }),
|
|
1423
|
+
/* @__PURE__ */ jsx17("small", { children: new Date(item.updatedAt).toLocaleDateString() })
|
|
1395
1424
|
] }, item.id)) }),
|
|
1396
|
-
historyLoading ? /* @__PURE__ */
|
|
1425
|
+
historyLoading ? /* @__PURE__ */ jsx17("p", { role: "status", children: "Loading history\u2026" }) : historyError ? /* @__PURE__ */ jsxs7("p", { role: "status", children: [
|
|
1397
1426
|
historyError,
|
|
1398
|
-
/* @__PURE__ */
|
|
1399
|
-
] }) : !conversations.length ? /* @__PURE__ */
|
|
1400
|
-
navigation ? /* @__PURE__ */
|
|
1427
|
+
/* @__PURE__ */ jsx17("button", { type: "button", onClick: () => void refresh(), children: "Try again" })
|
|
1428
|
+
] }) : !conversations.length ? /* @__PURE__ */ jsx17("p", { children: config?.allowAnonymous ? "Sign in to sync conversations across devices." : "Your conversations will appear here." }) : null,
|
|
1429
|
+
navigation ? /* @__PURE__ */ jsx17("div", { className: "frankie-host-navigation", children: navigation }) : null
|
|
1401
1430
|
] });
|
|
1402
|
-
return /* @__PURE__ */
|
|
1403
|
-
showHistory ? /* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1405
|
-
/* @__PURE__ */
|
|
1406
|
-
/* @__PURE__ */
|
|
1407
|
-
showHistory ? /* @__PURE__ */
|
|
1408
|
-
/* @__PURE__ */
|
|
1409
|
-
/* @__PURE__ */ jsx16("span", { className: "frankie-header-caption", children: "Product assistant" })
|
|
1431
|
+
return /* @__PURE__ */ jsxs7("div", { className: "frankie-page-shell" + (resolveMode(theme?.mode) === "dark" ? " dark" : ""), style: themeToCssVars(theme), children: [
|
|
1432
|
+
showHistory ? /* @__PURE__ */ jsx17("aside", { className: "frankie-history-desktop", children: history }) : null,
|
|
1433
|
+
/* @__PURE__ */ jsxs7("div", { className: "frankie-page-main", children: [
|
|
1434
|
+
/* @__PURE__ */ jsxs7("header", { className: "frankie-page-header", children: [
|
|
1435
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1436
|
+
showHistory ? /* @__PURE__ */ jsx17("button", { type: "button", className: "frankie-mobile-only", ref: menu, "aria-label": "Open conversations", "aria-haspopup": "dialog", "aria-expanded": drawerOpen, onClick: () => setDrawerOpen(true), children: /* @__PURE__ */ jsx17(Menu, { size: 20 }) }) : null,
|
|
1437
|
+
/* @__PURE__ */ jsx17("strong", { className: "frankie-thread-title", children: conversations.find((item) => item.id === currentId)?.title ?? messages.find((message) => message.role === "user")?.text ?? "New chat" })
|
|
1410
1438
|
] }),
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
/* @__PURE__ */
|
|
1413
|
-
/* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1439
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1440
|
+
/* @__PURE__ */ jsxs7("button", { type: "button", disabled: busy, onClick: onNew, "aria-label": "Start a new conversation", children: [
|
|
1441
|
+
/* @__PURE__ */ jsx17(Plus2, { size: 16 }),
|
|
1442
|
+
/* @__PURE__ */ jsx17("span", { children: "New chat" })
|
|
1415
1443
|
] }),
|
|
1416
|
-
/* @__PURE__ */
|
|
1417
|
-
/* @__PURE__ */
|
|
1418
|
-
/* @__PURE__ */
|
|
1419
|
-
/* @__PURE__ */
|
|
1420
|
-
/* @__PURE__ */
|
|
1421
|
-
/* @__PURE__ */
|
|
1422
|
-
/* @__PURE__ */
|
|
1444
|
+
/* @__PURE__ */ jsxs7("details", { className: "frankie-chat-settings", children: [
|
|
1445
|
+
/* @__PURE__ */ jsx17("summary", { "aria-label": "Chat settings", children: /* @__PURE__ */ jsx17(Settings, { size: 19 }) }),
|
|
1446
|
+
/* @__PURE__ */ jsxs7("div", { children: [
|
|
1447
|
+
/* @__PURE__ */ jsx17("strong", { children: "Chat settings" }),
|
|
1448
|
+
/* @__PURE__ */ jsx17("p", { children: "History syncs automatically for signed-in users." }),
|
|
1449
|
+
/* @__PURE__ */ jsxs7("button", { type: "button", onClick: () => void refresh(), disabled: historyLoading || !showHistory, children: [
|
|
1450
|
+
/* @__PURE__ */ jsx17(History, { size: 16 }),
|
|
1423
1451
|
"Refresh history"
|
|
1424
1452
|
] }),
|
|
1425
|
-
/* @__PURE__ */
|
|
1453
|
+
/* @__PURE__ */ jsx17("small", { children: "Skills are checked at the start of each message." })
|
|
1426
1454
|
] })
|
|
1427
1455
|
] })
|
|
1428
1456
|
] })
|
|
1429
1457
|
] }),
|
|
1430
|
-
/* @__PURE__ */
|
|
1458
|
+
/* @__PURE__ */ jsx17("div", { className: "frankie-page-canvas", children: /* @__PURE__ */ jsx17(FrankieInline, { ...panel, header: panel.header ?? false, emptyState: panel.emptyState ?? /* @__PURE__ */ jsx17(PageEmptyState, {}) }) })
|
|
1431
1459
|
] }),
|
|
1432
|
-
showHistory ? /* @__PURE__ */
|
|
1460
|
+
showHistory ? /* @__PURE__ */ jsx17("dialog", { ref: dialog, className: "frankie-history-dialog frankie-root", "aria-label": "Chat conversations", style: themeToCssVars(theme), onCancel: close, onClick: (event) => {
|
|
1433
1461
|
if (event.target === event.currentTarget) close();
|
|
1434
|
-
}, children: /* @__PURE__ */
|
|
1462
|
+
}, children: /* @__PURE__ */ jsx17("aside", { children: history }) }) : null
|
|
1435
1463
|
] });
|
|
1436
1464
|
}
|
|
1437
1465
|
function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, ...provider }) {
|
|
1438
1466
|
const [id, setId] = useState7(() => provider.conversationId ?? crypto.randomUUID());
|
|
1439
1467
|
const onNew = () => setId(crypto.randomUUID());
|
|
1440
|
-
return /* @__PURE__ */
|
|
1468
|
+
return /* @__PURE__ */ jsx17("div", { className: ["frankie-root", "frankie-chat-page", className].filter(Boolean).join(" "), style: { height: "100%", minHeight: "min(36rem, 100dvh)", width: "100%", ...style }, children: /* @__PURE__ */ jsx17(FrankieProvider, { ...provider, conversationId: id, storageNamespace: (provider.storageNamespace ?? "frankie." + provider.publishableKey) + "." + id, children: /* @__PURE__ */ jsx17(PageChrome, { showHistory, navigation, currentId: id, onSelect: setId, onNew, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus }) }) });
|
|
1441
1469
|
}
|
|
1442
1470
|
|
|
1443
1471
|
// src/components/ui/separator.tsx
|
|
1444
|
-
import { jsx as
|
|
1472
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
1445
1473
|
function Separator({ className }) {
|
|
1446
|
-
return /* @__PURE__ */
|
|
1474
|
+
return /* @__PURE__ */ jsx18("div", { role: "separator", className: cn("h-px w-full bg-border", className) });
|
|
1447
1475
|
}
|
|
1448
1476
|
export {
|
|
1449
1477
|
Avatar,
|