diff --git a/package.json b/package.json
index ea32c12ad9..24a2b60991 100644
--- a/package.json
+++ b/package.json
@@ -130,7 +130,7 @@
"@storybook/addon-essentials": "^8.6.11",
"@storybook/addon-interactions": "^8.6.11",
"@storybook/addon-links": "^8.6.11",
- "@storybook/addon-svelte-csf": "^5.0.0-next.23",
+ "@storybook/addon-svelte-csf": "^5.0.10",
"@storybook/addon-themes": "^8.6.11",
"@storybook/blocks": "^8.6.11",
"@storybook/icons": "^1.4.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 95722d4011..9b824bb2e2 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -170,7 +170,7 @@ importers:
specifier: ^8.6.11
version: 8.6.15(react@18.3.1)(storybook@8.6.15(prettier@3.5.3))
'@storybook/addon-svelte-csf':
- specifier: ^5.0.0-next.23
+ specifier: ^5.0.10
version: 5.0.10(@storybook/svelte@8.6.15(storybook@8.6.15(prettier@3.5.3))(svelte@5.46.0))(@sveltejs/vite-plugin-svelte@5.1.1(svelte@5.46.0)(vite@6.4.1(@types/node@18.19.130)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.2)))(storybook@8.6.15(prettier@3.5.3))(svelte@5.46.0)(vite@6.4.1(@types/node@18.19.130)(jiti@1.21.7)(terser@5.44.1)(yaml@2.8.2))
'@storybook/addon-themes':
specifier: ^8.6.11
diff --git a/src/lib/components/payload-input-with-encoding.svelte b/src/lib/components/payload-input-with-encoding.svelte
index eb01dff1c7..26eddb5599 100644
--- a/src/lib/components/payload-input-with-encoding.svelte
+++ b/src/lib/components/payload-input-with-encoding.svelte
@@ -1,19 +1,12 @@
-
-
+
+
diff --git a/src/lib/components/standalone-activity-form/types.ts b/src/lib/components/standalone-activity-form/types.ts
new file mode 100644
index 0000000000..d2dee1f420
--- /dev/null
+++ b/src/lib/components/standalone-activity-form/types.ts
@@ -0,0 +1,19 @@
+import type { PayloadInputEncoding } from '$lib/models/payload-encoding';
+import type { SearchAttributeInput } from '$lib/stores/search-attributes';
+
+export interface StandaloneActivityFormData {
+ identity: string;
+ namespace: string;
+ activityId: string;
+ taskQueue: string;
+ activityType: string;
+ startToCloseTimeout: string;
+ scheduleToCloseTimeout?: string;
+ scheduleToStartTimeout?: string;
+ input?: string;
+ encoding?: PayloadInputEncoding;
+ messageType?: string;
+ searchAttributes?: SearchAttributeInput[];
+ summary?: string;
+ details?: string;
+}
diff --git a/src/lib/components/workflow/client-actions/signal-confirmation-modal.svelte b/src/lib/components/workflow/client-actions/signal-confirmation-modal.svelte
index 8c36685900..c65bd3f643 100644
--- a/src/lib/components/workflow/client-actions/signal-confirmation-modal.svelte
+++ b/src/lib/components/workflow/client-actions/signal-confirmation-modal.svelte
@@ -1,14 +1,13 @@
-
+
{@render children?.()}
diff --git a/src/lib/holocene/duration-input/duration-input.mdx b/src/lib/holocene/duration-input/duration-input.mdx
new file mode 100644
index 0000000000..9630cb4d1b
--- /dev/null
+++ b/src/lib/holocene/duration-input/duration-input.mdx
@@ -0,0 +1,67 @@
+import { Meta } from '@storybook/blocks';
+
+import DurationInput from './duration-input.stories.svelte';
+
+
+
+# Duration Input
+
+`
` allows specifying a Duration in seconds (eg: `"10s"`) while allowing input in varying units.
+
+By default, `
` includes the following Unit options:
+
+- millisecond(s)
+- second(s)
+- minute(s)
+- hour(s)
+
+When entering a value with "millisecond(s)" selected, the value will be converted to a `Duration`, eg. `1000` will be converted to `"1s"`.
+
+An array of custom `Units` can be passed if more options are needed. A `Unit` is described by a `label`, and a `convert` function, with the type signature: `(n: number) => number`. The job of the `convert` function is to convert the value in the selected unit to seconds.
+
+```svelte
+
+
+
+
+```
+
+The `
` Props interface is generic, such that the type of the `initialUnit` prop can be inferred based on the array of `Units` passed in:
+
+
+```svelte
+
+
+
+
+/>
+```
diff --git a/src/lib/holocene/duration-input/duration-input.stories.svelte b/src/lib/holocene/duration-input/duration-input.stories.svelte
new file mode 100644
index 0000000000..6b44a4be99
--- /dev/null
+++ b/src/lib/holocene/duration-input/duration-input.stories.svelte
@@ -0,0 +1,51 @@
+
+
+{#snippet template(args: Args)}
+
+{/snippet}
+
+
+
+ n / Math.pow(10, 9) },
+ { label: 'Day(s)', convert: (n) => n * 86400 },
+ { label: 'Week(s)', convert: (n) => n * 604800 },
+ ],
+ initialUnit: 'Day(s)',
+ }}
+/>
diff --git a/src/lib/holocene/duration-input/duration-input.svelte b/src/lib/holocene/duration-input/duration-input.svelte
new file mode 100644
index 0000000000..b2e01af156
--- /dev/null
+++ b/src/lib/holocene/duration-input/duration-input.svelte
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+ {#each units as unit}
+ {unit.label}
+ {/each}
+
+
+ {#if hintText}
+
{hintText}
+ {/if}
+
diff --git a/src/lib/holocene/icon/svg/activity.svelte b/src/lib/holocene/icon/svg/activity.svelte
index ac1a65d8e6..2007b665e6 100644
--- a/src/lib/holocene/icon/svg/activity.svelte
+++ b/src/lib/holocene/icon/svg/activity.svelte
@@ -5,7 +5,7 @@
diff --git a/src/lib/holocene/navigation/navigation-container.svelte b/src/lib/holocene/navigation/navigation-container.svelte
index 2f9e76dcc6..d12a476c7f 100644
--- a/src/lib/holocene/navigation/navigation-container.svelte
+++ b/src/lib/holocene/navigation/navigation-container.svelte
@@ -24,7 +24,7 @@
diff --git a/src/lib/i18n/locales/en/activities.ts b/src/lib/i18n/locales/en/activities.ts
index 73c28b7a71..e4c3ed9300 100644
--- a/src/lib/i18n/locales/en/activities.ts
+++ b/src/lib/i18n/locales/en/activities.ts
@@ -16,4 +16,6 @@ export const Strings = {
'unpause-all-activity-types': 'Unpause all {{type}} runs',
'reset-heartbeat-details': 'Reset Heartbeat Details (optional)',
'reset-success': 'Activity {{activityId}} has been reset successfully.',
+ 'standalone-activities': 'Standalone Activities',
+ 'start-standalone-activity': 'Start Standalone Activity',
};
diff --git a/src/lib/models/payload-encoding.ts b/src/lib/models/payload-encoding.ts
new file mode 100644
index 0000000000..ab59f17c99
--- /dev/null
+++ b/src/lib/models/payload-encoding.ts
@@ -0,0 +1,7 @@
+export const encodings = ['json/plain', 'json/protobuf'] as const;
+
+export type PayloadInputEncoding = (typeof encodings)[number];
+
+export const isPayloadInputEncodingType = (
+ x: unknown,
+): x is PayloadInputEncoding => encodings.includes(x as PayloadInputEncoding);
diff --git a/src/lib/pages/start-workflow.svelte b/src/lib/pages/start-workflow.svelte
index 68fa4d939e..3e905b129c 100644
--- a/src/lib/pages/start-workflow.svelte
+++ b/src/lib/pages/start-workflow.svelte
@@ -6,9 +6,7 @@
import { page } from '$app/stores';
import CodecServerErrorBanner from '$lib/components/codec-server-error-banner.svelte';
- import PayloadInputWithEncoding, {
- type PayloadInputEncoding,
- } from '$lib/components/payload-input-with-encoding.svelte';
+ import PayloadInputWithEncoding from '$lib/components/payload-input-with-encoding.svelte';
import AddSearchAttributes from '$lib/components/workflow/add-search-attributes.svelte';
import Alert from '$lib/holocene/alert.svelte';
import Button from '$lib/holocene/button.svelte';
@@ -20,6 +18,7 @@
import MarkdownEditor from '$lib/holocene/markdown-editor/markdown-editor.svelte';
import Tooltip from '$lib/holocene/tooltip.svelte';
import { translate } from '$lib/i18n/translate';
+ import type { PayloadInputEncoding } from '$lib/models/payload-encoding';
import { getPollers } from '$lib/services/pollers-service';
import {
fetchInitialValuesForStartWorkflow,
diff --git a/src/lib/services/standalone-activities.ts b/src/lib/services/standalone-activities.ts
new file mode 100644
index 0000000000..c4797ced77
--- /dev/null
+++ b/src/lib/services/standalone-activities.ts
@@ -0,0 +1,128 @@
+import type { StandaloneActivityFormData } from '$lib/components/standalone-activity-form/types';
+import type {
+ ActivityType,
+ Payload,
+ Payloads,
+ TaskQueue,
+ UserMetadata,
+} from '$lib/types';
+import { encodePayloads } from '$lib/utilities/encode-payload';
+import { stringifyWithBigInt } from '$lib/utilities/parse-with-big-int';
+import { requestFromAPI } from '$lib/utilities/request-from-api';
+import { routeForApi } from '$lib/utilities/route-for-api';
+
+// TODO: Use @temporalio/proto once updated
+type StartActivityExecutionRequest = {
+ namespace: string;
+ identity: string;
+ requestId: string;
+ activityId: string;
+ activityType: ActivityType;
+ taskQueue: TaskQueue;
+ startToCloseTimeout: string;
+ scheduleToCloseTimeout: string;
+ scheduleToStartTimeout: string;
+ input?: Payloads;
+ userMetadata?: UserMetadata;
+};
+
+const toStartActivityExecutionRequest = async (
+ activityFormData: StandaloneActivityFormData,
+): Promise => {
+ let inputPayloads: Payload[] | null = null;
+ let summaryPayload: Payload | null = null;
+ let detailsPayload: Payload | null = null;
+
+ if (activityFormData.input) {
+ const { input, encoding, messageType } = activityFormData;
+ try {
+ inputPayloads = await encodePayloads({ input, encoding, messageType });
+ } catch {
+ throw new Error('Could not encode input for starting activity execution');
+ }
+ }
+
+ if (activityFormData.summary) {
+ try {
+ const payloads = await encodePayloads({
+ input: stringifyWithBigInt(activityFormData.summary),
+ encoding: 'json/plain',
+ });
+
+ if (payloads && payloads[0]) {
+ summaryPayload = payloads[0];
+ }
+ } catch {
+ throw new Error(
+ 'Could not encode summary for starting activity execution',
+ );
+ }
+ }
+
+ if (activityFormData.details) {
+ try {
+ const payloads = await encodePayloads({
+ input: stringifyWithBigInt(activityFormData.details),
+ encoding: 'json/plain',
+ });
+
+ if (payloads && payloads[0]) {
+ detailsPayload = payloads[0];
+ }
+ } catch {
+ throw new Error(
+ 'Could not encode details for starting activity execution',
+ );
+ }
+ }
+
+ return {
+ identity: activityFormData.identity,
+ namespace: activityFormData.namespace,
+ activityId: activityFormData.activityId,
+ requestId: crypto.randomUUID(),
+ activityType: { name: activityFormData.activityType },
+ taskQueue: { name: activityFormData.taskQueue },
+ input: { payloads: inputPayloads },
+ userMetadata: {
+ summary: summaryPayload,
+ details: detailsPayload,
+ },
+ ...(activityFormData.startToCloseTimeout && {
+ startToCloseTimeout: activityFormData.startToCloseTimeout,
+ }),
+ ...(activityFormData.scheduleToCloseTimeout && {
+ scheduleToCloseTimeout: activityFormData.scheduleToCloseTimeout,
+ }),
+ ...(activityFormData.scheduleToStartTimeout && {
+ scheduleToStartTimeout: activityFormData.scheduleToStartTimeout,
+ }),
+ };
+};
+
+export const startStandaloneActivity = async (
+ activity: StandaloneActivityFormData,
+) => {
+ const { activityId, namespace } = activity;
+
+ const route = routeForApi('standalone-activities.start', {
+ namespace,
+ activityId,
+ });
+
+ const startActivityExecutionRequest =
+ await toStartActivityExecutionRequest(activity);
+
+ return requestFromAPI(route, {
+ options: {
+ method: 'POST',
+ body: stringifyWithBigInt(startActivityExecutionRequest),
+ },
+ });
+};
+
+export const getActivityExecutions = (namespace: string) => {
+ const route = routeForApi('standalone-activities', { namespace });
+
+ return requestFromAPI(route);
+};
diff --git a/src/lib/services/workflow-service.ts b/src/lib/services/workflow-service.ts
index 85d6939512..9319808c98 100644
--- a/src/lib/services/workflow-service.ts
+++ b/src/lib/services/workflow-service.ts
@@ -2,11 +2,11 @@ import { get } from 'svelte/store';
import { page } from '$app/stores';
+import { translate } from '$lib/i18n/translate';
import {
isPayloadInputEncodingType,
type PayloadInputEncoding,
-} from '$lib/components/payload-input-with-encoding.svelte';
-import { translate } from '$lib/i18n/translate';
+} from '$lib/models/payload-encoding';
import { Action } from '$lib/models/workflow-actions';
import {
toWorkflowExecution,
diff --git a/src/lib/types/api.ts b/src/lib/types/api.ts
index 480da8c8be..55d58900ed 100644
--- a/src/lib/types/api.ts
+++ b/src/lib/types/api.ts
@@ -54,6 +54,8 @@ export type NexusAPIRoutePath = 'nexus-endpoint' | 'nexus-endpoint.update';
export type WorkerDeploymentsAPIRoutePath = 'worker-deployments';
export type WorkerDeploymentAPIRoutePath = 'worker-deployment';
export type WorkerDeploymentVersionAPIRoutePath = 'worker-deployment-version';
+export type StandaloneActivitiesAPIRoutePath = 'standalone-activities';
+export type StartStandaloneActivityAPIRoutePath = 'standalone-activities.start';
export type APIRoutePath =
| ParameterlessAPIRoutePath
@@ -73,7 +75,9 @@ export type APIRoutePath =
| NexusAPIRoutePath
| WorkerDeploymentsAPIRoutePath
| WorkerDeploymentAPIRoutePath
- | WorkerDeploymentVersionAPIRoutePath;
+ | WorkerDeploymentVersionAPIRoutePath
+ | StartStandaloneActivityAPIRoutePath
+ | StandaloneActivitiesAPIRoutePath;
export type APIRouteParameters = {
namespace: string;
@@ -92,6 +96,14 @@ export type APIRouteParameters = {
version: string;
};
+export type StandaloneActivitiesParameters = Pick<
+ APIRouteParameters,
+ 'namespace'
+>;
+export type StartStandaloneActivityParameters = Pick<
+ APIRouteParameters,
+ 'namespace' | 'activityId'
+>;
export type WorkflowListRouteParameters = Pick;
export type NamespaceRouteParameters = Pick;
export type ScheduleListRouteParameters = Pick;
diff --git a/src/lib/types/index.ts b/src/lib/types/index.ts
index 5070d8a159..1d93ec857a 100644
--- a/src/lib/types/index.ts
+++ b/src/lib/types/index.ts
@@ -221,9 +221,12 @@ export type Payloads = temporal.api.common.v1.IPayloads;
export type WorkflowExecutionInput = temporal.api.common.v1.IWorkflowExecution;
export type Memo = temporal.api.common.v1.IMemo;
export type Header = temporal.api.common.v1.IHeader;
+export type ActivityType = temporal.api.common.v1.IActivityType;
+export type RetryPolicy = temporal.api.common.v1.IRetryPolicy;
// api.taskqueue
+export type TaskQueue = temporal.api.taskqueue.v1.ITaskQueue;
export type TaskQueueRequest =
temporal.api.workflowservice.v1.IDescribeTaskQueueRequest;
export type TaskQueueResponse =
@@ -274,6 +277,7 @@ export type Failure = temporal.api.failure.v1.IFailure;
// google
export type Timestamp = google.protobuf.ITimestamp;
+export type Duration = google.protobuf.IDuration;
// extra APIs
export type SettingsResponse = {
@@ -303,3 +307,5 @@ export type SettingsResponse = {
FeedbackURL: string;
Version: string;
};
+
+export type UserMetadata = temporal.api.sdk.v1.IUserMetadata;
diff --git a/src/lib/types/schedule.ts b/src/lib/types/schedule.ts
index 583f20a3b3..09e24db3a6 100644
--- a/src/lib/types/schedule.ts
+++ b/src/lib/types/schedule.ts
@@ -1,4 +1,4 @@
-import type { PayloadInputEncoding } from '$lib/components/payload-input-with-encoding.svelte';
+import type { PayloadInputEncoding } from '$lib/models/payload-encoding';
import type { SearchAttributeInput } from '$lib/stores/search-attributes';
import type {
CalendarSpec,
diff --git a/src/lib/utilities/encode-payload.ts b/src/lib/utilities/encode-payload.ts
index 12273ff6f7..7244a2db64 100644
--- a/src/lib/utilities/encode-payload.ts
+++ b/src/lib/utilities/encode-payload.ts
@@ -1,9 +1,9 @@
import { get } from 'svelte/store';
-import type { PayloadInputEncoding } from '$lib/components/payload-input-with-encoding.svelte';
+import type { PayloadInputEncoding } from '$lib/models/payload-encoding';
import { encodePayloadsWithCodec } from '$lib/services/data-encoder';
import { dataEncoder } from '$lib/stores/data-encoder';
-import type { Payloads } from '$lib/types';
+import type { Payload } from '$lib/types';
import { btoa } from '$lib/utilities/btoa';
import {
parseWithBigInt,
@@ -57,7 +57,7 @@ export const encodePayloads = async ({
encoding,
messageType = '',
encodeWithCodec = true,
-}: EncodePayloads): Promise => {
+}: EncodePayloads): Promise => {
let payloads = null;
if (input) {
diff --git a/src/lib/utilities/route-for-api.ts b/src/lib/utilities/route-for-api.ts
index 0fc28685e1..ea22d2abe1 100644
--- a/src/lib/utilities/route-for-api.ts
+++ b/src/lib/utilities/route-for-api.ts
@@ -19,6 +19,10 @@ import type {
SchedulesAPIRoutePath,
SearchAttributesRouteParameters,
SearchAttributesRoutePath,
+ StandaloneActivitiesAPIRoutePath,
+ StandaloneActivitiesParameters,
+ StartStandaloneActivityAPIRoutePath,
+ StartStandaloneActivityParameters,
TaskQueueAPIRoutePath,
TaskQueueRouteParameters,
WorkerAPIRoutePath,
@@ -170,11 +174,23 @@ export function pathForApi(
'worker-deployments': `/namespaces/${parameters.namespace}/worker-deployments`,
'worker-deployment': `/namespaces/${parameters.namespace}/worker-deployments/${parameters.deploymentName}`,
'worker-deployment-version': `/namespaces/${parameters.namespace}/worker-deployment-versions/${parameters.version}`,
+ 'standalone-activities': `/namespaces/${parameters.namespace}/standalone-activities`,
+ 'standalone-activities.start': `/namespaces/${parameters.namespace}/activities/${parameters.activityId}`,
};
return getPath(routes[route]);
}
+export function routeForApi(
+ route: StandaloneActivitiesAPIRoutePath,
+ parameters: StandaloneActivitiesParameters,
+ shouldEncode?: boolean,
+): string;
+export function routeForApi(
+ route: StartStandaloneActivityAPIRoutePath,
+ parameters: StartStandaloneActivityParameters,
+ shouldEncode?: boolean,
+): string;
export function routeForApi(
route: WorkflowsAPIRoutePath,
parameters: WorkflowListRouteParameters,
diff --git a/src/lib/utilities/route-for.ts b/src/lib/utilities/route-for.ts
index bcfd0c98ae..9ef7ce15cb 100644
--- a/src/lib/utilities/route-for.ts
+++ b/src/lib/utilities/route-for.ts
@@ -94,6 +94,18 @@ export const routeForWorkflows = (parameters: NamespaceParameter): string => {
return `${routeForNamespace(parameters)}/workflows`;
};
+export const routeForStandaloneActivities = (
+ parameters: NamespaceParameter,
+): string => {
+ return `${routeForNamespace(parameters)}/activities`;
+};
+
+export const routeForStartStandaloneActivity = (
+ parameters: NamespaceParameter,
+): string => {
+ return `${routeForStandaloneActivities(parameters)}/start`;
+};
+
type StartWorkflowParameters = NamespaceParameter &
Partial<{
workflowId: string;
diff --git a/src/routes/(app)/+layout.svelte b/src/routes/(app)/+layout.svelte
index fb0775468a..531ef522ee 100644
--- a/src/routes/(app)/+layout.svelte
+++ b/src/routes/(app)/+layout.svelte
@@ -32,6 +32,7 @@
routeForNamespaces,
routeForNexus,
routeForSchedules,
+ routeForStandaloneActivities,
routeForWorkerDeployments,
routeForWorkflows,
} from '$lib/utilities/route-for';
@@ -72,6 +73,7 @@
const getRoutes = (namespace: string) => {
return {
workflowsRoute: routeForWorkflows({ namespace }),
+ standaloneActivitiesRoute: routeForStandaloneActivities({ namespace }),
schedulesRoute: routeForSchedules({ namespace }),
batchOperationsRoute: routeForBatchOperations({ namespace }),
workerDeploymentsRoute: routeForWorkerDeployments({ namespace }),
@@ -85,6 +87,7 @@
const getLinkList = (
{
workflowsRoute,
+ standaloneActivitiesRoute,
schedulesRoute,
batchOperationsRoute,
workerDeploymentsRoute,
@@ -94,6 +97,7 @@
historyImportRoute,
}: {
workflowsRoute: string;
+ standaloneActivitiesRoute: string;
schedulesRoute: string;
batchOperationsRoute: string;
workerDeploymentsRoute: string;
@@ -115,6 +119,7 @@
!path.includes(schedulesRoute) &&
!path.includes(batchOperationsRoute) &&
!path.includes(workerDeploymentsRoute) &&
+ !path.includes(standaloneActivitiesRoute) &&
!path.includes(archivalRoute),
},
{
@@ -123,6 +128,12 @@
label: translate('common.workflows'),
isActive: (path) => path.includes(workflowsRoute),
},
+ {
+ href: standaloneActivitiesRoute,
+ icon: 'activity',
+ label: translate('activities.standalone-activities'),
+ isActive: (path) => path.includes(standaloneActivitiesRoute),
+ },
{
href: schedulesRoute,
icon: 'schedules',
@@ -184,6 +195,7 @@
batchOperationsRoute,
workerDeploymentsRoute,
archivalRoute,
+ standaloneActivitiesRoute,
} = $derived(routes);
let showNamespacePicker = $derived(
[
@@ -192,6 +204,7 @@
workerDeploymentsRoute,
batchOperationsRoute,
archivalRoute,
+ standaloneActivitiesRoute,
].some((route) => page.url.href.includes(route)),
);
@@ -205,6 +218,10 @@
subPath: 'batch-operations',
fullRoute: routeForBatchOperations({ namespace }),
},
+ {
+ subPath: 'standalone-activities',
+ fullRoute: routeForStandaloneActivities({ namespace }),
+ },
{
subPath: 'worker-deployments',
fullRoute: routeForWorkerDeployments({ namespace }),
diff --git a/src/routes/(app)/namespaces/[namespace]/activities/+page.svelte b/src/routes/(app)/namespaces/[namespace]/activities/+page.svelte
new file mode 100644
index 0000000000..aac8ba0a53
--- /dev/null
+++ b/src/routes/(app)/namespaces/[namespace]/activities/+page.svelte
@@ -0,0 +1,26 @@
+
+
+
+
+
{translate('activities.standalone-activities')}
+
+ {translate('activities.start-standalone-activity')}
+
+
+
+{#await getActivityExecutions(namespace) then executions}
+ {JSON.stringify(executions, null, 2)}
+{/await}
diff --git a/src/routes/(app)/namespaces/[namespace]/activities/start/+page.svelte b/src/routes/(app)/namespaces/[namespace]/activities/start/+page.svelte
new file mode 100644
index 0000000000..1877ef7980
--- /dev/null
+++ b/src/routes/(app)/namespaces/[namespace]/activities/start/+page.svelte
@@ -0,0 +1,18 @@
+
+
+
+
+{translate('activities.start-standalone-activity')}
+
+