@gram-ai/elements 1.34.0 → 1.36.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/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
- package/dist/compat-shims-DxtUrORi.js.map +1 -1
- package/dist/compat-shims.d.ts +9 -8
- package/dist/components/Chat/index.d.ts +2 -1
- package/dist/components/ChatHistory.d.ts +1 -1
- package/dist/components/FrontendTools/index.d.ts +1 -1
- package/dist/components/Replay.d.ts +1 -1
- package/dist/components/Replay.stories.d.ts +2 -2
- package/dist/components/ShadowRoot.d.ts +1 -1
- package/dist/components/ShareButton/index.d.ts +1 -1
- package/dist/components/assistant-ui/thinking-indicator.d.ts +8 -0
- package/dist/components/ui/avatar.d.ts +3 -3
- package/dist/components/ui/button.d.ts +2 -2
- package/dist/components/ui/buttonVariants.d.ts +2 -2
- package/dist/components/ui/calendar.d.ts +2 -1
- package/dist/components/ui/collapsible.d.ts +3 -3
- package/dist/components/ui/dialog.d.ts +10 -10
- package/dist/components/ui/popover.d.ts +4 -4
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/time-range-picker.d.ts +2 -1
- package/dist/components/ui/tool-ui.d.ts +9 -7
- package/dist/components/ui/tooltip.d.ts +4 -4
- package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
- package/dist/contexts/ElementsProvider.d.ts +1 -1
- package/dist/contexts/ToolApprovalContext.d.ts +2 -2
- package/dist/contexts/ToolExecutionContext.d.ts +1 -1
- package/dist/contexts/portal-container.d.ts +1 -1
- package/dist/elements.cjs +1 -1
- package/dist/elements.css +1 -1
- package/dist/elements.js +2 -2
- package/dist/hooks/useDensity.d.ts +1 -1
- package/dist/hooks/useElements.d.ts +2 -1
- package/dist/hooks/useGramThreadListAdapter.d.ts +13 -0
- package/dist/hooks/useRadius.d.ts +1 -1
- package/dist/hooks/useThemeProps.d.ts +1 -1
- package/dist/hooks/useToolApproval.d.ts +2 -1
- package/dist/index-7fTI_vaV.cjs +194 -0
- package/dist/index-7fTI_vaV.cjs.map +1 -0
- package/dist/{index-B5lZrrO2.js → index-Bv-yE4G1.js} +2919 -2806
- package/dist/index-Bv-yE4G1.js.map +1 -0
- package/dist/{index-BFU6NvbL.js → index-BziIHO9O.js} +9621 -9308
- package/dist/index-BziIHO9O.js.map +1 -0
- package/dist/{index-C08dvTEo.cjs → index-CGBkMd0d.cjs} +48 -48
- package/dist/index-CGBkMd0d.cjs.map +1 -0
- package/dist/lib/errorTracking.d.ts +1 -1
- package/dist/lib/tools.d.ts +11 -10
- package/dist/plugins/generative-ui/catalog.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/accordion.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/action-button.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/alert.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/avatar-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/avatar.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/button.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/card.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/checkbox-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/data-table.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/dialog.d.ts +10 -10
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +15 -15
- package/dist/plugins/generative-ui/ui/grid.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/index.d.ts +57 -40
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/input.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/list.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/metric.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/pagination.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/popover.d.ts +7 -7
- package/dist/plugins/generative-ui/ui/progress.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/select-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/select.d.ts +10 -10
- package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/skeleton-wrapper.d.ts +2 -1
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/stack.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/switch.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/table.d.ts +8 -8
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/tabs.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/text.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/textarea.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +4 -4
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-BRnyr1GA.cjs → profiler-DuJEf_S6.cjs} +2 -2
- package/dist/{profiler-BRnyr1GA.cjs.map → profiler-DuJEf_S6.cjs.map} +1 -1
- package/dist/{profiler-KLSTpp6I.js → profiler-xLXOPfmj.js} +2 -2
- package/dist/{profiler-KLSTpp6I.js.map → profiler-xLXOPfmj.js.map} +1 -1
- package/dist/react-shim.cjs.map +1 -1
- package/dist/react-shim.d.ts +1 -1
- package/dist/react-shim.js +1 -4
- package/dist/react-shim.js.map +1 -1
- package/dist/server/bun.cjs.map +1 -1
- package/dist/server/bun.js.map +1 -1
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js.map +1 -1
- package/dist/server/fastify.cjs.map +1 -1
- package/dist/server/fastify.js.map +1 -1
- package/dist/server/hono.cjs.map +1 -1
- package/dist/server/hono.js.map +1 -1
- package/dist/server/nextjs.cjs.map +1 -1
- package/dist/server/nextjs.js.map +1 -1
- package/dist/server/tanstack-start.cjs.map +1 -1
- package/dist/server/tanstack-start.js.map +1 -1
- package/dist/{startRecording-CKx-YWbq.cjs → startRecording-C2XF9-Ol.cjs} +2 -2
- package/dist/{startRecording-CKx-YWbq.cjs.map → startRecording-C2XF9-Ol.cjs.map} +1 -1
- package/dist/{startRecording-BfxB1xxR.js → startRecording-qKnXr4lw.js} +2 -2
- package/dist/{startRecording-BfxB1xxR.js.map → startRecording-qKnXr4lw.js.map} +1 -1
- package/dist/types/index.d.ts +29 -3
- package/package.json +8 -11
- package/src/compat-shims.ts +16 -2
- package/src/components/Chat/index.tsx +4 -1
- package/src/components/Chat/stories/FrontendTools.stories.tsx +1 -1
- package/src/components/Chat/stories/ToolApproval.stories.tsx +2 -2
- package/src/components/Chat/stories/Tools.stories.tsx +13 -5
- package/src/components/ChatHistory.tsx +3 -1
- package/src/components/FrontendTools/index.tsx +1 -1
- package/src/components/MessageContent.tsx +1 -0
- package/src/components/Replay.stories.tsx +2 -3
- package/src/components/Replay.tsx +17 -10
- package/src/components/ShadowRoot.tsx +2 -2
- package/src/components/ShareButton/index.tsx +4 -2
- package/src/components/assistant-ui/assistant-modal.tsx +5 -3
- package/src/components/assistant-ui/attachment.tsx +1 -1
- package/src/components/assistant-ui/error-boundary.tsx +1 -1
- package/src/components/assistant-ui/markdown-text.tsx +1 -1
- package/src/components/assistant-ui/thinking-indicator.tsx +175 -0
- package/src/components/assistant-ui/thread.tsx +251 -27
- package/src/components/assistant-ui/tool-fallback.tsx +8 -8
- package/src/components/assistant-ui/tool-group.tsx +4 -13
- package/src/components/assistant-ui/tool-mention-autocomplete.tsx +1 -1
- package/src/components/ui/avatar.tsx +3 -3
- package/src/components/ui/calendar.tsx +1 -1
- package/src/components/ui/collapsible.tsx +7 -3
- package/src/components/ui/dialog.tsx +18 -10
- package/src/components/ui/generative-ui.tsx +9 -4
- package/src/components/ui/popover.tsx +4 -4
- package/src/components/ui/skeleton.tsx +4 -1
- package/src/components/ui/time-range-picker.stories.tsx +164 -154
- package/src/components/ui/time-range-picker.tsx +11 -5
- package/src/components/ui/tool-ui.tsx +68 -40
- package/src/components/ui/tooltip.tsx +4 -4
- package/src/contexts/ChatIdContext.tsx +1 -1
- package/src/contexts/ConnectionStatusContext.tsx +6 -5
- package/src/contexts/ElementsProvider.tsx +64 -41
- package/src/contexts/ReplayContext.ts +1 -1
- package/src/contexts/ToolApprovalContext.tsx +5 -1
- package/src/contexts/ToolExecutionContext.tsx +1 -1
- package/src/contexts/portal-container.tsx +1 -1
- package/src/global.css +55 -16
- package/src/hooks/useAuth.ts +2 -1
- package/src/hooks/useDensity.ts +1 -1
- package/src/hooks/useElements.ts +2 -1
- package/src/hooks/useFollowOnSuggestions.ts +3 -6
- package/src/hooks/useGramThreadListAdapter.tsx +50 -3
- package/src/hooks/useMCPTools.ts +2 -2
- package/src/hooks/useModel.ts +1 -3
- package/src/hooks/usePluginComponents.ts +3 -1
- package/src/hooks/useRadius.ts +1 -1
- package/src/hooks/useSession.ts +3 -1
- package/src/hooks/useThemeProps.ts +5 -5
- package/src/hooks/useToolApproval.ts +2 -1
- package/src/lib/cassette.ts +20 -8
- package/src/lib/errorTracking.ts +1 -4
- package/src/lib/messageConverter.test.ts +11 -13
- package/src/lib/messageConverter.ts +13 -4
- package/src/lib/token.ts +2 -5
- package/src/lib/tool-mentions.ts +5 -2
- package/src/lib/tools.byte-cap.test.ts +1 -1
- package/src/lib/tools.test.ts +1 -1
- package/src/lib/tools.ts +15 -5
- package/src/lib/utils.ts +2 -2
- package/src/lib.d.ts +8 -1
- package/src/plugins/chart/chart.test.ts +3 -4
- package/src/plugins/chart/component.tsx +7 -6
- package/src/plugins/chart/ui/area-chart.tsx +1 -1
- package/src/plugins/chart/ui/line-chart.tsx +1 -1
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +2 -2
- package/src/plugins/generative-ui/ui/accordion.tsx +4 -4
- package/src/plugins/generative-ui/ui/action-button.tsx +4 -2
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/alert.tsx +7 -3
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +5 -1
- package/src/plugins/generative-ui/ui/avatar.tsx +12 -6
- package/src/plugins/generative-ui/ui/badge.tsx +1 -1
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/button.tsx +1 -1
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/card.tsx +28 -7
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/checkbox.tsx +1 -1
- package/src/plugins/generative-ui/ui/data-table.tsx +1 -1
- package/src/plugins/generative-ui/ui/dialog.tsx +15 -10
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +33 -15
- package/src/plugins/generative-ui/ui/grid.tsx +1 -1
- package/src/plugins/generative-ui/ui/index.ts +154 -40
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/input.tsx +5 -1
- package/src/plugins/generative-ui/ui/label.tsx +1 -1
- package/src/plugins/generative-ui/ui/list.tsx +5 -1
- package/src/plugins/generative-ui/ui/metric.tsx +2 -1
- package/src/plugins/generative-ui/ui/pagination.tsx +12 -7
- package/src/plugins/generative-ui/ui/popover.tsx +13 -7
- package/src/plugins/generative-ui/ui/progress.tsx +1 -1
- package/src/plugins/generative-ui/ui/radio-group.tsx +2 -2
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/select.tsx +14 -10
- package/src/plugins/generative-ui/ui/separator.tsx +1 -1
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +1 -1
- package/src/plugins/generative-ui/ui/skeleton.tsx +4 -1
- package/src/plugins/generative-ui/ui/stack.tsx +1 -1
- package/src/plugins/generative-ui/ui/switch.tsx +1 -1
- package/src/plugins/generative-ui/ui/table.tsx +29 -8
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +5 -2
- package/src/plugins/generative-ui/ui/tabs.tsx +4 -4
- package/src/plugins/generative-ui/ui/text.tsx +1 -1
- package/src/plugins/generative-ui/ui/textarea.tsx +4 -1
- package/src/plugins/generative-ui/ui/tooltip.tsx +4 -4
- package/src/react-shim.ts +9 -4
- package/src/server/bun.ts +1 -1
- package/src/server/express.ts +1 -1
- package/src/server/fastify.ts +1 -1
- package/src/server/hono.ts +1 -1
- package/src/server/nextjs.ts +1 -1
- package/src/server/tanstack-start.ts +1 -1
- package/src/storybook.d.ts +5 -0
- package/src/types/index.ts +39 -3
- package/dist/index-B5lZrrO2.js.map +0 -1
- package/dist/index-BFU6NvbL.js.map +0 -1
- package/dist/index-C08dvTEo.cjs.map +0 -1
- package/dist/index-DzZ1-jQY.cjs +0 -194
- package/dist/index-DzZ1-jQY.cjs.map +0 -1
|
@@ -17,7 +17,6 @@ import {
|
|
|
17
17
|
wrapToolsWithApproval,
|
|
18
18
|
wrapToolsWithByteCap,
|
|
19
19
|
type ApprovalHelpers,
|
|
20
|
-
type FrontendTool,
|
|
21
20
|
} from "@/lib/tools";
|
|
22
21
|
import { compactForModel } from "@/lib/contextCompaction";
|
|
23
22
|
import { describeStreamError } from "@/lib/streamErrorMessage";
|
|
@@ -49,6 +48,8 @@ import {
|
|
|
49
48
|
type ChatTransport,
|
|
50
49
|
type UIMessage,
|
|
51
50
|
} from "ai";
|
|
51
|
+
|
|
52
|
+
type UIMessagePart = UIMessage["parts"][number];
|
|
52
53
|
import {
|
|
53
54
|
ReactNode,
|
|
54
55
|
useCallback,
|
|
@@ -67,43 +68,47 @@ import { ElementsContext } from "./contexts";
|
|
|
67
68
|
import { ToolApprovalProvider } from "./ToolApprovalContext";
|
|
68
69
|
import { ToolExecutionProvider } from "./ToolExecutionContext";
|
|
69
70
|
|
|
70
|
-
// Reads the active local thread id from the runtime's threads store.
|
|
71
|
-
//
|
|
72
|
-
// isolated here as the single point of breakage if the API moves.
|
|
71
|
+
// Reads the active local thread id from the runtime's threads store. Goes
|
|
72
|
+
// through assistant-ui's public ThreadListRuntime.getState() API.
|
|
73
73
|
function getActiveLocalThreadId(
|
|
74
74
|
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null>,
|
|
75
75
|
): string | undefined {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
76
|
+
const threadsState = runtimeRef.current?.threads.getState();
|
|
77
|
+
if (!threadsState) return undefined;
|
|
78
|
+
// `mainThreadId` is always populated by the SDK; the secondary read is a
|
|
79
|
+
// defensive fallback in case the SDK ever returns a state shape with an
|
|
80
|
+
// older `threadIds` field instead. The cast widens to an indexable shape
|
|
81
|
+
// because `ThreadListState` doesn't declare that historical field.
|
|
82
|
+
const legacy = (threadsState as { threadIds?: readonly string[] }).threadIds;
|
|
83
|
+
return threadsState.mainThreadId ?? legacy?.[0];
|
|
81
84
|
}
|
|
82
85
|
|
|
86
|
+
type ExecutableTool = {
|
|
87
|
+
execute?: (args: unknown, options?: unknown) => Promise<unknown>;
|
|
88
|
+
};
|
|
89
|
+
|
|
83
90
|
/**
|
|
84
91
|
* Extracts executable tools from frontend tool definitions.
|
|
85
92
|
* Frontend tools created via defineFrontendTool have an unstable_tool property
|
|
86
93
|
* that contains the tool definition with execute function.
|
|
94
|
+
*
|
|
95
|
+
* The AI SDK's `ToolExecuteFunction<INPUT, OUTPUT>` signature is too strict on
|
|
96
|
+
* its second parameter (a typed `ToolCallOptions`) and too broad on its return
|
|
97
|
+
* (`AsyncIterable | PromiseLike | OUTPUT`) to match `ExecutableTool.execute`
|
|
98
|
+
* directly. The reference is copied as-is — no runtime wrapping — and only the
|
|
99
|
+
* type surface is widened.
|
|
87
100
|
*/
|
|
88
101
|
function extractExecutableTools(
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
): Record<
|
|
92
|
-
string,
|
|
93
|
-
{ execute?: (args: unknown, options?: unknown) => Promise<unknown> }
|
|
94
|
-
> {
|
|
102
|
+
frontendTools: Record<string, AssistantTool> | undefined,
|
|
103
|
+
): Record<string, ExecutableTool> {
|
|
95
104
|
if (!frontendTools) return {};
|
|
96
105
|
|
|
97
106
|
return Object.fromEntries(
|
|
98
107
|
Object.entries(frontendTools).map(([name, tool]) => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
{
|
|
104
|
-
execute: toolDef?.execute,
|
|
105
|
-
},
|
|
106
|
-
];
|
|
108
|
+
const toolDef = tool.unstable_tool as {
|
|
109
|
+
execute?: ExecutableTool["execute"];
|
|
110
|
+
};
|
|
111
|
+
return [name, { execute: toolDef.execute }];
|
|
107
112
|
}),
|
|
108
113
|
);
|
|
109
114
|
}
|
|
@@ -154,13 +159,14 @@ function cleanMessagesForModel(messages: UIMessage[]): UIMessage[] {
|
|
|
154
159
|
return message;
|
|
155
160
|
}
|
|
156
161
|
|
|
157
|
-
// Process each part: strip providerOptions/providerMetadata and filter reasoning
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
// Process each part: strip providerOptions/providerMetadata and filter reasoning.
|
|
163
|
+
// `callProviderMetadata` is not declared on `UIMessagePart`, so we widen the
|
|
164
|
+
// part to an indexable record just for the destructure.
|
|
165
|
+
const cleanedParts = partsArray.map((part) => {
|
|
166
|
+
const { callProviderMetadata: _omit, ...cleanPart } =
|
|
167
|
+
part as UIMessagePart & { callProviderMetadata?: unknown };
|
|
168
|
+
void _omit;
|
|
169
|
+
return cleanPart as UIMessagePart;
|
|
164
170
|
});
|
|
165
171
|
|
|
166
172
|
return {
|
|
@@ -218,6 +224,7 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
218
224
|
projectSlug: config.projectSlug,
|
|
219
225
|
variant: config.variant,
|
|
220
226
|
});
|
|
227
|
+
// oxlint-disable-next-line react-hooks/exhaustive-deps -- one-time init at mount; later config changes are intentionally ignored
|
|
221
228
|
}, []);
|
|
222
229
|
|
|
223
230
|
// Generate a stable chat ID for server-side persistence (when history is disabled)
|
|
@@ -227,7 +234,11 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
227
234
|
// State to expose the current chat ID via context
|
|
228
235
|
const [currentChatId, setCurrentChatId] = useState<string | null>(null);
|
|
229
236
|
|
|
230
|
-
const {
|
|
237
|
+
const {
|
|
238
|
+
data: mcpTools,
|
|
239
|
+
mcpHeaders,
|
|
240
|
+
isLoading: mcpQueryLoading,
|
|
241
|
+
} = useMCPTools({
|
|
231
242
|
auth,
|
|
232
243
|
mcp: config.mcp,
|
|
233
244
|
mcps: config.mcps,
|
|
@@ -235,6 +246,11 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
235
246
|
toolsToInclude: config.tools?.toolsToInclude,
|
|
236
247
|
gramEnvironment: config.gramEnvironment,
|
|
237
248
|
});
|
|
249
|
+
// Treat auth-loading as "tools not yet resolved" too — the MCP query is
|
|
250
|
+
// disabled (and so not "loading") until auth settles, so without this a
|
|
251
|
+
// tool-list consumer would briefly see an empty, settled state before tools
|
|
252
|
+
// arrive.
|
|
253
|
+
const mcpToolsLoading = auth.isLoading || mcpQueryLoading;
|
|
238
254
|
|
|
239
255
|
// Store approval helpers in ref so they can be used in async contexts
|
|
240
256
|
const approvalHelpersRef = useRef<ApprovalHelpers>({
|
|
@@ -522,8 +538,11 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
522
538
|
config.contextCompaction?.maxTokens,
|
|
523
539
|
config.contextCompaction?.compactAtFraction,
|
|
524
540
|
config.contextCompaction?.keepRecent,
|
|
541
|
+
config.gramEnvironment,
|
|
542
|
+
config.api?.headers,
|
|
525
543
|
model,
|
|
526
544
|
mcpTools,
|
|
545
|
+
mcpHeaders,
|
|
527
546
|
getApprovalHelpers,
|
|
528
547
|
apiUrl,
|
|
529
548
|
auth.isLoading,
|
|
@@ -561,12 +580,13 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
561
580
|
setCurrentChatId(chatId);
|
|
562
581
|
};
|
|
563
582
|
}, [mcpHeaders, setCurrentChatId]);
|
|
583
|
+
const configTransport = config.transport;
|
|
564
584
|
const transport = useMemo<ChatTransport<UIMessage>>(() => {
|
|
565
|
-
if (typeof
|
|
566
|
-
return
|
|
585
|
+
if (typeof configTransport === "function") {
|
|
586
|
+
return configTransport({ getChatId, adoptChatId });
|
|
567
587
|
}
|
|
568
|
-
return
|
|
569
|
-
}, [
|
|
588
|
+
return configTransport ?? defaultTransport;
|
|
589
|
+
}, [configTransport, defaultTransport, getChatId, adoptChatId]);
|
|
570
590
|
|
|
571
591
|
const historyEnabled = config.history?.enabled ?? false;
|
|
572
592
|
|
|
@@ -582,8 +602,9 @@ const ElementsProviderInner = ({ children, config }: ElementsProviderProps) => {
|
|
|
582
602
|
setIsOpen,
|
|
583
603
|
plugins,
|
|
584
604
|
mcpTools,
|
|
605
|
+
mcpToolsLoading,
|
|
585
606
|
}),
|
|
586
|
-
[config, model, isExpanded, isOpen, plugins, mcpTools],
|
|
607
|
+
[config, model, isExpanded, isOpen, plugins, mcpTools, mcpToolsLoading],
|
|
587
608
|
);
|
|
588
609
|
|
|
589
610
|
const frontendTools = config.tools?.frontendTools ?? {};
|
|
@@ -657,8 +678,7 @@ interface ElementsProviderWithHistoryProps {
|
|
|
657
678
|
headers: Record<string, string>;
|
|
658
679
|
contextValue: React.ContextType<typeof ElementsContext>;
|
|
659
680
|
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null>;
|
|
660
|
-
|
|
661
|
-
frontendTools: Record<string, AssistantTool | FrontendTool<any, any>>;
|
|
681
|
+
frontendTools: Record<string, AssistantTool>;
|
|
662
682
|
localIdToUuidMap: Map<string, string>;
|
|
663
683
|
currentRemoteIdRef: React.RefObject<string | null>;
|
|
664
684
|
executableTools: ExecutableToolSet;
|
|
@@ -707,6 +727,7 @@ const ElementsProviderWithHistory = ({
|
|
|
707
727
|
localIdToUuidMap,
|
|
708
728
|
threadListFilters: contextValue?.config.history?.threadListFilters,
|
|
709
729
|
deferThreadIdMinting: contextValue?.config.history?.deferThreadIdMinting,
|
|
730
|
+
transformChatMessage: contextValue?.config.history?.transformChatMessage,
|
|
710
731
|
});
|
|
711
732
|
const initialThreadId = contextValue?.config.history?.initialThreadId;
|
|
712
733
|
|
|
@@ -714,6 +735,7 @@ const ElementsProviderWithHistory = ({
|
|
|
714
735
|
// half-finished: the tool-result is patched in but the agent never resumes,
|
|
715
736
|
// so the next user message lands on top of an unresolved tool-call sequence.
|
|
716
737
|
const useChatRuntimeHook = useCallback(() => {
|
|
738
|
+
// oxlint-disable-next-line react-hooks/rules-of-hooks -- intentional: useChatRuntime is invoked by useRemoteThreadListRuntime as a hook for each thread
|
|
717
739
|
return useChatRuntime({
|
|
718
740
|
transport,
|
|
719
741
|
sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithToolCalls,
|
|
@@ -785,8 +807,7 @@ interface ElementsProviderWithoutHistoryProps {
|
|
|
785
807
|
transport: ChatTransport<UIMessage>;
|
|
786
808
|
contextValue: React.ContextType<typeof ElementsContext>;
|
|
787
809
|
runtimeRef: React.RefObject<ReturnType<typeof useChatRuntime> | null>;
|
|
788
|
-
|
|
789
|
-
frontendTools: Record<string, AssistantTool | FrontendTool<any, any>>;
|
|
810
|
+
frontendTools: Record<string, AssistantTool>;
|
|
790
811
|
executableTools: ExecutableToolSet;
|
|
791
812
|
currentChatId: string | null;
|
|
792
813
|
}
|
|
@@ -835,7 +856,9 @@ const ElementsProviderWithoutHistory = ({
|
|
|
835
856
|
|
|
836
857
|
const queryClient = new QueryClient();
|
|
837
858
|
|
|
838
|
-
export const ElementsProvider = (
|
|
859
|
+
export const ElementsProvider = (
|
|
860
|
+
props: ElementsProviderProps,
|
|
861
|
+
): React.JSX.Element => {
|
|
839
862
|
return (
|
|
840
863
|
<QueryClientProvider client={queryClient}>
|
|
841
864
|
<ConnectionStatusProvider>
|
|
@@ -2,6 +2,6 @@ import { createContext, useContext } from "react";
|
|
|
2
2
|
|
|
3
3
|
export const ReplayContext = createContext<{ isReplay: boolean } | null>(null);
|
|
4
4
|
|
|
5
|
-
export function useReplayContext() {
|
|
5
|
+
export function useReplayContext(): { isReplay: boolean } | null {
|
|
6
6
|
return useContext(ReplayContext);
|
|
7
7
|
}
|
|
@@ -26,7 +26,11 @@ interface ToolApprovalContextType {
|
|
|
26
26
|
getPendingApproval: (toolCallId: string) => PendingApproval | undefined;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
-
export function ToolApprovalProvider({
|
|
29
|
+
export function ToolApprovalProvider({
|
|
30
|
+
children,
|
|
31
|
+
}: {
|
|
32
|
+
children: ReactNode;
|
|
33
|
+
}): React.JSX.Element {
|
|
30
34
|
const [pendingApprovals, setPendingApprovals] = useState<
|
|
31
35
|
Map<string, PendingApproval>
|
|
32
36
|
>(new Map());
|
|
@@ -36,7 +36,7 @@ interface ToolExecutionProviderProps {
|
|
|
36
36
|
export function ToolExecutionProvider({
|
|
37
37
|
children,
|
|
38
38
|
tools,
|
|
39
|
-
}: ToolExecutionProviderProps) {
|
|
39
|
+
}: ToolExecutionProviderProps): React.JSX.Element {
|
|
40
40
|
const executeTool = useCallback(
|
|
41
41
|
async (
|
|
42
42
|
toolName: string,
|
package/src/global.css
CHANGED
|
@@ -258,14 +258,36 @@
|
|
|
258
258
|
|
|
259
259
|
/* assistant-ui streaming indicator — rainbow gradient ring matches the
|
|
260
260
|
Speakeasy brand palette used elsewhere (see `INSIGHTS_AI_RAINBOW_BORDER_CLASS`
|
|
261
|
-
and the login page's BrandGradientBar).
|
|
261
|
+
and the login page's BrandGradientBar). Shared as a variable so the inline
|
|
262
|
+
trailing dot and the standalone "thinking" dot stay in sync. */
|
|
263
|
+
.gram-elements {
|
|
264
|
+
--aui-rainbow: conic-gradient(
|
|
265
|
+
from 0deg,
|
|
266
|
+
#320f1e,
|
|
267
|
+
#c83228,
|
|
268
|
+
#fb873f,
|
|
269
|
+
#d2dc91,
|
|
270
|
+
#5a8250,
|
|
271
|
+
#002314,
|
|
272
|
+
#00143c,
|
|
273
|
+
#2873d7,
|
|
274
|
+
#9bc3ff,
|
|
275
|
+
#320f1e
|
|
276
|
+
);
|
|
277
|
+
/* Spin shorthand shared by both rainbow dots so a single reduced-motion
|
|
278
|
+
override (below) stops all of them at once. */
|
|
279
|
+
--aui-spin: aui-rainbow-spin 1.6s linear infinite;
|
|
280
|
+
}
|
|
281
|
+
|
|
262
282
|
@keyframes aui-rainbow-spin {
|
|
263
283
|
to {
|
|
264
284
|
transform: rotate(360deg);
|
|
265
285
|
}
|
|
266
286
|
}
|
|
267
287
|
|
|
268
|
-
.
|
|
288
|
+
/* Trailing dot that follows answer text as it streams. The "before first
|
|
289
|
+
token" state is handled by <ThinkingIndicator> (cycling verbs), so there is
|
|
290
|
+
deliberately no `:empty::after` rule here. */
|
|
269
291
|
.gram-elements
|
|
270
292
|
:where(.aui-md[data-status="running"])
|
|
271
293
|
> :where(:not(ol):not(ul):not(pre)):last-child::after,
|
|
@@ -301,19 +323,29 @@
|
|
|
301
323
|
margin-right: 0.15rem;
|
|
302
324
|
padding: 3px;
|
|
303
325
|
border-radius: 50%;
|
|
304
|
-
background:
|
|
305
|
-
|
|
306
|
-
#
|
|
307
|
-
#
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
#
|
|
311
|
-
#
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
326
|
+
background: var(--aui-rainbow);
|
|
327
|
+
-webkit-mask:
|
|
328
|
+
linear-gradient(#fff 0 0) content-box,
|
|
329
|
+
linear-gradient(#fff 0 0);
|
|
330
|
+
-webkit-mask-composite: xor;
|
|
331
|
+
mask:
|
|
332
|
+
linear-gradient(#fff 0 0) content-box,
|
|
333
|
+
linear-gradient(#fff 0 0);
|
|
334
|
+
mask-composite: exclude;
|
|
335
|
+
animation: var(--aui-spin);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/* Standalone rainbow dot for the cycling "thinking" indicator (shown before
|
|
339
|
+
the assistant streams any answer text). Same ring as the inline dot above. */
|
|
340
|
+
.gram-elements .aui-thinking-dot {
|
|
341
|
+
display: inline-block;
|
|
342
|
+
box-sizing: border-box;
|
|
343
|
+
flex-shrink: 0;
|
|
344
|
+
width: 0.95em;
|
|
345
|
+
height: 0.95em;
|
|
346
|
+
padding: 2.5px;
|
|
347
|
+
border-radius: 50%;
|
|
348
|
+
background: var(--aui-rainbow);
|
|
317
349
|
-webkit-mask:
|
|
318
350
|
linear-gradient(#fff 0 0) content-box,
|
|
319
351
|
linear-gradient(#fff 0 0);
|
|
@@ -322,7 +354,14 @@
|
|
|
322
354
|
linear-gradient(#fff 0 0) content-box,
|
|
323
355
|
linear-gradient(#fff 0 0);
|
|
324
356
|
mask-composite: exclude;
|
|
325
|
-
animation: aui-
|
|
357
|
+
animation: var(--aui-spin);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@media (prefers-reduced-motion: reduce) {
|
|
361
|
+
/* Stops both the trailing streaming dot and the standalone thinking dot. */
|
|
362
|
+
.gram-elements {
|
|
363
|
+
--aui-spin: none;
|
|
364
|
+
}
|
|
326
365
|
}
|
|
327
366
|
|
|
328
367
|
/* Simple shimmer animation for title text */
|
package/src/hooks/useAuth.ts
CHANGED
|
@@ -131,7 +131,8 @@ export const useAuth = ({
|
|
|
131
131
|
// useMCPTools). Today the dashboard uses this to forward
|
|
132
132
|
// `Authorization: Bearer <user-session JWT>` so the runtime gateway can
|
|
133
133
|
// resolve the user's upstream credentials for issuer-gated toolsets.
|
|
134
|
-
const
|
|
134
|
+
const authHeaders = auth?.headers;
|
|
135
|
+
const extraHeaders = useMemo(() => authHeaders ?? {}, [authHeaders]);
|
|
135
136
|
|
|
136
137
|
const ensureValidHeaders = useCallback(async (): Promise<
|
|
137
138
|
Record<string, string>
|
package/src/hooks/useDensity.ts
CHANGED
|
@@ -102,7 +102,7 @@ type DensityToken = keyof (typeof densityClasses)["normal"];
|
|
|
102
102
|
* Hook to get density classes based on theme config
|
|
103
103
|
* Use: const d = useDensity(); then d('p-md') returns the appropriate padding class
|
|
104
104
|
*/
|
|
105
|
-
export const useDensity = () => {
|
|
105
|
+
export const useDensity = (): ((token: DensityToken) => string) => {
|
|
106
106
|
const { config } = useElements();
|
|
107
107
|
const density = config.theme?.density ?? "normal";
|
|
108
108
|
|
package/src/hooks/useElements.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
2
|
import { ElementsContext } from "@/contexts/contexts";
|
|
3
|
+
import type { ElementsContextType } from "@/types";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* @private Internal hook to access the ElementsContext
|
|
6
7
|
*
|
|
7
8
|
*/
|
|
8
|
-
export const useElements = () => {
|
|
9
|
+
export const useElements = (): ElementsContextType => {
|
|
9
10
|
const context = useContext(ElementsContext);
|
|
10
11
|
if (!context) {
|
|
11
12
|
throw new Error("useElements must be used within a ElementsProvider");
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { useReplayContext } from "@/contexts/ReplayContext";
|
|
2
|
-
import { getApiUrl } from "@/lib/api";
|
|
3
2
|
import { useAssistantState } from "@assistant-ui/react";
|
|
4
3
|
import { generateObject } from "ai";
|
|
5
4
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
@@ -64,8 +63,6 @@ export function useFollowOnSuggestions(): {
|
|
|
64
63
|
const isRunning = useAssistantState(({ thread }) => thread.isRunning);
|
|
65
64
|
const messages = useAssistantState(({ thread }) => thread.messages);
|
|
66
65
|
|
|
67
|
-
const apiUrl = getApiUrl(config);
|
|
68
|
-
|
|
69
66
|
const fetchSuggestions = useCallback(async () => {
|
|
70
67
|
if (!isEnabled || auth.isLoading || !auth.headers) return;
|
|
71
68
|
|
|
@@ -89,7 +86,7 @@ export function useFollowOnSuggestions(): {
|
|
|
89
86
|
let lastAssistantMessage = "";
|
|
90
87
|
for (let i = recentMessages.length - 1; i >= 0; i--) {
|
|
91
88
|
const msg = recentMessages[i];
|
|
92
|
-
if (msg.role === "assistant") {
|
|
89
|
+
if (msg && msg.role === "assistant") {
|
|
93
90
|
lastAssistantMessage = msg.content;
|
|
94
91
|
break;
|
|
95
92
|
}
|
|
@@ -191,7 +188,7 @@ ${conversation}`,
|
|
|
191
188
|
abortControllerRef.current = null;
|
|
192
189
|
}
|
|
193
190
|
}
|
|
194
|
-
}, [isEnabled,
|
|
191
|
+
}, [isEnabled, auth.headers, auth.isLoading, messages, model]);
|
|
195
192
|
|
|
196
193
|
// Fetch suggestions when:
|
|
197
194
|
// 1. The thread stops running (assistant finished responding)
|
|
@@ -224,7 +221,7 @@ ${conversation}`,
|
|
|
224
221
|
if (lastProcessedMessageIdRef.current === lastMessage.id) return;
|
|
225
222
|
|
|
226
223
|
lastProcessedMessageIdRef.current = lastMessage.id;
|
|
227
|
-
fetchSuggestions();
|
|
224
|
+
void fetchSuggestions();
|
|
228
225
|
}, [isRunning, messages, fetchSuggestions, auth.isLoading, auth.headers]);
|
|
229
226
|
|
|
230
227
|
// Cleanup on unmount
|
|
@@ -10,6 +10,7 @@ import { createAssistantStream, type AssistantStream } from "assistant-stream";
|
|
|
10
10
|
import {
|
|
11
11
|
GramChatOverview,
|
|
12
12
|
GramChat,
|
|
13
|
+
GramChatMessage,
|
|
13
14
|
convertGramMessagesToExported,
|
|
14
15
|
convertGramMessagesToUIMessages,
|
|
15
16
|
} from "@/lib/messageConverter";
|
|
@@ -60,6 +61,14 @@ async function waitForMappedId(
|
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Transforms or drops a persisted chat message before it is rendered from
|
|
66
|
+
* history. Return the (possibly rewritten) message, or `null` to omit it.
|
|
67
|
+
*/
|
|
68
|
+
export type ChatMessageTransform = (
|
|
69
|
+
message: GramChatMessage,
|
|
70
|
+
) => GramChatMessage | null;
|
|
71
|
+
|
|
63
72
|
export interface ThreadListAdapterOptions {
|
|
64
73
|
apiUrl: string;
|
|
65
74
|
headers: Record<string, string>;
|
|
@@ -78,6 +87,13 @@ export interface ThreadListAdapterOptions {
|
|
|
78
87
|
* `crypto.randomUUID()`. Use this when the backend owns chat-id creation.
|
|
79
88
|
*/
|
|
80
89
|
deferThreadIdMinting?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* Optional hook to transform or drop each persisted message before it is
|
|
92
|
+
* converted for rendering. Return a message to render it (possibly rewritten),
|
|
93
|
+
* or `null` to omit it. Keeps product-specific transcript conventions out of
|
|
94
|
+
* the library — see {@link HistoryConfig.transformChatMessage}.
|
|
95
|
+
*/
|
|
96
|
+
transformChatMessage?: ChatMessageTransform;
|
|
81
97
|
}
|
|
82
98
|
|
|
83
99
|
interface ListChatsResponse {
|
|
@@ -93,15 +109,41 @@ class GramThreadHistoryAdapter {
|
|
|
93
109
|
private apiUrl: string;
|
|
94
110
|
private headers: Record<string, string>;
|
|
95
111
|
private store: AssistantApi;
|
|
112
|
+
// Read lazily rather than captured: the adapter is constructed once, but the
|
|
113
|
+
// consumer may swap `transformChatMessage` across renders, so resolve it from
|
|
114
|
+
// the live options on every load instead of snapshotting it here.
|
|
115
|
+
private getTransformChatMessage?: () => ChatMessageTransform | undefined;
|
|
96
116
|
|
|
97
117
|
constructor(
|
|
98
118
|
apiUrl: string,
|
|
99
119
|
headers: Record<string, string>,
|
|
100
120
|
store: AssistantApi,
|
|
121
|
+
getTransformChatMessage?: () => ChatMessageTransform | undefined,
|
|
101
122
|
) {
|
|
102
123
|
this.apiUrl = apiUrl;
|
|
103
124
|
this.headers = headers;
|
|
104
125
|
this.store = store;
|
|
126
|
+
this.getTransformChatMessage = getTransformChatMessage;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Applies the consumer-supplied `transformChatMessage` hook to a loaded
|
|
131
|
+
* transcript: rewrites each message and drops any the hook returns `null` for.
|
|
132
|
+
* Without a hook configured the messages pass through untouched.
|
|
133
|
+
*/
|
|
134
|
+
private applyTransform(messages: GramChatMessage[]): GramChatMessage[] {
|
|
135
|
+
const transform = this.getTransformChatMessage?.();
|
|
136
|
+
if (!transform) {
|
|
137
|
+
return messages;
|
|
138
|
+
}
|
|
139
|
+
const result: GramChatMessage[] = [];
|
|
140
|
+
for (const message of messages) {
|
|
141
|
+
const transformed = transform(message);
|
|
142
|
+
if (transformed) {
|
|
143
|
+
result.push(transformed);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return result;
|
|
105
147
|
}
|
|
106
148
|
|
|
107
149
|
async load() {
|
|
@@ -122,7 +164,7 @@ class GramThreadHistoryAdapter {
|
|
|
122
164
|
}
|
|
123
165
|
|
|
124
166
|
const chat = (await response.json()) as GramChat;
|
|
125
|
-
return convertGramMessagesToExported(chat.messages);
|
|
167
|
+
return convertGramMessagesToExported(this.applyTransform(chat.messages));
|
|
126
168
|
} catch (error) {
|
|
127
169
|
console.error("Error loading chat:", error);
|
|
128
170
|
return { messages: [], headId: null };
|
|
@@ -156,7 +198,9 @@ class GramThreadHistoryAdapter {
|
|
|
156
198
|
}
|
|
157
199
|
|
|
158
200
|
const chat = (await response.json()) as GramChat;
|
|
159
|
-
return convertGramMessagesToUIMessages(
|
|
201
|
+
return convertGramMessagesToUIMessages(
|
|
202
|
+
this.applyTransform(chat.messages),
|
|
203
|
+
);
|
|
160
204
|
|
|
161
205
|
// // Filter out system messages (assistant-ui doesn't support them in the import path)
|
|
162
206
|
// const filteredMessages = chat.messages.filter(
|
|
@@ -211,6 +255,7 @@ function useGramThreadHistoryAdapter(
|
|
|
211
255
|
optionsRef.current.apiUrl,
|
|
212
256
|
optionsRef.current.headers,
|
|
213
257
|
store,
|
|
258
|
+
() => optionsRef.current.transformChatMessage,
|
|
214
259
|
),
|
|
215
260
|
);
|
|
216
261
|
// Cast to ThreadHistoryAdapter - the withFormat generic doesn't match but works at runtime
|
|
@@ -360,7 +405,9 @@ export function useGramThreadListAdapter(
|
|
|
360
405
|
// Title generation happens async server-side via Temporal after first completion.
|
|
361
406
|
// This delay allows the OpenRouter LLM call to complete before we fetch the title.
|
|
362
407
|
const TITLE_GENERATION_DELAY_MS = 2000;
|
|
363
|
-
await new Promise((r) =>
|
|
408
|
+
await new Promise((r) => {
|
|
409
|
+
setTimeout(r, TITLE_GENERATION_DELAY_MS);
|
|
410
|
+
});
|
|
364
411
|
|
|
365
412
|
try {
|
|
366
413
|
// TODO: rename generateTitle endpoint to getTitle
|
package/src/hooks/useMCPTools.ts
CHANGED
|
@@ -42,10 +42,10 @@ export function useMCPTools({
|
|
|
42
42
|
);
|
|
43
43
|
|
|
44
44
|
const envQueryKey = Object.entries(environment ?? {}).map(
|
|
45
|
-
([k, v]) => `${k}:${v}`,
|
|
45
|
+
([k, v]) => `${k}:${String(v)}`,
|
|
46
46
|
);
|
|
47
47
|
const authQueryKey = Object.entries(auth.headers ?? {}).map(
|
|
48
|
-
([k, v]) => `${k}:${v}`,
|
|
48
|
+
([k, v]) => `${k}:${String(v)}`,
|
|
49
49
|
);
|
|
50
50
|
const serversQueryKey = servers.map(
|
|
51
51
|
(s) => `${s.url}|${s.name ?? ""}|${s.environment ?? ""}`,
|
package/src/hooks/useModel.ts
CHANGED
|
@@ -5,9 +5,7 @@ import { useAuth } from "./useAuth";
|
|
|
5
5
|
import { useElements } from "./useElements";
|
|
6
6
|
|
|
7
7
|
// Creates an OpenRouter client to be used for "internal Gram" usage, such as follow-on suggestions
|
|
8
|
-
export const useModel = (
|
|
9
|
-
model: string = "openai/gpt-5.4-mini",
|
|
10
|
-
): LanguageModel => {
|
|
8
|
+
export const useModel = (model = "openai/gpt-5.4-mini"): LanguageModel => {
|
|
11
9
|
const { config } = useElements();
|
|
12
10
|
|
|
13
11
|
const auth = useAuth({
|
|
@@ -15,7 +15,9 @@ type ComponentsByLanguage =
|
|
|
15
15
|
>
|
|
16
16
|
| undefined;
|
|
17
17
|
|
|
18
|
-
export function useComponentsByLanguage(
|
|
18
|
+
export function useComponentsByLanguage(
|
|
19
|
+
plugins: Plugin[],
|
|
20
|
+
): ComponentsByLanguage {
|
|
19
21
|
return useMemo(() => {
|
|
20
22
|
return plugins.reduce((acc, plugin) => {
|
|
21
23
|
if (acc?.[plugin.language] && !plugin.overrideExisting) {
|
package/src/hooks/useRadius.ts
CHANGED
|
@@ -34,7 +34,7 @@ type RadiusSize = "sm" | "md" | "lg" | "xl" | "full";
|
|
|
34
34
|
* Hook to get radius classes based on theme config
|
|
35
35
|
* Use: const r = useRadius(); then r('lg') returns the appropriate rounded class
|
|
36
36
|
*/
|
|
37
|
-
export const useRadius = () => {
|
|
37
|
+
export const useRadius = (): ((size: RadiusSize) => string) => {
|
|
38
38
|
const { config } = useElements();
|
|
39
39
|
const radius = config.theme?.radius ?? "soft";
|
|
40
40
|
|
package/src/hooks/useSession.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { GetSessionFn } from "@/types";
|
|
2
2
|
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
|
|
4
|
-
export function getChatSessionQueryKey(
|
|
4
|
+
export function getChatSessionQueryKey(
|
|
5
|
+
projectSlug: string,
|
|
6
|
+
): readonly ["chatSession", string] {
|
|
5
7
|
return ["chatSession", projectSlug] as const;
|
|
6
8
|
}
|
|
7
9
|
|
|
@@ -4,13 +4,13 @@ import { useElements } from "./useElements";
|
|
|
4
4
|
/**
|
|
5
5
|
* Hook to get theme-related props including dark mode class
|
|
6
6
|
*/
|
|
7
|
-
export const useThemeProps = ()
|
|
7
|
+
export const useThemeProps = (): {
|
|
8
|
+
readonly className: string | undefined;
|
|
9
|
+
} => {
|
|
8
10
|
const { config } = useElements();
|
|
9
|
-
const
|
|
11
|
+
const colorScheme = config.theme?.colorScheme ?? "light";
|
|
10
12
|
|
|
11
13
|
return useMemo(() => {
|
|
12
|
-
const { colorScheme = "light" } = theme;
|
|
13
|
-
|
|
14
14
|
const isDark =
|
|
15
15
|
colorScheme === "dark" ||
|
|
16
16
|
(colorScheme === "system" &&
|
|
@@ -20,5 +20,5 @@ export const useThemeProps = () => {
|
|
|
20
20
|
return {
|
|
21
21
|
className: isDark ? "dark" : undefined,
|
|
22
22
|
} as const;
|
|
23
|
-
}, [
|
|
23
|
+
}, [colorScheme]);
|
|
24
24
|
};
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { useContext } from "react";
|
|
2
2
|
import { ToolApprovalContext } from "@/contexts/contexts";
|
|
3
|
+
import type { ToolApprovalContextType } from "@/contexts/ToolApprovalContext";
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Hook to access the tool approval context for managing human-in-the-loop
|
|
6
7
|
* tool execution approval.
|
|
7
8
|
*/
|
|
8
|
-
export const useToolApproval = () => {
|
|
9
|
+
export const useToolApproval = (): ToolApprovalContextType => {
|
|
9
10
|
const context = useContext(ToolApprovalContext);
|
|
10
11
|
if (!context) {
|
|
11
12
|
throw new Error(
|