diff --git a/plugins/example-plugin/src/nemo_example_plugin/web/dist/index.js b/plugins/example-plugin/src/nemo_example_plugin/web/dist/index.js index 28fb66c012..c240f91e88 100644 --- a/plugins/example-plugin/src/nemo_example_plugin/web/dist/index.js +++ b/plugins/example-plugin/src/nemo_example_plugin/web/dist/index.js @@ -3,15 +3,15 @@ import { Button as e, Flex as t, Stack as n, Text as r } from "@nvidia/foundations-react-core"; import { NavLink as i, Navigate as a, Outlet as o, Route as s, Routes as c } from "react-router"; import { RelativeTime as l, StatusBadge as u, StudioDataView as d, TableEmptyState as f, useStudioDataViewState as p } from "@nemo/common"; -import { useCallback as m } from "react"; -import { jsx as h, jsxs as g } from "react/jsx-runtime"; +import { useCallback as m, useEffect as h } from "react"; +import { jsx as g, jsxs as _ } from "react/jsx-runtime"; //#region src/paths.ts -var _ = (e, t) => `/workspaces/${e}/plugin/example/${t}`, v = [ +var v = (e, t) => `/workspaces/${e}/plugin/example/${t}`, y = [ "overview", "auth", "workspace", "shared-ui" -], y = { +], b = { overview: "Overview", auth: "Auth", workspace: "Workspace", @@ -19,11 +19,16 @@ var _ = (e, t) => `/workspaces/${e}/plugin/example/${t}`, v = [ }; //#endregion //#region src/SharedUiPage.tsx -function b({ host: e }) { +function x({ host: e }) { let { data: t, isPending: i, isError: a } = e.sdk.platform.useEntitiesListWorkspaces({ page: 1, page_size: 100 - }), o = t?.data ?? [], s = p(), c = m((e) => [ + }), o = t?.data ?? [], { set: s } = e.breadcrumbs, { workspaceId: c } = e; + h(() => (s([{ + label: "Example Plugin", + href: v(c, "overview") + }, { label: "Shared UI" }]), () => s([])), [s, c]); + let y = p(), b = m((e) => [ e.accessor("name", { header: "Name", size: 240 @@ -32,42 +37,42 @@ function b({ host: e }) { id: "status", header: "Status", size: 120, - cell: ({ row: e }) => /* @__PURE__ */ h(u, { status: e.original.status ?? "ready" }) + cell: ({ row: e }) => /* @__PURE__ */ g(u, { status: e.original.status ?? "ready" }) }), e.display({ id: "created", header: "Created", size: 160, - cell: ({ row: e }) => e.original.created_at ? /* @__PURE__ */ h(l, { datetime: e.original.created_at }) : /* @__PURE__ */ h(r, { + cell: ({ row: e }) => e.original.created_at ? /* @__PURE__ */ g(l, { datetime: e.original.created_at }) : /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "—" }) }) ], []); - return /* @__PURE__ */ g(n, { + return /* @__PURE__ */ _(n, { gap: "2", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/md", children: "Shared UI" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "This table is Studio's own StudioDataView, imported from @nemo/common and resolved through Studio's import map — not a copy bundled into the plugin." }), - a ? /* @__PURE__ */ h(f, { + a ? /* @__PURE__ */ g(f, { header: "Couldn't load workspaces", emptyMessage: "The request failed. Try again." - }) : !i && o.length === 0 ? /* @__PURE__ */ h(f, { + }) : !i && o.length === 0 ? /* @__PURE__ */ g(f, { header: "No workspaces", emptyMessage: "Create a workspace to see it listed here." - }) : /* @__PURE__ */ h(n, { + }) : /* @__PURE__ */ g(n, { className: "min-h-[320px]", - children: /* @__PURE__ */ h(d, { - dataViewState: s, - makeColumns: c, + children: /* @__PURE__ */ g(d, { + dataViewState: y, + makeColumns: b, attributes: { DataViewRoot: { data: o, totalCount: o.length, @@ -80,103 +85,103 @@ function b({ host: e }) { } //#endregion //#region src/Root.tsx -function x({ host: e }) { - return /* @__PURE__ */ h(c, { children: /* @__PURE__ */ g(s, { - element: /* @__PURE__ */ h(S, { workspaceId: e.workspaceId }), +function S({ host: e }) { + return /* @__PURE__ */ g(c, { children: /* @__PURE__ */ _(s, { + element: /* @__PURE__ */ g(C, { workspaceId: e.workspaceId }), children: [ - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { index: !0, - element: /* @__PURE__ */ h(a, { - to: _(e.workspaceId, "overview"), + element: /* @__PURE__ */ g(a, { + to: v(e.workspaceId, "overview"), replace: !0 }) }), - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { path: "overview", - element: /* @__PURE__ */ h(w, { host: e }) + element: /* @__PURE__ */ g(T, { host: e }) }), - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { path: "auth", - element: /* @__PURE__ */ h(T, { getAccessToken: e.auth.getAccessToken }) + element: /* @__PURE__ */ g(E, { getAccessToken: e.auth.getAccessToken }) }), - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { path: "workspace", - element: /* @__PURE__ */ h(E, { workspaceId: e.workspaceId }) + element: /* @__PURE__ */ g(D, { workspaceId: e.workspaceId }) }), - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { path: "shared-ui", - element: /* @__PURE__ */ h(b, { host: e }) + element: /* @__PURE__ */ g(x, { host: e }) }), - /* @__PURE__ */ h(s, { + /* @__PURE__ */ g(s, { path: "*", - element: /* @__PURE__ */ h(D, {}) + element: /* @__PURE__ */ g(O, {}) }) ] }) }); } -function S({ workspaceId: e }) { +function C({ workspaceId: e }) { let r = ({ isActive: e }) => `px-3 py-1 rounded text-sm font-medium transition-colors ${e ? "text-primary bg-surface-hover" : "text-subtle hover:text-primary"}`; - return /* @__PURE__ */ g(n, { + return /* @__PURE__ */ _(n, { gap: "4", className: "h-full p-4", - children: [/* @__PURE__ */ h(t, { + children: [/* @__PURE__ */ g(t, { gap: "2", className: "border-b border-subtle pb-2", - children: v.map((t) => /* @__PURE__ */ h(i, { - to: _(e, t), + children: y.map((t) => /* @__PURE__ */ g(i, { + to: v(e, t), className: r, - children: y[t] + children: b[t] }, t)) - }), /* @__PURE__ */ h("div", { + }), /* @__PURE__ */ g("div", { className: "flex-1", - children: /* @__PURE__ */ h(o, {}) + children: /* @__PURE__ */ g(o, {}) })] }); } -function C({ children: e }) { - return /* @__PURE__ */ h("pre", { +function w({ children: e }) { + return /* @__PURE__ */ g("pre", { className: "bg-surface-sunken text-subtle rounded p-3 text-xs overflow-x-auto font-mono", children: e }); } -function w({ host: i }) { +function T({ host: i }) { let { data: a, isPending: o, isError: s } = i.sdk.platform.useEntitiesListWorkspaces({ page: 1, page_size: 100 }, { query: { staleTime: 5e3 } }), c = a?.data ?? []; - return /* @__PURE__ */ g(n, { + return /* @__PURE__ */ _(n, { gap: "2", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/lg", children: "Example Plugin" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "This is an example Studio plugin. Use the tabs above or the Studio side nav to explore what information is available to a plugin at runtime." }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Shared SDK" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/xs", color: "secondary", children: "Listed via Studio's sdk.platform.useEntitiesListWorkspaces() — the platform's typed hook, running on Studio's authenticated axios and shared QueryClient rather than a plugin copy." }), - o ? /* @__PURE__ */ h(r, { + o ? /* @__PURE__ */ g(r, { kind: "body/regular/xs", color: "secondary", children: "Loading…" - }) : s ? /* @__PURE__ */ h(r, { + }) : s ? /* @__PURE__ */ g(r, { kind: "body/regular/xs", color: "danger", children: "Request failed." - }) : /* @__PURE__ */ g(r, { + }) : /* @__PURE__ */ _(r, { kind: "body/regular/sm", children: [ c.length, @@ -187,27 +192,27 @@ function w({ host: i }) { }) ] }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Host capabilities" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/xs", color: "secondary", children: "Studio's notifications, telemetry, and navigation, all off the host handle — no plugin-side setup." }), - /* @__PURE__ */ g(t, { + /* @__PURE__ */ _(t, { gap: "2", - children: [/* @__PURE__ */ h(e, { + children: [/* @__PURE__ */ g(e, { kind: "secondary", onClick: () => { i.notifications.notify("Toast from the example plugin", "success"), i.telemetry.event("overview_notify_clicked"); }, children: "Notify" - }), /* @__PURE__ */ h(e, { + }), /* @__PURE__ */ g(e, { kind: "secondary", onClick: () => i.navigation.navigate(`/workspaces/${i.workspaceId}/base-models`), children: "Go to Base Models" @@ -218,37 +223,37 @@ function w({ host: i }) { ] }); } -function T({ getAccessToken: e }) { +function E({ getAccessToken: e }) { let t = e(), i = null; try { let e = t.split(".")[1]; e && (i = JSON.parse(atob(e.replace(/-/g, "+").replace(/_/g, "/")))); } catch {} - return /* @__PURE__ */ g(n, { + return /* @__PURE__ */ _(n, { gap: "3", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/md", children: "Auth" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "Studio passes an OIDC access token to every plugin via the plugin's auth prop. Call getAccessToken() per request — it returns the current token after silent renewal — and use it as a Bearer token." }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", - children: [/* @__PURE__ */ h(r, { + children: [/* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Example API call" - }), /* @__PURE__ */ h(C, { children: "fetch('/apis/v1/workspaces', {\n headers: { Authorization: `Bearer ${getAccessToken()}` },\n})" })] + }), /* @__PURE__ */ g(w, { children: "fetch('/apis/v1/workspaces', {\n headers: { Authorization: `Bearer ${getAccessToken()}` },\n})" })] }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", - children: [/* @__PURE__ */ h(r, { + children: [/* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Token claims (decoded, not verified)" - }), i ? /* @__PURE__ */ h(C, { children: JSON.stringify(i, null, 2) }) : /* @__PURE__ */ h(r, { + }), i ? /* @__PURE__ */ g(w, { children: JSON.stringify(i, null, 2) }) : /* @__PURE__ */ g(r, { kind: "body/regular/xs", color: "secondary", children: t ? "Could not decode token." : "No token provided." @@ -257,38 +262,38 @@ function T({ getAccessToken: e }) { ] }); } -function E({ workspaceId: e }) { - return /* @__PURE__ */ g(n, { +function D({ workspaceId: e }) { + return /* @__PURE__ */ _(n, { gap: "3", children: [ - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "label/bold/md", children: "Workspace" }), - /* @__PURE__ */ h(r, { + /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "Studio passes the current workspace ID to every plugin via the plugin's workspaceId prop." }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", - children: [/* @__PURE__ */ h(r, { + children: [/* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Current workspace" - }), /* @__PURE__ */ h(C, { children: e })] + }), /* @__PURE__ */ g(w, { children: e })] }), - /* @__PURE__ */ g(n, { + /* @__PURE__ */ _(n, { gap: "1", - children: [/* @__PURE__ */ h(r, { + children: [/* @__PURE__ */ g(r, { kind: "label/bold/sm", children: "Example API call scoped to this workspace" - }), /* @__PURE__ */ h(C, { children: "fetch(`/apis/v1/workspaces/${workspaceId}/models`, {\n headers: { Authorization: `Bearer ${getAccessToken()}` },\n})" })] + }), /* @__PURE__ */ g(w, { children: "fetch(`/apis/v1/workspaces/${workspaceId}/models`, {\n headers: { Authorization: `Bearer ${getAccessToken()}` },\n})" })] }) ] }); } -function D() { - return /* @__PURE__ */ h(r, { +function O() { + return /* @__PURE__ */ g(r, { kind: "body/regular/sm", color: "secondary", children: "Page not found." @@ -296,34 +301,34 @@ function D() { } //#endregion //#region src/Nav.tsx -var O = (e) => [{ +var k = (e) => [{ group: "Example Plugin", items: [ { id: "example-overview", iconName: "flask-conical", label: "Overview", - href: _(e, "overview") + href: v(e, "overview") }, { id: "example-auth", iconName: "key-round", label: "Auth", - href: _(e, "auth") + href: v(e, "auth") }, { id: "example-workspace", iconName: "building-2", label: "Workspace", - href: _(e, "workspace") + href: v(e, "workspace") }, { id: "example-shared-ui", iconName: "table", label: "Shared UI", - href: _(e, "shared-ui") + href: v(e, "shared-ui") } ] }]; //#endregion -export { x as Root, O as navItems }; +export { S as Root, k as navItems }; diff --git a/plugins/example-plugin/web/AGENTS.md b/plugins/example-plugin/web/AGENTS.md index f401cf2f31..24c75a6078 100644 --- a/plugins/example-plugin/web/AGENTS.md +++ b/plugins/example-plugin/web/AGENTS.md @@ -31,11 +31,15 @@ Runtime contract: `../../../web/packages/studio/src/plugins/types.ts`. Studio injects everything a plugin needs through a **single `host` prop** (`host.workspaceId`, `host.auth`, `host.sdk`, `host.navigation`, -`host.notifications`, `host.telemetry`) — grouped so new capabilities extend the +`host.notifications`, `host.telemetry`, `host.breadcrumbs`) — grouped so new capabilities extend the handle without changing `Root`'s signature. Destructure what you use. All are backed by Studio's own singletons: `notifications` fires into Studio's shared toaster, `telemetry` logs to Studio's OTEL pipeline (auto-scoped to the plugin), -`navigation` drives Studio's shared router. +`navigation` drives Studio's shared router, and `breadcrumbs` writes Studio's +breadcrumb bar — which lives in GlobalNav, *outside* the plugin's subtree, so a +plugin cannot render it itself. Studio clears the trail when the plugin +unmounts, but **not between pages within the plugin**: return a cleanup that +clears it, or the trail follows you to the next page. See `src/SharedUiPage.tsx`. `@tanstack/react-query` **is** shared — call `useQuery`/`useMutation` and it reads Studio's `QueryClientProvider` (one cache across Studio and every plugin). Put the @@ -81,6 +85,16 @@ import { StudioDataView, useStudioDataViewState } from '@nemo/common'; Studio bundles the same files through its own graph. A plugin adds no CSS. - **`useStudioDataViewState` syncs to URL search params** on Studio's shared router — two DataViews on one route will fight over them. +- **Toasts need `onNotify`.** `ToastProvider` is *not* shared: Studio mounts it + by deep import, so this bundle carries its own `ToastContext` with nothing in + it. `ConfirmationModal`, `DeleteConfirmationModal` and `LogViewer` therefore + take an `onNotify` prop — pass `host.notifications.notify` and the message + lands in Studio's toaster. Omit it and the message is dropped with a + `logger.warn`; nothing throws, so the miss is silent in the UI. + + ```tsx + + ``` ## Contract @@ -92,8 +106,15 @@ import { StudioDataView, useStudioDataViewState } from '@nemo/common'; auth: { accessToken: string; getAccessToken: () => string }; sdk: { platform: /* @nemo/sdk platform hooks */ }; navigation: { navigate: (to: string) => void; back: () => void }; - notifications: { notify: (message: string, type?: 'success'|'error'|'info'|'warning') => void }; + notifications: { + notify: ( + message: string, + type?: 'success'|'error'|'info'|'warning', + options?: { durationMs?: number | false }, + ) => void; + }; telemetry: { info; warn; error: (m, cause?) => void; event: (name, attrs?) => void }; + breadcrumbs: { set: (trail: { label: string; href?: string }[]) => void }; }; } ``` diff --git a/plugins/example-plugin/web/src/SharedUiPage.tsx b/plugins/example-plugin/web/src/SharedUiPage.tsx index ea71cb18e3..3907e5420d 100644 --- a/plugins/example-plugin/web/src/SharedUiPage.tsx +++ b/plugins/example-plugin/web/src/SharedUiPage.tsx @@ -9,7 +9,8 @@ import { useStudioDataViewState, } from "@nemo/common"; import { Stack, Text } from "@nvidia/foundations-react-core"; -import { useCallback, type ComponentProps } from "react"; +import { useCallback, useEffect, type ComponentProps } from "react"; +import { pluginPath } from "./paths"; import type { PluginHost, Workspace } from "./types"; // `@nemo/common` is external like react — this is Studio's own StudioDataView, @@ -22,6 +23,18 @@ export function SharedUiPage({ host }: { host: PluginHost }) { }); const workspaces = data?.data ?? []; + // Renders in Studio's chrome, outside this subtree. Studio clears the trail + // when the plugin unmounts, but not between pages — so clear it here too. + const { set: setBreadcrumbs } = host.breadcrumbs; + const { workspaceId } = host; + useEffect(() => { + setBreadcrumbs([ + { label: "Example Plugin", href: pluginPath(workspaceId, "overview") }, + { label: "Shared UI" }, + ]); + return () => setBreadcrumbs([]); + }, [setBreadcrumbs, workspaceId]); + // Syncs to URL search params — one DataView per route or they fight. const dataViewState = useStudioDataViewState(); diff --git a/plugins/example-plugin/web/src/types.ts b/plugins/example-plugin/web/src/types.ts index 2efe153219..f6955147e8 100644 --- a/plugins/example-plugin/web/src/types.ts +++ b/plugins/example-plugin/web/src/types.ts @@ -35,8 +35,21 @@ export interface PluginNavigation { export type NotificationType = 'success' | 'error' | 'info' | 'warning'; +export interface NotificationOptions { + durationMs?: number | false; +} + export interface PluginNotifications { - notify: (message: string, type?: NotificationType) => void; + notify: (message: string, type?: NotificationType, options?: NotificationOptions) => void; +} + +export interface PluginBreadcrumb { + label: string; + href?: string; +} + +export interface PluginBreadcrumbs { + set: (trail: PluginBreadcrumb[]) => void; } export interface PluginTelemetry { @@ -56,6 +69,7 @@ export interface PluginHost { navigation: PluginNavigation; notifications: PluginNotifications; telemetry: PluginTelemetry; + breadcrumbs: PluginBreadcrumbs; } export interface PluginRootProps { diff --git a/web/packages/common/package.json b/web/packages/common/package.json index 4b1aa7d21c..bfa768d37b 100644 --- a/web/packages/common/package.json +++ b/web/packages/common/package.json @@ -53,6 +53,7 @@ "happy-dom": "catalog:", "msw": "catalog:", "p-limit": "catalog:", + "react-dropzone": "catalog:", "react-hook-form": "catalog:", "tsx": "catalog:", "typescript": "catalog:", @@ -77,6 +78,7 @@ "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", + "@opentelemetry/api-logs": "^0.219.0", "@tanstack/match-sorter-utils": "^8.19.4", "@tanstack/react-table": "8.20.5", "@tanstack/react-virtual": "3.13.6", diff --git a/web/packages/studio/src/api/common/utils.test.ts b/web/packages/common/src/api/common/utils.test.ts similarity index 98% rename from web/packages/studio/src/api/common/utils.test.ts rename to web/packages/common/src/api/common/utils.test.ts index 3c04d54feb..60179799cb 100644 --- a/web/packages/studio/src/api/common/utils.test.ts +++ b/web/packages/common/src/api/common/utils.test.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { getErrorMessage, isValidationErrorArray } from '@studio/api/common/utils'; +import { getErrorMessage, isValidationErrorArray } from '@nemo/common/src/api/common/utils'; import { AxiosError, AxiosHeaders, InternalAxiosRequestConfig } from 'axios'; describe('isValidationErrorArray', () => { diff --git a/web/packages/studio/src/api/common/utils.ts b/web/packages/common/src/api/common/utils.ts similarity index 100% rename from web/packages/studio/src/api/common/utils.ts rename to web/packages/common/src/api/common/utils.ts diff --git a/web/packages/studio/src/components/AccessibleTitle/index.test.tsx b/web/packages/common/src/components/AccessibleTitle/index.test.tsx similarity index 93% rename from web/packages/studio/src/components/AccessibleTitle/index.test.tsx rename to web/packages/common/src/components/AccessibleTitle/index.test.tsx index feb91bb83d..d62531662f 100644 --- a/web/packages/studio/src/components/AccessibleTitle/index.test.tsx +++ b/web/packages/common/src/components/AccessibleTitle/index.test.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { render, waitFor } from '@testing-library/react'; describe('AccessibleTitle', () => { diff --git a/web/packages/studio/src/components/AccessibleTitle/index.tsx b/web/packages/common/src/components/AccessibleTitle/index.tsx similarity index 100% rename from web/packages/studio/src/components/AccessibleTitle/index.tsx rename to web/packages/common/src/components/AccessibleTitle/index.tsx diff --git a/web/packages/studio/src/components/AccordionSection/index.tsx b/web/packages/common/src/components/AccordionSection/index.tsx similarity index 100% rename from web/packages/studio/src/components/AccordionSection/index.tsx rename to web/packages/common/src/components/AccordionSection/index.tsx diff --git a/web/packages/studio/src/components/CancelJobButton/index.tsx b/web/packages/common/src/components/CancelJobButton/index.tsx similarity index 91% rename from web/packages/studio/src/components/CancelJobButton/index.tsx rename to web/packages/common/src/components/CancelJobButton/index.tsx index f86af9b302..881a5ee86c 100644 --- a/web/packages/studio/src/components/CancelJobButton/index.tsx +++ b/web/packages/common/src/components/CancelJobButton/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { FormModal } from '@nemo/common/src/components/FormModal'; import { CJobCancellableStatuses } from '@nemo/common/src/constants/query'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; @@ -11,23 +12,26 @@ import { } from '@nemo/sdk/generated/platform/api'; import { PlatformJobStatus } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useQueryClient } from '@tanstack/react-query'; import { X } from 'lucide-react'; import { FC, MouseEvent, useState } from 'react'; interface CancelJobButtonProps { + workspace: string; jobName: string; jobStatus?: PlatformJobStatus; compact?: boolean; } -export const CancelJobButton: FC = ({ jobName, jobStatus, compact }) => { +export const CancelJobButton: FC = ({ + workspace, + jobName, + jobStatus, + compact, +}) => { const [isModalOpen, setIsModalOpen] = useState(false); const toast = useToast(); const queryClient = useQueryClient(); - const workspace = useWorkspaceFromPath(); const { mutateAsync, isPending } = useJobsCancelJob({ mutation: { diff --git a/web/packages/studio/src/components/modals/ConfirmationModal/index.tsx b/web/packages/common/src/components/ConfirmationModal/index.tsx similarity index 87% rename from web/packages/studio/src/components/modals/ConfirmationModal/index.tsx rename to web/packages/common/src/components/ConfirmationModal/index.tsx index 41cc0c491d..252756199d 100644 --- a/web/packages/studio/src/components/modals/ConfirmationModal/index.tsx +++ b/web/packages/common/src/components/ConfirmationModal/index.tsx @@ -4,10 +4,11 @@ import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; -import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import type { NotifyFn } from '@nemo/common/src/providers/toast/types'; +import { useNotify } from '@nemo/common/src/providers/toast/useNotify'; +import { getErrorMessage } from '@nemo/common/src/utils/error'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/components/NewDataDesignerJobForm/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { type ComponentProps, type FC, useState } from 'react'; import { useForm } from 'react-hook-form'; @@ -34,6 +35,8 @@ export interface ConfirmationModalProps extends Pick = ({ @@ -49,6 +52,7 @@ export const ConfirmationModal: FC = ({ submitButtonText = 'Confirm', submitButtonColor, suppressResultToasts = false, + onNotify, }) => { const { reset, @@ -60,7 +64,7 @@ export const ConfirmationModal: FC = ({ defaultValues: { confirmText: '' }, }); const [isPending, setIsPending] = useState(false); - const toast = useToast(); + const notify = useNotify(onNotify); const resetAndClose = () => { reset(); @@ -73,13 +77,13 @@ export const ConfirmationModal: FC = ({ const ok = await onConfirm(); if (!suppressResultToasts) { if (ok) { - toast.success(successText); + notify(successText, 'success'); } else { - toast.error(errorText); + notify(errorText, 'error'); } } } catch (error: unknown) { - toast.error(getErrorMessage(error)); + notify(getErrorMessage(error), 'error'); } finally { resetAndClose(); setIsPending(false); diff --git a/web/packages/studio/src/components/DeleteConfirmationModal/index.tsx b/web/packages/common/src/components/DeleteConfirmationModal/index.tsx similarity index 83% rename from web/packages/studio/src/components/DeleteConfirmationModal/index.tsx rename to web/packages/common/src/components/DeleteConfirmationModal/index.tsx index f4411770b1..b23541f732 100644 --- a/web/packages/studio/src/components/DeleteConfirmationModal/index.tsx +++ b/web/packages/common/src/components/DeleteConfirmationModal/index.tsx @@ -1,8 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { ConfirmationModal } from '@nemo/common/src/components/ConfirmationModal'; import { FormModalProps } from '@nemo/common/src/components/FormModal'; -import { ConfirmationModal } from '@studio/components/modals/ConfirmationModal'; +import type { NotifyFn } from '@nemo/common/src/providers/toast/types'; import { FC } from 'react'; interface DeleteModalProps extends Pick { @@ -14,6 +15,8 @@ interface DeleteModalProps extends Pick { successText?: string; errorText?: string; suppressResultToasts?: boolean; + /** Where result messages go. Defaults to the surrounding ToastProvider; plugins pass `host.notifications.notify`. */ + onNotify?: NotifyFn; } export const DeleteConfirmationModal: FC = ({ diff --git a/web/packages/studio/src/components/ErrorPanel/index.tsx b/web/packages/common/src/components/ErrorPanel/index.tsx similarity index 96% rename from web/packages/studio/src/components/ErrorPanel/index.tsx rename to web/packages/common/src/components/ErrorPanel/index.tsx index 885c0c2950..2691b78ed7 100644 --- a/web/packages/studio/src/components/ErrorPanel/index.tsx +++ b/web/packages/common/src/components/ErrorPanel/index.tsx @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; +import { logger } from '@nemo/common/src/utils/logger'; import { Flex, PageHeader, Panel, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { logger } from '@studio/util/logger'; import { FileX } from 'lucide-react'; import { ComponentProps, FC, ReactNode, useEffect } from 'react'; import { useRouteError, isRouteErrorResponse } from 'react-router'; diff --git a/web/packages/studio/src/components/ExpandableMessage/index.test.tsx b/web/packages/common/src/components/ExpandableMessage/index.test.tsx similarity index 98% rename from web/packages/studio/src/components/ExpandableMessage/index.test.tsx rename to web/packages/common/src/components/ExpandableMessage/index.test.tsx index 5c0c79bd43..9d71ec0fad 100644 --- a/web/packages/studio/src/components/ExpandableMessage/index.test.tsx +++ b/web/packages/common/src/components/ExpandableMessage/index.test.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ExpandableMessage } from '@studio/components/ExpandableMessage'; +import { ExpandableMessage } from '@nemo/common/src/components/ExpandableMessage'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; diff --git a/web/packages/studio/src/components/ExpandableMessage/index.tsx b/web/packages/common/src/components/ExpandableMessage/index.tsx similarity index 100% rename from web/packages/studio/src/components/ExpandableMessage/index.tsx rename to web/packages/common/src/components/ExpandableMessage/index.tsx diff --git a/web/packages/studio/src/components/FileTag/index.tsx b/web/packages/common/src/components/FileTag/index.tsx similarity index 100% rename from web/packages/studio/src/components/FileTag/index.tsx rename to web/packages/common/src/components/FileTag/index.tsx diff --git a/web/packages/studio/src/components/FileUpload/index.tsx b/web/packages/common/src/components/FileUpload/index.tsx similarity index 95% rename from web/packages/studio/src/components/FileUpload/index.tsx rename to web/packages/common/src/components/FileUpload/index.tsx index 5735e70cc0..ee624b4417 100644 --- a/web/packages/studio/src/components/FileUpload/index.tsx +++ b/web/packages/common/src/components/FileUpload/index.tsx @@ -1,14 +1,14 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { Block, Flex, Label, Stack, Text } from '@nvidia/foundations-react-core'; -import { FileTag, FileTagStatus } from '@studio/components/FileTag'; +import { FileTag, FileTagStatus } from '@nemo/common/src/components/FileTag'; import { getAcceptedFileExtensions, hadInvalidFileTypeError, hadTooManyFilesError, -} from '@studio/components/FileUpload/util'; -import { InputErrorText } from '@studio/components/InputErrorText'; +} from '@nemo/common/src/components/FileUpload/util'; +import { InputErrorText } from '@nemo/common/src/components/InputErrorText'; +import { Block, Flex, Label, Stack, Text } from '@nvidia/foundations-react-core'; import { FilePlus, FileText } from 'lucide-react'; import { FC, MouseEvent, MouseEventHandler, ReactNode, useCallback, useState } from 'react'; import { DropEvent, DropzoneOptions, FileRejection, useDropzone } from 'react-dropzone'; diff --git a/web/packages/studio/src/components/FileUpload/util.ts b/web/packages/common/src/components/FileUpload/util.ts similarity index 100% rename from web/packages/studio/src/components/FileUpload/util.ts rename to web/packages/common/src/components/FileUpload/util.ts diff --git a/web/packages/studio/src/components/InputErrorText/index.tsx b/web/packages/common/src/components/InputErrorText/index.tsx similarity index 100% rename from web/packages/studio/src/components/InputErrorText/index.tsx rename to web/packages/common/src/components/InputErrorText/index.tsx diff --git a/web/packages/common/src/components/LogViewer/index.tsx b/web/packages/common/src/components/LogViewer/index.tsx index f0f87a380d..3fa157d2cf 100644 --- a/web/packages/common/src/components/LogViewer/index.tsx +++ b/web/packages/common/src/components/LogViewer/index.tsx @@ -2,7 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { useStickToBottom } from '@nemo/common/src/hooks/useStickToBottom'; -import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import type { NotifyFn } from '@nemo/common/src/providers/toast/types'; +import { useNotify } from '@nemo/common/src/providers/toast/useNotify'; import { triggerDownload } from '@nemo/common/src/utils/file'; import { formatLogs } from '@nemo/common/src/utils/logs'; import type { PlatformJobLog } from '@nemo/sdk/generated/platform/schema'; @@ -27,6 +28,8 @@ interface LogViewerProps { downloadFilename?: string; rows?: number; emptyMessage?: string; + /** Where the copy confirmation goes. Defaults to the surrounding ToastProvider; plugins pass `host.notifications.notify`. */ + onNotify?: NotifyFn; } export const LogViewer: FC = ({ @@ -35,6 +38,7 @@ export const LogViewer: FC = ({ downloadFilename, rows = DEFAULT_ROW_COUNT, emptyMessage = 'No logs available yet', + onNotify, }) => { const [showAllLogs, setShowAllLogs] = useState(false); const [wrapLines, setWrapLines] = useState(false); @@ -45,7 +49,7 @@ export const LogViewer: FC = ({ const isShowingLogs = useMemo(() => logs.length > 0 && !isLoading, [logs.length, isLoading]); - const { success } = useToast(); + const notify = useNotify(onNotify); const { ref: codeScrollRef, scrollToBottom } = useStickToBottom({ enabled: isShowingLogs, @@ -87,7 +91,7 @@ export const LogViewer: FC = ({ kind="block" collapsible={false} rows={rows} - onCopySuccess={() => success('Copied to clipboard!', { durationMs: 3000 })} + onCopySuccess={() => notify('Copied to clipboard!', 'success', { durationMs: 3000 })} className="min-h-auto h-full" attributes={{ CodeSnippetCode: { diff --git a/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx b/web/packages/common/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx similarity index 93% rename from web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx rename to web/packages/common/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx index 9557eaa76c..168e7c1806 100644 --- a/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx +++ b/web/packages/common/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.test.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { render, screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; diff --git a/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.tsx b/web/packages/common/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.tsx similarity index 100% rename from web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.tsx rename to web/packages/common/src/components/QuickActionsMenu/QuickActionsMenuRoot/index.tsx diff --git a/web/packages/common/src/plugin.ts b/web/packages/common/src/plugin.ts index 8367f0c3bc..c0a2a4c864 100644 --- a/web/packages/common/src/plugin.ts +++ b/web/packages/common/src/plugin.ts @@ -4,6 +4,20 @@ // Plugin API: the surface Studio serves to plugin bundles as `@nemo/common`. // Removals are breaking. Explicit exports, not `export *`. +export { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +export { AccordionSection } from '@nemo/common/src/components/AccordionSection'; +export { ConfirmationModal } from '@nemo/common/src/components/ConfirmationModal'; +export { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +export type { AccordionSectionProps } from '@nemo/common/src/components/AccordionSection'; +export { ExpandableMessage } from '@nemo/common/src/components/ExpandableMessage'; +export { FileTag } from '@nemo/common/src/components/FileTag'; +export type { FileTagProps, FileTagStatus } from '@nemo/common/src/components/FileTag'; +export { FileUpload } from '@nemo/common/src/components/FileUpload'; +export type { FileUploadProps, RenderFileTagFn } from '@nemo/common/src/components/FileUpload'; +export { InputErrorText } from '@nemo/common/src/components/InputErrorText'; +export { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; +export type { QuickActionItem } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; + export { StudioDataView, StudioDataViewToolbar, @@ -46,6 +60,13 @@ export { } from '@nemo/common/src/utils/query'; export { triggerDownload } from '@nemo/common/src/utils/file'; +export { getErrorMessage } from '@nemo/common/src/utils/error'; +export { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; +export { logger, toError } from '@nemo/common/src/utils/logger'; + +export type { NotifyFn, NotifyType } from '@nemo/common/src/providers/toast/types'; + +export { JOB_POLLING_INTERVAL_MS } from '@nemo/common/src/constants'; export { DEFAULT_PAGE, DEFAULT_PAGE_SIZE, diff --git a/web/packages/common/src/providers/toast/types.ts b/web/packages/common/src/providers/toast/types.ts index 8143683135..362f679296 100644 --- a/web/packages/common/src/providers/toast/types.ts +++ b/web/packages/common/src/providers/toast/types.ts @@ -4,7 +4,7 @@ import { Toast } from '@nvidia/foundations-react-core'; import { ComponentProps, ReactNode } from 'react'; -interface MessageFnOptions { +export interface MessageFnOptions { durationMs?: number | false; // How long should the toast appear for? Pass false to prevent auto-dismiss. } interface AddToastFnOptions extends MessageFnOptions { @@ -27,6 +27,15 @@ export interface ToastObject { dismissToast: DismissToastFn; } +export type NotifyType = 'success' | 'error' | 'info' | 'warning'; + +/** + * Provider-independent notification sink. Components exposed across the plugin + * boundary take this as a prop so a plugin can route them at Studio's toaster + * without sharing a ToastContext module instance. + */ +export type NotifyFn = (message: string, type?: NotifyType, options?: MessageFnOptions) => void; + export interface ToastContextValue { addToast: AddToastFn; dismissToast: DismissToastFn; diff --git a/web/packages/common/src/providers/toast/useNotify.test.tsx b/web/packages/common/src/providers/toast/useNotify.test.tsx new file mode 100644 index 0000000000..93b6ce1b71 --- /dev/null +++ b/web/packages/common/src/providers/toast/useNotify.test.tsx @@ -0,0 +1,97 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { ToastProvider } from '@nemo/common/src/providers/toast/ToastProvider'; +import type { MessageFnOptions, NotifyFn } from '@nemo/common/src/providers/toast/types'; +import { useNotify } from '@nemo/common/src/providers/toast/useNotify'; +import { logger } from '@nemo/common/src/utils/logger'; +import { act, render, screen } from '@testing-library/react'; +import userEvent from '@testing-library/user-event'; +import type { FC } from 'react'; + +interface NotifierProps { + onNotify?: NotifyFn; + options?: MessageFnOptions; +} + +const Notifier: FC = ({ onNotify, options }) => { + const notify = useNotify(onNotify); + return ; +}; + +describe('useNotify', () => { + const user = userEvent.setup(); + + it('sends to the surrounding ToastProvider when no onNotify is given', async () => { + render( + + + + ); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + + expect(await screen.findByText('All done')).toBeInTheDocument(); + }); + + it('prefers onNotify over the provider', async () => { + const onNotify = vi.fn(); + render( + + + + ); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + + expect(onNotify).toHaveBeenCalledWith('All done', 'success', undefined); + expect(screen.queryByText('All done')).not.toBeInTheDocument(); + }); + + it('works without a provider, which is the plugin case', async () => { + const onNotify = vi.fn(); + render(); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + + expect(onNotify).toHaveBeenCalledWith('All done', 'success', undefined); + }); + + it('forwards toast options to onNotify', async () => { + const onNotify = vi.fn(); + render(); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + + expect(onNotify).toHaveBeenCalledWith('All done', 'success', { durationMs: false }); + }); + + it('honours durationMs on the provider path', async () => { + vi.useFakeTimers({ shouldAdvanceTime: true }); + render( + + + + ); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + expect(await screen.findByText('All done')).toBeInTheDocument(); + + await act(async () => { + vi.advanceTimersByTime(10_000); + }); + expect(screen.getByText('All done')).toBeInTheDocument(); + + vi.useRealTimers(); + }); + + it('drops the message with a warning when there is no sink at all', async () => { + const warn = vi.spyOn(logger, 'warn').mockImplementation(() => {}); + render(); + + await user.click(screen.getByRole('button', { name: 'Notify' })); + + expect(warn).toHaveBeenCalledWith(expect.stringContaining('All done')); + warn.mockRestore(); + }); +}); diff --git a/web/packages/common/src/providers/toast/useNotify.ts b/web/packages/common/src/providers/toast/useNotify.ts new file mode 100644 index 0000000000..af55a9bc0d --- /dev/null +++ b/web/packages/common/src/providers/toast/useNotify.ts @@ -0,0 +1,32 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import type { NotifyFn } from '@nemo/common/src/providers/toast/types'; +import { ToastContext } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; +import { useCallback, useContext } from 'react'; + +/** + * Resolves a notification sink: an explicit `onNotify` first, then the + * ToastProvider above this component. Unlike `useToast` this never throws — + * a plugin renders against a different `ToastContext` instance than the one + * Studio mounts, so the context is legitimately absent there. + */ +export const useNotify = (onNotify?: NotifyFn): NotifyFn => { + const toast = useContext(ToastContext)?.toast; + + return useCallback( + (message, type = 'info', options) => { + if (onNotify) { + onNotify(message, type, options); + return; + } + if (toast) { + toast[type](message, options); + return; + } + logger.warn(`[toast] dropped notification, no ToastProvider and no onNotify: ${message}`); + }, + [onNotify, toast] + ); +}; diff --git a/web/packages/common/src/utils/error.ts b/web/packages/common/src/utils/error.ts new file mode 100644 index 0000000000..f0a843780e --- /dev/null +++ b/web/packages/common/src/utils/error.ts @@ -0,0 +1,9 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +/** Display message from an unknown error; falls back when it is not an Error. */ +export function getErrorMessage(error: unknown, defaultMessage?: string): string { + return error instanceof Error + ? error.message + : (defaultMessage ?? 'Something went wrong. Please try again.'); +} diff --git a/web/packages/studio/src/util/forms/error.test.ts b/web/packages/common/src/utils/forms/error.test.ts similarity index 94% rename from web/packages/studio/src/util/forms/error.test.ts rename to web/packages/common/src/utils/forms/error.test.ts index 128e9cc50c..90cbfd4ee7 100644 --- a/web/packages/studio/src/util/forms/error.test.ts +++ b/web/packages/common/src/utils/forms/error.test.ts @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; -import { logger } from '@studio/util/logger'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; +import { logger } from '@nemo/common/src/utils/logger'; import { FieldErrors } from 'react-hook-form'; // Mock the websiteLogger -vi.mock('@studio/util/logger', () => ({ +vi.mock('@nemo/common/src/utils/logger', () => ({ logger: { error: vi.fn(), }, diff --git a/web/packages/studio/src/util/forms/error.ts b/web/packages/common/src/utils/forms/error.ts similarity index 94% rename from web/packages/studio/src/util/forms/error.ts rename to web/packages/common/src/utils/forms/error.ts index fd9502e4e2..49580c2e9e 100644 --- a/web/packages/studio/src/util/forms/error.ts +++ b/web/packages/common/src/utils/forms/error.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { logger } from '@studio/util/logger'; +import { logger } from '@nemo/common/src/utils/logger'; import { FieldErrors } from 'react-hook-form'; /** diff --git a/web/packages/studio/src/util/logger.ts b/web/packages/common/src/utils/logger.ts similarity index 83% rename from web/packages/studio/src/util/logger.ts rename to web/packages/common/src/utils/logger.ts index 519a8f6749..714c7b6d48 100644 --- a/web/packages/studio/src/util/logger.ts +++ b/web/packages/common/src/utils/logger.ts @@ -3,9 +3,15 @@ /* eslint-disable no-console */ import { Logger, logs, SeverityNumber } from '@opentelemetry/api-logs'; -import { OTEL_SERVICE_NAME, VERSION_SHA } from '@studio/constants/environment'; -const otelLogger: Logger = logs.getLogger(OTEL_SERVICE_NAME); +// The service name lives in Studio's env config, which a shared library must not +// import. Studio calls configureLogger() at bootstrap; until then, logs still go +// to the console and OTEL receives them under a placeholder name. +let otelLogger: Logger = logs.getLogger('nemo-studio'); + +export const configureLogger = (serviceName: string): void => { + otelLogger = logs.getLogger(serviceName); +}; /** * Wrapper class around the global OpenTelemetry Logger. Logs will be transported to: @@ -18,8 +24,8 @@ const otelLogger: Logger = logs.getLogger(OTEL_SERVICE_NAME); * Example usage: * * ``` - * import { websiteLogger } from '@studio/util/logger'; - * websiteLogger.info('Some message to log'); + * import { logger } from '@nemo/common/src/utils/logger'; + * logger.info('Some message to log'); * ``` */ class WebsiteLogger { @@ -82,12 +88,3 @@ export const handleGenericError = (error: Error | string) => { logger.error(error); } }; - -/** - * Logs the version of the app to the website logger. - */ -export const logVersion = async () => { - if (VERSION_SHA) { - logger.info(`Version: ${VERSION_SHA}`); - } -}; diff --git a/web/packages/studio/src/App.tsx b/web/packages/studio/src/App.tsx index 0876070c24..b8a87f1a6b 100644 --- a/web/packages/studio/src/App.tsx +++ b/web/packages/studio/src/App.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { configureLogger } from '@nemo/common/src/utils/logger'; import { ThemeProvider as KaizenThemeProvider, Theme, @@ -13,12 +14,13 @@ import { AUTH_SCOPE_PREFIX, AUTH_SCOPES, BASE_URL, + OTEL_SERVICE_NAME, } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { routes } from '@studio/routes'; import { useLocalStorage } from '@studio/util/hooks/useLocalStorage'; import { UI_THEME } from '@studio/util/localStorage'; -import { logVersion } from '@studio/util/logger'; +import { logVersion } from '@studio/util/logVersion'; import { QueryClientProvider } from '@tanstack/react-query'; import { WebStorageStateStore } from 'oidc-client-ts'; import { StrictMode } from 'react'; @@ -55,6 +57,7 @@ if (import.meta.hot) { } const router = createBrowserRouter(routes, { basename: BASE_URL ?? '/' }); +configureLogger(OTEL_SERVICE_NAME); logVersion(); const effectiveScope = expandScopes(AUTH_SCOPES || 'openid profile email', AUTH_SCOPE_PREFIX); diff --git a/web/packages/studio/src/api/datasets/useDatasetFileContent.ts b/web/packages/studio/src/api/datasets/useDatasetFileContent.ts index 6564437eb8..804c7b4243 100644 --- a/web/packages/studio/src/api/datasets/useDatasetFileContent.ts +++ b/web/packages/studio/src/api/datasets/useDatasetFileContent.ts @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { customFetch } from '@nemo/sdk/generated/fetchers/platform'; import { filesDownloadFile, getFilesDownloadFileQueryKey } from '@nemo/sdk/generated/platform/api'; import type { EntityIdentifier } from '@studio/api/common/types'; import { getDatasetFileContentQueryKey } from '@studio/api/datasets/invalidateDatasetCaches'; import { PLATFORM_BASE_URL } from '@studio/constants/environment'; import { isBinaryExtension } from '@studio/util/binaryFile'; -import { logger } from '@studio/util/logger'; import { queryOptions, useQuery, UseQueryOptions, useSuspenseQuery } from '@tanstack/react-query'; import axios from 'axios'; import { parquetRead } from 'hyparquet'; diff --git a/web/packages/studio/src/api/guardrail-checks/guardrailChecks.ts b/web/packages/studio/src/api/guardrail-checks/guardrailChecks.ts index 0e14950c8d..145498f8fb 100644 --- a/web/packages/studio/src/api/guardrail-checks/guardrailChecks.ts +++ b/web/packages/studio/src/api/guardrail-checks/guardrailChecks.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { isVersionConflictError } from '@nemo/common/src/api/common/utils'; import { entitiesCreateEntity, entitiesDeleteEntityByName, @@ -16,7 +17,6 @@ import type { GuardrailCheckResponse, RailsConfig, } from '@nemo/sdk/generated/platform/schema'; -import { isVersionConflictError } from '@studio/api/common/utils'; import { GUARDRAIL_CHECKS_ENTITY_TYPE, type GuardrailCheckData, diff --git a/web/packages/studio/src/components/AnonymizerJobActionsMenu/index.tsx b/web/packages/studio/src/components/AnonymizerJobActionsMenu/index.tsx index 04b458462f..7c4da6ff9c 100644 --- a/web/packages/studio/src/components/AnonymizerJobActionsMenu/index.tsx +++ b/web/packages/studio/src/components/AnonymizerJobActionsMenu/index.tsx @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { + type QuickActionItem, + QuickActionsMenuRoot, +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { CJobCancellableStatuses } from '@nemo/common/src/constants/query'; import { getAnonymizerListRunJobsQueryKey, @@ -8,10 +12,6 @@ import { } from '@nemo/sdk/generated/anonymizer/api'; import type { RunJob as AnonymizerJob } from '@nemo/sdk/generated/anonymizer/schema'; import { DeleteJobModal } from '@studio/components/dataViews/AnonymizerJobsDataView/DeleteJobModal'; -import { - type QuickActionItem, - QuickActionsMenuRoot, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getAnonymizerJobRoute } from '@studio/routes/utils'; import { useQueryClient } from '@tanstack/react-query'; diff --git a/web/packages/studio/src/components/BulkDeleteModal/index.tsx b/web/packages/studio/src/components/BulkDeleteModal/index.tsx index 4108f37908..7c1f6f4639 100644 --- a/web/packages/studio/src/components/BulkDeleteModal/index.tsx +++ b/web/packages/studio/src/components/BulkDeleteModal/index.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { useState } from 'react'; export interface BulkDeleteModalProps { diff --git a/web/packages/studio/src/components/CreateFilesetStart/useDescribeWithAi.ts b/web/packages/studio/src/components/CreateFilesetStart/useDescribeWithAi.ts index 89e9a3ba48..8c1411450c 100644 --- a/web/packages/studio/src/components/CreateFilesetStart/useDescribeWithAi.ts +++ b/web/packages/studio/src/components/CreateFilesetStart/useDescribeWithAi.ts @@ -4,12 +4,12 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { type ModelWorkspaceGroup } from '@nemo/common/src/api/models/useModels'; import { useChatCompletion } from '@nemo/common/src/hooks/useChatCompletion'; +import { getErrorMessage } from '@nemo/common/src/utils/error'; import type { CreateJobRequest as DataDesignerJobRequest } from '@nemo/sdk/generated/data-designer/schema'; import { buildFixMessages } from '@studio/components/CreateFilesetStart/fixRequest'; import { DATA_DESIGNER_JOB_GENERATOR_SYSTEM_PROMPT } from '@studio/components/NewDataDesignerJobForm/constants'; import { generateDataDesignerJobRequestTool } from '@studio/components/NewDataDesignerJobForm/tools'; import { - getErrorMessage, getWorkspaceAndModel, parseToolResponseToJobRequest, sanitizeJobRequestName, diff --git a/web/packages/studio/src/components/CustomizationFileTag/index.tsx b/web/packages/studio/src/components/CustomizationFileTag/index.tsx index 224e2a4b95..116bdc9e3d 100644 --- a/web/packages/studio/src/components/CustomizationFileTag/index.tsx +++ b/web/packages/studio/src/components/CustomizationFileTag/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { FileTag, FileTagProps } from '@nemo/common/src/components/FileTag'; import { Stack } from '@nvidia/foundations-react-core'; -import { FileTag, FileTagProps } from '@studio/components/FileTag'; import { CUSTOMIZATION_FILESET_FILE_ICONS, CUSTOMIZATION_FILESET_FILE_LABELS, diff --git a/web/packages/studio/src/components/CustomizationFileUpload/index.tsx b/web/packages/studio/src/components/CustomizationFileUpload/index.tsx index 3aef53d739..d2daafae1a 100644 --- a/web/packages/studio/src/components/CustomizationFileUpload/index.tsx +++ b/web/packages/studio/src/components/CustomizationFileUpload/index.tsx @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { RenderFileTagFn } from '@nemo/common/src/components/FileUpload'; import { CustomizationFileTag, CustomizationFileTagFile, } from '@studio/components/CustomizationFileTag'; import { DatasetFileUpload } from '@studio/components/DatasetFileUpload'; -import { RenderFileTagFn } from '@studio/components/FileUpload'; import { CUSTOMIZATION_FILESET_FILE_PREFIXES, CustomizationFileType, diff --git a/web/packages/studio/src/components/CustomizationFilesetCreateModal/index.tsx b/web/packages/studio/src/components/CustomizationFilesetCreateModal/index.tsx index 24451a5feb..baebe52d63 100644 --- a/web/packages/studio/src/components/CustomizationFilesetCreateModal/index.tsx +++ b/web/packages/studio/src/components/CustomizationFilesetCreateModal/index.tsx @@ -6,6 +6,7 @@ import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal import { FilesetFileUploadError } from '@nemo/common/src/datasets/constants'; import { getURNFromNamedEntityRef } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { TextInput, TextArea, FormField, Stack } from '@nvidia/foundations-react-core'; import { useDatasetCreate } from '@studio/api/datasets/useDatasetCreate'; @@ -25,7 +26,6 @@ import { datasetSchema } from '@studio/constants/zod'; import { useCustomizationFilesPreview } from '@studio/hooks/useCustomizationFiles'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { renameFile } from '@studio/util/files'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; diff --git a/web/packages/studio/src/components/DataDesignerJobActionsMenu/index.tsx b/web/packages/studio/src/components/DataDesignerJobActionsMenu/index.tsx index 25a502c409..fdf4ebea03 100644 --- a/web/packages/studio/src/components/DataDesignerJobActionsMenu/index.tsx +++ b/web/packages/studio/src/components/DataDesignerJobActionsMenu/index.tsx @@ -1,6 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { + type QuickActionItem, + QuickActionsMenuRoot, +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { CJobCancellableStatuses } from '@nemo/common/src/constants/query'; import { getDataDesignerListCreateJobsQueryKey, @@ -9,10 +13,6 @@ import { import type { CreateJob as DataDesignerJob } from '@nemo/sdk/generated/data-designer/schema'; import { DeleteJobModal } from '@studio/components/dataViews/DataDesignerJobsDataView/DeleteJobModal'; import { buildClonedJobRequest } from '@studio/components/NewDataDesignerJobForm/utils'; -import { - type QuickActionItem, - QuickActionsMenuRoot, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getDataDesignerJobBuildRoute, getDataDesignerJobDetailsRoute } from '@studio/routes/utils'; import { useQueryClient } from '@tanstack/react-query'; diff --git a/web/packages/studio/src/components/DatasetCreateModal/index.tsx b/web/packages/studio/src/components/DatasetCreateModal/index.tsx index 6f0ccab2d9..e270e86465 100644 --- a/web/packages/studio/src/components/DatasetCreateModal/index.tsx +++ b/web/packages/studio/src/components/DatasetCreateModal/index.tsx @@ -9,6 +9,7 @@ import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal import { FilesetFileUploadError } from '@nemo/common/src/datasets/constants'; import { getEntityReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { useFilesUpdateFilesetMetadata } from '@nemo/sdk/generated/platform/api'; import { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { Select, Stack } from '@nvidia/foundations-react-core'; @@ -26,7 +27,6 @@ import { CUSTOMIZATION_FILESET_FILE_PREFIXES } from '@studio/constants/customiza import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getFilesetDetailsRoute } from '@studio/routes/utils'; import { renameFile } from '@studio/util/files'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC, useState } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router'; diff --git a/web/packages/studio/src/components/DatasetFileUpload/index.tsx b/web/packages/studio/src/components/DatasetFileUpload/index.tsx index 78b9f76bc4..e952c50ed6 100644 --- a/web/packages/studio/src/components/DatasetFileUpload/index.tsx +++ b/web/packages/studio/src/components/DatasetFileUpload/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { FileTag, FileTagStatus } from '@studio/components/FileTag'; -import { RenderFileTagFn, FileUpload } from '@studio/components/FileUpload'; +import { FileTag, FileTagStatus } from '@nemo/common/src/components/FileTag'; +import { RenderFileTagFn, FileUpload } from '@nemo/common/src/components/FileUpload'; import { ComponentProps, FC } from 'react'; export interface DatasetFileUploadProps extends Omit< diff --git a/web/packages/studio/src/components/DatasetInputFile/index.tsx b/web/packages/studio/src/components/DatasetInputFile/index.tsx index 8db93d767e..99ca9ada1a 100644 --- a/web/packages/studio/src/components/DatasetInputFile/index.tsx +++ b/web/packages/studio/src/components/DatasetInputFile/index.tsx @@ -8,6 +8,7 @@ import { SubmitUploadType } from '@nemo/common/src/components/UploadModal/types' import { InputFileSchemaType } from '@nemo/common/src/types'; import { extractUserFriendlyKeysFromRow } from '@nemo/common/src/utils/file'; import { validateFileFormat, detectFileStructure } from '@nemo/common/src/utils/fileValidation'; +import { logger } from '@nemo/common/src/utils/logger'; import { Banner, Button, @@ -27,7 +28,6 @@ import type { import { useDatasetInputFileReducer } from '@studio/components/DatasetInputFile/useDatasetInputFileReducer'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getDatasetDisplayNameFromFilesUrl } from '@studio/util/files'; -import { logger } from '@studio/util/logger'; import { useQueryClient } from '@tanstack/react-query'; import { Plus, CircleCheck, CircleHelp, Eye, File as FileIcon, Trash2 } from 'lucide-react'; import { FC, useCallback, useEffect, useRef } from 'react'; diff --git a/web/packages/studio/src/components/DatasetsTable/index.test.tsx b/web/packages/studio/src/components/DatasetsTable/index.test.tsx index afc2f0dc4d..17edaede54 100644 --- a/web/packages/studio/src/components/DatasetsTable/index.test.tsx +++ b/web/packages/studio/src/components/DatasetsTable/index.test.tsx @@ -52,7 +52,7 @@ vi.mock('@studio/components/BulkDeleteModal', () => ({ ), })); -vi.mock('@studio/components/DeleteConfirmationModal', () => ({ +vi.mock('@nemo/common/src/components/DeleteConfirmationModal', () => ({ DeleteConfirmationModal: vi.fn( ({ open, diff --git a/web/packages/studio/src/components/DatasetsTable/index.tsx b/web/packages/studio/src/components/DatasetsTable/index.tsx index d75df25bd7..c0c84c065d 100644 --- a/web/packages/studio/src/components/DatasetsTable/index.tsx +++ b/web/packages/studio/src/components/DatasetsTable/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { getEntityReference } from '@nemo/common/src/namedEntity'; @@ -11,7 +12,6 @@ import { DatasetCreateModalMode } from '@studio/components/DatasetCreateModal/co import { makeDatasetsTableColumns } from '@studio/components/DatasetsTable/columns'; import { type DatasetsTableProps } from '@studio/components/DatasetsTable/types'; import { useDatasetsTable } from '@studio/components/DatasetsTable/useDatasetsTable'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { DocumentationButton } from '@studio/components/DocumentationButton'; import { Loading } from '@studio/components/Layouts/Loading'; import { NewDatasetButton } from '@studio/components/NewDatasetButton'; diff --git a/web/packages/studio/src/components/ExperimentCreateModal/index.tsx b/web/packages/studio/src/components/ExperimentCreateModal/index.tsx index 73237f1bdd..7c72ac1ffe 100644 --- a/web/packages/studio/src/components/ExperimentCreateModal/index.tsx +++ b/web/packages/studio/src/components/ExperimentCreateModal/index.tsx @@ -13,6 +13,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { FormModal, type FormModalProps } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { getListExperimentsQueryKey, useCreateExperiment } from '@nemo/sdk/generated/platform/api'; import { CodeSnippet, @@ -32,7 +33,6 @@ import { experimentCreateSchema, type ExperimentCreateFormFields, } from '@studio/components/ExperimentCreateModal/constants'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { AxiosError } from 'axios'; import { useState, type FC } from 'react'; import { useForm, type SubmitHandler } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/FilesTable/CreateFileSplitsModal/index.tsx b/web/packages/studio/src/components/FilesTable/CreateFileSplitsModal/index.tsx index 82df2da4ac..2501ddcf98 100644 --- a/web/packages/studio/src/components/FilesTable/CreateFileSplitsModal/index.tsx +++ b/web/packages/studio/src/components/FilesTable/CreateFileSplitsModal/index.tsx @@ -7,6 +7,7 @@ import { ControlledSelect } from '@nemo/common/src/components/form/ControlledSel import { FormModal } from '@nemo/common/src/components/FormModal'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { Banner, Divider, Flex, Label, Spinner, Stack, Text } from '@nvidia/foundations-react-core'; import { useDatasetFileContent } from '@studio/api/datasets/useDatasetFileContent'; import { useSplitDatasetFile } from '@studio/api/datasets/useSplitDatasetFile'; @@ -25,7 +26,6 @@ import { ValueWithLabel } from '@studio/components/ValueWithLabel'; import { useSelectedDatasetId } from '@studio/hooks/useSelectedDatasetId'; import { tooltipClassName } from '@studio/styles/common'; import { getContentSchema } from '@studio/util/files'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { getTextWithCount } from '@studio/util/strings'; import { Split } from 'lucide-react'; import { ComponentProps, FC, useMemo } from 'react'; diff --git a/web/packages/studio/src/components/FilesTable/DirectoryQuickActions/index.tsx b/web/packages/studio/src/components/FilesTable/DirectoryQuickActions/index.tsx index e3b272af5e..06631ec5e2 100644 --- a/web/packages/studio/src/components/FilesTable/DirectoryQuickActions/index.tsx +++ b/web/packages/studio/src/components/FilesTable/DirectoryQuickActions/index.tsx @@ -1,16 +1,16 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +import { + QuickActionsMenuRoot, + type QuickActionItem, +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useQueryParams } from '@nemo/common/src/hooks/useQueryParams'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { useDatasetDirectoryDelete } from '@studio/api/datasets/useDatasetDirectoryDelete'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { FileSystemDirectory } from '@studio/components/FilesTable/utils'; -import { - QuickActionsMenuRoot, - type QuickActionItem, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useSelectedDatasetId } from '@studio/hooks/useSelectedDatasetId'; import { QUERY_PARAMETERS } from '@studio/routes/constants'; import { resolveDatasetFilePath } from '@studio/util/files'; diff --git a/web/packages/studio/src/components/FilesTable/FileQuickActions/index.tsx b/web/packages/studio/src/components/FilesTable/FileQuickActions/index.tsx index 1f4b015205..4b92af34f7 100644 --- a/web/packages/studio/src/components/FilesTable/FileQuickActions/index.tsx +++ b/web/packages/studio/src/components/FilesTable/FileQuickActions/index.tsx @@ -1,10 +1,14 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +import { + type QuickActionItem, + QuickActionsMenuRoot, +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { useDatasetFileDelete } from '@studio/api/datasets/useDatasetFileDelete'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { AddToFolderModal } from '@studio/components/filesets/AddToFolderModal'; import { useBulkDownload } from '@studio/components/filesets/hooks/useBulkDownload'; import { useBulkDuplicate } from '@studio/components/filesets/hooks/useBulkDuplicate'; @@ -12,10 +16,6 @@ import { CreateFileSplitsModal } from '@studio/components/FilesTable/CreateFileS import { RenameFileModal } from '@studio/components/FilesTable/RenameFileModal'; import { TransformFileModal } from '@studio/components/FilesTable/TransformFileModal'; import { FileSystemFile, FileSystemNode } from '@studio/components/FilesTable/utils'; -import { - type QuickActionItem, - QuickActionsMenuRoot, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useSelectedDatasetId } from '@studio/hooks/useSelectedDatasetId'; import { resolveDatasetFilePath } from '@studio/util/files'; import { FC, useState } from 'react'; diff --git a/web/packages/studio/src/components/FilesTable/RenameFileModal/index.tsx b/web/packages/studio/src/components/FilesTable/RenameFileModal/index.tsx index 79b7489891..1c8eb6b4a9 100644 --- a/web/packages/studio/src/components/FilesTable/RenameFileModal/index.tsx +++ b/web/packages/studio/src/components/FilesTable/RenameFileModal/index.tsx @@ -5,9 +5,9 @@ import { ControlledTextInput } from '@nemo/common/src/components/form/Controlled import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { useDatasetFileRename } from '@studio/api/datasets/useDatasetFileRename'; import { useSelectedDatasetId } from '@studio/hooks/useSelectedDatasetId'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/FilesTable/TransformFileModal/index.tsx b/web/packages/studio/src/components/FilesTable/TransformFileModal/index.tsx index 267cec2d72..161eae907b 100644 --- a/web/packages/studio/src/components/FilesTable/TransformFileModal/index.tsx +++ b/web/packages/studio/src/components/FilesTable/TransformFileModal/index.tsx @@ -7,6 +7,7 @@ import { FormModal } from '@nemo/common/src/components/FormModal'; import { ModelSelect } from '@nemo/common/src/components/ModelSelect'; import { getEntityReference, getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { useModelsListModels } from '@nemo/sdk/generated/platform/api'; import { Divider, Flex, Label, Spinner, Stack, Text } from '@nvidia/foundations-react-core'; import { useDatasetFileContent } from '@studio/api/datasets/useDatasetFileContent'; @@ -20,7 +21,6 @@ import { ValueWithLabel } from '@studio/components/ValueWithLabel'; import { useSelectedDatasetId } from '@studio/hooks/useSelectedDatasetId'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getContentSchema } from '@studio/util/files'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { GitBranch } from 'lucide-react'; import { useMemo, type ComponentProps, type FC } from 'react'; import { useForm } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/FilesetActionMenu/index.tsx b/web/packages/studio/src/components/FilesetActionMenu/index.tsx index 47b120d96b..ba93ac5b19 100644 --- a/web/packages/studio/src/components/FilesetActionMenu/index.tsx +++ b/web/packages/studio/src/components/FilesetActionMenu/index.tsx @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { getEntityReference } from '@nemo/common/src/namedEntity'; +import { logger } from '@nemo/common/src/utils/logger'; import { useFilesDeleteFileset } from '@nemo/sdk/generated/platform/api'; import type { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { @@ -14,8 +16,6 @@ import { import { invalidateDatasetCaches } from '@studio/api/datasets/invalidateDatasetCaches'; import { DatasetCreateModal } from '@studio/components/DatasetCreateModal'; import { DatasetCreateModalMode } from '@studio/components/DatasetCreateModal/constants'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; -import { logger } from '@studio/util/logger'; import { EllipsisVertical } from 'lucide-react'; import { type FC, useState } from 'react'; diff --git a/web/packages/studio/src/components/FilesetCreateModal/index.tsx b/web/packages/studio/src/components/FilesetCreateModal/index.tsx index 1586a014b9..c0a59a6932 100644 --- a/web/packages/studio/src/components/FilesetCreateModal/index.tsx +++ b/web/packages/studio/src/components/FilesetCreateModal/index.tsx @@ -2,18 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage as getApiErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledTextArea'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { toValidFilesetName } from '@nemo/common/src/utils/filesetName'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { getFilesListFilesetsQueryKey, useFilesCreateFileset, } from '@nemo/sdk/generated/platform/api'; import { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { SegmentedControl, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage as getApiErrorMessage } from '@studio/api/common/utils'; import { filesetCreateFormSchema, type FilesetCreateFormData, @@ -26,7 +27,6 @@ import { FilesetDetailTab } from '@studio/routes/FilesetDetailRoute/constants'; import { CreateSecretModal } from '@studio/routes/SecretsListRoute/CreateSecretModal'; import { SecretSearchableSelect } from '@studio/routes/SecretsListRoute/SecretSearchableSelect'; import { getFilesetDetailRoute } from '@studio/routes/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { isHuggingFaceUrl, isNgcUrl, diff --git a/web/packages/studio/src/components/FilesetFilePreviewPanel/components/FileActions/index.tsx b/web/packages/studio/src/components/FilesetFilePreviewPanel/components/FileActions/index.tsx index 22ac0fa163..a16e6c6dc9 100644 --- a/web/packages/studio/src/components/FilesetFilePreviewPanel/components/FileActions/index.tsx +++ b/web/packages/studio/src/components/FilesetFilePreviewPanel/components/FileActions/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { triggerDownload } from '@nemo/common/src/utils/file'; import { @@ -12,7 +13,6 @@ import { Flex, } from '@nvidia/foundations-react-core'; import { useDatasetFileDelete } from '@studio/api/datasets/useDatasetFileDelete'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { CreateFileSplitsModal } from '@studio/components/FilesTable/CreateFileSplitsModal'; import { RenameFileModal } from '@studio/components/FilesTable/RenameFileModal'; import { FileSystemFile, FileSystemNode } from '@studio/components/FilesTable/utils'; diff --git a/web/packages/studio/src/components/IntakeDetail/IntakeComponents/AnnotationsPanel.tsx b/web/packages/studio/src/components/IntakeDetail/IntakeComponents/AnnotationsPanel.tsx index 6cde2fcbf4..f0300e32e8 100644 --- a/web/packages/studio/src/components/IntakeDetail/IntakeComponents/AnnotationsPanel.tsx +++ b/web/packages/studio/src/components/IntakeDetail/IntakeComponents/AnnotationsPanel.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { formatAbsoluteTimestamp } from '@nemo/common/src/components/RelativeTime/util'; import { DEFAULT_PAGE_SIZE } from '@nemo/common/src/constants/api'; import { useListAnnotations } from '@nemo/sdk/generated/platform/api'; @@ -19,7 +20,6 @@ import { Text, TextArea, } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { ThumbButton } from '@studio/components/buttons/ThumbButton'; import { useSpanAnnotationActions } from '@studio/components/IntakeDetail/IntakeComponents/useSpanAnnotationActions'; import { MessageSquarePlus, Trash2 } from 'lucide-react'; diff --git a/web/packages/studio/src/components/IntakeDetail/IntakeComponents/useSpanAnnotationActions.ts b/web/packages/studio/src/components/IntakeDetail/IntakeComponents/useSpanAnnotationActions.ts index 8b29b0bad0..f2c56ba0ac 100644 --- a/web/packages/studio/src/components/IntakeDetail/IntakeComponents/useSpanAnnotationActions.ts +++ b/web/packages/studio/src/components/IntakeDetail/IntakeComponents/useSpanAnnotationActions.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { getListAnnotationsQueryKey, listAnnotations, @@ -13,7 +14,6 @@ import { type FeedbackAnnotationInputValue, NoteAnnotationInputKind, } from '@nemo/sdk/generated/platform/schema'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useQueryClient } from '@tanstack/react-query'; import { useCallback, useState } from 'react'; diff --git a/web/packages/studio/src/components/IntakeDetail/SessionDetailView.tsx b/web/packages/studio/src/components/IntakeDetail/SessionDetailView.tsx index 9b058820b4..a1ed620cff 100644 --- a/web/packages/studio/src/components/IntakeDetail/SessionDetailView.tsx +++ b/web/packages/studio/src/components/IntakeDetail/SessionDetailView.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack, StatusMessage } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { TraceDetailLayout } from '@studio/components/IntakeDetail/TraceDetailLayout'; import { TraceSpanTree } from '@studio/components/IntakeDetail/TraceDetailSpanTree'; import { SessionSummaryHeader } from '@studio/components/IntakeDetail/TraceDetailSummaryHeader'; diff --git a/web/packages/studio/src/components/IntakeDetail/TraceSpanAccordions.tsx b/web/packages/studio/src/components/IntakeDetail/TraceSpanAccordions.tsx index d8ad94e2f8..12a8ff5c2d 100644 --- a/web/packages/studio/src/components/IntakeDetail/TraceSpanAccordions.tsx +++ b/web/packages/studio/src/components/IntakeDetail/TraceSpanAccordions.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { type ListSpansQueryError, @@ -15,7 +16,6 @@ import { type Trace, } from '@nemo/sdk/generated/platform/schema'; import { Flex, Spinner, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { IntakeErrorBanner } from '@studio/components/IntakeDetail/IntakeComponents/IntakeErrorBanner'; import { SpanListView } from '@studio/components/IntakeDetail/TraceSpanListView'; import { diff --git a/web/packages/studio/src/components/IntakeLists/IntakeSpansTable.tsx b/web/packages/studio/src/components/IntakeLists/IntakeSpansTable.tsx index de2804f814..baeb838da3 100644 --- a/web/packages/studio/src/components/IntakeLists/IntakeSpansTable.tsx +++ b/web/packages/studio/src/components/IntakeLists/IntakeSpansTable.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { dateTimeFilter } from '@nemo/common/src/components/DataView/dateTimeFilter'; import * as DataView from '@nemo/common/src/components/DataView/internal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; @@ -16,7 +17,6 @@ import { type SpanSortField, } from '@nemo/sdk/generated/platform/schema'; import { Anchor, Button, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { IntakeTelemetryStatusBadge } from '@studio/components/IntakeDetail/IntakeComponents/IntakeTelemetryStatusBadge'; import { isDefaultStartedAtFilter, diff --git a/web/packages/studio/src/components/IntakeLists/IntakeTracesTable.tsx b/web/packages/studio/src/components/IntakeLists/IntakeTracesTable.tsx index 2f89c6bae7..6a47cdf979 100644 --- a/web/packages/studio/src/components/IntakeLists/IntakeTracesTable.tsx +++ b/web/packages/studio/src/components/IntakeLists/IntakeTracesTable.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { EditColumnsMenu } from '@nemo/common/src/components/DataView/internal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -9,7 +10,6 @@ import { getSortParamWithWhitelist } from '@nemo/common/src/utils/query'; import { useListTraces } from '@nemo/sdk/generated/platform/api'; import type { Trace, TraceFilter, TraceSortField } from '@nemo/sdk/generated/platform/schema'; import { Button } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { isDefaultStartedAtFilter, makeDefaultStartedAtFilter, diff --git a/web/packages/studio/src/components/ModelActionsMenu/index.tsx b/web/packages/studio/src/components/ModelActionsMenu/index.tsx index 720ff45329..5f4c353415 100644 --- a/web/packages/studio/src/components/ModelActionsMenu/index.tsx +++ b/web/packages/studio/src/components/ModelActionsMenu/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { ModelEntity } from '@nemo/sdk/generated/platform/schema'; -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { modelToFormData } from '@studio/routes/PromptTuningFormRoute/utils'; import { getPromptTuningFormRoute } from '@studio/routes/utils'; import { GitFork, Trash, FolderOpen } from 'lucide-react'; diff --git a/web/packages/studio/src/components/ModelChat/index.tsx b/web/packages/studio/src/components/ModelChat/index.tsx index 3865e67ab6..2b37ebba3f 100644 --- a/web/packages/studio/src/components/ModelChat/index.tsx +++ b/web/packages/studio/src/components/ModelChat/index.tsx @@ -7,12 +7,12 @@ import { type AssistantChatProps, } from '@nemo/common/src/components/AssistantChat'; import type { AssistantMessageCompletion } from '@nemo/common/src/components/AssistantChat/types'; +import { handleGenericError } from '@nemo/common/src/utils/logger'; import type { ModelChatStatus } from '@nemo/common/src/utils/models'; import { DEFAULT_SEED_QUESTIONS } from '@studio/components/chat/defaultSeedQuestions'; import { SeedQuestions } from '@studio/components/chat/SeedQuestions'; import { StatsBadge, type ChatMetrics } from '@studio/components/chat/StatsBadge'; import type { ComposerSeed } from '@studio/routes/ModelCompareRoute/types'; -import { handleGenericError } from '@studio/util/logger'; import { type ReactNode, useEffect, useRef, useState, type FC } from 'react'; interface ModelChatProps extends Pick< diff --git a/web/packages/studio/src/components/ModelComparePrompts/useModelComparePrompts.ts b/web/packages/studio/src/components/ModelComparePrompts/useModelComparePrompts.ts index d531f9b7dd..251e6438ae 100644 --- a/web/packages/studio/src/components/ModelComparePrompts/useModelComparePrompts.ts +++ b/web/packages/studio/src/components/ModelComparePrompts/useModelComparePrompts.ts @@ -4,6 +4,7 @@ import type { SubmitUploadType } from '@nemo/common/src/components/UploadModal/types'; import { useChatCompletion } from '@nemo/common/src/hooks/useChatCompletion'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; +import { logger } from '@nemo/common/src/utils/logger'; import { type FileSampleMethod } from '@nemo/common/src/utils/sampleTextLines'; import { filesDownloadFile } from '@nemo/sdk/generated/platform/api'; import { SAMPLE_DATASETS } from '@studio/components/chat/sampleDatasets'; @@ -25,7 +26,6 @@ import type { ResponseResult, ResponseStats, } from '@studio/components/ModelComparePrompts/types'; -import { logger } from '@studio/util/logger'; import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; type UseModelComparePromptsArgs = Pick< diff --git a/web/packages/studio/src/components/NewCustomizationForm/index.tsx b/web/packages/studio/src/components/NewCustomizationForm/index.tsx index 6296d06c37..ce7b753b04 100644 --- a/web/packages/studio/src/components/NewCustomizationForm/index.tsx +++ b/web/packages/studio/src/components/NewCustomizationForm/index.tsx @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { generateDefaultName } from '@nemo/common/src/utils/generateDefaultName'; import { @@ -17,8 +19,6 @@ import { Panel, Stack, } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { CustomizationFilesetSelect } from '@studio/components/customizer/CustomizationFilesetSelect'; import { BackendSelectionSection } from '@studio/components/NewCustomizationForm/BackendSelectionSection'; import { ComputeResourcesSection } from '@studio/components/NewCustomizationForm/ComputeResourcesSection'; diff --git a/web/packages/studio/src/components/NewDataDesignerJobForm/JobRequestGenerator.tsx b/web/packages/studio/src/components/NewDataDesignerJobForm/JobRequestGenerator.tsx index 4ebac32948..0f562343d4 100644 --- a/web/packages/studio/src/components/NewDataDesignerJobForm/JobRequestGenerator.tsx +++ b/web/packages/studio/src/components/NewDataDesignerJobForm/JobRequestGenerator.tsx @@ -10,13 +10,13 @@ import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledTextArea'; import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; import { useChatCompletion } from '@nemo/common/src/hooks/useChatCompletion'; +import { getErrorMessage } from '@nemo/common/src/utils/error'; import type { CreateJobRequest as DataDesignerJobRequest } from '@nemo/sdk/generated/data-designer/schema'; import { Flex, Stack, Text } from '@nvidia/foundations-react-core'; import { DATA_DESIGNER_JOB_GENERATOR_SYSTEM_PROMPT } from '@studio/components/NewDataDesignerJobForm/constants'; import { generateDataDesignerJobRequestTool } from '@studio/components/NewDataDesignerJobForm/tools'; import { applyFormModelToJobRequest, - getErrorMessage, getWorkspaceAndModel, parseJsonContentToJobRequest, parseToolResponseToJobRequest, diff --git a/web/packages/studio/src/components/NewDataDesignerJobForm/usePreview.ts b/web/packages/studio/src/components/NewDataDesignerJobForm/usePreview.ts index 03d78a2e70..54cd0d2f57 100644 --- a/web/packages/studio/src/components/NewDataDesignerJobForm/usePreview.ts +++ b/web/packages/studio/src/components/NewDataDesignerJobForm/usePreview.ts @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/utils/error'; import type { DataDesignerConfig } from '@nemo/sdk/generated/data-designer/schema'; import { isAbortError, streamPreview } from '@studio/components/NewDataDesignerJobForm/previewApi'; -import { getErrorMessage } from '@studio/components/NewDataDesignerJobForm/utils'; import { useCallback, useRef, useState } from 'react'; const PREVIEW_PATH_TEMPLATE = '/apis/data-designer/v2/workspaces/:workspace/preview'; diff --git a/web/packages/studio/src/components/NewDataDesignerJobForm/utils.test.ts b/web/packages/studio/src/components/NewDataDesignerJobForm/utils.test.ts index 28b1c5a00f..6917310cb3 100644 --- a/web/packages/studio/src/components/NewDataDesignerJobForm/utils.test.ts +++ b/web/packages/studio/src/components/NewDataDesignerJobForm/utils.test.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { COPY_NAME_SUFFIX } from '@nemo/common/src/utils/entityName'; +import { getErrorMessage } from '@nemo/common/src/utils/error'; import type { CreateJob as DataDesignerJob, CreateJobRequest as DataDesignerJobRequest, @@ -10,7 +11,6 @@ import { applyFormModelToJobRequest, buildClonedJobRequest, getCloneJobRequestFromState, - getErrorMessage, getWorkspaceAndModel, modelsFromProviders, PARSE_ERROR_INVALID_JSON, diff --git a/web/packages/studio/src/components/NewDataDesignerJobForm/utils.ts b/web/packages/studio/src/components/NewDataDesignerJobForm/utils.ts index 64a66aee8b..d1e6208cea 100644 --- a/web/packages/studio/src/components/NewDataDesignerJobForm/utils.ts +++ b/web/packages/studio/src/components/NewDataDesignerJobForm/utils.ts @@ -12,13 +12,6 @@ import type { ModelEntity, ModelProvider } from '@nemo/sdk/generated/platform/sc /** Model option for the form: entity plus display name used in job requests */ export type DataDesignerModelOption = ModelEntity & { served_model_name: string }; -/** Get a display message from an unknown error; use default when not an Error instance */ -export function getErrorMessage(error: unknown, defaultMessage?: string): string { - return error instanceof Error - ? error.message - : (defaultMessage ?? 'Something went wrong. Please try again.'); -} - /** * Resolve workspace and model name for chat/completion calls. * If modelRef contains "/", parses as "workspace/name"; otherwise uses fallbackWorkspace and modelRef as name. diff --git a/web/packages/studio/src/components/PromptTuningForm/ImportFromDatasetModal/index.tsx b/web/packages/studio/src/components/PromptTuningForm/ImportFromDatasetModal/index.tsx index cca9c3510e..9d80abbb5f 100644 --- a/web/packages/studio/src/components/PromptTuningForm/ImportFromDatasetModal/index.tsx +++ b/web/packages/studio/src/components/PromptTuningForm/ImportFromDatasetModal/index.tsx @@ -3,11 +3,11 @@ import { FormModal } from '@nemo/common/src/components/FormModal'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { Banner, Stack } from '@nvidia/foundations-react-core'; import { useDatasetFileContent } from '@studio/api/datasets/useDatasetFileContent'; import { DatasetFileSelect } from '@studio/components/DatasetFileSelect'; import { DatasetSelect } from '@studio/components/DatasetSelect'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC } from 'react'; import { Controller, FormProvider, useForm, useWatch } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/PromptTuningForm/InContextLearningSection/index.tsx b/web/packages/studio/src/components/PromptTuningForm/InContextLearningSection/index.tsx index 0d01862983..2f3e9b9ed7 100644 --- a/web/packages/studio/src/components/PromptTuningForm/InContextLearningSection/index.tsx +++ b/web/packages/studio/src/components/PromptTuningForm/InContextLearningSection/index.tsx @@ -2,12 +2,12 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { AccordionSection } from '@nemo/common/src/components/AccordionSection'; import { FileContentPreview } from '@nemo/common/src/components/FileContentPreview'; import { FileList, FileListItem } from '@nemo/common/src/components/FileList'; import { UploadModal } from '@nemo/common/src/components/UploadModal'; import { compileSystemPrompt } from '@nemo/common/src/models/utils'; import { Button, Flex, SidePanel, Stack, Text } from '@nvidia/foundations-react-core'; -import { AccordionSection } from '@studio/components/AccordionSection'; import { ImportFileContentFormFields, importFileContentSchema, diff --git a/web/packages/studio/src/components/PromptTuningForm/InferenceParametersSection/index.tsx b/web/packages/studio/src/components/PromptTuningForm/InferenceParametersSection/index.tsx index c8e294ee12..72e829e19b 100644 --- a/web/packages/studio/src/components/PromptTuningForm/InferenceParametersSection/index.tsx +++ b/web/packages/studio/src/components/PromptTuningForm/InferenceParametersSection/index.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccordionSection } from '@nemo/common/src/components/AccordionSection'; import { InferenceParameters } from '@nemo/common/src/components/ModelSelectV2/InferenceParameters'; import type { InferenceParams } from '@nemo/sdk/generated/platform/schema'; -import { AccordionSection } from '@studio/components/AccordionSection'; import type { PromptTuningFormFields, PromptTuningFormSectionProps, diff --git a/web/packages/studio/src/components/PromptTuningForm/PromptTemplateSection/index.tsx b/web/packages/studio/src/components/PromptTuningForm/PromptTemplateSection/index.tsx index 310c7ac51f..c5cd16d216 100644 --- a/web/packages/studio/src/components/PromptTuningForm/PromptTemplateSection/index.tsx +++ b/web/packages/studio/src/components/PromptTuningForm/PromptTemplateSection/index.tsx @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccordionSection } from '@nemo/common/src/components/AccordionSection'; import { DEFAULT_PROMPT_TEMPLATE, DEFAULT_PROMPT_TEMPLATE_COMPILED, } from '@nemo/common/src/models/constants'; import { compileSystemPrompt } from '@nemo/common/src/models/utils'; import { Button, Flex, FormField, Stack, TextArea } from '@nvidia/foundations-react-core'; -import { AccordionSection } from '@studio/components/AccordionSection'; import type { PromptTuningFormFields, PromptTuningFormSectionProps, diff --git a/web/packages/studio/src/components/PromptTuningForm/ToolsSection/index.tsx b/web/packages/studio/src/components/PromptTuningForm/ToolsSection/index.tsx index 78b9a5a214..505fa48d82 100644 --- a/web/packages/studio/src/components/PromptTuningForm/ToolsSection/index.tsx +++ b/web/packages/studio/src/components/PromptTuningForm/ToolsSection/index.tsx @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { AccordionSection } from '@nemo/common/src/components/AccordionSection'; import { FormModal } from '@nemo/common/src/components/FormModal'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { Anchor, Button, Flex, Stack, Switch, Text } from '@nvidia/foundations-react-core'; -import { AccordionSection } from '@studio/components/AccordionSection'; import { DetailRow } from '@studio/components/DetailRow'; import { AddToolForm } from '@studio/components/PromptTuningForm/ToolsSection/components/AddToolForm'; import { ToolMetadataPanel } from '@studio/components/PromptTuningForm/ToolsSection/components/ToolMetadataPanel'; @@ -15,7 +16,6 @@ import { import { TOOL_JSON_EXAMPLE } from '@studio/components/PromptTuningForm/ToolsSection/constants'; import { useSubmitSingleTool } from '@studio/components/PromptTuningForm/ToolsSection/hooks/useSubmitSingleTool'; import { PromptTuningFormFields } from '@studio/routes/PromptTuningFormRoute/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { Plus, ExternalLink, Toolbox } from 'lucide-react'; import { FC, useState } from 'react'; import { FormProvider, useForm, useFormContext, useWatch } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuDefault/index.tsx b/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuDefault/index.tsx index ca8e9a4747..cf44837b12 100644 --- a/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuDefault/index.tsx +++ b/web/packages/studio/src/components/QuickActionsMenu/QuickActionsMenuDefault/index.tsx @@ -4,7 +4,7 @@ import { QuickActionItem, QuickActionsMenuRoot, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { Pencil, Trash, FolderOpen } from 'lucide-react'; import { FC, useMemo } from 'react'; import { useNavigate } from 'react-router'; diff --git a/web/packages/studio/src/components/WorkspaceCreateModal/index.tsx b/web/packages/studio/src/components/WorkspaceCreateModal/index.tsx index 8e14ebe344..b576932038 100644 --- a/web/packages/studio/src/components/WorkspaceCreateModal/index.tsx +++ b/web/packages/studio/src/components/WorkspaceCreateModal/index.tsx @@ -13,6 +13,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { getEntitiesListWorkspacesQueryKey, useEntitiesCreateWorkspace, @@ -21,7 +22,6 @@ import { FormField, Stack, TextArea, TextInput } from '@nvidia/foundations-react import { queryClient } from '@studio/api/queryClient'; import { workspaceCreateSchema } from '@studio/constants/zod'; import { getWorkspaceDetailsDefaultRoute } from '@studio/routes/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { AxiosError } from 'axios'; import { FC } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/WorkspaceDropdown/index.tsx b/web/packages/studio/src/components/WorkspaceDropdown/index.tsx index 078a66b9eb..b2ede06b9a 100644 --- a/web/packages/studio/src/components/WorkspaceDropdown/index.tsx +++ b/web/packages/studio/src/components/WorkspaceDropdown/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { useEntitiesListWorkspaces } from '@nemo/sdk/generated/platform/api'; import { Workspace } from '@nemo/sdk/generated/platform/schema'; import { @@ -23,7 +24,6 @@ import { useRecentWorkspaces } from '@studio/components/WorkspaceDropdown/useRec import { DEFAULT_LARGE_PAGE_SIZE } from '@studio/constants/constants'; import { getWorkspaceDetailsDefaultRoute } from '@studio/routes/utils'; import { useBoolean } from '@studio/util/hooks/useBoolean'; -import { logger } from '@studio/util/logger'; import cn from 'classnames'; import { Plus, Filter } from 'lucide-react'; import { ChangeEvent, FC, lazy, useMemo, useState } from 'react'; diff --git a/web/packages/studio/src/components/common/SearchBar/index.tsx b/web/packages/studio/src/components/common/SearchBar/index.tsx index b1bbb0bb74..ab8d6b97b4 100644 --- a/web/packages/studio/src/components/common/SearchBar/index.tsx +++ b/web/packages/studio/src/components/common/SearchBar/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { FormField, TextInput } from '@nvidia/foundations-react-core'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { Search } from 'lucide-react'; import { ComponentProps } from 'react'; import { useForm, type SubmitHandler } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/dataViews/AgentEvaluationsDataView/index.tsx b/web/packages/studio/src/components/dataViews/AgentEvaluationsDataView/index.tsx index c47f5d562a..5b22e58f9c 100644 --- a/web/packages/studio/src/components/dataViews/AgentEvaluationsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/AgentEvaluationsDataView/index.tsx @@ -7,6 +7,7 @@ import { ROW_SELECTION_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -26,7 +27,6 @@ import { import { Button } from '@nvidia/foundations-react-core'; import { agentNameForJob, evalConfigName } from '@studio/api/evaluation/agent-evaluations'; import { BulkDeleteModal } from '@studio/components/BulkDeleteModal'; -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { STATUS_FILTER_OPTIONS } from '@studio/constants/platformJobs'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getAgentEvaluationDetailRoute, getFilesetRoute } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/components/dataViews/AgentsDataView/index.tsx b/web/packages/studio/src/components/dataViews/AgentsDataView/index.tsx index ca2e5a1d85..6520c5f38d 100644 --- a/web/packages/studio/src/components/dataViews/AgentsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/AgentsDataView/index.tsx @@ -7,6 +7,7 @@ import { ROW_SELECTION_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -27,7 +28,6 @@ import type { Agent } from '@nemo/sdk/generated/agents/schema/Agent'; import type { AgentDeployment } from '@nemo/sdk/generated/agents/schema/AgentDeployment'; import { Button, Text } from '@nvidia/foundations-react-core'; import { getAgentModelNames } from '@studio/components/dataViews/AgentsDataView/utils'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { DocumentationButton } from '@studio/components/DocumentationButton'; import { MODEL_COMPARE_ENABLED } from '@studio/constants/environment'; import { LINK_DOCS_STUDIO } from '@studio/constants/links'; diff --git a/web/packages/studio/src/components/dataViews/CustomModelsDataView/index.tsx b/web/packages/studio/src/components/dataViews/CustomModelsDataView/index.tsx index 2c8e50ed55..22147f7b2f 100644 --- a/web/packages/studio/src/components/dataViews/CustomModelsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/CustomModelsDataView/index.tsx @@ -8,6 +8,7 @@ import { ROW_SELECTION_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -33,7 +34,6 @@ import { FINETUNING_TYPE_FILTER_OPTIONS } from '@studio/components/dataViews/Cus import { CustomizeModelButton } from '@studio/components/dataViews/CustomModelsDataView/CustomizeModelButton'; import { DeploymentIndicator } from '@studio/components/dataViews/CustomModelsDataView/DeploymentIndicator'; import { KindTag } from '@studio/components/dataViews/CustomModelsDataView/KindTag'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { DocumentationButton } from '@studio/components/DocumentationButton'; import { BaseModelSearchFilterField } from '@studio/components/FilterFields'; import type { ModelPanelTab } from '@studio/components/sidePanels/ModelPanels/ModelPanel'; diff --git a/web/packages/studio/src/components/dataViews/DeploymentsDataView/index.tsx b/web/packages/studio/src/components/dataViews/DeploymentsDataView/index.tsx index b0caedd500..04620f07da 100644 --- a/web/packages/studio/src/components/dataViews/DeploymentsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/DeploymentsDataView/index.tsx @@ -10,8 +10,10 @@ * its affiliates is strictly prohibited. */ +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { withOperators } from '@nemo/common/src/api/filterOperators'; import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -23,8 +25,6 @@ import { ModelDeploymentStatus, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { CUSTOMIZER_ENABLED } from '@studio/constants/environment'; import { keepPreviousData } from '@tanstack/react-query'; import { Rocket, Trash2 } from 'lucide-react'; diff --git a/web/packages/studio/src/components/dataViews/ExperimentDataView/index.tsx b/web/packages/studio/src/components/dataViews/ExperimentDataView/index.tsx index 003cf1a3ef..6392616b7c 100644 --- a/web/packages/studio/src/components/dataViews/ExperimentDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/ExperimentDataView/index.tsx @@ -13,6 +13,7 @@ import { StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; @@ -33,7 +34,6 @@ import { } from '@studio/components/dataViews/ExperimentDataView/useExperimentEvaluations'; import { useSortErrorRecovery } from '@studio/components/dataViews/ExperimentDataView/useSortErrorRecovery'; import { deriveEvaluatorNames } from '@studio/components/dataViews/ExperimentDataView/util'; -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getEvaluationDetailRoute } from '@studio/routes/utils'; import { tooltipClassName } from '@studio/styles/common'; diff --git a/web/packages/studio/src/components/dataViews/GuardrailsDataView/index.tsx b/web/packages/studio/src/components/dataViews/GuardrailsDataView/index.tsx index 5824138c11..cea8380227 100644 --- a/web/packages/studio/src/components/dataViews/GuardrailsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/GuardrailsDataView/index.tsx @@ -1,10 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ROW_ACTIONS_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; @@ -14,9 +16,7 @@ import type { GuardrailsListGuardrailConfigsParams, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { countRails } from '@studio/components/dataViews/GuardrailsDataView/guardrailUtils'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { keepPreviousData } from '@tanstack/react-query'; import { Copy, ShieldCheck, Trash } from 'lucide-react'; import { type ComponentProps, type FC, useCallback } from 'react'; diff --git a/web/packages/studio/src/components/dataViews/InferenceProvidersDataView/index.tsx b/web/packages/studio/src/components/dataViews/InferenceProvidersDataView/index.tsx index 2efbf52139..3bd52a3b05 100644 --- a/web/packages/studio/src/components/dataViews/InferenceProvidersDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/InferenceProvidersDataView/index.tsx @@ -10,11 +10,14 @@ * its affiliates is strictly prohibited. */ +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { withOperators } from '@nemo/common/src/api/filterOperators'; import { ROW_ACTIONS_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -32,9 +35,6 @@ import { ModelProviderSort, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Stack, StatusMessage, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { LINK_DOCS_INFERENCE_PROVIDERS } from '@studio/constants/links'; import { EditInferenceProviderModal } from '@studio/routes/InferenceProvidersListRoute/EditInferenceProviderModal'; import { InferenceProviderDetailsSidePanel } from '@studio/routes/InferenceProvidersListRoute/InferenceProviderDetailsSidePanel'; diff --git a/web/packages/studio/src/components/dataViews/JobsDataView/index.tsx b/web/packages/studio/src/components/dataViews/JobsDataView/index.tsx index 4001b9790e..1ce408cea6 100644 --- a/web/packages/studio/src/components/dataViews/JobsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/JobsDataView/index.tsx @@ -1,9 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { withOperators } from '@nemo/common/src/api/filterOperators'; +import { CancelJobButton } from '@nemo/common/src/components/CancelJobButton'; import { dateTimeFilter } from '@nemo/common/src/components/DataView/dateTimeFilter'; import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; @@ -16,15 +19,12 @@ import type { PlatformJobsListFilter, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, StatusMessage } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { CancelJobButton } from '@studio/components/CancelJobButton'; import { HIDDEN_JOB_SOURCES, JOB_SOURCE, SOURCE_OPTIONS, } from '@studio/components/dataViews/JobsDataView/constants'; import { getJobDetailRoute } from '@studio/components/dataViews/JobsDataView/utils'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { CUSTOMIZER_ENABLED } from '@studio/constants/environment'; import { LINK_DOCS_JOBS } from '@studio/constants/links'; import { STATUS_FILTER_OPTIONS } from '@studio/constants/platformJobs'; @@ -195,7 +195,12 @@ export const JobsDataView = () => { enableSorting: false, cell: ({ row }) => ( - + ), }), diff --git a/web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx b/web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx index 2bf792cc50..802e0ae89d 100644 --- a/web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/SafeSynthesizerJobsDataView/index.tsx @@ -9,6 +9,7 @@ import { ROW_SELECTION_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { ScoreGauge } from '@nemo/common/src/components/ScoreGauge'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; @@ -31,7 +32,6 @@ import { Banner, Button, Stack } from '@nvidia/foundations-react-core'; import { BulkDeleteModal } from '@studio/components/BulkDeleteModal'; import { isCancellableJob } from '@studio/components/dataViews/SafeSynthesizerJobsDataView/utils'; import { DocumentationButton } from '@studio/components/DocumentationButton'; -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { FilesetFilePreviewLink } from '@studio/components/SafeSynthesizerFilesetPreview/FilesetFilePreviewLink'; import { LINK_DOCS_SAFE_SYNTHESIZER } from '@studio/constants/links'; import { STATUS_FILTER_OPTIONS } from '@studio/constants/platformJobs'; diff --git a/web/packages/studio/src/components/dataViews/SecretsDataView/index.tsx b/web/packages/studio/src/components/dataViews/SecretsDataView/index.tsx index e49c4176fd..2709d5e509 100644 --- a/web/packages/studio/src/components/dataViews/SecretsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/SecretsDataView/index.tsx @@ -10,10 +10,13 @@ * its affiliates is strictly prohibited. */ +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ROW_ACTIONS_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; @@ -21,10 +24,7 @@ import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { useSecretsDeleteSecret, useSecretsListSecrets } from '@nemo/sdk/generated/platform/api'; import { PlatformSecretResponse } from '@nemo/sdk/generated/platform/schema'; import { Button, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { DocumentationButton } from '@studio/components/DocumentationButton'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { LINK_DOCS_SECRETS } from '@studio/constants/links'; import { EditSecretModal } from '@studio/routes/SecretsListRoute/EditSecretModal'; import { keepPreviousData } from '@tanstack/react-query'; diff --git a/web/packages/studio/src/components/dataViews/VirtualModelsDataView/index.tsx b/web/packages/studio/src/components/dataViews/VirtualModelsDataView/index.tsx index ae6d1c1941..a4eb248827 100644 --- a/web/packages/studio/src/components/dataViews/VirtualModelsDataView/index.tsx +++ b/web/packages/studio/src/components/dataViews/VirtualModelsDataView/index.tsx @@ -1,12 +1,15 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { withOperators } from '@nemo/common/src/api/filterOperators'; import { dateTimeFilter } from '@nemo/common/src/components/DataView/dateTimeFilter'; import { ROW_ACTIONS_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { useDeferredUnmount } from '@nemo/common/src/hooks/useDeferredUnmount'; @@ -23,9 +26,6 @@ import type { VirtualModelFilter, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Stack, StatusMessage, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { BaseModelSearchFilterField } from '@studio/components/FilterFields'; import { VirtualModelDetailsSidePanel } from '@studio/routes/VirtualModelsListRoute/VirtualModelDetailsSidePanel'; import { keepPreviousData, useQueryClient } from '@tanstack/react-query'; diff --git a/web/packages/studio/src/components/evaluation/Configurations/form/InputFile/useInputFile.ts b/web/packages/studio/src/components/evaluation/Configurations/form/InputFile/useInputFile.ts index 3921680d24..b1f8d707d0 100644 --- a/web/packages/studio/src/components/evaluation/Configurations/form/InputFile/useInputFile.ts +++ b/web/packages/studio/src/components/evaluation/Configurations/form/InputFile/useInputFile.ts @@ -9,6 +9,7 @@ import { FileValidationResult, FileFormatDetectionResult, } from '@nemo/common/src/utils/fileValidation'; +import { logger } from '@nemo/common/src/utils/logger'; import { datasetFileContentQueryOptions } from '@studio/api/datasets/useDatasetFileContent'; import { buildTemplatePreview } from '@studio/components/evaluation/Configurations/form/InputFile/helpers'; import { @@ -18,7 +19,6 @@ import { } from '@studio/hooks/evaluation/useCreateConfigurationForm'; import { useFileValidation } from '@studio/hooks/evaluation/useFileValidation'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; -import { logger } from '@studio/util/logger'; import { useQueryClient } from '@tanstack/react-query'; import { useState, useCallback, useEffect, useMemo } from 'react'; import { useFormContext } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/evaluation/Jobs/ActionMenu.tsx b/web/packages/studio/src/components/evaluation/Jobs/ActionMenu.tsx index b45d8cda96..71656d3dcc 100644 --- a/web/packages/studio/src/components/evaluation/Jobs/ActionMenu.tsx +++ b/web/packages/studio/src/components/evaluation/Jobs/ActionMenu.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { useEvaluatorDeleteEvaluateJob } from '@nemo/sdk/generated/evaluator/api'; import type { EvaluateJob } from '@nemo/sdk/generated/evaluator/schema'; import { @@ -17,7 +18,6 @@ import { } from '@nvidia/foundations-react-core'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getEvaluationJobName } from '@studio/selectors/evaluationJob'; -import { logger } from '@studio/util/logger'; import { ArrowRight, EllipsisVertical, Trash } from 'lucide-react'; import { FC, useState } from 'react'; diff --git a/web/packages/studio/src/components/evaluation/Jobs/DetailsPanel.tsx b/web/packages/studio/src/components/evaluation/Jobs/DetailsPanel.tsx index 07f06747f6..966ceaf292 100644 --- a/web/packages/studio/src/components/evaluation/Jobs/DetailsPanel.tsx +++ b/web/packages/studio/src/components/evaluation/Jobs/DetailsPanel.tsx @@ -14,6 +14,7 @@ import { getDifferenceInMilliseconds, utcToLocalDate, } from '@nemo/common/src/utils/date'; +import { logger } from '@nemo/common/src/utils/logger'; import { getEvaluatorGetEvaluateJobQueryKey, useEvaluatorCancelEvaluateJob, @@ -23,7 +24,6 @@ import { Banner, Button, Flex, Modal, Panel, Stack, Text } from '@nvidia/foundat import { ButtonLaunchEvaluation } from '@studio/components/evaluation/ButtonLaunchEvaluation'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getFilesetRoute } from '@studio/routes/utils'; -import { logger } from '@studio/util/logger'; import { useQueryClient } from '@tanstack/react-query'; import { ChartBar, CircleX } from 'lucide-react'; import { useState } from 'react'; diff --git a/web/packages/studio/src/components/evaluation/Jobs/EvaluationJobBulkDeleteModal.tsx b/web/packages/studio/src/components/evaluation/Jobs/EvaluationJobBulkDeleteModal.tsx index e10d0f96f2..1942dca878 100644 --- a/web/packages/studio/src/components/evaluation/Jobs/EvaluationJobBulkDeleteModal.tsx +++ b/web/packages/studio/src/components/evaluation/Jobs/EvaluationJobBulkDeleteModal.tsx @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { useEvaluatorDeleteEvaluateJob } from '@nemo/sdk/generated/evaluator/api'; import { Button, Flex, Modal } from '@nvidia/foundations-react-core'; import { useMutateMany } from '@studio/api/common/useMutateMany'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; -import { logger } from '@studio/util/logger'; import { Trash } from 'lucide-react'; import { FC, useState } from 'react'; diff --git a/web/packages/studio/src/components/evaluation/Jobs/form/ScoreDefinitions.tsx b/web/packages/studio/src/components/evaluation/Jobs/form/ScoreDefinitions.tsx index 00465def6e..8236f4754d 100644 --- a/web/packages/studio/src/components/evaluation/Jobs/form/ScoreDefinitions.tsx +++ b/web/packages/studio/src/components/evaluation/Jobs/form/ScoreDefinitions.tsx @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { InputErrorText } from '@nemo/common/src/components/InputErrorText'; import { Badge, Button, Flex, Stack, Text } from '@nvidia/foundations-react-core'; import { ScoreModal, type MetricPanelFormData, type PanelScoreFormData, } from '@studio/components/evaluation/Jobs/form/ScoreModal'; -import { InputErrorText } from '@studio/components/InputErrorText'; import { Pencil, Plus, Trash } from 'lucide-react'; import { FC, useState } from 'react'; import { FieldError, useFormContext, useWatch } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/filesets/AddToFolderModal/index.tsx b/web/packages/studio/src/components/filesets/AddToFolderModal/index.tsx index 9e9c366c1c..6b45c6e7f2 100644 --- a/web/packages/studio/src/components/filesets/AddToFolderModal/index.tsx +++ b/web/packages/studio/src/components/filesets/AddToFolderModal/index.tsx @@ -5,6 +5,7 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { useFilesListFilesetFiles } from '@nemo/sdk/generated/platform/api'; import { Button, @@ -28,7 +29,6 @@ import { FileSystemNode } from '@studio/components/FilesTable/utils'; import { useDatasetNavigator } from '@studio/hooks/useDatasetNavigator'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getFilesetDetailsRoute } from '@studio/routes/utils'; -import { logger } from '@studio/util/logger'; import { FolderOpen, Info } from 'lucide-react'; import { type FC, useMemo } from 'react'; import { useForm, useWatch } from 'react-hook-form'; diff --git a/web/packages/studio/src/components/filesets/FilesetFileExplorer/DatasetFileDropzone/index.tsx b/web/packages/studio/src/components/filesets/FilesetFileExplorer/DatasetFileDropzone/index.tsx index c1b83bb85e..1bb94bd314 100644 --- a/web/packages/studio/src/components/filesets/FilesetFileExplorer/DatasetFileDropzone/index.tsx +++ b/web/packages/studio/src/components/filesets/FilesetFileExplorer/DatasetFileDropzone/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { Block, Flex, Text } from '@nvidia/foundations-react-core'; -import { logger } from '@studio/util/logger'; import { FC, useCallback, ReactNode } from 'react'; import { useDropzone, FileRejection } from 'react-dropzone'; diff --git a/web/packages/studio/src/components/filesets/FilesetFileExplorer/NewDirectoryModal/index.tsx b/web/packages/studio/src/components/filesets/FilesetFileExplorer/NewDirectoryModal/index.tsx index 1f14c1a720..94fb303784 100644 --- a/web/packages/studio/src/components/filesets/FilesetFileExplorer/NewDirectoryModal/index.tsx +++ b/web/packages/studio/src/components/filesets/FilesetFileExplorer/NewDirectoryModal/index.tsx @@ -5,10 +5,10 @@ import { zodResolver } from '@hookform/resolvers/zod'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { useCreateDirectory } from '@studio/api/datasets/useCreateDirectory'; import { getSiblingFolders } from '@studio/components/filesets/AddToFolderModal/utils'; import { FileSystemNode } from '@studio/components/FilesTable/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; diff --git a/web/packages/studio/src/components/filesets/hooks/useBulkDownload.ts b/web/packages/studio/src/components/filesets/hooks/useBulkDownload.ts index 0e555ce47e..242afa577d 100644 --- a/web/packages/studio/src/components/filesets/hooks/useBulkDownload.ts +++ b/web/packages/studio/src/components/filesets/hooks/useBulkDownload.ts @@ -3,10 +3,10 @@ import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { triggerDownload } from '@nemo/common/src/utils/file'; +import { logger } from '@nemo/common/src/utils/logger'; import { useDownloadFileAsArrayBuffer } from '@studio/components/filesets/hooks/useDownloadFileAsArrayBuffer'; import { FileSystemFile } from '@studio/components/FilesTable/utils'; import { getFileNameFromPath } from '@studio/util/files'; -import { logger } from '@studio/util/logger'; import { useCallback, useState } from 'react'; export interface UseBulkDownloadOptions { diff --git a/web/packages/studio/src/components/filesets/hooks/useBulkDuplicate.ts b/web/packages/studio/src/components/filesets/hooks/useBulkDuplicate.ts index a97449354f..e4b0176894 100644 --- a/web/packages/studio/src/components/filesets/hooks/useBulkDuplicate.ts +++ b/web/packages/studio/src/components/filesets/hooks/useBulkDuplicate.ts @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { filesListFilesetFiles } from '@nemo/sdk/generated/platform/api'; import { useDatasetFilesUpload } from '@studio/api/datasets/useDatasetFilesUpload'; import { useDownloadFileAsArrayBuffer } from '@studio/components/filesets/hooks/useDownloadFileAsArrayBuffer'; import { FileSystemFile } from '@studio/components/FilesTable/utils'; -import { logger } from '@studio/util/logger'; import { useCallback, useState } from 'react'; export interface UseBulkDuplicateOptions { diff --git a/web/packages/studio/src/components/CancelJobButton/index.test.tsx b/web/packages/studio/src/components/promoted/CancelJobButton.test.tsx similarity index 95% rename from web/packages/studio/src/components/CancelJobButton/index.test.tsx rename to web/packages/studio/src/components/promoted/CancelJobButton.test.tsx index 18c164ce0b..c25666a513 100644 --- a/web/packages/studio/src/components/CancelJobButton/index.test.tsx +++ b/web/packages/studio/src/components/promoted/CancelJobButton.test.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { CancelJobButton } from '@nemo/common/src/components/CancelJobButton'; import { PlatformJobStatus } from '@nemo/sdk/generated/platform/schema'; -import { CancelJobButton } from '@studio/components/CancelJobButton'; import { PLATFORM_BASE_URL } from '@studio/constants/environment'; import { ROUTE_PARAMS } from '@studio/constants/routes'; import { workspace1 } from '@studio/mocks/entity-store/projects'; @@ -18,7 +18,7 @@ const JOB_NAME = 'test-job-abc123'; const renderButton = (status?: PlatformJobStatus) => render( - + ); diff --git a/web/packages/studio/src/components/DeleteConfirmationModal/index.test.tsx b/web/packages/studio/src/components/promoted/DeleteConfirmationModal.test.tsx similarity index 96% rename from web/packages/studio/src/components/DeleteConfirmationModal/index.test.tsx rename to web/packages/studio/src/components/promoted/DeleteConfirmationModal.test.tsx index df9b4f3002..97fba36921 100644 --- a/web/packages/studio/src/components/DeleteConfirmationModal/index.test.tsx +++ b/web/packages/studio/src/components/promoted/DeleteConfirmationModal.test.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { dataset } from '@studio/mocks/datasets'; import { render, screen } from '@studio/tests/util/render'; import { waitFor } from '@testing-library/react'; diff --git a/web/packages/studio/src/components/ErrorPanel/index.test.tsx b/web/packages/studio/src/components/promoted/ErrorPanel.test.tsx similarity index 98% rename from web/packages/studio/src/components/ErrorPanel/index.test.tsx rename to web/packages/studio/src/components/promoted/ErrorPanel.test.tsx index 1b32991af6..e86cb4e49c 100644 --- a/web/packages/studio/src/components/ErrorPanel/index.test.tsx +++ b/web/packages/studio/src/components/promoted/ErrorPanel.test.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { ErrorPanel, ErrorPanelProps } from '@nemo/common/src/components/ErrorPanel'; import { suppressConsoleError } from '@nemo/testing/utils/suppress-console'; -import { ErrorPanel, ErrorPanelProps } from '@studio/components/ErrorPanel'; import { mockUseNavigate } from '@studio/tests/util/mockUseParams'; import { render, screen, fireEvent } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; diff --git a/web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx b/web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx index 78c03333bb..0cad82a3b0 100644 --- a/web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx +++ b/web/packages/studio/src/components/sidePanels/MetricRunSidePanel/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { useModelEntity } from '@nemo/common/src/api/models/useModelEntity'; import { useModelSearch } from '@nemo/common/src/api/models/useModelSearch'; import { VariableButton } from '@nemo/common/src/components/buttons/VariableButton'; @@ -12,6 +13,7 @@ import { ControlledDatasetFileSelect } from '@nemo/common/src/components/Dataset import type { VariableDef } from '@nemo/common/src/components/form/VariableTextArea'; import { ModelSelectV2 } from '@nemo/common/src/components/ModelSelectV2'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { useEvaluatorCreateEvaluateJob } from '@nemo/sdk/generated/evaluator/api'; import type { EvaluateJobRequest, MetricInline, Model } from '@nemo/sdk/generated/evaluator/schema'; import { @@ -23,7 +25,6 @@ import { Stack, Text, } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import type { MetricItemWithId } from '@studio/components/dataViews/EvaluationMetricsDataView/types'; import { EvalCard } from '@studio/components/evaluation/EvalCard'; import { FileValidationPanel } from '@studio/components/sidePanels/MetricRunSidePanel/FileValidationPanel'; @@ -43,7 +44,6 @@ import { DEFAULT_INFERENCE_PARAMS_FORM_VALUES } from '@studio/hooks/evaluation/u import { QUERY_PARAMETERS } from '@studio/routes/constants'; import { getEvaluationResultDetailsRoute } from '@studio/routes/utils'; import { buildModelPayload } from '@studio/util/evaluations'; -import { logger } from '@studio/util/logger'; import { Plus } from 'lucide-react'; import { type FC, useCallback, useEffect, useMemo, useState } from 'react'; import { diff --git a/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/components.tsx b/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/components.tsx index 5d5ab9db5d..b5ff007294 100644 --- a/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/components.tsx +++ b/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/components.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { ExpandableMessage } from '@nemo/common/src/components/ExpandableMessage'; import { KVPair } from '@nemo/common/src/components/KVPair'; import { parametersToString } from '@nemo/common/src/components/ModelDetailsTooltip/utils'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; @@ -29,7 +30,6 @@ import { Tooltip, } from '@nvidia/foundations-react-core'; import { CopyButton } from '@studio/components/CopyButton'; -import { ExpandableMessage } from '@studio/components/ExpandableMessage'; import { getWorkspaceCustomizationJobDetailsRoute } from '@studio/routes/utils'; import { formatFinetuningType } from '@studio/util/customizations'; import { MessagesSquare, Eye, EyeOff, Layers, AlignLeft, AppWindow } from 'lucide-react'; diff --git a/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/index.tsx b/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/index.tsx index 50328cb4fb..93f01ad042 100644 --- a/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/index.tsx +++ b/web/packages/studio/src/components/sidePanels/ModelPanels/ModelPanel/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { getModelsListModelsQueryKey, getProviderProxyGetQueryKey, @@ -19,7 +20,6 @@ import { SidePanel, Stack, } from '@nvidia/foundations-react-core'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { Empty } from '@studio/components/Empty'; import { Loading } from '@studio/components/Layouts/Loading'; import { ModelChat } from '@studio/components/ModelChat'; diff --git a/web/packages/studio/src/main.tsx b/web/packages/studio/src/main.tsx index a5cb3a91b2..fa4a7672f9 100644 --- a/web/packages/studio/src/main.tsx +++ b/web/packages/studio/src/main.tsx @@ -3,9 +3,9 @@ import '@studio/index.css'; +import { logger } from '@nemo/common/src/utils/logger'; import { App } from '@studio/App'; import { UI_THEME } from '@studio/util/localStorage'; -import { logger } from '@studio/util/logger'; import ReactDOM from 'react-dom/client'; // OpenTelemetry patches fetch/XHR globally, so this must settle before React diff --git a/web/packages/studio/src/plugins/PluginErrorBoundary.tsx b/web/packages/studio/src/plugins/PluginErrorBoundary.tsx index 6f078f06c6..18b60752d5 100644 --- a/web/packages/studio/src/plugins/PluginErrorBoundary.tsx +++ b/web/packages/studio/src/plugins/PluginErrorBoundary.tsx @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; +import { logger } from '@nemo/common/src/utils/logger'; import { Button, PageHeader, Panel, Stack } from '@nvidia/foundations-react-core'; -import { logger } from '@studio/util/logger'; import { Component, type ErrorInfo, type ReactNode } from 'react'; interface PluginErrorBoundaryProps { diff --git a/web/packages/studio/src/plugins/PluginRenderer.test.tsx b/web/packages/studio/src/plugins/PluginRenderer.test.tsx index 8b41a2b492..77d3b19a22 100644 --- a/web/packages/studio/src/plugins/PluginRenderer.test.tsx +++ b/web/packages/studio/src/plugins/PluginRenderer.test.tsx @@ -4,7 +4,12 @@ import { usePlugins, usePluginsLoaded } from '@studio/plugins/PluginContext'; import { PluginRenderer } from '@studio/plugins/PluginRenderer'; import type { LoadedPlugin, PluginRootProps } from '@studio/plugins/types'; -import { render, screen } from '@testing-library/react'; +import { BreadcrumbsProvider } from '@studio/providers/breadcrumbs/BreadcrumbsProvider'; +import { + useBreadcrumbs, + type BreadcrumbsItemProps, +} from '@studio/providers/breadcrumbs/useBreadcrumbs'; +import { act, render, screen } from '@testing-library/react'; import { useEffect } from 'react'; import { MemoryRouter, Route, Routes } from 'react-router'; @@ -52,11 +57,13 @@ vi.mock('@studio/hooks/useWorkspaceFromPath', () => ({ function renderPlugin(pluginName = 'test-plugin') { return render( - - - } /> - - + + + + } /> + + + ); } @@ -73,11 +80,16 @@ describe('PluginRenderer', () => { vi.mocked(usePlugins).mockReturnValue([]); render( - - - } /> - - + + + + } + /> + + + ); expect(screen.getByText(/loading/i)).toBeInTheDocument(); @@ -106,11 +118,16 @@ describe('PluginRenderer', () => { authState.accessToken = 'renewed-token'; rerender( - - - } /> - - + + + + } + /> + + + ); expect(mountSpy).toHaveBeenCalledTimes(1); @@ -142,4 +159,79 @@ describe('PluginRenderer', () => { consoleError.mockRestore(); }); + + it('writes a plugin breadcrumb trail into Studio and clears it on unmount', () => { + const seen: BreadcrumbsItemProps[][] = []; + function BreadcrumbSpy() { + seen.push(useBreadcrumbs().breadcrumbs); + return null; + } + vi.mocked(usePlugins).mockReturnValue([makePlugin('test-plugin')]); + + // The provider and spy outlive the plugin subtree, so the cleared trail is + // still observable after the plugin itself unmounts. + const tree = (mounted: boolean) => ( + + + {mounted ? ( + + + } + /> + + + ) : null} + + ); + const { rerender } = render(tree(true)); + + act(() => + capturedProps?.host.breadcrumbs.set([ + { label: 'Runs', href: '/workspaces/ws1/plugin/test-plugin/runs' }, + { label: 'Run 7' }, + ]) + ); + expect(seen.at(-1)).toEqual([ + { slotLabel: 'Runs', href: '/workspaces/ws1/plugin/test-plugin/runs' }, + { slotLabel: 'Run 7', href: undefined }, + ]); + + rerender(tree(false)); + // Studio clears the trail itself; a plugin that forgets cannot leave one behind. + expect(seen.at(-1)).toEqual([]); + }); + + it('clears the trail when the router swaps one plugin for another', () => { + const seen: BreadcrumbsItemProps[][] = []; + function BreadcrumbSpy() { + seen.push(useBreadcrumbs().breadcrumbs); + return null; + } + vi.mocked(usePlugins).mockReturnValue([makePlugin('plugin-a'), makePlugin('plugin-b')]); + + render( + + + + + } + /> + + + + ); + + act(() => capturedProps?.host.breadcrumbs.set([{ label: 'From plugin A' }])); + expect(seen.at(-1)).toEqual([{ slotLabel: 'From plugin A', href: undefined }]); + + // Navigate for real: MemoryRouter only reads initialEntries once, and the + // point is that PluginRenderer stays mounted while :pluginName changes, so + // only the effect's deps can force the reset. + act(() => capturedProps?.host.navigation.navigate('/workspaces/ws1/plugin/plugin-b/')); + expect(seen.at(-1)).toEqual([]); + }); }); diff --git a/web/packages/studio/src/plugins/PluginRenderer.tsx b/web/packages/studio/src/plugins/PluginRenderer.tsx index 8d23e9084c..10f888b511 100644 --- a/web/packages/studio/src/plugins/PluginRenderer.tsx +++ b/web/packages/studio/src/plugins/PluginRenderer.tsx @@ -2,13 +2,19 @@ // SPDX-License-Identifier: Apache-2.0 import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import * as platformSdk from '@nemo/sdk/generated/platform/api'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { usePlugins, usePluginsLoaded } from '@studio/plugins/PluginContext'; import { PluginErrorBoundary } from '@studio/plugins/PluginErrorBoundary'; -import type { PluginHost, PluginSdk, PluginTelemetry } from '@studio/plugins/types'; -import { logger } from '@studio/util/logger'; -import { useCallback, useMemo, useRef, type ReactElement } from 'react'; +import type { + PluginBreadcrumb, + PluginHost, + PluginSdk, + PluginTelemetry, +} from '@studio/plugins/types'; +import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; +import { useCallback, useEffect, useMemo, useRef, type ReactElement } from 'react'; import { useAuth } from 'react-oidc-context'; import { useNavigate, useParams } from 'react-router'; @@ -41,16 +47,28 @@ export const PluginRenderer = (): ReactElement => { accessTokenRef.current = accessToken; const getAccessToken = useCallback(() => accessTokenRef.current, []); + const { setBreadcrumbs } = useBreadcrumbs(); + const setPluginBreadcrumbs = useCallback( + (trail: PluginBreadcrumb[]) => + setBreadcrumbs(trail.map(({ label, href }) => ({ slotLabel: label, href }))), + [setBreadcrumbs] + ); + // Studio owns the cleanup so a plugin can't leave a stale trail behind. Keyed + // on pluginName too: the router reuses this component across plugins, and the + // outgoing plugin's trail would otherwise persist until the next one sets its own. + useEffect(() => () => setBreadcrumbs([]), [setBreadcrumbs, pluginName]); + const host = useMemo( () => ({ workspaceId: workspace, auth: { accessToken, getAccessToken }, sdk: STUDIO_SDK, navigation: { navigate: (to) => navigate(to), back: () => navigate(-1) }, - notifications: { notify: (message, type = 'info') => toast[type](message) }, + notifications: { notify: (message, type = 'info', options) => toast[type](message, options) }, telemetry: makeTelemetry(pluginName ?? 'unknown'), + breadcrumbs: { set: setPluginBreadcrumbs }, }), - [workspace, accessToken, getAccessToken, navigate, toast, pluginName] + [workspace, accessToken, getAccessToken, navigate, toast, pluginName, setPluginBreadcrumbs] ); if (!isLoaded) { diff --git a/web/packages/studio/src/plugins/types.ts b/web/packages/studio/src/plugins/types.ts index 9cc4014b26..c3e6f07cf9 100644 --- a/web/packages/studio/src/plugins/types.ts +++ b/web/packages/studio/src/plugins/types.ts @@ -20,9 +20,29 @@ export interface PluginNavigation { export type NotificationType = 'success' | 'error' | 'info' | 'warning'; +export interface NotificationOptions { + /** ms before auto-dismiss; `false` keeps the toast until dismissed. Defaults per type. */ + durationMs?: number | false; +} + /** Fire a toast into Studio's shared toaster; defaults to `info`. */ export interface PluginNotifications { - notify: (message: string, type?: NotificationType) => void; + notify: (message: string, type?: NotificationType, options?: NotificationOptions) => void; +} + +export interface PluginBreadcrumb { + label: string; + /** Absolute Studio path; omit for the trailing (current) crumb. */ + href?: string; +} + +/** + * Write into Studio's breadcrumb bar, which renders in GlobalNav — outside the + * plugin's own subtree, so a plugin cannot render it itself. Studio clears the + * trail when the plugin unmounts. + */ +export interface PluginBreadcrumbs { + set: (trail: PluginBreadcrumb[]) => void; } /** Structured logging to Studio's OTEL pipeline, auto-scoped to the plugin. */ @@ -45,6 +65,7 @@ export interface PluginHost { navigation: PluginNavigation; notifications: PluginNotifications; telemetry: PluginTelemetry; + breadcrumbs: PluginBreadcrumbs; } export interface PluginRootProps { diff --git a/web/packages/studio/src/plugins/utils.ts b/web/packages/studio/src/plugins/utils.ts index f6c08fcf2c..0d5c1463bd 100644 --- a/web/packages/studio/src/plugins/utils.ts +++ b/web/packages/studio/src/plugins/utils.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { PLATFORM_BASE_URL } from '@studio/constants/environment'; import { PLUGINS_MANIFEST_ENDPOINT } from '@studio/plugins/consts'; import { isTrustedBundleUrl } from '@studio/plugins/security'; @@ -10,7 +11,6 @@ import type { PluginModule, PluginQueryData, } from '@studio/plugins/types'; -import { logger } from '@studio/util/logger'; export function isValidPluginManifest(obj: unknown): obj is PluginManifest { if (typeof obj !== 'object' || obj === null) return false; diff --git a/web/packages/studio/src/routes/AnonymizerBuilderRoute/components/AnonymizerBuilderForm.tsx b/web/packages/studio/src/routes/AnonymizerBuilderRoute/components/AnonymizerBuilderForm.tsx index f670759ff2..ad611a9999 100644 --- a/web/packages/studio/src/routes/AnonymizerBuilderRoute/components/AnonymizerBuilderForm.tsx +++ b/web/packages/studio/src/routes/AnonymizerBuilderRoute/components/AnonymizerBuilderForm.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { useAnonymizerCreateRunJob, useAnonymizerListEntityLabels, @@ -15,7 +16,6 @@ import { SegmentedControl, Stack, } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { parseAnonymizerApiError } from '@studio/routes/AnonymizerBuilderRoute/apiErrors'; import { ColumnsSection } from '@studio/routes/AnonymizerBuilderRoute/components/ColumnsSection'; diff --git a/web/packages/studio/src/routes/AnonymizerBuilderRoute/index.tsx b/web/packages/studio/src/routes/AnonymizerBuilderRoute/index.tsx index dca9e3f27a..6e344912aa 100644 --- a/web/packages/studio/src/routes/AnonymizerBuilderRoute/index.tsx +++ b/web/packages/studio/src/routes/AnonymizerBuilderRoute/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ANONYMIZER_ENABLED } from '@studio/constants/environment'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { AnonymizerBuilderForm } from '@studio/routes/AnonymizerBuilderRoute/components/AnonymizerBuilderForm'; diff --git a/web/packages/studio/src/routes/AnonymizerJobDetailRoute/index.tsx b/web/packages/studio/src/routes/AnonymizerJobDetailRoute/index.tsx index 3038d416c9..6a0bc9a874 100644 --- a/web/packages/studio/src/routes/AnonymizerJobDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/AnonymizerJobDetailRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { LogViewer } from '@nemo/common/src/components/LogViewer'; import { useAnonymizerGetRunJob, @@ -8,7 +9,6 @@ import { useAnonymizerListRunJobResults, } from '@nemo/sdk/generated/anonymizer/api'; import { Banner, Grid, PageHeader, Panel, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { AnonymizerJobActionsMenu } from '@studio/components/AnonymizerJobActionsMenu'; import { ANONYMIZER_ENABLED } from '@studio/constants/environment'; import { ROUTE_PARAMS } from '@studio/constants/routes'; diff --git a/web/packages/studio/src/routes/AnonymizerListRoute/index.tsx b/web/packages/studio/src/routes/AnonymizerListRoute/index.tsx index 860a45daa7..af25f7da34 100644 --- a/web/packages/studio/src/routes/AnonymizerListRoute/index.tsx +++ b/web/packages/studio/src/routes/AnonymizerListRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { AnonymizerJobsDataView } from '@studio/components/dataViews/AnonymizerJobsDataView'; import { ANONYMIZER_ENABLED } from '@studio/constants/environment'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/AuthSuccessRoute/index.tsx b/web/packages/studio/src/routes/AuthSuccessRoute/index.tsx index 3862c26d82..5356be4001 100644 --- a/web/packages/studio/src/routes/AuthSuccessRoute/index.tsx +++ b/web/packages/studio/src/routes/AuthSuccessRoute/index.tsx @@ -2,10 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { entitiesCreateWorkspace, entitiesGetWorkspace } from '@nemo/sdk/generated/platform/api'; import { Loading } from '@studio/components/Layouts/Loading'; import { useAuthProfile } from '@studio/providers/auth'; -import { logger } from '@studio/util/logger'; import { isAxiosError } from 'axios'; import { FC, useCallback, useEffect } from 'react'; import { useNavigate } from 'react-router'; diff --git a/web/packages/studio/src/routes/CustomizationJobDetailsRoute/DetailActions.tsx b/web/packages/studio/src/routes/CustomizationJobDetailsRoute/DetailActions.tsx index 3674ecea8c..5ad8f3a80b 100644 --- a/web/packages/studio/src/routes/CustomizationJobDetailsRoute/DetailActions.tsx +++ b/web/packages/studio/src/routes/CustomizationJobDetailsRoute/DetailActions.tsx @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { QuickActionsMenuRoot } from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; import { CJobCancellableStatuses, CJobLaunchableStatuses } from '@nemo/common/src/constants/query'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { @@ -10,8 +12,6 @@ import { import { getJobsGetJobQueryKey } from '@nemo/sdk/generated/platform/api'; import { PlatformJobStatus, type PlatformJobResponse } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { QuickActionsMenuRoot } from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getNewCustomizationJobRoute, getNewEvaluationMetricRoute } from '@studio/routes/utils'; import { CustomizationBackend, type CustomizationJob } from '@studio/util/customizationBackend'; diff --git a/web/packages/studio/src/routes/CustomizationJobDetailsRoute/index.tsx b/web/packages/studio/src/routes/CustomizationJobDetailsRoute/index.tsx index d32b684e2a..dd1ef7be58 100644 --- a/web/packages/studio/src/routes/CustomizationJobDetailsRoute/index.tsx +++ b/web/packages/studio/src/routes/CustomizationJobDetailsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useModelsGetModel } from '@nemo/sdk/generated/platform/api'; import { PageHeader, @@ -10,7 +11,6 @@ import { TabsRoot, TabsTrigger, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { CustomizationDetailsPanel } from '@studio/components/CustomizationDetailsPanel'; import { CustomizationFilesetDetailsPanel } from '@studio/components/CustomizationFilesetDetailsPanel'; import { Loading } from '@studio/components/Layouts/Loading'; diff --git a/web/packages/studio/src/routes/CustomizationJobListRoute/index.tsx b/web/packages/studio/src/routes/CustomizationJobListRoute/index.tsx index 8cf4229040..07bf126beb 100644 --- a/web/packages/studio/src/routes/CustomizationJobListRoute/index.tsx +++ b/web/packages/studio/src/routes/CustomizationJobListRoute/index.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import type { Adapter, ModelEntity } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { CustomModelsDataView } from '@studio/components/dataViews/CustomModelsDataView'; import { CustomizeModelButton } from '@studio/components/dataViews/CustomModelsDataView/CustomizeModelButton'; import { ModelPanel, ModelPanelTab } from '@studio/components/sidePanels/ModelPanels/ModelPanel'; diff --git a/web/packages/studio/src/routes/DashboardLandingRoute/index.tsx b/web/packages/studio/src/routes/DashboardLandingRoute/index.tsx index 3496e00055..9418c9c2fa 100644 --- a/web/packages/studio/src/routes/DashboardLandingRoute/index.tsx +++ b/web/packages/studio/src/routes/DashboardLandingRoute/index.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { GradientBackground } from '@nemo/common/src/components/GradientBackground'; import { Button, Flex, Text, TextArea, Tooltip } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { writeStoredActiveSessionId } from '@studio/routes/agents/CopilotChatRoute/activeSessionStorage'; diff --git a/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx b/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx index 577ec8bc27..f779382f91 100644 --- a/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx +++ b/web/packages/studio/src/routes/DataDesignerJobBuildRoute/index.tsx @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { DEFAULT_LARGE_PAGE_SIZE } from '@nemo/common/src/constants/api'; import { useDataDesignerCreateJob } from '@nemo/sdk/generated/data-designer/api'; import { useModelsListProviders } from '@nemo/sdk/generated/platform/api'; import { Flex, Stack } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { findTemplate } from '@studio/components/CreateFilesetStart/templates'; import { usePreview } from '@studio/components/NewDataDesignerJobForm/usePreview'; import { getCloneJobRequestFromState } from '@studio/components/NewDataDesignerJobForm/utils'; diff --git a/web/packages/studio/src/routes/DataDesignerJobDetailsRoute/index.tsx b/web/packages/studio/src/routes/DataDesignerJobDetailsRoute/index.tsx index 0a87aa5df0..1358c6a34a 100644 --- a/web/packages/studio/src/routes/DataDesignerJobDetailsRoute/index.tsx +++ b/web/packages/studio/src/routes/DataDesignerJobDetailsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import { PlatformJobTerminalStatuses } from '@nemo/common/src/constants/query'; @@ -15,7 +16,6 @@ import { TabsTrigger, Text, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { DataDesignerJobActionsMenu } from '@studio/components/DataDesignerJobActionsMenu'; import { CreateFileSplitsModal } from '@studio/components/FilesTable/CreateFileSplitsModal'; import { Loading } from '@studio/components/Layouts/Loading'; diff --git a/web/packages/studio/src/routes/DataDesignerJobListRoute/index.tsx b/web/packages/studio/src/routes/DataDesignerJobListRoute/index.tsx index a89a363ea3..c88e8fd5f9 100644 --- a/web/packages/studio/src/routes/DataDesignerJobListRoute/index.tsx +++ b/web/packages/studio/src/routes/DataDesignerJobListRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { DataDesignerJobsDataView } from '@studio/components/dataViews/DataDesignerJobsDataView'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/DeploymentsListRoute/CreateDeploymentSidePanel/useCreateDeploymentBySource.ts b/web/packages/studio/src/routes/DeploymentsListRoute/CreateDeploymentSidePanel/useCreateDeploymentBySource.ts index c79c944394..43c197e8a6 100644 --- a/web/packages/studio/src/routes/DeploymentsListRoute/CreateDeploymentSidePanel/useCreateDeploymentBySource.ts +++ b/web/packages/studio/src/routes/DeploymentsListRoute/CreateDeploymentSidePanel/useCreateDeploymentBySource.ts @@ -10,6 +10,7 @@ * its affiliates is strictly prohibited. */ +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { @@ -27,7 +28,6 @@ import { type CreateFilesetRequest, type CreateModelDeploymentConfigRequest, } from '@nemo/sdk/generated/platform/schema'; -import { getErrorMessage } from '@studio/api/common/utils'; import { additionalEnvsFormToApi, configNameFromWizardBaseName, diff --git a/web/packages/studio/src/routes/DeploymentsListRoute/index.tsx b/web/packages/studio/src/routes/DeploymentsListRoute/index.tsx index 52b10fd881..bf2247d850 100644 --- a/web/packages/studio/src/routes/DeploymentsListRoute/index.tsx +++ b/web/packages/studio/src/routes/DeploymentsListRoute/index.tsx @@ -10,12 +10,12 @@ * its affiliates is strictly prohibited. */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { resourceRefSchema, type ResourceRef } from '@nemo/common/src/types'; import { ModelDeployment } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { DeploymentsDataView } from '@studio/components/dataViews/DeploymentsDataView'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { DocumentationButton } from '@studio/components/DocumentationButton'; import { LINK_DOCS_DEPLOYMENTS } from '@studio/constants/links'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/DeploymentsListRoute/useDeleteDeploymentAndConfig.ts b/web/packages/studio/src/routes/DeploymentsListRoute/useDeleteDeploymentAndConfig.ts index dca77329a2..7f800913bd 100644 --- a/web/packages/studio/src/routes/DeploymentsListRoute/useDeleteDeploymentAndConfig.ts +++ b/web/packages/studio/src/routes/DeploymentsListRoute/useDeleteDeploymentAndConfig.ts @@ -12,6 +12,7 @@ import { getPartsFromReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { logger } from '@nemo/common/src/utils/logger'; import { filesDeleteFileset, getFilesListFilesetsQueryKey, @@ -37,7 +38,6 @@ import { HUGGING_FACE_DEPLOYMENT_SOURCE_VALUE, huggingFaceSourceFilesetName, } from '@studio/routes/DeploymentsListRoute/huggingFaceDeploymentArtifacts'; -import { logger } from '@studio/util/logger'; import { type QueryClient, useMutation, useQueryClient } from '@tanstack/react-query'; import { useCallback } from 'react'; diff --git a/web/packages/studio/src/routes/EvaluationDetailRoute/index.tsx b/web/packages/studio/src/routes/EvaluationDetailRoute/index.tsx index db6535d0fe..75c0c90d61 100644 --- a/web/packages/studio/src/routes/EvaluationDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/EvaluationDetailRoute/index.tsx @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useGetEvaluation, useGetExperiment } from '@nemo/sdk/generated/platform/api'; import { Badge, Card, Flex, PageHeader, Stack, Text } from '@nvidia/foundations-react-core'; import { useOptimizerGetInsight } from '@studio/api/optimizer'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { EvaluationSessionsDataView } from '@studio/components/dataViews/EvaluationSessionsDataView'; import { OriginatingInsightLink } from '@studio/components/OriginatingInsightLink'; import { OPTIMIZER_ENABLED } from '@studio/constants/environment'; diff --git a/web/packages/studio/src/routes/EvaluationSessionDetailRoute/SessionCompareColumn.tsx b/web/packages/studio/src/routes/EvaluationSessionDetailRoute/SessionCompareColumn.tsx index 43abdce5eb..eb70792310 100644 --- a/web/packages/studio/src/routes/EvaluationSessionDetailRoute/SessionCompareColumn.tsx +++ b/web/packages/studio/src/routes/EvaluationSessionDetailRoute/SessionCompareColumn.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { KVPair } from '@nemo/common/src/components/KVPair'; import { useListAnnotations } from '@nemo/sdk/generated/platform/api'; @@ -10,7 +11,6 @@ import { type FeedbackAnnotationInputValue, } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Spinner, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { SpanListView } from '@studio/components/IntakeDetail/TraceSpanListView'; import { type NoteRequest } from '@studio/components/IntakeDetail/traceSpanShared'; import { useSessionTrajectories } from '@studio/components/IntakeDetail/useSessionTrajectories'; diff --git a/web/packages/studio/src/routes/EvaluationSessionDetailRoute/TestCaseCompare.tsx b/web/packages/studio/src/routes/EvaluationSessionDetailRoute/TestCaseCompare.tsx index 9d5effdf91..3d119707ca 100644 --- a/web/packages/studio/src/routes/EvaluationSessionDetailRoute/TestCaseCompare.tsx +++ b/web/packages/studio/src/routes/EvaluationSessionDetailRoute/TestCaseCompare.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import type { EvaluationSessionResponse } from '@nemo/sdk/generated/platform/schema'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { Loading } from '@studio/components/Layouts/Loading'; import { type BreadcrumbsItemProps, diff --git a/web/packages/studio/src/routes/ExperimentDetailRoute/index.tsx b/web/packages/studio/src/routes/ExperimentDetailRoute/index.tsx index d4ebddde78..bd097eba36 100644 --- a/web/packages/studio/src/routes/ExperimentDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/ExperimentDetailRoute/index.tsx @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { useGetExperiment } from '@nemo/sdk/generated/platform/api'; import { Button, Card, Flex, PageHeader, Stack, Text } from '@nvidia/foundations-react-core'; import { useOptimizerGetInsight } from '@studio/api/optimizer'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { ExperimentDataView } from '@studio/components/dataViews/ExperimentDataView'; import { ExperimentEditModal } from '@studio/components/ExperimentEditModal'; import { OriginatingInsightLink } from '@studio/components/OriginatingInsightLink'; diff --git a/web/packages/studio/src/routes/ExperimentRoute/index.tsx b/web/packages/studio/src/routes/ExperimentRoute/index.tsx index e43c7820b1..b1402b5c9d 100644 --- a/web/packages/studio/src/routes/ExperimentRoute/index.tsx +++ b/web/packages/studio/src/routes/ExperimentRoute/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import type { WithFilterOperators } from '@nemo/common/src/api/filterOperators'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { dateTimeFilter, type DatetimeFilterValue, @@ -29,7 +30,6 @@ import { StatusMessage, Text, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { ExperimentCreateModal } from '@studio/components/ExperimentCreateModal'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { Loading } from '@studio/components/Layouts/Loading'; diff --git a/web/packages/studio/src/routes/FilesetDetailRoute/index.tsx b/web/packages/studio/src/routes/FilesetDetailRoute/index.tsx index bfa7c41248..dcc9cbc3c4 100644 --- a/web/packages/studio/src/routes/FilesetDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/FilesetDetailRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { creatorToIcon } from '@nemo/common/src/constants/modelMetadata'; import { useQueryParams } from '@nemo/common/src/hooks/useQueryParams'; import { getEntityReference } from '@nemo/common/src/namedEntity'; @@ -19,7 +20,6 @@ import { TabsTrigger, Text, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { FilesetActionMenu } from '@studio/components/FilesetActionMenu'; import { Loading } from '@studio/components/Layouts/Loading'; import { ROUTE_PARAMS } from '@studio/constants/routes'; diff --git a/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.test.tsx b/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.test.tsx index ffeddcb698..85b2e64579 100644 --- a/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.test.tsx +++ b/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.test.tsx @@ -32,7 +32,7 @@ vi.mock('@studio/components/DatasetCreateModal', () => ({ }), })); -vi.mock('@studio/components/DeleteConfirmationModal', () => ({ +vi.mock('@nemo/common/src/components/DeleteConfirmationModal', () => ({ DeleteConfirmationModal: vi.fn(({ open, onClose, onDelete, title }) => { if (!open) return null; return ( diff --git a/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.tsx b/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.tsx index e2899fc286..292b60468c 100644 --- a/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.tsx +++ b/web/packages/studio/src/routes/FilesetListRoute/ActionMenu/index.tsx @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { getEntityReference } from '@nemo/common/src/namedEntity'; +import { logger } from '@nemo/common/src/utils/logger'; import { useFilesDeleteFileset } from '@nemo/sdk/generated/platform/api'; import { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { @@ -14,8 +16,6 @@ import { import { invalidateDatasetCaches } from '@studio/api/datasets/invalidateDatasetCaches'; import { DatasetCreateModal } from '@studio/components/DatasetCreateModal'; import { DatasetCreateModalMode } from '@studio/components/DatasetCreateModal/constants'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; -import { logger } from '@studio/util/logger'; import { EllipsisVertical } from 'lucide-react'; import { FC, useState } from 'react'; diff --git a/web/packages/studio/src/routes/FilesetListRoute/index.tsx b/web/packages/studio/src/routes/FilesetListRoute/index.tsx index f0253f6df5..8b22063f8d 100644 --- a/web/packages/studio/src/routes/FilesetListRoute/index.tsx +++ b/web/packages/studio/src/routes/FilesetListRoute/index.tsx @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { getEntityReference } from '@nemo/common/src/namedEntity'; import { FilesetOutput } from '@nemo/sdk/generated/platform/schema'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { DatasetsTable } from '@studio/components/DatasetsTable'; import { NewDatasetButton } from '@studio/components/NewDatasetButton'; import { NewModelFilesetButton } from '@studio/components/NewModelFilesetButton'; diff --git a/web/packages/studio/src/routes/FilesetNewRoute/helpers.ts b/web/packages/studio/src/routes/FilesetNewRoute/helpers.ts index 751bccfa58..7a65e3ea6d 100644 --- a/web/packages/studio/src/routes/FilesetNewRoute/helpers.ts +++ b/web/packages/studio/src/routes/FilesetNewRoute/helpers.ts @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { getErrorMessage as getApiErrorMessage } from '@studio/api/common/utils'; +import { getErrorMessage as getApiErrorMessage } from '@nemo/common/src/api/common/utils'; import { isHuggingFaceUrl, isNgcUrl } from '@studio/util/storageConfigFromUrl'; export function getSampleDatasetName(workspace: string, sampleId: string): string { diff --git a/web/packages/studio/src/routes/FilesetNewRoute/index.tsx b/web/packages/studio/src/routes/FilesetNewRoute/index.tsx index 3822a6586d..64737e2d2b 100644 --- a/web/packages/studio/src/routes/FilesetNewRoute/index.tsx +++ b/web/packages/studio/src/routes/FilesetNewRoute/index.tsx @@ -6,6 +6,7 @@ import { checkDatasetQuality, type DatasetQualityReport, } from '@nemo/common/src/utils/datasetQuality'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { FilesetPurpose } from '@nemo/sdk/generated/platform/schema'; import { Button, SegmentedControl, SidePanel, Stack } from '@nvidia/foundations-react-core'; import { useSampleDatasetFiles } from '@studio/api/datasets/useSampleDatasetFiles'; @@ -23,7 +24,6 @@ import { import { useCreateFileset } from '@studio/routes/FilesetNewRoute/useCreateFileset'; import { CreateSecretModal } from '@studio/routes/SecretsListRoute/CreateSecretModal'; import { getWorkspaceFilesetsRoute } from '@studio/routes/utils'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { isHuggingFaceUrl, isNgcUrl } from '@studio/util/storageConfigFromUrl'; import { QueryObserverResult } from '@tanstack/react-query'; import { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react'; diff --git a/web/packages/studio/src/routes/FilesetNewRoute/useCreateFileset.ts b/web/packages/studio/src/routes/FilesetNewRoute/useCreateFileset.ts index 3f863060b1..fa073fa2b2 100644 --- a/web/packages/studio/src/routes/FilesetNewRoute/useCreateFileset.ts +++ b/web/packages/studio/src/routes/FilesetNewRoute/useCreateFileset.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage as getApiErrorMessage } from '@nemo/common/src/api/common/utils'; import { getEntityReference } from '@nemo/common/src/namedEntity'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { @@ -15,7 +16,6 @@ import { FilesetPurpose, CreateFilesetRequest, } from '@nemo/sdk/generated/platform/schema'; -import { getErrorMessage as getApiErrorMessage } from '@studio/api/common/utils'; import { FILESET_DETAILS_ENABLED } from '@studio/constants/environment'; import { DATASET_TYPE_SAMPLE } from '@studio/routes/FilesetNewRoute/constants'; import { diff --git a/web/packages/studio/src/routes/InferenceProvidersListRoute/CreateInferenceProviderSidePanel/index.tsx b/web/packages/studio/src/routes/InferenceProvidersListRoute/CreateInferenceProviderSidePanel/index.tsx index 1580eff242..ae8d405a2a 100644 --- a/web/packages/studio/src/routes/InferenceProvidersListRoute/CreateInferenceProviderSidePanel/index.tsx +++ b/web/packages/studio/src/routes/InferenceProvidersListRoute/CreateInferenceProviderSidePanel/index.tsx @@ -11,6 +11,7 @@ */ import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; @@ -21,7 +22,6 @@ import { useModelsListProviders, } from '@nemo/sdk/generated/platform/api'; import { Button, Flex, FormField, SidePanel, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { InferenceModelProviderSelect } from '@studio/routes/InferenceProvidersListRoute/CreateInferenceProviderSidePanel/InferenceModelProviderSelect'; import { PRESET_CREDENTIALS, diff --git a/web/packages/studio/src/routes/InferenceProvidersListRoute/index.tsx b/web/packages/studio/src/routes/InferenceProvidersListRoute/index.tsx index 3b9f5bc06d..f047d33e80 100644 --- a/web/packages/studio/src/routes/InferenceProvidersListRoute/index.tsx +++ b/web/packages/studio/src/routes/InferenceProvidersListRoute/index.tsx @@ -10,8 +10,8 @@ * its affiliates is strictly prohibited. */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { InferenceProvidersDataView } from '@studio/components/dataViews/InferenceProvidersDataView'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/IntakeLayout/index.tsx b/web/packages/studio/src/routes/IntakeLayout/index.tsx index 86c4847820..73a2ba6eaf 100644 --- a/web/packages/studio/src/routes/IntakeLayout/index.tsx +++ b/web/packages/studio/src/routes/IntakeLayout/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack, Tabs } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { Loading } from '@studio/components/Layouts/Loading'; import { ROUTES } from '@studio/constants/routes'; diff --git a/web/packages/studio/src/routes/JobDetailRoute/index.tsx b/web/packages/studio/src/routes/JobDetailRoute/index.tsx index 2e50f2a50a..4461e70768 100644 --- a/web/packages/studio/src/routes/JobDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/JobDetailRoute/index.tsx @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +import { CancelJobButton } from '@nemo/common/src/components/CancelJobButton'; import { KVPair } from '@nemo/common/src/components/KVPair'; import { LogViewer } from '@nemo/common/src/components/LogViewer'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; @@ -9,8 +11,6 @@ import { useJobLogs } from '@nemo/common/src/hooks/useJobLogs'; import { getJobRefetchInterval } from '@nemo/common/src/utils/query'; import { useJobsGetJob, useJobsListJobResults } from '@nemo/sdk/generated/platform/api'; import { Flex, Grid, PageHeader, Panel, Spinner, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; -import { CancelJobButton } from '@studio/components/CancelJobButton'; import { ROUTE_PARAMS } from '@studio/constants/routes'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; @@ -62,7 +62,7 @@ export const JobDetailRoute: FC = () => { slotHeading={jobName} slotActions={ - + } /> diff --git a/web/packages/studio/src/routes/JobsRoute/index.tsx b/web/packages/studio/src/routes/JobsRoute/index.tsx index c1f4cd82e4..8dab85a09a 100644 --- a/web/packages/studio/src/routes/JobsRoute/index.tsx +++ b/web/packages/studio/src/routes/JobsRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { JobsDataView } from '@studio/components/dataViews/JobsDataView'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { FC } from 'react'; diff --git a/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx b/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx index fa993f6d3b..be6a19acb7 100644 --- a/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx +++ b/web/packages/studio/src/routes/LegacyNewDataDesignerJobRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { NewDataDesignerJobForm } from '@studio/components/NewDataDesignerJobForm'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx b/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx index bc9609f8a2..2039c38960 100644 --- a/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx +++ b/web/packages/studio/src/routes/NewDataDesignerJobRoute/index.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { CreateFilesetStart } from '@studio/components/CreateFilesetStart'; import type { StartSelection } from '@studio/components/CreateFilesetStart/types'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/PromptTuningFormRoute/index.tsx b/web/packages/studio/src/routes/PromptTuningFormRoute/index.tsx index 827fe61eeb..dd51ede5b1 100644 --- a/web/packages/studio/src/routes/PromptTuningFormRoute/index.tsx +++ b/web/packages/studio/src/routes/PromptTuningFormRoute/index.tsx @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ChatEmptyState } from '@nemo/common/src/components/Chat/ChatEmptyState'; import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledTextArea'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; @@ -25,9 +27,7 @@ import { Stack, Text, } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { queryClient } from '@studio/api/queryClient'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { Loading } from '@studio/components/Layouts/Loading'; import { ModelChat } from '@studio/components/ModelChat'; import { InContextLearningSection } from '@studio/components/PromptTuningForm/InContextLearningSection'; diff --git a/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/components/ProgressSection.test.tsx b/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/components/ProgressSection.test.tsx index e3e38b7b1d..5d4d4d2d1b 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/components/ProgressSection.test.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/components/ProgressSection.test.tsx @@ -8,17 +8,6 @@ import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import { render, screen, waitFor, within } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; -// Mock the toast hook -const mockToastSuccess = vi.fn(); -vi.mock('@nemo/common/src/providers/toast/useToast', () => ({ - useToast: () => ({ - success: mockToastSuccess, - error: vi.fn(), - info: vi.fn(), - warning: vi.fn(), - }), -})); - vi.mock('lucide-react', async () => { return (await import('@nemo/testing/mocks/lucide')).mockLucideReact(await import('react')); }); diff --git a/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/index.tsx b/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/index.tsx index af185d47ae..58c7a922c2 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/index.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerJobDetailsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useJobLogs } from '@nemo/common/src/hooks/useJobLogs'; import { PlatformJobStatus } from '@nemo/sdk/generated/platform/schema'; import { @@ -8,7 +9,6 @@ import { useSafeSynthesizerGetJobSuspense as useGetJobV1beta1SafeSynthesizerJobsJobIdGetSuspense, } from '@nemo/sdk/generated/safe-synthesizer/api'; import { Grid, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { SafeSynthesizerNavigation } from '@studio/components/SafeSynthesizerNavigation'; import { SAFE_SYNTHESIZER_ENABLED } from '@studio/constants/environment'; import { ROUTE_PARAMS } from '@studio/constants/routes'; diff --git a/web/packages/studio/src/routes/SafeSynthesizerJobReportRoute/index.tsx b/web/packages/studio/src/routes/SafeSynthesizerJobReportRoute/index.tsx index 11284792f6..a90cb490cd 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerJobReportRoute/index.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerJobReportRoute/index.tsx @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useSafeSynthesizerDownloadJobResultSummary as useDownloadJobResultSummaryV1beta1SafeSynthesizerJobsJobIdResultsSummaryDownloadGet, useSafeSynthesizerGetJobSuspense as useGetJobV1beta1SafeSynthesizerJobsJobIdGetSuspense, } from '@nemo/sdk/generated/safe-synthesizer/api'; import { Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { SafeSynthesizerNavigation } from '@studio/components/SafeSynthesizerNavigation'; import { SAFE_SYNTHESIZER_ENABLED } from '@studio/constants/environment'; import { ROUTE_PARAMS } from '@studio/constants/routes'; diff --git a/web/packages/studio/src/routes/SafeSynthesizerListRoute/index.tsx b/web/packages/studio/src/routes/SafeSynthesizerListRoute/index.tsx index 3e14ffe8dd..b97747d5e5 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerListRoute/index.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerListRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { SafeSynthesizerJobsDataView } from '@studio/components/dataViews/SafeSynthesizerJobsDataView'; import { SAFE_SYNTHESIZER_ENABLED } from '@studio/constants/environment'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.test.tsx b/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.test.tsx index 160cc32092..b41a42d321 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.test.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.test.tsx @@ -127,7 +127,7 @@ describe('SafeSynthesizerNewRoute', () => { SAFE_SYNTHESIZER_ENABLED: true, OTEL_SERVICE_NAME: 'test-service', })); - vi.doMock('@studio/util/logger', () => ({ + vi.doMock('@nemo/common/src/utils/logger', () => ({ logger: { debug: vi.fn(), error: vi.fn(), info: vi.fn(), warn: vi.fn() }, })); @@ -141,7 +141,7 @@ describe('SafeSynthesizerNewRoute', () => { SAFE_SYNTHESIZER_ENABLED: false, OTEL_SERVICE_NAME: 'test-service', })); - vi.doMock('@studio/util/logger', () => ({ + vi.doMock('@nemo/common/src/utils/logger', () => ({ logger: { debug: vi.fn(), error: vi.fn(), info: vi.fn(), warn: vi.fn() }, })); @@ -795,7 +795,7 @@ describe('SafeSynthesizerNewRoute', () => { it('should have form validation error handling configured', async () => { // Remove any lingering vi.doMock for the logger (left by Feature flag tests) so // the component gets the real logger and we can assert via console.error. - vi.doUnmock('@studio/util/logger'); + vi.doUnmock('@nemo/common/src/utils/logger'); vi.doMock('@studio/constants/environment', () => ({ SAFE_SYNTHESIZER_ENABLED: true, OTEL_SERVICE_NAME: 'test-service', diff --git a/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.tsx b/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.tsx index 6de339e797..3aec570dc4 100644 --- a/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.tsx +++ b/web/packages/studio/src/routes/SafeSynthesizerNewRoute/index.tsx @@ -2,9 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { logger } from '@nemo/common/src/utils/logger'; import { useSafeSynthesizerCreateJob } from '@nemo/sdk/generated/safe-synthesizer/api'; import { Banner, Button, Divider, Flex, Panel, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { SAFE_SYNTHESIZER_ENABLED } from '@studio/constants/environment'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; @@ -18,7 +19,6 @@ import { getSafeSynthesizerFormDefaults, } from '@studio/routes/SafeSynthesizerNewRoute/schema'; import { getSafeSynthesizerJobRoute, getSafeSynthesizerRoute } from '@studio/routes/utils'; -import { logger } from '@studio/util/logger'; import { FC, useState } from 'react'; import { FormProvider, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router'; diff --git a/web/packages/studio/src/routes/SecretsListRoute/CreateSecretModal/index.tsx b/web/packages/studio/src/routes/SecretsListRoute/CreateSecretModal/index.tsx index dc3f00254e..f53d4135e2 100644 --- a/web/packages/studio/src/routes/SecretsListRoute/CreateSecretModal/index.tsx +++ b/web/packages/studio/src/routes/SecretsListRoute/CreateSecretModal/index.tsx @@ -11,6 +11,7 @@ */ import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledTextArea'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; @@ -21,7 +22,6 @@ import { useSecretsCreateSecret, } from '@nemo/sdk/generated/platform/api'; import { Stack } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useQueryClient } from '@tanstack/react-query'; import { FC } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; diff --git a/web/packages/studio/src/routes/SecretsListRoute/EditSecretModal/index.tsx b/web/packages/studio/src/routes/SecretsListRoute/EditSecretModal/index.tsx index f173710532..6671c6f2f0 100644 --- a/web/packages/studio/src/routes/SecretsListRoute/EditSecretModal/index.tsx +++ b/web/packages/studio/src/routes/SecretsListRoute/EditSecretModal/index.tsx @@ -11,6 +11,7 @@ */ import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledTextArea'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; @@ -21,7 +22,6 @@ import { } from '@nemo/sdk/generated/platform/api'; import { PlatformSecretResponse } from '@nemo/sdk/generated/platform/schema'; import { FormField, Stack, Text, TextInput } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useQueryClient } from '@tanstack/react-query'; import { FC } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; diff --git a/web/packages/studio/src/routes/SecretsListRoute/index.tsx b/web/packages/studio/src/routes/SecretsListRoute/index.tsx index bc2fdcbaeb..de9cddc8f0 100644 --- a/web/packages/studio/src/routes/SecretsListRoute/index.tsx +++ b/web/packages/studio/src/routes/SecretsListRoute/index.tsx @@ -10,8 +10,8 @@ * its affiliates is strictly prohibited. */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { SecretsDataView } from '@studio/components/dataViews/SecretsDataView'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/VirtualModelsListRoute/index.tsx b/web/packages/studio/src/routes/VirtualModelsListRoute/index.tsx index ad5c9bcf2b..b2f8c361b5 100644 --- a/web/packages/studio/src/routes/VirtualModelsListRoute/index.tsx +++ b/web/packages/studio/src/routes/VirtualModelsListRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { VirtualModelsDataView } from '@studio/components/dataViews/VirtualModelsDataView'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/WorkspaceBaseModelsRoute/index.tsx b/web/packages/studio/src/routes/WorkspaceBaseModelsRoute/index.tsx index 89b117628b..9dd5c34533 100644 --- a/web/packages/studio/src/routes/WorkspaceBaseModelsRoute/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceBaseModelsRoute/index.tsx @@ -15,6 +15,7 @@ import { type ModelEntityFilterInput, } from '@nemo/common/src/api/entity-store/useBaseModels'; import { usePromptTunableBaseModelIds } from '@nemo/common/src/api/entity-store/usePromptTunableBaseModelIds'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { dateTimeFilter } from '@nemo/common/src/components/DataView/dateTimeFilter'; import * as DataView from '@nemo/common/src/components/DataView/internal'; import { StudioDataView } from '@nemo/common/src/components/DataView/StudioDataView'; @@ -35,7 +36,6 @@ import { Text, Tooltip, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { BaseModelCard } from '@studio/components/BaseModelCard'; import { CustomizeModelButton } from '@studio/components/dataViews/CustomModelsDataView/CustomizeModelButton'; import { ModelPanel, ModelPanelTab } from '@studio/components/sidePanels/ModelPanels/ModelPanel'; diff --git a/web/packages/studio/src/routes/WorkspaceDashboardRoute/index.tsx b/web/packages/studio/src/routes/WorkspaceDashboardRoute/index.tsx index be30020efe..b48a500f0b 100644 --- a/web/packages/studio/src/routes/WorkspaceDashboardRoute/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceDashboardRoute/index.tsx @@ -1,12 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { GradientBackground } from '@nemo/common/src/components/GradientBackground'; import ModelEvaluationIcon from '@nemo/common/src/svgs/model_evaluation.svg?react'; import ModelPromptTuningIcon from '@nemo/common/src/svgs/model_prompt_tuning.svg?react'; import SafeSynthesizerLogo from '@nemo/common/src/svgs/safe_synthesizer_logo.svg?react'; import { Grid, PageHeader, Stack, Text } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { CUSTOMIZER_ENABLED, EVALUATOR_ENABLED, diff --git a/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.test.tsx b/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.test.tsx index 7db8d2c1a2..8f77fffe91 100644 --- a/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.test.tsx +++ b/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.test.tsx @@ -1,9 +1,12 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { PluginContext } from '@studio/plugins/PluginContext'; +import type { LoadedPlugin, PluginNavGroup } from '@studio/plugins/types'; import { WorkspaceSideNav } from '@studio/routes/WorkspaceLayout/WorkspaceSideNav'; import { renderRoute, screen } from '@studio/tests/util/render'; import userEvent from '@testing-library/user-event'; +import type { ReactElement } from 'react'; vi.hoisted(() => { vi.stubEnv('VITE_FF_OPTIMIZER_ENABLED', 'false'); @@ -24,6 +27,32 @@ const renderSideNav = (history = '/workspaces/test-workspace/dashboard') => ], }); +const makePlugin = (name: string, groups: PluginNavGroup[]): LoadedPlugin => ({ + name, + Root: () => null, + navItems: () => groups, +}); + +const renderWithPlugins = (plugins: LoadedPlugin[]) => { + const element: ReactElement = ( + p.name)), + isLoaded: true, + isError: false, + }} + > + + + ); + + return renderRoute(element, { + history: '/workspaces/test-workspace/dashboard', + routes: [{ path: '/workspaces/:workspace/*', element }], + }); +}; + describe('WorkspaceSideNav', () => { it('links to the traces view by default', () => { renderSideNav(); @@ -125,4 +154,100 @@ describe('WorkspaceSideNav', () => { await user.click(screen.getByRole('link', { name: 'Agents' })); expect(disclosure('Agents')).toHaveAttribute('aria-expanded', 'true'); }); + + it('folds a plugin group into the core group of the same name', () => { + renderWithPlugins([ + makePlugin('red-team', [ + { + group: 'Governance', + items: [ + { + id: 'red-team', + iconName: 'shield', + label: 'Red Team', + href: '/workspaces/test-workspace/red-team', + }, + ], + }, + ]), + ]); + + expect(screen.getAllByText('Governance')).toHaveLength(1); + expect(screen.getByRole('link', { name: 'Red Team' })).toBeInTheDocument(); + }); + + it('appends a plugin group that matches no core group', () => { + renderWithPlugins([ + makePlugin('red-team', [ + { + group: 'Red Team', + items: [ + { + id: 'probes', + iconName: 'shield', + label: 'Probes', + href: '/workspaces/test-workspace/probes', + }, + ], + }, + ]), + ]); + + expect(screen.getAllByText('Red Team')).toHaveLength(1); + expect(screen.getByRole('link', { name: 'Probes' })).toBeInTheDocument(); + }); + + it('keeps a plugin item whose id matches a core item in the merged group', () => { + const duplicateKeyWarning = vi.spyOn(console, 'error').mockImplementation(() => {}); + + renderWithPlugins([ + makePlugin('red-team', [ + { + group: 'Governance', + items: [ + { + id: 'guardrails', + iconName: 'shield', + label: 'Red Team Models', + href: '/workspaces/test-workspace/red-team-models', + }, + ], + }, + ]), + ]); + + expect(screen.getByRole('link', { name: 'Guardrails' })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'Red Team Models' })).toBeInTheDocument(); + const keyWarnings = duplicateKeyWarning.mock.calls + .map((args) => args.map(String).join(' ')) + .filter((message) => message.includes('same key')); + expect(keyWarnings).toEqual([]); + + duplicateKeyWarning.mockRestore(); + }); + + it('merges groups of the same name across two plugins', () => { + renderWithPlugins([ + makePlugin('one', [ + { + group: 'Governance', + items: [ + { id: 'one', iconName: 'shield', label: 'One', href: '/workspaces/test-workspace/one' }, + ], + }, + ]), + makePlugin('two', [ + { + group: 'Governance', + items: [ + { id: 'two', iconName: 'shield', label: 'Two', href: '/workspaces/test-workspace/two' }, + ], + }, + ]), + ]); + + expect(screen.getAllByText('Governance')).toHaveLength(1); + expect(screen.getByRole('link', { name: 'One' })).toBeInTheDocument(); + expect(screen.getByRole('link', { name: 'Two' })).toBeInTheDocument(); + }); }); diff --git a/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.tsx b/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.tsx index 833b977703..dc4469dbf0 100644 --- a/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.tsx +++ b/web/packages/studio/src/routes/WorkspaceLayout/WorkspaceSideNav.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { NavigationDrawer } from '@studio/components/Layouts/NavigationDrawer'; import type { NavInputItem, @@ -56,7 +57,6 @@ import { getWorkspaceSettingsRoute, getWorkspaceVirtualModelsRoute, } from '@studio/routes/utils'; -import { logger } from '@studio/util/logger'; import { Bot, Boxes, @@ -384,7 +384,9 @@ export const WorkspaceSideNav = ({ collapsed }: { collapsed?: boolean }) => { items: group.items.map((item) => { const Icon = getPluginIcon(item.iconName); return { - id: item.id, + // Namespaced: ids are React keys and accordion-state keys, and + // merging puts plugin items in the same array as core ones. + id: `${plugin.name}:${item.id}`, slotIcon: Icon ? : undefined, slotLabel: item.label, href: item.href, @@ -399,11 +401,27 @@ export const WorkspaceSideNav = ({ collapsed }: { collapsed?: boolean }) => { [plugins, workspace] ); - // A fresh array literal here would re-run NavigationDrawer's own memos on every render. - const allItems = useMemo( - () => [...items, ...pluginNavGroups, ...systemNavGroup], - [items, pluginNavGroups, systemNavGroup] - ); + // A plugin naming an existing group joins it rather than rendering a second + // header with the same label. Unmatched groups append in plugin order. + const allItems = useMemo(() => { + const pending = new Map(); + for (const { group, items: groupItems } of pluginNavGroups) { + pending.set(group, [...(pending.get(group) ?? []), ...groupItems]); + } + + const merged: NavInputItem[] = items.map((entry) => { + if (!isGroup(entry) || entry.group === undefined) return entry; + const extra = pending.get(entry.group); + if (!extra) return entry; + pending.delete(entry.group); + return { ...entry, items: [...entry.items, ...extra] }; + }); + + for (const [group, groupItems] of pending) { + merged.push({ group, items: groupItems }); + } + return [...merged, ...systemNavGroup]; + }, [items, pluginNavGroups, systemNavGroup]); return ; }; diff --git a/web/packages/studio/src/routes/WorkspaceMembersRoute/WorkspaceMemberModal.tsx b/web/packages/studio/src/routes/WorkspaceMembersRoute/WorkspaceMemberModal.tsx index 42934b5ab9..2234eba267 100644 --- a/web/packages/studio/src/routes/WorkspaceMembersRoute/WorkspaceMemberModal.tsx +++ b/web/packages/studio/src/routes/WorkspaceMembersRoute/WorkspaceMemberModal.tsx @@ -8,6 +8,7 @@ import { ControlledTextInput } from '@nemo/common/src/components/form/Controlled import { FormModal } from '@nemo/common/src/components/FormModal'; import { RadioCard } from '@nemo/common/src/components/RadioCard'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { getEntitiesListWorkspaceMembersQueryKey, useEntitiesAddWorkspaceMember, @@ -22,7 +23,6 @@ import { WORKSPACE_ROLE_DESCRIPTIONS, type WorkspaceMemberRole, } from '@studio/routes/WorkspaceMembersRoute/workspaceMemberRoles'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC, useEffect, useMemo } from 'react'; import { Controller, SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; diff --git a/web/packages/studio/src/routes/WorkspaceMembersRoute/index.tsx b/web/packages/studio/src/routes/WorkspaceMembersRoute/index.tsx index 3d5ac07f4b..cf40dfe0f2 100644 --- a/web/packages/studio/src/routes/WorkspaceMembersRoute/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceMembersRoute/index.tsx @@ -3,6 +3,8 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { getEntitiesListWorkspaceMembersQueryKey, useEntitiesListWorkspaceMembers, @@ -11,9 +13,7 @@ import { import type { WorkspaceMember } from '@nemo/sdk/generated/platform/schema'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; import { queryClient } from '@studio/api/queryClient'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { MembersDataView } from '@studio/components/dataViews/MembersDataView'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/routes/WorkspaceSettingsRoute/DeleteWorkspaceModal/index.tsx b/web/packages/studio/src/routes/WorkspaceSettingsRoute/DeleteWorkspaceModal/index.tsx index 3422166e8e..5ded8de630 100644 --- a/web/packages/studio/src/routes/WorkspaceSettingsRoute/DeleteWorkspaceModal/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceSettingsRoute/DeleteWorkspaceModal/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { DEFAULT_WORKSPACE } from '@nemo/common/src/models/constants'; import { getEntitiesListWorkspacesQueryKey, @@ -8,7 +9,6 @@ import { } from '@nemo/sdk/generated/platform/api'; import { WorkspacesPage } from '@nemo/sdk/generated/platform/schema'; import { queryClient } from '@studio/api/queryClient'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { useRecentWorkspaces } from '@studio/components/WorkspaceDropdown/useRecentWorkspaces'; import { getWorkspaceDetailsDefaultRoute } from '@studio/routes/utils'; import { FC } from 'react'; diff --git a/web/packages/studio/src/routes/WorkspaceSettingsRoute/EditDescriptionModal/index.tsx b/web/packages/studio/src/routes/WorkspaceSettingsRoute/EditDescriptionModal/index.tsx index ad91728446..8872a18448 100644 --- a/web/packages/studio/src/routes/WorkspaceSettingsRoute/EditDescriptionModal/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceSettingsRoute/EditDescriptionModal/index.tsx @@ -6,6 +6,7 @@ import { ControlledTextArea } from '@nemo/common/src/components/form/ControlledT import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal, FormModalProps } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; +import { handleFormErrorsGeneric } from '@nemo/common/src/utils/forms/error'; import { getEntitiesGetWorkspaceQueryKey, getEntitiesListWorkspacesQueryKey, @@ -15,7 +16,6 @@ import { import { EntitiesUpdateWorkspaceBody } from '@nemo/sdk/generated/platform/zod/entity-store'; import { Stack, Text } from '@nvidia/foundations-react-core'; import { queryClient } from '@studio/api/queryClient'; -import { handleFormErrorsGeneric } from '@studio/util/forms/error'; import { FC, useEffect } from 'react'; import { SubmitHandler, useForm } from 'react-hook-form'; import { z } from 'zod'; diff --git a/web/packages/studio/src/routes/WorkspaceSettingsRoute/index.tsx b/web/packages/studio/src/routes/WorkspaceSettingsRoute/index.tsx index 7a71a64d8c..1ecc1ce1ad 100644 --- a/web/packages/studio/src/routes/WorkspaceSettingsRoute/index.tsx +++ b/web/packages/studio/src/routes/WorkspaceSettingsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { DEFAULT_WORKSPACE } from '@nemo/common/src/models/constants'; import { Button, @@ -11,7 +12,6 @@ import { Stack, Text, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { INFERENCE_PROVIDER_ENABLED, diff --git a/web/packages/studio/src/routes/agents/AgentDetailRoute/DeploymentLogsView.tsx b/web/packages/studio/src/routes/agents/AgentDetailRoute/DeploymentLogsView.tsx index 0d418e4451..5b996e98a2 100644 --- a/web/packages/studio/src/routes/agents/AgentDetailRoute/DeploymentLogsView.tsx +++ b/web/packages/studio/src/routes/agents/AgentDetailRoute/DeploymentLogsView.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { LogViewer } from '@nemo/common/src/components/LogViewer'; +import { logger } from '@nemo/common/src/utils/logger'; import { getAgentsStreamDeploymentLogsQueryKey, useAgentsGetDeploymentLogs, @@ -10,7 +11,6 @@ import type { AgentDeployment } from '@nemo/sdk/generated/agents/schema'; import type { PlatformJobLog } from '@nemo/sdk/generated/platform/schema'; import { Block, Select, Stack, Text } from '@nvidia/foundations-react-core'; import { PLATFORM_BASE_URL } from '@studio/constants/environment'; -import { logger } from '@studio/util/logger'; import { streamSse } from '@studio/util/sseStream'; import { type FC, useEffect, useMemo, useState } from 'react'; import { useAuth } from 'react-oidc-context'; diff --git a/web/packages/studio/src/routes/agents/AgentDetailRoute/index.tsx b/web/packages/studio/src/routes/agents/AgentDetailRoute/index.tsx index c2cdb66598..958a419701 100644 --- a/web/packages/studio/src/routes/agents/AgentDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentDetailRoute/index.tsx @@ -1,6 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { StatusBadge } from '@nemo/common/src/components/StatusBadge'; import type { AgentDeployment } from '@nemo/sdk/generated/agents/schema/AgentDeployment'; import { @@ -14,9 +16,7 @@ import { TabsTrigger, Text, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { getAgentModelNames } from '@studio/components/dataViews/AgentsDataView/utils'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { SubmitEvaluationModal } from '@studio/components/evaluation/SubmitEvaluationModal'; import { ROUTE_PARAMS } from '@studio/constants/routes'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationDetailRoute.tsx b/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationDetailRoute.tsx index 6820847bf9..73faad3192 100644 --- a/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationDetailRoute.tsx +++ b/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationDetailRoute.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { AccordionPanel } from '@nemo/common/src/components/AccordionPanel'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; import { KVPair } from '@nemo/common/src/components/KVPair'; @@ -34,7 +35,6 @@ import { joinBundleByTask, parseBundleRef, } from '@studio/api/evaluation/agent-evaluations'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { AgentEvalTaskResultsPanel } from '@studio/components/evaluation/AgentEvalTaskResultsPanel'; import { EvalAggregateScoresTable } from '@studio/components/evaluation/EvalAggregateScoresTable'; import { StatusLogsContent } from '@studio/components/evaluation/Jobs/StatusLogsContent'; diff --git a/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationsListRoute.tsx b/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationsListRoute.tsx index 880ab967cc..00a0b87810 100644 --- a/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationsListRoute.tsx +++ b/web/packages/studio/src/routes/agents/AgentEvaluationsRoute/AgentEvaluationsListRoute.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { AgentEvaluationsDataView } from '@studio/components/dataViews/AgentEvaluationsDataView'; import { SubmitEvaluationModal } from '@studio/components/evaluation/SubmitEvaluationModal'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/agents/AgentMonitorRoute/index.tsx b/web/packages/studio/src/routes/agents/AgentMonitorRoute/index.tsx index 1b300c48de..5050573ccf 100644 --- a/web/packages/studio/src/routes/agents/AgentMonitorRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentMonitorRoute/index.tsx @@ -1,10 +1,10 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { agentsListAgents } from '@nemo/sdk/generated/agents/api'; import { useFilesListFilesetFiles } from '@nemo/sdk/generated/platform/api'; import { Combobox, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { InferenceLogsTable } from '@studio/routes/agents/AgentMonitorRoute/components/InferenceLogsTable'; diff --git a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/api.ts b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/api.ts index a24036af6d..d763eb8c02 100644 --- a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/api.ts +++ b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/api.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { toError } from '@nemo/common/src/utils/logger'; import { customFetch } from '@nemo/sdk/generated/fetchers/platform'; import { filesCreateFileset, @@ -29,7 +30,6 @@ import { serializeSuggestions, suggestionIdentity, } from '@studio/routes/agents/AgentSuggestionsRoute/utils'; -import { toError } from '@studio/util/logger'; export const TELEMETRY_FILESET = 'nemo-agent-telemetry'; export const OPTIMIZER_FILESET = 'nemo-agent-optimizer'; diff --git a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/index.tsx b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/index.tsx index 37a6bdd928..ee19dfaa28 100644 --- a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ColumnFilterPanel } from '@nemo/common/src/components/DataView/ColumnFilterPanel'; import { FilterPanel } from '@nemo/common/src/components/DataView/FilterPanel'; import { Root as DataView } from '@nemo/common/src/components/DataView/internal'; @@ -16,7 +17,6 @@ import { Text, TextInput, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { Loading } from '@studio/components/Layouts/Loading'; import { AgentGroupSection } from '@studio/routes/agents/AgentSuggestionsRoute/components/AgentGroupSection'; import { ApplyEvalConfigModal } from '@studio/routes/agents/AgentSuggestionsRoute/components/ApplyEvalConfigModal'; diff --git a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/useOptimizerSuggestions.ts b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/useOptimizerSuggestions.ts index 863ce26c0d..94892fe236 100644 --- a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/useOptimizerSuggestions.ts +++ b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/useOptimizerSuggestions.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { toError } from '@nemo/common/src/utils/logger'; import { ensureEvalConfigFileset } from '@studio/api/evaluation/eval-config-fileset'; import { applySuggestion, @@ -37,7 +38,6 @@ import { suggestionIdentity, } from '@studio/routes/agents/AgentSuggestionsRoute/utils'; import { getAgentEvaluationDetailRoute } from '@studio/routes/utils'; -import { toError } from '@studio/util/logger'; import { useQuery, useQueryClient } from '@tanstack/react-query'; import { useCallback, useEffect, useRef, useState } from 'react'; diff --git a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/utils.ts b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/utils.ts index 4f64e01f9c..2c33a0975f 100644 --- a/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/utils.ts +++ b/web/packages/studio/src/routes/agents/AgentSuggestionsRoute/utils.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import type { ModelEntity } from '@nemo/sdk/generated/platform/schema/ModelEntity'; import { SAMPLE_EVAL_CONFIG_PATH } from '@studio/api/evaluation/eval-config-fileset'; import type { AgentConfig } from '@studio/components/dataViews/AgentsDataView'; @@ -11,7 +12,6 @@ import type { SnapshotShape, SuggestionApplySpec, } from '@studio/routes/agents/AgentSuggestionsRoute/types'; -import { logger } from '@studio/util/logger'; /** Fileset name for an agent's eval bundle. */ export const evalFilesetForAgent = (agentName: string): string => `${agentName}-eval`; diff --git a/web/packages/studio/src/routes/agents/AgentsListRoute/CloneAgentModal/index.tsx b/web/packages/studio/src/routes/agents/AgentsListRoute/CloneAgentModal/index.tsx index 3ceb49cbca..e2a2654113 100644 --- a/web/packages/studio/src/routes/agents/AgentsListRoute/CloneAgentModal/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentsListRoute/CloneAgentModal/index.tsx @@ -2,13 +2,13 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledSearchableSelect } from '@nemo/common/src/components/form/ControlledSearchableSelect'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal } from '@nemo/common/src/components/FormModal'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { getAgentsListAgentsQueryKey, useAgentsCreateAgent } from '@nemo/sdk/generated/agents/api'; import { useModelsListModels } from '@nemo/sdk/generated/platform/api'; -import { getErrorMessage } from '@studio/api/common/utils'; import { DEFAULT_LARGE_PAGE_SIZE } from '@studio/constants/constants'; import { applyModelToConfig, diff --git a/web/packages/studio/src/routes/agents/AgentsListRoute/CreateExampleAgentModal/index.tsx b/web/packages/studio/src/routes/agents/AgentsListRoute/CreateExampleAgentModal/index.tsx index 16034c8dcb..fbe7d27db8 100644 --- a/web/packages/studio/src/routes/agents/AgentsListRoute/CreateExampleAgentModal/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentsListRoute/CreateExampleAgentModal/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledSearchableSelect } from '@nemo/common/src/components/form/ControlledSearchableSelect'; import { ControlledSelect } from '@nemo/common/src/components/form/ControlledSelect'; import { FormModal } from '@nemo/common/src/components/FormModal'; @@ -9,7 +10,6 @@ import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { getAgentsListAgentsQueryKey, useAgentsCreateAgent } from '@nemo/sdk/generated/agents/api'; import { useModelsListModels } from '@nemo/sdk/generated/platform/api'; import { loadSampleAgentConfig } from '@studio/api/agents/loadSampleAgentConfig'; -import { getErrorMessage } from '@studio/api/common/utils'; import { DEFAULT_LARGE_PAGE_SIZE } from '@studio/constants/constants'; import { buildSampleAgentName, diff --git a/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx b/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx index f4f5f75997..30d1bf07fa 100644 --- a/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/AgentsListRoute/index.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import type { Agent } from '@nemo/sdk/generated/agents/schema/Agent'; import { Button, PageHeader, Stack, Text } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { AgentsTable, type AgentTableRow } from '@studio/components/dataViews/AgentsDataView'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { diff --git a/web/packages/studio/src/routes/agents/CopilotChatRoute/ChatThreadErrorBoundary.tsx b/web/packages/studio/src/routes/agents/CopilotChatRoute/ChatThreadErrorBoundary.tsx index c627d90525..718aced993 100644 --- a/web/packages/studio/src/routes/agents/CopilotChatRoute/ChatThreadErrorBoundary.tsx +++ b/web/packages/studio/src/routes/agents/CopilotChatRoute/ChatThreadErrorBoundary.tsx @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; +import { logger } from '@nemo/common/src/utils/logger'; import { Button } from '@nvidia/foundations-react-core'; -import { logger } from '@studio/util/logger'; import { Component, type ErrorInfo, type ReactNode } from 'react'; interface ChatThreadErrorBoundaryProps { diff --git a/web/packages/studio/src/routes/agents/CopilotChatRoute/historyPanel/HistoryPanelContents.tsx b/web/packages/studio/src/routes/agents/CopilotChatRoute/historyPanel/HistoryPanelContents.tsx index f42e564f7b..d05370ab06 100644 --- a/web/packages/studio/src/routes/agents/CopilotChatRoute/historyPanel/HistoryPanelContents.tsx +++ b/web/packages/studio/src/routes/agents/CopilotChatRoute/historyPanel/HistoryPanelContents.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { Banner, Button, Flex, Text, Tooltip } from '@nvidia/foundations-react-core'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { Empty } from '@studio/components/Empty'; import { deleteCopilotSessionHistory, diff --git a/web/packages/studio/src/routes/agents/CopilotChatRoute/index.tsx b/web/packages/studio/src/routes/agents/CopilotChatRoute/index.tsx index d571dafa76..18db409ecc 100644 --- a/web/packages/studio/src/routes/agents/CopilotChatRoute/index.tsx +++ b/web/packages/studio/src/routes/agents/CopilotChatRoute/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Banner, Stack, Text } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { useCopilotChatContext } from '@studio/routes/agents/CopilotChatRoute/context/useCopilotChatContext'; diff --git a/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.test.ts b/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.test.ts index 0679bc9816..aa2bc7630a 100644 --- a/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.test.ts +++ b/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.test.ts @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { COPILOT_JOB_PROGRESS_MCP_TOOL_NAME } from '@studio/routes/agents/CopilotChatRoute/jobProgressConsts'; import { getAssistantPartsFromCopilotEvent, @@ -9,7 +10,6 @@ import { parseSseChunk, } from '@studio/routes/agents/CopilotChatRoute/stream'; import { COPILOT_SUBTLE_TOOL_GROUP_NAME } from '@studio/routes/agents/CopilotChatRoute/toolParts'; -import { logger } from '@studio/util/logger'; describe('Copilot stream utilities', () => { it('parses SSE events and preserves incomplete trailing data', () => { diff --git a/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.ts b/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.ts index e897804080..6d20f869b5 100644 --- a/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.ts +++ b/web/packages/studio/src/routes/agents/CopilotChatRoute/stream.ts @@ -2,11 +2,11 @@ // SPDX-License-Identifier: Apache-2.0 import type { ThreadAssistantMessagePart } from '@assistant-ui/react'; +import { logger } from '@nemo/common/src/utils/logger'; import { createCopilotToolCallPart, groupConsecutiveCopilotSubtleToolCalls, } from '@studio/routes/agents/CopilotChatRoute/toolParts'; -import { logger } from '@studio/util/logger'; interface ServerSentEvent { event?: string; diff --git a/web/packages/studio/src/routes/evaluation/EvaluationLayout/index.tsx b/web/packages/studio/src/routes/evaluation/EvaluationLayout/index.tsx index 37236bb6ba..89fde821d3 100644 --- a/web/packages/studio/src/routes/evaluation/EvaluationLayout/index.tsx +++ b/web/packages/studio/src/routes/evaluation/EvaluationLayout/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { DocumentationButton } from '@studio/components/DocumentationButton'; import { Loading } from '@studio/components/Layouts/Loading'; import { LINK_EVAL_DOCS_BENCHMARKS_INDUSTRY } from '@studio/constants/links'; diff --git a/web/packages/studio/src/routes/evaluation/EvaluationResultDetailsRoute/index.tsx b/web/packages/studio/src/routes/evaluation/EvaluationResultDetailsRoute/index.tsx index 66e9ef02b8..020aac62ca 100644 --- a/web/packages/studio/src/routes/evaluation/EvaluationResultDetailsRoute/index.tsx +++ b/web/packages/studio/src/routes/evaluation/EvaluationResultDetailsRoute/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { AccordionPanel } from '@nemo/common/src/components/AccordionPanel'; import { PlatformJobTerminalStatuses } from '@nemo/common/src/constants/query'; import { useEvaluatorGetEvaluateJob } from '@nemo/sdk/generated/evaluator/api'; @@ -15,7 +16,6 @@ import { Spinner, Stack, } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { EvalAggregateScoresTable } from '@studio/components/evaluation/EvalAggregateScoresTable'; import { DatasetEvalRowResultsPanel } from '@studio/components/evaluation/Jobs/datasetEval/DatasetEvalRowResultsPanel'; import { useDatasetEvalResults } from '@studio/components/evaluation/Jobs/datasetEval/useDatasetEvalResults'; diff --git a/web/packages/studio/src/routes/evaluation/EvaluationResultsLayout/index.tsx b/web/packages/studio/src/routes/evaluation/EvaluationResultsLayout/index.tsx index a4ae4221b0..dff85e77a2 100644 --- a/web/packages/studio/src/routes/evaluation/EvaluationResultsLayout/index.tsx +++ b/web/packages/studio/src/routes/evaluation/EvaluationResultsLayout/index.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { Loading } from '@studio/components/Layouts/Loading'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { FC, Suspense, useEffect } from 'react'; diff --git a/web/packages/studio/src/routes/groups/agentRoutes.tsx b/web/packages/studio/src/routes/groups/agentRoutes.tsx index b07163f8bb..1131d96b6b 100644 --- a/web/packages/studio/src/routes/groups/agentRoutes.tsx +++ b/web/packages/studio/src/routes/groups/agentRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { AGENTS_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { iconColorClass } from '@studio/routes/constants'; diff --git a/web/packages/studio/src/routes/groups/anonymizerRoutes.tsx b/web/packages/studio/src/routes/groups/anonymizerRoutes.tsx index f94032a1a8..fb1c799aaf 100644 --- a/web/packages/studio/src/routes/groups/anonymizerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/anonymizerRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ANONYMIZER_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateAnonymizerRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/customizationRoutes.tsx b/web/packages/studio/src/routes/groups/customizationRoutes.tsx index d150cf5221..6166e18f7d 100644 --- a/web/packages/studio/src/routes/groups/customizationRoutes.tsx +++ b/web/packages/studio/src/routes/groups/customizationRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateCustomizationRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/dashboardRoutes.tsx b/web/packages/studio/src/routes/groups/dashboardRoutes.tsx index 381f7b096e..b2e8476e49 100644 --- a/web/packages/studio/src/routes/groups/dashboardRoutes.tsx +++ b/web/packages/studio/src/routes/groups/dashboardRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { COPILOT_STUDIO_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateCopilotStudioRoutes, gateDashboardRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx index 173de5646f..1d3cb71102 100644 --- a/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/dataDesignerRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { DATA_DESIGNER_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateDataDesignerRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/deploymentRoutes.tsx b/web/packages/studio/src/routes/groups/deploymentRoutes.tsx index 30e6ce63ff..381e56a5f2 100644 --- a/web/packages/studio/src/routes/groups/deploymentRoutes.tsx +++ b/web/packages/studio/src/routes/groups/deploymentRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { DEPLOYMENTS_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateDeploymentsRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/evaluationRoutes.tsx b/web/packages/studio/src/routes/groups/evaluationRoutes.tsx index da001535e1..909bd54880 100644 --- a/web/packages/studio/src/routes/groups/evaluationRoutes.tsx +++ b/web/packages/studio/src/routes/groups/evaluationRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateEvaluationBenchmarksRoutes, gateEvaluationRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/experimentRoutes.tsx b/web/packages/studio/src/routes/groups/experimentRoutes.tsx index a34beba13e..73186a3fdc 100644 --- a/web/packages/studio/src/routes/groups/experimentRoutes.tsx +++ b/web/packages/studio/src/routes/groups/experimentRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateExperimentRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/filesetRoutes.tsx b/web/packages/studio/src/routes/groups/filesetRoutes.tsx index fe65bc377e..1dfdb8c832 100644 --- a/web/packages/studio/src/routes/groups/filesetRoutes.tsx +++ b/web/packages/studio/src/routes/groups/filesetRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateDatasetsRoutes, gateFilesetDetailsRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/guardrailsRoutes.tsx b/web/packages/studio/src/routes/groups/guardrailsRoutes.tsx index 135f12b6a0..a939766930 100644 --- a/web/packages/studio/src/routes/groups/guardrailsRoutes.tsx +++ b/web/packages/studio/src/routes/groups/guardrailsRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { GUARDRAIL_CHECKS_DEFAULT_SUB_TAB } from '@studio/routes/guardrails/GuardrailChecksTab/constants'; import { gateGuardrailsRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/inferenceProviderRoutes.tsx b/web/packages/studio/src/routes/groups/inferenceProviderRoutes.tsx index 32f92d189f..1e8fedfe9a 100644 --- a/web/packages/studio/src/routes/groups/inferenceProviderRoutes.tsx +++ b/web/packages/studio/src/routes/groups/inferenceProviderRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateInferenceProviderRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/intakeRoutes.tsx b/web/packages/studio/src/routes/groups/intakeRoutes.tsx index 3cf04d2f4e..b1399e6c09 100644 --- a/web/packages/studio/src/routes/groups/intakeRoutes.tsx +++ b/web/packages/studio/src/routes/groups/intakeRoutes.tsx @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { Stack } from '@nvidia/foundations-react-core'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { INTAKE_FILTER_ACTION_TARGET_ID } from '@studio/routes/IntakeLayout'; import { gateIntakeRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/jobRoutes.tsx b/web/packages/studio/src/routes/groups/jobRoutes.tsx index ed05e9de2c..60f4f07693 100644 --- a/web/packages/studio/src/routes/groups/jobRoutes.tsx +++ b/web/packages/studio/src/routes/groups/jobRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateJobsRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/memberRoutes.tsx b/web/packages/studio/src/routes/groups/memberRoutes.tsx index 66e40bd0c3..a0492267a8 100644 --- a/web/packages/studio/src/routes/groups/memberRoutes.tsx +++ b/web/packages/studio/src/routes/groups/memberRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateMembersRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/modelCompareRoutes.tsx b/web/packages/studio/src/routes/groups/modelCompareRoutes.tsx index cf7b5114a6..610d23a4f3 100644 --- a/web/packages/studio/src/routes/groups/modelCompareRoutes.tsx +++ b/web/packages/studio/src/routes/groups/modelCompareRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { MODEL_COMPARE_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateModelCompareRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/optimizerRoutes.tsx b/web/packages/studio/src/routes/groups/optimizerRoutes.tsx index 455addcab4..84be5c5126 100644 --- a/web/packages/studio/src/routes/groups/optimizerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/optimizerRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { OPTIMIZER_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateOptimizerRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/safeSynthesizerRoutes.tsx b/web/packages/studio/src/routes/groups/safeSynthesizerRoutes.tsx index 37948d7ff0..f9195be52e 100644 --- a/web/packages/studio/src/routes/groups/safeSynthesizerRoutes.tsx +++ b/web/packages/studio/src/routes/groups/safeSynthesizerRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { SAFE_SYNTHESIZER_ENABLED } from '@studio/constants/environment'; import { ROUTES } from '@studio/constants/routes'; import { gateSafeSynthesizerRoutes } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/groups/secretsRoutes.tsx b/web/packages/studio/src/routes/groups/secretsRoutes.tsx index 82201652ba..3b22f67ceb 100644 --- a/web/packages/studio/src/routes/groups/secretsRoutes.tsx +++ b/web/packages/studio/src/routes/groups/secretsRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateSecretsRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/settingsRoutes.tsx b/web/packages/studio/src/routes/groups/settingsRoutes.tsx index 6c70030c87..f1f5c0097a 100644 --- a/web/packages/studio/src/routes/groups/settingsRoutes.tsx +++ b/web/packages/studio/src/routes/groups/settingsRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { gateSettingsRoutes } from '@studio/routes/utils'; import { lazy } from 'react'; diff --git a/web/packages/studio/src/routes/groups/virtualModelsRoutes.tsx b/web/packages/studio/src/routes/groups/virtualModelsRoutes.tsx index 303cc67c09..781e187999 100644 --- a/web/packages/studio/src/routes/groups/virtualModelsRoutes.tsx +++ b/web/packages/studio/src/routes/groups/virtualModelsRoutes.tsx @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { ROUTES } from '@studio/constants/routes'; import { lazy } from 'react'; import type { RouteObject } from 'react-router'; diff --git a/web/packages/studio/src/routes/guardrails/CreateGuardrailModal/index.tsx b/web/packages/studio/src/routes/guardrails/CreateGuardrailModal/index.tsx index 5988ffdff7..ad632ce184 100644 --- a/web/packages/studio/src/routes/guardrails/CreateGuardrailModal/index.tsx +++ b/web/packages/studio/src/routes/guardrails/CreateGuardrailModal/index.tsx @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { zodResolver } from '@hookform/resolvers/zod'; +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { ControlledTextInput } from '@nemo/common/src/components/form/ControlledTextInput'; import { FormModal } from '@nemo/common/src/components/FormModal'; import { ENTITY_NAME_HELP, entityNameSchema, toCopyName } from '@nemo/common/src/utils/entityName'; @@ -11,7 +12,6 @@ import type { GuardrailConfigInput, GuardrailConfigInputData, } from '@nemo/sdk/generated/platform/schema'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { getGuardrailDetailRoute } from '@studio/routes/utils'; import { useQueryClient } from '@tanstack/react-query'; diff --git a/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCard.tsx b/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCard.tsx index 613f0702eb..7e6839a0e1 100644 --- a/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCard.tsx +++ b/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCard.tsx @@ -1,9 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import { Button, Flex, Panel, SegmentedControl, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useUpdateGuardrailCheck } from '@studio/api/guardrail-checks/hooks'; import type { GuardrailCheckEntity, diff --git a/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCasesEditor.tsx b/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCasesEditor.tsx index 82435f815f..f44fa68432 100644 --- a/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCasesEditor.tsx +++ b/web/packages/studio/src/routes/guardrails/GuardrailChecksTab/GuardrailTestCasesEditor.tsx @@ -1,11 +1,11 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { LoadingButton } from '@nemo/common/src/components/LoadingButton'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; import type { RailsConfig } from '@nemo/sdk/generated/platform/schema'; import { Button, Flex, Stack, Tabs, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { useCreateGuardrailCheck, useRunGuardrailChecks } from '@studio/api/guardrail-checks/hooks'; import type { GuardrailCheckEntity } from '@studio/api/guardrail-checks/types'; import { GuardrailChecksDataView } from '@studio/components/dataViews/GuardrailChecksDataView'; diff --git a/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/GuardrailDetailActions.tsx b/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/GuardrailDetailActions.tsx index f8b932d538..4d4c175dc8 100644 --- a/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/GuardrailDetailActions.tsx +++ b/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/GuardrailDetailActions.tsx @@ -1,13 +1,13 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 -import { useGuardrailsDeleteConfig } from '@nemo/sdk/generated/platform/api'; -import type { GuardrailConfig } from '@nemo/sdk/generated/platform/schema'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { type QuickActionItem, QuickActionsMenuRoot, -} from '@studio/components/QuickActionsMenu/QuickActionsMenuRoot'; +} from '@nemo/common/src/components/QuickActionsMenu/QuickActionsMenuRoot'; +import { useGuardrailsDeleteConfig } from '@nemo/sdk/generated/platform/api'; +import type { GuardrailConfig } from '@nemo/sdk/generated/platform/schema'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { CreateGuardrailModal } from '@studio/routes/guardrails/CreateGuardrailModal'; import { getGuardrailsRoute } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/index.tsx b/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/index.tsx index 53eca27220..a55367d0fb 100644 --- a/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/index.tsx +++ b/web/packages/studio/src/routes/guardrails/GuardrailDetailRoute/index.tsx @@ -3,9 +3,9 @@ * SPDX-License-Identifier: Apache-2.0 */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { useGuardrailsGetGuardrailConfig } from '@nemo/sdk/generated/platform/api'; import { Flex, PageHeader, Stack, Tabs, Text } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { Loading } from '@studio/components/Layouts/Loading'; import { ROUTE_PARAMS, ROUTES } from '@studio/constants/routes'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; diff --git a/web/packages/studio/src/routes/guardrails/GuardrailsRoute/index.tsx b/web/packages/studio/src/routes/guardrails/GuardrailsRoute/index.tsx index 909fe3f4ae..46fdf80c2d 100644 --- a/web/packages/studio/src/routes/guardrails/GuardrailsRoute/index.tsx +++ b/web/packages/studio/src/routes/guardrails/GuardrailsRoute/index.tsx @@ -10,15 +10,15 @@ * its affiliates is strictly prohibited. */ +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; +import { DeleteConfirmationModal } from '@nemo/common/src/components/DeleteConfirmationModal'; import { getGuardrailsGetGuardrailConfigQueryKey, useGuardrailsDeleteConfig, } from '@nemo/sdk/generated/platform/api'; import type { GuardrailConfig } from '@nemo/sdk/generated/platform/schema'; import { Button, PageHeader, Stack } from '@nvidia/foundations-react-core'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; import { GuardrailsDataView } from '@studio/components/dataViews/GuardrailsDataView'; -import { DeleteConfirmationModal } from '@studio/components/DeleteConfirmationModal'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; import { CreateGuardrailModal } from '@studio/routes/guardrails/CreateGuardrailModal'; diff --git a/web/packages/studio/src/routes/index.tsx b/web/packages/studio/src/routes/index.tsx index b54d11d538..c6d2b6d267 100644 --- a/web/packages/studio/src/routes/index.tsx +++ b/web/packages/studio/src/routes/index.tsx @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { Loading } from '@studio/components/Layouts/Loading'; import { ROUTES } from '@studio/constants/routes'; import { PluginProvider } from '@studio/plugins/PluginProvider'; diff --git a/web/packages/studio/src/routes/optimizer/InsightTracesTable/index.tsx b/web/packages/studio/src/routes/optimizer/InsightTracesTable/index.tsx index be8cf509ea..b5c0b6a421 100644 --- a/web/packages/studio/src/routes/optimizer/InsightTracesTable/index.tsx +++ b/web/packages/studio/src/routes/optimizer/InsightTracesTable/index.tsx @@ -1,6 +1,7 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; import { withOperators } from '@nemo/common/src/api/filterOperators'; import { EditColumnsMenu } from '@nemo/common/src/components/DataView/internal'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; @@ -9,7 +10,6 @@ import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataView import { useListTraces } from '@nemo/sdk/generated/platform/api'; import type { Trace, TraceFilter } from '@nemo/sdk/generated/platform/schema'; import { Flex, Stack, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { IntakeTelemetryDataView } from '@studio/components/IntakeLists/IntakeTelemetryDataView'; import { makeIntakeTraceColumns } from '@studio/components/IntakeLists/intakeTraceColumns'; import { getIntakeSessionTraceRoute } from '@studio/routes/utils'; diff --git a/web/packages/studio/src/routes/optimizer/OptimizerInsightRoute/index.tsx b/web/packages/studio/src/routes/optimizer/OptimizerInsightRoute/index.tsx index 3a758188b7..46d59f5e00 100644 --- a/web/packages/studio/src/routes/optimizer/OptimizerInsightRoute/index.tsx +++ b/web/packages/studio/src/routes/optimizer/OptimizerInsightRoute/index.tsx @@ -1,7 +1,9 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ErrorMessage } from '@nemo/common/src/components/ErrorMessage'; +import { ExpandableMessage } from '@nemo/common/src/components/ExpandableMessage'; import { KVPair } from '@nemo/common/src/components/KVPair'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { useToast } from '@nemo/common/src/providers/toast/useToast'; @@ -23,8 +25,6 @@ import { useOptimizerUpdateInsight, type InsightStatus, } from '@studio/api/optimizer'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; -import { ExpandableMessage } from '@studio/components/ExpandableMessage'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { Loading } from '@studio/components/Layouts/Loading'; import { LINK_DOCS_STUDIO_EVALUATION } from '@studio/constants/links'; diff --git a/web/packages/studio/src/routes/optimizer/OptimizerRoute/index.tsx b/web/packages/studio/src/routes/optimizer/OptimizerRoute/index.tsx index 1836ccc573..44c57d5f69 100644 --- a/web/packages/studio/src/routes/optimizer/OptimizerRoute/index.tsx +++ b/web/packages/studio/src/routes/optimizer/OptimizerRoute/index.tsx @@ -1,18 +1,18 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { getErrorMessage } from '@nemo/common/src/api/common/utils'; +import { AccessibleTitle } from '@nemo/common/src/components/AccessibleTitle'; import { ROW_ACTIONS_COLUMN_SIZE, StudioDataView, } from '@nemo/common/src/components/DataView/StudioDataView'; +import { ErrorPanel } from '@nemo/common/src/components/ErrorPanel'; import { RelativeTime } from '@nemo/common/src/components/RelativeTime'; import { TableEmptyState } from '@nemo/common/src/components/TableEmptyState'; import { useStudioDataViewState } from '@nemo/common/src/hooks/useStudioDataViewState'; import { Flex, PageHeader, Stack, Tag, Text } from '@nvidia/foundations-react-core'; -import { getErrorMessage } from '@studio/api/common/utils'; import { type InsightListItem, useOptimizerListInsights } from '@studio/api/optimizer'; -import { AccessibleTitle } from '@studio/components/AccessibleTitle'; -import { ErrorPanel } from '@studio/components/ErrorPanel'; import { FeatureFlagBadge } from '@studio/components/FeatureFlagBadge'; import { useWorkspaceFromPath } from '@studio/hooks/useWorkspaceFromPath'; import { useBreadcrumbs } from '@studio/providers/breadcrumbs/useBreadcrumbs'; diff --git a/web/packages/studio/src/util/files.ts b/web/packages/studio/src/util/files.ts index d34c6f60b7..f2b0d930cd 100644 --- a/web/packages/studio/src/util/files.ts +++ b/web/packages/studio/src/util/files.ts @@ -2,9 +2,9 @@ // SPDX-License-Identifier: Apache-2.0 import { ContentType } from '@nemo/common/src/components/CodeEditor/constants'; +import { logger } from '@nemo/common/src/utils/logger'; import { FileSystemDirectory, FileSystemNode } from '@studio/components/FilesTable/utils'; import { parseCSVTable } from '@studio/components/SafeSynthesizerFilesetPreview/util'; -import { logger } from '@studio/util/logger'; import { getTextWithCount, parseCSV } from '@studio/util/strings'; /** diff --git a/web/packages/studio/src/util/llm.ts b/web/packages/studio/src/util/llm.ts index c81ce0ecc4..13c9b99102 100644 --- a/web/packages/studio/src/util/llm.ts +++ b/web/packages/studio/src/util/llm.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import type { ExcludedChatCompletionMessageParam } from '@nemo/common/src/types/chat'; +import { logger } from '@nemo/common/src/utils/logger'; import { Row } from '@studio/util/files'; -import { logger } from '@studio/util/logger'; import Handlebars from 'handlebars'; export const extractUserMessage = (props: { row: Row; template?: string }): string => { diff --git a/web/packages/studio/src/util/logVersion.ts b/web/packages/studio/src/util/logVersion.ts new file mode 100644 index 0000000000..7993faf4c2 --- /dev/null +++ b/web/packages/studio/src/util/logVersion.ts @@ -0,0 +1,11 @@ +// SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. +// SPDX-License-Identifier: Apache-2.0 + +import { logger } from '@nemo/common/src/utils/logger'; +import { VERSION_SHA } from '@studio/constants/environment'; + +export const logVersion = async () => { + if (VERSION_SHA) { + logger.info(`Version: ${VERSION_SHA}`); + } +}; diff --git a/web/packages/studio/src/util/logger.test.ts b/web/packages/studio/src/util/logger.test.ts index 6c95f19fa2..cc11e406a1 100644 --- a/web/packages/studio/src/util/logger.test.ts +++ b/web/packages/studio/src/util/logger.test.ts @@ -31,8 +31,9 @@ vi.mock('@studio/constants/environment', async (importOriginal) => { }; }); +import { handleGenericError, logger } from '@nemo/common/src/utils/logger'; import { SeverityNumber } from '@opentelemetry/api-logs'; -import { handleGenericError, logVersion, logger } from '@studio/util/logger'; +import { logVersion } from '@studio/util/logVersion'; describe('WebsiteLogger', () => { beforeEach(() => { diff --git a/web/packages/studio/src/util/strings.ts b/web/packages/studio/src/util/strings.ts index 6863c17fbb..ed869784ff 100644 --- a/web/packages/studio/src/util/strings.ts +++ b/web/packages/studio/src/util/strings.ts @@ -1,8 +1,8 @@ // SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. // SPDX-License-Identifier: Apache-2.0 +import { logger } from '@nemo/common/src/utils/logger'; import { Row } from '@studio/util/files'; -import { logger } from '@studio/util/logger'; import Papa from 'papaparse'; export { getTextWithCount } from '@nemo/common/src/utils/formatters'; diff --git a/web/packages/studio/src/workers/LargeFileWorker.ts b/web/packages/studio/src/workers/LargeFileWorker.ts index 9857c566a0..ecb0c74a25 100644 --- a/web/packages/studio/src/workers/LargeFileWorker.ts +++ b/web/packages/studio/src/workers/LargeFileWorker.ts @@ -2,8 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 import { DEFAULT_WORKSPACE } from '@nemo/common/src/models/constants'; +import { toError } from '@nemo/common/src/utils/logger'; import { filesDownloadFile } from '@nemo/sdk/generated/platform/api'; -import { toError } from '@studio/util/logger'; import axios from 'axios'; export interface LargeFileWorkerMessage { diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 6048d1a4cc..9149347d16 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -319,6 +319,9 @@ importers: '@nvidia/foundations-react-core': specifier: 'catalog:' version: 1.7.0(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@opentelemetry/api-logs': + specifier: ^0.219.0 + version: 0.219.0 '@tanstack/match-sorter-utils': specifier: ^8.19.4 version: 8.19.4 @@ -443,6 +446,9 @@ importers: p-limit: specifier: 'catalog:' version: 6.2.0 + react-dropzone: + specifier: 'catalog:' + version: 14.4.1(react@19.2.7) react-hook-form: specifier: 'catalog:' version: 7.71.2(react@19.2.7)