diff --git a/README.md b/README.md
index 6beca54..00f9698 100644
--- a/README.md
+++ b/README.md
@@ -18,6 +18,8 @@ See [API reference](https://activitysmith.com/docs/api-reference/introduction)
- [Start & Update Live Activity](#start--update-live-activity)
- [End Live Activity](#end-live-activity)
- [Live Activity Action](#live-activity-action)
+ - [Icons and Badges](#icons-and-badges)
+ - [Live Activity Colors](#live-activity-colors)
- [Channels](#channels)
- [Widgets](#widgets)
@@ -235,14 +237,6 @@ await activitysmith.liveActivities.stream("customer-ops", {
});
```
-The `icon.symbol` value is an Apple SF Symbol name. Browse the catalog with one of these tools:
-
-- [ActivitySmith app](https://apps.apple.com/us/app/activitysmith/id6752254835) - Open Settings -> SF Symbols to browse 45 hand-picked icons ready to use
-- [SF Symbols](https://developer.apple.com/sf-symbols/) - Apple's official macOS app
-- [Interactful](https://apps.apple.com/app/interactful/id1528095640) - free third-party iOS app listing all SF Symbols under Foundations -> Iconography
-
-`icon` and `badge` are optional. If you omit either one, that element is not shown in the Live Activity.
-
### End Live Activity
Call `endStream(...)` with the same `streamKey` to dismiss the Live Activity. You can include final values before it is removed. By default, iOS removes the Live Activity after two minutes. Set `auto_dismiss_minutes` to choose a different dismissal time, including `0` for immediate dismissal.
@@ -316,6 +310,65 @@ await activitysmith.liveActivities.stream("search-reindex", {
});
```
+### Icons and Badges
+
+Add more context to Live Activities with icons and badges.
+
+#### Icon
+
+Supported Live Activity types: `stats`, `metrics`, `progress`, `segmented_progress`, and `alert`.
+
+
+
+
+
+```ts
+await activitysmith.liveActivities.stream("prod-web-1", {
+ content_state: ActivitySmith.contentState({
+ title: "Server Health",
+ subtitle: "prod-web-1",
+ type: "metrics",
+ icon: ActivitySmith.alertIcon("server.rack", { color: "blue" }),
+ metrics: [
+ { label: "CPU", value: 18, unit: "%" },
+ { label: "MEM", value: 42, unit: "%" },
+ ],
+ }),
+});
+```
+
+The `icon.symbol` value is an Apple SF Symbol name. Browse the catalog with one of these tools:
+
+- [ActivitySmith app](https://apps.apple.com/us/app/activitysmith/id6752254835) - Open Settings -> SF Symbols to browse 45 hand-picked icons ready to use
+- [SF Symbols](https://developer.apple.com/sf-symbols/) - Apple's official macOS app
+- [Interactful](https://apps.apple.com/app/interactful/id1528095640) - free third-party iOS app listing all SF Symbols under Foundations -> Iconography
+
+#### Badge
+
+Badges are supported by `alert`, `progress`, and `segmented_progress` Live Activities.
+
+
+
+
+
+```ts
+await activitysmith.liveActivities.stream("nightly-database-backup", {
+ content_state: ActivitySmith.contentState({
+ title: "Nightly Database Backup",
+ subtitle: "verify restore",
+ type: "progress",
+ badge: ActivitySmith.alertBadge("S3", { color: "cyan" }),
+ percentage: 62,
+ }),
+});
+```
+
+### Live Activity Colors
+
+Choose from these colors for the Live Activity accent, including progress bars and action buttons, or apply them to an individual icon or badge:
+
+`lime`, `green`, `cyan`, `blue`, `purple`, `magenta`, `red`, `orange`, `yellow`, `gray`
+
## Channels
Channels are used to target specific team members or devices. Can be used for both push notifications and live activities.
diff --git a/generated/models/index.ts b/generated/models/index.ts
index 32373dd..6982bb7 100644
--- a/generated/models/index.ts
+++ b/generated/models/index.ts
@@ -112,7 +112,7 @@ export interface ChannelTarget {
channels: Array;
}
/**
- * End payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message, with optional icon and badge. Type is optional when ending an existing activity. You can send an updated number_of_steps here if the workflow changed after start.
+ * End payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message. Optional icon is supported by all Live Activity types. Optional badge is supported by alert, progress, and segmented_progress. Type is optional when ending an existing activity. You can send an updated number_of_steps here if the workflow changed after start.
* @export
* @interface ContentStateEnd
*/
@@ -173,13 +173,13 @@ export interface ContentStateEnd {
*/
message?: string;
/**
- * Optional SF Symbol icon for type=alert.
+ * Optional SF Symbol icon. Supported by alert, progress, segmented_progress, metrics, and stats.
* @type {LiveActivityAlertIcon}
* @memberof ContentStateEnd
*/
icon?: LiveActivityAlertIcon;
/**
- * Optional badge for type=alert.
+ * Optional badge. Supported by alert, progress, and segmented_progress.
* @type {LiveActivityAlertBadge}
* @memberof ContentStateEnd
*/
@@ -281,7 +281,7 @@ export const ContentStateEndStepColorsEnum = {
export type ContentStateEndStepColorsEnum = typeof ContentStateEndStepColorsEnum[keyof typeof ContentStateEndStepColorsEnum];
/**
- * Start payload requires title and type. For segmented_progress include number_of_steps and current_step. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message, with optional icon and badge. For segmented_progress, number_of_steps is not locked and can be changed in later update or end calls.
+ * Start payload requires title and type. For segmented_progress include number_of_steps and current_step. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message. Optional icon is supported by all Live Activity types. Optional badge is supported by alert, progress, and segmented_progress. For segmented_progress, number_of_steps is not locked and can be changed in later update or end calls.
* @export
* @interface ContentStateStart
*/
@@ -342,13 +342,13 @@ export interface ContentStateStart {
*/
message?: string;
/**
- * Optional SF Symbol icon for type=alert.
+ * Optional SF Symbol icon. Supported by alert, progress, segmented_progress, metrics, and stats.
* @type {LiveActivityAlertIcon}
* @memberof ContentStateStart
*/
icon?: LiveActivityAlertIcon;
/**
- * Optional badge for type=alert.
+ * Optional badge. Supported by alert, progress, and segmented_progress.
* @type {LiveActivityAlertBadge}
* @memberof ContentStateStart
*/
@@ -444,7 +444,7 @@ export const ContentStateStartStepColorsEnum = {
export type ContentStateStartStepColorsEnum = typeof ContentStateStartStepColorsEnum[keyof typeof ContentStateStartStepColorsEnum];
/**
- * Update payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message, with optional icon and badge. Type is optional when updating an existing activity. You can increase or decrease number_of_steps during updates.
+ * Update payload requires title. For segmented_progress include current_step and optionally number_of_steps. For progress include percentage or value with upper_limit. For metrics and stats include a non-empty metrics array. For alert include message. Optional icon is supported by all Live Activity types. Optional badge is supported by alert, progress, and segmented_progress. Type is optional when updating an existing activity. You can increase or decrease number_of_steps during updates.
* @export
* @interface ContentStateUpdate
*/
@@ -505,13 +505,13 @@ export interface ContentStateUpdate {
*/
message?: string;
/**
- * Optional SF Symbol icon for type=alert.
+ * Optional SF Symbol icon. Supported by alert, progress, segmented_progress, metrics, and stats.
* @type {LiveActivityAlertIcon}
* @memberof ContentStateUpdate
*/
icon?: LiveActivityAlertIcon;
/**
- * Optional badge for type=alert.
+ * Optional badge. Supported by alert, progress, and segmented_progress.
* @type {LiveActivityAlertBadge}
* @memberof ContentStateUpdate
*/
@@ -646,7 +646,7 @@ export interface LiveActivityAction {
*/
type: LiveActivityActionType;
/**
- * HTTPS URL. For open_url it is opened in browser. For webhook it is called by ActivitySmith backend.
+ * Action URL. For open_url, use an HTTPS or shortcuts:// URL. For webhook, use an HTTPS URL called by the ActivitySmith backend.
* @type {string}
* @memberof LiveActivityAction
*/
@@ -676,7 +676,7 @@ export const LiveActivityActionType = {
export type LiveActivityActionType = typeof LiveActivityActionType[keyof typeof LiveActivityActionType];
/**
- * Optional badge for Alert Live Activities.
+ * Optional badge for Live Activities.
* @export
* @interface LiveActivityAlertBadge
*/
@@ -696,7 +696,7 @@ export interface LiveActivityAlertBadge {
color?: LiveActivityColor;
}
/**
- * Optional SF Symbol icon for Alert Live Activities.
+ * Optional SF Symbol icon for Live Activities.
* @export
* @interface LiveActivityAlertIcon
*/
@@ -1313,7 +1313,7 @@ export interface PushNotificationAction {
*/
type: PushNotificationActionType;
/**
- * HTTPS URL. For open_url it is opened in browser. For webhook it is called by ActivitySmith backend.
+ * Action URL. For open_url, use an HTTPS or shortcuts:// URL. For webhook, use an HTTPS URL called by the ActivitySmith backend.
* @type {string}
* @memberof PushNotificationAction
*/
@@ -1374,7 +1374,7 @@ export interface PushNotificationRequest {
*/
media?: string;
/**
- * Optional HTTPS URL opened when user taps the notification body. Overrides the default tap target from `media` when both are provided.
+ * Optional HTTPS or shortcuts:// URL opened when user taps the notification body. Overrides the default tap target from `media` when both are provided.
* @type {string}
* @memberof PushNotificationRequest
*/
@@ -1571,13 +1571,13 @@ export interface StreamContentState {
*/
message?: string;
/**
- * Optional SF Symbol icon for type=alert.
+ * Optional SF Symbol icon. Supported by alert, progress, segmented_progress, metrics, and stats.
* @type {LiveActivityAlertIcon}
* @memberof StreamContentState
*/
icon?: LiveActivityAlertIcon;
/**
- * Optional badge for type=alert.
+ * Optional badge. Supported by alert, progress, and segmented_progress.
* @type {LiveActivityAlertBadge}
* @memberof StreamContentState
*/
diff --git a/tests/resources.test.js b/tests/resources.test.js
index 505e040..575418f 100644
--- a/tests/resources.test.js
+++ b/tests/resources.test.js
@@ -69,6 +69,50 @@ describe("resource wrappers", () => {
expect(sendSpy).toHaveBeenCalledWith({ pushNotificationRequest: payload }, undefined);
});
+ it("preserves shortcuts redirection for notifications.send", async () => {
+ const ActivitySmith = require("../dist/src/index.js");
+ const generated = require("../dist/generated/index.js");
+
+ const sendSpy = vi
+ .spyOn(generated.PushNotificationsApi.prototype, "sendPushNotification")
+ .mockResolvedValue({ success: true });
+
+ const client = new ActivitySmith({ apiKey: "test" });
+ const payload = {
+ title: "Task finished",
+ redirection: "shortcuts://run-shortcut?name=Jarvis",
+ };
+
+ await client.notifications.send(payload);
+
+ expect(sendSpy).toHaveBeenCalledWith({ pushNotificationRequest: payload }, undefined);
+ });
+
+ it("preserves shortcuts open_url actions for notifications.send", async () => {
+ const ActivitySmith = require("../dist/src/index.js");
+ const generated = require("../dist/generated/index.js");
+
+ const sendSpy = vi
+ .spyOn(generated.PushNotificationsApi.prototype, "sendPushNotification")
+ .mockResolvedValue({ success: true });
+
+ const client = new ActivitySmith({ apiKey: "test" });
+ const payload = {
+ title: "Task finished",
+ actions: [
+ {
+ title: "Run Shortcut",
+ type: "open_url",
+ url: "shortcuts://run-shortcut?name=Jarvis",
+ },
+ ],
+ };
+
+ await client.notifications.send(payload);
+
+ expect(sendSpy).toHaveBeenCalledWith({ pushNotificationRequest: payload }, undefined);
+ });
+
it("rejects media with actions for notifications.send", async () => {
const ActivitySmith = require("../dist/src/index.js");
@@ -295,6 +339,68 @@ describe("resource wrappers", () => {
);
});
+ it("passes through icon and badge on non-alert live activity types", async () => {
+ const ActivitySmith = require("../dist/src/index.js");
+ const generated = require("../dist/generated/index.js");
+
+ const streamSpy = vi
+ .spyOn(generated.LiveActivitiesApi.prototype, "reconcileLiveActivityStream")
+ .mockResolvedValue({ operation: "started", stream_key: "prod-web-1" });
+
+ const client = new ActivitySmith({ apiKey: "test" });
+ await client.liveActivities.stream("prod-web-1", {
+ content_state: ActivitySmith.contentState({
+ title: "Server Health",
+ subtitle: "prod-web-1",
+ type: ActivitySmith.liveActivityTypes.metrics,
+ icon: ActivitySmith.alertIcon("server.rack", { color: "blue" }),
+ metrics: [{ label: "CPU", value: 18, unit: "%" }],
+ }),
+ });
+ await client.liveActivities.stream("nightly-database-backup", {
+ content_state: ActivitySmith.contentState({
+ title: "Nightly Database Backup",
+ subtitle: "verify restore",
+ type: ActivitySmith.liveActivityTypes.progress,
+ badge: ActivitySmith.alertBadge("S3", { color: "cyan" }),
+ percentage: 62,
+ }),
+ });
+
+ expect(streamSpy).toHaveBeenNthCalledWith(
+ 1,
+ {
+ streamKey: "prod-web-1",
+ liveActivityStreamRequest: {
+ content_state: {
+ title: "Server Health",
+ subtitle: "prod-web-1",
+ type: ActivitySmith.liveActivityTypes.metrics,
+ icon: { symbol: "server.rack", color: "blue" },
+ metrics: [{ label: "CPU", value: 18, unit: "%" }],
+ },
+ },
+ },
+ undefined,
+ );
+ expect(streamSpy).toHaveBeenNthCalledWith(
+ 2,
+ {
+ streamKey: "nightly-database-backup",
+ liveActivityStreamRequest: {
+ content_state: {
+ title: "Nightly Database Backup",
+ subtitle: "verify restore",
+ type: ActivitySmith.liveActivityTypes.progress,
+ badge: { title: "S3", color: "cyan" },
+ percentage: 62,
+ },
+ },
+ },
+ undefined,
+ );
+ });
+
it("wraps live activity stream payloads for short methods", async () => {
const ActivitySmith = require("../dist/src/index.js");
const generated = require("../dist/generated/index.js");
@@ -394,7 +500,7 @@ describe("resource wrappers", () => {
action: {
title: "Open Workflow",
type: "open_url",
- url: "https://github.com/acme/payments-api/actions/runs/1234567890",
+ url: "shortcuts://run-shortcut?name=Deploy%20Status",
},
};
@@ -426,7 +532,7 @@ describe("resource wrappers", () => {
action: {
title: "Open Workflow",
type: "open_url",
- url: "https://github.com/acme/payments-api/actions/runs/1234567890",
+ url: "shortcuts://run-shortcut?name=Deploy%20Status",
},
};