@elabs-ai/components-ai 4.0.0 → 4.2.0
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 +62 -12
- package/dist/{_audio-player-media-chrome-KA5DY54G.js → _audio-player-media-chrome-T3XVXWRZ.js} +8 -4
- package/dist/_audio-player-media-chrome-T3XVXWRZ.js.map +1 -0
- package/dist/{_flow-boundary-D63PJ65S.js → _flow-boundary-SHNWLQG5.js} +32 -43
- package/dist/_flow-boundary-SHNWLQG5.js.map +1 -0
- package/dist/{_persona-rive-RFR2EUWP.js → _persona-rive-JEG44YHX.js} +9 -5
- package/dist/_persona-rive-JEG44YHX.js.map +1 -0
- package/dist/index.d.ts +1152 -218
- package/dist/index.js +5601 -3241
- package/dist/index.js.map +1 -1
- package/package.json +30 -15
- package/src/__contract__/audio-visualizer.contract.test.tsx +49 -0
- package/src/__contract__/chat-shell.contract.test.tsx +49 -0
- package/src/__contract__/grouped-parts.contract.test.tsx +49 -0
- package/src/__contract__/image.contract.test.tsx +49 -0
- package/src/__contract__/markdown-view.contract.test.tsx +49 -0
- package/src/__contract__/message-feedback.contract.test.tsx +49 -0
- package/src/__contract__/message-form.contract.test.tsx +49 -0
- package/src/__contract__/message-table.contract.test.tsx +49 -0
- package/src/__contract__/model-provider-logo.contract.test.tsx +49 -0
- package/src/__contract__/persona.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-effort.contract.test.tsx +49 -0
- package/src/__contract__/prompt-input-mode.contract.test.tsx +49 -0
- package/src/_audio-player-media-chrome.tsx +102 -15
- package/src/_chat-shell-rail.tsx +2 -2
- package/src/_flow-boundary.tsx +68 -49
- package/src/_lazy-boundary-conformance.ts +38 -0
- package/src/_lazy-cjk.test.ts +43 -0
- package/src/_lazy-cjk.ts +73 -0
- package/src/_lazy-engine-boundary.tsx +61 -0
- package/src/_lazy-math.test.ts +63 -0
- package/src/_lazy-math.ts +90 -0
- package/src/_lazy-mermaid-absent.test.ts +53 -0
- package/src/_lazy-mermaid.test.ts +15 -0
- package/src/_lazy-mermaid.ts +24 -1
- package/src/_mermaid-error-panel.test.tsx +50 -0
- package/src/_mermaid-error-panel.tsx +66 -0
- package/src/_persona-rive.tsx +62 -10
- package/src/_streamdown-i18n.ts +94 -22
- package/src/_streamdown-safety.ts +170 -0
- package/src/_theme-scope-store.test.ts +83 -0
- package/src/_theme-scope-store.ts +103 -0
- package/src/agent-event.stories.tsx +97 -0
- package/src/agent-event.test.tsx +145 -0
- package/src/agent-event.tsx +187 -0
- package/src/agent-timeline.stories.tsx +9 -1
- package/src/agent.test.tsx +47 -0
- package/src/agent.tsx +43 -24
- package/src/agentic-workspace.stories.tsx +27 -20
- package/src/artifact.tsx +17 -14
- package/src/asset-preview.test.tsx +40 -0
- package/src/asset-preview.tsx +83 -12
- package/src/attachments.tsx +7 -4
- package/src/audio-player.test.tsx +78 -0
- package/src/audio-player.tsx +243 -56
- package/src/audio-visualizer.stories.tsx +126 -0
- package/src/audio-visualizer.test.tsx +438 -0
- package/src/audio-visualizer.tsx +367 -0
- package/src/canvas.stories.tsx +150 -1
- package/src/chat-shell.stories.tsx +18 -3
- package/src/chat.stories.tsx +16 -2
- package/src/code-block.stories.tsx +9 -1
- package/src/code-block.test.tsx +100 -1
- package/src/code-block.tsx +166 -103
- package/src/commit.tsx +30 -41
- package/src/composer.stories.tsx +372 -9
- package/src/composer.test.tsx +357 -6
- package/src/composer.tsx +217 -35
- package/src/confirmation.stories.tsx +72 -1
- package/src/confirmation.test.tsx +216 -2
- package/src/confirmation.tsx +264 -4
- package/src/context-panel.stories.tsx +9 -1
- package/src/context-panel.tsx +3 -2
- package/src/conversation.stories.tsx +86 -2
- package/src/conversation.test.tsx +66 -0
- package/src/conversation.tsx +52 -9
- package/src/diff-view.stories.tsx +196 -0
- package/src/diff-view.test.tsx +238 -0
- package/src/diff-view.tsx +697 -0
- package/src/environment-variables.tsx +20 -37
- package/src/file-tree.test.tsx +21 -0
- package/src/file-tree.tsx +12 -2
- package/src/gallery.tsx +4 -2
- package/src/index.ts +14 -4
- package/src/inline-citation.tsx +5 -5
- package/src/jsx-preview.stories.tsx +2 -2
- package/src/jsx-preview.tsx +151 -43
- package/src/markdown-view.stories.tsx +92 -1
- package/src/markdown-view.test.tsx +237 -2
- package/src/markdown-view.tsx +157 -6
- package/src/message-compare.stories.tsx +175 -0
- package/src/message-compare.test.tsx +207 -0
- package/src/message-compare.tsx +453 -0
- package/src/message-form.stories.tsx +65 -4
- package/src/message-form.test.tsx +8 -2
- package/src/message-form.tsx +15 -7
- package/src/message-table.stories.tsx +2 -2
- package/src/message-table.test.tsx +7 -0
- package/src/message-table.tsx +8 -4
- package/src/message.stories.tsx +9 -1
- package/src/message.test.tsx +247 -1
- package/src/message.tsx +125 -15
- package/src/microcopy.test.tsx +40 -0
- package/src/{model-selector.stories.tsx → model-provider-logo.stories.tsx} +17 -8
- package/src/model-provider-logo.test.tsx +109 -0
- package/src/model-provider-logo.tsx +183 -0
- package/src/open-in-chat.tsx +50 -29
- package/src/package-info.tsx +12 -12
- package/src/permission-mode-select.stories.tsx +82 -0
- package/src/permission-mode-select.test.tsx +100 -0
- package/src/permission-mode-select.tsx +137 -0
- package/src/persona-missing-peer.test.tsx +54 -0
- package/src/persona.tsx +68 -22
- package/src/plan.stories.tsx +166 -0
- package/src/plan.test.tsx +267 -0
- package/src/plan.tsx +182 -20
- package/src/prompt-input-effort.stories.tsx +123 -0
- package/src/prompt-input-effort.test.tsx +83 -0
- package/src/prompt-input-effort.tsx +136 -0
- package/src/prompt-input-mode.stories.tsx +108 -0
- package/src/prompt-input-mode.test.tsx +99 -0
- package/src/prompt-input-mode.tsx +169 -0
- package/src/prompt-input-slash.stories.tsx +211 -0
- package/src/prompt-input-slash.test.tsx +262 -0
- package/src/prompt-input-slash.tsx +541 -0
- package/src/prompt-input.stories.tsx +2 -2
- package/src/prompt-input.test.tsx +67 -1
- package/src/prompt-input.tsx +42 -4
- package/src/queue.tsx +4 -4
- package/src/reasoning.tsx +42 -17
- package/src/sandbox.stories.tsx +9 -1
- package/src/sandbox.tsx +3 -3
- package/src/schema-display.test.tsx +56 -0
- package/src/schema-display.tsx +71 -37
- package/src/selection-toolbar.stories.tsx +9 -1
- package/src/session-header.stories.tsx +128 -0
- package/src/session-header.test.tsx +138 -0
- package/src/session-header.tsx +243 -0
- package/src/session-status-bar.stories.tsx +73 -0
- package/src/session-status-bar.test.tsx +94 -0
- package/src/session-status-bar.tsx +165 -0
- package/src/snippet.stories.tsx +9 -1
- package/src/snippet.test.tsx +6 -2
- package/src/snippet.tsx +14 -34
- package/src/speech-input.test.tsx +109 -0
- package/src/speech-input.tsx +31 -3
- package/src/stack-trace.tsx +24 -36
- package/src/streamdown-i18n.test.tsx +24 -1
- package/src/task.stories.tsx +13 -3
- package/src/templates-ai-assistant.stories.tsx +21 -1
- package/src/test-results.tsx +47 -27
- package/src/token-usage.stories.tsx +24 -0
- package/src/token-usage.test.tsx +92 -0
- package/src/{context.tsx → token-usage.tsx} +73 -59
- package/src/tool-result-card.stories.tsx +9 -1
- package/src/tool.stories.tsx +13 -3
- package/src/tool.test.tsx +65 -0
- package/src/tool.tsx +73 -30
- package/src/transcription.tsx +1 -1
- package/src/turn-status.stories.tsx +124 -0
- package/src/turn-status.test.tsx +74 -0
- package/src/turn-status.tsx +174 -0
- package/src/use-audio-level.ts +104 -0
- package/src/voice-selector.tsx +5 -5
- package/src/web-preview.test.tsx +51 -1
- package/src/web-preview.tsx +46 -10
- package/dist/_audio-player-media-chrome-KA5DY54G.js.map +0 -1
- package/dist/_flow-boundary-D63PJ65S.js.map +0 -1
- package/dist/_interactive-terminal-xterm-ATJ5EW3G.js +0 -11
- package/dist/_interactive-terminal-xterm-ATJ5EW3G.js.map +0 -1
- package/dist/_persona-rive-RFR2EUWP.js.map +0 -1
- package/src/_interactive-terminal-xterm.ts +0 -32
- package/src/blocks-ai-composer.stories.tsx +0 -83
- package/src/context.stories.tsx +0 -16
- package/src/interactive-terminal.stories.tsx +0 -165
- package/src/interactive-terminal.test.tsx +0 -448
- package/src/interactive-terminal.tsx +0 -444
- package/src/model-selector.test.tsx +0 -55
- package/src/model-selector.tsx +0 -225
- package/src/terminal.tsx +0 -244
|
@@ -1,30 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* ModelProviderLogo — the mark of an AI model provider, sized for a list row.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* It fetches provider marks from a **remote origin**
|
|
5
5
|
* (`models.dev`) by default, so a restrictive `img-src` blocks them. Pass `src`
|
|
6
6
|
* to self-host, or `fallback` to control what a blocked load renders — it is
|
|
7
7
|
* never a broken-image glyph. See `docs/CSP-AND-NETWORK.md`.
|
|
8
8
|
*/
|
|
9
9
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
10
10
|
|
|
11
|
-
import {
|
|
11
|
+
import { ModelProviderLogo } from "./model-provider-logo";
|
|
12
12
|
|
|
13
13
|
const meta = {
|
|
14
|
-
title: "AI/
|
|
15
|
-
component:
|
|
14
|
+
title: "AI/ModelProviderLogo",
|
|
15
|
+
component: ModelProviderLogo,
|
|
16
16
|
parameters: {
|
|
17
17
|
layout: "centered",
|
|
18
18
|
docs: {
|
|
19
19
|
description: {
|
|
20
20
|
component:
|
|
21
|
-
"
|
|
21
|
+
"The mark of an AI model provider, sized for a row in a model list. Defaults to " +
|
|
22
|
+
"fetching from models.dev; pass `src` for a self-hosted asset and `fallback` for the " +
|
|
23
|
+
"blocked/offline case. A failed load renders a neutral glyph, never a broken image.\n\n" +
|
|
24
|
+
"**`ModelProviderLogo` vs `ServiceLogo` (`@elabs-ai/components-icons`).** An AI " +
|
|
25
|
+
"provider mark with a zero-config remote default → this component. Any other " +
|
|
26
|
+
"third-party service mark, resolved from a registry the app supplies and never " +
|
|
27
|
+
"fetched → `ServiceLogo`.\n\n" +
|
|
28
|
+
"For the list this logo sits in: an inline pill in a composer footer → `ModelPicker` " +
|
|
29
|
+
"(`@elabs-ai/components-ui`); a full ⌘K palette → compose `CommandDialog` + " +
|
|
30
|
+
"`Command*` from `@elabs-ai/components-ui` directly.",
|
|
22
31
|
},
|
|
23
32
|
},
|
|
24
33
|
},
|
|
25
34
|
args: { provider: "anthropic" },
|
|
26
35
|
tags: ["autodocs"],
|
|
27
|
-
} satisfies Meta<typeof
|
|
36
|
+
} satisfies Meta<typeof ModelProviderLogo>;
|
|
28
37
|
|
|
29
38
|
export default meta;
|
|
30
39
|
type Story = StoryObj<typeof meta>;
|
|
@@ -64,7 +73,7 @@ export const LiveRemoteLogos: Story = {
|
|
|
64
73
|
<div className="flex items-center gap-4">
|
|
65
74
|
{(["anthropic", "openai", "google", "mistral"] as const).map((provider) => (
|
|
66
75
|
<figure className="flex flex-col items-center gap-2" key={provider}>
|
|
67
|
-
<
|
|
76
|
+
<ModelProviderLogo provider={provider} />
|
|
68
77
|
<figcaption className="text-meta text-muted-foreground">{provider}</figcaption>
|
|
69
78
|
</figure>
|
|
70
79
|
))}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { cleanup, fireEvent, render, screen } from "@testing-library/react";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
MODEL_PROVIDER_LOGO_BASE_URL,
|
|
6
|
+
ModelProviderLogo,
|
|
7
|
+
ModelProviderLogoGroup,
|
|
8
|
+
} from "./model-provider-logo";
|
|
9
|
+
|
|
10
|
+
afterEach(() => {
|
|
11
|
+
cleanup();
|
|
12
|
+
document.documentElement.removeAttribute("data-theme");
|
|
13
|
+
document.documentElement.style.colorScheme = "";
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe("ModelProviderLogo — remote origin escape hatches", () => {
|
|
17
|
+
it("defaults to the models.dev logo for the provider", () => {
|
|
18
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
19
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveAttribute(
|
|
20
|
+
"src",
|
|
21
|
+
`${MODEL_PROVIDER_LOGO_BASE_URL}/anthropic.svg`,
|
|
22
|
+
);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("lets a caller override src for a self-hosted asset", () => {
|
|
26
|
+
// The old props type did `Omit<…, "src">` AND spread props before `src=`, so
|
|
27
|
+
// a self-hosting consumer had no way to win. Both are fixed.
|
|
28
|
+
render(<ModelProviderLogo provider="anthropic" src="/local/anthropic.svg" />);
|
|
29
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveAttribute(
|
|
30
|
+
"src",
|
|
31
|
+
"/local/anthropic.svg",
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it("renders a neutral glyph instead of a broken image when the load fails", () => {
|
|
36
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
37
|
+
fireEvent.error(screen.getByRole("img", { name: "anthropic logo" }));
|
|
38
|
+
|
|
39
|
+
const fallback = screen.getByRole("img", { name: "anthropic logo" });
|
|
40
|
+
expect(fallback.tagName.toLowerCase()).toBe("svg");
|
|
41
|
+
expect(fallback).not.toHaveAttribute("src");
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it("renders a caller-supplied fallback when provided", () => {
|
|
45
|
+
render(<ModelProviderLogo provider="anthropic" fallback={<span data-testid="fb">AI</span>} />);
|
|
46
|
+
fireEvent.error(screen.getByRole("img", { name: "anthropic logo" }));
|
|
47
|
+
expect(screen.getByTestId("fb")).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("still calls a caller's onError", () => {
|
|
51
|
+
const onError = vi.fn();
|
|
52
|
+
render(<ModelProviderLogo provider="anthropic" onError={onError} />);
|
|
53
|
+
fireEvent.error(screen.getByRole("img", { name: "anthropic logo" }));
|
|
54
|
+
expect(onError).toHaveBeenCalledTimes(1);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it("keeps an accessible name in both states", () => {
|
|
58
|
+
const { rerender } = render(<ModelProviderLogo provider="openai" />);
|
|
59
|
+
expect(screen.getByRole("img", { name: "openai logo" })).toBeInTheDocument();
|
|
60
|
+
|
|
61
|
+
fireEvent.error(screen.getByRole("img", { name: "openai logo" }));
|
|
62
|
+
rerender(<ModelProviderLogo provider="openai" />);
|
|
63
|
+
expect(screen.getByRole("img", { name: "openai logo" })).toBeInTheDocument();
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
// Was `dark:invert` — a bare Tailwind dark: variant, which per theming.md only
|
|
68
|
+
// covers the two SHIPPED themes and misses any consumer-registered dark theme
|
|
69
|
+
// (ADR 0029, open theme registry). Now driven by `resolveThemeIsDark`, the
|
|
70
|
+
// same signal `Toaster` uses, so it follows the ACTIVE theme's own
|
|
71
|
+
// `color-scheme`, not a hardcoded theme name (review #1).
|
|
72
|
+
describe("ModelProviderLogo — theme-driven invert (review #1)", () => {
|
|
73
|
+
it("does not invert under the light theme", () => {
|
|
74
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
75
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
76
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).not.toHaveClass("invert");
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("inverts under the dark theme", () => {
|
|
80
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
81
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
82
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveClass("invert");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("also inverts under a consumer-registered dark theme, not only the shipped one", () => {
|
|
86
|
+
// A raw `dark:` utility only ever matches `data-theme="dark"` — this
|
|
87
|
+
// is exactly the gap `resolveThemeIsDark`'s `color-scheme` reading closes.
|
|
88
|
+
document.documentElement.setAttribute("data-theme", "midnight");
|
|
89
|
+
document.documentElement.style.colorScheme = "dark";
|
|
90
|
+
render(<ModelProviderLogo provider="anthropic" />);
|
|
91
|
+
expect(screen.getByRole("img", { name: "anthropic logo" })).toHaveClass("invert");
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe("ModelProviderLogoGroup — theme-driven backdrop (review #1)", () => {
|
|
96
|
+
it("backs logos with --background in the light theme", () => {
|
|
97
|
+
document.documentElement.setAttribute("data-theme", "light");
|
|
98
|
+
const { container } = render(<ModelProviderLogoGroup />);
|
|
99
|
+
expect(container.firstElementChild?.className).toContain("[&>img]:bg-background");
|
|
100
|
+
expect(container.firstElementChild?.className).not.toContain("[&>img]:bg-foreground");
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it("backs logos with --foreground in the dark theme", () => {
|
|
104
|
+
document.documentElement.setAttribute("data-theme", "dark");
|
|
105
|
+
const { container } = render(<ModelProviderLogoGroup />);
|
|
106
|
+
expect(container.firstElementChild?.className).toContain("[&>img]:bg-foreground");
|
|
107
|
+
expect(container.firstElementChild?.className).not.toContain("[&>img]:bg-background");
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@elabs-ai/components-ui/lib/cn";
|
|
4
|
+
import { resolveThemeIsDark } from "@elabs-ai/components-tokens";
|
|
5
|
+
import { BotIcon } from "lucide-react";
|
|
6
|
+
import type { ComponentProps, ReactNode } from "react";
|
|
7
|
+
import { useEffect, useState } from "react";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Tracks the ACTIVE theme's own `color-scheme` (`resolveThemeIsDark`), not the
|
|
11
|
+
* two shipped `light`/`dark` themes — a consumer-authored dark theme still
|
|
12
|
+
* gets an inverted (visible) logo with no registration step (theming.md:
|
|
13
|
+
* "Use semantic tokens, not `dark:` — `@custom-variant dark` covers only the
|
|
14
|
+
* themes shipped HERE"). Mirrors `Toaster`'s `useDocumentThemeMode`
|
|
15
|
+
* (`packages/ui/src/components/sonner/sonner.tsx`).
|
|
16
|
+
*/
|
|
17
|
+
function useIsDarkTheme(): boolean {
|
|
18
|
+
const [isDark, setIsDark] = useState(false);
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
if (typeof document === "undefined") return;
|
|
21
|
+
const el = document.documentElement;
|
|
22
|
+
const read = () => setIsDark(resolveThemeIsDark(el));
|
|
23
|
+
read();
|
|
24
|
+
const obs = new MutationObserver(read);
|
|
25
|
+
obs.observe(el, { attributes: true, attributeFilter: ["data-theme"] });
|
|
26
|
+
return () => obs.disconnect();
|
|
27
|
+
}, []);
|
|
28
|
+
return isDark;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Where provider logos are fetched from by default.
|
|
33
|
+
*
|
|
34
|
+
* This is a REMOTE origin: a deployment with a restrictive `img-src` blocks it.
|
|
35
|
+
* Self-host the SVGs and pass `src` (or set a `fallback`) — see
|
|
36
|
+
* `docs/CSP-AND-NETWORK.md`.
|
|
37
|
+
*/
|
|
38
|
+
export const MODEL_PROVIDER_LOGO_BASE_URL = "https://models.dev/logos";
|
|
39
|
+
|
|
40
|
+
export type ModelProviderLogoProps = Omit<ComponentProps<"img">, "alt"> & {
|
|
41
|
+
/**
|
|
42
|
+
* Override the logo URL — point at a self-hosted or bundled asset when a CSP
|
|
43
|
+
* blocks `models.dev`. Defaults to `${MODEL_PROVIDER_LOGO_BASE_URL}/<provider>.svg`.
|
|
44
|
+
*/
|
|
45
|
+
src?: string;
|
|
46
|
+
/**
|
|
47
|
+
* Rendered when the logo fails to load (blocked, offline, or unknown
|
|
48
|
+
* provider). Defaults to a neutral Lucide glyph, so the row never collapses
|
|
49
|
+
* to a broken image.
|
|
50
|
+
*/
|
|
51
|
+
fallback?: ReactNode;
|
|
52
|
+
provider:
|
|
53
|
+
| "moonshotai-cn"
|
|
54
|
+
| "lucidquery"
|
|
55
|
+
| "moonshotai"
|
|
56
|
+
| "zai-coding-plan"
|
|
57
|
+
| "alibaba"
|
|
58
|
+
| "xai"
|
|
59
|
+
| "vultr"
|
|
60
|
+
| "nvidia"
|
|
61
|
+
| "upstage"
|
|
62
|
+
| "groq"
|
|
63
|
+
| "github-copilot"
|
|
64
|
+
| "mistral"
|
|
65
|
+
| "vercel"
|
|
66
|
+
| "nebius"
|
|
67
|
+
| "deepseek"
|
|
68
|
+
| "alibaba-cn"
|
|
69
|
+
| "google-vertex-anthropic"
|
|
70
|
+
| "venice"
|
|
71
|
+
| "chutes"
|
|
72
|
+
| "cortecs"
|
|
73
|
+
| "github-models"
|
|
74
|
+
| "togetherai"
|
|
75
|
+
| "azure"
|
|
76
|
+
| "baseten"
|
|
77
|
+
| "huggingface"
|
|
78
|
+
| "opencode"
|
|
79
|
+
| "fastrouter"
|
|
80
|
+
| "google"
|
|
81
|
+
| "google-vertex"
|
|
82
|
+
| "cloudflare-workers-ai"
|
|
83
|
+
| "inception"
|
|
84
|
+
| "wandb"
|
|
85
|
+
| "openai"
|
|
86
|
+
| "zhipuai-coding-plan"
|
|
87
|
+
| "perplexity"
|
|
88
|
+
| "openrouter"
|
|
89
|
+
| "zenmux"
|
|
90
|
+
| "v0"
|
|
91
|
+
| "iflowcn"
|
|
92
|
+
| "synthetic"
|
|
93
|
+
| "deepinfra"
|
|
94
|
+
| "zhipuai"
|
|
95
|
+
| "submodel"
|
|
96
|
+
| "zai"
|
|
97
|
+
| "inference"
|
|
98
|
+
| "requesty"
|
|
99
|
+
| "morph"
|
|
100
|
+
| "lmstudio"
|
|
101
|
+
| "anthropic"
|
|
102
|
+
| "aihubmix"
|
|
103
|
+
| "fireworks-ai"
|
|
104
|
+
| "modelscope"
|
|
105
|
+
| "llama"
|
|
106
|
+
| "scaleway"
|
|
107
|
+
| "amazon-bedrock"
|
|
108
|
+
| "cerebras"
|
|
109
|
+
// oxlint-disable-next-line typescript-eslint(ban-types) -- intentional pattern for autocomplete-friendly string union
|
|
110
|
+
| (string & {});
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* The mark of an AI model provider (`anthropic`, `openai`, `google`, …), sized
|
|
115
|
+
* for a row in a model list.
|
|
116
|
+
*
|
|
117
|
+
* **`ModelProviderLogo` vs `ServiceLogo` (`@elabs-ai/components-icons`).** An AI
|
|
118
|
+
* provider mark with a zero-config remote default — name the provider and a logo
|
|
119
|
+
* appears, no registration step — is this component. Any other third-party
|
|
120
|
+
* service mark, resolved from a registry the app supplies and never fetched, is
|
|
121
|
+
* `ServiceLogo`. The two are not interchangeable: `ServiceLogo` has no
|
|
122
|
+
* per-instance `src` and no `onError`, and this one reaches a remote origin by
|
|
123
|
+
* default (see `docs/CSP-AND-NETWORK.md`).
|
|
124
|
+
*/
|
|
125
|
+
export const ModelProviderLogo = ({
|
|
126
|
+
provider,
|
|
127
|
+
className,
|
|
128
|
+
src,
|
|
129
|
+
fallback,
|
|
130
|
+
onError,
|
|
131
|
+
...props
|
|
132
|
+
}: ModelProviderLogoProps) => {
|
|
133
|
+
const [failed, setFailed] = useState(false);
|
|
134
|
+
const isDark = useIsDarkTheme();
|
|
135
|
+
|
|
136
|
+
// A blocked/missing logo must not leave a broken-image glyph in the row.
|
|
137
|
+
if (failed) {
|
|
138
|
+
return (
|
|
139
|
+
<>
|
|
140
|
+
{fallback ?? (
|
|
141
|
+
<BotIcon aria-label={`${provider} logo`} className={cn("size-3", className)} role="img" />
|
|
142
|
+
)}
|
|
143
|
+
</>
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
return (
|
|
148
|
+
<img
|
|
149
|
+
{...props}
|
|
150
|
+
alt={`${provider} logo`}
|
|
151
|
+
className={cn("size-3", isDark && "invert", className)}
|
|
152
|
+
height={12}
|
|
153
|
+
onError={(event) => {
|
|
154
|
+
setFailed(true);
|
|
155
|
+
onError?.(event);
|
|
156
|
+
}}
|
|
157
|
+
// AFTER the spread, so the caller's `src` wins via the destructured
|
|
158
|
+
// default rather than being silently overwritten.
|
|
159
|
+
src={src ?? `${MODEL_PROVIDER_LOGO_BASE_URL}/${provider}.svg`}
|
|
160
|
+
width={12}
|
|
161
|
+
/>
|
|
162
|
+
);
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export type ModelProviderLogoGroupProps = ComponentProps<"div">;
|
|
166
|
+
|
|
167
|
+
export const ModelProviderLogoGroup = ({ className, ...props }: ModelProviderLogoGroupProps) => {
|
|
168
|
+
// Same active-theme signal as the single logo above (not the two shipped
|
|
169
|
+
// `light`/`dark` themes) — the backdrop swaps to `--foreground` so a
|
|
170
|
+
// transparent-background SVG still reads against a dark chip in ANY dark
|
|
171
|
+
// theme a consumer registers.
|
|
172
|
+
const isDark = useIsDarkTheme();
|
|
173
|
+
return (
|
|
174
|
+
<div
|
|
175
|
+
className={cn(
|
|
176
|
+
"flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:p-px [&>img]:ring-1",
|
|
177
|
+
isDark ? "[&>img]:bg-foreground" : "[&>img]:bg-background",
|
|
178
|
+
className,
|
|
179
|
+
)}
|
|
180
|
+
{...props}
|
|
181
|
+
/>
|
|
182
|
+
);
|
|
183
|
+
};
|
package/src/open-in-chat.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
|
-
import { Button } from "@elabs-ai/components-ui";
|
|
3
|
+
import { Button, useLocale } from "@elabs-ai/components-ui";
|
|
4
4
|
import {
|
|
5
5
|
DropdownMenu,
|
|
6
6
|
DropdownMenuContent,
|
|
@@ -23,11 +23,11 @@ const providers = {
|
|
|
23
23
|
})}`,
|
|
24
24
|
icon: (
|
|
25
25
|
<svg fill="currentColor" role="img" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
|
26
|
-
<title>OpenAI</title>
|
|
26
|
+
<title>OpenAI</title> {/* i18n-exempt: brand name */}
|
|
27
27
|
<path d="M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3655-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8144 3.3543-2.0201 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.7735-2.7818a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z" />
|
|
28
28
|
</svg>
|
|
29
29
|
),
|
|
30
|
-
title: "
|
|
30
|
+
title: "ChatGPT", // i18n-exempt: brand name
|
|
31
31
|
},
|
|
32
32
|
claude: {
|
|
33
33
|
createUrl: (q: string) =>
|
|
@@ -36,7 +36,7 @@ const providers = {
|
|
|
36
36
|
})}`,
|
|
37
37
|
icon: (
|
|
38
38
|
<svg fill="currentColor" role="img" viewBox="0 0 12 12" xmlns="http://www.w3.org/2000/svg">
|
|
39
|
-
<title>Claude</title>
|
|
39
|
+
<title>Claude</title> {/* i18n-exempt: brand name */}
|
|
40
40
|
<path
|
|
41
41
|
clipRule="evenodd"
|
|
42
42
|
d="M2.3545 7.9775L4.7145 6.654L4.7545 6.539L4.7145 6.475H4.6L4.205 6.451L2.856 6.4145L1.6865 6.366L0.5535 6.305L0.268 6.2445L0 5.892L0.0275 5.716L0.2675 5.5555L0.6105 5.5855L1.3705 5.637L2.5095 5.716L3.3355 5.7645L4.56 5.892H4.7545L4.782 5.8135L4.715 5.7645L4.6635 5.716L3.4845 4.918L2.2085 4.074L1.5405 3.588L1.1785 3.3425L0.9965 3.1115L0.9175 2.6075L1.2455 2.2465L1.686 2.2765L1.7985 2.307L2.245 2.65L3.199 3.388L4.4445 4.3045L4.627 4.4565L4.6995 4.405L4.709 4.3685L4.627 4.2315L3.9495 3.0085L3.2265 1.7635L2.9045 1.2475L2.8195 0.938C2.78711 0.819128 2.76965 0.696687 2.7675 0.5735L3.1415 0.067L3.348 0L3.846 0.067L4.056 0.249L4.366 0.956L4.867 2.0705L5.6445 3.5855L5.8725 4.0345L5.994 4.4505L6.0395 4.578H6.1185V4.505L6.1825 3.652L6.301 2.6045L6.416 1.257L6.456 0.877L6.644 0.422L7.0175 0.176L7.3095 0.316L7.5495 0.6585L7.516 0.8805L7.373 1.806L7.0935 3.2575L6.9115 4.2285H7.0175L7.139 4.1075L7.6315 3.4545L8.4575 2.4225L8.8225 2.0125L9.2475 1.5605L9.521 1.345H10.0375L10.4175 1.9095L10.2475 2.4925L9.7155 3.166L9.275 3.737L8.643 4.587L8.248 5.267L8.2845 5.322L8.3785 5.312L9.8065 5.009L10.578 4.869L11.4985 4.7115L11.915 4.9055L11.9605 5.103L11.7965 5.5065L10.812 5.7495L9.6575 5.9805L7.938 6.387L7.917 6.402L7.9415 6.4325L8.716 6.5055L9.047 6.5235H9.858L11.368 6.636L11.763 6.897L12 7.216L11.9605 7.4585L11.353 7.7685L10.533 7.574L8.6185 7.119L7.9625 6.9545H7.8715V7.0095L8.418 7.5435L9.421 8.4485L10.6755 9.6135L10.739 9.9025L10.578 10.13L10.408 10.1055L9.3055 9.277L8.88 8.9035L7.917 8.0935H7.853V8.1785L8.075 8.503L9.2475 10.2635L9.3085 10.8035L9.2235 10.98L8.9195 11.0865L8.5855 11.0255L7.8985 10.063L7.191 8.9795L6.6195 8.008L6.5495 8.048L6.2125 11.675L6.0545 11.86L5.69 12L5.3865 11.7695L5.2255 11.396L5.3865 10.658L5.581 9.696L5.7385 8.931L5.8815 7.981L5.9665 7.665L5.9605 7.644L5.8905 7.653L5.1735 8.6365L4.0835 10.109L3.2205 11.0315L3.0135 11.1135L2.655 10.9285L2.6885 10.5975L2.889 10.303L4.083 8.785L4.803 7.844L5.268 7.301L5.265 7.222H5.2375L2.066 9.28L1.501 9.353L1.2575 9.125L1.288 8.752L1.4035 8.6305L2.3575 7.9745L2.3545 7.9775Z"
|
|
@@ -44,7 +44,7 @@ const providers = {
|
|
|
44
44
|
/>
|
|
45
45
|
</svg>
|
|
46
46
|
),
|
|
47
|
-
title: "
|
|
47
|
+
title: "Claude", // i18n-exempt: brand name
|
|
48
48
|
},
|
|
49
49
|
cursor: {
|
|
50
50
|
createUrl: (text: string) => {
|
|
@@ -54,24 +54,24 @@ const providers = {
|
|
|
54
54
|
},
|
|
55
55
|
icon: (
|
|
56
56
|
<svg version="1.1" viewBox="0 0 466.73 532.09" xmlns="http://www.w3.org/2000/svg">
|
|
57
|
-
<title>Cursor</title>
|
|
57
|
+
<title>Cursor</title> {/* i18n-exempt: brand name */}
|
|
58
58
|
<path
|
|
59
59
|
d="M457.43,125.94L244.42,2.96c-6.84-3.95-15.28-3.95-22.12,0L9.3,125.94c-5.75,3.32-9.3,9.46-9.3,16.11v247.99c0,6.65,3.55,12.79,9.3,16.11l213.01,122.98c6.84,3.95,15.28,3.95,22.12,0l213.01-122.98c5.75-3.32,9.3-9.46,9.3-16.11v-247.99c0-6.65-3.55-12.79-9.3-16.11h-.01ZM444.05,151.99l-205.63,356.16c-1.39,2.4-5.06,1.42-5.06-1.36v-233.21c0-4.66-2.49-8.97-6.53-11.31L24.87,145.67c-2.4-1.39-1.42-5.06,1.36-5.06h411.26c5.84,0,9.49,6.33,6.57,11.39h-.01Z"
|
|
60
60
|
fill="currentColor"
|
|
61
61
|
/>
|
|
62
62
|
</svg>
|
|
63
63
|
),
|
|
64
|
-
title: "
|
|
64
|
+
title: "Cursor", // i18n-exempt: brand name
|
|
65
65
|
},
|
|
66
66
|
github: {
|
|
67
67
|
createUrl: (url: string) => url,
|
|
68
68
|
icon: (
|
|
69
69
|
<svg fill="currentColor" role="img" viewBox="0 0 24 24">
|
|
70
|
-
<title>GitHub</title>
|
|
70
|
+
<title>GitHub</title> {/* i18n-exempt: brand name */}
|
|
71
71
|
<path d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12" />
|
|
72
72
|
</svg>
|
|
73
73
|
),
|
|
74
|
-
title: "
|
|
74
|
+
title: "GitHub", // i18n-exempt: brand name
|
|
75
75
|
},
|
|
76
76
|
scira: {
|
|
77
77
|
createUrl: (q: string) =>
|
|
@@ -86,7 +86,7 @@ const providers = {
|
|
|
86
86
|
width="910"
|
|
87
87
|
xmlns="http://www.w3.org/2000/svg"
|
|
88
88
|
>
|
|
89
|
-
<title>Scira AI</title>
|
|
89
|
+
<title>Scira AI</title> {/* i18n-exempt: brand name */}
|
|
90
90
|
<path
|
|
91
91
|
d="M647.664 197.775C569.13 189.049 525.5 145.419 516.774 66.8849C508.048 145.419 464.418 189.049 385.884 197.775C464.418 206.501 508.048 250.131 516.774 328.665C525.5 250.131 569.13 206.501 647.664 197.775Z"
|
|
92
92
|
fill="currentColor"
|
|
@@ -136,7 +136,7 @@ const providers = {
|
|
|
136
136
|
/>
|
|
137
137
|
</svg>
|
|
138
138
|
),
|
|
139
|
-
title: "
|
|
139
|
+
title: "Scira", // i18n-exempt: brand name
|
|
140
140
|
},
|
|
141
141
|
t3: {
|
|
142
142
|
createUrl: (q: string) =>
|
|
@@ -144,7 +144,7 @@ const providers = {
|
|
|
144
144
|
q,
|
|
145
145
|
})}`,
|
|
146
146
|
icon: <MessageCircleIcon />,
|
|
147
|
-
title: "
|
|
147
|
+
title: "T3 Chat", // i18n-exempt: brand name
|
|
148
148
|
},
|
|
149
149
|
v0: {
|
|
150
150
|
createUrl: (q: string) =>
|
|
@@ -158,7 +158,7 @@ const providers = {
|
|
|
158
158
|
<path d="M147 56H133V23.9531L100.953 56H133V70H96.6875C85.8144 70 77 61.1856 77 50.3125V14H91V46.1562L123.156 14H91V0H127.312C138.186 0 147 8.81439 147 19.6875V56Z" />
|
|
159
159
|
</svg>
|
|
160
160
|
),
|
|
161
|
-
title: "
|
|
161
|
+
title: "v0", // i18n-exempt: brand name
|
|
162
162
|
},
|
|
163
163
|
};
|
|
164
164
|
|
|
@@ -208,21 +208,25 @@ export const OpenInSeparator = (props: OpenInSeparatorProps) => (
|
|
|
208
208
|
|
|
209
209
|
export type OpenInTriggerProps = ComponentProps<typeof DropdownMenuTrigger>;
|
|
210
210
|
|
|
211
|
-
export const OpenInTrigger = ({ children, ...props }: OpenInTriggerProps) =>
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
<
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
)
|
|
211
|
+
export const OpenInTrigger = ({ children, ...props }: OpenInTriggerProps) => {
|
|
212
|
+
const { t } = useLocale();
|
|
213
|
+
return (
|
|
214
|
+
<DropdownMenuTrigger {...props} asChild>
|
|
215
|
+
{children ?? (
|
|
216
|
+
<Button type="button" variant="outline">
|
|
217
|
+
{t("ai.openInChat.trigger")}
|
|
218
|
+
<ChevronDownIcon className="size-4" />
|
|
219
|
+
</Button>
|
|
220
|
+
)}
|
|
221
|
+
</DropdownMenuTrigger>
|
|
222
|
+
);
|
|
223
|
+
};
|
|
221
224
|
|
|
222
225
|
export type OpenInChatGPTProps = ComponentProps<typeof DropdownMenuItem>;
|
|
223
226
|
|
|
224
227
|
export const OpenInChatGPT = (props: OpenInChatGPTProps) => {
|
|
225
228
|
const { query } = useOpenInContext();
|
|
229
|
+
const { t } = useLocale();
|
|
226
230
|
return (
|
|
227
231
|
<DropdownMenuItem asChild {...props}>
|
|
228
232
|
<a
|
|
@@ -232,7 +236,9 @@ export const OpenInChatGPT = (props: OpenInChatGPTProps) => {
|
|
|
232
236
|
target="_blank"
|
|
233
237
|
>
|
|
234
238
|
<span className="shrink-0">{providers.chatgpt.icon}</span>
|
|
235
|
-
<span className="flex-1">
|
|
239
|
+
<span className="flex-1">
|
|
240
|
+
{t("ai.openInChat.openInProvider", { provider: providers.chatgpt.title })}
|
|
241
|
+
</span>
|
|
236
242
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
237
243
|
</a>
|
|
238
244
|
</DropdownMenuItem>
|
|
@@ -243,6 +249,7 @@ export type OpenInClaudeProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
243
249
|
|
|
244
250
|
export const OpenInClaude = (props: OpenInClaudeProps) => {
|
|
245
251
|
const { query } = useOpenInContext();
|
|
252
|
+
const { t } = useLocale();
|
|
246
253
|
return (
|
|
247
254
|
<DropdownMenuItem asChild {...props}>
|
|
248
255
|
<a
|
|
@@ -252,7 +259,9 @@ export const OpenInClaude = (props: OpenInClaudeProps) => {
|
|
|
252
259
|
target="_blank"
|
|
253
260
|
>
|
|
254
261
|
<span className="shrink-0">{providers.claude.icon}</span>
|
|
255
|
-
<span className="flex-1">
|
|
262
|
+
<span className="flex-1">
|
|
263
|
+
{t("ai.openInChat.openInProvider", { provider: providers.claude.title })}
|
|
264
|
+
</span>
|
|
256
265
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
257
266
|
</a>
|
|
258
267
|
</DropdownMenuItem>
|
|
@@ -263,6 +272,7 @@ export type OpenInT3Props = ComponentProps<typeof DropdownMenuItem>;
|
|
|
263
272
|
|
|
264
273
|
export const OpenInT3 = (props: OpenInT3Props) => {
|
|
265
274
|
const { query } = useOpenInContext();
|
|
275
|
+
const { t } = useLocale();
|
|
266
276
|
return (
|
|
267
277
|
<DropdownMenuItem asChild {...props}>
|
|
268
278
|
<a
|
|
@@ -272,7 +282,9 @@ export const OpenInT3 = (props: OpenInT3Props) => {
|
|
|
272
282
|
target="_blank"
|
|
273
283
|
>
|
|
274
284
|
<span className="shrink-0">{providers.t3.icon}</span>
|
|
275
|
-
<span className="flex-1">
|
|
285
|
+
<span className="flex-1">
|
|
286
|
+
{t("ai.openInChat.openInProvider", { provider: providers.t3.title })}
|
|
287
|
+
</span>
|
|
276
288
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
277
289
|
</a>
|
|
278
290
|
</DropdownMenuItem>
|
|
@@ -283,6 +295,7 @@ export type OpenInSciraProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
283
295
|
|
|
284
296
|
export const OpenInScira = (props: OpenInSciraProps) => {
|
|
285
297
|
const { query } = useOpenInContext();
|
|
298
|
+
const { t } = useLocale();
|
|
286
299
|
return (
|
|
287
300
|
<DropdownMenuItem asChild {...props}>
|
|
288
301
|
<a
|
|
@@ -292,7 +305,9 @@ export const OpenInScira = (props: OpenInSciraProps) => {
|
|
|
292
305
|
target="_blank"
|
|
293
306
|
>
|
|
294
307
|
<span className="shrink-0">{providers.scira.icon}</span>
|
|
295
|
-
<span className="flex-1">
|
|
308
|
+
<span className="flex-1">
|
|
309
|
+
{t("ai.openInChat.openInProvider", { provider: providers.scira.title })}
|
|
310
|
+
</span>
|
|
296
311
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
297
312
|
</a>
|
|
298
313
|
</DropdownMenuItem>
|
|
@@ -303,6 +318,7 @@ export type OpenInv0Props = ComponentProps<typeof DropdownMenuItem>;
|
|
|
303
318
|
|
|
304
319
|
export const OpenInv0 = (props: OpenInv0Props) => {
|
|
305
320
|
const { query } = useOpenInContext();
|
|
321
|
+
const { t } = useLocale();
|
|
306
322
|
return (
|
|
307
323
|
<DropdownMenuItem asChild {...props}>
|
|
308
324
|
<a
|
|
@@ -312,7 +328,9 @@ export const OpenInv0 = (props: OpenInv0Props) => {
|
|
|
312
328
|
target="_blank"
|
|
313
329
|
>
|
|
314
330
|
<span className="shrink-0">{providers.v0.icon}</span>
|
|
315
|
-
<span className="flex-1">
|
|
331
|
+
<span className="flex-1">
|
|
332
|
+
{t("ai.openInChat.openInProvider", { provider: providers.v0.title })}
|
|
333
|
+
</span>
|
|
316
334
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
317
335
|
</a>
|
|
318
336
|
</DropdownMenuItem>
|
|
@@ -323,6 +341,7 @@ export type OpenInCursorProps = ComponentProps<typeof DropdownMenuItem>;
|
|
|
323
341
|
|
|
324
342
|
export const OpenInCursor = (props: OpenInCursorProps) => {
|
|
325
343
|
const { query } = useOpenInContext();
|
|
344
|
+
const { t } = useLocale();
|
|
326
345
|
return (
|
|
327
346
|
<DropdownMenuItem asChild {...props}>
|
|
328
347
|
<a
|
|
@@ -332,7 +351,9 @@ export const OpenInCursor = (props: OpenInCursorProps) => {
|
|
|
332
351
|
target="_blank"
|
|
333
352
|
>
|
|
334
353
|
<span className="shrink-0">{providers.cursor.icon}</span>
|
|
335
|
-
<span className="flex-1">
|
|
354
|
+
<span className="flex-1">
|
|
355
|
+
{t("ai.openInChat.openInProvider", { provider: providers.cursor.title })}
|
|
356
|
+
</span>
|
|
336
357
|
<ExternalLinkIcon className="size-4 shrink-0" />
|
|
337
358
|
</a>
|
|
338
359
|
</DropdownMenuItem>
|
package/src/package-info.tsx
CHANGED
|
@@ -35,17 +35,17 @@ export const PackageInfoName = ({ className, children, ...props }: PackageInfoNa
|
|
|
35
35
|
return (
|
|
36
36
|
<div className={cn("flex items-center gap-2", className)} {...props}>
|
|
37
37
|
<PackageIcon className="size-4 text-muted-foreground" />
|
|
38
|
-
<span className="font-medium font-mono text-
|
|
38
|
+
<span className="font-medium font-mono text-body">{children ?? name}</span>
|
|
39
39
|
</div>
|
|
40
40
|
);
|
|
41
41
|
};
|
|
42
42
|
|
|
43
43
|
const changeTypeStyles: Record<ChangeType, string> = {
|
|
44
|
-
added: "bg-
|
|
45
|
-
major: "bg-
|
|
46
|
-
minor: "bg-
|
|
47
|
-
patch: "bg-
|
|
48
|
-
removed: "bg-
|
|
44
|
+
added: "bg-info/10 text-info-text",
|
|
45
|
+
major: "bg-destructive/10 text-destructive-text",
|
|
46
|
+
minor: "bg-warning/10 text-warning-text",
|
|
47
|
+
patch: "bg-success/10 text-success-text",
|
|
48
|
+
removed: "bg-muted text-muted-foreground",
|
|
49
49
|
};
|
|
50
50
|
|
|
51
51
|
const changeTypeIcons: Record<ChangeType, React.ReactNode> = {
|
|
@@ -71,7 +71,7 @@ export const PackageInfoChangeType = ({
|
|
|
71
71
|
|
|
72
72
|
return (
|
|
73
73
|
<Badge
|
|
74
|
-
className={cn("gap-1 text-
|
|
74
|
+
className={cn("gap-1 text-meta capitalize", changeTypeStyles[changeType], className)}
|
|
75
75
|
variant="secondary"
|
|
76
76
|
{...props}
|
|
77
77
|
>
|
|
@@ -93,7 +93,7 @@ export const PackageInfoVersion = ({ className, children, ...props }: PackageInf
|
|
|
93
93
|
return (
|
|
94
94
|
<div
|
|
95
95
|
className={cn(
|
|
96
|
-
"mt-2 flex items-center gap-2 font-mono text-muted-foreground text-
|
|
96
|
+
"mt-2 flex items-center gap-2 font-mono text-muted-foreground text-body",
|
|
97
97
|
className,
|
|
98
98
|
)}
|
|
99
99
|
{...props}
|
|
@@ -154,7 +154,7 @@ export const PackageInfoDescription = ({
|
|
|
154
154
|
children,
|
|
155
155
|
...props
|
|
156
156
|
}: PackageInfoDescriptionProps) => (
|
|
157
|
-
<p className={cn("mt-2 text-muted-foreground text-
|
|
157
|
+
<p className={cn("mt-2 text-muted-foreground text-body", className)} {...props}>
|
|
158
158
|
{children}
|
|
159
159
|
</p>
|
|
160
160
|
);
|
|
@@ -175,7 +175,7 @@ export const PackageInfoDependencies = ({
|
|
|
175
175
|
...props
|
|
176
176
|
}: PackageInfoDependenciesProps) => (
|
|
177
177
|
<div className={cn("space-y-2", className)} {...props}>
|
|
178
|
-
<span className="font-medium text-muted-foreground text-
|
|
178
|
+
<span className="font-medium text-muted-foreground text-meta uppercase tracking-wide">
|
|
179
179
|
Dependencies
|
|
180
180
|
</span>
|
|
181
181
|
<div className="space-y-1">{children}</div>
|
|
@@ -194,11 +194,11 @@ export const PackageInfoDependency = ({
|
|
|
194
194
|
children,
|
|
195
195
|
...props
|
|
196
196
|
}: PackageInfoDependencyProps) => (
|
|
197
|
-
<div className={cn("flex items-center justify-between text-
|
|
197
|
+
<div className={cn("flex items-center justify-between text-body", className)} {...props}>
|
|
198
198
|
{children ?? (
|
|
199
199
|
<>
|
|
200
200
|
<span className="font-mono text-muted-foreground">{name}</span>
|
|
201
|
-
{version && <span className="font-mono text-
|
|
201
|
+
{version && <span className="font-mono text-meta">{version}</span>}
|
|
202
202
|
</>
|
|
203
203
|
)}
|
|
204
204
|
</div>
|