@burdenoff/website-sdk 2026.921.1 → 2026.922.1
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/README.md +2 -2
- package/dist/components/explore.d.mts +10 -2
- package/dist/components/explore.d.ts +10 -2
- package/dist/components/explore.js +536 -221
- package/dist/components/explore.js.map +1 -1
- package/dist/components/explore.mjs +536 -221
- package/dist/components/explore.mjs.map +1 -1
- package/dist/index.js +536 -221
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +536 -221
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -2518,6 +2518,7 @@ function toPayload(staged) {
|
|
|
2518
2518
|
textContent
|
|
2519
2519
|
}));
|
|
2520
2520
|
}
|
|
2521
|
+
var SHORT_VIEWPORT_PX = 420;
|
|
2521
2522
|
var EXPLORE_CSS = `
|
|
2522
2523
|
@keyframes boff-explore-pulse {
|
|
2523
2524
|
0%, 80%, 100% { opacity: 0.25; transform: translateY(0); }
|
|
@@ -2552,6 +2553,12 @@ var EXPLORE_CSS = `
|
|
|
2552
2553
|
.boff-explore-scroll {
|
|
2553
2554
|
scrollbar-width: thin;
|
|
2554
2555
|
scrollbar-color: hsl(var(--border)) transparent;
|
|
2556
|
+
/* Stop the transcript handing its scroll to the page. Without this, the first
|
|
2557
|
+
swipe that exhausts the transcript chains to the document \u2014 and on a host site
|
|
2558
|
+
that is 65-70% footer, which is how a phone ends up showing nothing but the
|
|
2559
|
+
footer. Engines that do not support this property ignore the line and keep
|
|
2560
|
+
today's behaviour; the scroll correction above covers them. */
|
|
2561
|
+
overscroll-behavior: contain;
|
|
2555
2562
|
}
|
|
2556
2563
|
.boff-explore-scroll::-webkit-scrollbar { width: 10px; }
|
|
2557
2564
|
.boff-explore-scroll::-webkit-scrollbar-track { background: transparent; }
|
|
@@ -2669,6 +2676,68 @@ var EXPLORE_CSS = `
|
|
|
2669
2676
|
.boff-explore-busy::after { animation: none; }
|
|
2670
2677
|
.boff-explore-rise { animation: none; opacity: 1; transform: none; }
|
|
2671
2678
|
}
|
|
2679
|
+
|
|
2680
|
+
/* ---- Pane height ----------------------------------------------------------------------
|
|
2681
|
+
This used to be one inline declaration, \`height: calc(100dvh - 64px)\`, and it carried two
|
|
2682
|
+
defects. An inline style cannot carry a fallback, so every engine without \`dvh\` (iOS
|
|
2683
|
+
Safari < 15.4, Chrome/WebView < 108, Samsung Internet < 21, Firefox < 101) dropped it
|
|
2684
|
+
whole; and the 64px came from a prop hard-coded to 64 on all 41 host pages that matches
|
|
2685
|
+
none of them \u2014 the burdenoff/algoshred chrome is 80px, the sticky-header sites are 65.
|
|
2686
|
+
|
|
2687
|
+
A class plus custom properties lets the cascade do the work: \`vh\` is the floor and \`dvh\`
|
|
2688
|
+
an upgrade where it exists, while the two measured values stay *lengths*, never a height.
|
|
2689
|
+
That distinction is the whole prerender contract. The host sites snapshot \`/explore\` by
|
|
2690
|
+
loading the built SPA in headless Chromium and writing the POST-HYDRATION DOM to
|
|
2691
|
+
dist/explore/index.html, so every attribute and inline value this component sets after
|
|
2692
|
+
mount IS the static HTML CloudFront serves. A measured pixel *height* would therefore
|
|
2693
|
+
hand every phone the prerender viewport's height until the bundle executes; a measured
|
|
2694
|
+
*offset* is a site-header height, which is the same on every device to within a few
|
|
2695
|
+
pixels, and the height built from it stays viewport-relative on the visitor's own screen.
|
|
2696
|
+
|
|
2697
|
+
--boff-explore-offset chrome above the pane (measured root top; the prop pre-measure)
|
|
2698
|
+
--boff-explore-inset the slice of the layout viewport hidden below the visible area \u2014
|
|
2699
|
+
the on-screen keyboard, which \`dvh\` does not track. Zero unless
|
|
2700
|
+
something is actually covering the bottom, so a snapshot taken
|
|
2701
|
+
with no keyboard open never carries it. */
|
|
2702
|
+
.boff-explore-viewport {
|
|
2703
|
+
height: calc(100vh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
2704
|
+
}
|
|
2705
|
+
@supports (height: 100dvh) {
|
|
2706
|
+
.boff-explore-viewport {
|
|
2707
|
+
height: calc(100dvh - var(--boff-explore-offset, 0px) - var(--boff-explore-inset, 0px));
|
|
2708
|
+
}
|
|
2709
|
+
}
|
|
2710
|
+
|
|
2711
|
+
/* Landscape phones and other short viewports: a one-screen pane leaves a ~124px transcript
|
|
2712
|
+
sliver under ~610px of chrome and content. At or below the threshold the pane stops
|
|
2713
|
+
pretending to be a window and lets the page's own scrolling carry the transcript. The
|
|
2714
|
+
min-height is the available height rather than a fixed rem value, so the fallback can
|
|
2715
|
+
never be taller than the screen it is rescuing. */
|
|
2716
|
+
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
2717
|
+
.boff-explore-viewport {
|
|
2718
|
+
height: auto;
|
|
2719
|
+
min-height: calc(100vh - var(--boff-explore-offset, 0px));
|
|
2720
|
+
}
|
|
2721
|
+
}
|
|
2722
|
+
@supports (height: 100dvh) {
|
|
2723
|
+
@media (max-height: ${SHORT_VIEWPORT_PX}px) {
|
|
2724
|
+
.boff-explore-viewport {
|
|
2725
|
+
min-height: calc(100dvh - var(--boff-explore-offset, 0px));
|
|
2726
|
+
}
|
|
2727
|
+
}
|
|
2728
|
+
}
|
|
2729
|
+
|
|
2730
|
+
/* ---- Composer dock --------------------------------------------------------------------
|
|
2731
|
+
Three declarations, each the fallback for the one after it: a plain 1rem where \`max()\`
|
|
2732
|
+
is unknown, then the measured overlap of any FOREIGN fixed chrome sitting over the
|
|
2733
|
+
composer (the host sites' cookie banner is 229px tall and lands exactly there), then the
|
|
2734
|
+
same plus the home-indicator inset. \`--boff-explore-chrome\` is set on the root only once
|
|
2735
|
+
an overlap has actually been measured, so everywhere else this collapses to 1rem. */
|
|
2736
|
+
.boff-explore-composer {
|
|
2737
|
+
padding-bottom: 1rem;
|
|
2738
|
+
padding-bottom: max(1rem, var(--boff-explore-chrome, 0px));
|
|
2739
|
+
padding-bottom: max(1rem, env(safe-area-inset-bottom), var(--boff-explore-chrome, 0px));
|
|
2740
|
+
}
|
|
2672
2741
|
`;
|
|
2673
2742
|
function ExploreStyles() {
|
|
2674
2743
|
return /* @__PURE__ */ jsxRuntime.jsx("style", { dangerouslySetInnerHTML: { __html: EXPLORE_CSS } });
|
|
@@ -2690,6 +2759,8 @@ var CAPABILITIES = [
|
|
|
2690
2759
|
body: "Get the sources and next steps \u2014 pricing, docs, a demo \u2014 without hunting."
|
|
2691
2760
|
}
|
|
2692
2761
|
];
|
|
2762
|
+
var MIN_VISIBLE_FRACTION = 0.25;
|
|
2763
|
+
var SDK_OVERLAY_SELECTOR = "[data-boff-explore]";
|
|
2693
2764
|
var CTA_VARIANTS = {
|
|
2694
2765
|
CONTACT: "default",
|
|
2695
2766
|
WAITLIST: "default",
|
|
@@ -2705,6 +2776,28 @@ function normalizeOffset(offset) {
|
|
|
2705
2776
|
if (offset === void 0) return "0px";
|
|
2706
2777
|
return typeof offset === "number" ? `${offset}px` : offset;
|
|
2707
2778
|
}
|
|
2779
|
+
function isOwnElement(el, root) {
|
|
2780
|
+
if (el === document.body || el === document.documentElement) return true;
|
|
2781
|
+
if (root.contains(el)) return true;
|
|
2782
|
+
return el.closest(SDK_OVERLAY_SELECTOR) !== null;
|
|
2783
|
+
}
|
|
2784
|
+
function visualViewportHeight() {
|
|
2785
|
+
return window.visualViewport?.height ?? window.innerHeight;
|
|
2786
|
+
}
|
|
2787
|
+
function clamp(value, min, max) {
|
|
2788
|
+
return Math.min(Math.max(value, min), max);
|
|
2789
|
+
}
|
|
2790
|
+
function isRestoredNavigation() {
|
|
2791
|
+
const entries = window.performance?.getEntriesByType?.("navigation");
|
|
2792
|
+
const entry = entries?.[0];
|
|
2793
|
+
if (!entry) return true;
|
|
2794
|
+
const navigation = entry;
|
|
2795
|
+
return navigation.type === "reload" || navigation.type === "back_forward";
|
|
2796
|
+
}
|
|
2797
|
+
function pageOwnsScrolling(el) {
|
|
2798
|
+
if (window.innerHeight > SHORT_VIEWPORT_PX) return false;
|
|
2799
|
+
return el.scrollHeight - el.clientHeight <= 1;
|
|
2800
|
+
}
|
|
2708
2801
|
function isInternalPath(url) {
|
|
2709
2802
|
return url.startsWith("/") && !url.startsWith("//");
|
|
2710
2803
|
}
|
|
@@ -3553,6 +3646,8 @@ function ExplorePage({
|
|
|
3553
3646
|
const pickQueueRef = React.useRef(Promise.resolve());
|
|
3554
3647
|
const [shareLabel, setShareLabel] = React.useState(T.share);
|
|
3555
3648
|
const [shareUrl, setShareUrl] = React.useState(null);
|
|
3649
|
+
const rootRef = React.useRef(null);
|
|
3650
|
+
const composerRef = React.useRef(null);
|
|
3556
3651
|
const scrollRef = React.useRef(null);
|
|
3557
3652
|
const latestAssistantRef = React.useRef(null);
|
|
3558
3653
|
const alignedForRef = React.useRef(null);
|
|
@@ -3580,11 +3675,196 @@ function ExplorePage({
|
|
|
3580
3675
|
el.style.height = "auto";
|
|
3581
3676
|
el.style.height = `${Math.min(el.scrollHeight, 200)}px`;
|
|
3582
3677
|
}, [draft]);
|
|
3678
|
+
const [paneOffset, setPaneOffset] = React.useState(null);
|
|
3679
|
+
const [bottomInset, setBottomInset] = React.useState(0);
|
|
3680
|
+
const paneOffsetRef = React.useRef(0);
|
|
3681
|
+
const measurePane = React.useCallback(() => {
|
|
3682
|
+
if (heightMode !== "viewport") return;
|
|
3683
|
+
const el = rootRef.current;
|
|
3684
|
+
if (!el) return;
|
|
3685
|
+
const layoutHeight = window.innerHeight;
|
|
3686
|
+
if (layoutHeight === 0) return;
|
|
3687
|
+
const top = Math.round(
|
|
3688
|
+
clamp(el.getBoundingClientRect().top, 0, layoutHeight)
|
|
3689
|
+
);
|
|
3690
|
+
const viewport = window.visualViewport;
|
|
3691
|
+
const inset = viewport ? Math.round(
|
|
3692
|
+
clamp(
|
|
3693
|
+
layoutHeight - viewport.height - viewport.offsetTop,
|
|
3694
|
+
0,
|
|
3695
|
+
layoutHeight
|
|
3696
|
+
)
|
|
3697
|
+
) : 0;
|
|
3698
|
+
paneOffsetRef.current = top;
|
|
3699
|
+
setPaneOffset((current) => current === top ? current : top);
|
|
3700
|
+
setBottomInset((current) => current === inset ? current : inset);
|
|
3701
|
+
}, [heightMode]);
|
|
3702
|
+
const [chromeOverlap, setChromeOverlap] = React.useState(0);
|
|
3703
|
+
const measureChrome = React.useCallback(() => {
|
|
3704
|
+
const root = rootRef.current;
|
|
3705
|
+
const dock = composerRef.current;
|
|
3706
|
+
if (!root || !dock) return;
|
|
3707
|
+
if (typeof document.elementFromPoint !== "function") return;
|
|
3708
|
+
const viewportHeight = visualViewportHeight();
|
|
3709
|
+
const rect = dock.getBoundingClientRect();
|
|
3710
|
+
if (rect.width === 0 || viewportHeight === 0) return;
|
|
3711
|
+
if (rect.bottom <= 0 || rect.top >= viewportHeight) return;
|
|
3712
|
+
const edge = Math.min(rect.bottom, viewportHeight);
|
|
3713
|
+
const hit = document.elementFromPoint(rect.left + rect.width / 2, edge - 1);
|
|
3714
|
+
if (!hit || isOwnElement(hit, root)) {
|
|
3715
|
+
setChromeOverlap(0);
|
|
3716
|
+
return;
|
|
3717
|
+
}
|
|
3718
|
+
const overlap = Math.round(edge - hit.getBoundingClientRect().top);
|
|
3719
|
+
setChromeOverlap(overlap > 0 && overlap < viewportHeight ? overlap : 0);
|
|
3720
|
+
}, []);
|
|
3721
|
+
const remeasure = React.useCallback(() => {
|
|
3722
|
+
measurePane();
|
|
3723
|
+
measureChrome();
|
|
3724
|
+
}, [measurePane, measureChrome]);
|
|
3725
|
+
React.useEffect(() => {
|
|
3726
|
+
if (typeof window === "undefined") return;
|
|
3727
|
+
let frame = 0;
|
|
3728
|
+
const run = () => {
|
|
3729
|
+
frame = 0;
|
|
3730
|
+
remeasure();
|
|
3731
|
+
};
|
|
3732
|
+
const schedule = () => {
|
|
3733
|
+
if (frame) return;
|
|
3734
|
+
if (typeof window.requestAnimationFrame !== "function") {
|
|
3735
|
+
remeasure();
|
|
3736
|
+
return;
|
|
3737
|
+
}
|
|
3738
|
+
frame = window.requestAnimationFrame(run);
|
|
3739
|
+
};
|
|
3740
|
+
remeasure();
|
|
3741
|
+
const viewport = window.visualViewport;
|
|
3742
|
+
window.addEventListener("resize", schedule);
|
|
3743
|
+
window.addEventListener("orientationchange", schedule);
|
|
3744
|
+
viewport?.addEventListener("resize", schedule);
|
|
3745
|
+
viewport?.addEventListener("scroll", schedule);
|
|
3746
|
+
const observer = typeof MutationObserver === "function" ? new MutationObserver((records) => {
|
|
3747
|
+
const el = rootRef.current;
|
|
3748
|
+
if (el && records.every((record) => el.contains(record.target)))
|
|
3749
|
+
return;
|
|
3750
|
+
schedule();
|
|
3751
|
+
}) : null;
|
|
3752
|
+
observer?.observe(document.body, { childList: true, subtree: true });
|
|
3753
|
+
return () => {
|
|
3754
|
+
if (frame) window.cancelAnimationFrame(frame);
|
|
3755
|
+
window.removeEventListener("resize", schedule);
|
|
3756
|
+
window.removeEventListener("orientationchange", schedule);
|
|
3757
|
+
viewport?.removeEventListener("resize", schedule);
|
|
3758
|
+
viewport?.removeEventListener("scroll", schedule);
|
|
3759
|
+
observer?.disconnect();
|
|
3760
|
+
setPaneOffset(null);
|
|
3761
|
+
setBottomInset(0);
|
|
3762
|
+
setChromeOverlap(0);
|
|
3763
|
+
};
|
|
3764
|
+
}, [remeasure]);
|
|
3765
|
+
React.useEffect(() => {
|
|
3766
|
+
measureChrome();
|
|
3767
|
+
}, [measureChrome, paneOffset, bottomInset]);
|
|
3768
|
+
React.useEffect(() => {
|
|
3769
|
+
if (heightMode !== "viewport" || typeof window === "undefined") return;
|
|
3770
|
+
const { history: browserHistory } = window;
|
|
3771
|
+
const previousRestoration = "scrollRestoration" in browserHistory ? browserHistory.scrollRestoration : null;
|
|
3772
|
+
if (previousRestoration !== null)
|
|
3773
|
+
browserHistory.scrollRestoration = "manual";
|
|
3774
|
+
let frame = 0;
|
|
3775
|
+
let visitorMoved = false;
|
|
3776
|
+
const onVisitorMove = () => {
|
|
3777
|
+
visitorMoved = true;
|
|
3778
|
+
};
|
|
3779
|
+
const align = () => {
|
|
3780
|
+
if (visitorMoved) return;
|
|
3781
|
+
const el = rootRef.current;
|
|
3782
|
+
if (!el) return;
|
|
3783
|
+
const viewportHeight = visualViewportHeight();
|
|
3784
|
+
const rect = el.getBoundingClientRect();
|
|
3785
|
+
if (rect.height === 0 || viewportHeight === 0) return;
|
|
3786
|
+
const visible = Math.min(rect.bottom, viewportHeight) - Math.max(rect.top, 0);
|
|
3787
|
+
const reachable = Math.min(rect.height, viewportHeight);
|
|
3788
|
+
if (visible >= reachable * MIN_VISIBLE_FRACTION) return;
|
|
3789
|
+
window.scrollTo({
|
|
3790
|
+
top: Math.max(0, window.scrollY + rect.top),
|
|
3791
|
+
left: window.scrollX,
|
|
3792
|
+
behavior: "auto"
|
|
3793
|
+
});
|
|
3794
|
+
if (typeof document.elementFromPoint === "function") {
|
|
3795
|
+
const probe = document.elementFromPoint(
|
|
3796
|
+
Math.max(0, rect.left + rect.width / 2),
|
|
3797
|
+
1
|
|
3798
|
+
);
|
|
3799
|
+
if (probe && !isOwnElement(probe, el)) {
|
|
3800
|
+
const chrome = probe.getBoundingClientRect().bottom;
|
|
3801
|
+
if (chrome > 0 && chrome < viewportHeight / 2) {
|
|
3802
|
+
window.scrollBy({ top: -chrome, left: 0, behavior: "auto" });
|
|
3803
|
+
}
|
|
3804
|
+
}
|
|
3805
|
+
}
|
|
3806
|
+
remeasure();
|
|
3807
|
+
};
|
|
3808
|
+
const alignIfRestored = () => {
|
|
3809
|
+
if (!isRestoredNavigation()) return;
|
|
3810
|
+
align();
|
|
3811
|
+
};
|
|
3812
|
+
alignIfRestored();
|
|
3813
|
+
if (typeof window.requestAnimationFrame === "function") {
|
|
3814
|
+
frame = window.requestAnimationFrame(alignIfRestored);
|
|
3815
|
+
}
|
|
3816
|
+
const onPageShow = (event) => {
|
|
3817
|
+
if (!event.persisted) {
|
|
3818
|
+
alignIfRestored();
|
|
3819
|
+
return;
|
|
3820
|
+
}
|
|
3821
|
+
visitorMoved = false;
|
|
3822
|
+
align();
|
|
3823
|
+
};
|
|
3824
|
+
window.addEventListener("pageshow", onPageShow);
|
|
3825
|
+
window.addEventListener("wheel", onVisitorMove, { passive: true });
|
|
3826
|
+
window.addEventListener("touchmove", onVisitorMove, { passive: true });
|
|
3827
|
+
window.addEventListener("keydown", onVisitorMove, { passive: true });
|
|
3828
|
+
return () => {
|
|
3829
|
+
if (frame) window.cancelAnimationFrame(frame);
|
|
3830
|
+
window.removeEventListener("pageshow", onPageShow);
|
|
3831
|
+
window.removeEventListener("wheel", onVisitorMove);
|
|
3832
|
+
window.removeEventListener("touchmove", onVisitorMove);
|
|
3833
|
+
window.removeEventListener("keydown", onVisitorMove);
|
|
3834
|
+
if (previousRestoration !== null)
|
|
3835
|
+
browserHistory.scrollRestoration = previousRestoration;
|
|
3836
|
+
};
|
|
3837
|
+
}, [heightMode, remeasure]);
|
|
3583
3838
|
const handleScroll = React.useCallback(() => {
|
|
3584
3839
|
const el = scrollRef.current;
|
|
3585
3840
|
if (!el) return;
|
|
3586
3841
|
stickToBottomRef.current = el.scrollHeight - el.scrollTop - el.clientHeight < 80;
|
|
3587
3842
|
}, []);
|
|
3843
|
+
React.useEffect(() => {
|
|
3844
|
+
if (typeof window === "undefined") return;
|
|
3845
|
+
let frame = 0;
|
|
3846
|
+
const run = () => {
|
|
3847
|
+
frame = 0;
|
|
3848
|
+
const el = scrollRef.current;
|
|
3849
|
+
const dock = composerRef.current;
|
|
3850
|
+
if (!el || !dock) return;
|
|
3851
|
+
if (!pageOwnsScrolling(el)) return;
|
|
3852
|
+
stickToBottomRef.current = dock.getBoundingClientRect().bottom - visualViewportHeight() < 80;
|
|
3853
|
+
};
|
|
3854
|
+
const schedule = () => {
|
|
3855
|
+
if (frame) return;
|
|
3856
|
+
if (typeof window.requestAnimationFrame !== "function") {
|
|
3857
|
+
run();
|
|
3858
|
+
return;
|
|
3859
|
+
}
|
|
3860
|
+
frame = window.requestAnimationFrame(run);
|
|
3861
|
+
};
|
|
3862
|
+
window.addEventListener("scroll", schedule, { passive: true });
|
|
3863
|
+
return () => {
|
|
3864
|
+
if (frame) window.cancelAnimationFrame(frame);
|
|
3865
|
+
window.removeEventListener("scroll", schedule);
|
|
3866
|
+
};
|
|
3867
|
+
}, []);
|
|
3588
3868
|
const latestAssistantId = React.useMemo(() => {
|
|
3589
3869
|
for (let i = messages.length - 1; i >= 0; i -= 1) {
|
|
3590
3870
|
const message = messages[i];
|
|
@@ -3600,16 +3880,32 @@ function ExplorePage({
|
|
|
3600
3880
|
if (!stickToBottomRef.current) return;
|
|
3601
3881
|
if (anchor.offsetHeight === 0) return;
|
|
3602
3882
|
alignedForRef.current = latestAssistantId;
|
|
3603
|
-
|
|
3604
|
-
|
|
3883
|
+
if (!pageOwnsScrolling(el)) {
|
|
3884
|
+
const delta2 = anchor.getBoundingClientRect().top - el.getBoundingClientRect().top;
|
|
3885
|
+
el.scrollTop = Math.max(0, el.scrollTop + delta2 - 12);
|
|
3886
|
+
return;
|
|
3887
|
+
}
|
|
3888
|
+
const delta = Math.round(
|
|
3889
|
+
anchor.getBoundingClientRect().top - paneOffsetRef.current - 12
|
|
3890
|
+
);
|
|
3891
|
+
if (delta !== 0) window.scrollBy({ top: delta, left: 0, behavior: "auto" });
|
|
3605
3892
|
}, [latestAssistantId, messages]);
|
|
3606
3893
|
React.useEffect(() => {
|
|
3894
|
+
if (messages.length === 0) return;
|
|
3607
3895
|
if (!stickToBottomRef.current) return;
|
|
3608
3896
|
if (latestAssistantId && alignedForRef.current === latestAssistantId)
|
|
3609
3897
|
return;
|
|
3610
3898
|
const el = scrollRef.current;
|
|
3611
3899
|
if (!el) return;
|
|
3612
|
-
el
|
|
3900
|
+
if (!pageOwnsScrolling(el)) {
|
|
3901
|
+
el.scrollTop = el.scrollHeight;
|
|
3902
|
+
return;
|
|
3903
|
+
}
|
|
3904
|
+
const dock = composerRef.current ?? el;
|
|
3905
|
+
const delta = Math.round(
|
|
3906
|
+
dock.getBoundingClientRect().bottom - visualViewportHeight()
|
|
3907
|
+
);
|
|
3908
|
+
if (delta > 0) window.scrollBy({ top: delta, left: 0, behavior: "auto" });
|
|
3613
3909
|
}, [activity, latestAssistantId, messages]);
|
|
3614
3910
|
const handleShare = React.useCallback(async () => {
|
|
3615
3911
|
setShareLabel(T.copying);
|
|
@@ -3707,16 +4003,27 @@ function ExplorePage({
|
|
|
3707
4003
|
const captchaOn = Boolean(
|
|
3708
4004
|
catalog.captchaRequired && catalog.recaptchaSiteKey
|
|
3709
4005
|
);
|
|
3710
|
-
const rootStyle =
|
|
4006
|
+
const rootStyle = {};
|
|
4007
|
+
if (heightMode === "viewport") {
|
|
4008
|
+
rootStyle["--boff-explore-offset"] = paneOffset === null ? normalizeOffset(viewportOffset) : `${paneOffset}px`;
|
|
4009
|
+
if (bottomInset > 0) {
|
|
4010
|
+
rootStyle["--boff-explore-inset"] = `${bottomInset}px`;
|
|
4011
|
+
}
|
|
4012
|
+
}
|
|
4013
|
+
if (chromeOverlap > 0) {
|
|
4014
|
+
rootStyle["--boff-explore-chrome"] = `${chromeOverlap}px`;
|
|
4015
|
+
}
|
|
3711
4016
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
3712
4017
|
"div",
|
|
3713
4018
|
{
|
|
4019
|
+
ref: rootRef,
|
|
3714
4020
|
"data-boff-explore": "page",
|
|
3715
4021
|
"data-phase": phase,
|
|
3716
4022
|
"data-locale": activeLocale,
|
|
3717
4023
|
dir: rtl ? "rtl" : void 0,
|
|
3718
4024
|
className: cn(
|
|
3719
4025
|
"flex w-full min-w-0 flex-col overflow-x-hidden bg-background text-foreground",
|
|
4026
|
+
heightMode === "viewport" && "boff-explore-viewport",
|
|
3720
4027
|
heightMode === "auto" && "min-h-[70vh]",
|
|
3721
4028
|
className
|
|
3722
4029
|
),
|
|
@@ -3950,249 +4257,257 @@ function ExplorePage({
|
|
|
3950
4257
|
]
|
|
3951
4258
|
}
|
|
3952
4259
|
) : null,
|
|
3953
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3954
|
-
"
|
|
4260
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4261
|
+
"div",
|
|
3955
4262
|
{
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
"
|
|
4263
|
+
ref: composerRef,
|
|
4264
|
+
"data-boff-explore": "composer-dock",
|
|
4265
|
+
className: "boff-explore-composer border-t border-border bg-background/85 px-4 pt-3 backdrop-blur-md",
|
|
4266
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4267
|
+
"form",
|
|
4268
|
+
{
|
|
4269
|
+
"data-boff-explore": "composer",
|
|
4270
|
+
className: "mx-auto w-full max-w-3xl",
|
|
4271
|
+
onSubmit: (event) => {
|
|
4272
|
+
event.preventDefault();
|
|
4273
|
+
submitDraft();
|
|
4274
|
+
},
|
|
4275
|
+
children: [
|
|
4276
|
+
error ? /* @__PURE__ */ jsxRuntime.jsx(ErrorBanner, { error, onRetry: retry, onReset: reset }) : null,
|
|
4277
|
+
staged.length > 0 ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4278
|
+
"ul",
|
|
4279
|
+
{
|
|
4280
|
+
"data-boff-explore": "attachments",
|
|
4281
|
+
className: "mb-2 flex flex-wrap gap-1.5",
|
|
4282
|
+
"aria-label": T.attach,
|
|
4283
|
+
children: staged.map((entry) => /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4284
|
+
"li",
|
|
4285
|
+
{
|
|
4286
|
+
"data-boff-explore": "attachment",
|
|
4287
|
+
"data-text-extracted": entry.textExtracted ? "true" : "false",
|
|
4288
|
+
className: "flex max-w-full items-center gap-1.5 rounded-full border border-border bg-muted/60 py-1 pl-2.5 pr-1 text-xs",
|
|
4289
|
+
children: [
|
|
4290
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-3 w-3 shrink-0 text-muted-foreground" }),
|
|
4291
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", title: entry.name, children: entry.name }),
|
|
4292
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
4293
|
+
formatBytes(entry.size),
|
|
4294
|
+
entry.textExtracted ? "" : ` \xB7 ${T.attachmentNameOnly}`
|
|
4295
|
+
] }),
|
|
4296
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4297
|
+
"button",
|
|
4298
|
+
{
|
|
4299
|
+
type: "button",
|
|
4300
|
+
"data-boff-explore": "attachment-remove",
|
|
4301
|
+
onClick: () => removeAttachment(entry.id),
|
|
4302
|
+
"aria-label": fill(T.removeAttachment, { name: entry.name }),
|
|
4303
|
+
className: "ml-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-background hover:text-foreground",
|
|
4304
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "h-3 w-3" })
|
|
4305
|
+
}
|
|
4306
|
+
)
|
|
4307
|
+
]
|
|
4308
|
+
},
|
|
4309
|
+
entry.id
|
|
4310
|
+
))
|
|
4311
|
+
}
|
|
4312
|
+
) : null,
|
|
4313
|
+
attachmentNotice !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4314
|
+
"p",
|
|
3972
4315
|
{
|
|
3973
|
-
"data-boff-explore": "attachment",
|
|
3974
|
-
|
|
3975
|
-
className: "
|
|
4316
|
+
"data-boff-explore": "attachment-notice",
|
|
4317
|
+
role: "status",
|
|
4318
|
+
className: "mb-2 text-xs text-destructive",
|
|
4319
|
+
children: attachmentNotice
|
|
4320
|
+
}
|
|
4321
|
+
) : null,
|
|
4322
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4323
|
+
"div",
|
|
4324
|
+
{
|
|
4325
|
+
className: cn(
|
|
4326
|
+
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
4327
|
+
busy && "boff-explore-busy",
|
|
4328
|
+
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
4329
|
+
),
|
|
3976
4330
|
children: [
|
|
3977
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-3 w-3 shrink-0 text-muted-foreground" }),
|
|
3978
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate font-medium", title: entry.name, children: entry.name }),
|
|
3979
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "shrink-0 text-muted-foreground", children: [
|
|
3980
|
-
formatBytes(entry.size),
|
|
3981
|
-
entry.textExtracted ? "" : ` \xB7 ${T.attachmentNameOnly}`
|
|
3982
|
-
] }),
|
|
3983
4331
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
3984
|
-
|
|
4332
|
+
Textarea,
|
|
4333
|
+
{
|
|
4334
|
+
ref: textareaRef,
|
|
4335
|
+
value: draft,
|
|
4336
|
+
rows: 1,
|
|
4337
|
+
disabled: composerDisabled,
|
|
4338
|
+
onChange: (event) => setDraft(event.target.value),
|
|
4339
|
+
onKeyDown: handleKeyDown,
|
|
4340
|
+
onCompositionStart: () => {
|
|
4341
|
+
composingRef.current = true;
|
|
4342
|
+
},
|
|
4343
|
+
onCompositionEnd: () => {
|
|
4344
|
+
composingRef.current = false;
|
|
4345
|
+
},
|
|
4346
|
+
"aria-label": "Ask a question",
|
|
4347
|
+
placeholder: composerDisabled ? phase === "limited" ? "Message limit reached \u2014 please try again later" : "Explore is unavailable right now" : focusedProductName ? fill(T.placeholderProduct, {
|
|
4348
|
+
product: focusedProductName
|
|
4349
|
+
}) : T.placeholder,
|
|
4350
|
+
className: "max-h-[200px] min-h-[44px] w-full min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-base shadow-none focus-visible:ring-0 sm:text-sm"
|
|
4351
|
+
}
|
|
4352
|
+
),
|
|
4353
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4354
|
+
"input",
|
|
4355
|
+
{
|
|
4356
|
+
ref: fileInputRef,
|
|
4357
|
+
type: "file",
|
|
4358
|
+
multiple: true,
|
|
4359
|
+
className: "hidden",
|
|
4360
|
+
onChange: (event) => {
|
|
4361
|
+
void handleFilesPicked(event.target.files);
|
|
4362
|
+
event.target.value = "";
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
),
|
|
4366
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4367
|
+
Button,
|
|
4368
|
+
{
|
|
4369
|
+
"data-boff-explore": "attach",
|
|
4370
|
+
type: "button",
|
|
4371
|
+
size: "icon",
|
|
4372
|
+
variant: "ghost",
|
|
4373
|
+
disabled: composerDisabled || staged.length >= MAX_ATTACHMENTS,
|
|
4374
|
+
"aria-label": T.attach,
|
|
4375
|
+
title: `${T.attach} \u2014 ${fill(T.attachHint, {
|
|
4376
|
+
max: String(MAX_ATTACHMENTS),
|
|
4377
|
+
size: String(MAX_ATTACHMENT_BYTES / (1024 * 1024))
|
|
4378
|
+
})}`,
|
|
4379
|
+
onClick: () => fileInputRef.current?.click(),
|
|
4380
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-4 w-4" })
|
|
4381
|
+
}
|
|
4382
|
+
),
|
|
4383
|
+
speech.supported ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4384
|
+
Button,
|
|
4385
|
+
{
|
|
4386
|
+
"data-boff-explore": "mic",
|
|
4387
|
+
"data-listening": speech.listening ? "true" : "false",
|
|
4388
|
+
type: "button",
|
|
4389
|
+
size: "icon",
|
|
4390
|
+
variant: speech.listening ? "default" : "ghost",
|
|
4391
|
+
disabled: composerDisabled,
|
|
4392
|
+
"aria-label": speech.listening ? T.stopDictating : T.dictate,
|
|
4393
|
+
"aria-pressed": speech.listening,
|
|
4394
|
+
title: speech.listening ? T.stopDictating : T.dictate,
|
|
4395
|
+
onClick: speech.toggle,
|
|
4396
|
+
className: cn(speech.listening && "animate-pulse"),
|
|
4397
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: "h-4 w-4" })
|
|
4398
|
+
}
|
|
4399
|
+
) : null,
|
|
4400
|
+
busy ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4401
|
+
Button,
|
|
3985
4402
|
{
|
|
4403
|
+
"data-boff-explore": "stop",
|
|
3986
4404
|
type: "button",
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
"aria-label":
|
|
3990
|
-
|
|
3991
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.
|
|
4405
|
+
size: "icon",
|
|
4406
|
+
variant: "secondary",
|
|
4407
|
+
"aria-label": T.stop,
|
|
4408
|
+
onClick: stop,
|
|
4409
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { className: "h-4 w-4" })
|
|
4410
|
+
}
|
|
4411
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4412
|
+
Button,
|
|
4413
|
+
{
|
|
4414
|
+
"data-boff-explore": "send",
|
|
4415
|
+
type: "submit",
|
|
4416
|
+
size: "icon",
|
|
4417
|
+
disabled: !canSubmit,
|
|
4418
|
+
"aria-label": T.send,
|
|
4419
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
|
|
3992
4420
|
}
|
|
3993
4421
|
)
|
|
3994
4422
|
]
|
|
3995
|
-
}
|
|
3996
|
-
entry.id
|
|
3997
|
-
))
|
|
3998
|
-
}
|
|
3999
|
-
) : null,
|
|
4000
|
-
attachmentNotice !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4001
|
-
"p",
|
|
4002
|
-
{
|
|
4003
|
-
"data-boff-explore": "attachment-notice",
|
|
4004
|
-
role: "status",
|
|
4005
|
-
className: "mb-2 text-xs text-destructive",
|
|
4006
|
-
children: attachmentNotice
|
|
4007
|
-
}
|
|
4008
|
-
) : null,
|
|
4009
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4010
|
-
"div",
|
|
4011
|
-
{
|
|
4012
|
-
className: cn(
|
|
4013
|
-
"flex min-w-0 items-end gap-2 rounded-2xl border bg-card p-2 shadow-sm transition-all",
|
|
4014
|
-
busy && "boff-explore-busy",
|
|
4015
|
-
overLimit ? "border-destructive/60" : "border-border focus-within:border-primary/40 focus-within:ring-1 focus-within:ring-ring"
|
|
4423
|
+
}
|
|
4016
4424
|
),
|
|
4017
|
-
|
|
4018
|
-
|
|
4019
|
-
|
|
4425
|
+
speech.listening || speech.interim !== "" ? /* @__PURE__ */ jsxRuntime.jsxs(
|
|
4426
|
+
"p",
|
|
4427
|
+
{
|
|
4428
|
+
"data-boff-explore": "dictation",
|
|
4429
|
+
className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
|
|
4430
|
+
"aria-live": "polite",
|
|
4431
|
+
children: [
|
|
4432
|
+
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
|
|
4433
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
|
|
4434
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
4435
|
+
] }),
|
|
4436
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
|
|
4437
|
+
]
|
|
4438
|
+
}
|
|
4439
|
+
) : null,
|
|
4440
|
+
speech.error !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4441
|
+
"p",
|
|
4442
|
+
{
|
|
4443
|
+
"data-boff-explore": "dictation-error",
|
|
4444
|
+
className: "mt-2 text-xs text-destructive",
|
|
4445
|
+
children: speech.error
|
|
4446
|
+
}
|
|
4447
|
+
) : null,
|
|
4448
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
4449
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
|
|
4450
|
+
remainingToday !== null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-boff-explore": "remaining", children: [
|
|
4451
|
+
remainingToday,
|
|
4452
|
+
" message",
|
|
4453
|
+
remainingToday === 1 ? "" : "s",
|
|
4454
|
+
" left today"
|
|
4455
|
+
] }) : null,
|
|
4456
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4457
|
+
"span",
|
|
4020
4458
|
{
|
|
4021
|
-
|
|
4022
|
-
|
|
4023
|
-
|
|
4024
|
-
|
|
4025
|
-
|
|
4026
|
-
|
|
4027
|
-
|
|
4028
|
-
|
|
4029
|
-
|
|
4030
|
-
onCompositionEnd: () => {
|
|
4031
|
-
composingRef.current = false;
|
|
4032
|
-
},
|
|
4033
|
-
"aria-label": "Ask a question",
|
|
4034
|
-
placeholder: composerDisabled ? phase === "limited" ? "Message limit reached \u2014 please try again later" : "Explore is unavailable right now" : focusedProductName ? fill(T.placeholderProduct, {
|
|
4035
|
-
product: focusedProductName
|
|
4036
|
-
}) : T.placeholder,
|
|
4037
|
-
className: "max-h-[200px] min-h-[44px] w-full min-w-0 flex-1 resize-none border-0 bg-transparent px-2 py-2.5 text-base shadow-none focus-visible:ring-0 sm:text-sm"
|
|
4459
|
+
className: cn(
|
|
4460
|
+
"ml-auto tabular-nums",
|
|
4461
|
+
overLimit && "font-medium text-destructive"
|
|
4462
|
+
),
|
|
4463
|
+
children: [
|
|
4464
|
+
draft.length.toLocaleString(),
|
|
4465
|
+
" / ",
|
|
4466
|
+
maxLength.toLocaleString()
|
|
4467
|
+
]
|
|
4038
4468
|
}
|
|
4039
|
-
)
|
|
4469
|
+
)
|
|
4470
|
+
] }),
|
|
4471
|
+
shareUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
|
|
4040
4472
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4041
4473
|
"input",
|
|
4042
4474
|
{
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
|
|
4048
|
-
|
|
4049
|
-
event.target.value = "";
|
|
4050
|
-
}
|
|
4475
|
+
"data-boff-explore": "share-url",
|
|
4476
|
+
readOnly: true,
|
|
4477
|
+
value: shareUrl,
|
|
4478
|
+
"aria-label": "Read-only link to this conversation",
|
|
4479
|
+
onFocus: (event) => event.currentTarget.select(),
|
|
4480
|
+
className: "min-w-0 flex-1 rounded-md border border-border bg-muted px-2 py-1 text-xs text-muted-foreground"
|
|
4051
4481
|
}
|
|
4052
4482
|
),
|
|
4053
4483
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4054
|
-
|
|
4055
|
-
{
|
|
4056
|
-
"data-boff-explore": "attach",
|
|
4057
|
-
type: "button",
|
|
4058
|
-
size: "icon",
|
|
4059
|
-
variant: "ghost",
|
|
4060
|
-
disabled: composerDisabled || staged.length >= MAX_ATTACHMENTS,
|
|
4061
|
-
"aria-label": T.attach,
|
|
4062
|
-
title: `${T.attach} \u2014 ${fill(T.attachHint, {
|
|
4063
|
-
max: String(MAX_ATTACHMENTS),
|
|
4064
|
-
size: String(MAX_ATTACHMENT_BYTES / (1024 * 1024))
|
|
4065
|
-
})}`,
|
|
4066
|
-
onClick: () => fileInputRef.current?.click(),
|
|
4067
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Paperclip, { className: "h-4 w-4" })
|
|
4068
|
-
}
|
|
4069
|
-
),
|
|
4070
|
-
speech.supported ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4071
|
-
Button,
|
|
4484
|
+
"button",
|
|
4072
4485
|
{
|
|
4073
|
-
"data-boff-explore": "mic",
|
|
4074
|
-
"data-listening": speech.listening ? "true" : "false",
|
|
4075
4486
|
type: "button",
|
|
4076
|
-
|
|
4077
|
-
|
|
4078
|
-
|
|
4079
|
-
"aria-label": speech.listening ? T.stopDictating : T.dictate,
|
|
4080
|
-
"aria-pressed": speech.listening,
|
|
4081
|
-
title: speech.listening ? T.stopDictating : T.dictate,
|
|
4082
|
-
onClick: speech.toggle,
|
|
4083
|
-
className: cn(speech.listening && "animate-pulse"),
|
|
4084
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Mic, { className: "h-4 w-4" })
|
|
4487
|
+
className: "shrink-0 text-xs underline underline-offset-2",
|
|
4488
|
+
onClick: () => setShareUrl(null),
|
|
4489
|
+
children: "Done"
|
|
4085
4490
|
}
|
|
4086
|
-
)
|
|
4087
|
-
|
|
4088
|
-
|
|
4491
|
+
)
|
|
4492
|
+
] }) : null,
|
|
4493
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
4494
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: T.disclaimerShort }),
|
|
4495
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4496
|
+
"button",
|
|
4089
4497
|
{
|
|
4090
|
-
"data-boff-explore": "stop",
|
|
4091
4498
|
type: "button",
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
"
|
|
4095
|
-
|
|
4096
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Square, { className: "h-4 w-4" })
|
|
4097
|
-
}
|
|
4098
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
4099
|
-
Button,
|
|
4100
|
-
{
|
|
4101
|
-
"data-boff-explore": "send",
|
|
4102
|
-
type: "submit",
|
|
4103
|
-
size: "icon",
|
|
4104
|
-
disabled: !canSubmit,
|
|
4105
|
-
"aria-label": T.send,
|
|
4106
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUp, { className: "h-4 w-4" })
|
|
4499
|
+
"data-boff-explore": "disclaimer",
|
|
4500
|
+
onClick: () => setDisclaimerOpen(true),
|
|
4501
|
+
className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
4502
|
+
children: T.disclaimer
|
|
4107
4503
|
}
|
|
4108
4504
|
)
|
|
4109
|
-
]
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
"p",
|
|
4114
|
-
{
|
|
4115
|
-
"data-boff-explore": "dictation",
|
|
4116
|
-
className: "mt-2 flex items-center gap-2 text-xs text-muted-foreground",
|
|
4117
|
-
"aria-live": "polite",
|
|
4118
|
-
children: [
|
|
4119
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "relative flex h-2 w-2 shrink-0", children: [
|
|
4120
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute inline-flex h-full w-full animate-ping rounded-full bg-primary opacity-75" }),
|
|
4121
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "relative inline-flex h-2 w-2 rounded-full bg-primary" })
|
|
4122
|
-
] }),
|
|
4123
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "italic", children: speech.interim !== "" ? speech.interim : "Listening\u2026" })
|
|
4124
|
-
]
|
|
4125
|
-
}
|
|
4126
|
-
) : null,
|
|
4127
|
-
speech.error !== null ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
4128
|
-
"p",
|
|
4129
|
-
{
|
|
4130
|
-
"data-boff-explore": "dictation-error",
|
|
4131
|
-
className: "mt-2 text-xs text-destructive",
|
|
4132
|
-
children: speech.error
|
|
4133
|
-
}
|
|
4134
|
-
) : null,
|
|
4135
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
4136
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "hidden sm:inline", children: "Enter to send \xB7 Shift + Enter for a new line" }),
|
|
4137
|
-
remainingToday !== null ? /* @__PURE__ */ jsxRuntime.jsxs("span", { "data-boff-explore": "remaining", children: [
|
|
4138
|
-
remainingToday,
|
|
4139
|
-
" message",
|
|
4140
|
-
remainingToday === 1 ? "" : "s",
|
|
4141
|
-
" left today"
|
|
4142
|
-
] }) : null,
|
|
4143
|
-
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
4144
|
-
"span",
|
|
4145
|
-
{
|
|
4146
|
-
className: cn(
|
|
4147
|
-
"ml-auto tabular-nums",
|
|
4148
|
-
overLimit && "font-medium text-destructive"
|
|
4149
|
-
),
|
|
4150
|
-
children: [
|
|
4151
|
-
draft.length.toLocaleString(),
|
|
4152
|
-
" / ",
|
|
4153
|
-
maxLength.toLocaleString()
|
|
4154
|
-
]
|
|
4155
|
-
}
|
|
4156
|
-
)
|
|
4157
|
-
] }),
|
|
4158
|
-
shareUrl ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center gap-2", children: [
|
|
4159
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4160
|
-
"input",
|
|
4161
|
-
{
|
|
4162
|
-
"data-boff-explore": "share-url",
|
|
4163
|
-
readOnly: true,
|
|
4164
|
-
value: shareUrl,
|
|
4165
|
-
"aria-label": "Read-only link to this conversation",
|
|
4166
|
-
onFocus: (event) => event.currentTarget.select(),
|
|
4167
|
-
className: "min-w-0 flex-1 rounded-md border border-border bg-muted px-2 py-1 text-xs text-muted-foreground"
|
|
4168
|
-
}
|
|
4169
|
-
),
|
|
4170
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4171
|
-
"button",
|
|
4172
|
-
{
|
|
4173
|
-
type: "button",
|
|
4174
|
-
className: "shrink-0 text-xs underline underline-offset-2",
|
|
4175
|
-
onClick: () => setShareUrl(null),
|
|
4176
|
-
children: "Done"
|
|
4177
|
-
}
|
|
4178
|
-
)
|
|
4179
|
-
] }) : null,
|
|
4180
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground", children: [
|
|
4181
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "min-w-0 truncate", children: T.disclaimerShort }),
|
|
4182
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
4183
|
-
"button",
|
|
4184
|
-
{
|
|
4185
|
-
type: "button",
|
|
4186
|
-
"data-boff-explore": "disclaimer",
|
|
4187
|
-
onClick: () => setDisclaimerOpen(true),
|
|
4188
|
-
className: "shrink-0 underline underline-offset-2 transition-colors hover:text-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring",
|
|
4189
|
-
children: T.disclaimer
|
|
4190
|
-
}
|
|
4191
|
-
)
|
|
4192
|
-
] })
|
|
4193
|
-
]
|
|
4505
|
+
] })
|
|
4506
|
+
]
|
|
4507
|
+
}
|
|
4508
|
+
)
|
|
4194
4509
|
}
|
|
4195
|
-
)
|
|
4510
|
+
)
|
|
4196
4511
|
]
|
|
4197
4512
|
}
|
|
4198
4513
|
);
|