@agent-native/toolkit 0.227.0 → 0.228.0-nightly-20261111004142
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/agent-native.eject.json +8 -0
- package/dist/app/agent-page/AgentTabFrame.d.ts +2 -1
- package/dist/app/agent-page/AgentTabFrame.d.ts.map +1 -1
- package/dist/app/agent-page/AgentTabFrame.js +3 -2
- package/dist/app/agent-page/AgentTabFrame.js.map +1 -1
- 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 +47 -16
- 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/extensions/EmbeddedExtension.d.ts.map +1 -1
- package/dist/app/extensions/EmbeddedExtension.js +6 -4
- package/dist/app/extensions/EmbeddedExtension.js.map +1 -1
- package/dist/app/extensions/ExtensionViewer.d.ts.map +1 -1
- package/dist/app/extensions/ExtensionViewer.js +6 -3
- package/dist/app/extensions/ExtensionViewer.js.map +1 -1
- package/dist/app/extensions/InlineExtensionFrame.d.ts.map +1 -1
- package/dist/app/extensions/InlineExtensionFrame.js +32 -28
- package/dist/app/extensions/InlineExtensionFrame.js.map +1 -1
- package/dist/app/extensions/use-extension-chat-submission.d.ts +14 -0
- package/dist/app/extensions/use-extension-chat-submission.d.ts.map +1 -0
- package/dist/app/extensions/use-extension-chat-submission.js +36 -0
- package/dist/app/extensions/use-extension-chat-submission.js.map +1 -0
- package/dist/app/i18n/catalogs/ar-SA.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ar-SA.js +16 -0
- package/dist/app/i18n/catalogs/ar-SA.js.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/de-DE.js +12 -0
- package/dist/app/i18n/catalogs/de-DE.js.map +1 -1
- package/dist/app/i18n/catalogs/en-US.d.ts +12 -0
- package/dist/app/i18n/catalogs/en-US.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/en-US.js +12 -0
- package/dist/app/i18n/catalogs/en-US.js.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/es-ES.js +13 -0
- package/dist/app/i18n/catalogs/es-ES.js.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/fr-FR.js +13 -0
- package/dist/app/i18n/catalogs/fr-FR.js.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/hi-IN.js +12 -0
- package/dist/app/i18n/catalogs/hi-IN.js.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ja-JP.js +12 -0
- package/dist/app/i18n/catalogs/ja-JP.js.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/ko-KR.js +12 -0
- package/dist/app/i18n/catalogs/ko-KR.js.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/pt-BR.js +13 -0
- package/dist/app/i18n/catalogs/pt-BR.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-CN.js +12 -0
- package/dist/app/i18n/catalogs/zh-CN.js.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.d.ts.map +1 -1
- package/dist/app/i18n/catalogs/zh-TW.js +12 -0
- package/dist/app/i18n/catalogs/zh-TW.js.map +1 -1
- package/dist/app/org/OrgSwitcher.d.ts.map +1 -1
- package/dist/app/org/OrgSwitcher.js +3 -2
- package/dist/app/org/OrgSwitcher.js.map +1 -1
- package/dist/app/resources/McpAccessSettings.d.ts +9 -2
- package/dist/app/resources/McpAccessSettings.d.ts.map +1 -1
- package/dist/app/resources/McpAccessSettings.js +264 -86
- package/dist/app/resources/McpAccessSettings.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.d.ts +6 -0
- package/dist/app/settings/api-keys/ApiKeyDialogs.d.ts.map +1 -1
- package/dist/app/settings/api-keys/ApiKeyDialogs.js +74 -2
- package/dist/app/settings/api-keys/ApiKeyDialogs.js.map +1 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.d.ts.map +1 -1
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js +23 -9
- package/dist/app/settings/api-keys/ApiKeysSettingsPage.js.map +1 -1
- package/dist/app/settings/api-keys/api-keys-client.d.ts +10 -1
- package/dist/app/settings/api-keys/api-keys-client.d.ts.map +1 -1
- package/dist/app/settings/api-keys/api-keys-client.js +19 -1
- package/dist/app/settings/api-keys/api-keys-client.js.map +1 -1
- package/dist/app/settings/shell/pages/mcp.d.ts +1 -1
- package/dist/app/settings/shell/pages/mcp.d.ts.map +1 -1
- package/dist/app/settings/shell/pages/mcp.js +26 -20
- package/dist/app/settings/shell/pages/mcp.js.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/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.d.ts.map +1 -1
- package/dist/ui/index.js +1 -0
- package/dist/ui/index.js.map +1 -1
- package/dist/ui/numbered-steps.d.ts +14 -0
- package/dist/ui/numbered-steps.d.ts.map +1 -0
- package/dist/ui/numbered-steps.js +19 -0
- package/dist/ui/numbered-steps.js.map +1 -0
- package/package.json +4 -4
- package/src/app/agent-page/AgentTabFrame.tsx +21 -10
- 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 +167 -0
- package/src/app/chat/AgentSidebar.tsx +170 -101
- package/src/app/chat/chat/surface-types.ts +35 -31
- package/src/app/extensions/EmbeddedExtension.height.spec.tsx +45 -0
- package/src/app/extensions/EmbeddedExtension.tsx +6 -3
- package/src/app/extensions/ExtensionViewer.spec.tsx +28 -0
- package/src/app/extensions/ExtensionViewer.tsx +6 -2
- package/src/app/extensions/InlineExtensionFrame.spec.tsx +23 -0
- package/src/app/extensions/InlineExtensionFrame.tsx +23 -15
- package/src/app/extensions/use-extension-chat-submission.tsx +101 -0
- package/src/app/i18n/catalogs/ar-SA.ts +17 -0
- package/src/app/i18n/catalogs/de-DE.ts +15 -0
- package/src/app/i18n/catalogs/en-US.ts +13 -0
- package/src/app/i18n/catalogs/es-ES.ts +16 -0
- package/src/app/i18n/catalogs/fr-FR.ts +16 -0
- package/src/app/i18n/catalogs/hi-IN.ts +13 -0
- package/src/app/i18n/catalogs/ja-JP.ts +15 -0
- package/src/app/i18n/catalogs/ko-KR.ts +14 -0
- package/src/app/i18n/catalogs/pt-BR.ts +15 -0
- package/src/app/i18n/catalogs/zh-CN.ts +13 -0
- package/src/app/i18n/catalogs/zh-TW.ts +13 -0
- package/src/app/integrations/IntegrationsPanel.spec.tsx +1 -1
- package/src/app/org/OrgSwitcher.spec.tsx +43 -0
- package/src/app/org/OrgSwitcher.tsx +29 -24
- package/src/app/resources/McpAccessSettings.spec.tsx +354 -278
- package/src/app/resources/McpAccessSettings.tsx +1012 -323
- package/src/app/settings/api-keys/ApiKeyDialogs.tsx +148 -1
- package/src/app/settings/api-keys/ApiKeysSettingsPage.spec.tsx +190 -0
- package/src/app/settings/api-keys/ApiKeysSettingsPage.tsx +40 -1
- package/src/app/settings/api-keys/api-keys-client.spec.ts +49 -0
- package/src/app/settings/api-keys/api-keys-client.ts +32 -0
- package/src/app/settings/shell/pages/app-group-pages.spec.tsx +31 -21
- package/src/app/settings/shell/pages/mcp.tsx +50 -42
- 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
- package/src/ui/index.ts +1 -0
- package/src/ui/numbered-steps.tsx +104 -0
|
@@ -18,7 +18,7 @@ vi.mock("@agent-native/core/client/i18n", async () => {
|
|
|
18
18
|
}
|
|
19
19
|
return text;
|
|
20
20
|
};
|
|
21
|
-
return { useT: () => t };
|
|
21
|
+
return { useT: () => t, useLocale: () => ({ locale: "en-US" }) };
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
const modelDefault = vi.hoisted(() => ({
|
|
@@ -85,15 +85,33 @@ vi.mock("../../../chat/index.js", () => ({
|
|
|
85
85
|
</button>
|
|
86
86
|
),
|
|
87
87
|
}));
|
|
88
|
-
vi.mock("../../../resources/
|
|
88
|
+
vi.mock("../../../resources/McpAccessSettings.js", () => ({
|
|
89
|
+
McpHostMark: () => <span />,
|
|
89
90
|
McpAccessSettings: ({
|
|
90
91
|
appName,
|
|
91
92
|
hideHeader,
|
|
93
|
+
helpText,
|
|
94
|
+
guideId,
|
|
95
|
+
onSelectGuide,
|
|
96
|
+
guideHref,
|
|
92
97
|
}: {
|
|
93
98
|
appName: string;
|
|
94
99
|
hideHeader: boolean;
|
|
100
|
+
helpText?: string;
|
|
101
|
+
guideId?: string | null;
|
|
102
|
+
onSelectGuide: (id: string) => void;
|
|
103
|
+
guideHref: (id: string) => string;
|
|
95
104
|
}) => (
|
|
96
|
-
<div
|
|
105
|
+
<div
|
|
106
|
+
data-testid="mcp"
|
|
107
|
+
data-app={appName}
|
|
108
|
+
data-hide-header={hideHeader}
|
|
109
|
+
data-help={helpText}
|
|
110
|
+
data-guide={guideId}
|
|
111
|
+
data-cursor-href={guideHref("cursor")}
|
|
112
|
+
>
|
|
113
|
+
<button onClick={() => onSelectGuide("cursor")}>Choose Cursor</button>
|
|
114
|
+
</div>
|
|
97
115
|
),
|
|
98
116
|
}));
|
|
99
117
|
const changelogCard = vi.hoisted(() => ({
|
|
@@ -563,35 +581,27 @@ describe("app group pages", () => {
|
|
|
563
581
|
});
|
|
564
582
|
|
|
565
583
|
describe("MCP server", () => {
|
|
566
|
-
it("
|
|
584
|
+
it("routes client choices through the Settings shell", async () => {
|
|
567
585
|
await renderPage(McpServerSettingsPage);
|
|
568
|
-
expect(container.textContent).toContain(
|
|
569
|
-
"Connect Clips to Claude, ChatGPT, Cursor, or any AI app that supports MCP.",
|
|
570
|
-
);
|
|
571
586
|
const mcp = container.querySelector('[data-testid="mcp"]');
|
|
572
587
|
expect(mcp?.getAttribute("data-app")).toBe("Clips");
|
|
573
588
|
expect(mcp?.getAttribute("data-hide-header")).toBe("true");
|
|
574
|
-
expect(
|
|
575
|
-
"
|
|
589
|
+
expect(mcp?.getAttribute("data-cursor-href")).toBe(
|
|
590
|
+
"/settings/mcp/cursor",
|
|
576
591
|
);
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
);
|
|
580
|
-
|
|
581
|
-
link?.dispatchEvent(
|
|
582
|
-
new MouseEvent("click", { bubbles: true, cancelable: true }),
|
|
583
|
-
);
|
|
584
|
-
});
|
|
585
|
-
expect(navigate).toHaveBeenCalledWith("integrations");
|
|
592
|
+
expect(mcp?.getAttribute("data-guide")).toBeNull();
|
|
593
|
+
expect(header).toBeNull();
|
|
594
|
+
act(() => container.querySelector<HTMLButtonElement>("button")?.click());
|
|
595
|
+
expect(navigate).toHaveBeenCalledWith("mcp", "cursor");
|
|
586
596
|
});
|
|
587
597
|
|
|
588
598
|
it("uses the app's own about line when it passes one", async () => {
|
|
589
599
|
await renderPage(McpServerSettingsPage, {
|
|
590
600
|
input: { mcpAbout: "Find recordings from any MCP app." },
|
|
591
601
|
});
|
|
592
|
-
expect(
|
|
593
|
-
|
|
594
|
-
);
|
|
602
|
+
expect(
|
|
603
|
+
container.querySelector("[data-testid=mcp]")?.getAttribute("data-help"),
|
|
604
|
+
).toBe("Find recordings from any MCP app.");
|
|
595
605
|
});
|
|
596
606
|
});
|
|
597
607
|
|
|
@@ -1,54 +1,62 @@
|
|
|
1
1
|
import { useT } from "@agent-native/core/client/i18n";
|
|
2
|
-
import
|
|
2
|
+
import { useLocale } from "@agent-native/core/client/i18n";
|
|
3
|
+
import {
|
|
4
|
+
getMcpConnectGuides,
|
|
5
|
+
resolveMcpConnectGuideId,
|
|
6
|
+
} from "@agent-native/core/shared/mcp-connect-content";
|
|
7
|
+
import { useEffect, useMemo } from "react";
|
|
3
8
|
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
9
|
+
import { BreadcrumbTitle } from "../../../integrations/IntegrationDetailParts.js";
|
|
10
|
+
import {
|
|
11
|
+
McpAccessSettings,
|
|
12
|
+
McpHostMark,
|
|
13
|
+
} from "../../../resources/McpAccessSettings.js";
|
|
14
|
+
import { useSettingsPageHeader, useSettingsShell } from "../context.js";
|
|
6
15
|
import type { SettingsPageProps } from "../registry.js";
|
|
7
16
|
import { settingsPageHref } from "../routing.js";
|
|
8
17
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
18
|
+
export default function McpServerSettingsPage({
|
|
19
|
+
bridge,
|
|
20
|
+
sub,
|
|
21
|
+
}: SettingsPageProps) {
|
|
13
22
|
const t = useT();
|
|
23
|
+
const { locale } = useLocale();
|
|
14
24
|
const { navigate } = useSettingsShell();
|
|
25
|
+
const guides = useMemo(() => getMcpConnectGuides(locale), [locale]);
|
|
26
|
+
const guide = sub
|
|
27
|
+
? (guides.find((item) => item.id === sub) ??
|
|
28
|
+
guides.find((item) => item.id === "other"))
|
|
29
|
+
: null;
|
|
30
|
+
const header = useMemo(
|
|
31
|
+
() =>
|
|
32
|
+
guide
|
|
33
|
+
? {
|
|
34
|
+
title: (
|
|
35
|
+
<BreadcrumbTitle
|
|
36
|
+
name={guide.label}
|
|
37
|
+
mark={<McpHostMark id={guide.id} className="size-4" />}
|
|
38
|
+
/>
|
|
39
|
+
),
|
|
40
|
+
}
|
|
41
|
+
: null,
|
|
42
|
+
[guide],
|
|
43
|
+
);
|
|
44
|
+
useSettingsPageHeader(header);
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
const query = new URLSearchParams(window.location.search).get("guide");
|
|
47
|
+
if (!sub && query)
|
|
48
|
+
navigate("mcp", resolveMcpConnectGuideId(query), { replace: true });
|
|
49
|
+
}, [navigate, sub]);
|
|
15
50
|
const appName =
|
|
16
51
|
bridge.appName ?? t("agentChat.settingsShell.appFallbackName");
|
|
17
|
-
const [beforeLink, afterLink = ""] = t(
|
|
18
|
-
"agentChat.settingsShell.appGroup.mcpFootnote",
|
|
19
|
-
{ integrations: LINK_TOKEN },
|
|
20
|
-
).split(LINK_TOKEN);
|
|
21
|
-
const openIntegrations = (event: MouseEvent<HTMLAnchorElement>) => {
|
|
22
|
-
if (
|
|
23
|
-
event.button !== 0 ||
|
|
24
|
-
event.metaKey ||
|
|
25
|
-
event.ctrlKey ||
|
|
26
|
-
event.shiftKey ||
|
|
27
|
-
event.altKey
|
|
28
|
-
) {
|
|
29
|
-
return;
|
|
30
|
-
}
|
|
31
|
-
event.preventDefault();
|
|
32
|
-
navigate("integrations");
|
|
33
|
-
};
|
|
34
52
|
return (
|
|
35
|
-
<
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<a
|
|
44
|
-
href={settingsPageHref("integrations")}
|
|
45
|
-
onClick={openIntegrations}
|
|
46
|
-
className="font-medium text-foreground underline-offset-4 hover:underline"
|
|
47
|
-
>
|
|
48
|
-
{t("agentChat.settingsShell.page.integrations")}
|
|
49
|
-
</a>
|
|
50
|
-
{afterLink}
|
|
51
|
-
</p>
|
|
52
|
-
</div>
|
|
53
|
+
<McpAccessSettings
|
|
54
|
+
appName={appName}
|
|
55
|
+
hideHeader
|
|
56
|
+
{...(bridge.mcpAbout != null ? { helpText: bridge.mcpAbout } : {})}
|
|
57
|
+
guideId={guide?.id ?? null}
|
|
58
|
+
onSelectGuide={(id) => navigate("mcp", id)}
|
|
59
|
+
guideHref={(id) => settingsPageHref("mcp", id)}
|
|
60
|
+
/>
|
|
53
61
|
);
|
|
54
62
|
}
|
|
@@ -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
|
});
|
package/src/ui/index.ts
CHANGED
|
@@ -32,6 +32,7 @@ export * from "./label.js";
|
|
|
32
32
|
export * from "./logo.js";
|
|
33
33
|
export * from "./menubar.js";
|
|
34
34
|
export * from "./navigation-menu.js";
|
|
35
|
+
export * from "./numbered-steps.js";
|
|
35
36
|
export * from "./pagination.js";
|
|
36
37
|
export * from "./pointer-lock.js";
|
|
37
38
|
export * from "./popover.js";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { Slot } from "@radix-ui/react-slot";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
|
|
4
|
+
import { cn } from "../utils.js";
|
|
5
|
+
|
|
6
|
+
export function NumberedSteps({ className, ...props }: ComponentProps<"ol">) {
|
|
7
|
+
return (
|
|
8
|
+
<ol
|
|
9
|
+
data-slot="numbered-steps"
|
|
10
|
+
role="list"
|
|
11
|
+
className={cn("my-6 list-none p-0", className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function NumberedStep({
|
|
18
|
+
number,
|
|
19
|
+
last = false,
|
|
20
|
+
showConnector = true,
|
|
21
|
+
variant = "outline",
|
|
22
|
+
contentClassName,
|
|
23
|
+
className,
|
|
24
|
+
children,
|
|
25
|
+
...props
|
|
26
|
+
}: ComponentProps<"li"> & {
|
|
27
|
+
number: number;
|
|
28
|
+
last?: boolean;
|
|
29
|
+
showConnector?: boolean;
|
|
30
|
+
variant?: "outline" | "secondary";
|
|
31
|
+
contentClassName?: string;
|
|
32
|
+
}) {
|
|
33
|
+
return (
|
|
34
|
+
<li
|
|
35
|
+
data-slot="numbered-step"
|
|
36
|
+
className={cn("flex items-stretch gap-4", className)}
|
|
37
|
+
{...props}
|
|
38
|
+
>
|
|
39
|
+
<div
|
|
40
|
+
data-slot="numbered-step-aside"
|
|
41
|
+
className={cn(
|
|
42
|
+
"flex shrink-0 flex-col items-center",
|
|
43
|
+
variant === "outline" && "pt-0.5",
|
|
44
|
+
)}
|
|
45
|
+
aria-hidden="true"
|
|
46
|
+
>
|
|
47
|
+
<span
|
|
48
|
+
data-slot="numbered-step-indicator"
|
|
49
|
+
className={cn(
|
|
50
|
+
"relative z-10 flex size-[26px] shrink-0 items-center justify-center rounded-full text-xs font-semibold leading-none",
|
|
51
|
+
variant === "secondary"
|
|
52
|
+
? "bg-secondary text-secondary-foreground"
|
|
53
|
+
: "border-[1.5px] border-primary bg-background text-primary",
|
|
54
|
+
)}
|
|
55
|
+
>
|
|
56
|
+
{number}
|
|
57
|
+
</span>
|
|
58
|
+
{!last && showConnector && (
|
|
59
|
+
<span
|
|
60
|
+
data-slot="numbered-step-connector"
|
|
61
|
+
className="my-1.5 min-h-6 w-[1.5px] flex-1 bg-border"
|
|
62
|
+
/>
|
|
63
|
+
)}
|
|
64
|
+
</div>
|
|
65
|
+
<div
|
|
66
|
+
data-slot="numbered-step-content"
|
|
67
|
+
className={cn("min-w-0 flex-1", !last && "pb-7", contentClassName)}
|
|
68
|
+
>
|
|
69
|
+
{children}
|
|
70
|
+
</div>
|
|
71
|
+
</li>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function NumberedStepTitle({
|
|
76
|
+
asChild = false,
|
|
77
|
+
className,
|
|
78
|
+
...props
|
|
79
|
+
}: ComponentProps<"h3"> & { asChild?: boolean }) {
|
|
80
|
+
const Comp = asChild ? Slot : "h3";
|
|
81
|
+
return (
|
|
82
|
+
<Comp
|
|
83
|
+
data-slot="numbered-step-title"
|
|
84
|
+
className={cn(
|
|
85
|
+
"mt-0.5 mb-2 text-sm font-semibold leading-normal text-foreground",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function NumberedStepBody({
|
|
94
|
+
className,
|
|
95
|
+
...props
|
|
96
|
+
}: ComponentProps<"div">) {
|
|
97
|
+
return (
|
|
98
|
+
<div
|
|
99
|
+
data-slot="numbered-step-body"
|
|
100
|
+
className={cn("text-sm leading-6 text-muted-foreground", className)}
|
|
101
|
+
{...props}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
}
|