@opengeni/react 6.0.0-canary.0 → 6.1.0-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/capability-catalog-row.d.ts +20 -0
- package/dist/{chunk-VRBL3F5E.js → chunk-5GAL23FF.js} +10 -2
- package/dist/{chunk-VRBL3F5E.js.map → chunk-5GAL23FF.js.map} +1 -1
- package/dist/{chunk-IHQII5WO.js → chunk-G5JFM6ZE.js} +29 -4
- package/dist/chunk-G5JFM6ZE.js.map +1 -0
- package/dist/{chunk-UMECRJRV.js → chunk-HJ5VJ4JB.js} +21 -12
- package/dist/chunk-HJ5VJ4JB.js.map +1 -0
- package/dist/{chunk-R4KRSFGA.js → chunk-IP4BUBKH.js} +3 -3
- package/dist/{chunk-CC7AVRIJ.js → chunk-KZ4QDBGM.js} +87 -14
- package/dist/chunk-KZ4QDBGM.js.map +1 -0
- package/dist/{chunk-PZ27MCZP.js → chunk-L5VJYZIW.js} +34 -4
- package/dist/chunk-L5VJYZIW.js.map +1 -0
- package/dist/{chunk-45XEUVTK.js → chunk-UWVY3OXF.js} +12 -5
- package/dist/chunk-UWVY3OXF.js.map +1 -0
- package/dist/components/message-timeline.d.ts +2 -2
- package/dist/composer.js +4 -4
- package/dist/connect-panel.d.ts +2 -1
- package/dist/connect-setup.d.ts +3 -1
- package/dist/connect.d.ts +1 -0
- package/dist/connect.js +620 -424
- package/dist/connect.js.map +1 -1
- package/dist/connection-catalog.d.ts +6 -1
- package/dist/connection-installed.d.ts +1 -0
- package/dist/connection-logo.d.ts +4 -2
- package/dist/discovery-cache.d.ts +5 -0
- package/dist/index.js +7 -7
- package/dist/{model-policy-picker-menu-3R336DP2.js → model-policy-picker-menu-RVOQ4ZZG.js} +4 -4
- package/dist/model-policy.d.ts +6 -2
- package/dist/model-policy.js +1 -1
- package/dist/plugin-discovery.d.ts +4 -0
- package/dist/realtime.js +2 -2
- package/dist/session-ui.js +6 -6
- package/dist/session.js +1 -1
- package/dist/skill-discovery.d.ts +11 -0
- package/dist/timeline/projection.d.ts +3 -1
- package/dist/timeline/turn-summary.d.ts +2 -2
- package/package.json +3 -3
- package/src/capability-catalog-row.tsx +94 -0
- package/src/components/markdown.tsx +32 -3
- package/src/components/message-timeline.tsx +88 -4
- package/src/components/model-picker.tsx +11 -3
- package/src/components/model-policy-picker-menu.tsx +10 -3
- package/src/components/session-conversation.tsx +1 -3
- package/src/components/session-status.tsx +2 -2
- package/src/connect-panel.tsx +19 -8
- package/src/connect-setup.tsx +49 -14
- package/src/connect.ts +6 -0
- package/src/connection-catalog.tsx +61 -39
- package/src/connection-installed.tsx +2 -0
- package/src/connection-logo.tsx +14 -10
- package/src/discovery-cache.ts +42 -0
- package/src/hooks/use-session-events.ts +24 -2
- package/src/model-policy.ts +21 -0
- package/src/plugin-details.tsx +8 -5
- package/src/plugin-discovery.tsx +87 -71
- package/src/skill-discovery.tsx +101 -56
- package/src/timeline/projection.ts +31 -1
- package/src/timeline/turn-summary.tsx +3 -3
- package/styles/compiled.css +27 -0
- package/styles/connect.css +56 -15
- package/dist/chunk-45XEUVTK.js.map +0 -1
- package/dist/chunk-CC7AVRIJ.js.map +0 -1
- package/dist/chunk-IHQII5WO.js.map +0 -1
- package/dist/chunk-PZ27MCZP.js.map +0 -1
- package/dist/chunk-UMECRJRV.js.map +0 -1
- /package/dist/{chunk-R4KRSFGA.js.map → chunk-IP4BUBKH.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-3R336DP2.js.map → model-policy-picker-menu-RVOQ4ZZG.js.map} +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
|
+
import { type CapabilityCatalogStatus } from "./capability-catalog-row.js";
|
|
2
3
|
/** Status comes from the host/controller, never inferred from a display label. */
|
|
3
4
|
export type ConnectionCatalogOption = {
|
|
4
5
|
id: string;
|
|
@@ -6,6 +7,8 @@ export type ConnectionCatalogOption = {
|
|
|
6
7
|
description?: string | undefined;
|
|
7
8
|
status: string;
|
|
8
9
|
connected: boolean;
|
|
10
|
+
/** Explicit health/availability. Omit to retain visible legacy status text. */
|
|
11
|
+
state?: CapabilityCatalogStatus;
|
|
9
12
|
onOpen: () => void;
|
|
10
13
|
action?: ReactNode;
|
|
11
14
|
};
|
|
@@ -19,6 +22,8 @@ export type ConnectionCatalogProps = {
|
|
|
19
22
|
services: ConnectionCatalogService[];
|
|
20
23
|
query?: string;
|
|
21
24
|
columns?: 1 | 2;
|
|
25
|
+
resultLimit?: number;
|
|
26
|
+
onShowMore?: () => void;
|
|
22
27
|
/** Group capabilities by service, or show every capability independently. */
|
|
23
28
|
grouped?: boolean;
|
|
24
29
|
className?: string;
|
|
@@ -30,4 +35,4 @@ export declare function ConnectionOptionRow({ option }: {
|
|
|
30
35
|
export declare function ConnectionServiceRow({ service }: {
|
|
31
36
|
service: ConnectionCatalogService;
|
|
32
37
|
}): import("react").JSX.Element | null;
|
|
33
|
-
export declare function ConnectionCatalog({ services, query, columns, grouped, className, emptyMessage, }: ConnectionCatalogProps): import("react").JSX.Element;
|
|
38
|
+
export declare function ConnectionCatalog({ services, query, columns, grouped, resultLimit, onShowMore, className, emptyMessage, }: ConnectionCatalogProps): import("react").JSX.Element;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/** Brand identifier; failed/missing assets use the supplied fallback or stable initials. */
|
|
3
|
+
export declare function ConnectionLogo({ src, name, size, fallback, }: {
|
|
3
4
|
src: string | null;
|
|
4
5
|
name: string;
|
|
5
6
|
size?: number;
|
|
7
|
+
fallback?: ReactNode;
|
|
6
8
|
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** Short-lived presentation data only. Never cache installation or authorization state. */
|
|
2
|
+
export declare function createDiscoveryCache<T>(ttlMs?: number, capacity?: number): {
|
|
3
|
+
peek(client: object, key: string): T | undefined;
|
|
4
|
+
read(client: object, key: string, fetch: () => Promise<T>): Promise<T>;
|
|
5
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -44,7 +44,7 @@ import {
|
|
|
44
44
|
} from "./chunk-5YK2ZDIG.js";
|
|
45
45
|
import {
|
|
46
46
|
ModelPolicyPickerMenu
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-HJ5VJ4JB.js";
|
|
48
48
|
import {
|
|
49
49
|
ActivityDisclosure,
|
|
50
50
|
ActivityRail,
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
useWorkspaceModelCatalog,
|
|
109
109
|
userMessageLikelyNeedsDisclosure,
|
|
110
110
|
v4aToGitFileDiff
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-KZ4QDBGM.js";
|
|
112
112
|
import "./chunk-YBM6CQRU.js";
|
|
113
113
|
import "./chunk-5M6VTFJ5.js";
|
|
114
114
|
import "./chunk-ZYRGEKWS.js";
|
|
@@ -124,7 +124,7 @@ import {
|
|
|
124
124
|
retainedImageId,
|
|
125
125
|
sandboxFileLocationFromHref,
|
|
126
126
|
sandboxFilePathFromHref
|
|
127
|
-
} from "./chunk-
|
|
127
|
+
} from "./chunk-G5JFM6ZE.js";
|
|
128
128
|
import {
|
|
129
129
|
copyTextToClipboard,
|
|
130
130
|
tableElementToTsv
|
|
@@ -164,7 +164,7 @@ import {
|
|
|
164
164
|
useSlashCommands,
|
|
165
165
|
useTranscription,
|
|
166
166
|
useVoiceInput
|
|
167
|
-
} from "./chunk-
|
|
167
|
+
} from "./chunk-UWVY3OXF.js";
|
|
168
168
|
import {
|
|
169
169
|
Tooltip,
|
|
170
170
|
TooltipContent,
|
|
@@ -179,7 +179,7 @@ import {
|
|
|
179
179
|
PickerBackHeader,
|
|
180
180
|
PickerNavRow,
|
|
181
181
|
defaultModelPolicyPickerMessages
|
|
182
|
-
} from "./chunk-
|
|
182
|
+
} from "./chunk-IP4BUBKH.js";
|
|
183
183
|
import {
|
|
184
184
|
usePortalTokenSource,
|
|
185
185
|
usePortalTokenStyle
|
|
@@ -219,7 +219,7 @@ import {
|
|
|
219
219
|
useSession,
|
|
220
220
|
useSessionEvents,
|
|
221
221
|
useTurnQueue
|
|
222
|
-
} from "./chunk-
|
|
222
|
+
} from "./chunk-L5VJYZIW.js";
|
|
223
223
|
import {
|
|
224
224
|
FILE_ONLY_MESSAGE_TEXT,
|
|
225
225
|
composeSendInput,
|
|
@@ -269,7 +269,7 @@ import {
|
|
|
269
269
|
projectPickerRows,
|
|
270
270
|
runnableLatencyModesForModel,
|
|
271
271
|
sortPickerRows
|
|
272
|
-
} from "./chunk-
|
|
272
|
+
} from "./chunk-5GAL23FF.js";
|
|
273
273
|
import {
|
|
274
274
|
cn
|
|
275
275
|
} from "./chunk-22KP6LR5.js";
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ModelPolicyPickerMenu,
|
|
3
3
|
ModelPolicyPickerPopover
|
|
4
|
-
} from "./chunk-
|
|
5
|
-
import "./chunk-
|
|
4
|
+
} from "./chunk-HJ5VJ4JB.js";
|
|
5
|
+
import "./chunk-IP4BUBKH.js";
|
|
6
6
|
import "./chunk-VZTQ73XT.js";
|
|
7
|
-
import "./chunk-
|
|
7
|
+
import "./chunk-5GAL23FF.js";
|
|
8
8
|
import "./chunk-22KP6LR5.js";
|
|
9
9
|
export {
|
|
10
10
|
ModelPolicyPickerMenu,
|
|
11
11
|
ModelPolicyPickerPopover
|
|
12
12
|
};
|
|
13
|
-
//# sourceMappingURL=model-policy-picker-menu-
|
|
13
|
+
//# sourceMappingURL=model-policy-picker-menu-RVOQ4ZZG.js.map
|
package/dist/model-policy.d.ts
CHANGED
|
@@ -33,12 +33,16 @@ export declare function projectPickerRows(models: WorkspaceModelCatalogModel[]):
|
|
|
33
33
|
export declare function projectClientModelRows(models: ClientModel[]): PickerModelRow<ClientModel>[];
|
|
34
34
|
export declare function sortPickerRows<TCatalog extends ClientModel>(rows: PickerModelRow<TCatalog>[]): PickerModelRow<TCatalog>[];
|
|
35
35
|
export declare function findPickerRow<TCatalog extends ClientModel>(rows: PickerModelRow<TCatalog>[], modelId: string): PickerModelRow<TCatalog> | null;
|
|
36
|
-
export declare function groupPickerRowsByBillingClass(rows: PickerModelRow[]
|
|
36
|
+
export declare function groupPickerRowsByBillingClass(rows: PickerModelRow[], options?: {
|
|
37
|
+
codexOnly?: boolean;
|
|
38
|
+
}): Array<{
|
|
37
39
|
billingClass: PickerBillingClass;
|
|
38
40
|
label: string;
|
|
39
41
|
rows: PickerModelRow[];
|
|
40
42
|
}>;
|
|
41
|
-
export declare function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(rows: PickerModelRow<TCatalog>[]
|
|
43
|
+
export declare function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(rows: PickerModelRow<TCatalog>[], options?: {
|
|
44
|
+
codexOnly?: boolean;
|
|
45
|
+
}): Array<{
|
|
42
46
|
billingClass: PickerBillingClass;
|
|
43
47
|
label: string;
|
|
44
48
|
rows: PickerModelRow<TCatalog>[];
|
package/dist/model-policy.js
CHANGED
|
@@ -9,6 +9,10 @@ export type PluginDiscoveryProps = {
|
|
|
9
9
|
};
|
|
10
10
|
workspaceId: string;
|
|
11
11
|
query: string;
|
|
12
|
+
/** Discovery identities already installed in the current workspace. */
|
|
13
|
+
installedIds?: ReadonlySet<string>;
|
|
14
|
+
resultLimit?: number;
|
|
15
|
+
onShowMore?: () => void;
|
|
12
16
|
onOpen: (item: PluginDiscoveryItem) => void;
|
|
13
17
|
};
|
|
14
18
|
export declare function PluginDiscovery(props: PluginDiscoveryProps): import("react").JSX.Element;
|
package/dist/realtime.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
PickerAnimatedPage,
|
|
4
4
|
PickerBackHeader,
|
|
5
5
|
PickerNavRow
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-IP4BUBKH.js";
|
|
7
7
|
import {
|
|
8
8
|
usePortalTokenSource,
|
|
9
9
|
usePortalTokenStyle
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
import {
|
|
12
12
|
useEmbeddedRealtimeSession
|
|
13
13
|
} from "./chunk-NEXLLTP4.js";
|
|
14
|
-
import "./chunk-
|
|
14
|
+
import "./chunk-5GAL23FF.js";
|
|
15
15
|
import {
|
|
16
16
|
cn
|
|
17
17
|
} from "./chunk-22KP6LR5.js";
|
package/dist/session-ui.js
CHANGED
|
@@ -18,27 +18,27 @@ import {
|
|
|
18
18
|
setStartupDetails,
|
|
19
19
|
useStartupDetails,
|
|
20
20
|
userMessageLikelyNeedsDisclosure
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-KZ4QDBGM.js";
|
|
22
22
|
import "./chunk-YBM6CQRU.js";
|
|
23
23
|
import "./chunk-5M6VTFJ5.js";
|
|
24
24
|
import "./chunk-ZYRGEKWS.js";
|
|
25
25
|
import "./chunk-NOSYBS2D.js";
|
|
26
26
|
import "./chunk-PAJU5KKL.js";
|
|
27
|
-
import "./chunk-
|
|
27
|
+
import "./chunk-G5JFM6ZE.js";
|
|
28
28
|
import "./chunk-JVYQRRC4.js";
|
|
29
|
-
import "./chunk-
|
|
29
|
+
import "./chunk-UWVY3OXF.js";
|
|
30
30
|
import "./chunk-HQV2I5HV.js";
|
|
31
31
|
import "./chunk-WYWRYUNQ.js";
|
|
32
|
-
import "./chunk-
|
|
32
|
+
import "./chunk-IP4BUBKH.js";
|
|
33
33
|
import "./chunk-VZTQ73XT.js";
|
|
34
34
|
import {
|
|
35
35
|
createOlderHistoryLoadReceipt
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-L5VJYZIW.js";
|
|
37
37
|
import "./chunk-NYESIADM.js";
|
|
38
38
|
import "./chunk-PLYRL5ER.js";
|
|
39
39
|
import "./chunk-KZ73RL76.js";
|
|
40
40
|
import "./chunk-NEXLLTP4.js";
|
|
41
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-5GAL23FF.js";
|
|
42
42
|
import "./chunk-22KP6LR5.js";
|
|
43
43
|
export {
|
|
44
44
|
ApprovalSurface,
|
package/dist/session.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CapabilityCatalogStatus } from "./capability-catalog-row.js";
|
|
1
2
|
export type SkillDiscoveryItem = {
|
|
2
3
|
id: string;
|
|
3
4
|
name: string;
|
|
@@ -23,6 +24,16 @@ export type SkillDiscoveryProps = {
|
|
|
23
24
|
query: string;
|
|
24
25
|
onImport: (url: string) => void;
|
|
25
26
|
canManage: boolean;
|
|
27
|
+
localSkills?: readonly {
|
|
28
|
+
id: string;
|
|
29
|
+
name: string;
|
|
30
|
+
description?: string;
|
|
31
|
+
status?: CapabilityCatalogStatus;
|
|
32
|
+
statusLabel?: string;
|
|
33
|
+
onOpen: () => void;
|
|
34
|
+
}[];
|
|
35
|
+
resultLimit?: number;
|
|
36
|
+
onShowMore?: () => void;
|
|
26
37
|
onSearch?: (() => void) | undefined;
|
|
27
38
|
};
|
|
28
39
|
/** Host-owned query and import flow; browse presentation is reusable independently. */
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { type SessionEvent, type SessionStatus } from "@opengeni/sdk";
|
|
2
2
|
import type { TimelineGroup, TimelineItem } from "./types.js";
|
|
3
3
|
export { toolDisplayName, mcpToolLeaf, toolMatchesLeaf } from "./tool-display-name.js";
|
|
4
|
-
export declare function buildTimeline(events: SessionEvent[]
|
|
4
|
+
export declare function buildTimeline(events: SessionEvent[], options?: {
|
|
5
|
+
partialStart?: boolean;
|
|
6
|
+
}): TimelineItem[];
|
|
5
7
|
/**
|
|
6
8
|
* Codex subscription web search can return private citation handles without
|
|
7
9
|
* the URL annotation table that would make them resolvable. Keep the canonical
|
|
@@ -65,8 +65,8 @@ export type TurnSummaryProps = {
|
|
|
65
65
|
items: ActivityItem[];
|
|
66
66
|
/**
|
|
67
67
|
* The settled verdict — or absent for a completed CLUSTER of a still-running
|
|
68
|
-
* turn, which folds neutrally
|
|
69
|
-
*
|
|
68
|
+
* turn, which folds neutrally. Only unfinished activity inside this summary
|
|
69
|
+
* animates; the parent turn may still be composing a response elsewhere.
|
|
70
70
|
*/
|
|
71
71
|
outcome?: TurnOutcome | undefined;
|
|
72
72
|
/** A short failure reason shown inline on a failed chip (never hidden). */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.1.0-canary.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -133,8 +133,8 @@
|
|
|
133
133
|
"@dnd-kit/utilities": "3.2.2",
|
|
134
134
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
135
135
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
136
|
-
"@opengeni/connect": "^0.2.0-canary.
|
|
137
|
-
"@opengeni/sdk": "^6.
|
|
136
|
+
"@opengeni/connect": "^0.2.0-canary.11",
|
|
137
|
+
"@opengeni/sdk": "^6.1.0-canary.0",
|
|
138
138
|
"clsx": "^2.1.1",
|
|
139
139
|
"dequal": "2.0.3",
|
|
140
140
|
"lucide-react": "^1.8.0",
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ComponentPropsWithoutRef, ReactNode } from "react";
|
|
2
|
+
import { AlertTriangleIcon, CheckIcon, Loader2Icon, PlusIcon } from "lucide-react";
|
|
3
|
+
|
|
4
|
+
export type CapabilityCatalogStatus =
|
|
5
|
+
| "available"
|
|
6
|
+
| "added"
|
|
7
|
+
| "attention"
|
|
8
|
+
| "unavailable"
|
|
9
|
+
| "loading";
|
|
10
|
+
|
|
11
|
+
export type CapabilityCatalogRowProps = Omit<
|
|
12
|
+
ComponentPropsWithoutRef<"button">,
|
|
13
|
+
"children" | "onClick" | "name"
|
|
14
|
+
> & {
|
|
15
|
+
name: string;
|
|
16
|
+
description?: string | undefined;
|
|
17
|
+
icon?: ReactNode;
|
|
18
|
+
status?: CapabilityCatalogStatus;
|
|
19
|
+
statusLabel?: string | undefined;
|
|
20
|
+
/** Preserve opaque host status text when adopting this row without typed health. */
|
|
21
|
+
showStatusLabel?: boolean;
|
|
22
|
+
onOpen: () => void;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
const labels: Record<CapabilityCatalogStatus, string> = {
|
|
26
|
+
available: "Available to add",
|
|
27
|
+
added: "Added",
|
|
28
|
+
attention: "Needs attention",
|
|
29
|
+
unavailable: "Unavailable",
|
|
30
|
+
loading: "Working",
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** A display-only state. The entire catalog row is the action, never this glyph. */
|
|
34
|
+
export function CapabilityCatalogIndicator({
|
|
35
|
+
status = "available",
|
|
36
|
+
label,
|
|
37
|
+
showLabel = false,
|
|
38
|
+
}: {
|
|
39
|
+
status?: CapabilityCatalogStatus;
|
|
40
|
+
label?: string | undefined;
|
|
41
|
+
showLabel?: boolean;
|
|
42
|
+
}) {
|
|
43
|
+
const Icon =
|
|
44
|
+
status === "added"
|
|
45
|
+
? CheckIcon
|
|
46
|
+
: status === "attention" || status === "unavailable"
|
|
47
|
+
? AlertTriangleIcon
|
|
48
|
+
: status === "loading"
|
|
49
|
+
? Loader2Icon
|
|
50
|
+
: PlusIcon;
|
|
51
|
+
return (
|
|
52
|
+
<span className="og-capability-catalog-indicator" data-status={status}>
|
|
53
|
+
<Icon aria-hidden="true" />
|
|
54
|
+
<span
|
|
55
|
+
className={
|
|
56
|
+
showLabel || status === "attention" || status === "unavailable"
|
|
57
|
+
? "og-capability-catalog-notice"
|
|
58
|
+
: "og-capability-catalog-sr-only"
|
|
59
|
+
}
|
|
60
|
+
>
|
|
61
|
+
{label ?? labels[status]}
|
|
62
|
+
</span>
|
|
63
|
+
</span>
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Shared by connections, skills, and plugins: one row, one focus target, one action. */
|
|
68
|
+
export function CapabilityCatalogRow({
|
|
69
|
+
name,
|
|
70
|
+
description,
|
|
71
|
+
icon,
|
|
72
|
+
status = "available",
|
|
73
|
+
statusLabel,
|
|
74
|
+
showStatusLabel = false,
|
|
75
|
+
onOpen,
|
|
76
|
+
className = "",
|
|
77
|
+
...props
|
|
78
|
+
}: CapabilityCatalogRowProps) {
|
|
79
|
+
return (
|
|
80
|
+
<button
|
|
81
|
+
{...props}
|
|
82
|
+
type="button"
|
|
83
|
+
className={`og-capability-catalog-row ${className}`}
|
|
84
|
+
onClick={onOpen}
|
|
85
|
+
>
|
|
86
|
+
{icon ? <span className="og-capability-catalog-icon">{icon}</span> : null}
|
|
87
|
+
<span className="og-capability-catalog-copy">
|
|
88
|
+
<strong>{name}</strong>
|
|
89
|
+
{description ? <span>{description}</span> : null}
|
|
90
|
+
</span>
|
|
91
|
+
<CapabilityCatalogIndicator status={status} label={statusLabel} showLabel={showStatusLabel} />
|
|
92
|
+
</button>
|
|
93
|
+
);
|
|
94
|
+
}
|
|
@@ -19,6 +19,7 @@ import ReactMarkdown, {
|
|
|
19
19
|
type UrlTransform,
|
|
20
20
|
} from "react-markdown";
|
|
21
21
|
import remarkGfm from "remark-gfm";
|
|
22
|
+
import { LoaderCircleIcon } from "lucide-react";
|
|
22
23
|
import { cn } from "../lib/cn";
|
|
23
24
|
import { tableElementToTsv } from "../lib/clipboard";
|
|
24
25
|
import { prefersReducedMotion } from "../lib/motion";
|
|
@@ -46,6 +47,7 @@ import { TooltipProvider } from "./tooltip";
|
|
|
46
47
|
export type MarkdownInteractiveBlock = { kind: "html" | "site"; content: string };
|
|
47
48
|
const InteractiveContext = createContext<{
|
|
48
49
|
source: string;
|
|
50
|
+
streaming?: boolean | undefined;
|
|
49
51
|
render?: (block: MarkdownInteractiveBlock) => ReactNode;
|
|
50
52
|
renderImage?: (image: { src: string; alt: string }) => ReactNode;
|
|
51
53
|
}>({ source: "" });
|
|
@@ -406,7 +408,7 @@ function fenceLanguage(children: ReactNode): string | null {
|
|
|
406
408
|
}
|
|
407
409
|
|
|
408
410
|
function InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<"pre"> & ExtraProps) {
|
|
409
|
-
const { source, render } = useContext(InteractiveContext);
|
|
411
|
+
const { source, render, streaming } = useContext(InteractiveContext);
|
|
410
412
|
const language = fenceLanguage(children);
|
|
411
413
|
if (render && (language === "opengeni-html" || language === "opengeni-site")) {
|
|
412
414
|
const start = node?.position?.start.offset;
|
|
@@ -428,7 +430,33 @@ function InteractiveCodeBlock({ children, node }: ComponentPropsWithoutRef<"pre"
|
|
|
428
430
|
closing &&
|
|
429
431
|
closing.length >= opening[1]!.length &&
|
|
430
432
|
[...closing].every((c) => c === opening[1]![0]);
|
|
431
|
-
if (!complete)
|
|
433
|
+
if (!complete) {
|
|
434
|
+
return (
|
|
435
|
+
<div
|
|
436
|
+
role="status"
|
|
437
|
+
aria-live="polite"
|
|
438
|
+
aria-busy={streaming === true}
|
|
439
|
+
className="my-3 flex min-h-32 items-center gap-3 rounded-og-md bg-og-surface-1/70 px-4 py-5"
|
|
440
|
+
>
|
|
441
|
+
{streaming ? (
|
|
442
|
+
<LoaderCircleIcon
|
|
443
|
+
aria-hidden
|
|
444
|
+
className="size-5 shrink-0 text-og-fg-muted motion-safe:animate-spin"
|
|
445
|
+
/>
|
|
446
|
+
) : null}
|
|
447
|
+
<div>
|
|
448
|
+
<div className="text-og-sm font-medium text-og-fg">
|
|
449
|
+
{streaming ? "Preparing preview…" : "Preview incomplete"}
|
|
450
|
+
</div>
|
|
451
|
+
<div className="mt-1 text-og-sm text-og-fg-muted">
|
|
452
|
+
{streaming
|
|
453
|
+
? "Generating the interactive content. It will appear here when ready."
|
|
454
|
+
: "Generation stopped before the preview was ready."}
|
|
455
|
+
</div>
|
|
456
|
+
</div>
|
|
457
|
+
</div>
|
|
458
|
+
);
|
|
459
|
+
}
|
|
432
460
|
return (
|
|
433
461
|
<>
|
|
434
462
|
{render({
|
|
@@ -622,10 +650,11 @@ function MarkdownImpl({
|
|
|
622
650
|
const interactiveContext = useMemo(
|
|
623
651
|
() => ({
|
|
624
652
|
source: children,
|
|
653
|
+
streaming,
|
|
625
654
|
...(renderInteractiveBlock ? { render: renderInteractiveBlock } : {}),
|
|
626
655
|
...(renderImage ? { renderImage } : {}),
|
|
627
656
|
}),
|
|
628
|
-
[children, renderInteractiveBlock, renderImage],
|
|
657
|
+
[children, streaming, renderInteractiveBlock, renderImage],
|
|
629
658
|
);
|
|
630
659
|
|
|
631
660
|
// `min-w-0` lets the prose shrink inside flex parents (message bubbles) so
|
|
@@ -229,8 +229,8 @@ export type MessageTimelineProps = {
|
|
|
229
229
|
hasNewer?: boolean | undefined;
|
|
230
230
|
/** A newer history page is being fetched. */
|
|
231
231
|
loadingNewer?: boolean | undefined;
|
|
232
|
-
/** Page forward through history
|
|
233
|
-
onLoadNewer?: (() =>
|
|
232
|
+
/** Page forward through history. Return the request promise to enable inline error/retry. */
|
|
233
|
+
onLoadNewer?: (() => unknown) | undefined;
|
|
234
234
|
/**
|
|
235
235
|
* Reload the live tip window. When omitted, Jump to latest only re-pins and
|
|
236
236
|
* scrolls the in-memory window.
|
|
@@ -461,6 +461,67 @@ export function MessageTimeline({
|
|
|
461
461
|
// can acquire a different first-delta id when older text arrives).
|
|
462
462
|
const sourceItems = events ?? items;
|
|
463
463
|
const olderBoundaryKey = sourceItems?.[0]?.id;
|
|
464
|
+
const newerBoundaryKey = `${events?.[0]?.sessionId ?? ""}:${olderBoundaryKey ?? ""}`;
|
|
465
|
+
const newerScopeRef = useRef(newerBoundaryKey);
|
|
466
|
+
newerScopeRef.current = newerBoundaryKey;
|
|
467
|
+
const newerAttemptRef = useRef<{ pending: boolean } | null>(null);
|
|
468
|
+
const newerRetryButtonRef = useRef<HTMLButtonElement | null>(null);
|
|
469
|
+
const [newerRetryPending, setNewerRetryPending] = useState(false);
|
|
470
|
+
const [newerFailure, setNewerFailure] = useState<{ key: string; message: string } | null>(null);
|
|
471
|
+
useEffect(() => {
|
|
472
|
+
newerScopeRef.current = newerBoundaryKey;
|
|
473
|
+
newerAttemptRef.current = null;
|
|
474
|
+
setNewerFailure(null);
|
|
475
|
+
setNewerRetryPending(false);
|
|
476
|
+
return () => {
|
|
477
|
+
newerScopeRef.current = "";
|
|
478
|
+
newerAttemptRef.current = null;
|
|
479
|
+
};
|
|
480
|
+
}, [newerBoundaryKey]);
|
|
481
|
+
const requestNewer = useCallback(
|
|
482
|
+
(explicitRetry = false) => {
|
|
483
|
+
if (
|
|
484
|
+
newerScopeRef.current !== newerBoundaryKey ||
|
|
485
|
+
!onLoadNewer ||
|
|
486
|
+
loadingNewer ||
|
|
487
|
+
(newerAttemptRef.current && (!explicitRetry || newerAttemptRef.current.pending))
|
|
488
|
+
) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
const attempt = { pending: true };
|
|
492
|
+
newerAttemptRef.current = attempt;
|
|
493
|
+
if (explicitRetry) setNewerRetryPending(true);
|
|
494
|
+
const isCurrent = () =>
|
|
495
|
+
newerAttemptRef.current === attempt && newerScopeRef.current === newerBoundaryKey;
|
|
496
|
+
// Both synchronous host errors and rejected promises belong to this
|
|
497
|
+
// boundary. Retain the failed attempt so observers cannot hot-retry it.
|
|
498
|
+
void Promise.resolve()
|
|
499
|
+
.then(() => (isCurrent() ? onLoadNewer() : undefined))
|
|
500
|
+
.then(
|
|
501
|
+
() => {
|
|
502
|
+
if (!isCurrent()) return;
|
|
503
|
+
newerAttemptRef.current = null;
|
|
504
|
+
// The successful page removes the recovery control. Return focus
|
|
505
|
+
// to the reading surface without moving the reader's viewport.
|
|
506
|
+
if (document.activeElement === newerRetryButtonRef.current) {
|
|
507
|
+
scrollRef.current?.focus({ preventScroll: true });
|
|
508
|
+
}
|
|
509
|
+
setNewerFailure(null);
|
|
510
|
+
setNewerRetryPending(false);
|
|
511
|
+
},
|
|
512
|
+
(reason: unknown) => {
|
|
513
|
+
if (!isCurrent()) return;
|
|
514
|
+
attempt.pending = false;
|
|
515
|
+
setNewerRetryPending(false);
|
|
516
|
+
setNewerFailure({
|
|
517
|
+
key: newerBoundaryKey,
|
|
518
|
+
message: reason instanceof Error ? reason.message : String(reason),
|
|
519
|
+
});
|
|
520
|
+
},
|
|
521
|
+
);
|
|
522
|
+
},
|
|
523
|
+
[onLoadNewer, loadingNewer, newerBoundaryKey],
|
|
524
|
+
);
|
|
464
525
|
const previousSourceIdsRef = useRef(new Set<string>());
|
|
465
526
|
const previousSourceBoundaryRef = useRef<string | undefined>(undefined);
|
|
466
527
|
const readingAnchorRef = useRef<TimelineAnchor | null>(null);
|
|
@@ -1586,14 +1647,14 @@ export function MessageTimeline({
|
|
|
1586
1647
|
// An underfilled history window has both sentinels visible. Advancing
|
|
1587
1648
|
// it automatically would undo an explicit older-page navigation.
|
|
1588
1649
|
if (maxScrollOf(root) > 1 && entries.some((entry) => entry.isIntersecting)) {
|
|
1589
|
-
|
|
1650
|
+
requestNewer();
|
|
1590
1651
|
}
|
|
1591
1652
|
},
|
|
1592
1653
|
{ root, rootMargin: "0px 0px 1200px 0px" },
|
|
1593
1654
|
);
|
|
1594
1655
|
observer.observe(target);
|
|
1595
1656
|
return () => observer.disconnect();
|
|
1596
|
-
}, [hasNewer, loadingNewer, onLoadNewer, firstGroupKey]);
|
|
1657
|
+
}, [hasNewer, loadingNewer, onLoadNewer, requestNewer, firstGroupKey]);
|
|
1597
1658
|
|
|
1598
1659
|
// Late layout that React commits cannot see (images decoding, fonts, code
|
|
1599
1660
|
// blocks) grows content without a commit. While pinned, soft-follow the tip;
|
|
@@ -2061,6 +2122,29 @@ export function MessageTimeline({
|
|
|
2061
2122
|
{groups.length > 0 && trailingState ? (
|
|
2062
2123
|
<div data-og-timeline-trailing-state="">{trailingState}</div>
|
|
2063
2124
|
) : null}
|
|
2125
|
+
{hasNewer && newerFailure?.key === newerBoundaryKey ? (
|
|
2126
|
+
<div
|
|
2127
|
+
data-og-newer-error=""
|
|
2128
|
+
className="flex flex-col items-center gap-2 px-4 py-3 text-center text-og-menu text-og-fg-muted"
|
|
2129
|
+
>
|
|
2130
|
+
<p role="status" className="max-w-prose [overflow-wrap:anywhere]">
|
|
2131
|
+
Couldn’t load later activity. {newerFailure.message}
|
|
2132
|
+
</p>
|
|
2133
|
+
<button
|
|
2134
|
+
ref={newerRetryButtonRef}
|
|
2135
|
+
type="button"
|
|
2136
|
+
data-og-retry-newer=""
|
|
2137
|
+
className="min-h-11 rounded-og-md border border-og-border px-3 py-2 text-og-fg hover:bg-og-surface-2 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-og-accent"
|
|
2138
|
+
aria-disabled={loadingNewer || newerRetryPending}
|
|
2139
|
+
aria-busy={newerRetryPending}
|
|
2140
|
+
onClick={() => requestNewer(true)}
|
|
2141
|
+
>
|
|
2142
|
+
{newerRetryPending
|
|
2143
|
+
? "Retrying later activity…"
|
|
2144
|
+
: "Retry later activity"}
|
|
2145
|
+
</button>
|
|
2146
|
+
</div>
|
|
2147
|
+
) : null}
|
|
2064
2148
|
{hasNewer ? (
|
|
2065
2149
|
<div
|
|
2066
2150
|
ref={bottomSentinelRef}
|
|
@@ -59,7 +59,14 @@ function providerGroups(models: ClientModel[], codexOnly: boolean) {
|
|
|
59
59
|
optionDisabled: codexOnly && !isCodexClientModel(model),
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
const groups = [...byProvider.values()];
|
|
63
|
+
if (codexOnly) {
|
|
64
|
+
const codexIndex = groups.findIndex((group) =>
|
|
65
|
+
group.models.some((model) => !model.optionDisabled && isCodexClientModel(model)),
|
|
66
|
+
);
|
|
67
|
+
if (codexIndex > 0) groups.unshift(...groups.splice(codexIndex, 1));
|
|
68
|
+
}
|
|
69
|
+
return groups;
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
export function ModelPicker({
|
|
@@ -78,8 +85,9 @@ export function ModelPicker({
|
|
|
78
85
|
[rows, codexOnly],
|
|
79
86
|
);
|
|
80
87
|
const billingGroups = useMemo(
|
|
81
|
-
() =>
|
|
82
|
-
|
|
88
|
+
() =>
|
|
89
|
+
effectiveRows.length > 0 ? groupPickerRowsByBillingClass(effectiveRows, { codexOnly }) : [],
|
|
90
|
+
[effectiveRows, codexOnly],
|
|
83
91
|
);
|
|
84
92
|
const providerGroupList = useMemo(() => providerGroups(models, codexOnly), [models, codexOnly]);
|
|
85
93
|
const useBillingGroups = Boolean(effectiveRows.length);
|
|
@@ -34,7 +34,10 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
|
|
|
34
34
|
`${row.label} ${row.id} ${row.providerLabel} ${row.billingClassLabel} ${payerSummaryForModel(row.catalog)}`.toLowerCase();
|
|
35
35
|
return words.every((word) => text.includes(word));
|
|
36
36
|
});
|
|
37
|
-
const
|
|
37
|
+
const matchingIds = new Set(filtered.map((row) => row.id));
|
|
38
|
+
const groups = groupPickerRowsByBillingClass(rows, { codexOnly: props.codexOnly === true })
|
|
39
|
+
.map((group) => ({ ...group, rows: group.rows.filter((row) => matchingIds.has(row.id)) }))
|
|
40
|
+
.filter((group) => group.rows.length > 0);
|
|
38
41
|
const choose = (row: ClientPickerModelRow) => {
|
|
39
42
|
if (!row.selectable || props.disabled) return;
|
|
40
43
|
if (row.id !== props.model) props.onModelChange(row.id);
|
|
@@ -139,8 +142,12 @@ export function ModelPolicyPickerMenu(props: ModelPolicyPickerProps) {
|
|
|
139
142
|
) : (
|
|
140
143
|
<>
|
|
141
144
|
{groups.map((group) => (
|
|
142
|
-
<section
|
|
143
|
-
|
|
145
|
+
<section
|
|
146
|
+
key={group.billingClass}
|
|
147
|
+
aria-label={group.label}
|
|
148
|
+
className="py-2.5 first:pt-1 [&+section]:border-t [&+section]:border-og-border"
|
|
149
|
+
>
|
|
150
|
+
<div className="flex items-center gap-2 px-2.5 py-1.5 text-og-control font-semibold text-og-fg">
|
|
144
151
|
<BillingClassMark billingClass={group.billingClass} aria-label="" />
|
|
145
152
|
{group.label}
|
|
146
153
|
</div>
|