@apify/mcp-widgets 0.2.30 → 0.3.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.
Files changed (39) hide show
  1. package/README.md +4 -2
  2. package/dist/components/actor_run/actor_run_preview_skeleton.d.ts +2 -0
  3. package/dist/components/actor_run/actor_run_preview_skeleton.d.ts.map +1 -0
  4. package/dist/components/actor_run/actor_run_preview_skeleton.js +34 -0
  5. package/dist/components/actor_run/actor_run_table.styles.d.ts +3 -0
  6. package/dist/components/actor_run/actor_run_table.styles.d.ts.map +1 -0
  7. package/dist/components/actor_run/actor_run_table.styles.js +23 -0
  8. package/dist/components/actor_run/actor_run_widget.d.ts +30 -0
  9. package/dist/components/actor_run/actor_run_widget.d.ts.map +1 -0
  10. package/dist/components/actor_run/actor_run_widget.js +322 -0
  11. package/dist/components/actor_run/actor_run_widget.test_ids.d.ts +15 -0
  12. package/dist/components/actor_run/actor_run_widget.test_ids.d.ts.map +1 -0
  13. package/dist/components/actor_run/actor_run_widget.test_ids.js +17 -0
  14. package/dist/components/actor_run/live_actor_run_widget.d.ts +13 -0
  15. package/dist/components/actor_run/live_actor_run_widget.d.ts.map +1 -0
  16. package/dist/components/actor_run/live_actor_run_widget.js +30 -0
  17. package/dist/components/actor_run/use_actor_run_polling.d.ts +28 -0
  18. package/dist/components/actor_run/use_actor_run_polling.d.ts.map +1 -0
  19. package/dist/components/actor_run/use_actor_run_polling.js +202 -0
  20. package/dist/components/actor_search/actor_search_widget.js +1 -1
  21. package/dist/components/widget_layout/widget_layout.js +3 -2
  22. package/dist/files_hash_at_build.txt +1 -1
  23. package/dist/host/types.d.ts +15 -0
  24. package/dist/host/types.d.ts.map +1 -1
  25. package/dist/index.d.ts +4 -2
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +5 -3
  28. package/dist/test_ids.d.ts +1 -0
  29. package/dist/test_ids.d.ts.map +1 -1
  30. package/dist/test_ids.js +2 -1
  31. package/dist/types.d.ts +21 -0
  32. package/dist/types.d.ts.map +1 -1
  33. package/dist/utils/formatting.d.ts +7 -0
  34. package/dist/utils/formatting.d.ts.map +1 -1
  35. package/dist/utils/formatting.js +22 -1
  36. package/dist/utils/run_status.d.ts +2 -0
  37. package/dist/utils/run_status.d.ts.map +1 -0
  38. package/dist/utils/run_status.js +10 -0
  39. package/package.json +6 -4
package/README.md CHANGED
@@ -22,9 +22,11 @@ import { ActorSearchWidget, WidgetHostProvider } from '@apify/mcp-widgets';
22
22
  </WidgetHostProvider>;
23
23
  ```
24
24
 
25
- `WidgetHost` has one required member, `openLink`. `requestDisplayMode` and `maxHeight` are optional: hosts with a single display mode or no height limit leave them out.
25
+ `ActorRunWidget` only renders what it is given. `LiveActorRunWidget` keeps a run fresh on its own through the host's `callTool`, starting from as little as `{ runId }`.
26
26
 
27
- Exports: `ActorSearchWidget`, `ActorSearchDetail`, `ActorCard`, `ActorSearchResultsSkeleton`, `ActorSearchDetailSkeleton`, `WidgetLayout`, `SkeletonBlock`, `WidgetHostProvider`, `useWidgetHost` and the tool output types. Test ids for Playwright live under `@apify/mcp-widgets/test-ids`.
27
+ `WidgetHost` has one required member, `openLink`. `requestDisplayMode`, `maxHeight`, `isInPage` and `callTool` are optional: hosts with a single display mode, no height limit, an iframe of their own or no way to call MCP tools leave them out.
28
+
29
+ Exports: `ActorSearchWidget`, `ActorRunWidget`, `LiveActorRunWidget`, `ActorSearchDetail`, `ActorCard`, `ActorSearchResultsSkeleton`, `ActorSearchDetailSkeleton`, `WidgetLayout`, `SkeletonBlock`, `WidgetHostProvider`, `useWidgetHost` and the tool output types. Test ids for Playwright live under `@apify/mcp-widgets/test-ids`.
28
30
 
29
31
  ## Development
30
32
 
@@ -0,0 +1,2 @@
1
+ export declare function ActorRunPreviewSkeleton(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=actor_run_preview_skeleton.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor_run_preview_skeleton.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/actor_run_preview_skeleton.tsx"],"names":[],"mappings":"AASA,wBAAgB,uBAAuB,4CA6BtC"}
@@ -0,0 +1,34 @@
1
+ import { SkeletonBlock } from "../skeleton_block/skeleton_block.js";
2
+ import { PreviewTableWrapper } from "./actor_run_table.styles.js";
3
+ import { actorRunWidgetTestIds } from "./actor_run_widget.test_ids.js";
4
+ import { Table, TableBody, TableCell, TableHead, TableHeadCell, TableHeadRow, TableRow } from "@apify/ui-library";
5
+ import { jsx, jsxs } from "react/jsx-runtime";
6
+ //#region src/components/actor_run/actor_run_preview_skeleton.tsx
7
+ var COLUMNS = [
8
+ 0,
9
+ 1,
10
+ 2,
11
+ 3,
12
+ 4
13
+ ];
14
+ var ROWS = [
15
+ 0,
16
+ 1,
17
+ 2,
18
+ 3,
19
+ 4
20
+ ];
21
+ function ActorRunPreviewSkeleton() {
22
+ return /* @__PURE__ */ jsx(PreviewTableWrapper, { children: /* @__PURE__ */ jsxs(Table, {
23
+ "data-test": actorRunWidgetTestIds.PREVIEW_SKELETON,
24
+ children: [/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(TableHeadRow, { children: COLUMNS.map((column) => /* @__PURE__ */ jsx(TableHeadCell, { children: /* @__PURE__ */ jsx(SkeletonBlock, { style: {
25
+ width: `${60 + column * 10}px`,
26
+ height: "12px"
27
+ } }) }, column)) }) }), /* @__PURE__ */ jsx(TableBody, { children: ROWS.map((row) => /* @__PURE__ */ jsx(TableRow, { children: COLUMNS.map((column) => /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(SkeletonBlock, { style: {
28
+ width: `${70 + (row + column) % 3 * 30}px`,
29
+ height: "14px"
30
+ } }) }, column)) }, row)) })]
31
+ }) });
32
+ }
33
+ //#endregion
34
+ export { ActorRunPreviewSkeleton };
@@ -0,0 +1,3 @@
1
+ /** Shared by the preview table and its skeleton: a few rows tall, scrolling past that, with long values cut short. */
2
+ export declare const PreviewTableWrapper: import('styled-components/dist/types').IStyledComponentBase<"web", import('styled-components').FastOmit<import('@apify/ui-library').HorizontallyScrollableTableWrapperProps, never>> & string & Omit<import('react').FC<import('@apify/ui-library').HorizontallyScrollableTableWrapperProps>, keyof import('react').Component<any, {}, any>>;
3
+ //# sourceMappingURL=actor_run_table.styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor_run_table.styles.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/actor_run_table.styles.ts"],"names":[],"mappings":"AAIA,sHAAsH;AACtH,eAAO,MAAM,mBAAmB,8UAgB/B,CAAC"}
@@ -0,0 +1,23 @@
1
+ import styled from "styled-components";
2
+ import { HorizontallyScrollableTableWrapper, tableClassNames } from "@apify/ui-library";
3
+ //#region src/components/actor_run/actor_run_table.styles.ts
4
+ /** Shared by the preview table and its skeleton: a few rows tall, scrolling past that, with long values cut short. */
5
+ var PreviewTableWrapper = styled(HorizontallyScrollableTableWrapper)`
6
+ .${tableClassNames.WRAPPER_SCROLLABLE} {
7
+ max-height: 265px;
8
+ overflow-y: auto;
9
+ }
10
+
11
+ .${tableClassNames.HEAD_CELL} {
12
+ white-space: nowrap;
13
+ }
14
+
15
+ .${tableClassNames.BODY_CELL} {
16
+ max-width: 240px;
17
+ overflow: hidden;
18
+ text-overflow: ellipsis;
19
+ white-space: nowrap;
20
+ }
21
+ `;
22
+ //#endregion
23
+ export { PreviewTableWrapper };
@@ -0,0 +1,30 @@
1
+ import { ActorRunOutput, PreviewItem } from '../../types';
2
+ /**
3
+ * The collapsed widget's height in a host's own page (`isInPage`) when its header fits on one line, for the host
4
+ * to hold its place before it mounts. The `CollapsedInPage` story checks it against the rendered widget.
5
+ */
6
+ export declare const ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX = 62;
7
+ type ActorRunWidgetProps = {
8
+ /** `structuredContent` of `call-actor` / `get-actor-run`; `null` until the run is known. */
9
+ output: ActorRunOutput | null;
10
+ /** Shown instead of the loading state when the tool call failed before a run existed. */
11
+ errorMessage?: string | null;
12
+ costUsd?: number;
13
+ /** Results in the default dataset; defaults to the count in `output`, which can trail a run that just finished. */
14
+ itemCount?: number;
15
+ /** The line below the widget saying how many results the run found; off where the host replies in its own words. */
16
+ shouldShowResultSummary?: boolean;
17
+ pictureUrl?: string;
18
+ /** First items of the default dataset once the run has succeeded; `null` while they load, `[]` when there are none. */
19
+ previewItems: PreviewItem[] | null;
20
+ /** The Actor's page, from `fetch-actor-details`; defaults to its Store page on apify.com. */
21
+ actorUrl?: string;
22
+ /** The host lost track of the run, so its last known status may be stale and is not shown. */
23
+ isStatusUnknown?: boolean;
24
+ /** Label of the button opening the run, e.g. a shorter one where the host already is Apify. */
25
+ openRunButtonLabel?: string;
26
+ };
27
+ /** Renders a `call-actor` / `get-actor-run` result; the host keeps `output` fresh while the run is in flight. */
28
+ export declare function ActorRunWidget({ output, errorMessage, costUsd, itemCount, pictureUrl, previewItems, actorUrl: actorPageUrl, shouldShowResultSummary, openRunButtonLabel, isStatusUnknown, }: ActorRunWidgetProps): import("react/jsx-runtime").JSX.Element;
29
+ export {};
30
+ //# sourceMappingURL=actor_run_widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor_run_widget.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/actor_run_widget.tsx"],"names":[],"mappings":"AAsBA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAQ/D;;;GAGG;AACH,eAAO,MAAM,oCAAoC,KAAK,CAAC;AAEvD,KAAK,mBAAmB,GAAG;IACvB,4FAA4F;IAC5F,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,mHAAmH;IACnH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,oHAAoH;IACpH,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAClC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,uHAAuH;IACvH,YAAY,EAAE,WAAW,EAAE,GAAG,IAAI,CAAC;IACnC,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8FAA8F;IAC9F,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,+FAA+F;IAC/F,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC/B,CAAC;AAsQF,iHAAiH;AACjH,wBAAgB,cAAc,CAAC,EAC3B,MAAM,EACN,YAAY,EACZ,OAAO,EACP,SAA+D,EAC/D,UAAU,EACV,YAAY,EACZ,QAAQ,EAAE,YAAY,EACtB,uBAA8B,EAC9B,kBAAoC,EACpC,eAAuB,GAC1B,EAAE,mBAAmB,2CAqGrB"}
@@ -0,0 +1,322 @@
1
+ import { useWidgetHost } from "../../host/widget_host_provider.js";
2
+ import { capitalize, formatDuration, formatTimestamp, humanizeActorName } from "../../utils/formatting.js";
3
+ import { isRunFinished } from "../../utils/run_status.js";
4
+ import { WidgetLayout } from "../widget_layout/widget_layout.js";
5
+ import { PreviewTableWrapper } from "./actor_run_table.styles.js";
6
+ import { actorRunWidgetTestIds } from "./actor_run_widget.test_ids.js";
7
+ import { ActorRunPreviewSkeleton } from "./actor_run_preview_skeleton.js";
8
+ import styled from "styled-components";
9
+ import { CheckIcon, ChevronDownIcon, CrossIcon, LoaderIcon } from "@apify/ui-icons";
10
+ import { ActorAvatar, Badge, Button, IconButton, Table, TableBody, TableCell, TableHead, TableHeadCell, TableHeadRow, TableRow, Text, theme } from "@apify/ui-library";
11
+ import { useEffect, useState } from "react";
12
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
13
+ //#region src/components/actor_run/actor_run_widget.tsx
14
+ /**
15
+ * The collapsed widget's height in a host's own page (`isInPage`) when its header fits on one line, for the host
16
+ * to hold its place before it mounts. The `CollapsedInPage` story checks it against the rendered widget.
17
+ */
18
+ var ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX = 62;
19
+ var FAILED_BADGE = {
20
+ variant: "danger",
21
+ Icon: CrossIcon
22
+ };
23
+ var IN_PROGRESS_BADGE = {
24
+ variant: "primary_blue",
25
+ Icon: LoaderIcon
26
+ };
27
+ var NEUTRAL_BADGE = { variant: "neutral" };
28
+ var BADGE_BY_STATUS = {
29
+ SUCCEEDED: {
30
+ variant: "success",
31
+ Icon: CheckIcon
32
+ },
33
+ FAILED: FAILED_BADGE,
34
+ ABORTED: FAILED_BADGE,
35
+ "TIMED-OUT": FAILED_BADGE,
36
+ RUNNING: IN_PROGRESS_BADGE,
37
+ READY: IN_PROGRESS_BADGE,
38
+ "TIMING-OUT": IN_PROGRESS_BADGE,
39
+ ABORTING: IN_PROGRESS_BADGE
40
+ };
41
+ var Container = styled.div`
42
+ display: flex;
43
+ flex-direction: column;
44
+ width: 100%;
45
+ background: ${theme.color.neutral.background};
46
+ border: 1px solid ${theme.color.neutral.separatorSubtle};
47
+ border-radius: ${theme.radius.radius12};
48
+ padding: ${theme.space.space16};
49
+ `;
50
+ var Header = styled.div`
51
+ display: flex;
52
+ align-items: center;
53
+ gap: ${theme.space.space16};
54
+ width: 100%;
55
+ min-height: 24px;
56
+ `;
57
+ var ExpandToggle = styled.div`
58
+ display: flex;
59
+ flex-shrink: 0;
60
+
61
+ /* IconButton outlines itself on any focus, a mouse click included; keyboard focus keeps the browser's ring. */
62
+ button,
63
+ button:hover,
64
+ button:focus,
65
+ button:active {
66
+ border-color: transparent;
67
+ }
68
+
69
+ button:focus:not(:focus-visible) {
70
+ outline: none;
71
+ }
72
+
73
+ svg {
74
+ transition: transform 200ms ease-in-out;
75
+ transform: rotate(${({ $isExpanded }) => $isExpanded ? "180deg" : "0deg"});
76
+ }
77
+ `;
78
+ var Collapsible = styled.div`
79
+ display: grid;
80
+ grid-template-rows: ${({ $isExpanded }) => $isExpanded ? "1fr" : "0fr"};
81
+ visibility: ${({ $isExpanded }) => $isExpanded ? "visible" : "hidden"};
82
+ /* Shown at once on expand, hidden only once the collapse has finished. */
83
+ transition:
84
+ grid-template-rows 200ms ease-in-out,
85
+ visibility 0s linear ${({ $isExpanded }) => $isExpanded ? "0s" : "200ms"};
86
+ `;
87
+ var CollapsibleBody = styled.div`
88
+ min-height: 0;
89
+ overflow: hidden;
90
+ `;
91
+ var Body = styled.div`
92
+ display: flex;
93
+ flex-direction: column;
94
+ gap: ${theme.space.space8};
95
+ padding-top: ${theme.space.space8};
96
+ `;
97
+ var ActorName = styled.div`
98
+ display: flex;
99
+ align-items: center;
100
+ gap: ${theme.space.space6};
101
+ `;
102
+ var ActorNameLink = styled.a`
103
+ color: ${theme.color.neutral.text};
104
+ text-decoration: underline;
105
+ text-decoration-color: ${theme.color.neutral.text};
106
+ cursor: pointer;
107
+ ${theme.typography.shared.desktop.bodyMMedium};
108
+
109
+ &:hover {
110
+ color: ${theme.color.primary.action};
111
+ text-decoration-color: ${theme.color.primary.action};
112
+ }
113
+ `;
114
+ var StatusRow = styled.div`
115
+ display: flex;
116
+ align-items: center;
117
+ gap: ${theme.space.space16};
118
+ flex-wrap: nowrap;
119
+ overflow: hidden;
120
+ flex: 1;
121
+ `;
122
+ var MetadataRow = styled.div`
123
+ display: flex;
124
+ align-items: center;
125
+ gap: ${theme.space.space8};
126
+ flex-wrap: nowrap;
127
+ `;
128
+ function MetadataText({ children, ...rest }) {
129
+ return /* @__PURE__ */ jsx(Text, {
130
+ as: "span",
131
+ type: "body",
132
+ size: "small",
133
+ weight: "medium",
134
+ color: theme.color.neutral.text,
135
+ ...rest,
136
+ children
137
+ });
138
+ }
139
+ var Divider = styled.span`
140
+ color: ${theme.color.neutral.textMuted};
141
+ font-size: 12px;
142
+ display: flex;
143
+ align-items: center;
144
+ `;
145
+ var TableGradientOverlay = styled.div`
146
+ position: sticky;
147
+ bottom: 0;
148
+ left: 0;
149
+ width: 100%;
150
+ height: 86px;
151
+ margin-top: -86px;
152
+ background: linear-gradient(178.84deg, transparent 13.4%, ${theme.color.neutral.background} 81.59%);
153
+ pointer-events: none;
154
+ border-radius: 0 0 ${theme.radius.radius12} ${theme.radius.radius12};
155
+ z-index: 2;
156
+ `;
157
+ var EmptyState = styled.div`
158
+ padding: ${theme.space.space24} ${theme.space.space16};
159
+ text-align: center;
160
+ color: ${theme.color.neutral.textMuted};
161
+ display: flex;
162
+ flex-direction: column;
163
+ align-items: center;
164
+ gap: ${theme.space.space8};
165
+ `;
166
+ var Footer = styled.div`
167
+ display: flex;
168
+ align-items: center;
169
+ `;
170
+ function formatCell(value) {
171
+ if (value == null) return "—";
172
+ if (Array.isArray(value)) return `${value.length} items`;
173
+ if (typeof value === "object") return `${Object.keys(value).length} fields`;
174
+ return String(value) || "—";
175
+ }
176
+ function PreviewTable({ items }) {
177
+ const columns = Object.keys(items[0]);
178
+ return /* @__PURE__ */ jsxs(PreviewTableWrapper, { children: [/* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(TableHeadRow, { children: columns.map((column) => /* @__PURE__ */ jsx(TableHeadCell, { children: capitalize(column) }, column)) }) }), /* @__PURE__ */ jsx(TableBody, { children: items.map((item, index) => /* @__PURE__ */ jsx(TableRow, {
179
+ "data-test": actorRunWidgetTestIds.PREVIEW_ROW,
180
+ children: columns.map((column) => /* @__PURE__ */ jsx(TableCell, { children: formatCell(item[column]) }, column))
181
+ }, index)) })] }), items.length > 3 && /* @__PURE__ */ jsx(TableGradientOverlay, {})] });
182
+ }
183
+ /** Counts up every second while the run is going; the host only re-renders the widget when it polls. */
184
+ function RunDuration({ startedAt, finishedAt }) {
185
+ const [, setNow] = useState(Date.now);
186
+ useEffect(() => {
187
+ if (finishedAt) return void 0;
188
+ const interval = setInterval(() => setNow(Date.now()), 1e3);
189
+ return () => clearInterval(interval);
190
+ }, [finishedAt]);
191
+ return formatDuration(startedAt, finishedAt);
192
+ }
193
+ var RUN_PREVIEW_EMPTY_TEXT = {
194
+ READY: "The Actor is ready to run.",
195
+ UNKNOWN: "Could not load the run's status. Open the run to see it."
196
+ };
197
+ function RunPreview({ status, previewItems }) {
198
+ if (previewItems && previewItems.length > 0) return /* @__PURE__ */ jsx(PreviewTable, { items: previewItems });
199
+ if (!isRunFinished(status) && status !== "READY" && status !== "UNKNOWN" || status === "SUCCEEDED" && previewItems === null) return /* @__PURE__ */ jsx(ActorRunPreviewSkeleton, {});
200
+ return /* @__PURE__ */ jsx(EmptyState, {
201
+ "data-test": actorRunWidgetTestIds.EMPTY_STATE,
202
+ children: /* @__PURE__ */ jsx(Text, {
203
+ type: "body",
204
+ size: "small",
205
+ color: theme.color.neutral.textMuted,
206
+ children: RUN_PREVIEW_EMPTY_TEXT[status] ?? "No results available."
207
+ })
208
+ });
209
+ }
210
+ function RunPlaceholder({ errorMessage }) {
211
+ return /* @__PURE__ */ jsx(WidgetLayout, { children: /* @__PURE__ */ jsx(Container, {
212
+ "data-test": actorRunWidgetTestIds.ROOT,
213
+ children: /* @__PURE__ */ jsx(EmptyState, { children: errorMessage ? /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx(Badge, {
214
+ variant: "danger",
215
+ size: "small",
216
+ LeadingIcon: CrossIcon,
217
+ children: "Failed"
218
+ }), /* @__PURE__ */ jsx(Text, {
219
+ type: "body",
220
+ size: "small",
221
+ "data-test": actorRunWidgetTestIds.ERROR,
222
+ children: errorMessage
223
+ })] }) : /* @__PURE__ */ jsx(Text, {
224
+ type: "body",
225
+ size: "small",
226
+ color: theme.color.neutral.textMuted,
227
+ "data-test": actorRunWidgetTestIds.LOADING,
228
+ children: "Loading Actor run data ..."
229
+ }) })
230
+ }) });
231
+ }
232
+ /** Renders a `call-actor` / `get-actor-run` result; the host keeps `output` fresh while the run is in flight. */
233
+ function ActorRunWidget({ output, errorMessage, costUsd, itemCount = output?.storages?.datasets?.default?.itemCount ?? 0, pictureUrl, previewItems, actorUrl: actorPageUrl, shouldShowResultSummary = true, openRunButtonLabel = "View on Apify", isStatusUnknown = false }) {
234
+ const { openLink } = useWidgetHost();
235
+ const [isExpanded, setIsExpanded] = useState(false);
236
+ if (!output?.runId) return /* @__PURE__ */ jsx(RunPlaceholder, { errorMessage });
237
+ const { runId, startedAt, finishedAt } = output;
238
+ const status = isStatusUnknown ? "UNKNOWN" : output.status || "RUNNING";
239
+ const statusUpper = status.toUpperCase();
240
+ const badge = BADGE_BY_STATUS[statusUpper] ?? NEUTRAL_BADGE;
241
+ const { actorName } = output;
242
+ const actorTitle = actorName ? humanizeActorName(actorName.split("/").at(-1) ?? actorName) : "Unknown Actor";
243
+ const actorUrl = actorPageUrl ?? (actorName && `https://apify.com/${actorName}`);
244
+ const runUrl = output.apifyConsoleUrl ?? `https://console.apify.com/actors/runs/${runId}`;
245
+ return /* @__PURE__ */ jsxs(WidgetLayout, { children: [/* @__PURE__ */ jsxs(Container, {
246
+ "data-test": actorRunWidgetTestIds.ROOT,
247
+ children: [/* @__PURE__ */ jsxs(Header, { children: [
248
+ /* @__PURE__ */ jsxs(ActorName, { children: [/* @__PURE__ */ jsx(ActorAvatar, {
249
+ size: 20,
250
+ name: actorTitle,
251
+ url: pictureUrl
252
+ }), actorUrl ? /* @__PURE__ */ jsx(ActorNameLink, {
253
+ href: actorUrl,
254
+ "data-test": actorRunWidgetTestIds.ACTOR_LINK,
255
+ onClick: (event) => {
256
+ event.preventDefault();
257
+ openLink(actorUrl);
258
+ },
259
+ children: actorTitle
260
+ }) : /* @__PURE__ */ jsx(Text, {
261
+ weight: "medium",
262
+ children: actorTitle
263
+ })] }),
264
+ /* @__PURE__ */ jsxs(StatusRow, { children: [/* @__PURE__ */ jsx(Badge, {
265
+ variant: badge.variant,
266
+ size: "small",
267
+ LeadingIcon: badge.Icon,
268
+ "data-test": actorRunWidgetTestIds.STATUS,
269
+ children: capitalize(status.toLowerCase())
270
+ }), /* @__PURE__ */ jsxs(MetadataRow, { children: [
271
+ typeof costUsd === "number" && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs(MetadataText, { children: ["$", costUsd.toFixed(3)] }), /* @__PURE__ */ jsx(Divider, { children: "|" })] }),
272
+ /* @__PURE__ */ jsx(MetadataText, { children: startedAt ? formatTimestamp(startedAt) : "—" }),
273
+ /* @__PURE__ */ jsx(Divider, { children: "|" }),
274
+ /* @__PURE__ */ jsx(MetadataText, {
275
+ "data-test": actorRunWidgetTestIds.DURATION,
276
+ children: startedAt && !isStatusUnknown ? /* @__PURE__ */ jsx(RunDuration, {
277
+ startedAt,
278
+ finishedAt
279
+ }) : "—"
280
+ })
281
+ ] })] }),
282
+ /* @__PURE__ */ jsx(ExpandToggle, {
283
+ $isExpanded: isExpanded,
284
+ children: /* @__PURE__ */ jsx(IconButton, {
285
+ Icon: ChevronDownIcon,
286
+ size: "small",
287
+ "aria-label": isExpanded ? "Hide run details" : "Show run details",
288
+ "aria-expanded": isExpanded,
289
+ "data-test": actorRunWidgetTestIds.TOGGLE,
290
+ onClick: () => setIsExpanded((wasExpanded) => !wasExpanded)
291
+ })
292
+ })
293
+ ] }), /* @__PURE__ */ jsx(Collapsible, {
294
+ $isExpanded: isExpanded,
295
+ children: /* @__PURE__ */ jsx(CollapsibleBody, { children: /* @__PURE__ */ jsxs(Body, { children: [/* @__PURE__ */ jsx(RunPreview, {
296
+ status: statusUpper,
297
+ previewItems
298
+ }), /* @__PURE__ */ jsx(Footer, { children: /* @__PURE__ */ jsx(Button, {
299
+ variant: "secondary",
300
+ size: "small",
301
+ "data-test": actorRunWidgetTestIds.OPEN_RUN_BUTTON,
302
+ onClick: () => openLink(runUrl),
303
+ children: openRunButtonLabel
304
+ }) })] }) })
305
+ })]
306
+ }), shouldShowResultSummary && statusUpper === "SUCCEEDED" && itemCount > 0 && /* @__PURE__ */ jsxs(Text, {
307
+ as: "p",
308
+ color: theme.color.neutral.text,
309
+ "data-test": actorRunWidgetTestIds.RESULT_SUMMARY,
310
+ children: [
311
+ "The ",
312
+ actorTitle,
313
+ " found ",
314
+ itemCount,
315
+ " result",
316
+ itemCount !== 1 ? "s" : "",
317
+ ". You can visit results via the provided link."
318
+ ]
319
+ })] });
320
+ }
321
+ //#endregion
322
+ export { ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX, ActorRunWidget };
@@ -0,0 +1,15 @@
1
+ export declare const actorRunWidgetTestIds: {
2
+ readonly ROOT: "actor-run-widget";
3
+ readonly LOADING: "actor-run-widget-loading";
4
+ readonly ERROR: "actor-run-widget-error";
5
+ readonly STATUS: "actor-run-widget-status";
6
+ readonly DURATION: "actor-run-widget-duration";
7
+ readonly ACTOR_LINK: "actor-run-widget-actor-link";
8
+ readonly TOGGLE: "actor-run-widget-toggle";
9
+ readonly PREVIEW_ROW: "actor-run-widget-preview-row";
10
+ readonly PREVIEW_SKELETON: "actor-run-widget-preview-skeleton";
11
+ readonly EMPTY_STATE: "actor-run-widget-empty-state";
12
+ readonly OPEN_RUN_BUTTON: "actor-run-widget-open-run-button";
13
+ readonly RESULT_SUMMARY: "actor-run-widget-result-summary";
14
+ };
15
+ //# sourceMappingURL=actor_run_widget.test_ids.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"actor_run_widget.test_ids.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/actor_run_widget.test_ids.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;CAaxB,CAAC"}
@@ -0,0 +1,17 @@
1
+ //#region src/components/actor_run/actor_run_widget.test_ids.ts
2
+ var actorRunWidgetTestIds = {
3
+ ROOT: "actor-run-widget",
4
+ LOADING: "actor-run-widget-loading",
5
+ ERROR: "actor-run-widget-error",
6
+ STATUS: "actor-run-widget-status",
7
+ DURATION: "actor-run-widget-duration",
8
+ ACTOR_LINK: "actor-run-widget-actor-link",
9
+ TOGGLE: "actor-run-widget-toggle",
10
+ PREVIEW_ROW: "actor-run-widget-preview-row",
11
+ PREVIEW_SKELETON: "actor-run-widget-preview-skeleton",
12
+ EMPTY_STATE: "actor-run-widget-empty-state",
13
+ OPEN_RUN_BUTTON: "actor-run-widget-open-run-button",
14
+ RESULT_SUMMARY: "actor-run-widget-result-summary"
15
+ };
16
+ //#endregion
17
+ export { actorRunWidgetTestIds };
@@ -0,0 +1,13 @@
1
+ import { ComponentProps } from 'react';
2
+ import { ActorRunOutput } from '../../types';
3
+ import { ActorRunWidget } from './actor_run_widget';
4
+ type LiveActorRunWidgetProps = Pick<ComponentProps<typeof ActorRunWidget>, 'output' | 'costUsd' | 'errorMessage' | 'shouldShowResultSummary' | 'openRunButtonLabel'> & {
5
+ /** Called once when polling sees the run finish; not called for a run that had already finished. */
6
+ onRunFinished?: (output: ActorRunOutput) => void;
7
+ /** Wait before the run's next refresh, after `pollCount` refreshes. Absent to use the default back-off. */
8
+ pollDelayMs?: (pollCount: number) => number;
9
+ };
10
+ /** The run widget, kept up to date through the host's `callTool` until the run finishes. */
11
+ export declare function LiveActorRunWidget(props: LiveActorRunWidgetProps): import("react/jsx-runtime").JSX.Element;
12
+ export {};
13
+ //# sourceMappingURL=live_actor_run_widget.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"live_actor_run_widget.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/live_actor_run_widget.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAE5C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAGpD,KAAK,uBAAuB,GAAG,IAAI,CAC/B,cAAc,CAAC,OAAO,cAAc,CAAC,EACrC,QAAQ,GAAG,SAAS,GAAG,cAAc,GAAG,yBAAyB,GAAG,oBAAoB,CAC3F,GAAG;IACA,oGAAoG;IACpG,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,2GAA2G;IAC3G,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C,CAAC;AAiCF,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,2CAGhE"}
@@ -0,0 +1,30 @@
1
+ import { ActorRunWidget } from "./actor_run_widget.js";
2
+ import { useActorRunPolling } from "./use_actor_run_polling.js";
3
+ import { jsx } from "react/jsx-runtime";
4
+ //#region src/components/actor_run/live_actor_run_widget.tsx
5
+ function PolledActorRunWidget({ output, costUsd, errorMessage, shouldShowResultSummary, openRunButtonLabel, onRunFinished, pollDelayMs }) {
6
+ const { run, itemCount, pictureUrl, actorUrl, previewItems, isStatusUnknown } = useActorRunPolling({
7
+ output,
8
+ costUsd,
9
+ onRunFinished,
10
+ pollDelayMs
11
+ });
12
+ return /* @__PURE__ */ jsx(ActorRunWidget, {
13
+ output: run?.output ?? output,
14
+ errorMessage,
15
+ costUsd: run?.costUsd,
16
+ itemCount,
17
+ pictureUrl,
18
+ actorUrl,
19
+ previewItems,
20
+ isStatusUnknown,
21
+ shouldShowResultSummary,
22
+ openRunButtonLabel
23
+ });
24
+ }
25
+ /** The run widget, kept up to date through the host's `callTool` until the run finishes. */
26
+ function LiveActorRunWidget(props) {
27
+ return /* @__PURE__ */ jsx(PolledActorRunWidget, { ...props }, props.output?.runId);
28
+ }
29
+ //#endregion
30
+ export { LiveActorRunWidget };
@@ -0,0 +1,28 @@
1
+ import { ActorRunOutput, PreviewItem } from '../../types';
2
+ /** A `call-actor` / `get-actor-run` result: the run itself plus the cost the server sends in `_meta`. */
3
+ type RunSnapshot = {
4
+ output: ActorRunOutput;
5
+ costUsd?: number;
6
+ };
7
+ /**
8
+ * Keeps a run fresh through the host's `callTool`: polls `get-actor-run` until the run finishes, then
9
+ * loads a preview of its results with `get-dataset-items`, and the Actor's picture with `fetch-actor-details`.
10
+ * `output` is what the host knows about the run; a bare `{ runId }` is enough, and is fetched at once.
11
+ */
12
+ export declare function useActorRunPolling({ output, costUsd, onRunFinished, pollDelayMs, }: {
13
+ output: ActorRunOutput | null;
14
+ costUsd?: number;
15
+ onRunFinished?: (output: ActorRunOutput) => void;
16
+ /** Wait before the run's next refresh, after `pollCount` refreshes. */
17
+ pollDelayMs?: (pollCount: number) => number;
18
+ }): {
19
+ run: RunSnapshot | null;
20
+ itemCount: number;
21
+ pictureUrl: string | undefined;
22
+ actorUrl: string | undefined;
23
+ previewItems: PreviewItem[] | null;
24
+ /** Polling gave up, so the last known status is stale. */
25
+ isStatusUnknown: boolean;
26
+ };
27
+ export {};
28
+ //# sourceMappingURL=use_actor_run_polling.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use_actor_run_polling.d.ts","sourceRoot":"","sources":["../../../src/components/actor_run/use_actor_run_polling.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAM/D,yGAAyG;AACzG,KAAK,WAAW,GAAG;IACf,MAAM,EAAE,cAAc,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAyCF;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,EAC/B,MAAM,EACN,OAAO,EACP,aAAa,EACb,WAAgC,GACnC,EAAE;IACC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;IACjD,uEAAuE;IACvE,WAAW,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/C;;;;;;IAgKO,0DAA0D;;EAGjE"}
@@ -0,0 +1,202 @@
1
+ import { useWidgetHost } from "../../host/widget_host_provider.js";
2
+ import { isRunFinished } from "../../utils/run_status.js";
3
+ import { useEffect, useRef, useState } from "react";
4
+ //#region src/components/actor_run/use_actor_run_polling.ts
5
+ /** Where the MCP server puts a run's cost in a tool result's `_meta`. */
6
+ var ACTOR_RUN_META_KEY = "com.apify/ActorRun";
7
+ var PREVIEW_ITEM_LIMIT = 20;
8
+ var ITEM_COUNT_SETTLE_MS = 3e4;
9
+ var PREVIEW_REFRESH_MS = 5e3;
10
+ var delay = async (ms) => new Promise((resolve) => setTimeout(resolve, ms));
11
+ var getDataset = (output) => output.storages?.datasets?.default;
12
+ /** A revoked token or a run that no longer exists, which no later poll can recover from. */
13
+ var UNRECOVERABLE_ERRORS = [
14
+ "401",
15
+ "Unauthorized",
16
+ "not found"
17
+ ];
18
+ /** Unrecoverable errors in a row before polling gives up; one alone can be a token refresh or a lagging replica. */
19
+ var MAX_UNRECOVERABLE_ERRORS = 3;
20
+ var defaultPollDelayMs = (pollCount) => Math.min((Math.floor(pollCount / 2) * 5 + 5) * 1e3, 6e4);
21
+ function extractUsageTotalUsd(meta) {
22
+ const value = (meta?.[ACTOR_RUN_META_KEY])?.usageTotalUsd;
23
+ return typeof value === "number" ? value : void 0;
24
+ }
25
+ /** Hosts hand a tool error back as a result rather than throwing; this makes it throw. */
26
+ async function callToolOrThrow(callTool, name, args) {
27
+ const response = await callTool(name, args);
28
+ if (response.isError) {
29
+ const message = response.content?.map(({ text }) => text).join(" ");
30
+ throw new Error(`${name} failed: ${message}`);
31
+ }
32
+ return response;
33
+ }
34
+ /**
35
+ * Keeps a run fresh through the host's `callTool`: polls `get-actor-run` until the run finishes, then
36
+ * loads a preview of its results with `get-dataset-items`, and the Actor's picture with `fetch-actor-details`.
37
+ * `output` is what the host knows about the run; a bare `{ runId }` is enough, and is fetched at once.
38
+ */
39
+ function useActorRunPolling({ output, costUsd, onRunFinished, pollDelayMs = defaultPollDelayMs }) {
40
+ const { callTool } = useWidgetHost();
41
+ const callToolRef = useRef(callTool);
42
+ callToolRef.current = callTool;
43
+ const hasCallTool = Boolean(callTool);
44
+ const [run, setRun] = useState(null);
45
+ const [actorInfo, setActorInfo] = useState(null);
46
+ const [preview, setPreview] = useState(null);
47
+ const [isStatusUnknown, setIsStatusUnknown] = useState(false);
48
+ const onRunFinishedRef = useRef(onRunFinished);
49
+ onRunFinishedRef.current = onRunFinished;
50
+ const pollDelayMsRef = useRef(pollDelayMs);
51
+ pollDelayMsRef.current = pollDelayMs;
52
+ const runId = run?.output.runId;
53
+ const status = (run?.output.status ?? "").toUpperCase();
54
+ const actorName = run?.output.actorName;
55
+ const dataset = run ? getDataset(run.output) : void 0;
56
+ useEffect(() => {
57
+ if (!output?.runId) return;
58
+ setRun((current) => {
59
+ if (!current) return {
60
+ output,
61
+ costUsd
62
+ };
63
+ if (isRunFinished(current.output.status) || !output.status) return current;
64
+ return {
65
+ output: {
66
+ ...current.output,
67
+ ...output
68
+ },
69
+ costUsd: costUsd ?? current.costUsd
70
+ };
71
+ });
72
+ }, [output?.runId, output?.status]);
73
+ useEffect(() => {
74
+ const callTool = callToolRef.current;
75
+ if (!callTool || !run || isRunFinished(run.output.status) && run.costUsd !== void 0) return;
76
+ const current = run;
77
+ let isCancelled = false;
78
+ const poll = async () => {
79
+ let pollCount = 0;
80
+ let unrecoverableErrorCount = 0;
81
+ let nextDelayMs = current.output.status && current.costUsd !== void 0 ? pollDelayMsRef.current(pollCount) : 0;
82
+ while (!isCancelled) {
83
+ await delay(nextDelayMs);
84
+ if (isCancelled) return;
85
+ try {
86
+ const response = await callToolOrThrow(callTool, "get-actor-run", {
87
+ runId: current.output.runId,
88
+ waitSecs: 0
89
+ });
90
+ if (isCancelled) return;
91
+ const fetched = response.structuredContent;
92
+ if (!fetched?.runId) throw new Error("get-actor-run returned no run");
93
+ const output = fetched.actorName ? fetched : {
94
+ ...fetched,
95
+ actorName: current.output.actorName
96
+ };
97
+ const costUsd = extractUsageTotalUsd(response._meta);
98
+ unrecoverableErrorCount = 0;
99
+ setIsStatusUnknown(false);
100
+ setRun((latest) => ({
101
+ output,
102
+ costUsd: costUsd ?? latest?.costUsd
103
+ }));
104
+ if (isRunFinished(output.status)) {
105
+ if (!isRunFinished(current.output.status)) onRunFinishedRef.current?.(output);
106
+ return;
107
+ }
108
+ } catch (err) {
109
+ if (isRunFinished(current.output.status)) return;
110
+ unrecoverableErrorCount = err instanceof Error && UNRECOVERABLE_ERRORS.some((text) => err.message.includes(text)) ? unrecoverableErrorCount + 1 : 0;
111
+ if (unrecoverableErrorCount >= MAX_UNRECOVERABLE_ERRORS) {
112
+ setIsStatusUnknown(true);
113
+ return;
114
+ }
115
+ }
116
+ pollCount++;
117
+ nextDelayMs = pollDelayMsRef.current(pollCount);
118
+ }
119
+ };
120
+ poll();
121
+ return () => {
122
+ isCancelled = true;
123
+ };
124
+ }, [
125
+ runId,
126
+ status,
127
+ hasCallTool
128
+ ]);
129
+ const finishedAt = run?.output.finishedAt;
130
+ useEffect(() => {
131
+ const callTool = callToolRef.current;
132
+ if (!callTool || status !== "SUCCEEDED" || !dataset?.id) return;
133
+ const datasetId = dataset.id;
134
+ const settledAtMs = finishedAt ? Math.min(Date.parse(finishedAt), Date.now()) + ITEM_COUNT_SETTLE_MS : 0;
135
+ let isCancelled = false;
136
+ (async () => {
137
+ while (!isCancelled) {
138
+ try {
139
+ const response = await callToolOrThrow(callTool, "get-dataset-items", {
140
+ datasetId,
141
+ limit: PREVIEW_ITEM_LIMIT,
142
+ clean: true
143
+ });
144
+ if (isCancelled) return;
145
+ const content = response.structuredContent;
146
+ const items = Array.isArray(content?.items) ? content.items : [];
147
+ setPreview({
148
+ items,
149
+ total: Math.max(content?.totalItemCount ?? 0, items.length)
150
+ });
151
+ } catch {
152
+ if (!isCancelled) setPreview((current) => current ?? {
153
+ items: [],
154
+ total: 0
155
+ });
156
+ }
157
+ if (Date.now() >= settledAtMs) return;
158
+ await delay(PREVIEW_REFRESH_MS);
159
+ }
160
+ })();
161
+ return () => {
162
+ isCancelled = true;
163
+ };
164
+ }, [
165
+ hasCallTool,
166
+ status,
167
+ dataset?.id,
168
+ finishedAt
169
+ ]);
170
+ useEffect(() => {
171
+ const callTool = callToolRef.current;
172
+ if (!callTool || !actorName || actorInfo !== null) return;
173
+ let isCancelled = false;
174
+ (async () => {
175
+ try {
176
+ const fetched = (await callToolOrThrow(callTool, "fetch-actor-details", {
177
+ actor: actorName,
178
+ output: { metadata: true }
179
+ })).structuredContent?.actorInfo;
180
+ if (!isCancelled && fetched) setActorInfo(fetched);
181
+ } catch {}
182
+ })();
183
+ return () => {
184
+ isCancelled = true;
185
+ };
186
+ }, [
187
+ hasCallTool,
188
+ actorName,
189
+ actorInfo
190
+ ]);
191
+ return {
192
+ run,
193
+ itemCount: Math.max(dataset?.itemCount ?? 0, preview?.total ?? 0),
194
+ pictureUrl: actorInfo?.pictureUrl,
195
+ actorUrl: actorInfo?.url,
196
+ previewItems: preview?.items ?? null,
197
+ /** Polling gave up, so the last known status is stale. */
198
+ isStatusUnknown
199
+ };
200
+ }
201
+ //#endregion
202
+ export { useActorRunPolling };
@@ -1,7 +1,7 @@
1
1
  import { ActorCard } from "../actor_card/actor_card.js";
2
+ import { WidgetLayout } from "../widget_layout/widget_layout.js";
2
3
  import { ActorSearchDetail } from "./actor_search_detail.js";
3
4
  import { ActorSearchResultsSkeleton } from "./actor_search_skeleton.js";
4
- import { WidgetLayout } from "../widget_layout/widget_layout.js";
5
5
  import { actorSearchWidgetTestIds } from "./actor_search_widget.test_ids.js";
6
6
  import styled from "styled-components";
7
7
  import { Box, Message, theme } from "@apify/ui-library";
@@ -8,16 +8,17 @@ var Layout = styled(Box)`
8
8
  flex-direction: column;
9
9
  gap: 1.5rem;
10
10
  width: 100%;
11
- padding: 1rem 1.25rem 2.5rem;
11
+ padding: ${({ $isInPage }) => $isInPage ? "0" : "1rem 1.25rem 2.5rem"};
12
12
  overflow-y: auto;
13
13
  max-height: ${({ $maxHeight }) => $maxHeight ? `${$maxHeight}px` : "none"};
14
14
  `;
15
15
  function WidgetLayout({ children, className, style }) {
16
- const { maxHeight } = useWidgetHost();
16
+ const { maxHeight, isInPage } = useWidgetHost();
17
17
  return /* @__PURE__ */ jsx(Layout, {
18
18
  className,
19
19
  style,
20
20
  $maxHeight: maxHeight,
21
+ $isInPage: isInPage,
21
22
  children
22
23
  });
23
24
  }
@@ -1 +1 @@
1
- a3914439475f226af0d64163f3eb7b9d6d14f76e
1
+ 0c12a27a0062bd16f9aa2c3f2da1c88d9569af2e
@@ -1,4 +1,15 @@
1
1
  export type WidgetDisplayMode = 'inline' | 'fullscreen';
2
+ /** The parts of an MCP `tools/call` result the widgets read. */
3
+ export type ToolCallResult = {
4
+ /** Carries the error message when `isError` is set. */
5
+ content?: {
6
+ type: string;
7
+ text?: string;
8
+ }[];
9
+ structuredContent?: unknown;
10
+ _meta?: Record<string, unknown>;
11
+ isError?: boolean;
12
+ };
2
13
  /** What a widget needs from its host (MCP Apps iframe, Console, Storybook). */
3
14
  export type WidgetHost = {
4
15
  openLink: (url: string) => void;
@@ -6,5 +17,9 @@ export type WidgetHost = {
6
17
  requestDisplayMode?: (mode: WidgetDisplayMode) => Promise<void> | void;
7
18
  /** In px; absent when unconstrained. */
8
19
  maxHeight?: number;
20
+ /** The widget sits in the host's own page, not an iframe, so the page sets the spacing around it. */
21
+ isInPage?: boolean;
22
+ /** Calls a tool on the Apify MCP server, for widgets that refresh their own data. Absent when the host cannot. */
23
+ callTool?: (name: string, args: Record<string, unknown>) => Promise<ToolCallResult>;
9
24
  };
10
25
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAExD,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;CAEtB,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/host/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,YAAY,CAAC;AAExD,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG;IACzB,uDAAuD;IACvD,OAAO,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IAC5C,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB,CAAC;AAEF,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG;IACrB,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,sDAAsD;IACtD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;IACvE,wCAAwC;IACxC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qGAAqG;IACrG,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,kHAAkH;IAClH,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,cAAc,CAAC,CAAC;CACvF,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,12 @@
1
1
  export { ActorCard } from './components/actor_card/actor_card';
2
+ export { ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX, ActorRunWidget } from './components/actor_run/actor_run_widget';
3
+ export { LiveActorRunWidget } from './components/actor_run/live_actor_run_widget';
2
4
  export { ActorSearchDetail } from './components/actor_search/actor_search_detail';
3
5
  export { ActorSearchDetailSkeleton, ActorSearchResultsSkeleton } from './components/actor_search/actor_search_skeleton';
4
6
  export { ActorSearchWidget } from './components/actor_search/actor_search_widget';
5
7
  export { SkeletonBlock } from './components/skeleton_block/skeleton_block';
6
8
  export { WidgetLayout } from './components/widget_layout/widget_layout';
7
- export type { WidgetDisplayMode, WidgetHost } from './host/types';
9
+ export type { ToolCallResult, WidgetDisplayMode, WidgetHost } from './host/types';
8
10
  export { useWidgetHost, WidgetHostProvider } from './host/widget_host_provider';
9
- export type { Actor, ActorDetails, ActorStats, SearchActorsOutput, StructuredPricingInfo } from './types';
11
+ export type { Actor, ActorDetails, ActorRunOutput, ActorStats, SearchActorsOutput, StructuredPricingInfo, } from './types';
10
12
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AACxH,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AACxE,YAAY,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClE,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAChF,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,kBAAkB,EAAE,qBAAqB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,oCAAoC,CAAC;AAC/D,OAAO,EAAE,oCAAoC,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC/G,OAAO,EAAE,kBAAkB,EAAE,MAAM,8CAA8C,CAAC;AAClF,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,yBAAyB,EAAE,0BAA0B,EAAE,MAAM,iDAAiD,CAAC;AACxH,OAAO,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAC;AAC3E,OAAO,EAAE,YAAY,EAAE,MAAM,0CAA0C,CAAC;AACxE,YAAY,EAAE,cAAc,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAClF,OAAO,EAAE,aAAa,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAC;AAChF,YAAY,EACR,KAAK,EACL,YAAY,EACZ,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,qBAAqB,GACxB,MAAM,SAAS,CAAC"}
package/dist/index.js CHANGED
@@ -1,8 +1,10 @@
1
1
  import { WidgetHostProvider, useWidgetHost } from "./host/widget_host_provider.js";
2
2
  import { ActorCard } from "./components/actor_card/actor_card.js";
3
- import { ActorSearchDetail } from "./components/actor_search/actor_search_detail.js";
3
+ import { WidgetLayout } from "./components/widget_layout/widget_layout.js";
4
4
  import { SkeletonBlock } from "./components/skeleton_block/skeleton_block.js";
5
+ import { ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX, ActorRunWidget } from "./components/actor_run/actor_run_widget.js";
6
+ import { LiveActorRunWidget } from "./components/actor_run/live_actor_run_widget.js";
7
+ import { ActorSearchDetail } from "./components/actor_search/actor_search_detail.js";
5
8
  import { ActorSearchDetailSkeleton, ActorSearchResultsSkeleton } from "./components/actor_search/actor_search_skeleton.js";
6
- import { WidgetLayout } from "./components/widget_layout/widget_layout.js";
7
9
  import { ActorSearchWidget } from "./components/actor_search/actor_search_widget.js";
8
- export { ActorCard, ActorSearchDetail, ActorSearchDetailSkeleton, ActorSearchResultsSkeleton, ActorSearchWidget, SkeletonBlock, WidgetHostProvider, WidgetLayout, useWidgetHost };
10
+ export { ACTOR_RUN_WIDGET_COLLAPSED_HEIGHT_PX, ActorCard, ActorRunWidget, ActorSearchDetail, ActorSearchDetailSkeleton, ActorSearchResultsSkeleton, ActorSearchWidget, LiveActorRunWidget, SkeletonBlock, WidgetHostProvider, WidgetLayout, useWidgetHost };
@@ -1,4 +1,5 @@
1
1
  export { actorCardTestIds } from './components/actor_card/actor_card.test_ids.js';
2
+ export { actorRunWidgetTestIds } from './components/actor_run/actor_run_widget.test_ids.js';
2
3
  export { actorSearchDetailTestIds } from './components/actor_search/actor_search_detail.test_ids.js';
3
4
  export { actorSearchSkeletonTestIds } from './components/actor_search/actor_search_skeleton.test_ids.js';
4
5
  export { actorSearchWidgetTestIds } from './components/actor_search/actor_search_widget.test_ids.js';
@@ -1 +1 @@
1
- {"version":3,"file":"test_ids.d.ts","sourceRoot":"","sources":["../src/test_ids.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AAClF,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,EAAE,0BAA0B,EAAE,MAAM,6DAA6D,CAAC;AACzG,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC"}
1
+ {"version":3,"file":"test_ids.d.ts","sourceRoot":"","sources":["../src/test_ids.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,gBAAgB,EAAE,MAAM,gDAAgD,CAAC;AAClF,OAAO,EAAE,qBAAqB,EAAE,MAAM,qDAAqD,CAAC;AAC5F,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC;AACrG,OAAO,EAAE,0BAA0B,EAAE,MAAM,6DAA6D,CAAC;AACzG,OAAO,EAAE,wBAAwB,EAAE,MAAM,2DAA2D,CAAC"}
package/dist/test_ids.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { actorCardTestIds } from "./components/actor_card/actor_card.test_ids.js";
2
+ import { actorRunWidgetTestIds } from "./components/actor_run/actor_run_widget.test_ids.js";
2
3
  import { actorSearchDetailTestIds } from "./components/actor_search/actor_search_detail.test_ids.js";
3
4
  import { actorSearchSkeletonTestIds } from "./components/actor_search/actor_search_skeleton.test_ids.js";
4
5
  import { actorSearchWidgetTestIds } from "./components/actor_search/actor_search_widget.test_ids.js";
5
- export { actorCardTestIds, actorSearchDetailTestIds, actorSearchSkeletonTestIds, actorSearchWidgetTestIds };
6
+ export { actorCardTestIds, actorRunWidgetTestIds, actorSearchDetailTestIds, actorSearchSkeletonTestIds, actorSearchWidgetTestIds };
package/dist/types.d.ts CHANGED
@@ -56,4 +56,25 @@ export type SearchActorsOutput = {
56
56
  } | {
57
57
  actorDetails: ActorDetails;
58
58
  };
59
+ /** One row of a run's default dataset. */
60
+ export type PreviewItem = Record<string, unknown>;
61
+ /** `structuredContent` of `call-actor` / `get-actor-run`, as far as the run widget reads it. */
62
+ export type ActorRunOutput = {
63
+ runId?: string;
64
+ /** The run in Apify Console; the server sets it for Console sessions only. */
65
+ apifyConsoleUrl?: string;
66
+ /** `username/name`. */
67
+ actorName?: string;
68
+ status?: string;
69
+ startedAt?: string;
70
+ finishedAt?: string;
71
+ storages?: {
72
+ datasets?: {
73
+ default?: {
74
+ id: string;
75
+ itemCount?: number;
76
+ };
77
+ };
78
+ };
79
+ };
59
80
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;KACxB,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE;YACZ,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,EAAE,MAAM,CAAC;SACpB,EAAE,CAAC;KACP,EAAE,CAAC;IACJ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,iGAAiG;AACjG,MAAM,MAAM,KAAK,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,kBAAkB,EAAE,qBAAqB,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,CAAC;CACL,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,kBAAkB,GAAG;IAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,YAAY,EAAE,YAAY,CAAA;CAAE,CAAC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,YAAY,EAAE,MAAM,CAAC;KACxB,EAAE,CAAC;IACJ,MAAM,CAAC,EAAE;QACL,KAAK,EAAE,MAAM,CAAC;QACd,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,aAAa,CAAC,EAAE;YACZ,IAAI,EAAE,MAAM,CAAC;YACb,QAAQ,EAAE,MAAM,CAAC;SACpB,EAAE,CAAC;KACP,EAAE,CAAC;IACJ,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,wBAAwB,CAAC,EAAE,OAAO,CAAC;IACnC,qBAAqB,CAAC,EAAE,MAAM,CAAC;CAClC,CAAC;AAEF,MAAM,MAAM,UAAU,GAAG;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,iGAAiG;AACjG,MAAM,MAAM,KAAK,GAAG;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,2CAA2C;IAC3C,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,CAAC;IAClB,kBAAkB,EAAE,qBAAqB,CAAC;CAC7C,CAAC;AAEF,MAAM,MAAM,YAAY,GAAG;IACvB,SAAS,EAAE,KAAK,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,CAAC,EAAE;QACV,IAAI,EAAE,MAAM,CAAC;QACb,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACvC,CAAC;CACL,CAAC;AAEF,kGAAkG;AAClG,MAAM,MAAM,kBAAkB,GAAG;IAAE,YAAY,EAAE,KAAK,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,YAAY,EAAE,YAAY,CAAA;CAAE,CAAC;AAE3G,0CAA0C;AAC1C,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD,gGAAgG;AAChG,MAAM,MAAM,cAAc,GAAG;IACzB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,8EAA8E;IAC9E,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,uBAAuB;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE;QACP,QAAQ,CAAC,EAAE;YACP,OAAO,CAAC,EAAE;gBAAE,EAAE,EAAE,MAAM,CAAC;gBAAC,SAAS,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;SAChD,CAAC;KACL,CAAC;CACL,CAAC"}
@@ -2,4 +2,11 @@ import { StructuredPricingInfo } from '../types';
2
2
  export declare function formatPricing(pricing: StructuredPricingInfo): string;
3
3
  export declare function formatNumber(value: number): string;
4
4
  export declare function formatDecimalNumber(value: number): string;
5
+ /** Runs still in flight measure up to now. */
6
+ export declare function formatDuration(startedAt: string, finishedAt?: string): string;
7
+ /** `YYYY-MM-DD HH:mm` in the viewer's time zone. */
8
+ export declare function formatTimestamp(dateString: string): string;
9
+ export declare const capitalize: (value: string) => string;
10
+ /** `python-example` → `Python Example`. */
11
+ export declare function humanizeActorName(technicalName: string): string;
5
12
  //# sourceMappingURL=formatting.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/utils/formatting.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAyDtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAiBpE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD"}
1
+ {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/utils/formatting.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAyDtD,wBAAgB,aAAa,CAAC,OAAO,EAAE,qBAAqB,GAAG,MAAM,CAiBpE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAIlD;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAU7E;AAED,oDAAoD;AACpD,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAI1D;AAED,eAAO,MAAM,UAAU,GAAI,OAAO,MAAM,WAAmD,CAAC;AAE5F,2CAA2C;AAC3C,wBAAgB,iBAAiB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAE/D"}
@@ -58,5 +58,26 @@ function formatNumber(value) {
58
58
  function formatDecimalNumber(value) {
59
59
  return Number.isInteger(value) ? value.toString() : value.toFixed(1);
60
60
  }
61
+ /** Runs still in flight measure up to now. */
62
+ function formatDuration(startedAt, finishedAt) {
63
+ const end = finishedAt ? new Date(finishedAt).getTime() : Date.now();
64
+ const seconds = Math.max(0, Math.floor((end - new Date(startedAt).getTime()) / 1e3));
65
+ const minutes = Math.floor(seconds / 60);
66
+ const hours = Math.floor(minutes / 60);
67
+ if (hours > 0) return `${hours}h ${minutes % 60}m ${seconds % 60}s`;
68
+ if (minutes > 0) return `${minutes}m ${seconds % 60}s`;
69
+ return `${seconds}s`;
70
+ }
71
+ /** `YYYY-MM-DD HH:mm` in the viewer's time zone. */
72
+ function formatTimestamp(dateString) {
73
+ const date = new Date(dateString);
74
+ const pad = (value) => String(value).padStart(2, "0");
75
+ return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())} ${pad(date.getHours())}:${pad(date.getMinutes())}`;
76
+ }
77
+ var capitalize = (value) => value.charAt(0).toUpperCase() + value.slice(1);
78
+ /** `python-example` → `Python Example`. */
79
+ function humanizeActorName(technicalName) {
80
+ return technicalName.split("-").map(capitalize).join(" ");
81
+ }
61
82
  //#endregion
62
- export { formatDecimalNumber, formatNumber, formatPricing };
83
+ export { capitalize, formatDecimalNumber, formatDuration, formatNumber, formatPricing, formatTimestamp, humanizeActorName };
@@ -0,0 +1,2 @@
1
+ export declare const isRunFinished: (status: string | undefined) => boolean;
2
+ //# sourceMappingURL=run_status.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"run_status.d.ts","sourceRoot":"","sources":["../../src/utils/run_status.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,aAAa,GAAI,QAAQ,MAAM,GAAG,SAAS,YAA4D,CAAC"}
@@ -0,0 +1,10 @@
1
+ //#region src/utils/run_status.ts
2
+ var TERMINAL_RUN_STATUSES = new Set([
3
+ "SUCCEEDED",
4
+ "FAILED",
5
+ "ABORTED",
6
+ "TIMED-OUT"
7
+ ]);
8
+ var isRunFinished = (status) => TERMINAL_RUN_STATUSES.has((status ?? "").toUpperCase());
9
+ //#endregion
10
+ export { isRunFinished };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@apify/mcp-widgets",
3
- "version": "0.2.30",
3
+ "version": "0.3.0",
4
4
  "description": "React components that render the output of Apify MCP tools (Actor search, Actor details)",
5
5
  "keywords": [
6
6
  "apify",
@@ -46,8 +46,8 @@
46
46
  "postpublish": "rimraf ./dist"
47
47
  },
48
48
  "dependencies": {
49
- "@apify/ui-icons": "^1.53.0",
50
- "@apify/ui-library": "^1.165.37",
49
+ "@apify/ui-icons": "^1.54.0",
50
+ "@apify/ui-library": "^1.166.1",
51
51
  "pluralize": "^8.0.0"
52
52
  },
53
53
  "peerDependencies": {
@@ -57,15 +57,17 @@
57
57
  },
58
58
  "devDependencies": {
59
59
  "@storybook/react-vite": "^10.3.5",
60
+ "@testing-library/react": "^16.3.2",
60
61
  "@types/pluralize": "0.0.33",
61
62
  "@types/react": "19.2.2",
62
63
  "@types/react-dom": "19.2.2",
63
64
  "@vitejs/plugin-react-swc": "^4.3.1",
65
+ "jsdom": "^29.1.1",
64
66
  "react": "19.2.0",
65
67
  "react-dom": "19.2.0",
66
68
  "styled-components": "^6.1.19",
67
69
  "vite": "^8.0.14",
68
70
  "vite-plugin-dts": "^5.0.1"
69
71
  },
70
- "gitHead": "048ae8b5adce707866066e1442e3f71a533c79c9"
72
+ "gitHead": "97886d3a5a301bf1d8c907081bf752edaea290d6"
71
73
  }