@frankieai/react 0.2.19 → 0.2.21
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 +282 -158
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +51 -6
- package/dist/index.d.ts +51 -6
- package/dist/index.js +281 -158
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
- package/src/components/chat/ActionConfirm.tsx +85 -0
- package/src/components/chat/ChatPanel.tsx +3 -2
- package/src/components/chat/FrankieWidget.tsx +23 -0
- package/src/components/chat/Markdown.tsx +2 -1
- package/src/components/chat/MessageBubble.tsx +12 -0
- package/src/generated/css-content.ts +1 -1
- package/src/hooks/FrankieProvider.tsx +19 -2
- package/src/hooks/useFrankieChat.ts +26 -2
- package/src/index.ts +2 -1
- package/src/lib/client.ts +14 -4
- package/src/lib/messages.ts +21 -1
- package/src/lib/theme-inherit.test.ts +10 -0
- package/src/lib/theme-inherit.ts +7 -1
- package/src/outside-click.test.tsx +42 -0
- package/src/styles/frankie.css +45 -0
- package/src/theme-ready.test.tsx +19 -0
- package/src/widget.test.tsx +52 -3
package/dist/index.js
CHANGED
|
@@ -58,6 +58,8 @@ var FrankieClient = class {
|
|
|
58
58
|
session;
|
|
59
59
|
/** Identity (JWT `sub`, else the raw token) the cached session was issued for. */
|
|
60
60
|
sessionUserKey;
|
|
61
|
+
/** The host token fetched for the latest turn; sent with it so tools get a live one. */
|
|
62
|
+
latestUserToken;
|
|
61
63
|
get sessionId() {
|
|
62
64
|
return this.session?.sessionId;
|
|
63
65
|
}
|
|
@@ -119,6 +121,7 @@ var FrankieClient = class {
|
|
|
119
121
|
"user_token_unavailable"
|
|
120
122
|
);
|
|
121
123
|
}
|
|
124
|
+
this.latestUserToken = userToken;
|
|
122
125
|
const userKey = sessionIdentity(userToken);
|
|
123
126
|
if (userKey !== this.sessionUserKey) this.resetSession();
|
|
124
127
|
if (this.session && new Date(this.session.expiresAt).getTime() > Date.now() + 3e4) return this.session;
|
|
@@ -211,7 +214,11 @@ var FrankieClient = class {
|
|
|
211
214
|
message: input.message,
|
|
212
215
|
...input.context ? { context: input.context } : {},
|
|
213
216
|
...input.model ? { model: input.model } : {},
|
|
214
|
-
...input.attachments?.length ? { attachments: input.attachments } : {}
|
|
217
|
+
...input.attachments?.length ? { attachments: input.attachments } : {},
|
|
218
|
+
...this.latestUserToken ? { userToken: this.latestUserToken } : {},
|
|
219
|
+
// This widget shows the approval card, so the server may pause writes for it.
|
|
220
|
+
confirmActions: true,
|
|
221
|
+
...input.confirm ? { confirm: input.confirm } : {}
|
|
215
222
|
};
|
|
216
223
|
const response = await this.doFetch(`${this.baseUrl}/v1/chat`, {
|
|
217
224
|
method: "POST",
|
|
@@ -246,7 +253,7 @@ var FrankieClient = class {
|
|
|
246
253
|
await this.postToolResult(session, callId, result).catch(() => void 0);
|
|
247
254
|
}
|
|
248
255
|
async callHostApi(request) {
|
|
249
|
-
const api = this.options.api;
|
|
256
|
+
const api = this.options.api ?? (typeof window !== "undefined" ? { baseUrl: window.location.origin } : void 0);
|
|
250
257
|
if (!api) {
|
|
251
258
|
return {
|
|
252
259
|
status: 0,
|
|
@@ -353,6 +360,10 @@ function luminance([r, g, b]) {
|
|
|
353
360
|
};
|
|
354
361
|
return 0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b);
|
|
355
362
|
}
|
|
363
|
+
function colorful([r, g, b]) {
|
|
364
|
+
const max = Math.max(r, g, b), min = Math.min(r, g, b);
|
|
365
|
+
return max - min >= 40;
|
|
366
|
+
}
|
|
356
367
|
function contrastRatio(a, b) {
|
|
357
368
|
const la = luminance(a), lb = luminance(b);
|
|
358
369
|
return (Math.max(la, lb) + 0.05) / (Math.min(la, lb) + 0.05);
|
|
@@ -461,7 +472,7 @@ function inferHostTheme() {
|
|
|
461
472
|
break;
|
|
462
473
|
}
|
|
463
474
|
const text = parseRgb(style.color);
|
|
464
|
-
if (text && text[3] > 0.5 && !sameColor(text, foreground)) {
|
|
475
|
+
if (text && text[3] > 0.5 && !sameColor(text, foreground) && colorful(text)) {
|
|
465
476
|
accent = text;
|
|
466
477
|
break;
|
|
467
478
|
}
|
|
@@ -539,6 +550,11 @@ function applyEvent(message, event) {
|
|
|
539
550
|
} : tool
|
|
540
551
|
)
|
|
541
552
|
};
|
|
553
|
+
case "action_confirm":
|
|
554
|
+
return {
|
|
555
|
+
...message,
|
|
556
|
+
action: { toolUseId: event.toolUseId, name: event.name, label: event.label, preview: event.preview, state: "pending" }
|
|
557
|
+
};
|
|
542
558
|
case "tool_denied":
|
|
543
559
|
return {
|
|
544
560
|
...message,
|
|
@@ -758,6 +774,7 @@ function useFrankieChatState() {
|
|
|
758
774
|
...context ? { context } : {},
|
|
759
775
|
...opts?.model ? { model: opts.model } : {},
|
|
760
776
|
...attachments.length ? { attachments } : {},
|
|
777
|
+
...opts?.confirm ? { confirm: opts.confirm } : {},
|
|
761
778
|
signal: abort.signal
|
|
762
779
|
},
|
|
763
780
|
(event) => {
|
|
@@ -828,6 +845,21 @@ function useFrankieChatState() {
|
|
|
828
845
|
},
|
|
829
846
|
[busy, client, context, patchAssistant, restored, followRunningTurn]
|
|
830
847
|
);
|
|
848
|
+
const answerAction = useCallback(
|
|
849
|
+
async (toolUseId, approved) => {
|
|
850
|
+
if (abortRef.current) return;
|
|
851
|
+
const holder = messagesRef.current.find(
|
|
852
|
+
(message) => message.role === "assistant" && message.action?.toolUseId === toolUseId && message.action.state === "pending"
|
|
853
|
+
);
|
|
854
|
+
if (!holder) return;
|
|
855
|
+
patchAssistant(
|
|
856
|
+
holder.id,
|
|
857
|
+
(message) => message.action ? { ...message, action: { ...message.action, state: approved ? "approved" : "cancelled" } } : message
|
|
858
|
+
);
|
|
859
|
+
await send(approved ? "Go ahead" : "Cancel", { confirm: { toolUseId, approved } });
|
|
860
|
+
},
|
|
861
|
+
[patchAssistant, send]
|
|
862
|
+
);
|
|
831
863
|
const stop = useCallback(() => {
|
|
832
864
|
if (!abortRef.current) return;
|
|
833
865
|
void client.stopTurn();
|
|
@@ -848,7 +880,7 @@ function useFrankieChatState() {
|
|
|
848
880
|
} catch {
|
|
849
881
|
}
|
|
850
882
|
}, [client]);
|
|
851
|
-
return { messages, busy, loadedSkills, activeApis, restoring: !restored, ...error ? { error } : {}, send, stop, reset };
|
|
883
|
+
return { messages, busy, loadedSkills, activeApis, restoring: !restored, ...error ? { error } : {}, send, answerAction, stop, reset };
|
|
852
884
|
}
|
|
853
885
|
function conversationTitle(messages) {
|
|
854
886
|
const first = messages.find((message) => message.role === "user" && message.text.trim());
|
|
@@ -865,6 +897,7 @@ function stillWorking(messages) {
|
|
|
865
897
|
// src/hooks/FrankieProvider.tsx
|
|
866
898
|
import { jsx } from "react/jsx-runtime";
|
|
867
899
|
var useIsomorphicLayoutEffect = typeof window === "undefined" ? useEffect2 : useLayoutEffect;
|
|
900
|
+
var THEME_WAIT_MS = 5e3;
|
|
868
901
|
var FrankieContext = createContext2(null);
|
|
869
902
|
function FrankieProvider({
|
|
870
903
|
children,
|
|
@@ -918,6 +951,13 @@ function FrankieProvider({
|
|
|
918
951
|
setHostSampled(true);
|
|
919
952
|
return watchHostTheme(sample);
|
|
920
953
|
}, [inheriting]);
|
|
954
|
+
const themeKnown = Boolean(config || configError) && (!inheriting || hostSampled);
|
|
955
|
+
const [waitedForTheme, setWaitedForTheme] = useState2(false);
|
|
956
|
+
useEffect2(() => {
|
|
957
|
+
if (themeKnown) return;
|
|
958
|
+
const timer = setTimeout(() => setWaitedForTheme(true), THEME_WAIT_MS);
|
|
959
|
+
return () => clearTimeout(timer);
|
|
960
|
+
}, [themeKnown]);
|
|
921
961
|
const value = useMemo(
|
|
922
962
|
() => ({
|
|
923
963
|
client,
|
|
@@ -928,9 +968,9 @@ function FrankieProvider({
|
|
|
928
968
|
...theme ? { theme } : {},
|
|
929
969
|
injectStyles,
|
|
930
970
|
hostTheme,
|
|
931
|
-
themeReady:
|
|
971
|
+
themeReady: themeKnown || waitedForTheme
|
|
932
972
|
}),
|
|
933
|
-
[client, config, configError, loading, context, theme, injectStyles, hostTheme,
|
|
973
|
+
[client, config, configError, loading, context, theme, injectStyles, hostTheme, themeKnown, waitedForTheme]
|
|
934
974
|
);
|
|
935
975
|
return /* @__PURE__ */ jsx(FrankieContext.Provider, { value, children: /* @__PURE__ */ jsx(ConversationState, { children }, client.namespace + ":" + (client.conversationId ?? "")) });
|
|
936
976
|
}
|
|
@@ -954,7 +994,7 @@ function useFrankieTheme() {
|
|
|
954
994
|
}
|
|
955
995
|
|
|
956
996
|
// src/components/chat/FrankieWidget.tsx
|
|
957
|
-
import { MessageCircle, X as
|
|
997
|
+
import { MessageCircle, X as X5 } from "lucide-react";
|
|
958
998
|
import { useEffect as useEffect6, useRef as useRef5, useState as useState7 } from "react";
|
|
959
999
|
|
|
960
1000
|
// src/lib/styles-ready.ts
|
|
@@ -962,7 +1002,7 @@ import { useEffect as useEffect3, useLayoutEffect as useLayoutEffect2, useState
|
|
|
962
1002
|
|
|
963
1003
|
// src/generated/css-content.ts
|
|
964
1004
|
var FRANKIE_CSS_CONTENT = `/*! tailwindcss v4.3.3 | MIT License | https://tailwindcss.com */
|
|
965
|
-
@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}}`;
|
|
1005
|
+
@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}}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden])){flex-wrap:wrap;align-items:center;row-gap:8px}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>textarea{flex:1 0 100%;order:-1;width:100%}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>.frankie-composer-actions{flex:auto}.frankie-root:not(.frankie-chat-panel-fill) .frankie-composer-controls:has(.frankie-composer-actions>:not(input[hidden]))>button{margin-left:auto}.frankie-root .frankie-action{border:1px solid var(--frankie-border);border-radius:var(--frankie-radius);background:var(--frankie-background);color:var(--frankie-foreground);flex-direction:column;gap:10px;margin-top:12px;padding:12px 14px;font-size:14px;line-height:1.4;display:flex}.frankie-root .frankie-action.is-pending{border-color:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action.is-pending{border-color:color-mix(in srgb, var(--frankie-primary) 45%, var(--frankie-border))}}.frankie-root .frankie-action.is-pending{background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action.is-pending{background:color-mix(in srgb, var(--frankie-primary) 5%, var(--frankie-background))}}.frankie-root .frankie-action-head{align-items:flex-start;gap:10px;display:flex}.frankie-root .frankie-action-icon{color:var(--frankie-primary);flex-shrink:0;margin-top:2px}.frankie-root .frankie-action-title{flex-direction:column;gap:2px;min-width:0;display:flex}.frankie-root .frankie-action-title strong{overflow-wrap:anywhere;font-weight:600}.frankie-root .frankie-action-title span{color:var(--frankie-muted-foreground);font-size:12px}.frankie-root .frankie-action-route{border-radius:calc(var(--frankie-radius) * .6);background:var(--frankie-muted);overflow-wrap:anywhere;padding:6px 8px;font-size:12px;display:block}.frankie-root .frankie-action-route span{font-weight:600}.frankie-root .frankie-action-fields{gap:6px;margin:0;display:grid}.frankie-root .frankie-action-fields>div{grid-template-columns:minmax(0,35%) minmax(0,1fr);gap:10px;display:grid}.frankie-root .frankie-action-fields dt{color:var(--frankie-muted-foreground);overflow-wrap:anywhere;font-size:12px}.frankie-root .frankie-action-fields dd{white-space:pre-wrap;overflow-wrap:anywhere;max-height:8.5em;margin:0;font-size:13px;overflow:auto}.frankie-root .frankie-action-buttons{flex-wrap:wrap;gap:8px;display:flex}.frankie-root .frankie-action-buttons button{cursor:pointer;font:inherit;border-radius:var(--frankie-radius);border:1px solid var(--frankie-border);align-items:center;gap:6px;padding:7px 14px;font-size:14px;font-weight:500;transition:background-color .15s,border-color .15s;display:inline-flex}.frankie-root .frankie-action-approve{background:var(--frankie-primary);color:var(--frankie-primary-foreground);border-color:var(--frankie-primary)}.frankie-root .frankie-action-approve:hover:not(:disabled){background:var(--frankie-primary)}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action-approve:hover:not(:disabled){background:color-mix(in srgb, var(--frankie-primary) 88%, black)}}.frankie-root .frankie-action-cancel{background:var(--frankie-background);color:var(--frankie-foreground)}.frankie-root .frankie-action-cancel:hover:not(:disabled){background:var(--frankie-muted)}.frankie-root .frankie-action-buttons button:disabled{opacity:.55;cursor:not-allowed}.frankie-root .frankie-action-buttons button:focus-visible{box-shadow:0 0 0 2px var(--frankie-primary);outline:none}@supports (color:color-mix(in lab, red, red)){.frankie-root .frankie-action-buttons button:focus-visible{box-shadow:0 0 0 2px color-mix(in srgb, var(--frankie-primary) 40%, transparent)}}@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}}`;
|
|
966
1006
|
|
|
967
1007
|
// src/lib/ensure-frankie-styles.ts
|
|
968
1008
|
var STYLE_ELEMENT_ID = "frankie-fallback-styles";
|
|
@@ -1036,8 +1076,8 @@ function bottomObstruction(root, position, offset) {
|
|
|
1036
1076
|
seen.add(bar);
|
|
1037
1077
|
const rect = bar.getBoundingClientRect();
|
|
1038
1078
|
const anchoredToBottom = rect.bottom >= height - 4;
|
|
1039
|
-
const
|
|
1040
|
-
if (anchoredToBottom &&
|
|
1079
|
+
const short2 = rect.height > 0 && rect.height < height * 0.4;
|
|
1080
|
+
if (anchoredToBottom && short2) lift = Math.max(lift, height - rect.top);
|
|
1041
1081
|
}
|
|
1042
1082
|
}
|
|
1043
1083
|
return Math.round(lift);
|
|
@@ -1123,7 +1163,7 @@ function Button({ className, variant, size, asChild = false, ...props }) {
|
|
|
1123
1163
|
}
|
|
1124
1164
|
|
|
1125
1165
|
// src/components/chat/ChatPanel.tsx
|
|
1126
|
-
import { RotateCcw, X as
|
|
1166
|
+
import { RotateCcw, X as X4 } from "lucide-react";
|
|
1127
1167
|
import { useEffect as useEffect5, useRef as useRef4, useState as useState6 } from "react";
|
|
1128
1168
|
|
|
1129
1169
|
// src/components/ui/avatar.tsx
|
|
@@ -1499,7 +1539,7 @@ function readAsBase64(file) {
|
|
|
1499
1539
|
}
|
|
1500
1540
|
|
|
1501
1541
|
// src/components/chat/MessageBubble.tsx
|
|
1502
|
-
import { Check as
|
|
1542
|
+
import { Check as Check3, Copy, Paperclip as Paperclip2 } from "lucide-react";
|
|
1503
1543
|
import { memo as memo2, useState as useState5 } from "react";
|
|
1504
1544
|
|
|
1505
1545
|
// src/lib/choices.ts
|
|
@@ -1520,9 +1560,68 @@ function parseOptionLines(block) {
|
|
|
1520
1560
|
return block.split("\n").map((line) => line.trim().replace(/^([-*+]|\d+[.)])\s+/, "").trim()).filter(Boolean);
|
|
1521
1561
|
}
|
|
1522
1562
|
|
|
1563
|
+
// src/components/chat/ActionConfirm.tsx
|
|
1564
|
+
import { Check, ShieldAlert, X as X2 } from "lucide-react";
|
|
1565
|
+
import { jsx as jsx10, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
1566
|
+
function ActionConfirm({
|
|
1567
|
+
action,
|
|
1568
|
+
onAnswer,
|
|
1569
|
+
disabled,
|
|
1570
|
+
live
|
|
1571
|
+
}) {
|
|
1572
|
+
const pending = action.state === "pending";
|
|
1573
|
+
const fields = inputFields(action.preview.input);
|
|
1574
|
+
const status = pending ? live ? void 0 : "Not run" : action.state === "approved" ? "Approved" : "Cancelled";
|
|
1575
|
+
return /* @__PURE__ */ jsxs4("div", { className: "frankie-action" + (pending && live ? " is-pending" : ""), role: "group", "aria-label": `Approve: ${action.label}`, children: [
|
|
1576
|
+
/* @__PURE__ */ jsxs4("div", { className: "frankie-action-head", children: [
|
|
1577
|
+
/* @__PURE__ */ jsx10(ShieldAlert, { className: "frankie-action-icon size-4", "aria-hidden": true }),
|
|
1578
|
+
/* @__PURE__ */ jsxs4("div", { className: "frankie-action-title", children: [
|
|
1579
|
+
/* @__PURE__ */ jsx10("strong", { children: action.label }),
|
|
1580
|
+
/* @__PURE__ */ jsx10("span", { children: pending && live ? "Needs your approval before it runs" : status })
|
|
1581
|
+
] })
|
|
1582
|
+
] }),
|
|
1583
|
+
action.preview.method && action.preview.path ? /* @__PURE__ */ jsxs4("code", { className: "frankie-action-route", children: [
|
|
1584
|
+
/* @__PURE__ */ jsx10("span", { children: action.preview.method }),
|
|
1585
|
+
" ",
|
|
1586
|
+
action.preview.path
|
|
1587
|
+
] }) : null,
|
|
1588
|
+
fields.length ? /* @__PURE__ */ jsx10("dl", { className: "frankie-action-fields", children: fields.map(([key, value]) => /* @__PURE__ */ jsxs4("div", { children: [
|
|
1589
|
+
/* @__PURE__ */ jsx10("dt", { children: key }),
|
|
1590
|
+
/* @__PURE__ */ jsx10("dd", { children: value })
|
|
1591
|
+
] }, key)) }) : null,
|
|
1592
|
+
pending && live && onAnswer ? /* @__PURE__ */ jsxs4("div", { className: "frankie-action-buttons", children: [
|
|
1593
|
+
/* @__PURE__ */ jsxs4("button", { type: "button", className: "frankie-action-approve", disabled, onClick: () => onAnswer(action.toolUseId, true), children: [
|
|
1594
|
+
/* @__PURE__ */ jsx10(Check, { className: "size-4", "aria-hidden": true }),
|
|
1595
|
+
" Approve"
|
|
1596
|
+
] }),
|
|
1597
|
+
/* @__PURE__ */ jsxs4("button", { type: "button", className: "frankie-action-cancel", disabled, onClick: () => onAnswer(action.toolUseId, false), children: [
|
|
1598
|
+
/* @__PURE__ */ jsx10(X2, { className: "size-4", "aria-hidden": true }),
|
|
1599
|
+
" Cancel"
|
|
1600
|
+
] })
|
|
1601
|
+
] }) : null
|
|
1602
|
+
] });
|
|
1603
|
+
}
|
|
1604
|
+
function inputFields(input) {
|
|
1605
|
+
if (input === void 0 || input === null) return [];
|
|
1606
|
+
if (typeof input !== "object" || Array.isArray(input)) return [["input", short(input)]];
|
|
1607
|
+
const rows = [];
|
|
1608
|
+
for (const [key, value] of Object.entries(input)) {
|
|
1609
|
+
if (value === void 0) continue;
|
|
1610
|
+
if (key === "body" && value && typeof value === "object" && !Array.isArray(value)) {
|
|
1611
|
+
for (const [field, inner] of Object.entries(value)) rows.push([field, short(inner)]);
|
|
1612
|
+
continue;
|
|
1613
|
+
}
|
|
1614
|
+
rows.push([key, short(value)]);
|
|
1615
|
+
}
|
|
1616
|
+
return rows;
|
|
1617
|
+
}
|
|
1618
|
+
function short(value) {
|
|
1619
|
+
return typeof value === "string" ? value : JSON.stringify(value, null, 1);
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1523
1622
|
// src/components/chat/Choices.tsx
|
|
1524
1623
|
import { ArrowRight, PencilLine } from "lucide-react";
|
|
1525
|
-
import { jsx as
|
|
1624
|
+
import { jsx as jsx11, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1526
1625
|
function Choices({
|
|
1527
1626
|
options,
|
|
1528
1627
|
onPick,
|
|
@@ -1530,8 +1629,8 @@ function Choices({
|
|
|
1530
1629
|
disabled
|
|
1531
1630
|
}) {
|
|
1532
1631
|
if (options.length === 0) return null;
|
|
1533
|
-
return /* @__PURE__ */
|
|
1534
|
-
options.map((option, index) => /* @__PURE__ */
|
|
1632
|
+
return /* @__PURE__ */ jsxs5("div", { className: "frankie-choices", role: "group", "aria-label": "Choose an option", children: [
|
|
1633
|
+
options.map((option, index) => /* @__PURE__ */ jsxs5(
|
|
1535
1634
|
"button",
|
|
1536
1635
|
{
|
|
1537
1636
|
type: "button",
|
|
@@ -1539,14 +1638,14 @@ function Choices({
|
|
|
1539
1638
|
disabled,
|
|
1540
1639
|
onClick: () => onPick(option),
|
|
1541
1640
|
children: [
|
|
1542
|
-
/* @__PURE__ */
|
|
1543
|
-
/* @__PURE__ */
|
|
1544
|
-
/* @__PURE__ */
|
|
1641
|
+
/* @__PURE__ */ jsx11("span", { className: "frankie-choice-index", "aria-hidden": true, children: index + 1 }),
|
|
1642
|
+
/* @__PURE__ */ jsx11("span", { className: "frankie-choice-label", children: option }),
|
|
1643
|
+
/* @__PURE__ */ jsx11(ArrowRight, { className: "frankie-choice-go size-4", "aria-hidden": true })
|
|
1545
1644
|
]
|
|
1546
1645
|
},
|
|
1547
1646
|
`${index}-${option}`
|
|
1548
1647
|
)),
|
|
1549
|
-
onSomethingElse ? /* @__PURE__ */
|
|
1648
|
+
onSomethingElse ? /* @__PURE__ */ jsxs5(
|
|
1550
1649
|
"button",
|
|
1551
1650
|
{
|
|
1552
1651
|
type: "button",
|
|
@@ -1554,9 +1653,9 @@ function Choices({
|
|
|
1554
1653
|
disabled,
|
|
1555
1654
|
onClick: onSomethingElse,
|
|
1556
1655
|
children: [
|
|
1557
|
-
/* @__PURE__ */
|
|
1558
|
-
/* @__PURE__ */
|
|
1559
|
-
/* @__PURE__ */
|
|
1656
|
+
/* @__PURE__ */ jsx11("span", { className: "frankie-choice-index", "aria-hidden": true, children: /* @__PURE__ */ jsx11(PencilLine, { className: "size-3.5" }) }),
|
|
1657
|
+
/* @__PURE__ */ jsx11("span", { className: "frankie-choice-label", children: "Something else\u2026" }),
|
|
1658
|
+
/* @__PURE__ */ jsx11("span", { className: "frankie-choice-hint", "aria-hidden": true, children: "Type your own" })
|
|
1560
1659
|
]
|
|
1561
1660
|
}
|
|
1562
1661
|
) : null
|
|
@@ -1565,10 +1664,10 @@ function Choices({
|
|
|
1565
1664
|
|
|
1566
1665
|
// src/components/chat/Markdown.tsx
|
|
1567
1666
|
import { Fragment as Fragment2, memo, useMemo as useMemo2 } from "react";
|
|
1568
|
-
import { jsx as
|
|
1667
|
+
import { jsx as jsx12 } from "react/jsx-runtime";
|
|
1569
1668
|
var Markdown = memo(function Markdown2({ text, className }) {
|
|
1570
1669
|
const blocks = useMemo2(() => renderBlocks(text), [text]);
|
|
1571
|
-
return /* @__PURE__ */
|
|
1670
|
+
return /* @__PURE__ */ jsx12("div", { className: cn("frankie-prose", className), children: blocks });
|
|
1572
1671
|
});
|
|
1573
1672
|
function renderBlocks(text) {
|
|
1574
1673
|
const lines = text.split("\n");
|
|
@@ -1578,13 +1677,13 @@ function renderBlocks(text) {
|
|
|
1578
1677
|
let code = null;
|
|
1579
1678
|
const flushParagraph = () => {
|
|
1580
1679
|
if (paragraph.length === 0) return;
|
|
1581
|
-
blocks.push(/* @__PURE__ */
|
|
1680
|
+
blocks.push(/* @__PURE__ */ jsx12("p", { children: renderInline(paragraph.join(" ")) }, `p${blocks.length}`));
|
|
1582
1681
|
paragraph = [];
|
|
1583
1682
|
};
|
|
1584
1683
|
const flushList = () => {
|
|
1585
1684
|
if (!list) return;
|
|
1586
|
-
const items = list.items.map((item, index) => /* @__PURE__ */
|
|
1587
|
-
blocks.push(list.ordered ? /* @__PURE__ */
|
|
1685
|
+
const items = list.items.map((item, index) => /* @__PURE__ */ jsx12("li", { children: renderInline(item) }, index));
|
|
1686
|
+
blocks.push(list.ordered ? /* @__PURE__ */ jsx12("ol", { children: items }, `l${blocks.length}`) : /* @__PURE__ */ jsx12("ul", { children: items }, `l${blocks.length}`));
|
|
1588
1687
|
list = null;
|
|
1589
1688
|
};
|
|
1590
1689
|
for (const line of lines) {
|
|
@@ -1592,7 +1691,7 @@ function renderBlocks(text) {
|
|
|
1592
1691
|
if (fence) {
|
|
1593
1692
|
if (code) {
|
|
1594
1693
|
blocks.push(
|
|
1595
|
-
/* @__PURE__ */
|
|
1694
|
+
/* @__PURE__ */ jsx12("pre", { children: /* @__PURE__ */ jsx12("code", { children: code.lines.join("\n") }) }, `c${blocks.length}`)
|
|
1596
1695
|
);
|
|
1597
1696
|
code = null;
|
|
1598
1697
|
} else {
|
|
@@ -1629,7 +1728,7 @@ function renderBlocks(text) {
|
|
|
1629
1728
|
flushList();
|
|
1630
1729
|
const level = (heading[1] ?? "#").length;
|
|
1631
1730
|
const Tag = level <= 1 ? "h3" : level === 2 ? "h4" : "h5";
|
|
1632
|
-
blocks.push(/* @__PURE__ */
|
|
1731
|
+
blocks.push(/* @__PURE__ */ jsx12(Tag, { className: "frankie-h", children: renderInline(heading[2] ?? "") }, `h${blocks.length}`));
|
|
1633
1732
|
continue;
|
|
1634
1733
|
}
|
|
1635
1734
|
flushList();
|
|
@@ -1637,7 +1736,7 @@ function renderBlocks(text) {
|
|
|
1637
1736
|
}
|
|
1638
1737
|
if (code) {
|
|
1639
1738
|
blocks.push(
|
|
1640
|
-
/* @__PURE__ */
|
|
1739
|
+
/* @__PURE__ */ jsx12("pre", { children: /* @__PURE__ */ jsx12("code", { children: code.lines.join("\n") }) }, `c${blocks.length}`)
|
|
1641
1740
|
);
|
|
1642
1741
|
}
|
|
1643
1742
|
flushParagraph();
|
|
@@ -1652,30 +1751,30 @@ function renderInline(text) {
|
|
|
1652
1751
|
const token = match[0];
|
|
1653
1752
|
const start = match.index;
|
|
1654
1753
|
if (start > lastIndex) nodes.push(text.slice(lastIndex, start));
|
|
1655
|
-
nodes.push(/* @__PURE__ */
|
|
1754
|
+
nodes.push(/* @__PURE__ */ jsx12(Fragment2, { children: renderToken(token) }, start));
|
|
1656
1755
|
lastIndex = start + token.length;
|
|
1657
1756
|
}
|
|
1658
1757
|
if (lastIndex < text.length) nodes.push(text.slice(lastIndex));
|
|
1659
1758
|
return nodes;
|
|
1660
1759
|
}
|
|
1661
1760
|
function renderToken(token) {
|
|
1662
|
-
if (token.startsWith("**")) return /* @__PURE__ */
|
|
1663
|
-
if (token.startsWith("*")) return /* @__PURE__ */
|
|
1664
|
-
if (token.startsWith("`")) return /* @__PURE__ */
|
|
1761
|
+
if (token.startsWith("**")) return /* @__PURE__ */ jsx12("strong", { children: token.slice(2, -2) });
|
|
1762
|
+
if (token.startsWith("*")) return /* @__PURE__ */ jsx12("em", { children: token.slice(1, -1) });
|
|
1763
|
+
if (token.startsWith("`")) return /* @__PURE__ */ jsx12("code", { children: token.slice(1, -1) });
|
|
1665
1764
|
const link = /^\[([^\]]+)\]\(([^)]+)\)$/.exec(token);
|
|
1666
1765
|
const href = link ? link[2] ?? "" : token;
|
|
1667
1766
|
const label = link ? link[1] ?? "" : token;
|
|
1668
|
-
if (!/^(https?:\/\/|\/)/.test(href)) return token;
|
|
1669
|
-
return /* @__PURE__ */
|
|
1767
|
+
if (!/^(https?:\/\/|\/(?![\/\\]))/.test(href)) return token;
|
|
1768
|
+
return /* @__PURE__ */ jsx12("a", { href, target: "_blank", rel: "noopener noreferrer nofollow", children: label });
|
|
1670
1769
|
}
|
|
1671
1770
|
|
|
1672
1771
|
// src/components/chat/ToolActivityList.tsx
|
|
1673
|
-
import { Check, ChevronDown, Loader2, ShieldAlert, Sparkles as Sparkles3, Wrench, X as
|
|
1772
|
+
import { Check as Check2, ChevronDown, Loader2, ShieldAlert as ShieldAlert2, Sparkles as Sparkles3, Wrench, X as X3 } from "lucide-react";
|
|
1674
1773
|
import { SKILL_TOOL_NAME } from "@frankieai/core";
|
|
1675
1774
|
|
|
1676
1775
|
// src/components/ui/badge.tsx
|
|
1677
1776
|
import { cva as cva2 } from "class-variance-authority";
|
|
1678
|
-
import { jsx as
|
|
1777
|
+
import { jsx as jsx13 } from "react/jsx-runtime";
|
|
1679
1778
|
var badgeVariants = cva2(
|
|
1680
1779
|
"inline-flex items-center gap-1 rounded-full border px-2 py-0.5 text-[0.6875rem] font-medium",
|
|
1681
1780
|
{
|
|
@@ -1695,46 +1794,46 @@ function Badge({
|
|
|
1695
1794
|
variant,
|
|
1696
1795
|
...props
|
|
1697
1796
|
}) {
|
|
1698
|
-
return /* @__PURE__ */
|
|
1797
|
+
return /* @__PURE__ */ jsx13("span", { className: cn(badgeVariants({ variant }), className), ...props });
|
|
1699
1798
|
}
|
|
1700
1799
|
|
|
1701
1800
|
// src/components/chat/ToolActivityList.tsx
|
|
1702
|
-
import { jsx as
|
|
1801
|
+
import { jsx as jsx14, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1703
1802
|
function ToolActivityList({ tools, renderTool }) {
|
|
1704
1803
|
if (tools.length === 0) return null;
|
|
1705
1804
|
const running = tools.some((tool) => tool.status === "running");
|
|
1706
|
-
return /* @__PURE__ */
|
|
1707
|
-
/* @__PURE__ */
|
|
1708
|
-
/* @__PURE__ */
|
|
1709
|
-
running ? /* @__PURE__ */
|
|
1710
|
-
/* @__PURE__ */
|
|
1711
|
-
/* @__PURE__ */
|
|
1805
|
+
return /* @__PURE__ */ jsxs6("details", { className: "frankie-tool-activity mt-2", open: running, children: [
|
|
1806
|
+
/* @__PURE__ */ jsxs6("summary", { className: "inline-flex min-h-7 cursor-pointer list-none items-center gap-1.5 rounded-md px-1 text-xs font-medium text-muted-foreground hover:bg-muted hover:text-foreground [&::-webkit-details-marker]:hidden", children: [
|
|
1807
|
+
/* @__PURE__ */ jsx14(ChevronDown, { className: "frankie-tool-chevron size-3.5 transition-transform", "aria-hidden": "true" }),
|
|
1808
|
+
running ? /* @__PURE__ */ jsx14(Loader2, { className: "size-3.5 animate-spin", "aria-hidden": "true" }) : /* @__PURE__ */ jsx14(Wrench, { className: "size-3.5", "aria-hidden": "true" }),
|
|
1809
|
+
/* @__PURE__ */ jsx14("span", { children: running ? "Working" : "Activity" }),
|
|
1810
|
+
/* @__PURE__ */ jsxs6("span", { className: "text-[11px]", children: [
|
|
1712
1811
|
"\xB7 ",
|
|
1713
1812
|
tools.length,
|
|
1714
1813
|
" tool call",
|
|
1715
1814
|
tools.length === 1 ? "" : "s"
|
|
1716
1815
|
] })
|
|
1717
1816
|
] }),
|
|
1718
|
-
/* @__PURE__ */
|
|
1817
|
+
/* @__PURE__ */ jsx14("ul", { className: "mt-1 flex flex-col gap-1 border-l border-border pl-2", children: tools.map((tool) => /* @__PURE__ */ jsx14("li", { children: renderTool ? renderTool(tool) : /* @__PURE__ */ jsx14(ToolActivityRow, { tool }) }, tool.id)) })
|
|
1719
1818
|
] });
|
|
1720
1819
|
}
|
|
1721
1820
|
function ToolActivityRow({ tool }) {
|
|
1722
1821
|
const isSkill = tool.name === SKILL_TOOL_NAME;
|
|
1723
|
-
return /* @__PURE__ */
|
|
1724
|
-
isSkill && tool.status !== "running" ? /* @__PURE__ */
|
|
1725
|
-
/* @__PURE__ */
|
|
1822
|
+
return /* @__PURE__ */ jsxs6(Badge, { variant: badgeVariant(tool.status), title: tool.detail ?? tool.name, children: [
|
|
1823
|
+
isSkill && tool.status !== "running" ? /* @__PURE__ */ jsx14(Sparkles3, { className: "size-3", "aria-hidden": "true" }) : /* @__PURE__ */ jsx14(ToolIcon, { status: tool.status }),
|
|
1824
|
+
/* @__PURE__ */ jsx14("span", { className: "truncate", children: tool.label })
|
|
1726
1825
|
] });
|
|
1727
1826
|
}
|
|
1728
1827
|
function ToolIcon({ status }) {
|
|
1729
1828
|
switch (status) {
|
|
1730
1829
|
case "running":
|
|
1731
|
-
return /* @__PURE__ */
|
|
1830
|
+
return /* @__PURE__ */ jsx14(Loader2, { className: "size-3 animate-spin", "aria-hidden": "true" });
|
|
1732
1831
|
case "done":
|
|
1733
|
-
return /* @__PURE__ */
|
|
1832
|
+
return /* @__PURE__ */ jsx14(Check2, { className: "size-3", "aria-hidden": "true" });
|
|
1734
1833
|
case "denied":
|
|
1735
|
-
return /* @__PURE__ */
|
|
1834
|
+
return /* @__PURE__ */ jsx14(ShieldAlert2, { className: "size-3", "aria-hidden": "true" });
|
|
1736
1835
|
default:
|
|
1737
|
-
return /* @__PURE__ */
|
|
1836
|
+
return /* @__PURE__ */ jsx14(X3, { className: "size-3", "aria-hidden": "true" });
|
|
1738
1837
|
}
|
|
1739
1838
|
}
|
|
1740
1839
|
function badgeVariant(status) {
|
|
@@ -1745,7 +1844,7 @@ function badgeVariant(status) {
|
|
|
1745
1844
|
}
|
|
1746
1845
|
|
|
1747
1846
|
// src/components/chat/MessageBubble.tsx
|
|
1748
|
-
import { jsx as
|
|
1847
|
+
import { jsx as jsx15, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1749
1848
|
var MessageBubble = memo2(function MessageBubble2({
|
|
1750
1849
|
message,
|
|
1751
1850
|
assistantName,
|
|
@@ -1755,40 +1854,50 @@ var MessageBubble = memo2(function MessageBubble2({
|
|
|
1755
1854
|
onChoose,
|
|
1756
1855
|
onRequestInput,
|
|
1757
1856
|
active,
|
|
1758
|
-
busy
|
|
1857
|
+
busy,
|
|
1858
|
+
onAction
|
|
1759
1859
|
}) {
|
|
1760
1860
|
if (message.role === "user") {
|
|
1761
|
-
return /* @__PURE__ */
|
|
1762
|
-
message.attachments?.length ? /* @__PURE__ */
|
|
1861
|
+
return /* @__PURE__ */ jsxs7("div", { className: "frankie-user-message flex flex-col items-end gap-1", children: [
|
|
1862
|
+
message.attachments?.length ? /* @__PURE__ */ jsx15("div", { className: "flex max-w-[85%] flex-wrap justify-end gap-1", children: message.attachments.map((file, index) => /* @__PURE__ */ jsxs7(
|
|
1763
1863
|
"span",
|
|
1764
1864
|
{
|
|
1765
1865
|
className: "inline-flex max-w-[12rem] items-center gap-1 rounded-[var(--frankie-radius)] border border-border bg-muted px-2 py-1 text-xs",
|
|
1766
1866
|
children: [
|
|
1767
|
-
/* @__PURE__ */
|
|
1768
|
-
/* @__PURE__ */
|
|
1867
|
+
/* @__PURE__ */ jsx15(Paperclip2, { className: "size-3 shrink-0 opacity-70" }),
|
|
1868
|
+
/* @__PURE__ */ jsx15("span", { className: "truncate", children: file.name })
|
|
1769
1869
|
]
|
|
1770
1870
|
},
|
|
1771
1871
|
`${file.name}-${index}`
|
|
1772
1872
|
)) }) : null,
|
|
1773
|
-
message.text ? /* @__PURE__ */
|
|
1774
|
-
/* @__PURE__ */
|
|
1775
|
-
/* @__PURE__ */
|
|
1873
|
+
message.text ? /* @__PURE__ */ jsxs7("div", { className: "flex max-w-[85%] items-end gap-1", children: [
|
|
1874
|
+
/* @__PURE__ */ jsx15("div", { className: "frankie-user-bubble rounded-[var(--frankie-radius)] bg-primary px-3 py-2 text-sm text-primary-foreground", children: message.text }),
|
|
1875
|
+
/* @__PURE__ */ jsx15(MessageCopyButton, { text: message.text })
|
|
1776
1876
|
] }) : null
|
|
1777
1877
|
] });
|
|
1778
1878
|
}
|
|
1779
1879
|
const { body, options } = extractChoices(message.text);
|
|
1780
1880
|
const showChoices = Boolean(active && onChoose && options && message.status === "done");
|
|
1781
1881
|
const waiting = message.status === "streaming" && body.length === 0 && message.tools.length === 0;
|
|
1782
|
-
return /* @__PURE__ */
|
|
1783
|
-
/* @__PURE__ */
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
body ? /* @__PURE__ */
|
|
1882
|
+
return /* @__PURE__ */ jsxs7("div", { className: "frankie-assistant-message flex gap-2", children: [
|
|
1883
|
+
/* @__PURE__ */ jsxs7("div", { className: "frankie-assistant-actions mt-0.5 flex shrink-0 flex-col items-center gap-1", children: [
|
|
1884
|
+
/* @__PURE__ */ jsx15(Avatar, { ...avatarUrl ? { src: avatarUrl } : {}, name: assistantName }),
|
|
1885
|
+
body ? /* @__PURE__ */ jsx15(MessageCopyButton, { text: body }) : null
|
|
1786
1886
|
] }),
|
|
1787
|
-
/* @__PURE__ */
|
|
1788
|
-
message.status === "error" ? /* @__PURE__ */
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1887
|
+
/* @__PURE__ */ jsxs7("div", { className: "frankie-assistant-content min-w-0 max-w-[85%]", children: [
|
|
1888
|
+
message.status === "error" ? /* @__PURE__ */ jsx15("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__ */ jsx15(Markdown, { text: body, className: "text-sm" }) : null,
|
|
1889
|
+
message.action ? /* @__PURE__ */ jsx15(
|
|
1890
|
+
ActionConfirm,
|
|
1891
|
+
{
|
|
1892
|
+
action: message.action,
|
|
1893
|
+
live: Boolean(active),
|
|
1894
|
+
disabled: busy || message.status === "streaming",
|
|
1895
|
+
...onAction ? { onAnswer: onAction } : {}
|
|
1896
|
+
}
|
|
1897
|
+
) : null,
|
|
1898
|
+
showChoices ? /* @__PURE__ */ jsx15(Choices, { options: options ?? [], onPick: onChoose, ...onRequestInput ? { onSomethingElse: onRequestInput } : {}, disabled: busy }) : null,
|
|
1899
|
+
showToolActivity ? /* @__PURE__ */ jsx15(ToolActivityList, { tools: message.tools, ...renderTool ? { renderTool } : {} }) : null,
|
|
1900
|
+
waiting ? /* @__PURE__ */ jsx15(Thinking, { delayed: true }) : null
|
|
1792
1901
|
] })
|
|
1793
1902
|
] });
|
|
1794
1903
|
});
|
|
@@ -1802,12 +1911,12 @@ function MessageCopyButton({ text }) {
|
|
|
1802
1911
|
} catch {
|
|
1803
1912
|
}
|
|
1804
1913
|
};
|
|
1805
|
-
return /* @__PURE__ */
|
|
1914
|
+
return /* @__PURE__ */ jsx15("button", { type: "button", onClick: () => void copy(), className: "frankie-message-copy inline-flex size-8 shrink-0 items-center justify-center rounded-md border border-transparent text-muted-foreground transition hover:border-border hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary", "aria-label": copied ? "Copied" : "Copy message", title: copied ? "Copied" : "Copy message", children: copied ? /* @__PURE__ */ jsx15(Check3, { className: "size-3.5", "aria-hidden": "true" }) : /* @__PURE__ */ jsx15(Copy, { className: "size-3.5", "aria-hidden": "true" }) });
|
|
1806
1915
|
}
|
|
1807
1916
|
function TypingDots() {
|
|
1808
|
-
return /* @__PURE__ */
|
|
1809
|
-
/* @__PURE__ */
|
|
1810
|
-
[0, 1, 2].map((index) => /* @__PURE__ */
|
|
1917
|
+
return /* @__PURE__ */ jsxs7("span", { className: "frankie-thinking inline-flex items-center gap-1 py-2", role: "status", "aria-label": "Thinking", children: [
|
|
1918
|
+
/* @__PURE__ */ jsx15("span", { className: "frankie-thinking-label", children: "Thinking" }),
|
|
1919
|
+
[0, 1, 2].map((index) => /* @__PURE__ */ jsx15(
|
|
1811
1920
|
"span",
|
|
1812
1921
|
{
|
|
1813
1922
|
className: cn("size-1.5 animate-pulse rounded-full bg-muted-foreground"),
|
|
@@ -1818,15 +1927,15 @@ function TypingDots() {
|
|
|
1818
1927
|
] });
|
|
1819
1928
|
}
|
|
1820
1929
|
function Thinking({ delayed = false }) {
|
|
1821
|
-
return /* @__PURE__ */
|
|
1822
|
-
/* @__PURE__ */
|
|
1823
|
-
/* @__PURE__ */
|
|
1824
|
-
/* @__PURE__ */
|
|
1930
|
+
return /* @__PURE__ */ jsxs7("div", { className: "frankie-skeleton" + (delayed ? " is-delayed" : ""), role: "status", "aria-label": "Thinking", children: [
|
|
1931
|
+
/* @__PURE__ */ jsx15("span", { className: "frankie-skeleton-line", style: { width: "62%" } }),
|
|
1932
|
+
/* @__PURE__ */ jsx15("span", { className: "frankie-skeleton-line", style: { width: "92%" } }),
|
|
1933
|
+
/* @__PURE__ */ jsx15("span", { className: "frankie-skeleton-line", style: { width: "48%" } })
|
|
1825
1934
|
] });
|
|
1826
1935
|
}
|
|
1827
1936
|
|
|
1828
1937
|
// src/components/chat/ChatPanel.tsx
|
|
1829
|
-
import { jsx as
|
|
1938
|
+
import { jsx as jsx16, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1830
1939
|
function ChatPanel({
|
|
1831
1940
|
className,
|
|
1832
1941
|
header,
|
|
@@ -1841,7 +1950,7 @@ function ChatPanel({
|
|
|
1841
1950
|
}) {
|
|
1842
1951
|
const { config, configError, loading } = useFrankie();
|
|
1843
1952
|
const theme = useFrankieTheme();
|
|
1844
|
-
const { messages, busy, restoring, error, send, stop, reset } = useFrankieChat();
|
|
1953
|
+
const { messages, busy, restoring, error, send, answerAction, stop, reset } = useFrankieChat();
|
|
1845
1954
|
const scrollRef = useRef4(null);
|
|
1846
1955
|
const atBottomRef = useRef4(true);
|
|
1847
1956
|
const features = config?.features;
|
|
@@ -1870,7 +1979,7 @@ function ChatPanel({
|
|
|
1870
1979
|
}, [messages]);
|
|
1871
1980
|
const name = config?.branding.name ?? "Assistant";
|
|
1872
1981
|
const mode = useResolvedMode(theme?.mode);
|
|
1873
|
-
return /* @__PURE__ */
|
|
1982
|
+
return /* @__PURE__ */ jsxs8(
|
|
1874
1983
|
Card,
|
|
1875
1984
|
{
|
|
1876
1985
|
className: cn(
|
|
@@ -1883,22 +1992,22 @@ function ChatPanel({
|
|
|
1883
1992
|
style: themeToCssVars(theme),
|
|
1884
1993
|
"data-frankie-mode": mode,
|
|
1885
1994
|
children: [
|
|
1886
|
-
header === false ? null : header !== void 0 ? header : /* @__PURE__ */
|
|
1887
|
-
/* @__PURE__ */
|
|
1888
|
-
/* @__PURE__ */
|
|
1889
|
-
/* @__PURE__ */
|
|
1890
|
-
config?.branding.tagline ? /* @__PURE__ */
|
|
1995
|
+
header === false ? null : header !== void 0 ? header : /* @__PURE__ */ jsxs8(CardHeader, { children: [
|
|
1996
|
+
/* @__PURE__ */ jsx16(Avatar, { ...config?.branding.avatarUrl ? { src: config.branding.avatarUrl } : {}, name }),
|
|
1997
|
+
/* @__PURE__ */ jsxs8("div", { className: "min-w-0 flex-1", children: [
|
|
1998
|
+
/* @__PURE__ */ jsx16(CardTitle, { children: name }),
|
|
1999
|
+
config?.branding.tagline ? /* @__PURE__ */ jsx16(CardDescription, { children: config.branding.tagline }) : null
|
|
1891
2000
|
] }),
|
|
1892
|
-
/* @__PURE__ */
|
|
1893
|
-
onClose ? /* @__PURE__ */
|
|
2001
|
+
/* @__PURE__ */ jsx16(Button, { size: "icon", variant: "ghost", onClick: reset, "aria-label": "Start a new conversation", title: "New conversation", children: /* @__PURE__ */ jsx16(RotateCcw, {}) }),
|
|
2002
|
+
onClose ? /* @__PURE__ */ jsx16(Button, { size: "icon", variant: "ghost", onClick: onClose, "aria-label": "Close", children: /* @__PURE__ */ jsx16(X4, {}) }) : null
|
|
1894
2003
|
] }),
|
|
1895
|
-
/* @__PURE__ */
|
|
1896
|
-
/* @__PURE__ */
|
|
1897
|
-
/* @__PURE__ */
|
|
1898
|
-
/* @__PURE__ */
|
|
1899
|
-
/* @__PURE__ */
|
|
1900
|
-
] }) : loading ? /* @__PURE__ */
|
|
1901
|
-
(message, index) => renderMessage ? /* @__PURE__ */
|
|
2004
|
+
config?.agentContext === false ? null : /* @__PURE__ */ jsx16(AgentContext, {}),
|
|
2005
|
+
/* @__PURE__ */ jsx16(CardContent, { className: "frankie-conversation-content", children: /* @__PURE__ */ jsx16(ScrollArea, { ref: scrollRef, className: "frankie-transcript flex h-full flex-col gap-4 p-4", "aria-live": "polite", children: configError ? /* @__PURE__ */ jsxs8("div", { className: "frankie-config-error", role: "alert", children: [
|
|
2006
|
+
/* @__PURE__ */ jsx16("strong", { children: "Frankie couldn\u2019t start" }),
|
|
2007
|
+
/* @__PURE__ */ jsx16("p", { children: configError }),
|
|
2008
|
+
/* @__PURE__ */ jsx16("p", { children: "Refresh this page after checking the workspace\u2019s allowed origin and sign-in settings." })
|
|
2009
|
+
] }) : loading ? /* @__PURE__ */ jsx16("p", { className: "text-sm text-muted-foreground", children: "Starting Frankie\u2026" }) : messages.length === 0 ? emptyState ?? /* @__PURE__ */ jsx16(ChatEmptyState, {}) : /* @__PURE__ */ jsx16("div", { className: "frankie-message-list flex flex-col gap-4", children: messages.map(
|
|
2010
|
+
(message, index) => renderMessage ? /* @__PURE__ */ jsx16("div", { children: renderMessage(message) }, message.id) : /* @__PURE__ */ jsx16(
|
|
1902
2011
|
MessageBubble,
|
|
1903
2012
|
{
|
|
1904
2013
|
message,
|
|
@@ -1909,14 +2018,15 @@ function ChatPanel({
|
|
|
1909
2018
|
onChoose: send,
|
|
1910
2019
|
onRequestInput: focusComposer,
|
|
1911
2020
|
active: index === messages.length - 1,
|
|
1912
|
-
busy
|
|
2021
|
+
busy,
|
|
2022
|
+
onAction: (toolUseId, approved) => void answerAction(toolUseId, approved)
|
|
1913
2023
|
},
|
|
1914
2024
|
message.id
|
|
1915
2025
|
)
|
|
1916
2026
|
) }) }) }),
|
|
1917
|
-
/* @__PURE__ */
|
|
1918
|
-
error ? /* @__PURE__ */
|
|
1919
|
-
/* @__PURE__ */
|
|
2027
|
+
/* @__PURE__ */ jsxs8(CardFooter, { className: "frankie-composer-footer flex flex-col gap-2", children: [
|
|
2028
|
+
error ? /* @__PURE__ */ jsx16("p", { className: "w-full text-xs text-red-600 dark:text-red-400", children: error }) : null,
|
|
2029
|
+
/* @__PURE__ */ jsx16("div", { className: "w-full", children: /* @__PURE__ */ jsx16(
|
|
1920
2030
|
Composer,
|
|
1921
2031
|
{
|
|
1922
2032
|
onSend: send,
|
|
@@ -1938,7 +2048,7 @@ function ChatPanel({
|
|
|
1938
2048
|
}
|
|
1939
2049
|
|
|
1940
2050
|
// src/components/chat/FrankieWidget.tsx
|
|
1941
|
-
import { jsx as
|
|
2051
|
+
import { jsx as jsx17, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1942
2052
|
function FrankieWidget({
|
|
1943
2053
|
position = "bottom-right",
|
|
1944
2054
|
offset = 24,
|
|
@@ -1949,6 +2059,7 @@ function FrankieWidget({
|
|
|
1949
2059
|
renderLauncher,
|
|
1950
2060
|
launcher = true,
|
|
1951
2061
|
avoidBottomBars = true,
|
|
2062
|
+
closeOnOutsideClick = true,
|
|
1952
2063
|
...panelProps
|
|
1953
2064
|
}) {
|
|
1954
2065
|
const { config, injectStyles, themeReady } = useFrankie();
|
|
@@ -1969,6 +2080,17 @@ function FrankieWidget({
|
|
|
1969
2080
|
window.addEventListener("keydown", onKeyDown);
|
|
1970
2081
|
return () => window.removeEventListener("keydown", onKeyDown);
|
|
1971
2082
|
}, [open]);
|
|
2083
|
+
useEffect6(() => {
|
|
2084
|
+
if (!open || !closeOnOutsideClick) return;
|
|
2085
|
+
const onPointerDown = (event) => {
|
|
2086
|
+
const target = event.target;
|
|
2087
|
+
if (!(target instanceof Node) || !target.isConnected || rootRef.current?.contains(target)) return;
|
|
2088
|
+
if (target instanceof Element && target.closest("[data-frankie-open], [data-frankie-keep-open]")) return;
|
|
2089
|
+
setOpen(false);
|
|
2090
|
+
};
|
|
2091
|
+
document.addEventListener("pointerdown", onPointerDown, true);
|
|
2092
|
+
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
2093
|
+
}, [open, closeOnOutsideClick]);
|
|
1972
2094
|
useEffect6(() => {
|
|
1973
2095
|
const onClick = (event) => {
|
|
1974
2096
|
const target = event.target instanceof Element ? event.target.closest("[data-frankie-open]") : null;
|
|
@@ -2012,7 +2134,7 @@ function FrankieWidget({
|
|
|
2012
2134
|
const label = config?.branding.launcherLabel ?? "Need a hand?";
|
|
2013
2135
|
const mode = useResolvedMode(theme?.mode);
|
|
2014
2136
|
const side = position === "bottom-right" ? { right: offset } : { left: offset };
|
|
2015
|
-
return /* @__PURE__ */
|
|
2137
|
+
return /* @__PURE__ */ jsx17(
|
|
2016
2138
|
"div",
|
|
2017
2139
|
{
|
|
2018
2140
|
ref: rootRef,
|
|
@@ -2020,9 +2142,9 @@ function FrankieWidget({
|
|
|
2020
2142
|
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 } },
|
|
2021
2143
|
"data-frankie-mode": mode,
|
|
2022
2144
|
"data-frankie-styles": stylesReady ? "ready" : "pending",
|
|
2023
|
-
children: /* @__PURE__ */
|
|
2024
|
-
open ? /* @__PURE__ */
|
|
2025
|
-
launcher || renderLauncher ? /* @__PURE__ */
|
|
2145
|
+
children: /* @__PURE__ */ jsxs9("div", { className: cn("flex flex-col gap-3", position === "bottom-left" ? "items-start" : "items-end"), children: [
|
|
2146
|
+
open ? /* @__PURE__ */ jsx17("div", { style: { pointerEvents: "auto" }, children: /* @__PURE__ */ jsx17(ChatPanel, { ...panelProps, className: cn("frankie-widget-panel", panelProps.className), onClose: () => setOpen(false), autoFocus: true }) }) : null,
|
|
2147
|
+
launcher || renderLauncher ? /* @__PURE__ */ jsx17("div", { style: { pointerEvents: "auto" }, children: renderLauncher ? renderLauncher({ open, toggle: () => setOpen(!open), label }) : /* @__PURE__ */ jsx17(
|
|
2026
2148
|
Button,
|
|
2027
2149
|
{
|
|
2028
2150
|
size: "icon",
|
|
@@ -2031,7 +2153,7 @@ function FrankieWidget({
|
|
|
2031
2153
|
"aria-expanded": open,
|
|
2032
2154
|
"aria-label": open ? "Close the assistant" : label,
|
|
2033
2155
|
title: label,
|
|
2034
|
-
children: open ? /* @__PURE__ */
|
|
2156
|
+
children: open ? /* @__PURE__ */ jsx17(X5, { className: "size-5" }) : /* @__PURE__ */ jsx17(MessageCircle, { className: "size-5" })
|
|
2035
2157
|
}
|
|
2036
2158
|
) }) : null
|
|
2037
2159
|
] })
|
|
@@ -2041,14 +2163,14 @@ function FrankieWidget({
|
|
|
2041
2163
|
|
|
2042
2164
|
// src/components/chat/FrankieInline.tsx
|
|
2043
2165
|
import { useRef as useRef6 } from "react";
|
|
2044
|
-
import { jsx as
|
|
2166
|
+
import { jsx as jsx18 } from "react/jsx-runtime";
|
|
2045
2167
|
function FrankieInline({ className, style, ...panelProps }) {
|
|
2046
2168
|
const { injectStyles, themeReady } = useFrankie();
|
|
2047
2169
|
const theme = useFrankieTheme();
|
|
2048
2170
|
const mode = useResolvedMode(theme?.mode);
|
|
2049
2171
|
const rootRef = useRef6(null);
|
|
2050
2172
|
const stylesReady = useStylesReady(rootRef, void 0, injectStyles);
|
|
2051
|
-
return /* @__PURE__ */
|
|
2173
|
+
return /* @__PURE__ */ jsx18(
|
|
2052
2174
|
"div",
|
|
2053
2175
|
{
|
|
2054
2176
|
ref: rootRef,
|
|
@@ -2056,14 +2178,14 @@ function FrankieInline({ className, style, ...panelProps }) {
|
|
|
2056
2178
|
style: { ...themeToCssVars(theme), display: "flex", height: "100%", width: "100%", ...style, ...stylesReady && themeReady ? null : { opacity: 0 } },
|
|
2057
2179
|
"data-frankie-mode": mode,
|
|
2058
2180
|
"data-frankie-styles": stylesReady ? "ready" : "pending",
|
|
2059
|
-
children: /* @__PURE__ */
|
|
2181
|
+
children: /* @__PURE__ */ jsx18(ChatPanel, { ...panelProps, fill: true })
|
|
2060
2182
|
}
|
|
2061
2183
|
);
|
|
2062
2184
|
}
|
|
2063
2185
|
|
|
2064
2186
|
// src/components/chat/FrankieChatPage.tsx
|
|
2065
2187
|
import { useCallback as useCallback3, useEffect as useEffect8, useRef as useRef7, useState as useState9 } from "react";
|
|
2066
|
-
import { ChevronsLeft, ChevronsRight, History, Menu, Plus as Plus2, Settings, X as
|
|
2188
|
+
import { ChevronsLeft, ChevronsRight, History, Menu, Plus as Plus2, Settings, X as X6 } from "lucide-react";
|
|
2067
2189
|
|
|
2068
2190
|
// src/lib/viewport-fill.ts
|
|
2069
2191
|
import { useEffect as useEffect7, useState as useState8 } from "react";
|
|
@@ -2105,7 +2227,7 @@ function useViewportFill(ref, enabled) {
|
|
|
2105
2227
|
}
|
|
2106
2228
|
|
|
2107
2229
|
// src/components/chat/FrankieChatPage.tsx
|
|
2108
|
-
import { Fragment as Fragment3, jsx as
|
|
2230
|
+
import { Fragment as Fragment3, jsx as jsx19, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
2109
2231
|
function groupByDate(items) {
|
|
2110
2232
|
const now = /* @__PURE__ */ new Date();
|
|
2111
2233
|
const startOfToday = new Date(now.getFullYear(), now.getMonth(), now.getDate()).getTime();
|
|
@@ -2198,70 +2320,70 @@ function PageChrome({ showHistory, navigation, currentId, onSelect, onNew, histo
|
|
|
2198
2320
|
close();
|
|
2199
2321
|
}
|
|
2200
2322
|
};
|
|
2201
|
-
const history = /* @__PURE__ */
|
|
2202
|
-
/* @__PURE__ */
|
|
2203
|
-
/* @__PURE__ */
|
|
2204
|
-
/* @__PURE__ */
|
|
2323
|
+
const history = /* @__PURE__ */ jsxs10(Fragment3, { children: [
|
|
2324
|
+
/* @__PURE__ */ jsxs10("div", { className: "frankie-history-heading", children: [
|
|
2325
|
+
/* @__PURE__ */ jsx19("strong", { children: "Conversations" }),
|
|
2326
|
+
/* @__PURE__ */ jsx19("button", { type: "button", className: "frankie-mobile-only", "aria-label": "Close conversations", onClick: close, children: /* @__PURE__ */ jsx19(X6, { size: 18 }) })
|
|
2205
2327
|
] }),
|
|
2206
|
-
/* @__PURE__ */
|
|
2328
|
+
/* @__PURE__ */ jsxs10("button", { type: "button", className: "frankie-new-chat", disabled: busy, onClick: () => {
|
|
2207
2329
|
onNew();
|
|
2208
2330
|
close();
|
|
2209
2331
|
}, children: [
|
|
2210
|
-
/* @__PURE__ */
|
|
2332
|
+
/* @__PURE__ */ jsx19(Plus2, { size: 17 }),
|
|
2211
2333
|
"New chat"
|
|
2212
2334
|
] }),
|
|
2213
|
-
/* @__PURE__ */
|
|
2214
|
-
/* @__PURE__ */
|
|
2215
|
-
/* @__PURE__ */
|
|
2335
|
+
/* @__PURE__ */ jsxs10("label", { className: "frankie-history-search", children: [
|
|
2336
|
+
/* @__PURE__ */ jsx19("span", { children: "Search conversations" }),
|
|
2337
|
+
/* @__PURE__ */ jsx19("input", { type: "search", placeholder: "Search chats\u2026", value: query, onChange: (event) => setQuery(event.target.value) })
|
|
2216
2338
|
] }),
|
|
2217
|
-
/* @__PURE__ */
|
|
2218
|
-
/* @__PURE__ */
|
|
2219
|
-
group.items.map((item) => /* @__PURE__ */
|
|
2220
|
-
/* @__PURE__ */
|
|
2221
|
-
/* @__PURE__ */
|
|
2339
|
+
/* @__PURE__ */ jsx19("nav", { className: "frankie-history-list", "aria-label": "Conversations", children: groupByDate(conversations.filter((item) => item.title.toLowerCase().includes(query.toLowerCase()))).map((group) => /* @__PURE__ */ jsxs10("div", { className: "frankie-history-group", children: [
|
|
2340
|
+
/* @__PURE__ */ jsx19("p", { className: "frankie-history-group-label", children: group.label }),
|
|
2341
|
+
group.items.map((item) => /* @__PURE__ */ jsxs10("button", { type: "button", "aria-current": item.id === currentId ? "page" : void 0, disabled: busy, onClick: () => select(item.id), children: [
|
|
2342
|
+
/* @__PURE__ */ jsx19("span", { children: item.title }),
|
|
2343
|
+
/* @__PURE__ */ jsx19("small", { children: new Date(item.updatedAt).toLocaleDateString() })
|
|
2222
2344
|
] }, item.id))
|
|
2223
2345
|
] }, group.label)) }),
|
|
2224
|
-
historyLoading && !conversations.length ? /* @__PURE__ */
|
|
2346
|
+
historyLoading && !conversations.length ? /* @__PURE__ */ jsx19("p", { role: "status", children: "Loading history\u2026" }) : historyError ? /* @__PURE__ */ jsxs10("p", { role: "status", children: [
|
|
2225
2347
|
historyError,
|
|
2226
|
-
/* @__PURE__ */
|
|
2227
|
-
] }) : !conversations.length && !historyLoading ? /* @__PURE__ */
|
|
2228
|
-
navigation ? /* @__PURE__ */
|
|
2348
|
+
/* @__PURE__ */ jsx19("button", { type: "button", onClick: () => void refresh(), children: "Try again" })
|
|
2349
|
+
] }) : !conversations.length && !historyLoading ? /* @__PURE__ */ jsx19("p", { children: config?.allowAnonymous ? "Sign in to sync conversations across devices." : "Your conversations will appear here." }) : null,
|
|
2350
|
+
navigation ? /* @__PURE__ */ jsx19("div", { className: "frankie-host-navigation", children: navigation }) : null
|
|
2229
2351
|
] });
|
|
2230
|
-
return /* @__PURE__ */
|
|
2231
|
-
showHistory ? /* @__PURE__ */
|
|
2232
|
-
/* @__PURE__ */
|
|
2352
|
+
return /* @__PURE__ */ jsxs10("div", { className: "frankie-page-shell" + (mode === "dark" ? " dark" : ""), style: { ...themeToCssVars(theme), ...themeReady ? null : { opacity: 0 } }, children: [
|
|
2353
|
+
showHistory ? /* @__PURE__ */ jsxs10("aside", { className: "frankie-history-desktop" + (collapsed ? " frankie-history-collapsed" : ""), children: [
|
|
2354
|
+
/* @__PURE__ */ jsx19("button", { type: "button", className: "frankie-history-toggle", "aria-label": collapsed ? "Expand conversations" : "Collapse conversations", "aria-expanded": !collapsed, onClick: () => setCollapsed((value) => !value), children: collapsed ? /* @__PURE__ */ jsx19(ChevronsRight, { size: 16 }) : /* @__PURE__ */ jsx19(ChevronsLeft, { size: 16 }) }),
|
|
2233
2355
|
collapsed ? null : history
|
|
2234
2356
|
] }) : null,
|
|
2235
|
-
/* @__PURE__ */
|
|
2236
|
-
/* @__PURE__ */
|
|
2237
|
-
/* @__PURE__ */
|
|
2238
|
-
showHistory ? /* @__PURE__ */
|
|
2239
|
-
/* @__PURE__ */
|
|
2357
|
+
/* @__PURE__ */ jsxs10("div", { className: "frankie-page-main", children: [
|
|
2358
|
+
/* @__PURE__ */ jsxs10("header", { className: "frankie-page-header", children: [
|
|
2359
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
2360
|
+
showHistory ? /* @__PURE__ */ jsx19("button", { type: "button", className: "frankie-mobile-only", ref: menu, "aria-label": "Open conversations", "aria-haspopup": "dialog", "aria-expanded": drawerOpen, onClick: () => setDrawerOpen(true), children: /* @__PURE__ */ jsx19(Menu, { size: 20 }) }) : null,
|
|
2361
|
+
/* @__PURE__ */ jsx19("strong", { className: "frankie-thread-title", children: conversations.find((item) => item.id === currentId)?.title ?? messages.find((message) => message.role === "user")?.text ?? "New chat" })
|
|
2240
2362
|
] }),
|
|
2241
|
-
/* @__PURE__ */
|
|
2242
|
-
/* @__PURE__ */
|
|
2243
|
-
/* @__PURE__ */
|
|
2244
|
-
/* @__PURE__ */
|
|
2363
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
2364
|
+
/* @__PURE__ */ jsxs10("button", { type: "button", disabled: busy, onClick: onNew, "aria-label": "Start a new conversation", children: [
|
|
2365
|
+
/* @__PURE__ */ jsx19(Plus2, { size: 16 }),
|
|
2366
|
+
/* @__PURE__ */ jsx19("span", { children: "New chat" })
|
|
2245
2367
|
] }),
|
|
2246
|
-
/* @__PURE__ */
|
|
2247
|
-
/* @__PURE__ */
|
|
2248
|
-
/* @__PURE__ */
|
|
2249
|
-
/* @__PURE__ */
|
|
2250
|
-
/* @__PURE__ */
|
|
2251
|
-
/* @__PURE__ */
|
|
2252
|
-
/* @__PURE__ */
|
|
2368
|
+
/* @__PURE__ */ jsxs10("details", { className: "frankie-chat-settings", children: [
|
|
2369
|
+
/* @__PURE__ */ jsx19("summary", { "aria-label": "Chat settings", children: /* @__PURE__ */ jsx19(Settings, { size: 19 }) }),
|
|
2370
|
+
/* @__PURE__ */ jsxs10("div", { children: [
|
|
2371
|
+
/* @__PURE__ */ jsx19("strong", { children: "Chat settings" }),
|
|
2372
|
+
/* @__PURE__ */ jsx19("p", { children: "History syncs automatically for signed-in users." }),
|
|
2373
|
+
/* @__PURE__ */ jsxs10("button", { type: "button", onClick: () => void refresh(), disabled: historyLoading || !showHistory, children: [
|
|
2374
|
+
/* @__PURE__ */ jsx19(History, { size: 16 }),
|
|
2253
2375
|
"Refresh history"
|
|
2254
2376
|
] }),
|
|
2255
|
-
/* @__PURE__ */
|
|
2377
|
+
/* @__PURE__ */ jsx19("small", { children: "Skills are checked at the start of each message." })
|
|
2256
2378
|
] })
|
|
2257
2379
|
] })
|
|
2258
2380
|
] })
|
|
2259
2381
|
] }),
|
|
2260
|
-
/* @__PURE__ */
|
|
2382
|
+
/* @__PURE__ */ jsx19("div", { className: "frankie-page-canvas", children: /* @__PURE__ */ jsx19(FrankieInline, { ...panel, header: panel.header ?? false, placeholder: panel.placeholder ?? `Ask anything about your ${config?.branding.name ?? "Frankie"} workspace\u2026`, emptyState: panel.emptyState }) })
|
|
2261
2383
|
] }),
|
|
2262
|
-
showHistory ? /* @__PURE__ */
|
|
2384
|
+
showHistory ? /* @__PURE__ */ jsx19("dialog", { ref: dialog, className: "frankie-history-dialog frankie-root", "aria-label": "Chat conversations", style: themeToCssVars(theme), onCancel: close, onClick: (event) => {
|
|
2263
2385
|
if (event.target === event.currentTarget) close();
|
|
2264
|
-
}, children: /* @__PURE__ */
|
|
2386
|
+
}, children: /* @__PURE__ */ jsx19("aside", { children: history }) }) : null
|
|
2265
2387
|
] });
|
|
2266
2388
|
}
|
|
2267
2389
|
function FrankieChatPage({ className, style, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus, showHistory = true, navigation, fillViewport = true, ...provider }) {
|
|
@@ -2271,18 +2393,18 @@ function FrankieChatPage({ className, style, header, footer, emptyState, renderM
|
|
|
2271
2393
|
const pageRef = useRef7(null);
|
|
2272
2394
|
const stylesReady = useStylesReady(pageRef, void 0, provider.injectStyles);
|
|
2273
2395
|
const fill = useViewportFill(pageRef, fillViewport);
|
|
2274
|
-
return /* @__PURE__ */
|
|
2396
|
+
return /* @__PURE__ */ jsx19("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__ */ jsx19(FrankieProvider, { ...provider, conversationId: id, storageNamespace: (provider.storageNamespace ?? "frankie." + provider.publishableKey) + "." + id, children: /* @__PURE__ */ jsx19(PageChrome, { showHistory, navigation, currentId: id, onSelect: setId, onNew, history, header, footer, emptyState, renderMessage, renderTool, placeholder, autoFocus }) }) });
|
|
2275
2397
|
}
|
|
2276
2398
|
|
|
2277
2399
|
// src/components/chat/SuggestedPrompts.tsx
|
|
2278
|
-
import { jsx as
|
|
2400
|
+
import { jsx as jsx20 } from "react/jsx-runtime";
|
|
2279
2401
|
function SuggestedPrompts({
|
|
2280
2402
|
prompts,
|
|
2281
2403
|
onPick,
|
|
2282
2404
|
disabled
|
|
2283
2405
|
}) {
|
|
2284
2406
|
if (prompts.length === 0) return null;
|
|
2285
|
-
return /* @__PURE__ */
|
|
2407
|
+
return /* @__PURE__ */ jsx20("div", { className: "flex flex-wrap gap-2", children: prompts.map((prompt) => /* @__PURE__ */ jsx20(
|
|
2286
2408
|
Button,
|
|
2287
2409
|
{
|
|
2288
2410
|
type: "button",
|
|
@@ -2297,11 +2419,12 @@ function SuggestedPrompts({
|
|
|
2297
2419
|
}
|
|
2298
2420
|
|
|
2299
2421
|
// src/components/ui/separator.tsx
|
|
2300
|
-
import { jsx as
|
|
2422
|
+
import { jsx as jsx21 } from "react/jsx-runtime";
|
|
2301
2423
|
function Separator({ className }) {
|
|
2302
|
-
return /* @__PURE__ */
|
|
2424
|
+
return /* @__PURE__ */ jsx21("div", { role: "separator", className: cn("h-px w-full bg-border", className) });
|
|
2303
2425
|
}
|
|
2304
2426
|
export {
|
|
2427
|
+
ActionConfirm,
|
|
2305
2428
|
Avatar,
|
|
2306
2429
|
Badge,
|
|
2307
2430
|
Button,
|