@agent-native/toolkit 0.227.0 → 0.228.0-nightly-20261110212400
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/app/chat/AgentPanel.d.ts +15 -15
- package/dist/app/chat/AgentPanel.d.ts.map +1 -1
- package/dist/app/chat/AgentPanel.js.map +1 -1
- package/dist/app/chat/AgentSidebar.d.ts +7 -1
- package/dist/app/chat/AgentSidebar.d.ts.map +1 -1
- package/dist/app/chat/AgentSidebar.js +33 -11
- package/dist/app/chat/AgentSidebar.js.map +1 -1
- package/dist/app/chat/chat/surface-types.d.ts +26 -26
- package/dist/app/chat/chat/surface-types.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.d.ts +2 -0
- package/dist/app/sharing/ShareButton.d.ts.map +1 -1
- package/dist/app/sharing/ShareButton.js +4 -3
- package/dist/app/sharing/ShareButton.js.map +1 -1
- package/dist/composer/TiptapComposer.js +3 -0
- package/dist/composer/TiptapComposer.js.map +1 -1
- package/dist/sharing/ShareControls.d.ts +2 -0
- package/dist/sharing/ShareControls.d.ts.map +1 -1
- package/dist/sharing/ShareControls.js +7 -2
- package/dist/sharing/ShareControls.js.map +1 -1
- package/package.json +4 -4
- package/src/app/chat/AgentPanel.header.spec.ts +4 -1
- package/src/app/chat/AgentPanel.tsx +19 -14
- package/src/app/chat/AgentSidebar.spec.tsx +108 -0
- package/src/app/chat/AgentSidebar.tsx +151 -96
- package/src/app/chat/chat/surface-types.ts +35 -31
- package/src/app/sharing/ShareButton.tsx +10 -3
- package/src/composer/TiptapComposer.tsx +2 -0
- package/src/composer/TiptapComposer.voice-toolbar.spec.tsx +16 -0
- package/src/sharing/ShareControls.tsx +44 -24
|
@@ -73,7 +73,7 @@ import React, {
|
|
|
73
73
|
lazy,
|
|
74
74
|
Suspense,
|
|
75
75
|
} from "react";
|
|
76
|
-
import { flushSync } from "react-dom";
|
|
76
|
+
import { createPortal, flushSync } from "react-dom";
|
|
77
77
|
|
|
78
78
|
import { setComposerReferenceEventTarget } from "../../composer/runtime-adapters.js";
|
|
79
79
|
import { Skeleton } from "../../ui/skeleton.js";
|
|
@@ -296,6 +296,12 @@ export interface AgentSidebarProps {
|
|
|
296
296
|
forceOverlay?: boolean;
|
|
297
297
|
defaultOpen?: boolean;
|
|
298
298
|
disableChatShortcut?: boolean;
|
|
299
|
+
/**
|
|
300
|
+
* Draws the panel inside this element instead of beside the page, for an app
|
|
301
|
+
* whose own sidebar holds the agent. The app then owns the panel's width,
|
|
302
|
+
* motion and close button, and the key that toggles the sidebar.
|
|
303
|
+
*/
|
|
304
|
+
panelHost?: HTMLElement | null;
|
|
299
305
|
showCollapseButton?: boolean;
|
|
300
306
|
animateMobile?: boolean;
|
|
301
307
|
animateDesktop?: boolean;
|
|
@@ -376,6 +382,7 @@ export function AgentSidebar({
|
|
|
376
382
|
forceOverlay = false,
|
|
377
383
|
defaultOpen,
|
|
378
384
|
disableChatShortcut = false,
|
|
385
|
+
panelHost,
|
|
379
386
|
showCollapseButton = true,
|
|
380
387
|
animateMobile = true,
|
|
381
388
|
animateDesktop = true,
|
|
@@ -644,9 +651,18 @@ export function AgentSidebar({
|
|
|
644
651
|
!isPerAppChatHosted &&
|
|
645
652
|
!presentationMode &&
|
|
646
653
|
(!frameCodeMode || !shouldParentFrameOwnAgentPanel());
|
|
654
|
+
const hostedPanel = ownsPanel && panelHost ? panelHost : null;
|
|
655
|
+
// A host switches between the agent and its other sections, so the chat
|
|
656
|
+
// stays mounted behind them once it has opened.
|
|
657
|
+
const [hostedPanelOpened, setHostedPanelOpened] = useState(false);
|
|
658
|
+
useEffect(() => {
|
|
659
|
+
if (!hostedPanel) setHostedPanelOpened(false);
|
|
660
|
+
else if (open) setHostedPanelOpened(true);
|
|
661
|
+
}, [hostedPanel, open]);
|
|
647
662
|
const shouldMountPanel =
|
|
648
663
|
ownsPanel &&
|
|
649
664
|
(open ||
|
|
665
|
+
hostedPanelOpened ||
|
|
650
666
|
backgroundPanelActive ||
|
|
651
667
|
runningTabIds.size > 0 ||
|
|
652
668
|
hasPendingPanelEvents);
|
|
@@ -978,10 +994,12 @@ export function AgentSidebar({
|
|
|
978
994
|
: !presentationMode && (frameOwned ? frameSidebarOpen : open)),
|
|
979
995
|
source: frameOwned ? "frame" : "app",
|
|
980
996
|
mode: frameOwned ? "code" : "app",
|
|
997
|
+
hosted: Boolean(hostedPanel),
|
|
981
998
|
});
|
|
982
999
|
}, [
|
|
983
1000
|
frameCodeMode,
|
|
984
1001
|
frameSidebarOpen,
|
|
1002
|
+
hostedPanel,
|
|
985
1003
|
open,
|
|
986
1004
|
presentationMode,
|
|
987
1005
|
hasFrameSidebarState,
|
|
@@ -1468,14 +1486,22 @@ export function AgentSidebar({
|
|
|
1468
1486
|
useEffect(() => {
|
|
1469
1487
|
if (!enabled) return;
|
|
1470
1488
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
1471
|
-
|
|
1489
|
+
// A host's element that isn't on the page yet, such as one a loading
|
|
1490
|
+
// page's skeleton holds, has no panel to answer ⌘\, so the shell does.
|
|
1491
|
+
if (!panelHost?.isConnected && isAgentSidebarToggleShortcut(e)) {
|
|
1472
1492
|
e.preventDefault();
|
|
1473
1493
|
window.dispatchEvent(
|
|
1474
1494
|
new CustomEvent("agent-panel:toggle", { detail: { focus: true } }),
|
|
1475
1495
|
);
|
|
1476
1496
|
return;
|
|
1477
1497
|
}
|
|
1478
|
-
|
|
1498
|
+
// A Mac reports ⇧⌘I with a lowercase key, and a hosting page's panel
|
|
1499
|
+
// owns that key.
|
|
1500
|
+
if (
|
|
1501
|
+
!disableChatShortcut &&
|
|
1502
|
+
isAgentPanelChatShortcut(e) &&
|
|
1503
|
+
!(panelHost && e.shiftKey)
|
|
1504
|
+
) {
|
|
1479
1505
|
e.preventDefault();
|
|
1480
1506
|
let selectionText = "";
|
|
1481
1507
|
try {
|
|
@@ -1500,7 +1526,7 @@ export function AgentSidebar({
|
|
|
1500
1526
|
};
|
|
1501
1527
|
document.addEventListener("keydown", handleKeyDown);
|
|
1502
1528
|
return () => document.removeEventListener("keydown", handleKeyDown);
|
|
1503
|
-
}, [disableChatShortcut, enabled]);
|
|
1529
|
+
}, [disableChatShortcut, enabled, panelHost]);
|
|
1504
1530
|
|
|
1505
1531
|
useEffect(() => {
|
|
1506
1532
|
const handler = (event: MessageEvent) => {
|
|
@@ -1602,12 +1628,12 @@ export function AgentSidebar({
|
|
|
1602
1628
|
}, [persistWidth]);
|
|
1603
1629
|
|
|
1604
1630
|
const isLeft = effectivePosition === "left";
|
|
1605
|
-
const isOverlay = isMobile || forceOverlay;
|
|
1606
|
-
const wideDrawerEnabled = isWideDrawer && !isOverlay;
|
|
1631
|
+
const isOverlay = !hostedPanel && (isMobile || forceOverlay);
|
|
1632
|
+
const wideDrawerEnabled = isWideDrawer && !isOverlay && !hostedPanel;
|
|
1607
1633
|
const mobileAnimationEnabled =
|
|
1608
1634
|
!presentationMode && isOverlay && animateMobile;
|
|
1609
1635
|
const desktopAnimationEnabled =
|
|
1610
|
-
!presentationMode && !isOverlay && effectiveAnimateDesktop;
|
|
1636
|
+
!presentationMode && !isOverlay && !hostedPanel && effectiveAnimateDesktop;
|
|
1611
1637
|
const sidebarAnimationEnabled =
|
|
1612
1638
|
mobileAnimationEnabled || desktopAnimationEnabled;
|
|
1613
1639
|
const [renderAnimatedPanel, setRenderAnimatedPanel] =
|
|
@@ -1713,7 +1739,119 @@ export function AgentSidebar({
|
|
|
1713
1739
|
};
|
|
1714
1740
|
}
|
|
1715
1741
|
|
|
1716
|
-
const
|
|
1742
|
+
const panelBody = (
|
|
1743
|
+
<LazyChunkErrorBoundary
|
|
1744
|
+
fallback={<LazyChunkRetryFallback />}
|
|
1745
|
+
shouldHandleError={isPanelImportError}
|
|
1746
|
+
onError={onPanelLoadFailed}
|
|
1747
|
+
>
|
|
1748
|
+
<Suspense
|
|
1749
|
+
fallback={
|
|
1750
|
+
<div
|
|
1751
|
+
aria-hidden="true"
|
|
1752
|
+
className="flex min-h-0 flex-1 flex-col gap-3 p-3"
|
|
1753
|
+
>
|
|
1754
|
+
<Skeleton className="h-8 w-full" />
|
|
1755
|
+
<div className="flex-1" />
|
|
1756
|
+
<Skeleton className="h-28 w-full rounded-xl" />
|
|
1757
|
+
</div>
|
|
1758
|
+
}
|
|
1759
|
+
>
|
|
1760
|
+
<AgentSidebarPanel
|
|
1761
|
+
onReadyChange={onPanelReadyChange}
|
|
1762
|
+
onReferenceTargetChange={onReferenceTargetChange}
|
|
1763
|
+
onNavigationChange={onNavigationChange}
|
|
1764
|
+
onTabsClosing={onTabsClosing}
|
|
1765
|
+
emptyStateText={emptyStateText}
|
|
1766
|
+
mcpSuggestionExcludedIntegrationIds={
|
|
1767
|
+
mcpSuggestionExcludedIntegrationIds
|
|
1768
|
+
}
|
|
1769
|
+
suggestions={suggestions}
|
|
1770
|
+
dynamicSuggestions={dynamicSuggestions}
|
|
1771
|
+
suggestionPlacement="context-chips"
|
|
1772
|
+
composerToolbarSlot={composerToolbarSlot}
|
|
1773
|
+
composerSlot={composerSlot}
|
|
1774
|
+
// A host's tab opens the chat without taking focus from the tab.
|
|
1775
|
+
{...(hostedPanel ? { composerAutoFocus: false } : {})}
|
|
1776
|
+
composerContextProvider={composerContextProvider}
|
|
1777
|
+
onComposerTextChange={onComposerTextChange}
|
|
1778
|
+
imageModelMenu={imageModelMenu}
|
|
1779
|
+
availableAgents={effectiveAvailableAgents}
|
|
1780
|
+
availableModels={availableModels}
|
|
1781
|
+
modelListLoading={modelListLoading}
|
|
1782
|
+
selectedAgent={effectiveSelectedAgent}
|
|
1783
|
+
onAgentChange={effectiveOnAgentChange}
|
|
1784
|
+
hostedHarness={hostedHarnessEnabled}
|
|
1785
|
+
onConnectProvider={onConnectProvider}
|
|
1786
|
+
builderConnectTransport={builderConnectTransport}
|
|
1787
|
+
onConnectLocalRuntime={onConnectLocalRuntime}
|
|
1788
|
+
runtime={runtime}
|
|
1789
|
+
adapterReloadKey={adapterReloadKey}
|
|
1790
|
+
threadFooterSlot={threadFooterSlot}
|
|
1791
|
+
apiUrl={apiUrl}
|
|
1792
|
+
agentChatSurface={agentChatSurface}
|
|
1793
|
+
desktopIdentityUnauthenticated={desktopIdentityUnauthenticated}
|
|
1794
|
+
desktopIdentityAuthenticated={desktopIdentityAuthenticated}
|
|
1795
|
+
showTabBar={effectiveShowTabBar}
|
|
1796
|
+
suppressInlineOpenApp={suppressInlineOpenApp}
|
|
1797
|
+
composerPlaceholder={composerPlaceholder}
|
|
1798
|
+
showMissingApiKeySetup={showMissingApiKeySetup}
|
|
1799
|
+
setupCardOwner={setupCardOwner}
|
|
1800
|
+
showGuidedQuestions={showGuidedQuestions}
|
|
1801
|
+
missingApiKeySetupLayout="sidebar"
|
|
1802
|
+
defaultMode={defaultMode}
|
|
1803
|
+
onCollapse={() => setOpenPersisted(false)}
|
|
1804
|
+
showCollapseButton={!hostedPanel && showCollapseButton}
|
|
1805
|
+
onSnapTo75Percent={
|
|
1806
|
+
isOverlay || hostedPanel ? undefined : snapTo75Percent
|
|
1807
|
+
}
|
|
1808
|
+
isWideDrawer={wideDrawerEnabled}
|
|
1809
|
+
onExitWideDrawer={
|
|
1810
|
+
isOverlay || hostedPanel ? undefined : exitWideDrawer
|
|
1811
|
+
}
|
|
1812
|
+
onFullViewRequest={onFullscreenRequest}
|
|
1813
|
+
onOpenSettings={onOpenSettings}
|
|
1814
|
+
onNewCliTab={onNewCliTab}
|
|
1815
|
+
onNewUiTab={onNewUiTab}
|
|
1816
|
+
renderCliTab={renderCliTab}
|
|
1817
|
+
newTabMode={newTabMode}
|
|
1818
|
+
newCliTabLabel={newCliTabLabel}
|
|
1819
|
+
newUiTabLabel={newUiTabLabel}
|
|
1820
|
+
storageKey={storageKey}
|
|
1821
|
+
restoreActiveThread={restoreActiveThread}
|
|
1822
|
+
scope={scope}
|
|
1823
|
+
{...(chatHistory ? { chatHistory } : {})}
|
|
1824
|
+
isolateHistoryByScope={isolateHistoryByScope}
|
|
1825
|
+
showScopeBadge={showScopeBadge}
|
|
1826
|
+
browserTabId={resolvedBrowserTabId}
|
|
1827
|
+
threadUrlSync={threadUrlSync}
|
|
1828
|
+
agentPageHref={agentPageHref}
|
|
1829
|
+
thinkingDisplay={thinkingDisplay}
|
|
1830
|
+
showModelSelector={showModelSelector}
|
|
1831
|
+
chatOnly={chatOnly}
|
|
1832
|
+
/>
|
|
1833
|
+
</Suspense>
|
|
1834
|
+
</LazyChunkErrorBoundary>
|
|
1835
|
+
);
|
|
1836
|
+
|
|
1837
|
+
const sidebar = !shouldRenderPanel ? null : hostedPanel ? (
|
|
1838
|
+
createPortal(
|
|
1839
|
+
<div
|
|
1840
|
+
className="agent-sidebar-panel agent-kit-density flex min-h-0 flex-1 flex-col overflow-hidden"
|
|
1841
|
+
data-agent-sidebar-layout="hosted"
|
|
1842
|
+
data-agent-sidebar-state={panelOpen ? "open" : "closed"}
|
|
1843
|
+
inert={!panelOpen || undefined}
|
|
1844
|
+
>
|
|
1845
|
+
<div
|
|
1846
|
+
ref={panelElementRef}
|
|
1847
|
+
className="agent-sidebar-panel-inner relative flex min-h-0 flex-1 flex-col"
|
|
1848
|
+
>
|
|
1849
|
+
{panelBody}
|
|
1850
|
+
</div>
|
|
1851
|
+
</div>,
|
|
1852
|
+
hostedPanel,
|
|
1853
|
+
)
|
|
1854
|
+
) : (
|
|
1717
1855
|
<>
|
|
1718
1856
|
{showResizeHandle && !isLeft && (
|
|
1719
1857
|
<ResizeHandle
|
|
@@ -1766,92 +1904,7 @@ export function AgentSidebar({
|
|
|
1766
1904
|
ref={panelElementRef}
|
|
1767
1905
|
className="agent-sidebar-panel-inner relative flex min-h-0 flex-1 flex-col"
|
|
1768
1906
|
>
|
|
1769
|
-
|
|
1770
|
-
fallback={<LazyChunkRetryFallback />}
|
|
1771
|
-
shouldHandleError={isPanelImportError}
|
|
1772
|
-
onError={onPanelLoadFailed}
|
|
1773
|
-
>
|
|
1774
|
-
<Suspense
|
|
1775
|
-
fallback={
|
|
1776
|
-
<div
|
|
1777
|
-
aria-hidden="true"
|
|
1778
|
-
className="flex min-h-0 flex-1 flex-col gap-3 p-3"
|
|
1779
|
-
>
|
|
1780
|
-
<Skeleton className="h-8 w-full" />
|
|
1781
|
-
<div className="flex-1" />
|
|
1782
|
-
<Skeleton className="h-28 w-full rounded-xl" />
|
|
1783
|
-
</div>
|
|
1784
|
-
}
|
|
1785
|
-
>
|
|
1786
|
-
<AgentSidebarPanel
|
|
1787
|
-
onReadyChange={onPanelReadyChange}
|
|
1788
|
-
onReferenceTargetChange={onReferenceTargetChange}
|
|
1789
|
-
onNavigationChange={onNavigationChange}
|
|
1790
|
-
onTabsClosing={onTabsClosing}
|
|
1791
|
-
emptyStateText={emptyStateText}
|
|
1792
|
-
mcpSuggestionExcludedIntegrationIds={
|
|
1793
|
-
mcpSuggestionExcludedIntegrationIds
|
|
1794
|
-
}
|
|
1795
|
-
suggestions={suggestions}
|
|
1796
|
-
dynamicSuggestions={dynamicSuggestions}
|
|
1797
|
-
suggestionPlacement="context-chips"
|
|
1798
|
-
composerToolbarSlot={composerToolbarSlot}
|
|
1799
|
-
composerSlot={composerSlot}
|
|
1800
|
-
composerContextProvider={composerContextProvider}
|
|
1801
|
-
onComposerTextChange={onComposerTextChange}
|
|
1802
|
-
imageModelMenu={imageModelMenu}
|
|
1803
|
-
availableAgents={effectiveAvailableAgents}
|
|
1804
|
-
availableModels={availableModels}
|
|
1805
|
-
modelListLoading={modelListLoading}
|
|
1806
|
-
selectedAgent={effectiveSelectedAgent}
|
|
1807
|
-
onAgentChange={effectiveOnAgentChange}
|
|
1808
|
-
hostedHarness={hostedHarnessEnabled}
|
|
1809
|
-
onConnectProvider={onConnectProvider}
|
|
1810
|
-
builderConnectTransport={builderConnectTransport}
|
|
1811
|
-
onConnectLocalRuntime={onConnectLocalRuntime}
|
|
1812
|
-
runtime={runtime}
|
|
1813
|
-
adapterReloadKey={adapterReloadKey}
|
|
1814
|
-
threadFooterSlot={threadFooterSlot}
|
|
1815
|
-
apiUrl={apiUrl}
|
|
1816
|
-
agentChatSurface={agentChatSurface}
|
|
1817
|
-
desktopIdentityUnauthenticated={desktopIdentityUnauthenticated}
|
|
1818
|
-
desktopIdentityAuthenticated={desktopIdentityAuthenticated}
|
|
1819
|
-
showTabBar={effectiveShowTabBar}
|
|
1820
|
-
suppressInlineOpenApp={suppressInlineOpenApp}
|
|
1821
|
-
composerPlaceholder={composerPlaceholder}
|
|
1822
|
-
showMissingApiKeySetup={showMissingApiKeySetup}
|
|
1823
|
-
setupCardOwner={setupCardOwner}
|
|
1824
|
-
showGuidedQuestions={showGuidedQuestions}
|
|
1825
|
-
missingApiKeySetupLayout="sidebar"
|
|
1826
|
-
defaultMode={defaultMode}
|
|
1827
|
-
onCollapse={() => setOpenPersisted(false)}
|
|
1828
|
-
showCollapseButton={showCollapseButton}
|
|
1829
|
-
onSnapTo75Percent={isOverlay ? undefined : snapTo75Percent}
|
|
1830
|
-
isWideDrawer={isOverlay ? false : isWideDrawer}
|
|
1831
|
-
onExitWideDrawer={isOverlay ? undefined : exitWideDrawer}
|
|
1832
|
-
onFullViewRequest={onFullscreenRequest}
|
|
1833
|
-
onOpenSettings={onOpenSettings}
|
|
1834
|
-
onNewCliTab={onNewCliTab}
|
|
1835
|
-
onNewUiTab={onNewUiTab}
|
|
1836
|
-
renderCliTab={renderCliTab}
|
|
1837
|
-
newTabMode={newTabMode}
|
|
1838
|
-
newCliTabLabel={newCliTabLabel}
|
|
1839
|
-
newUiTabLabel={newUiTabLabel}
|
|
1840
|
-
storageKey={storageKey}
|
|
1841
|
-
restoreActiveThread={restoreActiveThread}
|
|
1842
|
-
scope={scope}
|
|
1843
|
-
chatHistory={chatHistory}
|
|
1844
|
-
isolateHistoryByScope={isolateHistoryByScope}
|
|
1845
|
-
showScopeBadge={showScopeBadge}
|
|
1846
|
-
browserTabId={resolvedBrowserTabId}
|
|
1847
|
-
threadUrlSync={threadUrlSync}
|
|
1848
|
-
agentPageHref={agentPageHref}
|
|
1849
|
-
thinkingDisplay={thinkingDisplay}
|
|
1850
|
-
showModelSelector={showModelSelector}
|
|
1851
|
-
chatOnly={chatOnly}
|
|
1852
|
-
/>
|
|
1853
|
-
</Suspense>
|
|
1854
|
-
</LazyChunkErrorBoundary>
|
|
1907
|
+
{panelBody}
|
|
1855
1908
|
</div>
|
|
1856
1909
|
</div>
|
|
1857
1910
|
{showResizeHandle && isLeft && (
|
|
@@ -1866,7 +1919,7 @@ export function AgentSidebar({
|
|
|
1866
1919
|
/>
|
|
1867
1920
|
)}
|
|
1868
1921
|
</>
|
|
1869
|
-
)
|
|
1922
|
+
);
|
|
1870
1923
|
|
|
1871
1924
|
const drawerPlaceholder =
|
|
1872
1925
|
wideDrawerEnabled && !presentationMode && panelOpen ? (
|
|
@@ -1928,7 +1981,9 @@ export function AgentSidebar({
|
|
|
1928
1981
|
className="agent-sidebar-main-surface flex flex-1 flex-col overflow-auto min-w-0"
|
|
1929
1982
|
data-agent-sidebar-main-position={effectivePosition}
|
|
1930
1983
|
data-agent-sidebar-main-state={
|
|
1931
|
-
!isOverlay && !presentationMode && panelOpen
|
|
1984
|
+
!isOverlay && !hostedPanel && !presentationMode && panelOpen
|
|
1985
|
+
? "open"
|
|
1986
|
+
: "closed"
|
|
1932
1987
|
}
|
|
1933
1988
|
data-agent-sidebar-resizing={isResizing ? "true" : undefined}
|
|
1934
1989
|
>
|
|
@@ -195,7 +195,7 @@ export interface AssistantChatAdapterContext {
|
|
|
195
195
|
|
|
196
196
|
export interface AssistantChatProps {
|
|
197
197
|
/** API endpoint URL. Default: "/_agent-native/agent-chat" */
|
|
198
|
-
apiUrl?: string;
|
|
198
|
+
apiUrl?: string | undefined;
|
|
199
199
|
/** Optional Nitro response-streaming endpoint, usually supplied by VITE_AGENT_NATIVE_AGENT_CHAT_STREAM_URL. */
|
|
200
200
|
streamingUrl?: string;
|
|
201
201
|
/** Stable tab identifier passed to the adapter for event correlation */
|
|
@@ -218,21 +218,21 @@ export interface AssistantChatProps {
|
|
|
218
218
|
* Identifies which surface hosts this chat. Defaults to "app", which keeps
|
|
219
219
|
* dev filesystem/bash code-editing tools out of in-product sidebars.
|
|
220
220
|
*/
|
|
221
|
-
agentChatSurface?: AgentChatSurfaceKind;
|
|
221
|
+
agentChatSurface?: AgentChatSurfaceKind | undefined;
|
|
222
222
|
/** Whether the desktop host is currently showing its unauthenticated identity gate. */
|
|
223
|
-
desktopIdentityUnauthenticated?: boolean;
|
|
223
|
+
desktopIdentityUnauthenticated?: boolean | undefined;
|
|
224
224
|
/** Whether the desktop host has just established its authenticated identity session. */
|
|
225
|
-
desktopIdentityAuthenticated?: boolean;
|
|
225
|
+
desktopIdentityAuthenticated?: boolean | undefined;
|
|
226
226
|
/** Route completed first-party open_app calls through the host app pane. */
|
|
227
|
-
suppressInlineOpenApp?: boolean;
|
|
227
|
+
suppressInlineOpenApp?: boolean | undefined;
|
|
228
228
|
/** Placeholder text for empty state */
|
|
229
|
-
emptyStateText?: string;
|
|
229
|
+
emptyStateText?: string | undefined;
|
|
230
230
|
/** Initial empty-thread starters. Completed turns use the runtime's model-authored suggestions. */
|
|
231
|
-
suggestions?: AgentSuggestionInput[];
|
|
231
|
+
suggestions?: AgentSuggestionInput[] | undefined;
|
|
232
232
|
/** Initial empty-thread suggestion behavior; never disables or replaces runtime follow-ups. */
|
|
233
|
-
dynamicSuggestions?: AgentDynamicSuggestionsOption;
|
|
233
|
+
dynamicSuggestions?: AgentDynamicSuggestionsOption | undefined;
|
|
234
234
|
/** Exclude providers whose connection flow is handled by the host app. */
|
|
235
|
-
mcpSuggestionExcludedIntegrationIds?: readonly string[];
|
|
235
|
+
mcpSuggestionExcludedIntegrationIds?: readonly string[] | undefined;
|
|
236
236
|
/** Where suggestions appear. Context chips use a next-action bar at the thread base. */
|
|
237
237
|
suggestionPlacement?:
|
|
238
238
|
| "empty-state"
|
|
@@ -298,7 +298,9 @@ export interface AssistantChatProps {
|
|
|
298
298
|
/** Optional ordering policy for assistant message text and attached widgets. */
|
|
299
299
|
resolveMessageContentOrder?: AgentMessageContentOrderResolver;
|
|
300
300
|
/** App-owned context controller, mounted separately for each chat thread. */
|
|
301
|
-
composerContextProvider?:
|
|
301
|
+
composerContextProvider?:
|
|
302
|
+
| React.ComponentType<AssistantChatComposerContextProviderProps>
|
|
303
|
+
| undefined;
|
|
302
304
|
/** Optional home content rendered above the composer on an empty chat. */
|
|
303
305
|
homeIntroSlot?: React.ReactNode;
|
|
304
306
|
/** Optional content rendered below the composer on an empty chat. */
|
|
@@ -309,17 +311,17 @@ export interface AssistantChatProps {
|
|
|
309
311
|
* Host apps can use this to render contextual, non-destructive affordances
|
|
310
312
|
* beside the shared composer without replacing the composer stack.
|
|
311
313
|
*/
|
|
312
|
-
onComposerTextChange?: (text: string) => void;
|
|
314
|
+
onComposerTextChange?: ((text: string) => void) | undefined;
|
|
313
315
|
/** Class applied to the shared composer area for host-specific sizing/skin. */
|
|
314
316
|
composerAreaClassName?: string;
|
|
315
317
|
/** Placeholder for the shared composer in its normal idle state. */
|
|
316
|
-
composerPlaceholder?: string;
|
|
318
|
+
composerPlaceholder?: string | undefined;
|
|
317
319
|
/** Controls the compactness of the provider setup panel attached above the composer. */
|
|
318
320
|
missingApiKeySetupLayout?: BuilderSetupCardLayout;
|
|
319
321
|
/** Hide the provider setup panel when another host surface owns that prompt. */
|
|
320
|
-
showMissingApiKeySetup?: boolean;
|
|
322
|
+
showMissingApiKeySetup?: boolean | undefined;
|
|
321
323
|
/** Keep chat-owned recovery cards hidden when the host renders the setup card. */
|
|
322
|
-
setupCardOwner?: "chat" | "host";
|
|
324
|
+
setupCardOwner?: "chat" | "host" | undefined;
|
|
323
325
|
/** Visual density for the shared composer shell. */
|
|
324
326
|
composerLayoutVariant?: AgentComposerLayoutVariant;
|
|
325
327
|
/** Center the composer on a fresh empty chat instead of pinning it low. */
|
|
@@ -331,7 +333,7 @@ export interface AssistantChatProps {
|
|
|
331
333
|
/** Optional action rendered beside the voice/send controls. */
|
|
332
334
|
composerExtraActionButton?: React.ReactNode;
|
|
333
335
|
/** Show the framework model picker in the shared composer. Defaults to true. */
|
|
334
|
-
showModelSelector?: boolean;
|
|
336
|
+
showModelSelector?: boolean | undefined;
|
|
335
337
|
/** Disable the composer for capability-gated surfaces while still showing history. */
|
|
336
338
|
composerDisabled?: boolean;
|
|
337
339
|
/** Block sending while keeping the composer editable. */
|
|
@@ -357,14 +359,16 @@ export interface AssistantChatProps {
|
|
|
357
359
|
/** Selected effort override for this conversation */
|
|
358
360
|
selectedEffort?: ReasoningEffort;
|
|
359
361
|
/** Available engine/model list for the model picker */
|
|
360
|
-
availableModels?:
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
362
|
+
availableModels?:
|
|
363
|
+
| Array<{
|
|
364
|
+
engine: string;
|
|
365
|
+
label: string;
|
|
366
|
+
models: string[];
|
|
367
|
+
configured: boolean;
|
|
368
|
+
}>
|
|
369
|
+
| undefined;
|
|
366
370
|
/** Whether the model list is still being resolved. */
|
|
367
|
-
modelListLoading?: boolean;
|
|
371
|
+
modelListLoading?: boolean | undefined;
|
|
368
372
|
/** Whether the model list failed to load. */
|
|
369
373
|
modelListError?: boolean;
|
|
370
374
|
/** Retry loading a host-managed model list. */
|
|
@@ -374,24 +378,24 @@ export interface AssistantChatProps {
|
|
|
374
378
|
/** Callback when user picks an effort from the picker */
|
|
375
379
|
onEffortChange?: (effort: ReasoningEffort) => void;
|
|
376
380
|
/** Local or hosted agent runtimes shown above the model list. */
|
|
377
|
-
availableAgents?: ComposerAgentOption[];
|
|
381
|
+
availableAgents?: ComposerAgentOption[] | undefined;
|
|
378
382
|
/** Selected agent runtime identifier. */
|
|
379
|
-
selectedAgent?: string;
|
|
383
|
+
selectedAgent?: string | undefined;
|
|
380
384
|
/** Mark the selected runtime as the hosted tools-only harness mode. */
|
|
381
385
|
hostedHarness?: boolean;
|
|
382
386
|
/** Callback when the user picks an agent runtime. */
|
|
383
|
-
onAgentChange?: (agent: string) => void;
|
|
387
|
+
onAgentChange?: ((agent: string) => void) | undefined;
|
|
384
388
|
/**
|
|
385
389
|
* Optional secondary model menu (e.g. an image-generation model) shown inside
|
|
386
390
|
* the composer's model picker. Opt-in; chat-only apps omit it.
|
|
387
391
|
*/
|
|
388
|
-
imageModelMenu?: ComposerImageModelMenu;
|
|
392
|
+
imageModelMenu?: ComposerImageModelMenu | undefined;
|
|
389
393
|
/** Callback when user clicks "Fork Chat" in the message actions menu */
|
|
390
394
|
onForkChat?: () => void | boolean | Promise<void | boolean>;
|
|
391
395
|
/** Override Builder/provider connect routing for embedded hosts. */
|
|
392
|
-
onConnectProvider?: () => void;
|
|
396
|
+
onConnectProvider?: (() => void) | undefined;
|
|
393
397
|
/** Route local runtime setup through the host's native bridge. */
|
|
394
|
-
onConnectLocalRuntime?: (engine: string) => void;
|
|
398
|
+
onConnectLocalRuntime?: ((engine: string) => void) | undefined;
|
|
395
399
|
/**
|
|
396
400
|
* Controls the shared composer + menu. Sidebar keeps the full menu by default;
|
|
397
401
|
* hosts without the sidebar provider stack can use upload-only.
|
|
@@ -400,13 +404,13 @@ export interface AssistantChatProps {
|
|
|
400
404
|
/** Replace the built-in transport with an AgentKit-native BYO transport. */
|
|
401
405
|
createTransport?: (context: AssistantChatAdapterContext) => AgentTransport;
|
|
402
406
|
/** Hide the default guided-question card when another host owns its flow. */
|
|
403
|
-
showGuidedQuestions?: boolean;
|
|
407
|
+
showGuidedQuestions?: boolean | undefined;
|
|
404
408
|
/**
|
|
405
409
|
* Bring-your-own agent runtime. When supplied, AssistantChat keeps the
|
|
406
410
|
* standard composer/transcript/tool rendering shell but sends turns through
|
|
407
411
|
* this runtime instead of the built-in Agent-Native SSE endpoint.
|
|
408
412
|
*/
|
|
409
|
-
runtime?: AgentChatRuntime;
|
|
413
|
+
runtime?: AgentChatRuntime | undefined;
|
|
410
414
|
/**
|
|
411
415
|
* Explicitly recreate an injected adapter or runtime when its identity
|
|
412
416
|
* changes. Omit for the production sidebar so parent rerenders do not reset
|
|
@@ -446,5 +450,5 @@ export interface AssistantChatProps {
|
|
|
446
450
|
* let the reader's own preference apply, which is what surfaces the in-chat
|
|
447
451
|
* control — a pinned mode hides it rather than leaving a dead menu item.
|
|
448
452
|
*/
|
|
449
|
-
thinkingDisplay?: ThinkingDisplay;
|
|
453
|
+
thinkingDisplay?: ThinkingDisplay | undefined;
|
|
450
454
|
}
|
|
@@ -130,6 +130,8 @@ export interface ShareButtonProps {
|
|
|
130
130
|
copiedLabel: string;
|
|
131
131
|
onCopy: () => Promise<boolean | void> | boolean | void;
|
|
132
132
|
size?: "default" | "sm";
|
|
133
|
+
/** `plain` draws Share as text, set apart from Copy link. */
|
|
134
|
+
appearance?: "joined" | "plain";
|
|
133
135
|
className?: string;
|
|
134
136
|
};
|
|
135
137
|
/** Tooltip for the Copy link button: which link it copies. */
|
|
@@ -413,10 +415,11 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
413
415
|
controller.handleShareTabChange,
|
|
414
416
|
]);
|
|
415
417
|
const triggerLabel = t("agentChat.share.share", { defaultValue: "Share" });
|
|
418
|
+
const plainQuickCopy = props.quickCopy?.appearance === "plain";
|
|
416
419
|
const trigger = (
|
|
417
420
|
<ShareTrigger
|
|
418
421
|
label={
|
|
419
|
-
props.quickCopy ? (
|
|
422
|
+
props.quickCopy && !plainQuickCopy ? (
|
|
420
423
|
<span className="flex items-center gap-2">
|
|
421
424
|
<IconUserPlus aria-hidden="true" />
|
|
422
425
|
<span>{props.triggerContent ?? triggerLabel}</span>
|
|
@@ -428,8 +431,10 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
428
431
|
className={props.triggerClassName}
|
|
429
432
|
aria-label={triggerLabel}
|
|
430
433
|
title={triggerLabel}
|
|
431
|
-
intent={props.quickCopy ? "primary" : undefined}
|
|
432
|
-
emphasis={
|
|
434
|
+
intent={props.quickCopy && !plainQuickCopy ? "primary" : undefined}
|
|
435
|
+
emphasis={
|
|
436
|
+
plainQuickCopy ? "ghost" : props.quickCopy ? "solid" : undefined
|
|
437
|
+
}
|
|
433
438
|
/>
|
|
434
439
|
);
|
|
435
440
|
if (isSmallScreen) {
|
|
@@ -442,6 +447,7 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
442
447
|
copiedLabel={props.quickCopy.copiedLabel}
|
|
443
448
|
onCopy={props.quickCopy.onCopy}
|
|
444
449
|
size={props.quickCopy.size}
|
|
450
|
+
appearance={props.quickCopy.appearance}
|
|
445
451
|
className={props.quickCopy.className}
|
|
446
452
|
/>
|
|
447
453
|
) : (
|
|
@@ -482,6 +488,7 @@ export function ShareButton(props: ShareButtonProps) {
|
|
|
482
488
|
copiedLabel={props.quickCopy.copiedLabel}
|
|
483
489
|
onCopy={props.quickCopy.onCopy}
|
|
484
490
|
size={props.quickCopy.size}
|
|
491
|
+
appearance={props.quickCopy.appearance}
|
|
485
492
|
className={props.quickCopy.className}
|
|
486
493
|
/>
|
|
487
494
|
</PopoverAnchor>
|
|
@@ -4824,6 +4824,8 @@ function TiptapComposerSurface({
|
|
|
4824
4824
|
(e.metaKey || e.ctrlKey) &&
|
|
4825
4825
|
!e.altKey;
|
|
4826
4826
|
if (isToggleCombo) {
|
|
4827
|
+
// A page that has already used the key keeps it.
|
|
4828
|
+
if (e.defaultPrevented) return;
|
|
4827
4829
|
e.preventDefault();
|
|
4828
4830
|
if (voiceChatActiveRef.current) return;
|
|
4829
4831
|
if (voice.state === "recording" || voice.state === "starting") {
|
|
@@ -559,6 +559,22 @@ describe("TiptapComposer voice chat", () => {
|
|
|
559
559
|
expect(voice.start).not.toHaveBeenCalled();
|
|
560
560
|
});
|
|
561
561
|
|
|
562
|
+
it("leaves the dictation key to a page that has already used it", async () => {
|
|
563
|
+
await act(async () => root.render(<Harness />));
|
|
564
|
+
|
|
565
|
+
const event = new KeyboardEvent("keydown", {
|
|
566
|
+
key: "m",
|
|
567
|
+
metaKey: true,
|
|
568
|
+
shiftKey: true,
|
|
569
|
+
cancelable: true,
|
|
570
|
+
});
|
|
571
|
+
event.preventDefault();
|
|
572
|
+
act(() => {
|
|
573
|
+
window.dispatchEvent(event);
|
|
574
|
+
});
|
|
575
|
+
expect(voice.start).not.toHaveBeenCalled();
|
|
576
|
+
});
|
|
577
|
+
|
|
562
578
|
it("shows a failed session as an amber composer notice with a retry", async () => {
|
|
563
579
|
activate("error");
|
|
564
580
|
realtime.errorMessage = "The realtime voice connection failed.";
|
|
@@ -13,6 +13,8 @@ export interface JoinedShareControlProps {
|
|
|
13
13
|
copiedLabel: string;
|
|
14
14
|
onCopy: () => Promise<boolean | void> | boolean | void;
|
|
15
15
|
size?: "default" | "sm";
|
|
16
|
+
/** `plain` sets the two apart, as quiet controls in a toolbar. */
|
|
17
|
+
appearance?: "joined" | "plain";
|
|
16
18
|
disabled?: boolean;
|
|
17
19
|
blocked?: boolean;
|
|
18
20
|
className?: string;
|
|
@@ -29,6 +31,7 @@ export const JoinedShareControl = forwardRef<
|
|
|
29
31
|
copiedLabel,
|
|
30
32
|
onCopy,
|
|
31
33
|
size = "default",
|
|
34
|
+
appearance = "joined",
|
|
32
35
|
disabled,
|
|
33
36
|
blocked,
|
|
34
37
|
className,
|
|
@@ -55,6 +58,46 @@ export const JoinedShareControl = forwardRef<
|
|
|
55
58
|
};
|
|
56
59
|
|
|
57
60
|
const label = copied ? copiedLabel : copyLabel;
|
|
61
|
+
const plain = appearance === "plain";
|
|
62
|
+
const copyButton = (
|
|
63
|
+
// A click closes a tooltip, so the confirmation holds it open.
|
|
64
|
+
<Tooltip {...(copied ? { open: true } : {})}>
|
|
65
|
+
<TooltipTrigger asChild>
|
|
66
|
+
<Button
|
|
67
|
+
type="button"
|
|
68
|
+
size="sm"
|
|
69
|
+
variant={plain ? "ghost" : "default"}
|
|
70
|
+
className={cn(
|
|
71
|
+
"w-8 shrink-0 px-0 shadow-none",
|
|
72
|
+
plain && "text-muted-foreground hover:text-foreground",
|
|
73
|
+
blocked && "opacity-50",
|
|
74
|
+
)}
|
|
75
|
+
aria-label={label}
|
|
76
|
+
disabled={disabled}
|
|
77
|
+
data-blocked={blocked ? "" : undefined}
|
|
78
|
+
onClick={() => void copy()}
|
|
79
|
+
>
|
|
80
|
+
{copied ? (
|
|
81
|
+
<IconCheck aria-hidden="true" />
|
|
82
|
+
) : (
|
|
83
|
+
<IconLink aria-hidden="true" />
|
|
84
|
+
)}
|
|
85
|
+
</Button>
|
|
86
|
+
</TooltipTrigger>
|
|
87
|
+
<TooltipContent side="bottom">{label}</TooltipContent>
|
|
88
|
+
</Tooltip>
|
|
89
|
+
);
|
|
90
|
+
if (plain) {
|
|
91
|
+
return (
|
|
92
|
+
<div
|
|
93
|
+
ref={ref}
|
|
94
|
+
className={cn("flex shrink-0 items-center gap-0.5", className)}
|
|
95
|
+
>
|
|
96
|
+
{trigger}
|
|
97
|
+
{copyButton}
|
|
98
|
+
</div>
|
|
99
|
+
);
|
|
100
|
+
}
|
|
58
101
|
const segmentHeightClass =
|
|
59
102
|
size === "sm"
|
|
60
103
|
? "[&>*:not([data-slot=button-group-separator]):not([data-button-group-ignore]):not(select[aria-hidden=true])]:!h-8"
|
|
@@ -69,30 +112,7 @@ export const JoinedShareControl = forwardRef<
|
|
|
69
112
|
)}
|
|
70
113
|
>
|
|
71
114
|
{trigger}
|
|
72
|
-
|
|
73
|
-
<TooltipTrigger asChild>
|
|
74
|
-
<Button
|
|
75
|
-
type="button"
|
|
76
|
-
size="sm"
|
|
77
|
-
variant="default"
|
|
78
|
-
className={cn(
|
|
79
|
-
"w-8 shrink-0 px-0 shadow-none",
|
|
80
|
-
blocked && "opacity-50",
|
|
81
|
-
)}
|
|
82
|
-
aria-label={label}
|
|
83
|
-
disabled={disabled}
|
|
84
|
-
data-blocked={blocked ? "" : undefined}
|
|
85
|
-
onClick={() => void copy()}
|
|
86
|
-
>
|
|
87
|
-
{copied ? (
|
|
88
|
-
<IconCheck aria-hidden="true" />
|
|
89
|
-
) : (
|
|
90
|
-
<IconLink aria-hidden="true" />
|
|
91
|
-
)}
|
|
92
|
-
</Button>
|
|
93
|
-
</TooltipTrigger>
|
|
94
|
-
<TooltipContent side="bottom">{label}</TooltipContent>
|
|
95
|
-
</Tooltip>
|
|
115
|
+
{copyButton}
|
|
96
116
|
</ButtonGroup>
|
|
97
117
|
);
|
|
98
118
|
});
|