@frankieai/react 0.2.17 → 0.2.19
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 +280 -87
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +47 -4
- package/dist/index.d.ts +47 -4
- package/dist/index.js +241 -49
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/bottom-bars.test.tsx +67 -0
- package/src/components/chat/ChatEmptyState.tsx +1 -1
- package/src/components/chat/ChatPanel.tsx +2 -2
- package/src/components/chat/FrankieChatPage.tsx +17 -5
- package/src/components/chat/FrankieInline.tsx +4 -4
- package/src/components/chat/FrankieWidget.tsx +70 -7
- package/src/components/chat/MessageBubble.tsx +7 -5
- package/src/generated/css-content.ts +1 -1
- package/src/hooks/FrankieProvider.tsx +17 -3
- package/src/hooks/useFrankieChat.ts +59 -8
- package/src/index.ts +1 -1
- package/src/lib/bottom-bars.ts +43 -0
- package/src/lib/client.ts +21 -3
- package/src/lib/theme.ts +22 -1
- package/src/lib/viewport-fill.ts +39 -0
- package/src/styles/frankie.css +22 -3
- package/src/theme-ready.test.tsx +36 -0
- package/src/turn-follow.test.tsx +111 -0
package/dist/index.cjs
CHANGED
|
@@ -61,7 +61,8 @@ __export(index_exports, {
|
|
|
61
61
|
themeToCssVars: () => themeToCssVars,
|
|
62
62
|
useFrankie: () => useFrankie,
|
|
63
63
|
useFrankieChat: () => useFrankieChat,
|
|
64
|
-
useFrankieTheme: () => useFrankieTheme
|
|
64
|
+
useFrankieTheme: () => useFrankieTheme,
|
|
65
|
+
useResolvedMode: () => useResolvedMode
|
|
65
66
|
});
|
|
66
67
|
module.exports = __toCommonJS(index_exports);
|
|
67
68
|
|
|
@@ -209,6 +210,22 @@ var FrankieClient = class {
|
|
|
209
210
|
const body = await this.unwrap(response);
|
|
210
211
|
return Array.isArray(body.conversations) ? body.conversations : [];
|
|
211
212
|
}
|
|
213
|
+
/** Whether a conversation can be reloaded from the server (a signed-in user and a conversation id). */
|
|
214
|
+
get canResume() {
|
|
215
|
+
return Boolean(this.options.conversationId && this.options.getUserToken);
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Ask the server to stop the running turn. Closing the stream no longer
|
|
219
|
+
* stops it (a closed tab shouldn't lose the reply), so Stop says so
|
|
220
|
+
* explicitly. Best effort: the stream is closed right after either way.
|
|
221
|
+
*/
|
|
222
|
+
async stopTurn() {
|
|
223
|
+
if (!this.session) return;
|
|
224
|
+
await this.doFetch(`${this.baseUrl}/v1/chat/stop`, {
|
|
225
|
+
method: "POST",
|
|
226
|
+
headers: { "x-frankie-key": this.options.publishableKey, authorization: `Bearer ${this.session.sessionToken}` }
|
|
227
|
+
}).catch(() => void 0);
|
|
228
|
+
}
|
|
212
229
|
async resumeConversation(id = this.options.conversationId) {
|
|
213
230
|
if (!id || !this.options.getUserToken) return void 0;
|
|
214
231
|
const userToken = await this.requireUserToken();
|
|
@@ -221,7 +238,7 @@ var FrankieClient = class {
|
|
|
221
238
|
this.session = body.session;
|
|
222
239
|
this.sessionUserKey = sessionIdentity(userToken);
|
|
223
240
|
this.writeStored();
|
|
224
|
-
return { messages: Array.isArray(body.messages) ? body.messages : [], title: body.title ?? "New chat" };
|
|
241
|
+
return { messages: Array.isArray(body.messages) ? body.messages : [], title: body.title ?? "New chat", busy: body.busy === true };
|
|
225
242
|
}
|
|
226
243
|
async syncConversation(messages, title) {
|
|
227
244
|
if (!this.session || !this.options.conversationId) return;
|
|
@@ -662,6 +679,30 @@ function useFrankieChatState() {
|
|
|
662
679
|
};
|
|
663
680
|
}, []);
|
|
664
681
|
const storageKey = `${client.namespace}.messages`;
|
|
682
|
+
const followingRef = (0, import_react.useRef)(false);
|
|
683
|
+
const followRunningTurn = (0, import_react.useCallback)(async () => {
|
|
684
|
+
if (followingRef.current || !client.canResume) return;
|
|
685
|
+
followingRef.current = true;
|
|
686
|
+
setBusy(true);
|
|
687
|
+
try {
|
|
688
|
+
const deadline = Date.now() + 3 * 6e4;
|
|
689
|
+
while (mounted.current && Date.now() < deadline) {
|
|
690
|
+
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
691
|
+
const remote = await client.resumeConversation().catch(() => void 0);
|
|
692
|
+
if (!remote || remote.busy) continue;
|
|
693
|
+
if (remote.messages.length) {
|
|
694
|
+
const finished = remote.messages;
|
|
695
|
+
messagesRef.current = finished;
|
|
696
|
+
if (mounted.current) setMessages(finished);
|
|
697
|
+
}
|
|
698
|
+
if (mounted.current) setError(void 0);
|
|
699
|
+
return;
|
|
700
|
+
}
|
|
701
|
+
} finally {
|
|
702
|
+
followingRef.current = false;
|
|
703
|
+
if (mounted.current) setBusy(false);
|
|
704
|
+
}
|
|
705
|
+
}, [client]);
|
|
665
706
|
(0, import_react.useEffect)(() => {
|
|
666
707
|
let active = true;
|
|
667
708
|
const restore = async () => {
|
|
@@ -679,10 +720,11 @@ function useFrankieChatState() {
|
|
|
679
720
|
try {
|
|
680
721
|
const remote = await client.resumeConversation();
|
|
681
722
|
if (active && remote?.messages.length) {
|
|
682
|
-
const restoredMessages = remote.messages;
|
|
723
|
+
const restoredMessages = remote.busy ? stillWorking(remote.messages) : remote.messages;
|
|
683
724
|
messagesRef.current = restoredMessages;
|
|
684
725
|
setMessages(restoredMessages);
|
|
685
726
|
}
|
|
727
|
+
if (active && remote?.busy) void followRunningTurn();
|
|
686
728
|
} catch (caught) {
|
|
687
729
|
if (active) {
|
|
688
730
|
const message = caught instanceof FrankieApiError ? caught.message : caught instanceof Error && caught.message.startsWith("Frankie") ? caught.message : "Could not restore this conversation. Check your sign-in and connection, then try opening it again.";
|
|
@@ -696,7 +738,7 @@ function useFrankieChatState() {
|
|
|
696
738
|
return () => {
|
|
697
739
|
active = false;
|
|
698
740
|
};
|
|
699
|
-
}, [client, storageKey]);
|
|
741
|
+
}, [client, storageKey, followRunningTurn]);
|
|
700
742
|
(0, import_react.useEffect)(() => {
|
|
701
743
|
if (!client.persist || !restored || busy) return;
|
|
702
744
|
try {
|
|
@@ -772,6 +814,7 @@ function useFrankieChatState() {
|
|
|
772
814
|
pendingThinking = "";
|
|
773
815
|
patchAssistant(assistantId, (message) => ({ ...message, text: message.text + addText, thinking: message.thinking + addThinking }));
|
|
774
816
|
};
|
|
817
|
+
let lostStream = false;
|
|
775
818
|
try {
|
|
776
819
|
let receivedText = false;
|
|
777
820
|
let receivedToolActivity = false;
|
|
@@ -825,6 +868,8 @@ function useFrankieChatState() {
|
|
|
825
868
|
status: "done",
|
|
826
869
|
text: message.text || "Stopped."
|
|
827
870
|
}));
|
|
871
|
+
} else if (!(caught instanceof FrankieApiError) && client.canResume) {
|
|
872
|
+
lostStream = true;
|
|
828
873
|
} else {
|
|
829
874
|
const message = caught instanceof FrankieApiError ? caught.message : caught instanceof Error && caught.message.startsWith("Frankie") ? caught.message : "Could not reach the assistant. Check your connection and try again.";
|
|
830
875
|
if (mounted.current) setError(message);
|
|
@@ -832,21 +877,28 @@ function useFrankieChatState() {
|
|
|
832
877
|
}
|
|
833
878
|
} finally {
|
|
834
879
|
flushDeltas();
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
880
|
+
if (!lostStream) {
|
|
881
|
+
patchAssistant(
|
|
882
|
+
assistantId,
|
|
883
|
+
(message) => message.status === "streaming" ? { ...message, status: "done" } : message
|
|
884
|
+
);
|
|
885
|
+
}
|
|
839
886
|
const transcript = messagesRef.current;
|
|
840
887
|
if (transcript.length) {
|
|
841
888
|
await client.syncConversation(transcript, conversationTitle(transcript)).catch(() => void 0);
|
|
842
889
|
}
|
|
843
890
|
if (mounted.current) setBusy(false);
|
|
844
891
|
abortRef.current = null;
|
|
892
|
+
if (lostStream) void followRunningTurn();
|
|
845
893
|
}
|
|
846
894
|
},
|
|
847
|
-
[busy, client, context, patchAssistant, restored]
|
|
895
|
+
[busy, client, context, patchAssistant, restored, followRunningTurn]
|
|
848
896
|
);
|
|
849
|
-
const stop = (0, import_react.useCallback)(() =>
|
|
897
|
+
const stop = (0, import_react.useCallback)(() => {
|
|
898
|
+
if (!abortRef.current) return;
|
|
899
|
+
void client.stopTurn();
|
|
900
|
+
abortRef.current.abort();
|
|
901
|
+
}, [client]);
|
|
850
902
|
const reset = (0, import_react.useCallback)(() => {
|
|
851
903
|
abortRef.current?.abort();
|
|
852
904
|
client.resetSession();
|
|
@@ -870,9 +922,15 @@ function conversationTitle(messages) {
|
|
|
870
922
|
const title = first.text.trim().replace(/\s+/g, " ");
|
|
871
923
|
return title.length > 42 ? `${title.slice(0, 42)}\u2026` : title;
|
|
872
924
|
}
|
|
925
|
+
function stillWorking(messages) {
|
|
926
|
+
const last = messages[messages.length - 1];
|
|
927
|
+
if (last?.role === "assistant") return [...messages.slice(0, -1), { ...last, status: "streaming", error: void 0 }];
|
|
928
|
+
return [...messages, { id: "pending_" + Date.now(), role: "assistant", text: "", thinking: "", tools: [], status: "streaming", createdAt: Date.now() }];
|
|
929
|
+
}
|
|
873
930
|
|
|
874
931
|
// src/hooks/FrankieProvider.tsx
|
|
875
932
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
933
|
+
var useIsomorphicLayoutEffect = typeof window === "undefined" ? import_react2.useEffect : import_react2.useLayoutEffect;
|
|
876
934
|
var FrankieContext = (0, import_react2.createContext)(null);
|
|
877
935
|
function FrankieProvider({
|
|
878
936
|
children,
|
|
@@ -912,7 +970,8 @@ function FrankieProvider({
|
|
|
912
970
|
}, [configKey, providedConfig]);
|
|
913
971
|
const inheriting = (theme?.source ?? config?.theme?.source) === "inherit";
|
|
914
972
|
const [hostTheme, setHostTheme] = (0, import_react2.useState)(null);
|
|
915
|
-
(0, import_react2.
|
|
973
|
+
const [hostSampled, setHostSampled] = (0, import_react2.useState)(false);
|
|
974
|
+
useIsomorphicLayoutEffect(() => {
|
|
916
975
|
if (!inheriting) {
|
|
917
976
|
setHostTheme(null);
|
|
918
977
|
return;
|
|
@@ -922,6 +981,7 @@ function FrankieProvider({
|
|
|
922
981
|
return JSON.stringify(previous) === JSON.stringify(next) ? previous : next;
|
|
923
982
|
});
|
|
924
983
|
sample();
|
|
984
|
+
setHostSampled(true);
|
|
925
985
|
return watchHostTheme(sample);
|
|
926
986
|
}, [inheriting]);
|
|
927
987
|
const value = (0, import_react2.useMemo)(
|
|
@@ -933,9 +993,10 @@ function FrankieProvider({
|
|
|
933
993
|
...context ? { context } : {},
|
|
934
994
|
...theme ? { theme } : {},
|
|
935
995
|
injectStyles,
|
|
936
|
-
hostTheme
|
|
996
|
+
hostTheme,
|
|
997
|
+
themeReady: Boolean(config || configError) && (!inheriting || hostSampled)
|
|
937
998
|
}),
|
|
938
|
-
[client, config, configError, loading, context, theme, injectStyles, hostTheme]
|
|
999
|
+
[client, config, configError, loading, context, theme, injectStyles, hostTheme, inheriting, hostSampled]
|
|
939
1000
|
);
|
|
940
1001
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FrankieContext.Provider, { value, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ConversationState, { children }, client.namespace + ":" + (client.conversationId ?? "")) });
|
|
941
1002
|
}
|
|
@@ -960,14 +1021,14 @@ function useFrankieTheme() {
|
|
|
960
1021
|
|
|
961
1022
|
// src/components/chat/FrankieWidget.tsx
|
|
962
1023
|
var import_lucide_react8 = require("lucide-react");
|
|
963
|
-
var
|
|
1024
|
+
var import_react11 = require("react");
|
|
964
1025
|
|
|
965
1026
|
// src/lib/styles-ready.ts
|
|
966
1027
|
var import_react3 = require("react");
|
|
967
1028
|
|
|
968
1029
|
// src/generated/css-content.ts
|
|
969
1030
|
var FRANKIE_CSS_CONTENT = `/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
970
|
-
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{.frankie-root{--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--leading-tight:1.25;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-background:var(--frankie-background);--color-foreground:var(--frankie-foreground);--color-primary:var(--frankie-primary)}}@layer components{.frankie-page-shell{background:var(--frankie-background);height:100%;min-height:0;color:var(--frankie-foreground);flex:1;display:flex}.frankie-page-main{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.frankie-history-desktop{border-right:1px solid var(--frankie-border);background:var(--frankie-secondary);flex-direction:column;flex-shrink:0;gap:16px;width:260px;padding:20px 14px;transition:width .16s,padding .16s;display:flex;overflow:auto}.frankie-history-toggle{border:1px solid var(--frankie-border);background:var(--frankie-background);width:32px;height:32px;color:var(--frankie-muted-foreground);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-self:flex-end;align-items:center;display:flex}.frankie-history-toggle:hover{color:var(--frankie-foreground)}.frankie-history-collapsed{width:56px;padding:20px 8px;overflow:visible}.frankie-history-collapsed .frankie-history-toggle{align-self:center}.frankie-history-heading{justify-content:space-between;align-items:center;font-size:14px;display:flex}.frankie-new-chat{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;gap:8px;min-height:44px;font-weight:600;display:flex}.frankie-history-search>span{color:var(--frankie-muted-foreground);margin-bottom:6px;font-size:12px;display:block}.frankie-history-search input{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:8px;width:100%;min-height:40px;padding:8px 10px}.frankie-history-list{flex-direction:column;flex:1;gap:4px;display:flex}.frankie-history-list button{text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;flex-direction:column;align-items:flex-start;gap:5px;min-height:54px;padding:10px;display:flex}.frankie-history-list button span{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:13px;overflow:hidden}.frankie-history-list small{color:var(--frankie-muted-foreground);font-size:11px}.frankie-history-list button:hover,.frankie-history-list button[aria-current]{background:var(--frankie-background);border-color:var(--frankie-border)}.frankie-history-desktop>p,.frankie-history-dialog p{color:var(--frankie-muted-foreground);font-size:13px}.frankie-header-caption{color:var(--frankie-muted-foreground);font-size:12px}.frankie-page-header button{cursor:pointer;justify-content:center;align-items:center;gap:6px;min-width:40px;min-height:40px;display:inline-flex}.frankie-chat-settings{position:relative}.frankie-chat-settings summary{cursor:pointer;border-radius:8px;place-items:center;width:40px;height:40px;list-style:none;display:grid}.frankie-chat-settings summary::-webkit-details-marker{display:none}.frankie-chat-settings>div{z-index:5;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:12px;gap:12px;width:min(280px,80vw);padding:16px;font-size:13px;display:grid;position:absolute;top:46px;right:0;box-shadow:0 10px 30px #0002}.frankie-chat-settings p{color:var(--frankie-muted-foreground);margin:0}.frankie-loaded-skills{width:100%;max-width:760px;color:var(--frankie-muted-foreground);font-size:12px}.frankie-loaded-skills summary{cursor:pointer;padding:6px 0}.frankie-loaded-skills ul{margin:0;padding:4px 0;list-style:none}.frankie-loaded-skills li{gap:8px;padding:4px 0;display:flex}.frankie-loaded-skills small{opacity:.7}.frankie-history-dialog{background:var(--frankie-secondary);width:min(320px,86vw);max-width:none;height:100dvh;max-height:none;color:var(--frankie-foreground);border:0;margin:0;padding:0}.frankie-history-dialog::backdrop{background:#0008}.frankie-history-dialog>aside{height:100%;padding:calc(20px + env(safe-area-inset-top)) 16px calc(20px + env(safe-area-inset-bottom));flex-direction:column;gap:16px;display:flex;overflow:auto}.frankie-mobile-only{display:none!important}.frankie-widget-panel{max-height:calc(100dvh - 100px)}.frankie-root button:focus-visible,.frankie-root summary:focus-visible,.frankie-root input:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:3px}.frankie-root button:disabled{opacity:.5;cursor:not-allowed}@media (max-width:760px){.frankie-history-desktop{display:none}.frankie-mobile-only{display:inline-flex!important}.frankie-header-caption{display:none}.frankie-page-header{padding:6px 10px}.frankie-history-heading button{background:0 0;border:0;justify-content:center;align-items:center;min-width:44px;min-height:44px}.frankie-page-prompts{gap:8px}.frankie-widget-panel{width:calc(100vw - 32px)}}.frankie-root{--frankie-styles-ready:1;--frankie-background:#fff;--frankie-foreground:#0a0a0a;--frankie-primary:#171717;--frankie-primary-foreground:#fafafa;--frankie-secondary:#f5f5f5;--frankie-secondary-foreground:#171717;--frankie-muted:#f5f5f5;--frankie-muted-foreground:#737373;--frankie-border:#e5e5e5;--frankie-radius:.625rem;--frankie-font:ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;font-family:var(--frankie-font);color:var(--frankie-foreground);-webkit-font-smoothing:antialiased;line-height:1.5}.frankie-root.dark{--frankie-background:#0a0a0a;--frankie-foreground:#fafafa;--frankie-primary:#fafafa;--frankie-primary-foreground:#171717;--frankie-secondary:#262626;--frankie-secondary-foreground:#fafafa;--frankie-muted:#262626;--frankie-muted-foreground:#a3a3a3;--frankie-border:#262626}.frankie-root,.frankie-root *,.frankie-root :before,.frankie-root :after{box-sizing:border-box}.frankie-root button,.frankie-root textarea,.frankie-root input{font:inherit;color:inherit;margin:0}.frankie-root button{background-color:#0000;border:0 solid}.frankie-root textarea{background-color:var(--frankie-background)}.frankie-focus:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:2px}.frankie-scroll{scrollbar-width:thin;scrollbar-color:var(--frankie-border) transparent}.frankie-prose>*+*{margin-top:.6rem}.frankie-prose p{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;line-height:1.6}.frankie-prose>.frankie-h{letter-spacing:-.01em;margin-top:1.15rem;line-height:1.3}.frankie-prose>.frankie-h:first-child{margin-top:0}.frankie-prose h3.frankie-h{font-size:1.06em;font-weight:700}.frankie-prose h4.frankie-h{font-size:1em;font-weight:600}.frankie-prose h5.frankie-h{color:var(--frankie-muted-foreground);font-size:.92em;font-weight:600}.frankie-prose strong{font-weight:600}.frankie-prose ul,.frankie-prose ol{flex-direction:column;gap:.35rem;margin:0;padding-left:1.4rem;display:flex}.frankie-prose li{padding-left:.15rem;line-height:1.55}.frankie-prose li::marker{color:var(--frankie-muted-foreground)}.frankie-prose ul{list-style:outside}.frankie-prose ol{list-style:decimal}.frankie-prose a{color:var(--frankie-primary);text-underline-offset:2px;text-decoration:underline}.frankie-prose code{background-color:var(--frankie-muted);border-radius:.25rem;padding:.1em .3em;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:.85em}.frankie-prose pre{border-radius:var(--frankie-radius);background-color:var(--frankie-muted);margin:0;padding:.625rem .75rem;overflow-x:auto}.frankie-prose pre code{background:0 0;padding:0}.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, var(--frankie-primary), transparent 65%), var(--frankie-background);flex-direction:column;min-width:0;display:flex;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, color-mix(in srgb, var(--frankie-primary) 8%, transparent), transparent 65%), var(--frankie-background)}}.frankie-chat-page{color:var(--frankie-foreground)}.frankie-page-header{border-bottom:1px solid var(--frankie-border);background:var(--frankie-background);flex:0 0 56px;justify-content:space-between;align-items:center;gap:16px;min-height:56px;padding:0 24px;display:flex}@supports (color:color-mix(in lab, red, red)){.frankie-page-header{background:color-mix(in srgb, var(--frankie-background) 92%, transparent)}}.frankie-page-header{-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.frankie-page-header>div{align-items:center;gap:10px;display:flex}.frankie-page-header strong{font-size:.925rem;font-weight:650}.frankie-page-status{background:var(--frankie-primary);width:10px;height:10px;box-shadow:0 0 0 4px var(--frankie-primary);border-radius:999px}@supports (color:color-mix(in lab, red, red)){.frankie-page-status{box-shadow:0 0 0 4px color-mix(in srgb, var(--frankie-primary) 16%, transparent)}}.frankie-page-header button{border:1px solid var(--frankie-border);border-radius:calc(var(--frankie-radius) * .9);background:var(--frankie-secondary);cursor:pointer;min-height:36px;padding:0 13px}.frankie-page-header button:hover{border-color:var(--frankie-primary)}.frankie-page-canvas{flex:1;min-width:0;min-height:0;display:flex}.frankie-page-canvas>.frankie-inline{width:100%;max-width:56rem;margin:0 auto}.frankie-page-canvas .frankie-scroll{padding:2rem clamp(1rem,4vw,4rem) 1.25rem}.frankie-page-canvas .frankie-root>.border-t{background:linear-gradient(180deg, transparent, var(--frankie-background) 30%);border-top:0;padding:.75rem clamp(1rem,4vw,4rem) 1.5rem}.frankie-page-empty{text-align:center;flex-direction:column;flex-shrink:0;justify-content:safe center;align-items:center;gap:2rem;width:min(100%,44rem);min-height:100%;margin:auto;padding:clamp(2rem,7vh,4.5rem) 1rem 2rem;display:flex;container-type:inline-size}.frankie-page-welcome h1{letter-spacing:-.02em;margin:0;font-size:clamp(1.55rem,3.4vw,2rem);font-weight:700;line-height:1.15}.frankie-page-welcome p{max-width:34rem;color:var(--frankie-muted-foreground);margin:.6rem auto 0;font-size:1rem}.frankie-page-suggest{flex-direction:column;align-items:center;gap:1rem;width:100%;display:flex}.frankie-page-suggest-label{color:var(--frankie-muted-foreground);letter-spacing:.12em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:600}.frankie-page-prompts{grid-template-columns:1fr;gap:.75rem;width:100%;display:grid}@container (min-width:30rem){.frankie-page-prompts{grid-template-columns:repeat(2,minmax(0,1fr))}}.frankie-page-prompts button{border:1px solid var(--frankie-border);background:var(--frankie-background);text-align:left;cursor:pointer;border-radius:14px;align-items:flex-start;gap:.85rem;min-height:64px;padding:.9rem 1rem;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-page-prompts button:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-page-prompts button:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 4%, var(--frankie-background))}}.frankie-page-prompts button:hover:not(:disabled){transform:translateY(-1px)}.frankie-prompt-icon{background:var(--frankie-muted);width:34px;height:34px;color:var(--frankie-foreground);border-radius:9px;flex:none;place-items:center;display:grid}.frankie-prompt-text{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-prompt-text strong{font-size:.9rem;font-weight:600;line-height:1.3}.frankie-prompt-text small{color:var(--frankie-muted-foreground);font-size:.8rem;line-height:1.35;overflow:hidden}.frankie-page-suggest-hint{color:var(--frankie-muted-foreground);margin:0;font-size:.85rem}@media (max-width:640px){.frankie-page-header{flex-basis:52px;min-height:52px;padding:0 12px}.frankie-page-canvas .frankie-scroll{padding:1.25rem .75rem .5rem}.frankie-page-canvas .frankie-root>.border-t{padding:.65rem .75rem calc(.65rem + env(safe-area-inset-bottom))}}}@layer utilities;.frankie-root .\\@container{container-type:inline-size}.frankie-root .invisible{visibility:hidden}.frankie-root .visible{visibility:visible}.frankie-root .absolute{position:absolute}.frankie-root .fixed{position:fixed}.frankie-root .relative{position:relative}.frankie-root .static{position:static}.frankie-root .isolate{isolation:isolate}.frankie-root .container{width:100%}@media (min-width:40rem){.frankie-root .container{max-width:40rem}}@media (min-width:48rem){.frankie-root .container{max-width:48rem}}@media (min-width:64rem){.frankie-root .container{max-width:64rem}}@media (min-width:80rem){.frankie-root .container{max-width:80rem}}@media (min-width:96rem){.frankie-root .container{max-width:96rem}}.frankie-root .mt-0{margin-top:0}.frankie-root .mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.frankie-root .mt-1{margin-top:var(--spacing)}.frankie-root .mt-2{margin-top:calc(var(--spacing) * 2)}.frankie-root .block{display:block}.frankie-root .contents{display:contents}.frankie-root .flex{display:flex}.frankie-root .grid{display:grid}.frankie-root .hidden{display:none}.frankie-root .inline{display:inline}.frankie-root .inline-flex{display:inline-flex}.frankie-root .table{display:table}.frankie-root .size-1{width:var(--spacing);height:var(--spacing)}.frankie-root .size-1\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.frankie-root .size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.frankie-root .size-3\\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.frankie-root .size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.frankie-root .size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.frankie-root .size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.frankie-root .size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.frankie-root .size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.frankie-root .size-full{width:100%;height:100%}.frankie-root .h-8{height:calc(var(--spacing) * 8)}.frankie-root .h-9{height:calc(var(--spacing) * 9)}.frankie-root .h-10{height:calc(var(--spacing) * 10)}.frankie-root .h-\\[32rem\\]{height:32rem}.frankie-root .h-full{height:100%}.frankie-root .h-px{height:1px}.frankie-root .min-h-7{min-height:calc(var(--spacing) * 7)}.frankie-root .min-h-\\[2\\.25rem\\]{min-height:2.25rem}.frankie-root .min-h-screen{min-height:100vh}.frankie-root .w-\\[22rem\\]{width:22rem}.frankie-root .w-full{width:100%}.frankie-root .max-w-\\[12rem\\]{max-width:12rem}.frankie-root .max-w-\\[14rem\\]{max-width:14rem}.frankie-root .max-w-\\[85\\%\\]{max-width:85%}.frankie-root .max-w-\\[calc\\(100vw-2rem\\)\\]{max-width:calc(100vw - 2rem)}.frankie-root .min-w-0{min-width:0}.frankie-root .flex-1{flex:1}.frankie-root .shrink-0{flex-shrink:0}.frankie-root .grow{flex-grow:1}.frankie-root .animate-pulse{animation:var(--animate-pulse)}.frankie-root .animate-spin{animation:var(--animate-spin)}.frankie-root .cursor-pointer{cursor:pointer}.frankie-root .resize-none{resize:none}.frankie-root .list-none{list-style-type:none}.frankie-root .flex-col{flex-direction:column}.frankie-root .flex-wrap{flex-wrap:wrap}.frankie-root .items-center{align-items:center}.frankie-root .items-end{align-items:flex-end}.frankie-root .items-start{align-items:flex-start}.frankie-root .justify-center{justify-content:center}.frankie-root .justify-end{justify-content:flex-end}.frankie-root .gap-1{gap:var(--spacing)}.frankie-root .gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.frankie-root .gap-2{gap:calc(var(--spacing) * 2)}.frankie-root .gap-3{gap:calc(var(--spacing) * 3)}.frankie-root .gap-4{gap:calc(var(--spacing) * 4)}.frankie-root .truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.frankie-root .overflow-hidden{overflow:hidden}.frankie-root .overflow-y-auto{overflow-y:auto}.frankie-root .overscroll-contain{overscroll-behavior:contain}.frankie-root .rounded{border-radius:.25rem}.frankie-root .rounded-\\[calc\\(var\\(--frankie-radius\\)\\+4px\\)\\]{border-radius:calc(var(--frankie-radius) + 4px)}.frankie-root .rounded-\\[calc\\(var\\(--frankie-radius\\)-2px\\)\\]{border-radius:calc(var(--frankie-radius) - 2px)}.frankie-root .rounded-\\[var\\(--frankie-radius\\)\\]{border-radius:var(--frankie-radius)}.frankie-root .rounded-full{border-radius:3.40282e38px}.frankie-root .rounded-md{border-radius:var(--radius-md)}.frankie-root .rounded-none{border-radius:0}.frankie-root .border{border-style:var(--tw-border-style);border-width:1px}.frankie-root .border-0{border-style:var(--tw-border-style);border-width:0}.frankie-root .border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.frankie-root .border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.frankie-root .border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.frankie-root .border-border{border-color:var(--frankie-border)}.frankie-root .border-transparent{border-color:#0000}.frankie-root .bg-background{background-color:var(--frankie-background)}.frankie-root .bg-border{background-color:var(--frankie-border)}.frankie-root .bg-emerald-500{background-color:var(--color-emerald-500)}.frankie-root .bg-emerald-500\\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-emerald-500\\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.frankie-root .bg-muted{background-color:var(--frankie-muted)}.frankie-root .bg-muted-foreground{background-color:var(--frankie-muted-foreground)}.frankie-root .bg-primary{background-color:var(--frankie-primary)}.frankie-root .bg-red-500{background-color:var(--color-red-500)}.frankie-root .bg-red-500\\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-red-500\\/10{background-color:color-mix(in oklab, var(--color-red-500) 10%, transparent)}}.frankie-root .bg-secondary{background-color:var(--frankie-secondary)}.frankie-root .object-cover{object-fit:cover}.frankie-root .p-3{padding:calc(var(--spacing) * 3)}.frankie-root .p-4{padding:calc(var(--spacing) * 4)}.frankie-root .px-1{padding-inline:var(--spacing)}.frankie-root .px-2{padding-inline:calc(var(--spacing) * 2)}.frankie-root .px-3{padding-inline:calc(var(--spacing) * 3)}.frankie-root .px-4{padding-inline:calc(var(--spacing) * 4)}.frankie-root .px-6{padding-inline:calc(var(--spacing) * 6)}.frankie-root .py-0{padding-block:0}.frankie-root .py-0\\.5{padding-block:calc(var(--spacing) * .5)}.frankie-root .py-1{padding-block:var(--spacing)}.frankie-root .py-2{padding-block:calc(var(--spacing) * 2)}.frankie-root .py-3{padding-block:calc(var(--spacing) * 3)}.frankie-root .pl-2{padding-left:calc(var(--spacing) * 2)}.frankie-root .text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.frankie-root .text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.frankie-root .text-\\[0\\.625rem\\]{font-size:.625rem}.frankie-root .text-\\[0\\.6875rem\\]{font-size:.6875rem}.frankie-root .text-\\[11px\\]{font-size:11px}.frankie-root .leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.frankie-root .font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.frankie-root .font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.frankie-root .whitespace-nowrap{white-space:nowrap}.frankie-root .text-emerald-600{color:var(--color-emerald-600)}.frankie-root .text-foreground{color:var(--frankie-foreground)}.frankie-root .text-muted-foreground{color:var(--frankie-muted-foreground)}.frankie-root .text-primary{color:var(--frankie-primary)}.frankie-root .text-primary-foreground{color:var(--frankie-primary-foreground)}.frankie-root .text-red-600{color:var(--color-red-600)}.frankie-root .text-secondary-foreground{color:var(--frankie-secondary-foreground)}.frankie-root .italic{font-style:italic}.frankie-root .underline-offset-4{text-underline-offset:4px}.frankie-root .opacity-60{opacity:.6}.frankie-root .opacity-70{opacity:.7}.frankie-root .shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .outline{outline-style:var(--tw-outline-style);outline-width:1px}.frankie-root .transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.frankie-root .placeholder\\:text-muted-foreground::placeholder{color:var(--frankie-muted-foreground)}@media (hover:hover){.frankie-root .hover\\:border-border:hover{border-color:var(--frankie-border)}.frankie-root .hover\\:bg-muted:hover{background-color:var(--frankie-muted)}.frankie-root .hover\\:text-foreground:hover{color:var(--frankie-foreground)}.frankie-root .hover\\:underline:hover{text-decoration-line:underline}.frankie-root .hover\\:opacity-90:hover{opacity:.9}.frankie-root .hover\\:opacity-100:hover{opacity:1}}.frankie-root .focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .focus-visible\\:ring-primary:focus-visible{--tw-ring-color:var(--frankie-primary)}.frankie-root .focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.frankie-root .disabled\\:pointer-events-none:disabled{pointer-events:none}.frankie-root .disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.frankie-root .disabled\\:opacity-50:disabled{opacity:.5}.frankie-root .dark\\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.frankie-root .dark\\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.frankie-root .\\[\\&_svg\\]\\:pointer-events-none svg{pointer-events:none}.frankie-root .\\[\\&_svg\\]\\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .\\[\\&_svg\\]\\:shrink-0 svg{flex-shrink:0}.frankie-root .\\[\\&\\:\\:-webkit-details-marker\\]\\:hidden::-webkit-details-marker{display:none}.frankie-root.frankie-chat-panel{min-height:0;position:relative}.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px var(--frankie-primary), 0 12px 28px -12px #0f172a47, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px color-mix(in srgb, var(--frankie-primary) 22%, #0f172a6b), 0 12px 28px -12px #0f172a47, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 8%, transparent)}}.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 12%, transparent)}}.frankie-root .frankie-agent-context-wrap{flex:none;justify-content:center;padding:12px 10px;display:flex}.frankie-root .frankie-agent-context{max-width:100%;color:var(--frankie-foreground);font-size:13px;position:relative}.frankie-root .frankie-agent-context>summary{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;white-space:nowrap;border-radius:999px;justify-content:center;align-items:center;gap:10px;min-height:44px;padding:7px 13px;list-style:none;display:flex;box-shadow:0 1px 3px #0000000d}.frankie-root .frankie-agent-context>summary::-webkit-details-marker{display:none}.frankie-root .frankie-agent-context>summary>svg{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-context-count{background:var(--frankie-muted);color:var(--frankie-muted-foreground);border-radius:999px;padding:2px 10px;font-size:12px}.frankie-root .frankie-context-status{color:var(--frankie-muted-foreground);align-items:center;gap:6px;font-size:12px;display:flex}.frankie-root .frankie-context-status i{background:currentColor;border-radius:50%;width:7px;height:7px}.frankie-root .frankie-context-status[data-active=true]{color:#00976b}.frankie-root .frankie-context-detail{z-index:10;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:16px;width:340px;max-width:calc(100vw - 32px);padding:18px;position:absolute;top:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 12px 32px #00000016}.frankie-root .frankie-context-detail p{color:var(--frankie-muted-foreground);margin:10px 0 0;font-size:12px;line-height:1.6}.frankie-root .frankie-context-detail ul{max-height:220px;margin:12px 0;padding:0;list-style:none;overflow:auto}.frankie-root .frankie-context-detail li{border-bottom:1px solid var(--frankie-border);overflow-wrap:anywhere;justify-content:space-between;gap:12px;padding:8px 0;display:flex}.frankie-root .frankie-context-detail small{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.frankie-root.frankie-chat-panel-fill{--frankie-canvas:var(--frankie-background)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill{--frankie-canvas:color-mix(in srgb,var(--frankie-background) 60%,var(--frankie-muted))}}.frankie-root.frankie-chat-panel-fill{background:var(--frankie-canvas)}.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:44px 16px 26px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:14px max(24px,50% - 480px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-message-list{gap:38px}.frankie-root.frankie-chat-panel-fill .frankie-user-bubble{background:var(--frankie-muted);color:var(--frankie-foreground);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:24px;padding:12px 20px;font-size:15px}.frankie-root.frankie-chat-panel-fill .frankie-assistant-content{flex:1;max-width:100%;font-size:15px;line-height:1.7}.frankie-root.frankie-chat-panel-fill .frankie-assistant-message{gap:14px}.frankie-root .frankie-thinking{color:var(--frankie-muted-foreground);gap:5px}.frankie-root .frankie-thinking-label{margin-right:6px;font-size:14px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{background:0 0;border:0;flex:none;padding:16px max(24px,50% - 500px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-composer{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:28px;padding:14px;box-shadow:0 2px 4px #0000000a}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:color-mix(in srgb,var(--frankie-foreground) 32%,var(--frankie-border))}}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px color-mix(in srgb,var(--frankie-primary) 8%,transparent)}}.frankie-root.frankie-chat-panel-fill .frankie-composer-controls{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.frankie-root.frankie-chat-panel-fill .frankie-composer textarea{resize:none;width:100%;min-height:32px;max-height:140px;color:var(--frankie-foreground);box-shadow:none;background:0 0;border:0;border-radius:0;outline:none;flex:0 0 100%;order:-1;margin-bottom:4px;padding:2px 6px;font-size:16px}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Send],.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Stop]{background:var(--frankie-primary);width:42px;height:42px;color:var(--frankie-primary-foreground);border:0;border-radius:50%;margin-left:auto}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Attach\\ files]{border-radius:50%;width:42px;height:42px}.frankie-root .frankie-composer-actions{align-items:center;gap:6px;min-width:0;display:flex}.frankie-root .frankie-composer-meta{align-items:center;gap:8px;margin-top:6px;padding:0 2px;display:flex}.frankie-root .frankie-model-picker{align-items:center;min-width:0;display:inline-flex}.frankie-root .frankie-model-select{appearance:none;max-width:12rem;color:var(--frankie-muted-foreground);font:inherit;cursor:pointer;text-overflow:ellipsis;background-color:#0000;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b8b8b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");background-position:right 6px center;background-repeat:no-repeat;background-size:11px;border:0;border-radius:8px;padding:5px 22px 5px 8px;font-size:12px;font-weight:500;line-height:1;transition:background-color .12s,color .12s}.frankie-root .frankie-model-select:hover{background-color:var(--frankie-muted);color:var(--frankie-foreground)}.frankie-root .frankie-model-select:disabled{opacity:.5;cursor:not-allowed}.frankie-root .frankie-model-select option{color:var(--frankie-foreground);background:var(--frankie-background);font-weight:400}.frankie-root .frankie-history-desktop{background:var(--frankie-background);width:290px;padding:20px 12px}.frankie-root .frankie-history-collapsed{width:56px;padding:20px 8px}.frankie-root .frankie-new-chat{background:0 0;border:0;justify-content:flex-start;padding:8px 12px;font-size:15px}.frankie-root .frankie-history-heading{color:var(--frankie-muted-foreground);padding:0 12px;font-size:12px;font-weight:400}.frankie-root .frankie-history-list button{min-height:44px;padding:10px 12px}.frankie-root .frankie-history-list button span{font-size:14px}.frankie-root .frankie-history-list button[aria-current],.frankie-root .frankie-history-list button:hover{background:var(--frankie-muted);border-color:#0000}.frankie-root .frankie-history-list small{display:none}.frankie-root .frankie-history-group{flex-direction:column;gap:2px;display:flex}.frankie-root .frankie-history-group+.frankie-history-group{margin-top:12px}.frankie-root .frankie-history-group-label{letter-spacing:.04em;color:var(--frankie-muted-foreground);margin:0;padding:4px 12px;font-size:11px;font-weight:600}.frankie-root .frankie-page-header{background:var(--frankie-background);height:56px;min-height:56px}.frankie-root .frankie-page-header>div{min-width:0}.frankie-root .frankie-thread-title{text-overflow:ellipsis;white-space:nowrap;max-width:55vw;font-weight:500;overflow:hidden}.frankie-root .frankie-page-header button{background:0 0;border-color:#0000}.frankie-root .frankie-page-canvas>.frankie-inline{max-width:none}@media (max-width:760px){.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:20px 8px 14px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:12px 16px 16px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{padding:10px 12px calc(12px + env(safe-area-inset-bottom))}.frankie-root .frankie-agent-context>summary{gap:6px;padding:7px 9px;font-size:12px}.frankie-root .frankie-context-count{padding:2px 7px;font-size:11px}.frankie-root .frankie-context-status{gap:4px;font-size:11px}.frankie-root .frankie-agent-context>summary>svg{width:14px}.frankie-root .frankie-thread-title{max-width:42vw}}.frankie-root .frankie-agent-context>summary>svg:first-child{box-sizing:content-box;background:var(--frankie-muted);border-radius:50%;padding:4px}.frankie-root.frankie-chat-panel-fill .frankie-agent-context>summary{font-size:14px}.frankie-root .frankie-agent-context>summary>span:first-of-type{border-right:1px solid var(--frankie-border);padding-right:10px}@media (max-width:760px){.frankie-root .frankie-page-header button>span{display:none}}.frankie-root .frankie-choices{flex-direction:column;gap:8px;margin-top:12px;display:flex}.frankie-root .frankie-choice{text-align:left;cursor:pointer;border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);width:100%;color:var(--frankie-foreground);font:inherit;align-items:center;gap:12px;padding:11px 14px;font-size:14px;line-height:1.4;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-root .frankie-choice:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 55%, var(--frankie-border))}}.frankie-root .frankie-choice:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 6%, var(--frankie-background))}}.frankie-root .frankie-choice:active:not(:disabled){transform:scale(.99)}.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}.frankie-root .frankie-choice:disabled{opacity:.6;cursor:default}.frankie-root .frankie-choice-index{border:1px solid var(--frankie-border);background:var(--frankie-muted);width:24px;height:24px;color:var(--frankie-muted-foreground);border-radius:50%;flex:none;place-items:center;font-size:12px;font-weight:600;transition:background-color .15s,color .15s,border-color .15s;display:grid}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-primary);border-color:var(--frankie-primary);color:var(--frankie-primary-foreground)}.frankie-root .frankie-choice-label{flex:1;min-width:0}.frankie-root .frankie-choice-go{color:var(--frankie-muted-foreground);opacity:0;flex:none;transition:opacity .15s,transform .15s;transform:translate(-4px)}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-go{opacity:1;color:var(--frankie-primary);transform:none}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-choice,.frankie-root .frankie-choice-go,.frankie-root .frankie-choice-index{transition:none}}.frankie-root .frankie-choice-other{color:var(--frankie-muted-foreground);border-style:dashed}.frankie-root .frankie-choice-other .frankie-choice-index{background:0 0}.frankie-root .frankie-choice-other:hover:not(:disabled){color:var(--frankie-foreground)}.frankie-root .frankie-choice-other:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-muted);border-color:var(--frankie-border);color:var(--frankie-foreground)}.frankie-root .frankie-choice-hint{color:var(--frankie-muted-foreground);flex:none;font-size:12px}.frankie-root .frankie-skeleton{flex-direction:column;gap:8px;max-width:min(420px,100%);padding:4px 0 6px;display:flex}.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, var(--frankie-muted-foreground) 25%, var(--frankie-muted-foreground) 37%, var(--frankie-muted-foreground) 63%);border-radius:6px;height:12px}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 25%, color-mix(in srgb, var(--frankie-muted-foreground) 26%, transparent) 37%, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 63%)}}.frankie-root .frankie-skeleton-line{background-size:400% 100%;animation:1.4s infinite frankie-shimmer}@keyframes frankie-shimmer{0%{background-position:100%}to{background-position:0}}@keyframes frankie-skeleton-pulse{0%,to{opacity:.45}50%{opacity:.85}}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-skeleton-line{background:var(--frankie-muted-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:color-mix(in srgb, var(--frankie-muted-foreground) 16%, transparent)}}.frankie-root .frankie-skeleton-line{animation:1.6s ease-in-out infinite frankie-skeleton-pulse}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}`;
|
|
1031
|
+
@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-ease:initial}}}@layer theme{.frankie-root{--color-red-400:oklch(70.4% .191 22.216);--color-red-500:oklch(63.7% .237 25.331);--color-red-600:oklch(57.7% .245 27.325);--color-emerald-400:oklch(76.5% .177 163.223);--color-emerald-500:oklch(69.6% .17 162.48);--color-emerald-600:oklch(59.6% .145 163.225);--spacing:.25rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--font-weight-medium:500;--font-weight-semibold:600;--leading-tight:1.25;--radius-md:.375rem;--ease-in-out:cubic-bezier(.4, 0, .2, 1);--animate-spin:spin 1s linear infinite;--animate-pulse:pulse 2s cubic-bezier(.4, 0, .6, 1) infinite;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--color-background:var(--frankie-background);--color-foreground:var(--frankie-foreground);--color-primary:var(--frankie-primary)}}@layer components{.frankie-page-shell{background:var(--frankie-background);height:100%;min-height:0;color:var(--frankie-foreground);flex:1;display:flex}.frankie-page-main{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.frankie-history-desktop{border-right:1px solid var(--frankie-border);background:var(--frankie-secondary);flex-direction:column;flex-shrink:0;gap:16px;width:260px;padding:20px 14px;transition:width .16s,padding .16s;display:flex;overflow:auto}.frankie-history-toggle{border:1px solid var(--frankie-border);background:var(--frankie-background);width:32px;height:32px;color:var(--frankie-muted-foreground);cursor:pointer;border-radius:8px;flex-shrink:0;justify-content:center;align-self:flex-end;align-items:center;display:flex}.frankie-history-toggle:hover{color:var(--frankie-foreground)}.frankie-history-collapsed{width:56px;padding:20px 8px;overflow:visible}.frankie-history-collapsed .frankie-history-toggle{align-self:center}.frankie-history-heading{justify-content:space-between;align-items:center;font-size:14px;display:flex}.frankie-new-chat{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;border-radius:10px;justify-content:center;align-items:center;gap:8px;min-height:44px;font-weight:600;display:flex}.frankie-history-search>span{color:var(--frankie-muted-foreground);margin-bottom:6px;font-size:12px;display:block}.frankie-history-search input{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:8px;width:100%;min-height:40px;padding:8px 10px}.frankie-history-list{flex-direction:column;flex:1;gap:4px;display:flex}.frankie-history-list button{text-align:left;cursor:pointer;background:0 0;border:1px solid #0000;border-radius:8px;flex-direction:column;align-items:flex-start;gap:5px;min-height:54px;padding:10px;display:flex}.frankie-history-list button span{text-overflow:ellipsis;white-space:nowrap;width:100%;font-size:13px;overflow:hidden}.frankie-history-list small{color:var(--frankie-muted-foreground);font-size:11px}.frankie-history-list button:hover,.frankie-history-list button[aria-current]{background:var(--frankie-background);border-color:var(--frankie-border)}.frankie-history-desktop>p,.frankie-history-dialog p{color:var(--frankie-muted-foreground);font-size:13px}.frankie-header-caption{color:var(--frankie-muted-foreground);font-size:12px}.frankie-page-header button{cursor:pointer;justify-content:center;align-items:center;gap:6px;min-width:40px;min-height:40px;display:inline-flex}.frankie-chat-settings{position:relative}.frankie-chat-settings summary{cursor:pointer;border-radius:8px;place-items:center;width:40px;height:40px;list-style:none;display:grid}.frankie-chat-settings summary::-webkit-details-marker{display:none}.frankie-chat-settings>div{z-index:5;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:12px;gap:12px;width:min(280px,80vw);padding:16px;font-size:13px;display:grid;position:absolute;top:46px;right:0;box-shadow:0 10px 30px #0002}.frankie-chat-settings p{color:var(--frankie-muted-foreground);margin:0}.frankie-loaded-skills{width:100%;max-width:760px;color:var(--frankie-muted-foreground);font-size:12px}.frankie-loaded-skills summary{cursor:pointer;padding:6px 0}.frankie-loaded-skills ul{margin:0;padding:4px 0;list-style:none}.frankie-loaded-skills li{gap:8px;padding:4px 0;display:flex}.frankie-loaded-skills small{opacity:.7}.frankie-history-dialog{background:var(--frankie-secondary);width:min(320px,86vw);max-width:none;height:100dvh;max-height:none;color:var(--frankie-foreground);border:0;margin:0;padding:0}.frankie-history-dialog::backdrop{background:#0008}.frankie-history-dialog>aside{height:100%;padding:calc(20px + env(safe-area-inset-top)) 16px calc(20px + env(safe-area-inset-bottom));flex-direction:column;gap:16px;display:flex;overflow:auto}.frankie-mobile-only{display:none!important}.frankie-widget-panel{max-height:calc(100dvh - 100px - var(--frankie-lift,0px))}.frankie-root button:focus-visible,.frankie-root summary:focus-visible,.frankie-root input:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:3px}.frankie-root button:disabled{opacity:.5;cursor:not-allowed}@media (max-width:760px){.frankie-history-desktop{display:none}.frankie-mobile-only{display:inline-flex!important}.frankie-header-caption{display:none}.frankie-page-header{padding:6px 10px}.frankie-history-heading button{background:0 0;border:0;justify-content:center;align-items:center;min-width:44px;min-height:44px}.frankie-page-prompts{gap:8px}.frankie-widget-panel{width:calc(100vw - 32px)}}.frankie-root{--frankie-styles-ready:1;--frankie-background:#fff;--frankie-foreground:#0a0a0a;--frankie-primary:#171717;--frankie-primary-foreground:#fafafa;--frankie-secondary:#f5f5f5;--frankie-secondary-foreground:#171717;--frankie-muted:#f5f5f5;--frankie-muted-foreground:#737373;--frankie-border:#e5e5e5;--frankie-radius:.625rem;--frankie-font:ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;font-family:var(--frankie-font);color:var(--frankie-foreground);-webkit-font-smoothing:antialiased;line-height:1.5}.frankie-root.dark{--frankie-background:#0a0a0a;--frankie-foreground:#fafafa;--frankie-primary:#fafafa;--frankie-primary-foreground:#171717;--frankie-secondary:#262626;--frankie-secondary-foreground:#fafafa;--frankie-muted:#262626;--frankie-muted-foreground:#a3a3a3;--frankie-border:#262626}.frankie-root,.frankie-root *,.frankie-root :before,.frankie-root :after{box-sizing:border-box}.frankie-root button,.frankie-root textarea,.frankie-root input{font:inherit;color:inherit;margin:0}.frankie-root button{background-color:#0000;border:0 solid}.frankie-root textarea{background-color:var(--frankie-background)}.frankie-focus:focus-visible{outline:2px solid var(--frankie-primary);outline-offset:2px}.frankie-scroll{scrollbar-width:thin;scrollbar-color:var(--frankie-border) transparent}.frankie-prose>*+*{margin-top:.6rem}.frankie-prose p{white-space:pre-wrap;overflow-wrap:anywhere;margin:0;line-height:1.6}.frankie-prose>.frankie-h{letter-spacing:-.01em;margin-top:1.15rem;line-height:1.3}.frankie-prose>.frankie-h:first-child{margin-top:0}.frankie-prose h3.frankie-h{font-size:1.06em;font-weight:700}.frankie-prose h4.frankie-h{font-size:1em;font-weight:600}.frankie-prose h5.frankie-h{color:var(--frankie-muted-foreground);font-size:.92em;font-weight:600}.frankie-prose strong{font-weight:600}.frankie-prose ul,.frankie-prose ol{flex-direction:column;gap:.35rem;margin:0;padding-left:1.4rem;display:flex}.frankie-prose li{padding-left:.15rem;line-height:1.55}.frankie-prose li::marker{color:var(--frankie-muted-foreground)}.frankie-prose ul{list-style:outside}.frankie-prose ol{list-style:decimal}.frankie-prose a{color:var(--frankie-primary);text-underline-offset:2px;text-decoration:underline}.frankie-prose code{background-color:var(--frankie-muted);border-radius:.25rem;padding:.1em .3em;font-family:ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,monospace;font-size:.85em}.frankie-prose pre{border-radius:var(--frankie-radius);background-color:var(--frankie-muted);margin:0;padding:.625rem .75rem;overflow-x:auto}.frankie-prose pre code{background:0 0;padding:0}.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, var(--frankie-primary), transparent 65%), var(--frankie-background);flex-direction:column;min-width:0;display:flex;overflow:hidden}@supports (color:color-mix(in lab, red, red)){.frankie-chat-page{background:radial-gradient(60rem 24rem at 50% -12rem, color-mix(in srgb, var(--frankie-primary) 8%, transparent), transparent 65%), var(--frankie-background)}}.frankie-chat-page{color:var(--frankie-foreground)}.frankie-page-header{border-bottom:1px solid var(--frankie-border);background:var(--frankie-background);flex:0 0 56px;justify-content:space-between;align-items:center;gap:16px;min-height:56px;padding:0 24px;display:flex}@supports (color:color-mix(in lab, red, red)){.frankie-page-header{background:color-mix(in srgb, var(--frankie-background) 92%, transparent)}}.frankie-page-header{-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.frankie-page-header>div{align-items:center;gap:10px;display:flex}.frankie-page-header strong{font-size:.925rem;font-weight:650}.frankie-page-status{background:var(--frankie-primary);width:10px;height:10px;box-shadow:0 0 0 4px var(--frankie-primary);border-radius:999px}@supports (color:color-mix(in lab, red, red)){.frankie-page-status{box-shadow:0 0 0 4px color-mix(in srgb, var(--frankie-primary) 16%, transparent)}}.frankie-page-header button{border:1px solid var(--frankie-border);border-radius:calc(var(--frankie-radius) * .9);background:var(--frankie-secondary);cursor:pointer;min-height:36px;padding:0 13px}.frankie-page-header button:hover{border-color:var(--frankie-primary)}.frankie-page-canvas{flex:1;min-width:0;min-height:0;display:flex}.frankie-page-canvas>.frankie-inline{width:100%;max-width:56rem;margin:0 auto}.frankie-page-canvas .frankie-scroll{padding:2rem clamp(1rem,4vw,4rem) 1.25rem}.frankie-page-canvas .frankie-root>.border-t{background:linear-gradient(180deg, transparent, var(--frankie-background) 30%);border-top:0;padding:.75rem clamp(1rem,4vw,4rem) 1.5rem}.frankie-page-empty{text-align:center;flex-direction:column;flex-shrink:0;justify-content:safe center;align-items:center;gap:2rem;width:min(100%,44rem);min-height:100%;margin:auto;padding:clamp(2rem,7vh,4.5rem) 1rem 2rem;display:flex;container-type:inline-size}.frankie-page-welcome h1{letter-spacing:-.02em;margin:0;font-size:clamp(1.55rem,3.4vw,2rem);font-weight:700;line-height:1.15}.frankie-page-welcome p{max-width:34rem;color:var(--frankie-muted-foreground);margin:.6rem auto 0;font-size:1rem}.frankie-page-suggest{flex-direction:column;align-items:center;gap:1rem;width:100%;display:flex}.frankie-page-suggest-label{color:var(--frankie-muted-foreground);letter-spacing:.12em;text-transform:uppercase;margin:0;font-size:.72rem;font-weight:600}.frankie-page-prompts{grid-template-columns:1fr;gap:.75rem;width:100%;display:grid}@container (min-width:30rem){.frankie-page-prompts{grid-template-columns:repeat(2,minmax(0,1fr))}}.frankie-page-prompts button{border:1px solid var(--frankie-border);background:var(--frankie-background);text-align:left;cursor:pointer;border-radius:14px;align-items:flex-start;gap:.85rem;min-height:64px;padding:.9rem 1rem;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-page-prompts button:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-page-prompts button:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-page-prompts button:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 4%, var(--frankie-background))}}.frankie-page-prompts button:hover:not(:disabled){transform:translateY(-1px)}.frankie-prompt-icon{background:var(--frankie-muted);width:34px;height:34px;color:var(--frankie-foreground);border-radius:9px;flex:none;place-items:center;display:grid}.frankie-prompt-text{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-prompt-text strong{font-size:.9rem;font-weight:600;line-height:1.3}.frankie-prompt-text small{color:var(--frankie-muted-foreground);font-size:.8rem;line-height:1.35;overflow:hidden}.frankie-page-suggest-hint{color:var(--frankie-muted-foreground);margin:0;font-size:.85rem}.frankie-widget-panel .frankie-page-empty{gap:1rem;padding:1rem .25rem}.frankie-widget-panel .frankie-page-welcome h1{font-size:1.25rem}.frankie-widget-panel .frankie-page-welcome p{margin-top:.35rem;font-size:.88rem;line-height:1.45}.frankie-widget-panel .frankie-page-suggest{gap:.55rem}.frankie-widget-panel .frankie-page-suggest-label{font-size:.66rem}.frankie-widget-panel .frankie-page-prompts{flex-wrap:wrap;justify-content:center;gap:.4rem;display:flex}.frankie-widget-panel .frankie-page-prompts button{border-radius:999px;align-items:center;gap:.4rem;max-width:100%;min-height:34px;padding:.3rem .8rem .3rem .45rem}.frankie-widget-panel .frankie-prompt-icon{border-radius:999px;width:22px;height:22px}.frankie-widget-panel .frankie-prompt-icon svg{width:13px;height:13px}.frankie-widget-panel .frankie-prompt-text strong{white-space:nowrap;text-overflow:ellipsis;font-size:.82rem;font-weight:550;overflow:hidden}.frankie-widget-panel .frankie-prompt-text small,.frankie-widget-panel .frankie-page-suggest-hint{display:none}@media (max-width:640px){.frankie-page-header{flex-basis:52px;min-height:52px;padding:0 12px}.frankie-page-canvas .frankie-scroll{padding:1.25rem .75rem .5rem}.frankie-page-canvas .frankie-root>.border-t{padding:.65rem .75rem calc(.65rem + env(safe-area-inset-bottom))}}}@layer utilities;.frankie-root .\\@container{container-type:inline-size}.frankie-root .invisible{visibility:hidden}.frankie-root .visible{visibility:visible}.frankie-root .absolute{position:absolute}.frankie-root .fixed{position:fixed}.frankie-root .relative{position:relative}.frankie-root .static{position:static}.frankie-root .sticky{position:sticky}.frankie-root .isolate{isolation:isolate}.frankie-root .container{width:100%}@media (min-width:40rem){.frankie-root .container{max-width:40rem}}@media (min-width:48rem){.frankie-root .container{max-width:48rem}}@media (min-width:64rem){.frankie-root .container{max-width:64rem}}@media (min-width:80rem){.frankie-root .container{max-width:80rem}}@media (min-width:96rem){.frankie-root .container{max-width:96rem}}.frankie-root .mt-0{margin-top:0}.frankie-root .mt-0\\.5{margin-top:calc(var(--spacing) * .5)}.frankie-root .mt-1{margin-top:var(--spacing)}.frankie-root .mt-2{margin-top:calc(var(--spacing) * 2)}.frankie-root .block{display:block}.frankie-root .contents{display:contents}.frankie-root .flex{display:flex}.frankie-root .grid{display:grid}.frankie-root .hidden{display:none}.frankie-root .inline{display:inline}.frankie-root .inline-flex{display:inline-flex}.frankie-root .table{display:table}.frankie-root .size-1{width:var(--spacing);height:var(--spacing)}.frankie-root .size-1\\.5{width:calc(var(--spacing) * 1.5);height:calc(var(--spacing) * 1.5)}.frankie-root .size-3{width:calc(var(--spacing) * 3);height:calc(var(--spacing) * 3)}.frankie-root .size-3\\.5{width:calc(var(--spacing) * 3.5);height:calc(var(--spacing) * 3.5)}.frankie-root .size-4{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .size-5{width:calc(var(--spacing) * 5);height:calc(var(--spacing) * 5)}.frankie-root .size-7{width:calc(var(--spacing) * 7);height:calc(var(--spacing) * 7)}.frankie-root .size-8{width:calc(var(--spacing) * 8);height:calc(var(--spacing) * 8)}.frankie-root .size-9{width:calc(var(--spacing) * 9);height:calc(var(--spacing) * 9)}.frankie-root .size-12{width:calc(var(--spacing) * 12);height:calc(var(--spacing) * 12)}.frankie-root .size-full{width:100%;height:100%}.frankie-root .h-8{height:calc(var(--spacing) * 8)}.frankie-root .h-9{height:calc(var(--spacing) * 9)}.frankie-root .h-10{height:calc(var(--spacing) * 10)}.frankie-root .h-\\[32rem\\]{height:32rem}.frankie-root .h-full{height:100%}.frankie-root .h-px{height:1px}.frankie-root .min-h-7{min-height:calc(var(--spacing) * 7)}.frankie-root .min-h-\\[2\\.25rem\\]{min-height:2.25rem}.frankie-root .min-h-screen{min-height:100vh}.frankie-root .w-\\[22rem\\]{width:22rem}.frankie-root .w-full{width:100%}.frankie-root .max-w-\\[12rem\\]{max-width:12rem}.frankie-root .max-w-\\[14rem\\]{max-width:14rem}.frankie-root .max-w-\\[85\\%\\]{max-width:85%}.frankie-root .max-w-\\[calc\\(100vw-2rem\\)\\]{max-width:calc(100vw - 2rem)}.frankie-root .min-w-0{min-width:0}.frankie-root .flex-1{flex:1}.frankie-root .shrink-0{flex-shrink:0}.frankie-root .grow{flex-grow:1}.frankie-root .animate-pulse{animation:var(--animate-pulse)}.frankie-root .animate-spin{animation:var(--animate-spin)}.frankie-root .cursor-pointer{cursor:pointer}.frankie-root .resize{resize:both}.frankie-root .resize-none{resize:none}.frankie-root .list-none{list-style-type:none}.frankie-root .flex-col{flex-direction:column}.frankie-root .flex-wrap{flex-wrap:wrap}.frankie-root .items-center{align-items:center}.frankie-root .items-end{align-items:flex-end}.frankie-root .items-start{align-items:flex-start}.frankie-root .justify-center{justify-content:center}.frankie-root .justify-end{justify-content:flex-end}.frankie-root .gap-1{gap:var(--spacing)}.frankie-root .gap-1\\.5{gap:calc(var(--spacing) * 1.5)}.frankie-root .gap-2{gap:calc(var(--spacing) * 2)}.frankie-root .gap-3{gap:calc(var(--spacing) * 3)}.frankie-root .gap-4{gap:calc(var(--spacing) * 4)}.frankie-root .truncate{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.frankie-root .overflow-hidden{overflow:hidden}.frankie-root .overflow-y-auto{overflow-y:auto}.frankie-root .overscroll-contain{overscroll-behavior:contain}.frankie-root .rounded{border-radius:.25rem}.frankie-root .rounded-\\[calc\\(var\\(--frankie-radius\\)\\+4px\\)\\]{border-radius:calc(var(--frankie-radius) + 4px)}.frankie-root .rounded-\\[calc\\(var\\(--frankie-radius\\)-2px\\)\\]{border-radius:calc(var(--frankie-radius) - 2px)}.frankie-root .rounded-\\[var\\(--frankie-radius\\)\\]{border-radius:var(--frankie-radius)}.frankie-root .rounded-full{border-radius:3.40282e38px}.frankie-root .rounded-md{border-radius:var(--radius-md)}.frankie-root .rounded-none{border-radius:0}.frankie-root .border{border-style:var(--tw-border-style);border-width:1px}.frankie-root .border-0{border-style:var(--tw-border-style);border-width:0}.frankie-root .border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.frankie-root .border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.frankie-root .border-l{border-left-style:var(--tw-border-style);border-left-width:1px}.frankie-root .border-border{border-color:var(--frankie-border)}.frankie-root .border-transparent{border-color:#0000}.frankie-root .bg-background{background-color:var(--frankie-background)}.frankie-root .bg-border{background-color:var(--frankie-border)}.frankie-root .bg-emerald-500{background-color:var(--color-emerald-500)}.frankie-root .bg-emerald-500\\/10{background-color:#00bb7f1a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-emerald-500\\/10{background-color:color-mix(in oklab, var(--color-emerald-500) 10%, transparent)}}.frankie-root .bg-muted{background-color:var(--frankie-muted)}.frankie-root .bg-muted-foreground{background-color:var(--frankie-muted-foreground)}.frankie-root .bg-primary{background-color:var(--frankie-primary)}.frankie-root .bg-red-500{background-color:var(--color-red-500)}.frankie-root .bg-red-500\\/10{background-color:#fb2c361a}@supports (color:color-mix(in lab, red, red)){.frankie-root .bg-red-500\\/10{background-color:color-mix(in oklab, var(--color-red-500) 10%, transparent)}}.frankie-root .bg-secondary{background-color:var(--frankie-secondary)}.frankie-root .object-cover{object-fit:cover}.frankie-root .p-3{padding:calc(var(--spacing) * 3)}.frankie-root .p-4{padding:calc(var(--spacing) * 4)}.frankie-root .px-1{padding-inline:var(--spacing)}.frankie-root .px-2{padding-inline:calc(var(--spacing) * 2)}.frankie-root .px-3{padding-inline:calc(var(--spacing) * 3)}.frankie-root .px-4{padding-inline:calc(var(--spacing) * 4)}.frankie-root .px-6{padding-inline:calc(var(--spacing) * 6)}.frankie-root .py-0{padding-block:0}.frankie-root .py-0\\.5{padding-block:calc(var(--spacing) * .5)}.frankie-root .py-1{padding-block:var(--spacing)}.frankie-root .py-2{padding-block:calc(var(--spacing) * 2)}.frankie-root .py-3{padding-block:calc(var(--spacing) * 3)}.frankie-root .pl-2{padding-left:calc(var(--spacing) * 2)}.frankie-root .text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.frankie-root .text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.frankie-root .text-\\[0\\.625rem\\]{font-size:.625rem}.frankie-root .text-\\[0\\.6875rem\\]{font-size:.6875rem}.frankie-root .text-\\[11px\\]{font-size:11px}.frankie-root .leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.frankie-root .font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.frankie-root .font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.frankie-root .whitespace-nowrap{white-space:nowrap}.frankie-root .text-emerald-600{color:var(--color-emerald-600)}.frankie-root .text-foreground{color:var(--frankie-foreground)}.frankie-root .text-muted-foreground{color:var(--frankie-muted-foreground)}.frankie-root .text-primary{color:var(--frankie-primary)}.frankie-root .text-primary-foreground{color:var(--frankie-primary-foreground)}.frankie-root .text-red-600{color:var(--color-red-600)}.frankie-root .text-secondary-foreground{color:var(--frankie-secondary-foreground)}.frankie-root .italic{font-style:italic}.frankie-root .underline-offset-4{text-underline-offset:4px}.frankie-root .opacity-60{opacity:.6}.frankie-root .opacity-70{opacity:.7}.frankie-root .shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-lg{--tw-shadow:0 10px 15px -3px var(--tw-shadow-color,#0000001a), 0 4px 6px -4px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .shadow-none{--tw-shadow:0 0 #0000;box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .outline{outline-style:var(--tw-outline-style);outline-width:1px}.frankie-root .transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.frankie-root .ease-in-out{--tw-ease:var(--ease-in-out);transition-timing-function:var(--ease-in-out)}.frankie-root .select-all{-webkit-user-select:all;user-select:all}.frankie-root .placeholder\\:text-muted-foreground::placeholder{color:var(--frankie-muted-foreground)}@media (hover:hover){.frankie-root .hover\\:border-border:hover{border-color:var(--frankie-border)}.frankie-root .hover\\:bg-muted:hover{background-color:var(--frankie-muted)}.frankie-root .hover\\:text-foreground:hover{color:var(--frankie-foreground)}.frankie-root .hover\\:underline:hover{text-decoration-line:underline}.frankie-root .hover\\:opacity-90:hover{opacity:.9}.frankie-root .hover\\:opacity-100:hover{opacity:1}}.frankie-root .focus-visible\\:ring-2:focus-visible{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.frankie-root .focus-visible\\:ring-primary:focus-visible{--tw-ring-color:var(--frankie-primary)}.frankie-root .focus-visible\\:outline-none:focus-visible{--tw-outline-style:none;outline-style:none}.frankie-root .disabled\\:pointer-events-none:disabled{pointer-events:none}.frankie-root .disabled\\:cursor-not-allowed:disabled{cursor:not-allowed}.frankie-root .disabled\\:opacity-50:disabled{opacity:.5}.frankie-root .dark\\:text-emerald-400:where(.dark,.dark *){color:var(--color-emerald-400)}.frankie-root .dark\\:text-red-400:where(.dark,.dark *){color:var(--color-red-400)}.frankie-root .\\[\\&_svg\\]\\:pointer-events-none svg{pointer-events:none}.frankie-root .\\[\\&_svg\\]\\:size-4 svg{width:calc(var(--spacing) * 4);height:calc(var(--spacing) * 4)}.frankie-root .\\[\\&_svg\\]\\:shrink-0 svg{flex-shrink:0}.frankie-root .\\[\\&\\:\\:-webkit-details-marker\\]\\:hidden::-webkit-details-marker{display:none}.frankie-root.frankie-chat-panel{min-height:0;position:relative}.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px var(--frankie-primary), 0 12px 28px -12px #0f172a47, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel{box-shadow:0 32px 72px -18px color-mix(in srgb, var(--frankie-primary) 22%, #0f172a6b), 0 12px 28px -12px #0f172a47, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 8%, transparent)}}.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-widget-panel.dark{box-shadow:0 34px 80px -16px #000000b8, 0 14px 32px -12px #0009, 0 0 0 1px color-mix(in srgb, var(--frankie-foreground) 12%, transparent)}}.frankie-root .frankie-agent-context-wrap{flex:none;justify-content:center;padding:12px 10px;display:flex}.frankie-root .frankie-agent-context{max-width:100%;color:var(--frankie-foreground);font-size:13px;position:relative}.frankie-root .frankie-agent-context>summary{border:1px solid var(--frankie-border);background:var(--frankie-background);cursor:pointer;white-space:nowrap;border-radius:999px;justify-content:center;align-items:center;gap:10px;min-height:44px;padding:7px 13px;list-style:none;display:flex;box-shadow:0 1px 3px #0000000d}.frankie-root .frankie-agent-context>summary::-webkit-details-marker{display:none}.frankie-root .frankie-agent-context>summary>svg{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-context-count{background:var(--frankie-muted);color:var(--frankie-muted-foreground);border-radius:999px;padding:2px 10px;font-size:12px}.frankie-root .frankie-context-status{color:var(--frankie-muted-foreground);align-items:center;gap:6px;font-size:12px;display:flex}.frankie-root .frankie-context-status i{background:currentColor;border-radius:50%;width:7px;height:7px}.frankie-root .frankie-context-status[data-active=true]{color:#00976b}.frankie-root .frankie-context-detail{z-index:10;border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:16px;width:340px;max-width:calc(100vw - 32px);padding:18px;position:absolute;top:calc(100% + 10px);left:50%;transform:translate(-50%);box-shadow:0 12px 32px #00000016}.frankie-root .frankie-context-detail p{color:var(--frankie-muted-foreground);margin:10px 0 0;font-size:12px;line-height:1.6}.frankie-root .frankie-context-detail ul{max-height:220px;margin:12px 0;padding:0;list-style:none;overflow:auto}.frankie-root .frankie-context-detail li{border-bottom:1px solid var(--frankie-border);overflow-wrap:anywhere;justify-content:space-between;gap:12px;padding:8px 0;display:flex}.frankie-root .frankie-context-detail small{color:var(--frankie-muted-foreground);flex:none}.frankie-root .frankie-sr-only{clip:rect(0,0,0,0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.frankie-root.frankie-chat-panel-fill{--frankie-canvas:var(--frankie-background)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill{--frankie-canvas:color-mix(in srgb,var(--frankie-background) 60%,var(--frankie-muted))}}.frankie-root.frankie-chat-panel-fill{background:var(--frankie-canvas)}.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:44px 16px 26px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:14px max(24px,50% - 480px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-message-list{gap:38px}.frankie-root.frankie-chat-panel-fill .frankie-user-bubble{background:var(--frankie-muted);color:var(--frankie-foreground);white-space:pre-wrap;overflow-wrap:anywhere;border-radius:24px;padding:12px 20px;font-size:15px}.frankie-root.frankie-chat-panel-fill .frankie-assistant-content{flex:1;max-width:100%;font-size:15px;line-height:1.7}.frankie-root.frankie-chat-panel-fill .frankie-assistant-message{gap:14px}.frankie-root .frankie-thinking{color:var(--frankie-muted-foreground);gap:5px}.frankie-root .frankie-thinking-label{margin-right:6px;font-size:14px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{background:0 0;border:0;flex:none;padding:16px max(24px,50% - 500px) 24px}.frankie-root.frankie-chat-panel-fill .frankie-composer{border:1px solid var(--frankie-border);background:var(--frankie-background);border-radius:28px;padding:14px;box-shadow:0 2px 4px #0000000a}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:var(--frankie-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{border-color:color-mix(in srgb,var(--frankie-foreground) 32%,var(--frankie-border))}}.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root.frankie-chat-panel-fill .frankie-composer:focus-within{box-shadow:0 0 0 2px color-mix(in srgb,var(--frankie-primary) 8%,transparent)}}.frankie-root.frankie-chat-panel-fill .frankie-composer-controls{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.frankie-root.frankie-chat-panel-fill .frankie-composer textarea{resize:none;width:100%;min-height:32px;max-height:140px;color:var(--frankie-foreground);box-shadow:none;background:0 0;border:0;border-radius:0;outline:none;flex:0 0 100%;order:-1;margin-bottom:4px;padding:2px 6px;font-size:16px}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Send],.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Stop]{background:var(--frankie-primary);width:42px;height:42px;color:var(--frankie-primary-foreground);border:0;border-radius:50%;margin-left:auto}.frankie-root.frankie-chat-panel-fill .frankie-composer button[aria-label=Attach\\ files]{border-radius:50%;width:42px;height:42px}.frankie-root .frankie-composer-actions{align-items:center;gap:6px;min-width:0;display:flex}.frankie-root .frankie-composer-meta{align-items:center;gap:8px;margin-top:6px;padding:0 2px;display:flex}.frankie-root .frankie-model-picker{align-items:center;min-width:0;display:inline-flex}.frankie-root .frankie-model-select{appearance:none;max-width:12rem;color:var(--frankie-muted-foreground);font:inherit;cursor:pointer;text-overflow:ellipsis;background-color:#0000;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238b8b8b' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");background-position:right 6px center;background-repeat:no-repeat;background-size:11px;border:0;border-radius:8px;padding:5px 22px 5px 8px;font-size:12px;font-weight:500;line-height:1;transition:background-color .12s,color .12s}.frankie-root .frankie-model-select:hover{background-color:var(--frankie-muted);color:var(--frankie-foreground)}.frankie-root .frankie-model-select:disabled{opacity:.5;cursor:not-allowed}.frankie-root .frankie-model-select option{color:var(--frankie-foreground);background:var(--frankie-background);font-weight:400}.frankie-root .frankie-history-desktop{background:var(--frankie-background);width:290px;padding:20px 12px}.frankie-root .frankie-history-collapsed{width:56px;padding:20px 8px}.frankie-root .frankie-new-chat{background:0 0;border:0;justify-content:flex-start;padding:8px 12px;font-size:15px}.frankie-root .frankie-history-heading{color:var(--frankie-muted-foreground);padding:0 12px;font-size:12px;font-weight:400}.frankie-root .frankie-history-list button{min-height:44px;padding:10px 12px}.frankie-root .frankie-history-list button span{font-size:14px}.frankie-root .frankie-history-list button[aria-current],.frankie-root .frankie-history-list button:hover{background:var(--frankie-muted);border-color:#0000}.frankie-root .frankie-history-list small{display:none}.frankie-root .frankie-history-group{flex-direction:column;gap:2px;display:flex}.frankie-root .frankie-history-group+.frankie-history-group{margin-top:12px}.frankie-root .frankie-history-group-label{letter-spacing:.04em;color:var(--frankie-muted-foreground);margin:0;padding:4px 12px;font-size:11px;font-weight:600}.frankie-root .frankie-page-header{background:var(--frankie-background);height:56px;min-height:56px}.frankie-root .frankie-page-header>div{min-width:0}.frankie-root .frankie-thread-title{text-overflow:ellipsis;white-space:nowrap;max-width:55vw;font-weight:500;overflow:hidden}.frankie-root .frankie-page-header button{background:0 0;border-color:#0000}.frankie-root .frankie-page-canvas>.frankie-inline{max-width:none}@media (max-width:760px){.frankie-root.frankie-chat-panel-fill .frankie-agent-context-wrap{padding:20px 8px 14px}.frankie-root.frankie-chat-panel-fill .frankie-transcript{padding:12px 16px 16px}.frankie-root.frankie-chat-panel-fill .frankie-composer-footer{padding:10px 12px calc(12px + env(safe-area-inset-bottom))}.frankie-root .frankie-agent-context>summary{gap:6px;padding:7px 9px;font-size:12px}.frankie-root .frankie-context-count{padding:2px 7px;font-size:11px}.frankie-root .frankie-context-status{gap:4px;font-size:11px}.frankie-root .frankie-agent-context>summary>svg{width:14px}.frankie-root .frankie-thread-title{max-width:42vw}}.frankie-root .frankie-agent-context>summary>svg:first-child{box-sizing:content-box;background:var(--frankie-muted);border-radius:50%;padding:4px}.frankie-root.frankie-chat-panel-fill .frankie-agent-context>summary{font-size:14px}.frankie-root .frankie-agent-context>summary>span:first-of-type{border-right:1px solid var(--frankie-border);padding-right:10px}@media (max-width:760px){.frankie-root .frankie-page-header button>span{display:none}}.frankie-root .frankie-choices{flex-direction:column;gap:8px;margin-top:12px;display:flex}.frankie-root .frankie-choice{text-align:left;cursor:pointer;border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);width:100%;color:var(--frankie-foreground);font:inherit;align-items:center;gap:12px;padding:11px 14px;font-size:14px;line-height:1.4;transition:border-color .15s,background-color .15s,transform .12s;display:flex}.frankie-root .frankie-choice:hover:not(:disabled){border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){border-color:color-mix(in srgb, var(--frankie-primary) 55%, var(--frankie-border))}}.frankie-root .frankie-choice:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 6%, var(--frankie-background))}}.frankie-root .frankie-choice:active:not(:disabled){transform:scale(.99)}.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-choice:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}.frankie-root .frankie-choice:disabled{opacity:.6;cursor:default}.frankie-root .frankie-choice-index{border:1px solid var(--frankie-border);background:var(--frankie-muted);width:24px;height:24px;color:var(--frankie-muted-foreground);border-radius:50%;flex:none;place-items:center;font-size:12px;font-weight:600;transition:background-color .15s,color .15s,border-color .15s;display:grid}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-primary);border-color:var(--frankie-primary);color:var(--frankie-primary-foreground)}.frankie-root .frankie-choice-label{flex:1;min-width:0}.frankie-root .frankie-choice-go{color:var(--frankie-muted-foreground);opacity:0;flex:none;transition:opacity .15s,transform .15s;transform:translate(-4px)}.frankie-root .frankie-choice:hover:not(:disabled) .frankie-choice-go{opacity:1;color:var(--frankie-primary);transform:none}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-choice,.frankie-root .frankie-choice-go,.frankie-root .frankie-choice-index{transition:none}}.frankie-root .frankie-choice-other{color:var(--frankie-muted-foreground);border-style:dashed}.frankie-root .frankie-choice-other .frankie-choice-index{background:0 0}.frankie-root .frankie-choice-other:hover:not(:disabled){color:var(--frankie-foreground)}.frankie-root .frankie-choice-other:hover:not(:disabled) .frankie-choice-index{background:var(--frankie-muted);border-color:var(--frankie-border);color:var(--frankie-foreground)}.frankie-root .frankie-choice-hint{color:var(--frankie-muted-foreground);flex:none;font-size:12px}.frankie-root .frankie-skeleton{flex-direction:column;gap:8px;max-width:min(420px,100%);padding:4px 0 6px;display:flex}.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, var(--frankie-muted-foreground) 25%, var(--frankie-muted-foreground) 37%, var(--frankie-muted-foreground) 63%);border-radius:6px;height:12px}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:linear-gradient(90deg, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 25%, color-mix(in srgb, var(--frankie-muted-foreground) 19%, transparent) 37%, color-mix(in srgb, var(--frankie-muted-foreground) 11%, transparent) 63%)}}.frankie-root .frankie-skeleton-line{background-size:400% 100%;animation:2.2s ease-in-out infinite frankie-shimmer}.frankie-root .frankie-skeleton.is-delayed{animation:.25s .35s both frankie-skeleton-in}@keyframes frankie-skeleton-in{0%{opacity:0}to{opacity:1}}@keyframes frankie-shimmer{0%{background-position:100%}to{background-position:0}}@keyframes frankie-skeleton-pulse{0%,to{opacity:.45}50%{opacity:.85}}@media (prefers-reduced-motion:reduce){.frankie-root .frankie-skeleton-line{background:var(--frankie-muted-foreground)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-skeleton-line{background:color-mix(in srgb, var(--frankie-muted-foreground) 16%, transparent)}}.frankie-root .frankie-skeleton-line{animation:1.6s ease-in-out infinite frankie-skeleton-pulse}}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"<percentage>";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"<length>";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-outline-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-ease{syntax:"*";inherits:false}@keyframes spin{to{transform:rotate(360deg)}}@keyframes pulse{50%{opacity:.5}}`;
|
|
971
1032
|
|
|
972
1033
|
// src/lib/ensure-frankie-styles.ts
|
|
973
1034
|
var STYLE_ELEMENT_ID = "frankie-fallback-styles";
|
|
@@ -983,11 +1044,11 @@ function ensureFrankieStyles() {
|
|
|
983
1044
|
}
|
|
984
1045
|
|
|
985
1046
|
// src/lib/styles-ready.ts
|
|
986
|
-
var
|
|
1047
|
+
var useIsomorphicLayoutEffect2 = typeof window === "undefined" ? import_react3.useEffect : import_react3.useLayoutEffect;
|
|
987
1048
|
var SENTINEL = "--frankie-styles-ready";
|
|
988
1049
|
function useStylesReady(ref, timeoutMs = 600, injectFallback = true) {
|
|
989
1050
|
const [ready, setReady] = (0, import_react3.useState)(false);
|
|
990
|
-
|
|
1051
|
+
useIsomorphicLayoutEffect2(() => {
|
|
991
1052
|
if (ready) return;
|
|
992
1053
|
const node = ref.current;
|
|
993
1054
|
if (!node || typeof window === "undefined" || typeof window.getComputedStyle !== "function") {
|
|
@@ -1023,7 +1084,42 @@ function useStylesReady(ref, timeoutMs = 600, injectFallback = true) {
|
|
|
1023
1084
|
return ready;
|
|
1024
1085
|
}
|
|
1025
1086
|
|
|
1087
|
+
// src/lib/bottom-bars.ts
|
|
1088
|
+
function bottomObstruction(root, position, offset) {
|
|
1089
|
+
if (typeof document === "undefined" || typeof window === "undefined" || typeof document.elementsFromPoint !== "function") return 0;
|
|
1090
|
+
const width = window.innerWidth;
|
|
1091
|
+
const height = window.innerHeight;
|
|
1092
|
+
if (!width || !height) return 0;
|
|
1093
|
+
const xs = position === "bottom-right" ? [width - offset - 24, width - 8] : [offset + 24, 8];
|
|
1094
|
+
const ys = [height - offset - 24, height - 4];
|
|
1095
|
+
let lift = 0;
|
|
1096
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1097
|
+
for (const x of xs) for (const y of ys) {
|
|
1098
|
+
for (const hit of document.elementsFromPoint(x, y)) {
|
|
1099
|
+
if (root?.contains(hit)) continue;
|
|
1100
|
+
const bar = anchoredAncestor(hit);
|
|
1101
|
+
if (!bar || seen.has(bar)) continue;
|
|
1102
|
+
seen.add(bar);
|
|
1103
|
+
const rect = bar.getBoundingClientRect();
|
|
1104
|
+
const anchoredToBottom = rect.bottom >= height - 4;
|
|
1105
|
+
const short = rect.height > 0 && rect.height < height * 0.4;
|
|
1106
|
+
if (anchoredToBottom && short) lift = Math.max(lift, height - rect.top);
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
return Math.round(lift);
|
|
1110
|
+
}
|
|
1111
|
+
function anchoredAncestor(element) {
|
|
1112
|
+
let node = element;
|
|
1113
|
+
for (let depth = 0; node && depth < 8 && node !== document.body && node !== document.documentElement; depth += 1) {
|
|
1114
|
+
const position = getComputedStyle(node).position;
|
|
1115
|
+
if (position === "fixed" || position === "sticky") return node;
|
|
1116
|
+
node = node.parentElement;
|
|
1117
|
+
}
|
|
1118
|
+
return null;
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1026
1121
|
// src/lib/theme.ts
|
|
1122
|
+
var import_react4 = require("react");
|
|
1027
1123
|
function themeToCssVars(theme) {
|
|
1028
1124
|
if (!theme) return {};
|
|
1029
1125
|
const vars = {};
|
|
@@ -1042,6 +1138,18 @@ function resolveMode(mode) {
|
|
|
1042
1138
|
if (typeof window === "undefined" || !window.matchMedia) return "light";
|
|
1043
1139
|
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
1044
1140
|
}
|
|
1141
|
+
var DARK_QUERY = "(prefers-color-scheme: dark)";
|
|
1142
|
+
function subscribeToScheme(onChange) {
|
|
1143
|
+
const media = typeof window !== "undefined" && window.matchMedia ? window.matchMedia(DARK_QUERY) : void 0;
|
|
1144
|
+
media?.addEventListener?.("change", onChange);
|
|
1145
|
+
return () => media?.removeEventListener?.("change", onChange);
|
|
1146
|
+
}
|
|
1147
|
+
var prefersDark = () => typeof window !== "undefined" && Boolean(window.matchMedia?.(DARK_QUERY).matches);
|
|
1148
|
+
function useResolvedMode(mode) {
|
|
1149
|
+
const dark = (0, import_react4.useSyncExternalStore)(subscribeToScheme, prefersDark, () => false);
|
|
1150
|
+
if (mode === "light" || mode === "dark") return mode;
|
|
1151
|
+
return dark ? "dark" : "light";
|
|
1152
|
+
}
|
|
1045
1153
|
|
|
1046
1154
|
// src/lib/utils.ts
|
|
1047
1155
|
var import_clsx = require("clsx");
|
|
@@ -1082,7 +1190,7 @@ function Button({ className, variant, size, asChild = false, ...props }) {
|
|
|
1082
1190
|
|
|
1083
1191
|
// src/components/chat/ChatPanel.tsx
|
|
1084
1192
|
var import_lucide_react7 = require("lucide-react");
|
|
1085
|
-
var
|
|
1193
|
+
var import_react10 = require("react");
|
|
1086
1194
|
|
|
1087
1195
|
// src/components/ui/avatar.tsx
|
|
1088
1196
|
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
@@ -1132,9 +1240,9 @@ function CardFooter({ className, ...props }) {
|
|
|
1132
1240
|
}
|
|
1133
1241
|
|
|
1134
1242
|
// src/components/ui/scroll-area.tsx
|
|
1135
|
-
var
|
|
1243
|
+
var import_react5 = require("react");
|
|
1136
1244
|
var import_jsx_runtime5 = require("react/jsx-runtime");
|
|
1137
|
-
var ScrollArea = (0,
|
|
1245
|
+
var ScrollArea = (0, import_react5.forwardRef)(
|
|
1138
1246
|
function ScrollArea2({ className, ...props }, ref) {
|
|
1139
1247
|
return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { ref, className: cn("frankie-scroll overflow-y-auto overscroll-contain", className), ...props });
|
|
1140
1248
|
}
|
|
@@ -1229,7 +1337,7 @@ function ChatEmptyState() {
|
|
|
1229
1337
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { className: "frankie-page-suggest-label", children: "Try asking me to" }),
|
|
1230
1338
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "frankie-page-prompts", children: starters.map((card, index) => {
|
|
1231
1339
|
const Icon = card.icon && NAMED_ICONS[card.icon] || PROMPT_ICONS[index % PROMPT_ICONS.length];
|
|
1232
|
-
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("button", { type: "button", disabled: busy || restoring, onClick: () => void send(card.message), children: [
|
|
1340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("button", { type: "button", disabled: busy || restoring, onClick: () => void send(card.message), title: card.description || void 0, children: [
|
|
1233
1341
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "frankie-prompt-icon", "aria-hidden": true, children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Icon, { size: 18 }) }),
|
|
1234
1342
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("span", { className: "frankie-prompt-text", children: [
|
|
1235
1343
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)("strong", { children: card.title }),
|
|
@@ -1244,12 +1352,12 @@ function ChatEmptyState() {
|
|
|
1244
1352
|
|
|
1245
1353
|
// src/components/chat/Composer.tsx
|
|
1246
1354
|
var import_lucide_react3 = require("lucide-react");
|
|
1247
|
-
var
|
|
1355
|
+
var import_react7 = require("react");
|
|
1248
1356
|
|
|
1249
1357
|
// src/components/ui/textarea.tsx
|
|
1250
|
-
var
|
|
1358
|
+
var import_react6 = require("react");
|
|
1251
1359
|
var import_jsx_runtime8 = require("react/jsx-runtime");
|
|
1252
|
-
var Textarea = (0,
|
|
1360
|
+
var Textarea = (0, import_react6.forwardRef)(
|
|
1253
1361
|
function Textarea2({ className, ...props }, ref) {
|
|
1254
1362
|
return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)(
|
|
1255
1363
|
"textarea",
|
|
@@ -1269,12 +1377,12 @@ var Textarea = (0, import_react5.forwardRef)(
|
|
|
1269
1377
|
var import_jsx_runtime9 = require("react/jsx-runtime");
|
|
1270
1378
|
var MAX_ROWS_PX = 140;
|
|
1271
1379
|
function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, attachments, modelPicker, focusHandleRef }) {
|
|
1272
|
-
const [value, setValue] = (0,
|
|
1273
|
-
const [files, setFiles] = (0,
|
|
1274
|
-
const [fileError, setFileError] = (0,
|
|
1275
|
-
const textareaRef = (0,
|
|
1276
|
-
const fileInputRef = (0,
|
|
1277
|
-
(0,
|
|
1380
|
+
const [value, setValue] = (0, import_react7.useState)("");
|
|
1381
|
+
const [files, setFiles] = (0, import_react7.useState)([]);
|
|
1382
|
+
const [fileError, setFileError] = (0, import_react7.useState)(void 0);
|
|
1383
|
+
const textareaRef = (0, import_react7.useRef)(null);
|
|
1384
|
+
const fileInputRef = (0, import_react7.useRef)(null);
|
|
1385
|
+
(0, import_react7.useEffect)(() => {
|
|
1278
1386
|
if (!focusHandleRef) return;
|
|
1279
1387
|
focusHandleRef.current = () => textareaRef.current?.focus();
|
|
1280
1388
|
return () => {
|
|
@@ -1283,13 +1391,13 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
1283
1391
|
}, [focusHandleRef]);
|
|
1284
1392
|
const canAttach = Boolean(attachments?.enabled);
|
|
1285
1393
|
const models = modelPicker?.models ?? [];
|
|
1286
|
-
(0,
|
|
1394
|
+
(0, import_react7.useEffect)(() => {
|
|
1287
1395
|
const node = textareaRef.current;
|
|
1288
1396
|
if (!node) return;
|
|
1289
1397
|
node.style.height = "auto";
|
|
1290
1398
|
node.style.height = `${Math.min(node.scrollHeight, MAX_ROWS_PX)}px`;
|
|
1291
1399
|
}, [value]);
|
|
1292
|
-
const addFiles = (0,
|
|
1400
|
+
const addFiles = (0, import_react7.useCallback)(
|
|
1293
1401
|
async (list) => {
|
|
1294
1402
|
if (!list || !attachments) return;
|
|
1295
1403
|
setFileError(void 0);
|
|
@@ -1322,7 +1430,7 @@ function Composer({ onSend, onStop, busy, placeholder, disabled, autoFocus, atta
|
|
|
1322
1430
|
[attachments, files.length]
|
|
1323
1431
|
);
|
|
1324
1432
|
const removeFile = (id) => setFiles((current) => current.filter((file) => file.id !== id));
|
|
1325
|
-
const submit = (0,
|
|
1433
|
+
const submit = (0, import_react7.useCallback)(() => {
|
|
1326
1434
|
const text = value.trim();
|
|
1327
1435
|
if (!text && files.length === 0 || busy || disabled) return;
|
|
1328
1436
|
setValue("");
|
|
@@ -1458,7 +1566,7 @@ function readAsBase64(file) {
|
|
|
1458
1566
|
|
|
1459
1567
|
// src/components/chat/MessageBubble.tsx
|
|
1460
1568
|
var import_lucide_react6 = require("lucide-react");
|
|
1461
|
-
var
|
|
1569
|
+
var import_react9 = require("react");
|
|
1462
1570
|
|
|
1463
1571
|
// src/lib/choices.ts
|
|
1464
1572
|
var CLOSED = /```frankie:choices[ \t]*\r?\n([\s\S]*?)```/;
|
|
@@ -1522,10 +1630,10 @@ function Choices({
|
|
|
1522
1630
|
}
|
|
1523
1631
|
|
|
1524
1632
|
// src/components/chat/Markdown.tsx
|
|
1525
|
-
var
|
|
1633
|
+
var import_react8 = require("react");
|
|
1526
1634
|
var import_jsx_runtime11 = require("react/jsx-runtime");
|
|
1527
|
-
var Markdown = (0,
|
|
1528
|
-
const blocks = (0,
|
|
1635
|
+
var Markdown = (0, import_react8.memo)(function Markdown2({ text, className }) {
|
|
1636
|
+
const blocks = (0, import_react8.useMemo)(() => renderBlocks(text), [text]);
|
|
1529
1637
|
return /* @__PURE__ */ (0, import_jsx_runtime11.jsx)("div", { className: cn("frankie-prose", className), children: blocks });
|
|
1530
1638
|
});
|
|
1531
1639
|
function renderBlocks(text) {
|
|
@@ -1610,7 +1718,7 @@ function renderInline(text) {
|
|
|
1610
1718
|
const token = match[0];
|
|
1611
1719
|
const start = match.index;
|
|
1612
1720
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
1613
|
-
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(
|
|
1721
|
+
nodes.push(/* @__PURE__ */ (0, import_jsx_runtime11.jsx)(import_react8.Fragment, { children: renderToken(token) }, start));
|
|
1614
1722
|
lastIndex = start + token.length;
|
|
1615
1723
|
}
|
|
1616
1724
|
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
@@ -1704,7 +1812,7 @@ function badgeVariant(status) {
|
|
|
1704
1812
|
|
|
1705
1813
|
// src/components/chat/MessageBubble.tsx
|
|
1706
1814
|
var import_jsx_runtime14 = require("react/jsx-runtime");
|
|
1707
|
-
var MessageBubble = (0,
|
|
1815
|
+
var MessageBubble = (0, import_react9.memo)(function MessageBubble2({
|
|
1708
1816
|
message,
|
|
1709
1817
|
assistantName,
|
|
1710
1818
|
avatarUrl,
|
|
@@ -1736,8 +1844,7 @@ var MessageBubble = (0, import_react8.memo)(function MessageBubble2({
|
|
|
1736
1844
|
}
|
|
1737
1845
|
const { body, options } = extractChoices(message.text);
|
|
1738
1846
|
const showChoices = Boolean(active && onChoose && options && message.status === "done");
|
|
1739
|
-
const
|
|
1740
|
-
const waiting = message.status === "streaming" && body.length === 0 && !toolsRunning;
|
|
1847
|
+
const waiting = message.status === "streaming" && body.length === 0 && message.tools.length === 0;
|
|
1741
1848
|
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "frankie-assistant-message flex gap-2", children: [
|
|
1742
1849
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "frankie-assistant-actions mt-0.5 flex shrink-0 flex-col items-center gap-1", children: [
|
|
1743
1850
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Avatar, { ...avatarUrl ? { src: avatarUrl } : {}, name: assistantName }),
|
|
@@ -1747,12 +1854,12 @@ var MessageBubble = (0, import_react8.memo)(function MessageBubble2({
|
|
|
1747
1854
|
message.status === "error" ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)("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 }) : body ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Markdown, { text: body, className: "text-sm" }) : null,
|
|
1748
1855
|
showChoices ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Choices, { options: options ?? [], onPick: onChoose, ...onRequestInput ? { onSomethingElse: onRequestInput } : {}, disabled: busy }) : null,
|
|
1749
1856
|
showToolActivity ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(ToolActivityList, { tools: message.tools, ...renderTool ? { renderTool } : {} }) : null,
|
|
1750
|
-
waiting ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Thinking, {}) : null
|
|
1857
|
+
waiting ? /* @__PURE__ */ (0, import_jsx_runtime14.jsx)(Thinking, { delayed: true }) : null
|
|
1751
1858
|
] })
|
|
1752
1859
|
] });
|
|
1753
1860
|
});
|
|
1754
1861
|
function MessageCopyButton({ text }) {
|
|
1755
|
-
const [copied, setCopied] = (0,
|
|
1862
|
+
const [copied, setCopied] = (0, import_react9.useState)(false);
|
|
1756
1863
|
const copy = async () => {
|
|
1757
1864
|
try {
|
|
1758
1865
|
await navigator.clipboard.writeText(text);
|
|
@@ -1776,8 +1883,8 @@ function TypingDots() {
|
|
|
1776
1883
|
))
|
|
1777
1884
|
] });
|
|
1778
1885
|
}
|
|
1779
|
-
function Thinking() {
|
|
1780
|
-
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "frankie-skeleton", role: "status", "aria-label": "Thinking", children: [
|
|
1886
|
+
function Thinking({ delayed = false }) {
|
|
1887
|
+
return /* @__PURE__ */ (0, import_jsx_runtime14.jsxs)("div", { className: "frankie-skeleton" + (delayed ? " is-delayed" : ""), role: "status", "aria-label": "Thinking", children: [
|
|
1781
1888
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "frankie-skeleton-line", style: { width: "62%" } }),
|
|
1782
1889
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "frankie-skeleton-line", style: { width: "92%" } }),
|
|
1783
1890
|
/* @__PURE__ */ (0, import_jsx_runtime14.jsx)("span", { className: "frankie-skeleton-line", style: { width: "48%" } })
|
|
@@ -1801,14 +1908,14 @@ function ChatPanel({
|
|
|
1801
1908
|
const { config, configError, loading } = useFrankie();
|
|
1802
1909
|
const theme = useFrankieTheme();
|
|
1803
1910
|
const { messages, busy, restoring, error, send, stop, reset } = useFrankieChat();
|
|
1804
|
-
const scrollRef = (0,
|
|
1805
|
-
const atBottomRef = (0,
|
|
1911
|
+
const scrollRef = (0, import_react10.useRef)(null);
|
|
1912
|
+
const atBottomRef = (0, import_react10.useRef)(true);
|
|
1806
1913
|
const features = config?.features;
|
|
1807
1914
|
const pickerModels = features?.modelPicker.enabled ? features.modelPicker.models : [];
|
|
1808
|
-
const [model, setModel] = (0,
|
|
1809
|
-
const composerFocus = (0,
|
|
1915
|
+
const [model, setModel] = (0, import_react10.useState)(void 0);
|
|
1916
|
+
const composerFocus = (0, import_react10.useRef)(null);
|
|
1810
1917
|
const focusComposer = () => composerFocus.current?.();
|
|
1811
|
-
(0,
|
|
1918
|
+
(0, import_react10.useEffect)(() => {
|
|
1812
1919
|
const node = scrollRef.current;
|
|
1813
1920
|
if (!node) return;
|
|
1814
1921
|
const onScroll = () => {
|
|
@@ -1817,7 +1924,7 @@ function ChatPanel({
|
|
|
1817
1924
|
node.addEventListener("scroll", onScroll, { passive: true });
|
|
1818
1925
|
return () => node.removeEventListener("scroll", onScroll);
|
|
1819
1926
|
}, []);
|
|
1820
|
-
(0,
|
|
1927
|
+
(0, import_react10.useEffect)(() => {
|
|
1821
1928
|
const node = scrollRef.current;
|
|
1822
1929
|
if (!node) return;
|
|
1823
1930
|
if (messages.length === 0) {
|
|
@@ -1828,7 +1935,7 @@ function ChatPanel({
|
|
|
1828
1935
|
node.scrollTop = node.scrollHeight;
|
|
1829
1936
|
}, [messages]);
|
|
1830
1937
|
const name = config?.branding.name ?? "Assistant";
|
|
1831
|
-
const mode =
|
|
1938
|
+
const mode = useResolvedMode(theme?.mode);
|
|
1832
1939
|
return /* @__PURE__ */ (0, import_jsx_runtime15.jsxs)(
|
|
1833
1940
|
Card,
|
|
1834
1941
|
{
|
|
@@ -1906,19 +2013,21 @@ function FrankieWidget({
|
|
|
1906
2013
|
open: controlledOpen,
|
|
1907
2014
|
onOpenChange,
|
|
1908
2015
|
renderLauncher,
|
|
2016
|
+
launcher = true,
|
|
2017
|
+
avoidBottomBars = true,
|
|
1909
2018
|
...panelProps
|
|
1910
2019
|
}) {
|
|
1911
|
-
const { config, injectStyles } = useFrankie();
|
|
2020
|
+
const { config, injectStyles, themeReady } = useFrankie();
|
|
1912
2021
|
const theme = useFrankieTheme();
|
|
1913
|
-
const rootRef = (0,
|
|
2022
|
+
const rootRef = (0, import_react11.useRef)(null);
|
|
1914
2023
|
const stylesReady = useStylesReady(rootRef, void 0, injectStyles);
|
|
1915
|
-
const [uncontrolledOpen, setUncontrolledOpen] = (0,
|
|
2024
|
+
const [uncontrolledOpen, setUncontrolledOpen] = (0, import_react11.useState)(defaultOpen);
|
|
1916
2025
|
const open = controlledOpen ?? uncontrolledOpen;
|
|
1917
2026
|
const setOpen = (next) => {
|
|
1918
2027
|
if (controlledOpen === void 0) setUncontrolledOpen(next);
|
|
1919
2028
|
onOpenChange?.(next);
|
|
1920
2029
|
};
|
|
1921
|
-
(0,
|
|
2030
|
+
(0, import_react11.useEffect)(() => {
|
|
1922
2031
|
if (!open) return;
|
|
1923
2032
|
const onKeyDown = (event) => {
|
|
1924
2033
|
if (event.key === "Escape") setOpen(false);
|
|
@@ -1926,20 +2035,60 @@ function FrankieWidget({
|
|
|
1926
2035
|
window.addEventListener("keydown", onKeyDown);
|
|
1927
2036
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1928
2037
|
}, [open]);
|
|
2038
|
+
(0, import_react11.useEffect)(() => {
|
|
2039
|
+
const onClick = (event) => {
|
|
2040
|
+
const target = event.target instanceof Element ? event.target.closest("[data-frankie-open]") : null;
|
|
2041
|
+
if (!target || rootRef.current?.contains(target)) return;
|
|
2042
|
+
if (target instanceof HTMLAnchorElement && !target.getAttribute("href")?.startsWith("#")) event.preventDefault();
|
|
2043
|
+
setOpen(true);
|
|
2044
|
+
};
|
|
2045
|
+
document.addEventListener("click", onClick);
|
|
2046
|
+
return () => document.removeEventListener("click", onClick);
|
|
2047
|
+
}, []);
|
|
2048
|
+
const [lift, setLift] = (0, import_react11.useState)(0);
|
|
2049
|
+
(0, import_react11.useEffect)(() => {
|
|
2050
|
+
if (!avoidBottomBars || typeof window === "undefined") {
|
|
2051
|
+
setLift(0);
|
|
2052
|
+
return;
|
|
2053
|
+
}
|
|
2054
|
+
let frame = 0;
|
|
2055
|
+
let last = 0;
|
|
2056
|
+
const measure = () => {
|
|
2057
|
+
frame = 0;
|
|
2058
|
+
last = Date.now();
|
|
2059
|
+
setLift(bottomObstruction(rootRef.current, position, offset));
|
|
2060
|
+
};
|
|
2061
|
+
const schedule = () => {
|
|
2062
|
+
if (frame) return;
|
|
2063
|
+
const wait = Math.max(0, 400 - (Date.now() - last));
|
|
2064
|
+
frame = window.setTimeout(() => requestAnimationFrame(measure), wait);
|
|
2065
|
+
};
|
|
2066
|
+
schedule();
|
|
2067
|
+
window.addEventListener("resize", schedule);
|
|
2068
|
+
const observer = typeof MutationObserver === "function" ? new MutationObserver((records) => {
|
|
2069
|
+
if (records.some((record) => !rootRef.current?.contains(record.target))) schedule();
|
|
2070
|
+
}) : void 0;
|
|
2071
|
+
observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "style"] });
|
|
2072
|
+
return () => {
|
|
2073
|
+
window.clearTimeout(frame);
|
|
2074
|
+
window.removeEventListener("resize", schedule);
|
|
2075
|
+
observer?.disconnect();
|
|
2076
|
+
};
|
|
2077
|
+
}, [avoidBottomBars, position, offset]);
|
|
1929
2078
|
const label = config?.branding.launcherLabel ?? "Need a hand?";
|
|
1930
|
-
const mode =
|
|
2079
|
+
const mode = useResolvedMode(theme?.mode);
|
|
1931
2080
|
const side = position === "bottom-right" ? { right: offset } : { left: offset };
|
|
1932
2081
|
return /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1933
2082
|
"div",
|
|
1934
2083
|
{
|
|
1935
2084
|
ref: rootRef,
|
|
1936
2085
|
className: cn("frankie-root frankie-widget", mode === "dark" && "dark"),
|
|
1937
|
-
style: { ...themeToCssVars(theme), position: "fixed", bottom: offset, ...side, zIndex, pointerEvents: "none", ...stylesReady ? null : { opacity: 0 } },
|
|
2086
|
+
style: { ...themeToCssVars(theme), position: "fixed", bottom: lift ? lift + Math.min(offset, 12) : offset, ...side, zIndex, pointerEvents: "none", ["--frankie-lift"]: `${lift}px`, ...stylesReady && themeReady ? null : { opacity: 0 } },
|
|
1938
2087
|
"data-frankie-mode": mode,
|
|
1939
2088
|
"data-frankie-styles": stylesReady ? "ready" : "pending",
|
|
1940
2089
|
children: /* @__PURE__ */ (0, import_jsx_runtime16.jsxs)("div", { className: cn("flex flex-col gap-3", position === "bottom-left" ? "items-start" : "items-end"), children: [
|
|
1941
|
-
open ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { pointerEvents: "auto"
|
|
1942
|
-
/* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { pointerEvents: "auto" }, children: renderLauncher ? renderLauncher({ open, toggle: () => setOpen(!open), label }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
2090
|
+
open ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { pointerEvents: "auto" }, children: /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(ChatPanel, { ...panelProps, className: cn("frankie-widget-panel", panelProps.className), onClose: () => setOpen(false), autoFocus: true }) }) : null,
|
|
2091
|
+
launcher || renderLauncher ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)("div", { style: { pointerEvents: "auto" }, children: renderLauncher ? renderLauncher({ open, toggle: () => setOpen(!open), label }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(
|
|
1943
2092
|
Button,
|
|
1944
2093
|
{
|
|
1945
2094
|
size: "icon",
|
|
@@ -1950,27 +2099,27 @@ function FrankieWidget({
|
|
|
1950
2099
|
title: label,
|
|
1951
2100
|
children: open ? /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react8.X, { className: "size-5" }) : /* @__PURE__ */ (0, import_jsx_runtime16.jsx)(import_lucide_react8.MessageCircle, { className: "size-5" })
|
|
1952
2101
|
}
|
|
1953
|
-
) })
|
|
2102
|
+
) }) : null
|
|
1954
2103
|
] })
|
|
1955
2104
|
}
|
|
1956
2105
|
);
|
|
1957
2106
|
}
|
|
1958
2107
|
|
|
1959
2108
|
// src/components/chat/FrankieInline.tsx
|
|
1960
|
-
var
|
|
2109
|
+
var import_react12 = require("react");
|
|
1961
2110
|
var import_jsx_runtime17 = require("react/jsx-runtime");
|
|
1962
2111
|
function FrankieInline({ className, style, ...panelProps }) {
|
|
1963
|
-
const { injectStyles } = useFrankie();
|
|
2112
|
+
const { injectStyles, themeReady } = useFrankie();
|
|
1964
2113
|
const theme = useFrankieTheme();
|
|
1965
|
-
const mode =
|
|
1966
|
-
const rootRef = (0,
|
|
2114
|
+
const mode = useResolvedMode(theme?.mode);
|
|
2115
|
+
const rootRef = (0, import_react12.useRef)(null);
|
|
1967
2116
|
const stylesReady = useStylesReady(rootRef, void 0, injectStyles);
|
|
1968
2117
|
return /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(
|
|
1969
2118
|
"div",
|
|
1970
2119
|
{
|
|
1971
2120
|
ref: rootRef,
|
|
1972
2121
|
className: cn("frankie-root frankie-inline", mode === "dark" && "dark", className),
|
|
1973
|
-
style: { ...themeToCssVars(theme), display: "flex", height: "100%", width: "100%", ...style, ...stylesReady ? null : { opacity: 0 } },
|
|
2122
|
+
style: { ...themeToCssVars(theme), display: "flex", height: "100%", width: "100%", ...style, ...stylesReady && themeReady ? null : { opacity: 0 } },
|
|
1974
2123
|
"data-frankie-mode": mode,
|
|
1975
2124
|
"data-frankie-styles": stylesReady ? "ready" : "pending",
|
|
1976
2125
|
children: /* @__PURE__ */ (0, import_jsx_runtime17.jsx)(ChatPanel, { ...panelProps, fill: true })
|
|
@@ -1979,8 +2128,49 @@ function FrankieInline({ className, style, ...panelProps }) {
|
|
|
1979
2128
|
}
|
|
1980
2129
|
|
|
1981
2130
|
// src/components/chat/FrankieChatPage.tsx
|
|
1982
|
-
var
|
|
2131
|
+
var import_react14 = require("react");
|
|
1983
2132
|
var import_lucide_react9 = require("lucide-react");
|
|
2133
|
+
|
|
2134
|
+
// src/lib/viewport-fill.ts
|
|
2135
|
+
var import_react13 = require("react");
|
|
2136
|
+
function useViewportFill(ref, enabled) {
|
|
2137
|
+
const [fill, setFill] = (0, import_react13.useState)(0);
|
|
2138
|
+
(0, import_react13.useEffect)(() => {
|
|
2139
|
+
if (!enabled || typeof window === "undefined") {
|
|
2140
|
+
setFill(0);
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
let timer = 0;
|
|
2144
|
+
let last = 0;
|
|
2145
|
+
const measure = () => {
|
|
2146
|
+
timer = 0;
|
|
2147
|
+
last = Date.now();
|
|
2148
|
+
const node = ref.current;
|
|
2149
|
+
if (!node) return;
|
|
2150
|
+
const top = node.getBoundingClientRect().top + window.scrollY;
|
|
2151
|
+
const lift = bottomObstruction(node, "bottom-right", 0);
|
|
2152
|
+
setFill(Math.max(0, Math.round(window.innerHeight - top - lift)));
|
|
2153
|
+
};
|
|
2154
|
+
const schedule = () => {
|
|
2155
|
+
if (timer) return;
|
|
2156
|
+
timer = window.setTimeout(() => requestAnimationFrame(measure), Math.max(0, 300 - (Date.now() - last)));
|
|
2157
|
+
};
|
|
2158
|
+
measure();
|
|
2159
|
+
window.addEventListener("resize", schedule);
|
|
2160
|
+
const observer = typeof MutationObserver === "function" ? new MutationObserver((records) => {
|
|
2161
|
+
if (records.some((record) => !ref.current?.contains(record.target))) schedule();
|
|
2162
|
+
}) : void 0;
|
|
2163
|
+
observer?.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ["class", "style"] });
|
|
2164
|
+
return () => {
|
|
2165
|
+
window.clearTimeout(timer);
|
|
2166
|
+
window.removeEventListener("resize", schedule);
|
|
2167
|
+
observer?.disconnect();
|
|
2168
|
+
};
|
|
2169
|
+
}, [ref, enabled]);
|
|
2170
|
+
return fill;
|
|
2171
|
+
}
|
|
2172
|
+
|
|
2173
|
+
// src/components/chat/FrankieChatPage.tsx
|
|
1984
2174
|
var import_jsx_runtime18 = require("react/jsx-runtime");
|
|
1985
2175
|
function groupByDate(items) {
|
|
1986
2176
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -2000,29 +2190,30 @@ function groupByDate(items) {
|
|
|
2000
2190
|
return buckets.filter((bucket) => bucket.items.length);
|
|
2001
2191
|
}
|
|
2002
2192
|
function useHistoryState() {
|
|
2003
|
-
const [conversations, setConversations] = (0,
|
|
2004
|
-
const [historyError, setHistoryError] = (0,
|
|
2005
|
-
const [historyLoading, setHistoryLoading] = (0,
|
|
2006
|
-
const [query, setQuery] = (0,
|
|
2193
|
+
const [conversations, setConversations] = (0, import_react14.useState)([]);
|
|
2194
|
+
const [historyError, setHistoryError] = (0, import_react14.useState)("");
|
|
2195
|
+
const [historyLoading, setHistoryLoading] = (0, import_react14.useState)(false);
|
|
2196
|
+
const [query, setQuery] = (0, import_react14.useState)("");
|
|
2007
2197
|
return { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery };
|
|
2008
2198
|
}
|
|
2009
2199
|
function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, history: state, ...panel }) {
|
|
2010
|
-
const { client, config } = useFrankie();
|
|
2200
|
+
const { client, config, themeReady } = useFrankie();
|
|
2011
2201
|
const { busy, messages } = useFrankieChat();
|
|
2012
2202
|
const theme = useFrankieTheme();
|
|
2203
|
+
const mode = useResolvedMode(theme?.mode);
|
|
2013
2204
|
const { conversations, setConversations, historyError, setHistoryError, historyLoading, setHistoryLoading, query, setQuery } = state;
|
|
2014
|
-
const [drawerOpen, setDrawerOpen] = (0,
|
|
2015
|
-
const [collapsed, setCollapsed] = (0,
|
|
2205
|
+
const [drawerOpen, setDrawerOpen] = (0, import_react14.useState)(false);
|
|
2206
|
+
const [collapsed, setCollapsed] = (0, import_react14.useState)(() => {
|
|
2016
2207
|
try {
|
|
2017
2208
|
return window.localStorage.getItem("frankie.history-collapsed") === "1";
|
|
2018
2209
|
} catch {
|
|
2019
2210
|
return false;
|
|
2020
2211
|
}
|
|
2021
2212
|
});
|
|
2022
|
-
const dialog = (0,
|
|
2023
|
-
const menu = (0,
|
|
2024
|
-
const requestVersion = (0,
|
|
2025
|
-
const refresh = (0,
|
|
2213
|
+
const dialog = (0, import_react14.useRef)(null);
|
|
2214
|
+
const menu = (0, import_react14.useRef)(null);
|
|
2215
|
+
const requestVersion = (0, import_react14.useRef)(0);
|
|
2216
|
+
const refresh = (0, import_react14.useCallback)(async () => {
|
|
2026
2217
|
if (!showHistory || !config || config.allowAnonymous) {
|
|
2027
2218
|
setHistoryError("");
|
|
2028
2219
|
return;
|
|
@@ -2043,19 +2234,19 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, histo
|
|
|
2043
2234
|
if (version === requestVersion.current) setHistoryLoading(false);
|
|
2044
2235
|
}
|
|
2045
2236
|
}, [client, showHistory, config?.allowAnonymous, setConversations, setHistoryError, setHistoryLoading]);
|
|
2046
|
-
(0,
|
|
2237
|
+
(0, import_react14.useEffect)(() => {
|
|
2047
2238
|
if (!busy) void refresh();
|
|
2048
2239
|
return () => {
|
|
2049
2240
|
requestVersion.current++;
|
|
2050
2241
|
};
|
|
2051
2242
|
}, [refresh, busy]);
|
|
2052
|
-
(0,
|
|
2243
|
+
(0, import_react14.useEffect)(() => {
|
|
2053
2244
|
try {
|
|
2054
2245
|
window.localStorage.setItem("frankie.history-collapsed", collapsed ? "1" : "0");
|
|
2055
2246
|
} catch {
|
|
2056
2247
|
}
|
|
2057
2248
|
}, [collapsed]);
|
|
2058
|
-
(0,
|
|
2249
|
+
(0, import_react14.useEffect)(() => {
|
|
2059
2250
|
const node = dialog.current;
|
|
2060
2251
|
if (drawerOpen) {
|
|
2061
2252
|
node?.showModal?.();
|
|
@@ -2102,7 +2293,7 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, histo
|
|
|
2102
2293
|
] }) : !conversations.length && !historyLoading ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("p", { children: config?.allowAnonymous ? "Sign in to sync conversations across devices." : "Your conversations will appear here." }) : null,
|
|
2103
2294
|
navigation ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { className: "frankie-host-navigation", children: navigation }) : null
|
|
2104
2295
|
] });
|
|
2105
|
-
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "frankie-page-shell" + (
|
|
2296
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("div", { className: "frankie-page-shell" + (mode === "dark" ? " dark" : ""), style: { ...themeToCssVars(theme), ...themeReady ? null : { opacity: 0 } }, children: [
|
|
2106
2297
|
showHistory ? /* @__PURE__ */ (0, import_jsx_runtime18.jsxs)("aside", { className: "frankie-history-desktop" + (collapsed ? " frankie-history-collapsed" : ""), children: [
|
|
2107
2298
|
/* @__PURE__ */ (0, import_jsx_runtime18.jsx)("button", { type: "button", className: "frankie-history-toggle", "aria-label": collapsed ? "Expand conversations" : "Collapse conversations", "aria-expanded": !collapsed, onClick: () => setCollapsed((value) => !value), children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react9.ChevronsRight, { size: 16 }) : /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(import_lucide_react9.ChevronsLeft, { size: 16 }) }),
|
|
2108
2299
|
collapsed ? null : history
|
|
@@ -2139,13 +2330,14 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, histo
|
|
|
2139
2330
|
}, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("aside", { children: history }) }) : null
|
|
2140
2331
|
] });
|
|
2141
2332
|
}
|
|
2142
|
-
function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, ...provider }) {
|
|
2143
|
-
const [id, setId] = (0,
|
|
2333
|
+
function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, fillViewport = true, ...provider }) {
|
|
2334
|
+
const [id, setId] = (0, import_react14.useState)(() => provider.conversationId ?? crypto.randomUUID());
|
|
2144
2335
|
const onNew = () => setId(crypto.randomUUID());
|
|
2145
2336
|
const history = useHistoryState();
|
|
2146
|
-
const pageRef = (0,
|
|
2337
|
+
const pageRef = (0, import_react14.useRef)(null);
|
|
2147
2338
|
const stylesReady = useStylesReady(pageRef, void 0, provider.injectStyles);
|
|
2148
|
-
|
|
2339
|
+
const fill = useViewportFill(pageRef, fillViewport);
|
|
2340
|
+
return /* @__PURE__ */ (0, import_jsx_runtime18.jsx)("div", { ref: pageRef, className: ["frankie-root", "frankie-chat-page", className].filter(Boolean).join(" "), style: { height: "100%", minHeight: fill > 360 ? `${fill}px` : "min(36rem, 100dvh)", width: "100%", ...style, ...stylesReady ? null : { opacity: 0 } }, "data-frankie-styles": stylesReady ? "ready" : "pending", children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(FrankieProvider, { ...provider, conversationId: id, storageNamespace: (provider.storageNamespace ?? "frankie." + provider.publishableKey) + "." + id, children: /* @__PURE__ */ (0, import_jsx_runtime18.jsx)(PageChrome, { showHistory, navigation, currentId: id, onSelect: setId, onNew, history, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus }) }) });
|
|
2149
2341
|
}
|
|
2150
2342
|
|
|
2151
2343
|
// src/components/chat/SuggestedPrompts.tsx
|
|
@@ -2217,6 +2409,7 @@ function Separator({ className }) {
|
|
|
2217
2409
|
themeToCssVars,
|
|
2218
2410
|
useFrankie,
|
|
2219
2411
|
useFrankieChat,
|
|
2220
|
-
useFrankieTheme
|
|
2412
|
+
useFrankieTheme,
|
|
2413
|
+
useResolvedMode
|
|
2221
2414
|
});
|
|
2222
2415
|
//# sourceMappingURL=index.cjs.map
|