@gram-ai/elements 1.27.4 → 1.27.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.
- package/README.md +72 -60
- package/README.typedoc.md +6 -6
- package/bin/cli.js +74 -74
- package/dist/compat-shims-CO9JXXV4.cjs.map +1 -1
- package/dist/{compat-shims-BPJ7Q68c.js → compat-shims-DxtUrORi.js} +4 -2
- package/dist/compat-shims-DxtUrORi.js.map +1 -0
- package/dist/components/ShareButton/index.d.ts +2 -2
- package/dist/components/assistant-ui/message-feedback.d.ts +1 -1
- package/dist/components/assistant-ui/tooltip-icon-button.d.ts +2 -2
- package/dist/components/ui/avatar.d.ts +2 -2
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/calendar.d.ts +1 -1
- package/dist/components/ui/collapsible.d.ts +1 -1
- package/dist/components/ui/dialog.d.ts +4 -4
- package/dist/components/ui/popover.d.ts +2 -2
- package/dist/components/ui/skeleton.d.ts +1 -1
- package/dist/components/ui/time-range-picker.d.ts +1 -1
- package/dist/components/ui/tool-ui.d.ts +7 -7
- package/dist/components/ui/tooltip.d.ts +2 -2
- package/dist/contexts/ConnectionStatusContext.d.ts +1 -1
- package/dist/elements.cjs +1 -1
- package/dist/elements.js +2 -2
- package/dist/hooks/useDensity.d.ts +73 -73
- package/dist/hooks/useMCPTools.d.ts +1 -1
- package/dist/hooks/useRadius.d.ts +1 -1
- package/dist/{index-BpJstUh1.cjs → index-C4bFBGfl.cjs} +4 -4
- package/dist/{index-BpJstUh1.cjs.map → index-C4bFBGfl.cjs.map} +1 -1
- package/dist/{index-CUitXazZ.js → index-D93pV0_o.js} +55 -55
- package/dist/{index-CUitXazZ.js.map → index-D93pV0_o.js.map} +1 -1
- package/dist/{index-D0bAYNQy.js → index-DuCQRbcQ.js} +279 -265
- package/dist/index-DuCQRbcQ.js.map +1 -0
- package/dist/{index-KSX4Qjip.cjs → index-y_PNN5vK.cjs} +10 -10
- package/dist/index-y_PNN5vK.cjs.map +1 -0
- package/dist/lib/cassette.d.ts +4 -4
- package/dist/lib/errorTracking.d.ts +1 -1
- package/dist/lib/messageConverter.d.ts +1 -1
- package/dist/lib/models.d.ts +1 -1
- package/dist/plugins/chart/ui/bar-chart.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/accordion-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/accordion.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/action-button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/alert-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/alert.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/avatar.d.ts +5 -5
- package/dist/plugins/generative-ui/ui/badge.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/button.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card-wrapper.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/card.d.ts +8 -8
- package/dist/plugins/generative-ui/ui/checkbox.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/data-table.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/dialog.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/dropdown-menu.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/grid.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/input-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/input.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/label.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/metric.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/pagination.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/popover.d.ts +4 -4
- package/dist/plugins/generative-ui/ui/progress.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/radio-group.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/select.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/separator.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/skeleton.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/stack.d.ts +6 -6
- package/dist/plugins/generative-ui/ui/switch.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/table.d.ts +9 -9
- package/dist/plugins/generative-ui/ui/tabs-wrapper.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/tabs.d.ts +1 -1
- package/dist/plugins/generative-ui/ui/text.d.ts +3 -3
- package/dist/plugins/generative-ui/ui/textarea.d.ts +2 -2
- package/dist/plugins/generative-ui/ui/tooltip.d.ts +1 -1
- package/dist/plugins.cjs +1 -1
- package/dist/plugins.js +1 -1
- package/dist/{profiler-BFkhZRxj.js → profiler-FpBY9eRv.js} +2 -2
- package/dist/{profiler-BFkhZRxj.js.map → profiler-FpBY9eRv.js.map} +1 -1
- package/dist/{profiler-CyzxBxVz.cjs → profiler-_mthyjvo.cjs} +2 -2
- package/dist/{profiler-CyzxBxVz.cjs.map → profiler-_mthyjvo.cjs.map} +1 -1
- package/dist/react-shim.js +1 -1
- package/dist/server/express.cjs.map +1 -1
- package/dist/server/express.js.map +1 -1
- package/dist/{startRecording-Dq92sEHf.cjs → startRecording-NJcpiHw-.cjs} +2 -2
- package/dist/{startRecording-Dq92sEHf.cjs.map → startRecording-NJcpiHw-.cjs.map} +1 -1
- package/dist/{startRecording-C-PPAs_Z.js → startRecording-r5MXQ2Dm.js} +2 -2
- package/dist/{startRecording-C-PPAs_Z.js.map → startRecording-r5MXQ2Dm.js.map} +1 -1
- package/dist/types/index.d.ts +2 -2
- package/package.json +1 -5
- package/src/compat-plugin.ts +14 -14
- package/src/compat-shims.ts +33 -31
- package/src/compat.test.ts +48 -48
- package/src/compat.ts +6 -6
- package/src/components/Chat/index.tsx +17 -17
- package/src/components/Chat/stories/Charts.stories.tsx +98 -98
- package/src/components/Chat/stories/Composer.stories.tsx +15 -15
- package/src/components/Chat/stories/ConnectionConfiguration.stories.tsx +44 -44
- package/src/components/Chat/stories/CustomComponents.stories.tsx +17 -17
- package/src/components/Chat/stories/Density.stories.tsx +20 -20
- package/src/components/Chat/stories/ErrorBoundary.stories.tsx +47 -47
- package/src/components/Chat/stories/FrontendTools.stories.tsx +39 -39
- package/src/components/Chat/stories/GenerativeUI.stories.tsx +48 -48
- package/src/components/Chat/stories/MessageFeedback.stories.tsx +52 -52
- package/src/components/Chat/stories/Modal.stories.tsx +28 -28
- package/src/components/Chat/stories/Model.stories.tsx +11 -11
- package/src/components/Chat/stories/Radius.stories.tsx +20 -20
- package/src/components/Chat/stories/Sidecar.stories.tsx +13 -13
- package/src/components/Chat/stories/StyleIsolation.stories.tsx +11 -11
- package/src/components/Chat/stories/Theme.stories.tsx +25 -25
- package/src/components/Chat/stories/Thread.stories.tsx +25 -25
- package/src/components/Chat/stories/ToolApproval.stories.tsx +55 -55
- package/src/components/Chat/stories/ToolMentions.stories.tsx +17 -17
- package/src/components/Chat/stories/Tools.stories.tsx +88 -88
- package/src/components/Chat/stories/Variants.stories.tsx +32 -32
- package/src/components/Chat/stories/Welcome.stories.tsx +14 -14
- package/src/components/ChatHistory.tsx +7 -7
- package/src/components/FrontendTools/index.tsx +5 -5
- package/src/components/Replay.stories.tsx +157 -157
- package/src/components/Replay.tsx +76 -73
- package/src/components/ShadowRoot.tsx +40 -40
- package/src/components/ShareButton/index.tsx +32 -32
- package/src/components/assistant-ui/assistant-modal.tsx +92 -87
- package/src/components/assistant-ui/assistant-sidecar.tsx +35 -35
- package/src/components/assistant-ui/attachment.tsx +80 -80
- package/src/components/assistant-ui/connection-status-indicator.tsx +33 -33
- package/src/components/assistant-ui/error-boundary.tsx +34 -34
- package/src/components/assistant-ui/follow-on-suggestions.tsx +26 -26
- package/src/components/assistant-ui/markdown-text.tsx +69 -69
- package/src/components/assistant-ui/mentioned-tools-badges.tsx +38 -38
- package/src/components/assistant-ui/message-feedback.tsx +57 -50
- package/src/components/assistant-ui/reasoning.tsx +83 -83
- package/src/components/assistant-ui/thread-list.tsx +45 -45
- package/src/components/assistant-ui/thread.tsx +278 -278
- package/src/components/assistant-ui/tool-fallback.tsx +37 -37
- package/src/components/assistant-ui/tool-group.tsx +26 -26
- package/src/components/assistant-ui/tool-mention-autocomplete.tsx +122 -122
- package/src/components/assistant-ui/tooltip-icon-button.tsx +18 -18
- package/src/components/ui/avatar.tsx +12 -12
- package/src/components/ui/button.tsx +12 -12
- package/src/components/ui/buttonVariants.ts +17 -17
- package/src/components/ui/calendar.tsx +106 -106
- package/src/components/ui/charts.stories.tsx +56 -56
- package/src/components/ui/collapsible.tsx +5 -5
- package/src/components/ui/dialog.tsx +30 -30
- package/src/components/ui/generative-ui.stories.tsx +200 -200
- package/src/components/ui/generative-ui.tsx +26 -26
- package/src/components/ui/popover.tsx +14 -14
- package/src/components/ui/skeleton.tsx +5 -5
- package/src/components/ui/time-range-picker.stories.tsx +80 -80
- package/src/components/ui/time-range-picker.tsx +245 -244
- package/src/components/ui/tool-ui.stories.tsx +37 -37
- package/src/components/ui/tool-ui.tsx +221 -215
- package/src/components/ui/tooltip.tsx +15 -15
- package/src/constants/tailwind.ts +1 -1
- package/src/contexts/ChatIdContext.tsx +7 -7
- package/src/contexts/ConnectionStatusContext.tsx +64 -64
- package/src/contexts/ElementsProvider.tsx +214 -213
- package/src/contexts/ReplayContext.ts +3 -3
- package/src/contexts/ToolApprovalContext.tsx +54 -54
- package/src/contexts/ToolExecutionContext.tsx +34 -34
- package/src/contexts/contexts.ts +7 -7
- package/src/contexts/portal-container-context.ts +2 -2
- package/src/contexts/portal-container.tsx +7 -7
- package/src/embedded.ts +1 -1
- package/src/global.css +25 -25
- package/src/hooks/useAuth.ts +72 -72
- package/src/hooks/useDensity.ts +79 -79
- package/src/hooks/useElements.ts +6 -6
- package/src/hooks/useExpanded.ts +12 -12
- package/src/hooks/useFollowOnSuggestions.ts +83 -83
- package/src/hooks/useGramThreadListAdapter.tsx +99 -99
- package/src/hooks/useMCPTools.ts +47 -47
- package/src/hooks/useModel.ts +14 -14
- package/src/hooks/usePluginComponents.ts +11 -11
- package/src/hooks/usePortalContainer.ts +5 -5
- package/src/hooks/useRadius.ts +23 -23
- package/src/hooks/useRecordCassette.ts +34 -34
- package/src/hooks/useSession.ts +11 -11
- package/src/hooks/useThemeProps.ts +13 -13
- package/src/hooks/useThreadId.ts +4 -4
- package/src/hooks/useToolApproval.ts +7 -7
- package/src/hooks/useToolMentions.ts +40 -40
- package/src/index.ts +26 -26
- package/src/lib/api.test.ts +61 -61
- package/src/lib/api.ts +4 -3
- package/src/lib/auth.ts +13 -13
- package/src/lib/cassette.ts +84 -84
- package/src/lib/easing.ts +1 -1
- package/src/lib/errorTracking.config.ts +5 -5
- package/src/lib/errorTracking.ts +29 -29
- package/src/lib/generative-ui.ts +7 -7
- package/src/lib/humanize.ts +3 -3
- package/src/lib/messageConverter.test.ts +130 -127
- package/src/lib/messageConverter.ts +196 -196
- package/src/lib/models.ts +21 -20
- package/src/lib/token.test.ts +56 -56
- package/src/lib/token.ts +14 -14
- package/src/lib/tool-mentions.ts +45 -45
- package/src/lib/tools.ts +66 -62
- package/src/lib/utils.ts +5 -5
- package/src/lib.d.ts +1 -1
- package/src/plugins/README.md +5 -5
- package/src/plugins/chart/catalog.ts +18 -18
- package/src/plugins/chart/chart.test.ts +31 -31
- package/src/plugins/chart/component.tsx +34 -34
- package/src/plugins/chart/index.ts +4 -4
- package/src/plugins/chart/ui/area-chart.tsx +42 -42
- package/src/plugins/chart/ui/bar-chart.tsx +46 -46
- package/src/plugins/chart/ui/donut-chart.tsx +48 -48
- package/src/plugins/chart/ui/index.ts +7 -7
- package/src/plugins/chart/ui/line-chart.tsx +43 -43
- package/src/plugins/chart/ui/pie-chart.tsx +44 -44
- package/src/plugins/chart/ui/radar-chart.tsx +33 -33
- package/src/plugins/chart/ui/scatter-chart.tsx +43 -43
- package/src/plugins/components/MacOSWindowFrame.tsx +15 -15
- package/src/plugins/components/PluginLoadingState.tsx +10 -10
- package/src/plugins/components/index.ts +1 -1
- package/src/plugins/generative-ui/catalog.ts +54 -54
- package/src/plugins/generative-ui/component.tsx +85 -85
- package/src/plugins/generative-ui/index.ts +4 -4
- package/src/plugins/generative-ui/ui/accordion-wrapper.tsx +16 -16
- package/src/plugins/generative-ui/ui/accordion.tsx +16 -16
- package/src/plugins/generative-ui/ui/action-button.tsx +28 -28
- package/src/plugins/generative-ui/ui/alert-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/alert.tsx +20 -20
- package/src/plugins/generative-ui/ui/avatar-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/avatar.tsx +30 -30
- package/src/plugins/generative-ui/ui/badge.tsx +22 -22
- package/src/plugins/generative-ui/ui/button-wrapper.tsx +12 -12
- package/src/plugins/generative-ui/ui/button.tsx +28 -28
- package/src/plugins/generative-ui/ui/card-wrapper.tsx +8 -8
- package/src/plugins/generative-ui/ui/card.tsx +27 -27
- package/src/plugins/generative-ui/ui/checkbox-wrapper.tsx +9 -9
- package/src/plugins/generative-ui/ui/checkbox.tsx +9 -9
- package/src/plugins/generative-ui/ui/data-table.tsx +8 -8
- package/src/plugins/generative-ui/ui/dialog.tsx +31 -31
- package/src/plugins/generative-ui/ui/dropdown-menu.tsx +44 -44
- package/src/plugins/generative-ui/ui/grid.tsx +12 -12
- package/src/plugins/generative-ui/ui/index.ts +40 -40
- package/src/plugins/generative-ui/ui/input-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/input.tsx +9 -9
- package/src/plugins/generative-ui/ui/label.tsx +8 -8
- package/src/plugins/generative-ui/ui/list.tsx +11 -11
- package/src/plugins/generative-ui/ui/metric.tsx +23 -23
- package/src/plugins/generative-ui/ui/pagination.tsx +28 -28
- package/src/plugins/generative-ui/ui/popover.tsx +21 -21
- package/src/plugins/generative-ui/ui/progress.tsx +13 -13
- package/src/plugins/generative-ui/ui/radio-group.tsx +12 -12
- package/src/plugins/generative-ui/ui/select-wrapper.tsx +7 -7
- package/src/plugins/generative-ui/ui/select.tsx +37 -37
- package/src/plugins/generative-ui/ui/separator.tsx +9 -9
- package/src/plugins/generative-ui/ui/skeleton-wrapper.tsx +10 -10
- package/src/plugins/generative-ui/ui/skeleton.tsx +5 -5
- package/src/plugins/generative-ui/ui/stack.tsx +28 -28
- package/src/plugins/generative-ui/ui/switch.tsx +11 -11
- package/src/plugins/generative-ui/ui/table.tsx +32 -32
- package/src/plugins/generative-ui/ui/tabs-wrapper.tsx +11 -11
- package/src/plugins/generative-ui/ui/tabs.tsx +26 -26
- package/src/plugins/generative-ui/ui/text.tsx +12 -12
- package/src/plugins/generative-ui/ui/textarea.tsx +7 -7
- package/src/plugins/generative-ui/ui/tooltip.tsx +12 -12
- package/src/plugins/index.ts +7 -7
- package/src/react-shim.ts +6 -6
- package/src/server/bun.ts +12 -12
- package/src/server/core.ts +25 -25
- package/src/server/express.ts +17 -15
- package/src/server/fastify.ts +14 -14
- package/src/server/hono.ts +9 -9
- package/src/server/nextjs.ts +12 -12
- package/src/server/tanstack-start.ts +12 -12
- package/src/server.ts +27 -27
- package/src/storybook.d.ts +4 -4
- package/src/types/index.ts +122 -122
- package/src/types/plugins.ts +7 -7
- package/src/vite-env.d.ts +12 -12
- package/dist/compat-shims-BPJ7Q68c.js.map +0 -1
- package/dist/index-D0bAYNQy.js.map +0 -1
- package/dist/index-KSX4Qjip.cjs.map +0 -1
package/src/lib/api.test.ts
CHANGED
|
@@ -1,90 +1,90 @@
|
|
|
1
|
-
import type { ElementsConfig } from
|
|
2
|
-
import { beforeEach, describe, expect, it, vi } from
|
|
1
|
+
import type { ElementsConfig } from "@/types";
|
|
2
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
|
|
4
|
-
describe(
|
|
4
|
+
describe("getApiUrl", () => {
|
|
5
5
|
beforeEach(() => {
|
|
6
|
-
vi.resetModules()
|
|
7
|
-
})
|
|
6
|
+
vi.resetModules();
|
|
7
|
+
});
|
|
8
8
|
|
|
9
9
|
async function loadGetApiUrl(gramApiUrl: string | undefined) {
|
|
10
|
-
vi.stubGlobal(
|
|
11
|
-
const { getApiUrl } = await import(
|
|
12
|
-
return getApiUrl
|
|
10
|
+
vi.stubGlobal("__GRAM_API_URL__", gramApiUrl);
|
|
11
|
+
const { getApiUrl } = await import("./api");
|
|
12
|
+
return getApiUrl;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
it(
|
|
16
|
-
const getApiUrl = await loadGetApiUrl(
|
|
15
|
+
it("uses config.api.url when set", async () => {
|
|
16
|
+
const getApiUrl = await loadGetApiUrl("https://env.example.com");
|
|
17
17
|
const config: ElementsConfig = {
|
|
18
|
-
projectSlug:
|
|
19
|
-
api: { url:
|
|
20
|
-
}
|
|
18
|
+
projectSlug: "test",
|
|
19
|
+
api: { url: "https://config.example.com", session: "test-key" },
|
|
20
|
+
};
|
|
21
21
|
|
|
22
|
-
expect(getApiUrl(config)).toBe(
|
|
23
|
-
})
|
|
22
|
+
expect(getApiUrl(config)).toBe("https://config.example.com");
|
|
23
|
+
});
|
|
24
24
|
|
|
25
|
-
it(
|
|
26
|
-
const getApiUrl = await loadGetApiUrl(
|
|
25
|
+
it("falls back to __GRAM_API_URL__ when config.api.url is not set", async () => {
|
|
26
|
+
const getApiUrl = await loadGetApiUrl("https://env.example.com");
|
|
27
27
|
const config: ElementsConfig = {
|
|
28
|
-
projectSlug:
|
|
29
|
-
api: { session:
|
|
30
|
-
}
|
|
28
|
+
projectSlug: "test",
|
|
29
|
+
api: { session: "test-key" },
|
|
30
|
+
};
|
|
31
31
|
|
|
32
|
-
expect(getApiUrl(config)).toBe(
|
|
33
|
-
})
|
|
32
|
+
expect(getApiUrl(config)).toBe("https://env.example.com");
|
|
33
|
+
});
|
|
34
34
|
|
|
35
|
-
it(
|
|
36
|
-
const getApiUrl = await loadGetApiUrl(
|
|
35
|
+
it("falls back to __GRAM_API_URL__ when config.api is undefined", async () => {
|
|
36
|
+
const getApiUrl = await loadGetApiUrl("https://env.example.com");
|
|
37
37
|
const config: ElementsConfig = {
|
|
38
|
-
projectSlug:
|
|
39
|
-
}
|
|
38
|
+
projectSlug: "test",
|
|
39
|
+
};
|
|
40
40
|
|
|
41
|
-
expect(getApiUrl(config)).toBe(
|
|
42
|
-
})
|
|
41
|
+
expect(getApiUrl(config)).toBe("https://env.example.com");
|
|
42
|
+
});
|
|
43
43
|
|
|
44
|
-
it(
|
|
45
|
-
const getApiUrl = await loadGetApiUrl(
|
|
44
|
+
it("falls back to default URL when both config.api.url and __GRAM_API_URL__ are not set", async () => {
|
|
45
|
+
const getApiUrl = await loadGetApiUrl("");
|
|
46
46
|
const config: ElementsConfig = {
|
|
47
|
-
projectSlug:
|
|
48
|
-
}
|
|
47
|
+
projectSlug: "test",
|
|
48
|
+
};
|
|
49
49
|
|
|
50
|
-
expect(getApiUrl(config)).toBe(
|
|
51
|
-
})
|
|
50
|
+
expect(getApiUrl(config)).toBe("https://app.getgram.ai");
|
|
51
|
+
});
|
|
52
52
|
|
|
53
|
-
it(
|
|
54
|
-
const getApiUrl = await loadGetApiUrl(undefined)
|
|
53
|
+
it("falls back to default URL when __GRAM_API_URL__ is undefined", async () => {
|
|
54
|
+
const getApiUrl = await loadGetApiUrl(undefined);
|
|
55
55
|
const config: ElementsConfig = {
|
|
56
|
-
projectSlug:
|
|
57
|
-
}
|
|
56
|
+
projectSlug: "test",
|
|
57
|
+
};
|
|
58
58
|
|
|
59
|
-
expect(getApiUrl(config)).toBe(
|
|
60
|
-
})
|
|
59
|
+
expect(getApiUrl(config)).toBe("https://app.getgram.ai");
|
|
60
|
+
});
|
|
61
61
|
|
|
62
|
-
it(
|
|
63
|
-
const getApiUrl = await loadGetApiUrl(
|
|
62
|
+
it("skips empty string config.api.url and uses __GRAM_API_URL__", async () => {
|
|
63
|
+
const getApiUrl = await loadGetApiUrl("https://env.example.com");
|
|
64
64
|
const config: ElementsConfig = {
|
|
65
|
-
projectSlug:
|
|
66
|
-
api: { url:
|
|
67
|
-
}
|
|
65
|
+
projectSlug: "test",
|
|
66
|
+
api: { url: "", session: "test-key" },
|
|
67
|
+
};
|
|
68
68
|
|
|
69
|
-
expect(getApiUrl(config)).toBe(
|
|
70
|
-
})
|
|
69
|
+
expect(getApiUrl(config)).toBe("https://env.example.com");
|
|
70
|
+
});
|
|
71
71
|
|
|
72
|
-
it(
|
|
73
|
-
const getApiUrl = await loadGetApiUrl(
|
|
72
|
+
it("removes trailing slashes from the URL", async () => {
|
|
73
|
+
const getApiUrl = await loadGetApiUrl("");
|
|
74
74
|
const config: ElementsConfig = {
|
|
75
|
-
projectSlug:
|
|
76
|
-
api: { url:
|
|
77
|
-
}
|
|
75
|
+
projectSlug: "test",
|
|
76
|
+
api: { url: "https://config.example.com///", session: "test-key" },
|
|
77
|
+
};
|
|
78
78
|
|
|
79
|
-
expect(getApiUrl(config)).toBe(
|
|
80
|
-
})
|
|
79
|
+
expect(getApiUrl(config)).toBe("https://config.example.com");
|
|
80
|
+
});
|
|
81
81
|
|
|
82
|
-
it(
|
|
83
|
-
const getApiUrl = await loadGetApiUrl(
|
|
82
|
+
it("removes trailing slashes from __GRAM_API_URL__", async () => {
|
|
83
|
+
const getApiUrl = await loadGetApiUrl("https://env.example.com//");
|
|
84
84
|
const config: ElementsConfig = {
|
|
85
|
-
projectSlug:
|
|
86
|
-
}
|
|
85
|
+
projectSlug: "test",
|
|
86
|
+
};
|
|
87
87
|
|
|
88
|
-
expect(getApiUrl(config)).toBe(
|
|
89
|
-
})
|
|
90
|
-
})
|
|
88
|
+
expect(getApiUrl(config)).toBe("https://env.example.com");
|
|
89
|
+
});
|
|
90
|
+
});
|
package/src/lib/api.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ElementsConfig } from
|
|
1
|
+
import { ElementsConfig } from "@/types";
|
|
2
2
|
|
|
3
3
|
export function getApiUrl(config: ElementsConfig): string {
|
|
4
4
|
// The api.url in the config should take precedence over the __GRAM_API_URL__ environment variable
|
|
5
5
|
// because it is a user-defined override
|
|
6
|
-
const apiURL =
|
|
7
|
-
|
|
6
|
+
const apiURL =
|
|
7
|
+
config.api?.url || __GRAM_API_URL__ || "https://app.getgram.ai";
|
|
8
|
+
return apiURL.replace(/\/+$/, ""); // Remove trailing slashes
|
|
8
9
|
}
|
package/src/lib/auth.ts
CHANGED
|
@@ -6,42 +6,42 @@ import {
|
|
|
6
6
|
SessionAuthConfig,
|
|
7
7
|
StaticSessionAuthConfig,
|
|
8
8
|
UnifiedSessionAuthConfig,
|
|
9
|
-
} from
|
|
9
|
+
} from "@/types";
|
|
10
10
|
|
|
11
11
|
export function isDangerousApiKeyAuth(
|
|
12
|
-
auth: ApiConfig | undefined
|
|
12
|
+
auth: ApiConfig | undefined,
|
|
13
13
|
): auth is DangerousApiKeyAuthConfig {
|
|
14
|
-
return !!auth &&
|
|
14
|
+
return !!auth && "dangerousApiKey" in auth;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function isUnifiedStaticSession(
|
|
18
|
-
auth: ApiConfig | undefined
|
|
18
|
+
auth: ApiConfig | undefined,
|
|
19
19
|
): auth is UnifiedSessionAuthConfig & { session: string } {
|
|
20
|
-
return !!auth &&
|
|
20
|
+
return !!auth && "session" in auth && typeof auth.session === "string";
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
export function isUnifiedFunctionSession(
|
|
24
|
-
auth: ApiConfig | undefined
|
|
24
|
+
auth: ApiConfig | undefined,
|
|
25
25
|
): auth is BaseApiConfig & { session: GetSessionFn } {
|
|
26
|
-
return !!auth &&
|
|
26
|
+
return !!auth && "session" in auth && typeof auth.session === "function";
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
/** @deprecated Legacy check for `{ sessionToken }` configs. */
|
|
30
30
|
export function isStaticSessionAuth(
|
|
31
|
-
auth: ApiConfig | undefined
|
|
31
|
+
auth: ApiConfig | undefined,
|
|
32
32
|
): auth is StaticSessionAuthConfig {
|
|
33
|
-
return !!auth &&
|
|
33
|
+
return !!auth && "sessionToken" in auth;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
/** @deprecated Legacy check for `{ sessionFn }` configs. */
|
|
37
37
|
export function hasExplicitSessionAuth(
|
|
38
|
-
auth: ApiConfig | undefined
|
|
38
|
+
auth: ApiConfig | undefined,
|
|
39
39
|
): auth is SessionAuthConfig {
|
|
40
|
-
if (!auth) return false
|
|
41
|
-
return
|
|
40
|
+
if (!auth) return false;
|
|
41
|
+
return "sessionFn" in auth;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
/** Returns true when either the legacy `sessionToken` or the unified static `session` string is used. */
|
|
45
45
|
export function isAnyStaticSession(auth: ApiConfig | undefined): boolean {
|
|
46
|
-
return isStaticSessionAuth(auth) || isUnifiedStaticSession(auth)
|
|
46
|
+
return isStaticSessionAuth(auth) || isUnifiedStaticSession(auth);
|
|
47
47
|
}
|
package/src/lib/cassette.ts
CHANGED
|
@@ -10,35 +10,35 @@
|
|
|
10
10
|
* 3. Pass it to `<Replay cassette={...}>` to play it back
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createUIMessageStream, type ChatTransport, type UIMessage } from
|
|
14
|
-
import type { ThreadMessage } from
|
|
13
|
+
import { createUIMessageStream, type ChatTransport, type UIMessage } from "ai";
|
|
14
|
+
import type { ThreadMessage } from "@assistant-ui/react";
|
|
15
15
|
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Cassette types
|
|
18
18
|
// ---------------------------------------------------------------------------
|
|
19
19
|
|
|
20
|
-
export type CassetteTextPart = { type:
|
|
21
|
-
export type CassetteReasoningPart = { type:
|
|
20
|
+
export type CassetteTextPart = { type: "text"; text: string };
|
|
21
|
+
export type CassetteReasoningPart = { type: "reasoning"; text: string };
|
|
22
22
|
export type CassetteToolCallPart = {
|
|
23
|
-
type:
|
|
24
|
-
toolCallId: string
|
|
25
|
-
toolName: string
|
|
26
|
-
args: unknown
|
|
27
|
-
result?: unknown
|
|
28
|
-
}
|
|
23
|
+
type: "tool-call";
|
|
24
|
+
toolCallId: string;
|
|
25
|
+
toolName: string;
|
|
26
|
+
args: unknown;
|
|
27
|
+
result?: unknown;
|
|
28
|
+
};
|
|
29
29
|
|
|
30
30
|
export type CassettePart =
|
|
31
31
|
| CassetteTextPart
|
|
32
32
|
| CassetteReasoningPart
|
|
33
|
-
| CassetteToolCallPart
|
|
33
|
+
| CassetteToolCallPart;
|
|
34
34
|
|
|
35
35
|
export interface CassetteMessage {
|
|
36
|
-
role:
|
|
37
|
-
content: CassettePart[]
|
|
36
|
+
role: "user" | "assistant";
|
|
37
|
+
content: CassettePart[];
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
export interface Cassette {
|
|
41
|
-
messages: CassetteMessage[]
|
|
41
|
+
messages: CassetteMessage[];
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// ---------------------------------------------------------------------------
|
|
@@ -47,13 +47,13 @@ export interface Cassette {
|
|
|
47
47
|
|
|
48
48
|
export interface ReplayOptions {
|
|
49
49
|
/** Milliseconds per character when streaming text. @default 15 */
|
|
50
|
-
typingSpeed?: number
|
|
50
|
+
typingSpeed?: number;
|
|
51
51
|
/** Milliseconds to wait before showing each user message. @default 800 */
|
|
52
|
-
userMessageDelay?: number
|
|
52
|
+
userMessageDelay?: number;
|
|
53
53
|
/** Milliseconds to wait before the assistant starts "typing". @default 400 */
|
|
54
|
-
assistantStartDelay?: number
|
|
54
|
+
assistantStartDelay?: number;
|
|
55
55
|
/** Called when the full replay sequence finishes. */
|
|
56
|
-
onComplete?: () => void
|
|
56
|
+
onComplete?: () => void;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
// ---------------------------------------------------------------------------
|
|
@@ -65,47 +65,47 @@ export interface ReplayOptions {
|
|
|
65
65
|
* System messages are filtered out since they aren't displayed.
|
|
66
66
|
*/
|
|
67
67
|
export function recordCassette(messages: readonly ThreadMessage[]): Cassette {
|
|
68
|
-
const cassetteMessages: CassetteMessage[] = []
|
|
68
|
+
const cassetteMessages: CassetteMessage[] = [];
|
|
69
69
|
|
|
70
70
|
for (const msg of messages) {
|
|
71
|
-
if (msg.role ===
|
|
71
|
+
if (msg.role === "system") continue;
|
|
72
72
|
|
|
73
|
-
const parts: CassettePart[] = []
|
|
73
|
+
const parts: CassettePart[] = [];
|
|
74
74
|
|
|
75
75
|
for (const part of msg.content) {
|
|
76
76
|
switch (part.type) {
|
|
77
|
-
case
|
|
77
|
+
case "text":
|
|
78
78
|
if (part.text) {
|
|
79
|
-
parts.push({ type:
|
|
79
|
+
parts.push({ type: "text", text: part.text });
|
|
80
80
|
}
|
|
81
|
-
break
|
|
82
|
-
case
|
|
81
|
+
break;
|
|
82
|
+
case "reasoning":
|
|
83
83
|
if (part.text) {
|
|
84
|
-
parts.push({ type:
|
|
84
|
+
parts.push({ type: "reasoning", text: part.text });
|
|
85
85
|
}
|
|
86
|
-
break
|
|
87
|
-
case
|
|
86
|
+
break;
|
|
87
|
+
case "tool-call":
|
|
88
88
|
parts.push({
|
|
89
|
-
type:
|
|
89
|
+
type: "tool-call",
|
|
90
90
|
toolCallId: part.toolCallId,
|
|
91
91
|
toolName: part.toolName,
|
|
92
92
|
args: part.args,
|
|
93
93
|
result: part.result,
|
|
94
|
-
})
|
|
95
|
-
break
|
|
94
|
+
});
|
|
95
|
+
break;
|
|
96
96
|
// Skip image, file, audio, source, data parts for now
|
|
97
97
|
}
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
if (parts.length > 0) {
|
|
101
101
|
cassetteMessages.push({
|
|
102
|
-
role: msg.role as
|
|
102
|
+
role: msg.role as "user" | "assistant",
|
|
103
103
|
content: parts,
|
|
104
|
-
})
|
|
104
|
+
});
|
|
105
105
|
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
return { messages: cassetteMessages }
|
|
108
|
+
return { messages: cassetteMessages };
|
|
109
109
|
}
|
|
110
110
|
|
|
111
111
|
// ---------------------------------------------------------------------------
|
|
@@ -116,19 +116,19 @@ export function recordCassette(messages: readonly ThreadMessage[]): Cassette {
|
|
|
116
116
|
function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
117
117
|
return new Promise((resolve, reject) => {
|
|
118
118
|
if (signal?.aborted) {
|
|
119
|
-
reject(new DOMException(
|
|
120
|
-
return
|
|
119
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
120
|
+
return;
|
|
121
121
|
}
|
|
122
|
-
const timeout = setTimeout(resolve, ms)
|
|
122
|
+
const timeout = setTimeout(resolve, ms);
|
|
123
123
|
signal?.addEventListener(
|
|
124
|
-
|
|
124
|
+
"abort",
|
|
125
125
|
() => {
|
|
126
|
-
clearTimeout(timeout)
|
|
127
|
-
reject(new DOMException(
|
|
126
|
+
clearTimeout(timeout);
|
|
127
|
+
reject(new DOMException("Aborted", "AbortError"));
|
|
128
128
|
},
|
|
129
|
-
{ once: true }
|
|
130
|
-
)
|
|
131
|
-
})
|
|
129
|
+
{ once: true },
|
|
130
|
+
);
|
|
131
|
+
});
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
/**
|
|
@@ -138,14 +138,14 @@ function sleep(ms: number, signal?: AbortSignal): Promise<void> {
|
|
|
138
138
|
*/
|
|
139
139
|
export function createReplayTransport(
|
|
140
140
|
cassette: Cassette,
|
|
141
|
-
options?: ReplayOptions
|
|
141
|
+
options?: ReplayOptions,
|
|
142
142
|
): ChatTransport<UIMessage> {
|
|
143
|
-
const typingSpeed = options?.typingSpeed ?? 15
|
|
144
|
-
const assistantStartDelay = options?.assistantStartDelay ?? 400
|
|
143
|
+
const typingSpeed = options?.typingSpeed ?? 15;
|
|
144
|
+
const assistantStartDelay = options?.assistantStartDelay ?? 400;
|
|
145
145
|
|
|
146
146
|
// Cursor tracking which message index to serve next.
|
|
147
147
|
// Starts at 0 and advances past user+assistant pairs.
|
|
148
|
-
let cursor = 0
|
|
148
|
+
let cursor = 0;
|
|
149
149
|
|
|
150
150
|
return {
|
|
151
151
|
sendMessages: async ({ abortSignal }) => {
|
|
@@ -153,51 +153,51 @@ export function createReplayTransport(
|
|
|
153
153
|
// Advance cursor past it (it should be pointing at a user message).
|
|
154
154
|
if (
|
|
155
155
|
cursor < cassette.messages.length &&
|
|
156
|
-
cassette.messages[cursor].role ===
|
|
156
|
+
cassette.messages[cursor].role === "user"
|
|
157
157
|
) {
|
|
158
|
-
cursor
|
|
158
|
+
cursor++;
|
|
159
159
|
}
|
|
160
160
|
|
|
161
161
|
// Collect the next assistant message(s).
|
|
162
162
|
// Multiple consecutive assistant messages are possible (e.g. multi-step tool calls).
|
|
163
|
-
const assistantMessages: CassetteMessage[] = []
|
|
163
|
+
const assistantMessages: CassetteMessage[] = [];
|
|
164
164
|
while (
|
|
165
165
|
cursor < cassette.messages.length &&
|
|
166
|
-
cassette.messages[cursor].role ===
|
|
166
|
+
cassette.messages[cursor].role === "assistant"
|
|
167
167
|
) {
|
|
168
|
-
assistantMessages.push(cassette.messages[cursor])
|
|
169
|
-
cursor
|
|
168
|
+
assistantMessages.push(cassette.messages[cursor]);
|
|
169
|
+
cursor++;
|
|
170
170
|
}
|
|
171
171
|
|
|
172
172
|
// Return a stream that emits the pre-recorded content
|
|
173
173
|
return createUIMessageStream({
|
|
174
174
|
execute: async ({ writer }) => {
|
|
175
175
|
if (assistantMessages.length === 0) {
|
|
176
|
-
return
|
|
176
|
+
return;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
179
|
try {
|
|
180
|
-
await sleep(assistantStartDelay, abortSignal)
|
|
180
|
+
await sleep(assistantStartDelay, abortSignal);
|
|
181
181
|
} catch {
|
|
182
|
-
return // aborted
|
|
182
|
+
return; // aborted
|
|
183
183
|
}
|
|
184
184
|
|
|
185
185
|
for (const message of assistantMessages) {
|
|
186
186
|
for (const part of message.content) {
|
|
187
187
|
try {
|
|
188
|
-
await writeReplayPart(writer, part, typingSpeed, abortSignal)
|
|
188
|
+
await writeReplayPart(writer, part, typingSpeed, abortSignal);
|
|
189
189
|
} catch {
|
|
190
|
-
return // aborted
|
|
190
|
+
return; // aborted
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
}
|
|
194
194
|
},
|
|
195
|
-
})
|
|
195
|
+
});
|
|
196
196
|
},
|
|
197
197
|
reconnectToStream: async () => {
|
|
198
|
-
return null
|
|
198
|
+
return null;
|
|
199
199
|
},
|
|
200
|
-
}
|
|
200
|
+
};
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
// ---------------------------------------------------------------------------
|
|
@@ -206,55 +206,55 @@ export function createReplayTransport(
|
|
|
206
206
|
|
|
207
207
|
interface StreamWriter {
|
|
208
208
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
209
|
-
write(part: any): void
|
|
209
|
+
write(part: any): void;
|
|
210
210
|
}
|
|
211
211
|
|
|
212
212
|
async function writeReplayPart(
|
|
213
213
|
writer: StreamWriter,
|
|
214
214
|
part: CassettePart,
|
|
215
215
|
typingSpeed: number,
|
|
216
|
-
abortSignal?: AbortSignal
|
|
216
|
+
abortSignal?: AbortSignal,
|
|
217
217
|
): Promise<void> {
|
|
218
218
|
switch (part.type) {
|
|
219
|
-
case
|
|
220
|
-
const partId = crypto.randomUUID()
|
|
221
|
-
writer.write({ type:
|
|
219
|
+
case "text": {
|
|
220
|
+
const partId = crypto.randomUUID();
|
|
221
|
+
writer.write({ type: "text-start", id: partId });
|
|
222
222
|
for (const char of part.text) {
|
|
223
|
-
writer.write({ type:
|
|
224
|
-
await sleep(typingSpeed, abortSignal)
|
|
223
|
+
writer.write({ type: "text-delta", id: partId, delta: char });
|
|
224
|
+
await sleep(typingSpeed, abortSignal);
|
|
225
225
|
}
|
|
226
|
-
writer.write({ type:
|
|
227
|
-
break
|
|
226
|
+
writer.write({ type: "text-end", id: partId });
|
|
227
|
+
break;
|
|
228
228
|
}
|
|
229
229
|
|
|
230
|
-
case
|
|
231
|
-
const partId = crypto.randomUUID()
|
|
232
|
-
writer.write({ type:
|
|
230
|
+
case "reasoning": {
|
|
231
|
+
const partId = crypto.randomUUID();
|
|
232
|
+
writer.write({ type: "reasoning-start", id: partId });
|
|
233
233
|
for (const char of part.text) {
|
|
234
|
-
writer.write({ type:
|
|
235
|
-
await sleep(typingSpeed, abortSignal)
|
|
234
|
+
writer.write({ type: "reasoning-delta", id: partId, delta: char });
|
|
235
|
+
await sleep(typingSpeed, abortSignal);
|
|
236
236
|
}
|
|
237
|
-
writer.write({ type:
|
|
238
|
-
break
|
|
237
|
+
writer.write({ type: "reasoning-end", id: partId });
|
|
238
|
+
break;
|
|
239
239
|
}
|
|
240
240
|
|
|
241
|
-
case
|
|
241
|
+
case "tool-call": {
|
|
242
242
|
writer.write({
|
|
243
|
-
type:
|
|
243
|
+
type: "tool-input-available",
|
|
244
244
|
toolCallId: part.toolCallId,
|
|
245
245
|
toolName: part.toolName,
|
|
246
246
|
input: part.args,
|
|
247
|
-
})
|
|
247
|
+
});
|
|
248
248
|
if (part.result !== undefined) {
|
|
249
249
|
// Brief pause to simulate tool execution
|
|
250
|
-
await sleep(300, abortSignal)
|
|
250
|
+
await sleep(300, abortSignal);
|
|
251
251
|
writer.write({
|
|
252
|
-
type:
|
|
252
|
+
type: "tool-output-available",
|
|
253
253
|
toolCallId: part.toolCallId,
|
|
254
254
|
output: part.result,
|
|
255
|
-
})
|
|
255
|
+
});
|
|
256
256
|
}
|
|
257
|
-
break
|
|
257
|
+
break;
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
}
|
package/src/lib/easing.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const
|
|
1
|
+
export const EASE_OUT_QUINT = [0.23, 1, 0.32, 1] as const;
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
* - VITE_DATADOG_SITE (optional, defaults to datadoghq.com)
|
|
10
10
|
*/
|
|
11
11
|
export const DATADOG_CONFIG = {
|
|
12
|
-
applicationId: import.meta.env.VITE_DATADOG_APPLICATION_ID ??
|
|
13
|
-
clientToken: import.meta.env.VITE_DATADOG_CLIENT_TOKEN ??
|
|
14
|
-
site: import.meta.env.VITE_DATADOG_SITE ??
|
|
15
|
-
service:
|
|
16
|
-
} as const
|
|
12
|
+
applicationId: import.meta.env.VITE_DATADOG_APPLICATION_ID ?? "",
|
|
13
|
+
clientToken: import.meta.env.VITE_DATADOG_CLIENT_TOKEN ?? "",
|
|
14
|
+
site: import.meta.env.VITE_DATADOG_SITE ?? "datadoghq.com",
|
|
15
|
+
service: "gram-elements",
|
|
16
|
+
} as const;
|