Move integrations and live trading into settings navigation
Integrations and Live Trading no longer compete as sidebar peers with the account entry. Settings opens from a dedicated sidebar control; integrations live in the settings nav, and Live Trading is a discover banner above the account block in General. Co-authored-by: cynarAI <cynarAI@users.noreply.github.com>
This commit is contained in:
parent
6c7eed7580
commit
2f2f0eb21e
@ -50,8 +50,8 @@ test("logout protects bot deep links and sign-in restores the session", async ({
|
|||||||
const protectedBotPath = new URL(page.url()).pathname;
|
const protectedBotPath = new URL(page.url()).pathname;
|
||||||
await expect(page.getByPlaceholder("Message Chief")).toBeVisible();
|
await expect(page.getByPlaceholder("Message Chief")).toBeVisible();
|
||||||
|
|
||||||
await page.getByRole("button", { name: new RegExp(userName, "i") }).click();
|
await expect(page.getByTestId("settings-entry")).toBeVisible();
|
||||||
await expect(page.getByRole("button", { name: "Settings", exact: true })).toBeVisible();
|
await page.getByTestId("user-menu-trigger").click();
|
||||||
await expect(page.getByRole("button", { name: "Usage", exact: true })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Usage", exact: true })).toBeVisible();
|
||||||
await expect(page.getByRole("button", { name: "Log out" })).toBeVisible();
|
await expect(page.getByRole("button", { name: "Log out" })).toBeVisible();
|
||||||
await expect(
|
await expect(
|
||||||
@ -152,8 +152,7 @@ test("changes and recovers an email password", async ({ page }, testInfo) => {
|
|||||||
await completeOnboarding(page);
|
await completeOnboarding(page);
|
||||||
await page.waitForURL(/\/app\/[^/]+$/);
|
await page.waitForURL(/\/app\/[^/]+$/);
|
||||||
|
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
const settings = page.getByTestId("user-settings");
|
const settings = page.getByTestId("user-settings");
|
||||||
await expect(settings).toBeVisible();
|
await expect(settings).toBeVisible();
|
||||||
await expect(page.getByTestId("sidebar-search").locator("input")).toHaveAttribute(
|
await expect(page.getByTestId("sidebar-search").locator("input")).toHaveAttribute(
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import {
|
|||||||
activeBotId,
|
activeBotId,
|
||||||
captureScreenshot,
|
captureScreenshot,
|
||||||
completeOnboarding,
|
completeOnboarding,
|
||||||
|
openIntegrations,
|
||||||
realSandboxTimeout,
|
realSandboxTimeout,
|
||||||
rpc,
|
rpc,
|
||||||
signup,
|
signup,
|
||||||
@ -134,7 +135,7 @@ test("takeover, routine, plugins, and export are reachable", async ({ page }, te
|
|||||||
await expect(page.getByText("Monday briefing")).toBeVisible();
|
await expect(page.getByText("Monday briefing")).toBeVisible();
|
||||||
await captureScreenshot(page, testInfo, "10-routine-created");
|
await captureScreenshot(page, testInfo, "10-routine-created");
|
||||||
|
|
||||||
await page.getByText("Integrations").click();
|
await openIntegrations(page);
|
||||||
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
||||||
const featured = page.getByTestId("featured-connectors");
|
const featured = page.getByTestId("featured-connectors");
|
||||||
await expect(featured).toContainText(
|
await expect(featured).toContainText(
|
||||||
@ -178,8 +179,8 @@ test("takeover, routine, plugins, and export are reachable", async ({ page }, te
|
|||||||
await expect(detail.getByLabel("Account label").nth(1)).toHaveValue("Work");
|
await expect(detail.getByLabel("Account label").nth(1)).toHaveValue("Work");
|
||||||
await captureScreenshot(page, testInfo, "11a2-multi-account-plugins");
|
await captureScreenshot(page, testInfo, "11a2-multi-account-plugins");
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Close integrations" }).click();
|
await page.getByRole("button", { name: "Close integrations settings" }).click();
|
||||||
await page.getByText("Integrations").click();
|
await openIntegrations(page);
|
||||||
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
||||||
const gmailTileAgain = page.getByTestId("connection-tile-gmail");
|
const gmailTileAgain = page.getByTestId("connection-tile-gmail");
|
||||||
await expect(gmailTileAgain.getByRole("button", { name: "Added", exact: true })).toBeVisible();
|
await expect(gmailTileAgain.getByRole("button", { name: "Added", exact: true })).toBeVisible();
|
||||||
@ -296,7 +297,7 @@ test("takeover, routine, plugins, and export are reachable", async ({ page }, te
|
|||||||
).toBeVisible();
|
).toBeVisible();
|
||||||
await captureScreenshot(page, testInfo, "11d-provider-emulators");
|
await captureScreenshot(page, testInfo, "11d-provider-emulators");
|
||||||
|
|
||||||
await page.getByRole("button", { name: "Close integrations" }).click();
|
await page.getByRole("button", { name: "Close integrations settings" }).click();
|
||||||
|
|
||||||
await page.getByText("Chief").first().click();
|
await page.getByText("Chief").first().click();
|
||||||
const gear = page.getByRole("button", { name: "Show settings" });
|
const gear = page.getByRole("button", { name: "Show settings" });
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { expect, test } from "@playwright/test";
|
import { expect, test } from "@playwright/test";
|
||||||
import { captureScreenshot, completeOnboarding, signup } from "./helpers";
|
import { captureScreenshot, completeOnboarding, openIntegrations, signup } from "./helpers";
|
||||||
|
|
||||||
test("advanced GraphQL install shows Add GraphQL in MCP, OpenAPI, GraphQL, Executor, Treg order", async ({
|
test("advanced GraphQL install shows Add GraphQL in MCP, OpenAPI, GraphQL, Executor, Treg order", async ({
|
||||||
page,
|
page,
|
||||||
@ -8,7 +8,7 @@ test("advanced GraphQL install shows Add GraphQL in MCP, OpenAPI, GraphQL, Execu
|
|||||||
await signup(page, `graphql-source-${stamp}@rakazo.test`, "password12", `GraphQL ${stamp}`);
|
await signup(page, `graphql-source-${stamp}@rakazo.test`, "password12", `GraphQL ${stamp}`);
|
||||||
await completeOnboarding(page);
|
await completeOnboarding(page);
|
||||||
|
|
||||||
await page.getByText("Integrations").click();
|
await openIntegrations(page);
|
||||||
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
await expect(page.getByPlaceholder("Search apps")).toBeVisible();
|
||||||
await expect(page.getByRole("button", { name: "Add GraphQL", exact: true })).toBeHidden();
|
await expect(page.getByRole("button", { name: "Add GraphQL", exact: true })).toBeHidden();
|
||||||
|
|
||||||
|
|||||||
@ -127,10 +127,9 @@ export async function createBotFromPicker(
|
|||||||
/** Open the user Settings overlay, optionally switching to a sidebar section. */
|
/** Open the user Settings overlay, optionally switching to a sidebar section. */
|
||||||
export async function openUserSettings(
|
export async function openUserSettings(
|
||||||
page: Page,
|
page: Page,
|
||||||
section?: "general" | "models" | "memory" | "voice" | "usage" | "computer" | "updates",
|
section?: "general" | "integrations" | "models" | "memory" | "voice" | "usage" | "computer" | "updates",
|
||||||
) {
|
) {
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
const settings = page.getByTestId("user-settings");
|
const settings = page.getByTestId("user-settings");
|
||||||
await expect(settings).toBeVisible();
|
await expect(settings).toBeVisible();
|
||||||
if (section && section !== "general") {
|
if (section && section !== "general") {
|
||||||
@ -139,6 +138,11 @@ export async function openUserSettings(
|
|||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Open Integrations inside the Settings overlay. */
|
||||||
|
export async function openIntegrations(page: Page) {
|
||||||
|
return openUserSettings(page, "integrations");
|
||||||
|
}
|
||||||
|
|
||||||
/** Create a named bot via RPC for test setup (skips the + picker). */
|
/** Create a named bot via RPC for test setup (skips the + picker). */
|
||||||
export async function createNamedBot(
|
export async function createNamedBot(
|
||||||
page: Page,
|
page: Page,
|
||||||
|
|||||||
@ -245,8 +245,7 @@ test("configured server owners manage providers from settings", async ({ page },
|
|||||||
await signup(page, `configured-owner-${Date.now()}@rakazo.test`, "password12", "Server Owner");
|
await signup(page, `configured-owner-${Date.now()}@rakazo.test`, "password12", "Server Owner");
|
||||||
await expect(page.getByRole("heading", { name: "Server integrations" })).toBeHidden();
|
await expect(page.getByRole("heading", { name: "Server integrations" })).toBeHidden();
|
||||||
await completeOnboarding(page);
|
await completeOnboarding(page);
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
const settings = page.getByTestId("user-settings");
|
const settings = page.getByTestId("user-settings");
|
||||||
const link = settings.getByRole("link", { name: "Server integrations", exact: true });
|
const link = settings.getByRole("link", { name: "Server integrations", exact: true });
|
||||||
await expect(link).toBeVisible();
|
await expect(link).toBeVisible();
|
||||||
|
|||||||
@ -83,7 +83,8 @@ test("connects an MCP server through the OAuth popup callback", async ({ page },
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
await page.getByText("Integrations", { exact: true }).click();
|
await page.getByTestId("settings-entry").click();
|
||||||
|
await page.getByTestId("settings-nav-integrations").click();
|
||||||
await page.getByTestId("integrations-advanced").evaluate((element) => {
|
await page.getByTestId("integrations-advanced").evaluate((element) => {
|
||||||
(element as HTMLDetailsElement).open = true;
|
(element as HTMLDetailsElement).open = true;
|
||||||
});
|
});
|
||||||
|
|||||||
@ -9,12 +9,12 @@ test("settings shell is two-pane and deep-links Models Memory Voice Usage", asyn
|
|||||||
await signup(page, `settings-shell-${stamp}@rakazo.test`, "password12", userName);
|
await signup(page, `settings-shell-${stamp}@rakazo.test`, "password12", userName);
|
||||||
await completeOnboarding(page);
|
await completeOnboarding(page);
|
||||||
|
|
||||||
|
await expect(page.getByTestId("settings-entry")).toBeVisible();
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("user-menu-trigger").click();
|
||||||
const menu = page.locator('[data-slot="popover-content"]');
|
const menu = page.locator('[data-slot="popover-content"]');
|
||||||
await expect(menu.getByRole("button", { name: "Settings", exact: true })).toBeVisible();
|
|
||||||
await expect(menu.getByRole("button", { name: "Usage", exact: true })).toBeVisible();
|
await expect(menu.getByRole("button", { name: "Usage", exact: true })).toBeVisible();
|
||||||
await expect(menu.getByRole("button", { name: "Log out", exact: true })).toBeVisible();
|
await expect(menu.getByRole("button", { name: "Log out", exact: true })).toBeVisible();
|
||||||
await expect(menu.getByRole("button", { name: "Models", exact: true })).toHaveCount(0);
|
await expect(menu.getByRole("button", { name: "Settings", exact: true })).toHaveCount(0);
|
||||||
await captureScreenshot(page, testInfo, "settings-account-menu-lean");
|
await captureScreenshot(page, testInfo, "settings-account-menu-lean");
|
||||||
await page.keyboard.press("Escape");
|
await page.keyboard.press("Escape");
|
||||||
|
|
||||||
@ -25,9 +25,16 @@ test("settings shell is two-pane and deep-links Models Memory Voice Usage", asyn
|
|||||||
"page",
|
"page",
|
||||||
);
|
);
|
||||||
await expect(settings.getByRole("heading", { name: "General", exact: true })).toBeVisible();
|
await expect(settings.getByRole("heading", { name: "General", exact: true })).toBeVisible();
|
||||||
|
await expect(settings.getByTestId("live-trading-banner")).toBeVisible();
|
||||||
await expect(settings.getByRole("heading", { name: "Account", exact: true })).toBeVisible();
|
await expect(settings.getByRole("heading", { name: "Account", exact: true })).toBeVisible();
|
||||||
await captureScreenshot(page, testInfo, "settings-shell-general");
|
await captureScreenshot(page, testInfo, "settings-shell-general");
|
||||||
|
|
||||||
|
await settings.getByTestId("settings-nav-integrations").click();
|
||||||
|
await expect(settings).toHaveAttribute("data-settings-section", "integrations");
|
||||||
|
await expect(settings.getByRole("heading", { name: "Integrations", exact: true })).toBeVisible();
|
||||||
|
await expect(settings.getByTestId("integrations-settings")).toBeVisible();
|
||||||
|
await captureScreenshot(page, testInfo, "settings-shell-integrations");
|
||||||
|
|
||||||
await settings.getByTestId("settings-nav-models").click();
|
await settings.getByTestId("settings-nav-models").click();
|
||||||
await expect(settings).toHaveAttribute("data-settings-section", "models");
|
await expect(settings).toHaveAttribute("data-settings-section", "models");
|
||||||
await expect(settings.getByRole("heading", { name: "Models", exact: true })).toBeVisible();
|
await expect(settings.getByRole("heading", { name: "Models", exact: true })).toBeVisible();
|
||||||
|
|||||||
@ -49,8 +49,7 @@ test("account settings appearance control switches to light mode", async ({ page
|
|||||||
await signup(page, `ui-appearance-${stamp}@rakazo.test`, "password12", "Appearance QA");
|
await signup(page, `ui-appearance-${stamp}@rakazo.test`, "password12", "Appearance QA");
|
||||||
await completeOnboarding(page, testInfo);
|
await completeOnboarding(page, testInfo);
|
||||||
|
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
const settings = page.getByTestId("user-settings");
|
const settings = page.getByTestId("user-settings");
|
||||||
await expect(settings).toBeVisible();
|
await expect(settings).toBeVisible();
|
||||||
await expect(settings.getByRole("heading", { name: "Appearance", exact: true })).toBeVisible();
|
await expect(settings.getByRole("heading", { name: "Appearance", exact: true })).toBeVisible();
|
||||||
@ -84,8 +83,7 @@ test("account settings appearance control switches to light mode", async ({ page
|
|||||||
await expect(inlinePath).toHaveCSS("color", "rgb(26, 26, 26)");
|
await expect(inlinePath).toHaveCSS("color", "rgb(26, 26, 26)");
|
||||||
await captureScreenshot(page, testInfo, "inline-code-light");
|
await captureScreenshot(page, testInfo, "inline-code-light");
|
||||||
|
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
await expect(settings).toBeVisible();
|
await expect(settings).toBeVisible();
|
||||||
await settings.getByTestId("ui-appearance-dark").click();
|
await settings.getByTestId("ui-appearance-dark").click();
|
||||||
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
await expect(page.locator("html")).toHaveAttribute("data-theme", "dark");
|
||||||
@ -114,9 +112,9 @@ test("sidebar bot rows hover with the same tone as the integrations row", async
|
|||||||
const chiefBg = await readChiefBg();
|
const chiefBg = await readChiefBg();
|
||||||
await captureScreenshot(page, testInfo, "sidebar-row-hover");
|
await captureScreenshot(page, testInfo, "sidebar-row-hover");
|
||||||
|
|
||||||
const integrations = sidebar.getByRole("button", { name: "Integrations", exact: true });
|
const settingsEntry = sidebar.getByTestId("settings-entry");
|
||||||
await integrations.hover();
|
await settingsEntry.hover();
|
||||||
await expect
|
await expect
|
||||||
.poll(() => integrations.evaluate((el) => getComputedStyle(el).backgroundColor))
|
.poll(() => settingsEntry.evaluate((el) => getComputedStyle(el).backgroundColor))
|
||||||
.toBe(chiefBg);
|
.toBe(chiefBg);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -77,8 +77,7 @@ test("account settings language picker includes Russian and persists it", async
|
|||||||
await signup(page, `ui-locale-ru-${stamp}@rakazo.test`, "password12", "Locale QA");
|
await signup(page, `ui-locale-ru-${stamp}@rakazo.test`, "password12", "Locale QA");
|
||||||
await completeOnboarding(page, testInfo);
|
await completeOnboarding(page, testInfo);
|
||||||
|
|
||||||
await page.getByTestId("user-menu-trigger").click();
|
await page.getByTestId("settings-entry").click();
|
||||||
await page.getByRole("button", { name: "Settings", exact: true }).click();
|
|
||||||
const settings = page.getByTestId("user-settings");
|
const settings = page.getByTestId("user-settings");
|
||||||
await expect(settings).toBeVisible();
|
await expect(settings).toBeVisible();
|
||||||
|
|
||||||
|
|||||||
@ -477,6 +477,9 @@
|
|||||||
"Server URL": "Server-URL",
|
"Server URL": "Server-URL",
|
||||||
"Set up voice to call": "Stimme für Anrufe einrichten",
|
"Set up voice to call": "Stimme für Anrufe einrichten",
|
||||||
"Settings": "Einstellungen",
|
"Settings": "Einstellungen",
|
||||||
|
"Discover now": "Jetzt entdecken",
|
||||||
|
"Live Trading": "Live Trading",
|
||||||
|
"Close integrations settings": "Integrationen-Einstellungen schließen",
|
||||||
"Settings: General": "Einstellungen: Allgemein",
|
"Settings: General": "Einstellungen: Allgemein",
|
||||||
"Settings: Usage": "Einstellungen: Nutzung",
|
"Settings: Usage": "Einstellungen: Nutzung",
|
||||||
"Setup help": "Einrichtungshilfe",
|
"Setup help": "Einrichtungshilfe",
|
||||||
|
|||||||
@ -200,9 +200,10 @@ msgstr "Aktionen für {0}"
|
|||||||
msgid "Actions for space"
|
msgid "Actions for space"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/AccountSettingsOverlay.tsx:76
|
||||||
#: src/pages/RoutineEditor.tsx:297
|
#: src/pages/RoutineEditor.tsx:297
|
||||||
msgid "Active"
|
msgid "Active"
|
||||||
msgstr ""
|
msgstr "Aktiv"
|
||||||
|
|
||||||
#: src/pages/ModelSettingsOverlay.tsx:393
|
#: src/pages/ModelSettingsOverlay.tsx:393
|
||||||
msgid "Active model"
|
msgid "Active model"
|
||||||
@ -814,6 +815,10 @@ msgstr "Bildvorschau schließen"
|
|||||||
msgid "Close integrations"
|
msgid "Close integrations"
|
||||||
msgstr "Integrationen schließen"
|
msgstr "Integrationen schließen"
|
||||||
|
|
||||||
|
#: src/pages/SettingsOverlay.tsx:108
|
||||||
|
msgid "Close integrations settings"
|
||||||
|
msgstr "Integrationen-Einstellungen schließen"
|
||||||
|
|
||||||
#: src/pages/McpServersOverlay.tsx:258
|
#: src/pages/McpServersOverlay.tsx:258
|
||||||
msgid "Close MCP servers"
|
msgid "Close MCP servers"
|
||||||
msgstr "MCP-Server schließen"
|
msgstr "MCP-Server schließen"
|
||||||
@ -2098,6 +2103,10 @@ msgstr ""
|
|||||||
msgid "Listen"
|
msgid "Listen"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: src/pages/AccountSettingsOverlay.tsx:72
|
||||||
|
msgid "Live Trading"
|
||||||
|
msgstr "Live Trading"
|
||||||
|
|
||||||
#: src/pages/ExternalConversationSettings.tsx:93
|
#: src/pages/ExternalConversationSettings.tsx:93
|
||||||
msgid "Listening"
|
msgid "Listening"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -3864,6 +3873,10 @@ msgstr "Hilfe & Vibe-Passung"
|
|||||||
msgid "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
msgid "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
||||||
msgstr "Vertrau uns: Es sind viele von uns. Einfach anfangen — Thermomix oder harte Mathe, gleicher ruhiger Einstieg — mit Experten-Spezialisten wenn du sie brauchst, kein mittelmäßiger Alleskönner."
|
msgstr "Vertrau uns: Es sind viele von uns. Einfach anfangen — Thermomix oder harte Mathe, gleicher ruhiger Einstieg — mit Experten-Spezialisten wenn du sie brauchst, kein mittelmäßiger Alleskönner."
|
||||||
|
|
||||||
|
#: src/pages/AccountSettingsOverlay.tsx:88
|
||||||
|
msgid "Discover now"
|
||||||
|
msgstr "Jetzt entdecken"
|
||||||
|
|
||||||
#: src/components/onboarding/OnboardingDiscoveryShell.tsx:96
|
#: src/components/onboarding/OnboardingDiscoveryShell.tsx:96
|
||||||
msgid "Discovery"
|
msgid "Discovery"
|
||||||
msgstr "Entdeckung"
|
msgstr "Entdeckung"
|
||||||
|
|||||||
@ -814,6 +814,10 @@ msgstr "Close image preview"
|
|||||||
msgid "Close integrations"
|
msgid "Close integrations"
|
||||||
msgstr "Close integrations"
|
msgstr "Close integrations"
|
||||||
|
|
||||||
|
#: src/pages/SettingsOverlay.tsx:108
|
||||||
|
msgid "Close integrations settings"
|
||||||
|
msgstr "Close integrations settings"
|
||||||
|
|
||||||
#: src/pages/McpServersOverlay.tsx:258
|
#: src/pages/McpServersOverlay.tsx:258
|
||||||
msgid "Close MCP servers"
|
msgid "Close MCP servers"
|
||||||
msgstr "Close MCP servers"
|
msgstr "Close MCP servers"
|
||||||
@ -2098,6 +2102,10 @@ msgstr "Link a chat app"
|
|||||||
msgid "Listen"
|
msgid "Listen"
|
||||||
msgstr "Listen"
|
msgstr "Listen"
|
||||||
|
|
||||||
|
#: src/pages/AccountSettingsOverlay.tsx:72
|
||||||
|
msgid "Live Trading"
|
||||||
|
msgstr "Live Trading"
|
||||||
|
|
||||||
#: src/pages/ExternalConversationSettings.tsx:93
|
#: src/pages/ExternalConversationSettings.tsx:93
|
||||||
msgid "Listening"
|
msgid "Listening"
|
||||||
msgstr "Listening"
|
msgstr "Listening"
|
||||||
@ -3864,6 +3872,10 @@ msgstr "Help & vibe fit"
|
|||||||
msgid "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
msgid "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
||||||
msgstr "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
msgstr "Trust us: there are many of us. Simple to start — Thermomix or hard math, same calm entry — with expert-depth specialists when you need them, not one mediocre everything-bot."
|
||||||
|
|
||||||
|
#: src/pages/AccountSettingsOverlay.tsx:88
|
||||||
|
msgid "Discover now"
|
||||||
|
msgstr "Discover now"
|
||||||
|
|
||||||
#: src/components/onboarding/OnboardingDiscoveryShell.tsx:96
|
#: src/components/onboarding/OnboardingDiscoveryShell.tsx:96
|
||||||
msgid "Discovery"
|
msgid "Discovery"
|
||||||
msgstr "Discovery"
|
msgstr "Discovery"
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import { Trans, useLingui } from "@lingui/react/macro";
|
import { Trans, useLingui } from "@lingui/react/macro";
|
||||||
import type { AvatarStyle } from "@rakazo/contracts";
|
import type { AvatarStyle } from "@rakazo/contracts";
|
||||||
import { BotAvatar, Button, Field, FieldLabel, Input, Toggle } from "@rakazo/ui-web";
|
import { BotAvatar, Button, Field, FieldLabel, Input, Toggle } from "@rakazo/ui-web";
|
||||||
import { ChevronDown } from "lucide-react";
|
import { Activity, ChevronDown } from "lucide-react";
|
||||||
import {
|
import {
|
||||||
type KeyboardEvent as ReactKeyboardEvent,
|
type KeyboardEvent as ReactKeyboardEvent,
|
||||||
type RefObject,
|
type RefObject,
|
||||||
@ -33,8 +33,66 @@ export type SettingsGeneralProps = {
|
|||||||
messagingEnabled?: boolean;
|
messagingEnabled?: boolean;
|
||||||
onOpenMessaging?: () => void;
|
onOpenMessaging?: () => void;
|
||||||
isDeploymentOwner?: boolean;
|
isDeploymentOwner?: boolean;
|
||||||
|
tradingMode?: "live" | "paper";
|
||||||
|
onDiscoverLiveTrading?: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function LiveTradingBanner({
|
||||||
|
tradingMode,
|
||||||
|
onDiscover,
|
||||||
|
}: {
|
||||||
|
tradingMode: "live" | "paper";
|
||||||
|
onDiscover: () => void;
|
||||||
|
}) {
|
||||||
|
const isLive = tradingMode === "live";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section
|
||||||
|
className={`rounded-xl border px-4 py-4 ${
|
||||||
|
isLive ? "border-amber-500/30 bg-amber-500/5" : "border-border bg-card/40"
|
||||||
|
}`}
|
||||||
|
data-testid="live-trading-banner"
|
||||||
|
>
|
||||||
|
<div className="flex items-center justify-between gap-4">
|
||||||
|
<div className="flex min-w-0 items-center gap-3">
|
||||||
|
<span
|
||||||
|
className={`grid size-10 shrink-0 place-items-center rounded-lg ${
|
||||||
|
isLive
|
||||||
|
? "bg-amber-500/10 text-amber-500 shadow-[0_0_12px_rgba(245,158,11,0.2)]"
|
||||||
|
: "bg-muted text-muted-foreground"
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
<Activity
|
||||||
|
strokeWidth={1.8}
|
||||||
|
className={`size-5 ${isLive ? "animate-pulse" : ""}`}
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
<div className="min-w-0">
|
||||||
|
<h3 className="text-ui-body font-medium text-foreground">
|
||||||
|
<Trans>Live Trading</Trans>
|
||||||
|
</h3>
|
||||||
|
{isLive ? (
|
||||||
|
<p className="mt-0.5 text-ui-body-sm text-muted-foreground/70">
|
||||||
|
<Trans>Active</Trans>
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!isLive ? (
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="secondary"
|
||||||
|
className="shrink-0 rounded-full"
|
||||||
|
onClick={onDiscover}
|
||||||
|
>
|
||||||
|
<Trans>Discover now</Trans>
|
||||||
|
</Button>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function GeneralSettingsPanels({
|
export function GeneralSettingsPanels({
|
||||||
email,
|
email,
|
||||||
name,
|
name,
|
||||||
@ -43,6 +101,8 @@ export function GeneralSettingsPanels({
|
|||||||
messagingEnabled = false,
|
messagingEnabled = false,
|
||||||
onOpenMessaging,
|
onOpenMessaging,
|
||||||
isDeploymentOwner = false,
|
isDeploymentOwner = false,
|
||||||
|
tradingMode,
|
||||||
|
onDiscoverLiveTrading,
|
||||||
}: SettingsGeneralProps) {
|
}: SettingsGeneralProps) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const [locale, setLocale] = useState<UiLocale>(() => getActiveUiLocale());
|
const [locale, setLocale] = useState<UiLocale>(() => getActiveUiLocale());
|
||||||
@ -78,6 +138,9 @@ export function GeneralSettingsPanels({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="space-y-5">
|
<div className="space-y-5">
|
||||||
|
{tradingMode && onDiscoverLiveTrading ? (
|
||||||
|
<LiveTradingBanner tradingMode={tradingMode} onDiscover={onDiscoverLiveTrading} />
|
||||||
|
) : null}
|
||||||
<section className="rounded-xl border border-border px-4 py-4">
|
<section className="rounded-xl border border-border px-4 py-4">
|
||||||
<h3 className="text-ui-body font-medium text-foreground">
|
<h3 className="text-ui-body font-medium text-foreground">
|
||||||
<Trans>Account</Trans>
|
<Trans>Account</Trans>
|
||||||
|
|||||||
@ -73,10 +73,13 @@ export function PluginsOverlay({
|
|||||||
onClose,
|
onClose,
|
||||||
onOpenMcp,
|
onOpenMcp,
|
||||||
activeBotId,
|
activeBotId,
|
||||||
|
embedded = false,
|
||||||
}: {
|
}: {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onOpenMcp?: () => void;
|
onOpenMcp?: () => void;
|
||||||
activeBotId?: string;
|
activeBotId?: string;
|
||||||
|
/** Render panel body only for the shared Settings shell. */
|
||||||
|
embedded?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const [setupOpen, setSetupOpen] = useState(false);
|
const [setupOpen, setSetupOpen] = useState(false);
|
||||||
@ -663,44 +666,27 @@ export function PluginsOverlay({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
const searchField = !detailItem ? (
|
||||||
<Dialog
|
<div className={embedded ? "px-6 pt-1 sm:px-8" : "px-8 pt-4"}>
|
||||||
open
|
<Input
|
||||||
onOpenChange={(open) => {
|
value={query}
|
||||||
if (!open) onClose();
|
onChange={(event) => {
|
||||||
}}
|
setQuery(event.target.value);
|
||||||
>
|
setVisibleCount(CONNECTION_CATALOG_PAGE_SIZE);
|
||||||
<DialogContent
|
}}
|
||||||
showCloseButton={false}
|
aria-label={t`Search apps`}
|
||||||
className="flex h-[760px] max-h-[calc(100%-2rem)] w-[1080px] max-w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden rounded-2xl bg-card p-0 sm:max-w-[1080px]"
|
placeholder={t`Search apps`}
|
||||||
|
className="h-11 rounded-xl px-4 md:text-ui-body"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
) : null;
|
||||||
|
|
||||||
|
const catalogBody = (
|
||||||
|
<>
|
||||||
|
<div
|
||||||
|
id="integration-list"
|
||||||
|
className={`rk-scroll flex-1 overflow-y-auto py-6 ${embedded ? "px-6 sm:px-8" : "px-8"}`}
|
||||||
>
|
>
|
||||||
<DialogHeader className="flex-row items-start justify-between px-8 pt-7">
|
|
||||||
<DialogTitle className="text-2xl text-foreground">
|
|
||||||
<Trans>Integrations</Trans>
|
|
||||||
</DialogTitle>
|
|
||||||
<DialogClose
|
|
||||||
render={<Button variant="ghost" size="icon-sm" aria-label={t`Close integrations`} />}
|
|
||||||
>
|
|
||||||
<X />
|
|
||||||
</DialogClose>
|
|
||||||
</DialogHeader>
|
|
||||||
|
|
||||||
{!detailItem ? (
|
|
||||||
<div className="px-8 pt-4">
|
|
||||||
<Input
|
|
||||||
value={query}
|
|
||||||
onChange={(event) => {
|
|
||||||
setQuery(event.target.value);
|
|
||||||
setVisibleCount(CONNECTION_CATALOG_PAGE_SIZE);
|
|
||||||
}}
|
|
||||||
aria-label={t`Search apps`}
|
|
||||||
placeholder={t`Search apps`}
|
|
||||||
className="h-11 rounded-xl px-4 md:text-ui-body"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
|
|
||||||
<div id="integration-list" className="rk-scroll flex-1 overflow-y-auto px-8 py-6">
|
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
className="mb-4"
|
className="mb-4"
|
||||||
@ -1165,6 +1151,43 @@ export function PluginsOverlay({
|
|||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
if (embedded) {
|
||||||
|
return (
|
||||||
|
<div className="flex min-h-0 flex-1 flex-col overflow-hidden" data-testid="integrations-settings">
|
||||||
|
{searchField}
|
||||||
|
{catalogBody}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog
|
||||||
|
open
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (!open) onClose();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DialogContent
|
||||||
|
showCloseButton={false}
|
||||||
|
className="flex h-[760px] max-h-[calc(100%-2rem)] w-[1080px] max-w-[calc(100%-2rem)] flex-col gap-0 overflow-hidden rounded-2xl bg-card p-0 sm:max-w-[1080px]"
|
||||||
|
>
|
||||||
|
<DialogHeader className="flex-row items-start justify-between px-8 pt-7">
|
||||||
|
<DialogTitle className="text-2xl text-foreground">
|
||||||
|
<Trans>Integrations</Trans>
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogClose
|
||||||
|
render={<Button variant="ghost" size="icon-sm" aria-label={t`Close integrations`} />}
|
||||||
|
>
|
||||||
|
<X />
|
||||||
|
</DialogClose>
|
||||||
|
</DialogHeader>
|
||||||
|
|
||||||
|
{searchField}
|
||||||
|
|
||||||
|
{catalogBody}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,7 +1,17 @@
|
|||||||
import { useLingui } from "@lingui/react/macro";
|
import { useLingui } from "@lingui/react/macro";
|
||||||
import type { AvatarStyle, SpaceMemoryConfig } from "@rakazo/contracts";
|
import type { AvatarStyle, SpaceMemoryConfig } from "@rakazo/contracts";
|
||||||
import { Button, Dialog, DialogClose, DialogContent, DialogTitle } from "@rakazo/ui-web";
|
import { Button, Dialog, DialogClose, DialogContent, DialogTitle } from "@rakazo/ui-web";
|
||||||
import { Brain, CloudDownload, Cpu, Gauge, Monitor, Settings, Volume2, XIcon } from "lucide-react";
|
import {
|
||||||
|
Brain,
|
||||||
|
CloudDownload,
|
||||||
|
Cpu,
|
||||||
|
Gauge,
|
||||||
|
LayoutGrid,
|
||||||
|
Monitor,
|
||||||
|
Settings,
|
||||||
|
Volume2,
|
||||||
|
XIcon,
|
||||||
|
} from "lucide-react";
|
||||||
import { type ComponentType, useEffect, useRef, useState } from "react";
|
import { type ComponentType, useEffect, useRef, useState } from "react";
|
||||||
import { computersAreUnavailable } from "../components/ComputersUnavailableHint";
|
import { computersAreUnavailable } from "../components/ComputersUnavailableHint";
|
||||||
import {
|
import {
|
||||||
@ -12,10 +22,12 @@ import {
|
|||||||
} from "./AccountSettingsOverlay";
|
} from "./AccountSettingsOverlay";
|
||||||
import { MemorySettingsOverlay } from "./MemorySettingsOverlay";
|
import { MemorySettingsOverlay } from "./MemorySettingsOverlay";
|
||||||
import { ModelSettingsOverlay } from "./ModelSettingsOverlay";
|
import { ModelSettingsOverlay } from "./ModelSettingsOverlay";
|
||||||
|
import { PluginsOverlay } from "./PluginsOverlay";
|
||||||
import { VoiceSettingsOverlay } from "./VoiceSettingsOverlay";
|
import { VoiceSettingsOverlay } from "./VoiceSettingsOverlay";
|
||||||
|
|
||||||
export type SettingsSection =
|
export type SettingsSection =
|
||||||
| "general"
|
| "general"
|
||||||
|
| "integrations"
|
||||||
| "models"
|
| "models"
|
||||||
| "memory"
|
| "memory"
|
||||||
| "voice"
|
| "voice"
|
||||||
@ -44,6 +56,10 @@ export function SettingsOverlay({
|
|||||||
onMemoryConfigChange,
|
onMemoryConfigChange,
|
||||||
onClose,
|
onClose,
|
||||||
onVoiceStatusMaybeChanged,
|
onVoiceStatusMaybeChanged,
|
||||||
|
activeBotId,
|
||||||
|
onOpenMcp,
|
||||||
|
tradingMode,
|
||||||
|
onDiscoverLiveTrading,
|
||||||
}: {
|
}: {
|
||||||
email?: string | null;
|
email?: string | null;
|
||||||
name: string;
|
name: string;
|
||||||
@ -59,6 +75,10 @@ export function SettingsOverlay({
|
|||||||
onMemoryConfigChange: (config: SpaceMemoryConfig | null) => void;
|
onMemoryConfigChange: (config: SpaceMemoryConfig | null) => void;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onVoiceStatusMaybeChanged?: () => void | Promise<void>;
|
onVoiceStatusMaybeChanged?: () => void | Promise<void>;
|
||||||
|
activeBotId?: string;
|
||||||
|
onOpenMcp?: () => void;
|
||||||
|
tradingMode?: "live" | "paper";
|
||||||
|
onDiscoverLiveTrading?: () => void;
|
||||||
}) {
|
}) {
|
||||||
const { t } = useLingui();
|
const { t } = useLingui();
|
||||||
const panelRef = useRef<HTMLDivElement>(null);
|
const panelRef = useRef<HTMLDivElement>(null);
|
||||||
@ -81,6 +101,7 @@ export function SettingsOverlay({
|
|||||||
|
|
||||||
const navItems: NavItem[] = [
|
const navItems: NavItem[] = [
|
||||||
{ id: "general", label: t`General`, icon: Settings },
|
{ id: "general", label: t`General`, icon: Settings },
|
||||||
|
{ id: "integrations", label: t`Integrations`, icon: LayoutGrid },
|
||||||
{ id: "models", label: t`Models`, icon: Cpu },
|
{ id: "models", label: t`Models`, icon: Cpu },
|
||||||
{ id: "memory", label: t`Memory`, icon: Brain },
|
{ id: "memory", label: t`Memory`, icon: Brain },
|
||||||
{ id: "voice", label: t`Voice`, icon: Volume2 },
|
{ id: "voice", label: t`Voice`, icon: Volume2 },
|
||||||
@ -100,7 +121,9 @@ export function SettingsOverlay({
|
|||||||
? t`Close memory settings`
|
? t`Close memory settings`
|
||||||
: section === "voice"
|
: section === "voice"
|
||||||
? t`Close voice settings`
|
? t`Close voice settings`
|
||||||
: t`Close user settings`;
|
: section === "integrations"
|
||||||
|
? t`Close integrations settings`
|
||||||
|
: t`Close user settings`;
|
||||||
|
|
||||||
async function refreshVoiceStatus() {
|
async function refreshVoiceStatus() {
|
||||||
await onVoiceStatusMaybeChanged?.();
|
await onVoiceStatusMaybeChanged?.();
|
||||||
@ -115,7 +138,7 @@ export function SettingsOverlay({
|
|||||||
leaveSettings(onClose);
|
leaveSettings(onClose);
|
||||||
}
|
}
|
||||||
|
|
||||||
const widePane = section === "models" || section === "voice";
|
const widePane = section === "models" || section === "voice" || section === "integrations";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog
|
<Dialog
|
||||||
@ -189,7 +212,10 @@ export function SettingsOverlay({
|
|||||||
|
|
||||||
<div
|
<div
|
||||||
className={`min-h-0 flex-1 ${
|
className={`min-h-0 flex-1 ${
|
||||||
section === "models" || section === "voice" || section === "memory"
|
section === "models" ||
|
||||||
|
section === "voice" ||
|
||||||
|
section === "memory" ||
|
||||||
|
section === "integrations"
|
||||||
? "flex flex-col overflow-hidden"
|
? "flex flex-col overflow-hidden"
|
||||||
: "rk-scroll overflow-y-auto overscroll-contain px-6 pb-6 pt-5 sm:px-8 sm:pb-8"
|
: "rk-scroll overflow-y-auto overscroll-contain px-6 pb-6 pt-5 sm:px-8 sm:pb-8"
|
||||||
}`}
|
}`}
|
||||||
@ -205,6 +231,16 @@ export function SettingsOverlay({
|
|||||||
onOpenMessaging ? () => leaveSettings(onOpenMessaging) : undefined
|
onOpenMessaging ? () => leaveSettings(onOpenMessaging) : undefined
|
||||||
}
|
}
|
||||||
isDeploymentOwner={isDeploymentOwner}
|
isDeploymentOwner={isDeploymentOwner}
|
||||||
|
tradingMode={tradingMode}
|
||||||
|
onDiscoverLiveTrading={onDiscoverLiveTrading}
|
||||||
|
/>
|
||||||
|
) : null}
|
||||||
|
{section === "integrations" ? (
|
||||||
|
<PluginsOverlay
|
||||||
|
embedded
|
||||||
|
activeBotId={activeBotId}
|
||||||
|
onClose={requestClose}
|
||||||
|
onOpenMcp={onOpenMcp ? () => leaveSettings(onOpenMcp) : undefined}
|
||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
{section === "usage" ? (
|
{section === "usage" ? (
|
||||||
|
|||||||
@ -93,7 +93,6 @@ import {
|
|||||||
Copy,
|
Copy,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
Gauge,
|
Gauge,
|
||||||
LayoutGrid,
|
|
||||||
Lock,
|
Lock,
|
||||||
LogOut,
|
LogOut,
|
||||||
Maximize2,
|
Maximize2,
|
||||||
@ -266,9 +265,6 @@ const SettingsOverlay = lazy(() =>
|
|||||||
const PeerMessagesOverlay = lazy(() =>
|
const PeerMessagesOverlay = lazy(() =>
|
||||||
import("./PeerMessagesOverlay").then((module) => ({ default: module.PeerMessagesOverlay })),
|
import("./PeerMessagesOverlay").then((module) => ({ default: module.PeerMessagesOverlay })),
|
||||||
);
|
);
|
||||||
const PluginsOverlay = lazy(() =>
|
|
||||||
import("./PluginsOverlay").then((module) => ({ default: module.PluginsOverlay })),
|
|
||||||
);
|
|
||||||
const McpServersOverlay = lazy(() =>
|
const McpServersOverlay = lazy(() =>
|
||||||
import("./McpServersOverlay").then((module) => ({ default: module.McpServersOverlay })),
|
import("./McpServersOverlay").then((module) => ({ default: module.McpServersOverlay })),
|
||||||
);
|
);
|
||||||
@ -358,15 +354,14 @@ export function ShellPage() {
|
|||||||
return "live";
|
return "live";
|
||||||
});
|
});
|
||||||
|
|
||||||
const toggleTradingMode = useCallback(() => {
|
const discoverLiveTrading = useCallback(() => {
|
||||||
setTradingMode((prev) => {
|
setTradingMode("live");
|
||||||
const next = prev === "live" ? "paper" : "live";
|
if (typeof window !== "undefined" && window.localStorage) {
|
||||||
if (typeof window !== "undefined" && window.localStorage) {
|
window.localStorage.setItem("evoo_trading_mode", "live");
|
||||||
window.localStorage.setItem("evoo_trading_mode", next);
|
}
|
||||||
}
|
setActiveNavPillar("heute");
|
||||||
return next;
|
navigate("/app");
|
||||||
});
|
}, [navigate]);
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (botId) setActiveNavPillar("team");
|
if (botId) setActiveNavPillar("team");
|
||||||
@ -539,7 +534,6 @@ export function ShellPage() {
|
|||||||
function updateSnapshot(update: (prev: ThreadSnapshot | null) => ThreadSnapshot | null) {
|
function updateSnapshot(update: (prev: ThreadSnapshot | null) => ThreadSnapshot | null) {
|
||||||
commitSnapshot(update(snapshotRef.current));
|
commitSnapshot(update(snapshotRef.current));
|
||||||
}
|
}
|
||||||
const [pluginsOpen, setPluginsOpen] = useState(false);
|
|
||||||
const [mcpOpen, setMcpOpen] = useState(false);
|
const [mcpOpen, setMcpOpen] = useState(false);
|
||||||
const [settingsOpen, setSettingsOpen] = useState(false);
|
const [settingsOpen, setSettingsOpen] = useState(false);
|
||||||
const [settingsSection, setSettingsSection] = useState<SettingsSection>("general");
|
const [settingsSection, setSettingsSection] = useState<SettingsSection>("general");
|
||||||
@ -3666,35 +3660,16 @@ export function ShellPage() {
|
|||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={toggleTradingMode}
|
data-testid="settings-entry"
|
||||||
className="mx-3 lg:mx-5 2xl:mx-7 mb-1 flex items-center gap-3 rounded-xl px-2.5 py-2 hover:bg-sidebar-accent group"
|
aria-label={t`Settings`}
|
||||||
>
|
onClick={() => openSettings("general")}
|
||||||
<span
|
|
||||||
className={`grid h-[30px] w-[30px] place-items-center rounded-lg transition-colors ${
|
|
||||||
tradingMode === "live"
|
|
||||||
? "bg-amber-500/10 text-amber-500 shadow-[0_0_12px_rgba(245,158,11,0.2)]"
|
|
||||||
: "bg-indigo-500/10 text-indigo-400"
|
|
||||||
}`}
|
|
||||||
>
|
|
||||||
<Activity
|
|
||||||
strokeWidth={1.8}
|
|
||||||
className={`size-icon-md ${tradingMode === "live" ? "animate-pulse" : ""}`}
|
|
||||||
/>
|
|
||||||
</span>
|
|
||||||
<span className="text-ui-body font-medium text-foreground/90">
|
|
||||||
{tradingMode === "live" ? "Live Trading" : "Paper Sim"}
|
|
||||||
</span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => setPluginsOpen(true)}
|
|
||||||
className="mx-3 lg:mx-5 2xl:mx-7 mb-1 flex items-center gap-3 rounded-xl px-2.5 py-2 hover:bg-sidebar-accent"
|
className="mx-3 lg:mx-5 2xl:mx-7 mb-1 flex items-center gap-3 rounded-xl px-2.5 py-2 hover:bg-sidebar-accent"
|
||||||
>
|
>
|
||||||
<span className="grid h-[30px] w-[30px] place-items-center rounded-lg bg-accent text-foreground/80">
|
<span className="grid h-[30px] w-[30px] place-items-center rounded-lg bg-accent text-foreground/80">
|
||||||
<LayoutGrid strokeWidth={1.8} className="size-icon-md" />
|
<Settings strokeWidth={1.8} className="size-icon-md" />
|
||||||
</span>
|
</span>
|
||||||
<span className="text-ui-body font-medium text-foreground/90">
|
<span className="text-ui-body font-medium text-foreground/90">
|
||||||
<Trans>Integrations</Trans>
|
<Trans>Settings</Trans>
|
||||||
</span>
|
</span>
|
||||||
</button>
|
</button>
|
||||||
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
|
<Popover open={menuOpen} onOpenChange={setMenuOpen}>
|
||||||
@ -3713,18 +3688,6 @@ export function ShellPage() {
|
|||||||
align="start"
|
align="start"
|
||||||
className="w-[calc(316px-1.5rem)] max-w-[calc(100vw-3rem)] gap-0 p-1 data-closed:animate-none"
|
className="w-[calc(316px-1.5rem)] max-w-[calc(100vw-3rem)] gap-0 p-1 data-closed:animate-none"
|
||||||
>
|
>
|
||||||
<Button
|
|
||||||
variant="ghost"
|
|
||||||
className="w-full justify-start font-normal"
|
|
||||||
aria-label={t`Settings`}
|
|
||||||
onClick={() => {
|
|
||||||
setMenuOpen(false);
|
|
||||||
openSettings("general");
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Settings className="text-muted-foreground" strokeWidth={1.75} />
|
|
||||||
<Trans>Settings</Trans>
|
|
||||||
</Button>
|
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="w-full justify-start font-normal"
|
className="w-full justify-start font-normal"
|
||||||
@ -4841,16 +4804,6 @@ export function ShellPage() {
|
|||||||
/>
|
/>
|
||||||
) : null}
|
) : null}
|
||||||
|
|
||||||
{pluginsOpen ? (
|
|
||||||
<PluginsOverlay
|
|
||||||
activeBotId={activeBotId.current}
|
|
||||||
onClose={() => setPluginsOpen(false)}
|
|
||||||
onOpenMcp={() => {
|
|
||||||
setPluginsOpen(false);
|
|
||||||
setMcpOpen(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
) : null}
|
|
||||||
{mcpOpen ? <McpServersOverlay onClose={() => setMcpOpen(false)} /> : null}
|
{mcpOpen ? <McpServersOverlay onClose={() => setMcpOpen(false)} /> : null}
|
||||||
{messagingSettingsOpen ? (
|
{messagingSettingsOpen ? (
|
||||||
<MessagingSettingsOverlay onClose={() => setMessagingSettingsOpen(false)} />
|
<MessagingSettingsOverlay onClose={() => setMessagingSettingsOpen(false)} />
|
||||||
@ -4891,6 +4844,13 @@ export function ShellPage() {
|
|||||||
setVoiceStatus(null);
|
setVoiceStatus(null);
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
|
activeBotId={activeBotId.current}
|
||||||
|
tradingMode={tradingMode}
|
||||||
|
onDiscoverLiveTrading={discoverLiveTrading}
|
||||||
|
onOpenMcp={() => {
|
||||||
|
setSettingsOpen(false);
|
||||||
|
setMcpOpen(true);
|
||||||
|
}}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setSettingsOpen(false);
|
setSettingsOpen(false);
|
||||||
setSettingsSection("general");
|
setSettingsSection("general");
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user