@kortix/ui 0.14.4 → 0.14.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (118) hide show
  1. package/README.md +8 -4
  2. package/dist/customize/sections/llm-provider/constants.d.ts +0 -1
  3. package/dist/customize/sections/llm-provider/constants.js +0 -2
  4. package/dist/customize/sections/llm-provider/models-tab-group.d.ts +1 -1
  5. package/dist/customize/sections/llm-provider/models-tab-group.js +7 -4
  6. package/dist/customize/sections/llm-provider/models-tab-row.d.ts +2 -18
  7. package/dist/customize/sections/llm-provider/models-tab-row.js +6 -30
  8. package/dist/customize/sections/llm-provider/models-tab.d.ts +0 -1
  9. package/dist/customize/sections/llm-provider/models-tab.js +1 -10
  10. package/dist/customize/sections/llm-provider/use-connected-providers.js +2 -2
  11. package/dist/customize/sections/llm-provider/utils.d.ts +1 -1
  12. package/dist/customize/sections/llm-provider/utils.js +7 -14
  13. package/dist/customize/sections/view/gateway/_shared.js +1 -2
  14. package/dist/customize/sections/view/gateway/gateway-routing.js +3 -2
  15. package/dist/customize/sections/view/sandbox-provider-coverage.d.ts +3 -18
  16. package/dist/customize/sections/view/sandbox-provider-coverage.js +7 -27
  17. package/dist/customize/settings/tabs/sandbox-tab.d.ts +2 -27
  18. package/dist/customize/settings/tabs/sandbox-tab.js +13 -112
  19. package/dist/customize/shared/entity/entity-modal.d.ts +5 -1
  20. package/dist/customize/shared/entity/entity-modal.js +7 -4
  21. package/dist/customize/skills/remove-skill-button.d.ts +16 -0
  22. package/dist/customize/skills/remove-skill-button.js +42 -0
  23. package/dist/customize/skills/skills-page.js +1 -1
  24. package/dist/customize/triggers/schedule/trigger-list.d.ts +0 -2
  25. package/dist/customize/triggers/schedule/trigger-list.js +1 -18
  26. package/dist/customize/triggers/schedule/trigger-tile.d.ts +1 -32
  27. package/dist/customize/triggers/schedule/trigger-tile.js +1 -16
  28. package/dist/files/project/hooks/use-change-requests.d.ts +14 -1
  29. package/dist/files/project/hooks/use-change-requests.js +44 -25
  30. package/dist/files/renderers/docx/docx-viewer.js +4 -1
  31. package/dist/files/renderers/pdf/pdf-renderer.d.ts +3 -1
  32. package/dist/files/renderers/pdf/pdf-renderer.js +2 -2
  33. package/dist/files/renderers/pdf/pdf-viewer.d.ts +3 -0
  34. package/dist/files/renderers/pdf/pdf-viewer.js +15 -6
  35. package/dist/files/viewer/file-content-renderer.d.ts +4 -1
  36. package/dist/files/viewer/file-content-renderer.js +2 -2
  37. package/dist/i18n/en.d.ts +26 -35
  38. package/dist/i18n/en.js +35 -44
  39. package/dist/index.d.ts +1 -1
  40. package/dist/internal/hooks/use-notification-center.js +21 -15
  41. package/dist/internal/hooks/use-review-session-summary.d.ts +6 -0
  42. package/dist/internal/hooks/use-review-session-summary.js +11 -0
  43. package/dist/internal/lib/browser-error-noise.d.ts +1 -1
  44. package/dist/internal/lib/browser-error-noise.js +1 -1
  45. package/dist/internal/lib/browser-noise/rules/rejections.d.ts +10 -0
  46. package/dist/internal/lib/browser-noise/rules/rejections.js +27 -1
  47. package/dist/internal/lib/provider-branding.js +14 -3
  48. package/dist/internal/lib/trigger-clock.d.ts +42 -0
  49. package/dist/internal/lib/trigger-clock.js +34 -0
  50. package/dist/internal/lib/web-notifications.d.ts +6 -0
  51. package/dist/internal/lib/web-notifications.js +57 -7
  52. package/dist/internal/notifications/notification-bell.d.ts +13 -0
  53. package/dist/internal/notifications/notification-bell.js +1 -1
  54. package/dist/internal/reminders/reminder-filter.d.ts +12 -0
  55. package/dist/internal/reminders/reminder-filter.js +34 -0
  56. package/dist/internal/reminders/reminder-format.d.ts +6 -0
  57. package/dist/internal/reminders/reminder-format.js +20 -2
  58. package/dist/internal/stores/kortix-computer-store.d.ts +1 -1
  59. package/dist/internal/stores/session-hover-store.d.ts +20 -5
  60. package/dist/internal/stores/session-hover-store.js +31 -13
  61. package/dist/internal/stores/session-panel-memory-store.js +2 -0
  62. package/dist/primitives/count-badge.d.ts +3 -1
  63. package/dist/primitives/count-badge.js +3 -3
  64. package/dist/primitives/glyph-paths-source.js +1 -1
  65. package/dist/primitives/glyph-paths.generated.js +1 -1
  66. package/dist/providers/ui-provider.d.ts +57 -0
  67. package/dist/providers/ui-provider.js +4 -0
  68. package/dist/session/composer/ComposerCard.d.ts +2 -0
  69. package/dist/session/composer/ComposerCard.js +5 -1
  70. package/dist/session/composer/agent-selector.js +12 -5
  71. package/dist/session/composer/composer-toolbar.js +4 -2
  72. package/dist/session/composer/composer-underbar.js +3 -1
  73. package/dist/session/composer/composer.js +34 -5
  74. package/dist/session/composer/menus/slash-actions.d.ts +22 -2
  75. package/dist/session/composer/menus/slash-actions.js +20 -0
  76. package/dist/session/composer/menus/slash-items.js +19 -3
  77. package/dist/session/header/reminder-parts.d.ts +285 -0
  78. package/dist/session/header/reminder-parts.js +115 -0
  79. package/dist/session/header/session-reminders-indicator.d.ts +13 -0
  80. package/dist/session/header/session-reminders-indicator.js +142 -35
  81. package/dist/session/models/model-availability.d.ts +23 -0
  82. package/dist/session/models/model-availability.js +20 -0
  83. package/dist/session/models/model-grouping.js +1 -4
  84. package/dist/session/models/model-selector.js +23 -14
  85. package/dist/session/models/use-model-connection-gate.js +4 -1
  86. package/dist/session/outcomes/session-outcomes-provider.d.ts +3 -1
  87. package/dist/session/outcomes/session-outcomes-provider.js +31 -3
  88. package/dist/session/overrides/session-overrides-composer.d.ts +1 -1
  89. package/dist/session/overrides/session-overrides-composer.js +5 -1
  90. package/dist/session/panel/mobile-tool-drawer.js +4 -2
  91. package/dist/session/panel/open-session-quick-view.js +2 -1
  92. package/dist/session/panel/panel-fallback.js +8 -4
  93. package/dist/session/panel/session-panel-provider.d.ts +1 -0
  94. package/dist/session/panel/session-panel-provider.js +13 -1
  95. package/dist/session/panel/session-reminders-panel.d.ts +4 -0
  96. package/dist/session/panel/session-reminders-panel.js +109 -0
  97. package/dist/session/panel/shared/use-panel-handoffs.d.ts +2 -2
  98. package/dist/session/panel/shared/use-panel-handoffs.js +5 -1
  99. package/dist/session/print/session-print-header.d.ts +0 -10
  100. package/dist/session/print/session-print-header.js +16 -1
  101. package/dist/session/status/session-surface.js +1 -1
  102. package/dist/session/thread/optimistic-turn.d.ts +5 -1
  103. package/dist/session/thread/optimistic-turn.js +8 -6
  104. package/dist/session/thread/session-chat.d.ts +6 -1
  105. package/dist/session/thread/session-chat.js +22 -3
  106. package/dist/session/thread/session-thread.d.ts +10 -1
  107. package/dist/session/thread/session-thread.js +2 -2
  108. package/dist/session/turn/burst-step-body.js +4 -0
  109. package/dist/styles.css +1 -1
  110. package/dist/workspace/sidebar/project-session-list.js +52 -55
  111. package/dist/workspace/sidebar/session-brief-hover-card.d.ts +35 -18
  112. package/dist/workspace/sidebar/session-brief-hover-card.js +114 -37
  113. package/dist/workspace/sidebar/session-status-mark.d.ts +15 -1
  114. package/dist/workspace/sidebar/session-status-mark.js +63 -17
  115. package/package.json +7 -3
  116. package/skills/kortix-ui/SKILL.md +1 -1
  117. package/dist/customize/sections/view/sandbox-provider-result.d.ts +0 -47
  118. package/dist/customize/sections/view/sandbox-provider-result.js +0 -79
package/README.md CHANGED
@@ -6,10 +6,14 @@ by CSS variables. The whole session UI lives here, one module per
6
6
  responsibility (layout below). `apps/web` imports it like any host: the module
7
7
  entries, the primitives, and a short list of deep paths (below).
8
8
 
9
- **Built with @kortix/ui:** [Lumen](../../apps/whitelabel-demo), a complete white-label agent
10
- product in its own brand. Every screen is a module of this package.
9
+ **Built with @kortix/ui:** three white-label examples in `apps/white-label/`
10
+ ([README](../../apps/white-label/README.md)):
11
+ [Lumen Workspace](../../apps/white-label/reskin), the Kortix workspace under its own
12
+ theme, where every screen is a module of this package;
13
+ [Team Console](../../apps/white-label/team-console), an internal tool; and
14
+ [Lumen](../../apps/white-label/lumen), a legal AI SaaS with its own stack.
11
15
 
12
- <p align="center"><img src="https://github.com/kortix-ai/suna/raw/dev/apps/whitelabel-demo/public/screenshots/session.png" width="720" alt="Lumen: the Kortix session workspace in Lumen's brand" /></p>
16
+ <p align="center"><img src="https://github.com/kortix-ai/suna/raw/dev/apps/white-label/lumen/public/screenshots/assistant-source.png" width="720" alt="Lumen: the @kortix/ui session thread in Lumen's brand, with a cited answer and its source" /></p>
13
17
 
14
18
  Install: `npm i @kortix/ui @kortix/sdk`. Both packages carry the Kortix release
15
19
  version, so install the same version of each. The module entries (`session`,
@@ -298,7 +302,7 @@ const brand: KortixTheme = {
298
302
  `THEME_COLOR_VARS` maps each color key to its variable. Plain CSS works too:
299
303
  set the same variables in your own `:root` / `.dark` after the import. Never
300
304
  fork a component. Roobert (`--font-roobert`) is for Kortix-owned surfaces only.
301
- `apps/whitelabel-demo/src/config/theme.ts` is the reference.
305
+ `apps/white-label/reskin/src/config/theme.ts` is the reference.
302
306
 
303
307
  How it stays themable:
304
308
 
@@ -1,3 +1,2 @@
1
- export declare const MANAGED_MODEL_ID_SET: Set<string>;
2
1
  export declare const CODEX_AUTH_JSON_SECRET_NAME = "CODEX_AUTH_JSON";
3
2
  export declare const LEGACY_RUNTIME_AUTH_JSON_SECRET_NAME = "OPENCODE_AUTH_JSON";
@@ -1,4 +1,2 @@
1
- import { DEFAULT_MANAGED_MODEL_IDS } from '@kortix/llm-catalog';
2
- export const MANAGED_MODEL_ID_SET = new Set(DEFAULT_MANAGED_MODEL_IDS);
3
1
  export const CODEX_AUTH_JSON_SECRET_NAME = 'CODEX_AUTH_JSON';
4
2
  export const LEGACY_RUNTIME_AUTH_JSON_SECRET_NAME = 'OPENCODE_AUTH_JSON';
@@ -1,6 +1,6 @@
1
1
  import { type ModelContext } from './models-tab-row.js';
2
2
  import type { ModelGroup } from './model-rows.js';
3
- export declare function ModelsTabGroup({ group, access, enablement, defaults, pickerCatalog, canWrite, holdsDataClaim, platformFee, }: ModelContext & {
3
+ export declare function ModelsTabGroup({ group, access, enablement, defaults, canWrite, platformFee, }: ModelContext & {
4
4
  group: ModelGroup;
5
5
  platformFee: number | undefined;
6
6
  }): import("react").JSX.Element;
@@ -6,18 +6,21 @@ import { errorToast } from '../../../primitives/toast.js';
6
6
  import { ProviderLogoStatus } from '../../providers/provider-connect.js';
7
7
  import { useTranslations } from '../../../i18n/index.js';
8
8
  import { ModelsTabRow } from './models-tab-row.js';
9
- export function ModelsTabGroup({ group, access, enablement, defaults, pickerCatalog, canWrite, holdsDataClaim, platformFee, }) {
9
+ export function ModelsTabGroup({ group, access, enablement, defaults, canWrite, platformFee, }) {
10
10
  const tAccess = useTranslations('modelAccess');
11
11
  const tList = useTranslations('modelsList');
12
12
  const providerDisabled = access.data?.disabledProviders.includes(group.providerID) ?? false;
13
13
  const groupOn = group.rows.filter(({ model }) => model.enabled).length;
14
- const dataClaim = group.providerID === 'kortix' &&
15
- group.rows.every(({ model }) => holdsDataClaim(model.modelID));
14
+ // Platinum Inference serves every model it lists on zero-retention terms:
15
+ // the Kortix managed models and the Platinum provider alike.
16
+ const dataClaim = group.providerID === 'platinum' ||
17
+ (group.providerID === 'kortix' &&
18
+ group.rows.every(({ model }) => !model.modelID.includes('/')));
16
19
  return (_jsxs("section", { "data-provider-group": group.providerID, className: "bg-popover overflow-hidden rounded-lg border", children: [_jsxs("header", { className: "border-border flex items-center gap-3 border-b px-4 py-3", children: [_jsx(ProviderLogoStatus, { providerId: group.providerID, label: group.providerName, status: providerDisabled
17
20
  ? 'none'
18
21
  : group.providerID === 'kortix'
19
22
  ? 'included'
20
- : 'connected', size: "medium" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: group.providerName }), group.providerID === 'kortix' && platformFee !== undefined ? (_jsx("p", { className: "text-muted-foreground text-xs", children: tList('platformFee', { fee: platformFee }) })) : null, dataClaim ? (_jsxs("p", { className: "text-muted-foreground text-xs", children: [_jsx(Hint, { label: tAccess('zdrDescription'), side: "top", className: "max-w-xs", children: _jsx("span", { tabIndex: 0, className: "cursor-help", children: tAccess('zdrTitle') }) }), ' · ', _jsx(Hint, { label: tAccess('usProvidersDescription'), side: "top", className: "max-w-xs", children: _jsx("span", { tabIndex: 0, className: "cursor-help", children: tAccess('usProvidersTitle') }) })] })) : null] }), _jsx("span", { className: "text-muted-foreground shrink-0 text-xs tabular-nums", children: tList('onCount', { on: groupOn, total: group.rows.length }) }), _jsx(ProviderSwitch, { access: access, providerId: group.providerID, name: group.providerName, canWrite: canWrite })] }), group.rows.map(({ model, wireId, isRollingAlias }) => (_jsx(ModelsTabRow, { model: model, wireId: wireId, isRollingAlias: isRollingAlias, group: group, providerDisabled: providerDisabled, access: access, enablement: enablement, defaults: defaults, pickerCatalog: pickerCatalog, canWrite: canWrite, holdsDataClaim: holdsDataClaim }, wireId)))] }, group.providerID));
23
+ : 'connected', size: "medium" }), _jsxs("div", { className: "min-w-0 flex-1", children: [_jsx("p", { className: "text-foreground truncate text-sm font-medium", children: group.providerName }), group.providerID === 'kortix' && platformFee !== undefined ? (_jsx("p", { className: "text-muted-foreground text-xs", children: tList('platformFee', { fee: platformFee }) })) : null, dataClaim ? (_jsx("p", { className: "text-muted-foreground text-xs", children: _jsx(Hint, { label: tAccess('zdrDescription'), side: "top", className: "max-w-xs", children: _jsx("span", { tabIndex: 0, className: "cursor-help", children: tAccess('zdrTitle') }) }) })) : null] }), _jsx("span", { className: "text-muted-foreground shrink-0 text-xs tabular-nums", children: tList('onCount', { on: groupOn, total: group.rows.length }) }), _jsx(ProviderSwitch, { access: access, providerId: group.providerID, name: group.providerName, canWrite: canWrite })] }), group.rows.map(({ model, wireId, isRollingAlias }) => (_jsx(ModelsTabRow, { model: model, wireId: wireId, isRollingAlias: isRollingAlias, group: group, providerDisabled: providerDisabled, access: access, enablement: enablement, defaults: defaults, canWrite: canWrite }, wireId)))] }, group.providerID));
21
24
  }
22
25
  /**
23
26
  * A whole provider on or off for this project. Credentials stay either way.
@@ -1,28 +1,12 @@
1
- import { useModelAccess, useModelDefaults, useModelEnablement, useProjectModelPickerCatalog } from '@kortix/sdk/react';
2
- import { type ReactNode } from 'react';
1
+ import { useModelAccess, useModelDefaults, useModelEnablement } from '@kortix/sdk/react';
3
2
  import type { ModelGroup, ModelRow } from './model-rows.js';
4
3
  export type ModelContext = {
5
4
  access: ReturnType<typeof useModelAccess>;
6
5
  enablement: ReturnType<typeof useModelEnablement>;
7
6
  defaults: ReturnType<typeof useModelDefaults>;
8
- pickerCatalog: ReturnType<typeof useProjectModelPickerCatalog>;
9
7
  canWrite: boolean;
10
- holdsDataClaim: (modelID: string) => boolean;
11
8
  };
12
- export declare function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDisabled, access, enablement, defaults, pickerCatalog, canWrite, holdsDataClaim, }: ModelRow & ModelContext & {
9
+ export declare function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDisabled, access, enablement, defaults, canWrite, }: ModelRow & ModelContext & {
13
10
  group: ModelGroup;
14
11
  providerDisabled: boolean;
15
12
  }): import("react").JSX.Element;
16
- type ManagedPricingRoute = NonNullable<NonNullable<ReturnType<typeof useProjectModelPickerCatalog>>['managedPricingRoutes']>[string][number];
17
- /**
18
- * The row's price, underlined; hovering opens every serving route's price and
19
- * the data notes. The routes used to be printed as full sentences under each
20
- * managed model — four lines of numbers per row.
21
- */
22
- export declare function ModelPriceHoverCard({ modelName, routes, dataClaim, children, }: {
23
- modelName: string;
24
- routes: readonly ManagedPricingRoute[];
25
- dataClaim: boolean;
26
- children: ReactNode;
27
- }): import("react").JSX.Element;
28
- export {};
@@ -2,13 +2,11 @@
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { Badge } from '../../../primitives/badge.js';
4
4
  import Hint from '../../../primitives/hint.js';
5
- import { HoverCard, HoverCardContent, HoverCardTrigger } from '../../../primitives/hover-card.js';
6
5
  import { Switch } from '../../../primitives/switch.js';
7
6
  import { Tag } from '../../../primitives/tag.js';
8
7
  import { errorToast } from '../../../primitives/toast.js';
9
8
  import { useTranslations } from '../../../i18n/index.js';
10
9
  import { cn } from '../../../internal/lib/utils.js';
11
- import { isManagedModelId, MANAGED_ENDPOINT_PROVIDERS } from '@kortix/llm-catalog';
12
10
  import { wireToModelKey, } from '@kortix/sdk/react';
13
11
  import { CheckIcon as Check, FolderSimpleIcon as Folder, DotsThreeIcon as MoreHorizontal, StarIcon as Star, } from '@phosphor-icons/react';
14
12
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from '../../../primitives/dropdown-menu.js';
@@ -18,11 +16,11 @@ import { formatPricePerMillion, formatTokenCount } from './utils.js';
18
16
  /** A row status pill as the Models board draws it: sentence case, Roobert,
19
17
  * round. `Badge`'s default is the uppercase mono label used elsewhere. */
20
18
  const STATUS_PILL = 'rounded-full font-sans text-xs tracking-normal normal-case';
21
- export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDisabled, access, enablement, defaults, pickerCatalog, canWrite, holdsDataClaim, }) {
19
+ export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDisabled, access, enablement, defaults, canWrite, }) {
22
20
  const tAccess = useTranslations('modelAccess');
23
21
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
24
22
  const tList = useTranslations('modelsList');
25
- const isManaged = group.providerID === 'kortix' && isManagedModelId(model.modelID);
23
+ const isManaged = group.providerID === 'kortix' && !model.modelID.includes('/');
26
24
  const enabled = !!model.enabled;
27
25
  const modelDisabled = access.data?.disabledModels.includes(wireId) ?? false;
28
26
  const hiddenFromPicker = !!access.data && !enabled && !providerDisabled && !modelDisabled;
@@ -36,19 +34,8 @@ export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDis
36
34
  // provider — so this is the same comparison as the line above.
37
35
  const isAccountDefault = defaults.accountDefault?.modelID === wireId;
38
36
  const ctx = formatTokenCount(model.contextWindow);
39
- const pricingRoutes = isManaged
40
- ? pickerCatalog?.managedPricingRoutes?.[wireId]
41
- : undefined;
42
- // The row shows ONE price — the cheapest route's, as "from" —
43
- // and the hover card shows every route. The routes used to be
44
- // printed as full sentences under each managed model.
45
- const hasRoutes = !!pricingRoutes && pricingRoutes.length > 0;
46
- const priceIn = formatPricePerMillion(hasRoutes
47
- ? Math.min(...pricingRoutes.map((route) => route.input))
48
- : model.cost?.input);
49
- const priceOut = formatPricePerMillion(hasRoutes
50
- ? Math.min(...pricingRoutes.map((route) => route.output))
51
- : model.cost?.output);
37
+ const priceIn = formatPricePerMillion(model.cost?.input);
38
+ const priceOut = formatPricePerMillion(model.cost?.output);
52
39
  const turnedOff = modelDisabled || providerDisabled;
53
40
  const metaLine = hiddenFromPicker
54
41
  ? tList('hiddenLine')
@@ -62,7 +49,7 @@ export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDis
62
49
  : null,
63
50
  ctx ? tList('context', { value: ctx }) : null,
64
51
  priceIn && priceOut
65
- ? tList(hasRoutes ? 'fromPrice' : 'price', {
52
+ ? tList('price', {
66
53
  input: priceIn,
67
54
  output: priceOut,
68
55
  })
@@ -74,7 +61,7 @@ export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDis
74
61
  // A plain row, NOT a <label>: it holds several controls and a
75
62
  // label binds to the FIRST labelable one, so "click the row
76
63
  // to toggle" never did what it looked like.
77
- _jsxs("div", { "data-model-id": wireId, className: "border-border hover:bg-hover flex min-h-13 items-center gap-3 border-t px-4 py-2 transition-[background-color] duration-(--duration-normal) first-of-type:border-t-0", children: [_jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: cn('truncate text-sm font-medium', turnedOff ? 'text-muted-foreground' : 'text-foreground'), children: model.modelName }), isProjectDefault && (_jsx(Hint, { label: tI18nComplete.raw('text68cde35131cf'), side: "bottom", children: _jsx(Badge, { variant: "info", size: "sm", className: STATUS_PILL, children: tI18nComplete.raw('text5e06ae1125b5') }) })), isAccountDefault && (_jsx(Hint, { label: tI18nComplete.raw('textd197e66e9634'), side: "bottom", children: _jsx(Badge, { variant: "info", size: "sm", className: STATUS_PILL, children: tI18nComplete.raw('text071c0f5e8495') }) })), hiddenFromPicker && (_jsx(Hint, { label: tAccess('hiddenHint'), side: "bottom", children: _jsx(Badge, { variant: "muted", size: "sm", className: STATUS_PILL, children: tAccess('hidden') }) })), modelDisabled && !providerDisabled && (_jsx(Badge, { variant: "destructive", size: "sm", className: STATUS_PILL, children: tAccess('disabled') })), isRollingAlias && (_jsx(Hint, { label: tI18nComplete.raw('text7482f09d1b13'), side: "bottom", children: _jsx(Tag, { children: tI18nComplete.raw('textf096ba0a2aa6') }) }))] }), metaLine ? (hasRoutes && !hiddenFromPicker && !modelDisabled ? (_jsx(ModelPriceHoverCard, { modelName: model.modelName, routes: pricingRoutes, dataClaim: holdsDataClaim(model.modelID), children: metaLine })) : (_jsx("p", { className: "text-muted-foreground text-xs tabular-nums", children: metaLine }))) : null] }), _jsx("div", { className: "flex w-18 shrink-0 justify-end", children: _jsx(ModelCapabilityIcons, { chips: true, reasoning: model.capabilities?.reasoning, toolCall: model.capabilities?.toolcall, vision: model.capabilities?.vision }) }), canWrite && (enabled || hiddenFromPicker) ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: defaults.isUpdating, "aria-label": tI18nComplete('text43d1a2b807aa', {
64
+ _jsxs("div", { "data-model-id": wireId, className: "border-border hover:bg-hover flex min-h-13 items-center gap-3 border-t px-4 py-2 transition-[background-color] duration-(--duration-normal) first-of-type:border-t-0", children: [_jsxs("div", { className: "min-w-0 flex-1 space-y-0.5", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsx("span", { className: cn('truncate text-sm font-medium', turnedOff ? 'text-muted-foreground' : 'text-foreground'), children: model.modelName }), isProjectDefault && (_jsx(Hint, { label: tI18nComplete.raw('text68cde35131cf'), side: "bottom", children: _jsx(Badge, { variant: "info", size: "sm", className: STATUS_PILL, children: tI18nComplete.raw('text5e06ae1125b5') }) })), isAccountDefault && (_jsx(Hint, { label: tI18nComplete.raw('textd197e66e9634'), side: "bottom", children: _jsx(Badge, { variant: "info", size: "sm", className: STATUS_PILL, children: tI18nComplete.raw('text071c0f5e8495') }) })), hiddenFromPicker && (_jsx(Hint, { label: tAccess('hiddenHint'), side: "bottom", children: _jsx(Badge, { variant: "muted", size: "sm", className: STATUS_PILL, children: tAccess('hidden') }) })), modelDisabled && !providerDisabled && (_jsx(Badge, { variant: "destructive", size: "sm", className: STATUS_PILL, children: tAccess('disabled') })), isRollingAlias && (_jsx(Hint, { label: tI18nComplete.raw('text7482f09d1b13'), side: "bottom", children: _jsx(Tag, { children: tI18nComplete.raw('textf096ba0a2aa6') }) }))] }), metaLine ? (_jsx("p", { className: "text-muted-foreground text-xs tabular-nums", children: metaLine })) : null] }), _jsx("div", { className: "flex w-18 shrink-0 justify-end", children: _jsx(ModelCapabilityIcons, { chips: true, reasoning: model.capabilities?.reasoning, toolCall: model.capabilities?.toolcall, vision: model.capabilities?.vision }) }), canWrite && (enabled || hiddenFromPicker) ? (_jsxs(DropdownMenu, { children: [_jsx(DropdownMenuTrigger, { asChild: true, children: _jsx("button", { type: "button", disabled: defaults.isUpdating, "aria-label": tI18nComplete('text43d1a2b807aa', {
78
65
  value0: model.modelName,
79
66
  }), title: tI18nComplete.raw('textc3b312278ee5'), className: "text-muted-foreground hover:text-foreground hover:bg-muted data-[state=open]:bg-muted data-[state=open]:text-foreground flex size-6 shrink-0 cursor-pointer items-center justify-center rounded-md transition-[color,background-color] duration-(--duration-normal) disabled:cursor-not-allowed disabled:opacity-50", children: _jsx(MoreHorizontal, { className: "size-4" }) }) }), _jsxs(DropdownMenuContent, { align: "end", className: "w-90", children: [hiddenFromPicker && (_jsx(DropdownMenuItem, { disabled: access.isUpdating, onSelect: () => void access
80
67
  .setEnabled({ target: 'model', id: wireId, enabled: false })
@@ -96,14 +83,3 @@ export function ModelsTabRow({ model, wireId, isRollingAlias, group, providerDis
96
83
  .setEnabled({ target: 'model', id: wireId, enabled: next })
97
84
  .catch((error) => errorToast(error instanceof Error ? error.message : tAccess('modelError'))), className: "shrink-0" })] }, wireId));
98
85
  }
99
- /**
100
- * The row's price, underlined; hovering opens every serving route's price and
101
- * the data notes. The routes used to be printed as full sentences under each
102
- * managed model — four lines of numbers per row.
103
- */
104
- export function ModelPriceHoverCard({ modelName, routes, dataClaim, children, }) {
105
- const tAccess = useTranslations('modelAccess');
106
- const tList = useTranslations('modelsList');
107
- return (_jsxs(HoverCard, { openDelay: 150, children: [_jsx(HoverCardTrigger, { asChild: true, children: _jsx("button", { type: "button", className: "text-foreground decoration-muted-foreground block cursor-help text-left text-xs tabular-nums underline decoration-dotted underline-offset-3", children: children }) }), _jsxs(HoverCardContent, { align: "start", className: "w-98 space-y-3 p-4", children: [_jsxs("div", { className: "flex items-baseline justify-between gap-3", children: [_jsx("p", { className: "text-foreground text-sm font-medium tabular-nums", children: tList('perMillion', { model: modelName }) }), _jsx(Hint, { label: tAccess('pricingEstimate'), children: _jsx("span", { tabIndex: 0, className: "text-muted-foreground cursor-help text-xs", children: tList('estimate') }) })] }), _jsxs("div", { className: "space-y-1.5 text-xs tabular-nums", children: [_jsxs("div", { className: "text-muted-foreground flex gap-2", children: [_jsx("span", { className: "min-w-0 flex-1", children: tList('route') }), _jsx("span", { className: "w-13 shrink-0 text-right", children: tList('input') }), _jsx("span", { className: "w-13 shrink-0 text-right", children: tList('cached') }), _jsx("span", { className: "w-13 shrink-0 text-right", children: tList('output') })] }), routes.map((price) => (_jsxs("div", { className: "text-foreground flex gap-2", "data-pricing-route": price.route, children: [_jsxs("span", { className: "min-w-0 flex-1 truncate", children: [MANAGED_ENDPOINT_PROVIDERS[price.route] ??
108
- price.route.split('/')[0], price.role === 'preferred' ? ` · ${tAccess('pricingPreferred')}` : ''] }), _jsx("span", { className: "w-13 shrink-0 text-right", children: formatPricePerMillion(price.input) }), _jsx("span", { className: "w-13 shrink-0 text-right", children: formatPricePerMillion(price.cacheRead) }), _jsx("span", { className: "w-13 shrink-0 text-right", children: formatPricePerMillion(price.output) })] }, price.route)))] }), dataClaim ? (_jsxs("div", { className: "border-border text-muted-foreground space-y-1 border-t pt-3 text-xs text-pretty", children: [_jsxs("p", { children: [tAccess('zdrTitle'), ". ", tAccess('zdrDescription')] }), _jsxs("p", { children: [tAccess('usProvidersTitle'), ". ", tAccess('usProvidersDescription')] })] })) : null] })] }));
109
- }
@@ -1,4 +1,3 @@
1
- export { ModelPriceHoverCard } from './models-tab-row.js';
2
1
  /**
3
2
  * `search` used to be driven by the provider modal's always-on search bar,
4
3
  * which JAY-510 deleted (it sat above the tabs and filtered whichever tab
@@ -1,7 +1,6 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ModelsTabGroup } from './models-tab-group.js';
4
- export { ModelPriceHoverCard } from './models-tab-row.js';
5
4
  import { useTranslations } from '../../../i18n/index.js';
6
5
  /**
7
6
  * `ModelsTab` — "which models can this project use".
@@ -23,7 +22,6 @@ import { useTranslations } from '../../../i18n/index.js';
23
22
  */
24
23
  import { Button } from '../../../primitives/button.js';
25
24
  import { errorToast } from '../../../primitives/toast.js';
26
- import { isManagedModelId } from '@kortix/llm-catalog';
27
25
  import { useModelAccess, useModelDefaults, useModelEnablement, useProjectModelPickerCatalog, useProjectModels, } from '@kortix/sdk/react';
28
26
  import { useMemo, useRef, useState } from 'react';
29
27
  import { InputGroupSearch, InputGroupSearchClear, InputGroupSearchIcon, InputGroupSearchInput, } from '../../../primitives/input-group.js';
@@ -74,13 +72,6 @@ export function ModelsTab({ projectId, search: hostSearch, canWrite = false, })
74
72
  if (models.length === 0) {
75
73
  return (_jsxs("div", { className: "flex min-h-[200px] flex-col items-center justify-center gap-1 px-6 text-center", children: [_jsx("p", { className: "text-foreground text-sm", children: tI18nComplete.raw('textc7a9aa43558f') }), _jsx("p", { className: "text-muted-foreground max-w-xs text-xs text-pretty", children: tI18nComplete.raw('text2cedbc7a36d8') })] }));
76
74
  }
77
- // One managed model clears the ZDR + US claim when it serves from Kortix's
78
- // own managed pool. Only a Morph route breaks it; a missing price feed still
79
- // serves from the verified US ZDR pool.
80
- const holdsDataClaim = (modelID) => {
81
- const routes = pickerCatalog?.managedPricingRoutes?.[modelID];
82
- return isManagedModelId(modelID) && !routes?.some((route) => route.route === 'morph');
83
- };
84
75
  // No horizontal padding of its own: `CapabilityPageShell` supplies the page
85
76
  // column, and 20px more here indented this tab's content past the tab strip
86
77
  // that selects it.
@@ -89,6 +80,6 @@ export function ModelsTab({ projectId, search: hostSearch, canWrite = false, })
89
80
  .catch((error) => errorToast(error instanceof Error ? error.message : tAccess('resetError'))), children: tI18nComplete.raw('text5eed7e9fc8f3') })), _jsx("span", { className: "text-muted-foreground ml-auto shrink-0 text-xs tabular-nums", children: tList('onCount', { on: enabledCount, total: models.length }) })] }), groups.length === 0 ? (_jsx("div", { className: "flex min-h-[200px] items-center justify-center px-6 text-center", children: _jsx("p", { className: "text-muted-foreground text-xs", children: search
90
81
  ? tI18nComplete('textb475669b9d30', { value0: search })
91
82
  : tI18nComplete.raw('textb12da9202e8c') }) })) : (_jsx("div", { className: "space-y-6", children: groups.map((group) => {
92
- return (_jsx(ModelsTabGroup, { group: group, access: access, enablement: enablement, defaults: defaults, pickerCatalog: pickerCatalog, canWrite: canWrite, holdsDataClaim: holdsDataClaim, platformFee: platformFee }, group.providerID));
83
+ return (_jsx(ModelsTabGroup, { group: group, access: access, enablement: enablement, defaults: defaults, canWrite: canWrite, platformFee: platformFee }, group.providerID));
93
84
  }) }))] }));
94
85
  }
@@ -9,7 +9,7 @@ import { useMemo } from 'react';
9
9
  import { useTranslations } from '../../../i18n/index.js';
10
10
  import { PROJECT_ACTIONS } from '../../../internal/lib/project-actions.js';
11
11
  import { useProjectCan } from '../../../internal/lib/use-project-can.js';
12
- import { CODEX_AUTH_JSON_SECRET_NAME, LEGACY_RUNTIME_AUTH_JSON_SECRET_NAME, MANAGED_MODEL_ID_SET, } from './constants.js';
12
+ import { CODEX_AUTH_JSON_SECRET_NAME, LEGACY_RUNTIME_AUTH_JSON_SECRET_NAME, } from './constants.js';
13
13
  import { isProviderStateLoading } from './provider-loading-state.js';
14
14
  import { useLlmProviderCatalogRevision } from './use-live-catalog.js';
15
15
  import { buildCodexProvider, buildKortixManagedProvider } from './utils.js';
@@ -50,7 +50,7 @@ export function useConnectedProviders(projectId, enabled) {
50
50
  models: Object.fromEntries(Object.entries(pickerCatalog.models).filter(([, model]) => model.enabled !== false)),
51
51
  },
52
52
  ],
53
- }, MANAGED_MODEL_ID_SET, tI18nComplete.raw('texte0a770200f93'));
53
+ }, tI18nComplete.raw('texte0a770200f93'));
54
54
  }, [managedModelsEnabled, pickerCatalog, tI18nComplete]);
55
55
  const connectedProviders = useMemo(() => {
56
56
  const hasCodexSubscription = secretNames.has(CODEX_AUTH_JSON_SECRET_NAME) ||
@@ -32,7 +32,7 @@ export declare function buildCodexProvider(ocProviders: RuntimeProvidersSnapshot
32
32
  * Pure and exported so both rules are pinned by tests; the hook only wires
33
33
  * inputs in.
34
34
  */
35
- export declare function buildKortixManagedProvider(ocProviders: RuntimeProvidersSnapshot, managedModelIds: ReadonlySet<string>, includedHint: string): LlmProviderEntry | null;
35
+ export declare function buildKortixManagedProvider(ocProviders: RuntimeProvidersSnapshot, includedHint: string): LlmProviderEntry | null;
36
36
  /**
37
37
  * The managed provider row's note on the "Add a key" grid.
38
38
  *
@@ -1,4 +1,3 @@
1
- import { getManagedModel } from '@kortix/llm-catalog';
2
1
  import { CODEX_AUTH_JSON_SECRET_NAME, LEGACY_RUNTIME_AUTH_JSON_SECRET_NAME } from './constants.js';
3
2
  export function providerDisconnectPlan(provider) {
4
3
  const removesSubscription = provider.id === 'codex' || provider.id === 'openai';
@@ -72,32 +71,26 @@ export function buildCodexProvider(ocProviders, tI18nComplete) {
72
71
  * Pure and exported so both rules are pinned by tests; the hook only wires
73
72
  * inputs in.
74
73
  */
75
- export function buildKortixManagedProvider(ocProviders, managedModelIds, includedHint) {
74
+ export function buildKortixManagedProvider(ocProviders, includedHint) {
76
75
  const connectedIds = new Set(ocProviders?.connected ?? []);
77
76
  const kortix = (ocProviders?.all ?? []).find((p) => p.id === 'kortix');
78
77
  if (!kortix || !connectedIds.has('kortix'))
79
78
  return null;
80
79
  const models = [];
81
80
  for (const [id, m] of Object.entries(kortix.models ?? {})) {
82
- if (!managedModelIds.has(id))
83
- continue;
84
- // Best-effort capability/limit passthrough for the "Models in depth"
85
- // display (models-tab.tsx / catalog-tab.tsx) — the opencode provider
86
- // snapshot mirrors these when it has them; `vision`/`limit` otherwise
87
- // fall back to `@kortix/llm-catalog`'s curated managed-model table,
88
- // the canonical home for both (see MANAGED_MODELS' doc comment —
89
- // managed slugs aren't reliably on models.dev, so this table is
90
- // authoritative for them, not a guess).
81
+ // Managed = served under the `kortix` provider: what Platinum Inference
82
+ // offers. Capabilities and limits come with the served entry.
91
83
  const raw = m;
92
- const managed = getManagedModel(id);
84
+ if (raw.provider !== 'kortix' || id.includes('/'))
85
+ continue;
93
86
  models.push({
94
87
  id,
95
88
  name: (raw.name || id).replace('(latest)', '').trim(),
96
89
  released: raw.release_date ?? null,
97
90
  reasoning: raw.reasoning,
98
91
  tool_call: raw.tool_call,
99
- attachment: managed?.vision,
100
- limit: raw.limit ?? managed?.limit,
92
+ attachment: raw.attachment,
93
+ limit: raw.limit,
101
94
  });
102
95
  }
103
96
  if (models.length === 0)
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
- import { getManagedModel } from '@kortix/llm-catalog';
4
3
  import { CheckIcon as Check } from '@phosphor-icons/react';
5
4
  import { Copy } from '../../../../icons/copy.js';
6
5
  import { useTranslations } from '../../../../i18n/index.js';
@@ -17,7 +16,7 @@ export function modelLabel(id) {
17
16
  return tail.replace(/[-_]/g, ' ').replace(/\b\w/g, (c) => c.toUpperCase());
18
17
  }
19
18
  export function displayModel(id) {
20
- return getManagedModel(id)?.name ?? id.split('/').pop() ?? id;
19
+ return id.split('/').pop() ?? id;
21
20
  }
22
21
  export function tint(accent, pct) {
23
22
  return `color-mix(in oklch, ${accent} ${pct}%, transparent)`;
@@ -425,8 +425,9 @@ export function GatewayRouting({ projectId, canWrite, projectDefaultPending, })
425
425
  if (current.defaultFallback?.models.length)
426
426
  return current;
427
427
  const inherited = routing.data.effective.defaultFallback.models.filter((model) => model !== primaryModel);
428
- const preferred = models.find((model) => modelKeyToWire(model) === 'deepseek-v4.1-flash') ??
429
- models.find((model) => modelKeyToWire(model) !== primaryModel);
428
+ // No model id is hardcoded: the platform fallback already follows the
429
+ // served lineup (`inherited`); otherwise take the first other model.
430
+ const preferred = models.find((model) => modelKeyToWire(model) !== primaryModel);
430
431
  return {
431
432
  ...current,
432
433
  defaultFallback: {
@@ -8,19 +8,14 @@ export type SandboxProviderMode = 'automatic' | 'pinned';
8
8
  export declare function SandboxProviderLabel({ provider }: {
9
9
  provider: string;
10
10
  }): import("react").JSX.Element;
11
- export declare function describeProviderMode(mode: SandboxProviderMode, selectedProvider: SandboxProvider | null, tI18nComplete: UiTranslator): {
12
- label: string;
13
- selectedProvider: string | null;
14
- };
15
11
  export declare function describeProviderCoverage(status: ProviderCoverageStatus, tI18nComplete: UiTranslator): {
16
12
  label: string;
17
13
  tone: 'ok' | 'busy' | 'fail' | 'idle';
18
14
  };
19
15
  export interface SandboxProviderBadgeCopy {
20
16
  states?: Partial<Record<ProviderCoverageStatus, string>>;
21
- selected?: string;
22
- selectedAria?: string;
23
17
  }
18
+ /** Every sandbox runs on Platinum: a deprecated provider's coverage is not shown. */
24
19
  export declare function availableProviderCoverage(coverage: SandboxTemplate['provider_coverage'] | null | undefined): ProviderCoverageEntry[];
25
20
  /**
26
21
  * The freshest observation across every routable provider, or `null` when
@@ -34,19 +29,9 @@ export declare function latestObservedAt(coverage: SandboxTemplate['provider_cov
34
29
  *
35
30
  * It replaces `SandboxTemplateProviderCoverage`, which baked its own
36
31
  * "Session runtime" label, its badge list and its "Checked …" stamp into one
37
- * `flex` block. That composite could only ever be dropped into a layout whole,
38
- * so `sandbox-tab.tsx` ended up nesting a labelled row inside another labelled
39
- * row — two labels and an un-positionable timestamp in a single wrapping
40
- * footer. Owning just the badge lets the caller build the row.
41
- *
42
- * `SandboxTemplateProviderModeBadge` is gone with it: routing mode is static
43
- * configuration, not live status, so the card states it as a labelled fact in
44
- * the spec grid (`describeProviderMode` still supplies the words) rather than
45
- * floating a bare badge in the runtime strip.
32
+ * `flex` block. Owning just the badge lets the caller build the row.
46
33
  */
47
- export declare function SandboxProviderBadge({ item, selected, copy, }: {
34
+ export declare function SandboxProviderBadge({ item, copy, }: {
48
35
  item: ProviderCoverageEntry;
49
- /** Pinned mode only — marks the provider sessions actually route to. */
50
- selected?: boolean;
51
36
  copy?: SandboxProviderBadgeCopy;
52
37
  }): import("react").JSX.Element;
@@ -1,21 +1,12 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useTranslations } from '../../../i18n/index.js';
3
3
  import { Badge } from '../../../primitives/badge.js';
4
- import { formatSandboxProvider, isLegacySandboxProvider, sandboxProviderRank, } from '../../../internal/lib/sandbox-providers.js';
4
+ import { formatSandboxProvider, isLegacySandboxProvider, } from '../../../internal/lib/sandbox-providers.js';
5
5
  /** A provider's name, with a `Legacy` badge for the legacy fallback provider (Daytona). */
6
6
  export function SandboxProviderLabel({ provider }) {
7
7
  const t = useTranslations('settings.sandbox');
8
8
  return (_jsxs("span", { className: "inline-flex items-center gap-1.5", children: [formatSandboxProvider(provider), isLegacySandboxProvider(provider) ? (_jsx(Badge, { variant: "muted", size: "sm", children: t('provider.legacy') })) : null] }));
9
9
  }
10
- export function describeProviderMode(mode, selectedProvider, tI18nComplete) {
11
- if (mode === 'automatic')
12
- return { label: tI18nComplete.raw('textd461a493a375'), selectedProvider: null };
13
- const selected = selectedProvider ? formatSandboxProvider(selectedProvider) : null;
14
- return {
15
- label: tI18nComplete.raw('text3b3c31642567'),
16
- selectedProvider: selected,
17
- };
18
- }
19
10
  export function describeProviderCoverage(status, tI18nComplete) {
20
11
  switch (status) {
21
12
  case 'ready':
@@ -32,10 +23,9 @@ export function describeProviderCoverage(status, tI18nComplete) {
32
23
  return { label: tI18nComplete.raw('textb764cdc0eab7'), tone: 'idle' };
33
24
  }
34
25
  }
26
+ /** Every sandbox runs on Platinum: a deprecated provider's coverage is not shown. */
35
27
  export function availableProviderCoverage(coverage) {
36
- return (coverage ?? [])
37
- .filter((item) => item.available)
38
- .sort((a, b) => sandboxProviderRank(a.provider) - sandboxProviderRank(b.provider));
28
+ return (coverage ?? []).filter((item) => item.available && !isLegacySandboxProvider(item.provider));
39
29
  }
40
30
  function providerCoverageVariant(tone) {
41
31
  if (tone === 'ok')
@@ -64,22 +54,12 @@ export function latestObservedAt(coverage) {
64
54
  *
65
55
  * It replaces `SandboxTemplateProviderCoverage`, which baked its own
66
56
  * "Session runtime" label, its badge list and its "Checked …" stamp into one
67
- * `flex` block. That composite could only ever be dropped into a layout whole,
68
- * so `sandbox-tab.tsx` ended up nesting a labelled row inside another labelled
69
- * row — two labels and an un-positionable timestamp in a single wrapping
70
- * footer. Owning just the badge lets the caller build the row.
71
- *
72
- * `SandboxTemplateProviderModeBadge` is gone with it: routing mode is static
73
- * configuration, not live status, so the card states it as a labelled fact in
74
- * the spec grid (`describeProviderMode` still supplies the words) rather than
75
- * floating a bare badge in the runtime strip.
57
+ * `flex` block. Owning just the badge lets the caller build the row.
76
58
  */
77
- export function SandboxProviderBadge({ item, selected = false, copy = {}, }) {
59
+ export function SandboxProviderBadge({ item, copy = {}, }) {
78
60
  const tI18nComplete = useTranslations('hardcodedUi.i18nComplete');
79
- const t = useTranslations('settings.sandbox');
80
- const legacy = isLegacySandboxProvider(item.provider);
81
61
  const state = describeProviderCoverage(item.status, tI18nComplete);
82
62
  const stateLabel = copy.states?.[item.status] ?? state.label;
83
63
  const provider = formatSandboxProvider(item.provider);
84
- return (_jsxs(Badge, { variant: providerCoverageVariant(state.tone), size: "xs", "aria-label": `${provider}${legacy ? ` ${t('provider.legacy')}` : ''}${selected ? ` ${copy.selectedAria ?? 'selected'}` : ''}: ${stateLabel}`, children: [provider, legacy ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "opacity-50", "aria-hidden": "true", children: tI18nComplete.raw('text3b9453dad42b') }), t('provider.legacy')] })) : null, selected ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "opacity-50", "aria-hidden": "true", children: tI18nComplete.raw('text3b9453dad42b') }), copy.selected ?? 'Selected'] })) : null, _jsx("span", { className: "opacity-50", "aria-hidden": "true", children: tI18nComplete.raw('text3b9453dad42b') }), stateLabel] }));
64
+ return (_jsxs(Badge, { variant: providerCoverageVariant(state.tone), size: "xs", "aria-label": `${provider}: ${stateLabel}`, children: [provider, _jsx("span", { className: "opacity-50", "aria-hidden": "true", children: tI18nComplete.raw('text3b9453dad42b') }), stateLabel] }));
85
65
  }
@@ -1,8 +1,6 @@
1
1
  import { type ReactNode } from 'react';
2
- import type { UiTranslator } from '../../../i18n/index.js';
3
2
  import { type SandboxTemplate } from '@kortix/sdk';
4
3
  import { type Icon as PhosphorIcon } from '@phosphor-icons/react';
5
- import { type SandboxProvider, type SandboxProviderMode } from '../../sections/view/sandbox-provider-coverage.js';
6
4
  type TemplateTone = 'ok' | 'busy' | 'fail' | 'idle';
7
5
  /** Exported for test only — `TemplateCard` itself owns mutations and cannot
8
6
  * render under `renderToStaticMarkup`, so the presentation decisions are
@@ -36,27 +34,13 @@ export declare function describeBase(template: SandboxTemplate, labels?: {
36
34
  icon: PhosphorIcon;
37
35
  mono: boolean;
38
36
  };
39
- /** Where sessions built from this template actually run. Static configuration,
40
- * not live status — which is why it belongs in the spec grid next to CPU and
41
- * memory, and NOT in the runtime footer where a bare `Automatic` badge used
42
- * to float with no label attached to it. */
43
- export declare function describeRouting(providerMode: SandboxProviderMode, selectedProvider: SandboxProvider | null, tI18nComplete: UiTranslator, labels?: {
44
- automatic?: string;
45
- pinned?: string;
46
- pinnedWithProvider?: (provider: string) => string;
47
- }): {
48
- label: string;
49
- icon: PhosphorIcon;
50
- };
51
37
  /** Locale-aware age for the runtime footer's "Checked …" stamp. */
52
38
  export declare function TemplateStateLine({ tone, label }: {
53
39
  tone: TemplateTone;
54
40
  label: string;
55
41
  }): import("react").JSX.Element;
56
- export declare function TemplateRuntimeFooter({ coverage, providerMode, selectedProvider, formatObservedAt, runtimeLabel, checkedLabel, badgeCopy, }: {
42
+ export declare function TemplateRuntimeFooter({ coverage, formatObservedAt, runtimeLabel, checkedLabel, badgeCopy, }: {
57
43
  coverage: SandboxTemplate['provider_coverage'] | null | undefined;
58
- providerMode: SandboxProviderMode;
59
- selectedProvider: SandboxProvider | null;
60
44
  formatObservedAt: (observedAt: string) => string;
61
45
  runtimeLabel: string;
62
46
  checkedLabel: (value: string) => string;
@@ -71,15 +55,6 @@ export interface SandboxTabViewProps {
71
55
  * by the container from the same `canManage` gate as everything else in
72
56
  * this tab. `undefined` (not rendered) for a non-manager. */
73
57
  headerAction?: ReactNode;
74
- /** `SandboxProviderRow` — the per-project provider pin, moved here from
75
- * `general-tab.tsx`. A slot for the same reason `templatesSlot` is one: it
76
- * owns a `useMutation`/`useQueryClient` and cannot render under
77
- * `renderToStaticMarkup` with no `QueryClientProvider`.
78
- *
79
- * Rendered ABOVE the loading/error branch on purpose. It is fed by the
80
- * PROJECT query, not the snapshots query, so a slow or failed template read
81
- * must not hide a control that has nothing to do with it. */
82
- sandboxProviderSlot?: ReactNode;
83
58
  /** `1 | 2 | null` — drives whether the manifest hint below reads
84
59
  * `kortix.toml` or `kortix.yaml`. `null`/anything else falls back to
85
60
  * `kortix.toml`, matching `sandbox-view.tsx`'s own ternary default. */
@@ -110,7 +85,7 @@ export interface SandboxTabViewProps {
110
85
  * read. Kept separate from `SandboxTab` so this renders under
111
86
  * `renderToStaticMarkup` with no `QueryClientProvider` — see
112
87
  * `GeneralTabView` for the same split. */
113
- export declare function SandboxTabView({ isLoading, isError, errorMessage, onRetry, headerAction, sandboxProviderSlot, manifestVersion, templatesError, isEmpty, emptyAction, templatesSlot, copy: copyOverride, }: SandboxTabViewProps): import("react").JSX.Element;
88
+ export declare function SandboxTabView({ isLoading, isError, errorMessage, onRetry, headerAction, manifestVersion, templatesError, isEmpty, emptyAction, templatesSlot, copy: copyOverride, }: SandboxTabViewProps): import("react").JSX.Element;
114
89
  /** Container: owns every hook (project + snapshots queries, the template
115
90
  * form's open/edit state) and renders `SandboxTabView` with real data +
116
91
  * the templates slot, plus `SandboxTemplateForm` as a sibling (it owns its