Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
80 commits
Select commit Hold shift + click to select a range
c0e2576
wip(automations): port automations natively in to desktop part 1
AnkhHeart Jun 4, 2026
d948472
feat(automations): implement drag-and-drop reordering and enhance act…
AnkhHeart Jun 4, 2026
ae96ad1
wip(automations): simplify logic
AnkhHeart Jun 4, 2026
f4195cd
wip(automations): consolidate conditions and instructions into flat f…
AnkhHeart Jun 4, 2026
f388ad5
refactor(automations): enhance AutomationEditor with Ant Design compo…
AnkhHeart Jun 4, 2026
088aefc
feat(automations): add dropdownMatchSelectWidth prop to Select compon…
AnkhHeart Jun 4, 2026
86eaf73
feat(automations): implement AutomationsElement component and integra…
AnkhHeart Jun 15, 2026
2558e8e
feat(automations): update AutomationsElement and EditAutomations for …
AnkhHeart Jun 15, 2026
2343384
feat(automations): enhance Scrollable component with layout update an…
AnkhHeart Jun 18, 2026
06507e6
feat(sources): add methods to remove and set filter visibility in Sou…
AnkhHeart Jun 19, 2026
a54f258
feat(automations): add ResizeObserver to Scrollable for dynamic scrol…
AnkhHeart Jun 19, 2026
9a37480
wip(UX): working on automations UX
AnkhHeart Jun 24, 2026
b375b39
feat(automations): update styles and functionality for automation com…
AnkhHeart Jun 24, 2026
49807b6
feat(automations): add downloadUrl to sources and update related auto…
AnkhHeart Jun 24, 2026
5b41fef
fix(automations): update condition type for 'Enemy Eliminated' automa…
AnkhHeart Jun 24, 2026
609dd5c
feat(automations): add placeholder image for empty automations state
AnkhHeart Jun 25, 2026
701147d
feat(automations): filter sources to include only video sources in Au…
AnkhHeart Jun 25, 2026
55ec079
feat(automations): add comprehensive migration documentation for Auto…
AnkhHeart Jul 2, 2026
5c98ecf
feat(automations): integrate Intelligent Streaming Agent app detectio…
AnkhHeart Jul 8, 2026
d1c2210
refactor(automations): streamline agent app detection logic and renam…
AnkhHeart Jul 8, 2026
0000e9e
feat(automations): add ISA app requirements and status messages to au…
AnkhHeart Jul 8, 2026
fa54ec3
feat(automations): enhance error handling and retry logic in automati…
AnkhHeart Jul 8, 2026
0e29a3d
feat(automations): implement analytics tracking for automation action…
AnkhHeart Jul 8, 2026
f7397fc
feat(automations): enhance UI layout and functionality for pre-made a…
AnkhHeart Jul 9, 2026
0660ac5
feat(automations): improve localization and dynamic labels in automat…
AnkhHeart Jul 9, 2026
9e75232
feat(automations): refactor AutomationEditor and EditAutomations comp…
AnkhHeart Jul 10, 2026
e520346
feat(automations): enhance PreMadeAutomations component with video su…
AnkhHeart Jul 10, 2026
6dd9aa6
feat(automations): add branding header to PreMadeAutomations modal wi…
AnkhHeart Jul 10, 2026
1e928ed
feat(automations): remove brand icon from PreMadeAutomations and add …
AnkhHeart Jul 10, 2026
9e9ec4a
feat(automations): update cover panel height in PreMadeAutomations fo…
AnkhHeart Jul 10, 2026
05485a1
feat(automations): refactor to remove AgentSocketService and replace …
AnkhHeart Jul 14, 2026
8a771bf
feat(automations): remove AUTOMATIONS_MIGRATION.md and consolidate co…
AnkhHeart Jul 14, 2026
a306dd2
wip(automations): port automations natively in to desktop part 1
AnkhHeart Jun 4, 2026
7ffcc7a
feat(automations): implement drag-and-drop reordering and enhance act…
AnkhHeart Jun 4, 2026
7ca07fe
wip(automations): simplify logic
AnkhHeart Jun 4, 2026
2e64188
wip(automations): consolidate conditions and instructions into flat f…
AnkhHeart Jun 4, 2026
cad6af5
refactor(automations): enhance AutomationEditor with Ant Design compo…
AnkhHeart Jun 4, 2026
5f0d46b
feat(automations): add dropdownMatchSelectWidth prop to Select compon…
AnkhHeart Jun 4, 2026
b387e78
feat(automations): implement AutomationsElement component and integra…
AnkhHeart Jun 15, 2026
895269a
feat(automations): update AutomationsElement and EditAutomations for …
AnkhHeart Jun 15, 2026
b742b6c
feat(automations): enhance Scrollable component with layout update an…
AnkhHeart Jun 18, 2026
52eadc6
feat(sources): add methods to remove and set filter visibility in Sou…
AnkhHeart Jun 19, 2026
867de0d
feat(automations): add ResizeObserver to Scrollable for dynamic scrol…
AnkhHeart Jun 19, 2026
85f8e77
wip(UX): working on automations UX
AnkhHeart Jun 24, 2026
28463c9
feat(automations): update styles and functionality for automation com…
AnkhHeart Jun 24, 2026
e614de6
feat(automations): add downloadUrl to sources and update related auto…
AnkhHeart Jun 24, 2026
dc2dc28
fix(automations): update condition type for 'Enemy Eliminated' automa…
AnkhHeart Jun 24, 2026
5cde0d9
feat(automations): add placeholder image for empty automations state
AnkhHeart Jun 25, 2026
c54a0c4
feat(automations): filter sources to include only video sources in Au…
AnkhHeart Jun 25, 2026
2527b6e
feat(automations): add comprehensive migration documentation for Auto…
AnkhHeart Jul 2, 2026
4e8368b
feat(automations): integrate Intelligent Streaming Agent app detectio…
AnkhHeart Jul 8, 2026
acc1927
refactor(automations): streamline agent app detection logic and renam…
AnkhHeart Jul 8, 2026
82014a0
feat(automations): add ISA app requirements and status messages to au…
AnkhHeart Jul 8, 2026
e1d015f
feat(automations): enhance error handling and retry logic in automati…
AnkhHeart Jul 8, 2026
2ee31cd
feat(automations): implement analytics tracking for automation action…
AnkhHeart Jul 8, 2026
13435ea
feat(automations): enhance UI layout and functionality for pre-made a…
AnkhHeart Jul 9, 2026
c2d2544
feat(automations): improve localization and dynamic labels in automat…
AnkhHeart Jul 9, 2026
ca9c957
feat(automations): refactor AutomationEditor and EditAutomations comp…
AnkhHeart Jul 10, 2026
ddc96f1
feat(automations): enhance PreMadeAutomations component with video su…
AnkhHeart Jul 10, 2026
6c0a1ff
feat(automations): add branding header to PreMadeAutomations modal wi…
AnkhHeart Jul 10, 2026
1b72a4a
feat(automations): remove brand icon from PreMadeAutomations and add …
AnkhHeart Jul 10, 2026
b81583f
feat(automations): update cover panel height in PreMadeAutomations fo…
AnkhHeart Jul 10, 2026
ff0fbb4
feat(automations): refactor to remove AgentSocketService and replace …
AnkhHeart Jul 14, 2026
50f35f6
feat(automations): remove AUTOMATIONS_MIGRATION.md and consolidate co…
AnkhHeart Jul 14, 2026
7e2825e
Merge branch 'ankh/automations' of github.com:streamlabs/desktop into…
AnkhHeart Jul 14, 2026
45bd646
feat(automations): add fitToScreen call for scene items in createTemp…
AnkhHeart Jul 15, 2026
c44377f
feat(automations): implement AutomationsEmptyState component and enha…
AnkhHeart Jul 17, 2026
19cd5fc
refactor(automations): remove unnecessary comments in AutomationEdito…
AnkhHeart Jul 17, 2026
4455681
refactor(automations): simplify welcome screen logic in EditAutomatio…
AnkhHeart Jul 17, 2026
e7b6144
Refactor AutomationEditor and related components
AnkhHeart Jul 17, 2026
d36acf6
refactor(automations): streamline ActionEditor by extracting controls…
AnkhHeart Jul 17, 2026
a042045
feat(automations): add AutomationTemplates and AutomationTemplatesFoo…
AnkhHeart Jul 17, 2026
78c898b
refactor(automations): improve conditional rendering for game card su…
AnkhHeart Jul 17, 2026
02a8eec
refactor(automations): enhance visibility handling for scene items in…
AnkhHeart Jul 17, 2026
193c3ad
feat(automations): implement instructions fetching and update action …
AnkhHeart Jul 20, 2026
195d71d
feat(automations): refactor downloadAsset function to use downloadFil…
AnkhHeart Jul 21, 2026
dc7f6e2
feat(automations): update streamAvatarApi logic to use new beta host …
AnkhHeart Jul 21, 2026
ef222c8
feat(automations): update streamAvatarApi logic and enhance environme…
AnkhHeart Jul 21, 2026
a3ac92f
Add icon.
michelinewu Jul 21, 2026
aad7dd2
feat(automations): add Automations icon to SceneSelector and reorder …
AnkhHeart Jul 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions app/app-services.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ export { SettingsManagerService } from 'services/settings-manager';
export { MarkersService } from 'services/markers';
export { RealmService } from 'services/realm';
export { StreamAvatarService } from 'services/stream-avatar/stream-avatar-service';
export { StreamAvatarApiService } from 'services/stream-avatar/stream-avatar-api-service';
export { AutomationsService } from 'services/stream-avatar/automations-service';
export { AutomationsEngineService } from 'services/stream-avatar/automations-engine-service';
export { OnboardingV2Service } from 'services/onboarding/onboarding-v2';

// ONLINE SERVICES
Expand Down Expand Up @@ -210,6 +213,9 @@ import { SignalsService } from 'services/signals-manager';
import { TroubleshooterService } from 'services/troubleshooter';
import { OnboardingV2Service } from 'services/onboarding/onboarding-v2';
import { VirtualWebcamService } from 'services/virtual-webcam';
import { StreamAvatarApiService } from 'services/stream-avatar/stream-avatar-api-service';
import { AutomationsService } from 'services/stream-avatar/automations-service';
import { AutomationsEngineService } from 'services/stream-avatar/automations-engine-service';

export const AppServices = {
AppService,
Expand Down Expand Up @@ -298,4 +304,7 @@ export const AppServices = {
ReactiveDataService,
TroubleshooterService,
OnboardingV2Service,
StreamAvatarApiService,
AutomationsService,
AutomationsEngineService,
};
118 changes: 118 additions & 0 deletions app/components-react/editor/elements/AutomationsElement.m.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
@import '../../../styles/index';

.wrapper {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}

.topBar {
display: flex;
align-items: center;
padding: 4px 8px 4px 12px;
flex-shrink: 0;
gap: 4px;
}

.title {
font-weight: 500;
font-size: 14px;
color: var(--title);
margin-right: auto;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.list {
flex: 1;
background: var(--section);
border-radius: 4px;
overflow: hidden;
}

.row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 6px 12px;
cursor: pointer;
transition: background 0.1s;

&:hover {
background: var(--solid-input);

.name {
color: var(--title);
}
}
}

.rowInfo {
display: flex;
flex-direction: column;
min-width: 0;
flex: 1;
margin-right: 8px;
}

.name {
font-size: 12px;
color: var(--paragraph);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.4;
}

.condition {
font-size: 10px;
color: var(--paragraph);
opacity: 0.6;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
line-height: 1.3;
}

.rowActions {
display: flex;
align-items: center;
gap: 8px;
flex-shrink: 0;

i {
font-size: 14px;
opacity: 0.7;
cursor: pointer;

&:hover {
opacity: 1;
}
}
}

.disabled {
opacity: 0.4 !important;
cursor: not-allowed !important;
}

.empty {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--paragraph);
opacity: 0.6;
font-size: 12px;
text-align: center;
padding: 16px;
}

.center {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
157 changes: 157 additions & 0 deletions app/components-react/editor/elements/AutomationsElement.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
import React, { useEffect, useRef, useState } from 'react';
import { Spin, Switch, Tooltip } from 'antd';
import Scrollable from 'components-react/shared/Scrollable';
import { useVuex } from 'components-react/hooks';
import { Services } from 'components-react/service-provider';
import { $t } from 'services/i18n';
import { Conditions } from 'services/stream-avatar/engine/conditions';
import type { TAutomationExport } from 'services/stream-avatar/engine/automations';
import useBaseElement from './hooks';
import styles from './AutomationsElement.m.less';

const mins = { x: 220, y: 120 };

function conditionLabel(automation: TAutomationExport) {
const c = automation.conditions[0];
if (!c?.type) return '';
const def = Conditions[c.type as keyof typeof Conditions];
return def ? def.label : c.type;
}

function AutomationsContent() {
const { AutomationsService, AutomationsEngineService, UserService } = Services;
const { automations, loaded, error, isLoggedIn } = useVuex(() => ({
automations: AutomationsService.state.automations,
loaded: AutomationsService.state.loaded,
error: AutomationsService.state.error,
isLoggedIn: UserService.views.isLoggedIn,
}));

const [simulatingId, setSimulatingId] = useState<number | null>(null);

useEffect(() => {
if (!isLoggedIn) return;
AutomationsService.actions.fetchAll();
}, [isLoggedIn]);

async function simulate(e: React.MouseEvent, id: number) {
e.stopPropagation();
if (simulatingId !== null) return;
setSimulatingId(id);
try {
await AutomationsEngineService.actions.return.simulateAutomation(id);
} finally {
setSimulatingId(null);
}
}

function toggleEnabled(automation: TAutomationExport) {
if (!automation.id) return;
AutomationsService.actions.update(automation.id, {
...automation,
enabled: !automation.enabled,
});
}

function openCreate() {
AutomationsService.actions.showCreateAutomation();
}

function retryNow() {
AutomationsService.actions.fetchAll();
}

if (!isLoggedIn) {
return (
<div className={styles.empty}>
<span>{$t('Log in to use Automations.')}</span>
</div>
);
}

if (error) {
return (
<div className={styles.empty}>
<span>{$t('Unable to reach the automations server. Retrying…')}</span>
<Tooltip title={$t('Retry Now')}>
<i className="icon-repeat icon-button" style={{ marginLeft: 6 }} onClick={retryNow} />
</Tooltip>
</div>
);
}

if (!loaded) {
return (
<div className={styles.center}>
<Spin size="small" />
</div>
);
}

return (
<div className={styles.wrapper}>
<div className={styles.topBar}>
<span className={styles.title}>{$t('Automations')}</span>
<Tooltip title={$t('Add Automation')}>
<i className="icon-add-circle icon-button icon-button--lg" onClick={openCreate} />
</Tooltip>
</div>

{automations.length === 0 ? (
<div className={styles.empty}>
<span>{$t('No automations yet.')}</span>
</div>
) : (
<Scrollable className={styles.list}>
{automations.map(automation => (
<div
key={automation.id}
className={styles.row}
onClick={() =>
automation.id != null &&
AutomationsService.actions.showAutomationEditor(automation.id)
}
>
<div className={styles.rowInfo}>
<span className={styles.name}>
{automation.description || $t('(no description)')}
</span>
<span className={styles.condition}>{conditionLabel(automation)}</span>
</div>
<div className={styles.rowActions} onClick={e => e.stopPropagation()}>
{simulatingId === automation.id ? (
<Spin size="small" />
) : (
<Tooltip title={$t('Test')}>
<i
className={`icon-play-round ${simulatingId !== null ? styles.disabled : ''}`}
onClick={e => automation.id != null && simulate(e, automation.id)}
/>
</Tooltip>
)}
<Switch
size="small"
checked={automation.enabled}
onChange={() => toggleEnabled(automation)}
/>
</div>
</div>
))}
</Scrollable>
)}
</div>
);
}

export function AutomationsElement() {
const containerRef = useRef<HTMLDivElement>(null);
const { renderElement } = useBaseElement(<AutomationsContent />, mins, containerRef.current);

return (
<div ref={containerRef} style={{ height: '100%' }}>
{renderElement()}
</div>
);
}

AutomationsElement.mins = mins;
40 changes: 28 additions & 12 deletions app/components-react/editor/elements/SceneSelector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { Services } from 'components-react/service-provider';
import { useVuex } from 'components-react/hooks';
import HelpTip from 'components-react/shared/HelpTip';
import Scrollable from 'components-react/shared/Scrollable';
import { AutomationsIcon } from 'components-react/shared/icons';
import { useTree, IOnDropInfo } from 'components-react/hooks/useTree';
import { $t } from 'services/i18n';
import { EDismissable } from 'services/dismissables';
Expand All @@ -25,23 +26,28 @@ function SceneSelector() {
SourceFiltersService,
ProjectorService,
EditorCommandsService,
AutomationsService,
UserService,
} = Services;

const { treeSort } = useTree(true);

const [showDropdown, setShowDropdown] = useState(false);
const { scenes, activeSceneId, activeScene, collections, activeCollection } = useVuex(() => ({
scenes: ScenesService.views.scenes.map(scene => ({
title: <TreeNode scene={scene} removeScene={removeScene} />,
key: scene.id,
selectable: true,
isLeaf: true,
})),
activeScene: ScenesService.views.activeScene,
activeSceneId: ScenesService.views.activeSceneId,
activeCollection: SceneCollectionsService.activeCollection,
collections: SceneCollectionsService.collections,
}));
const { scenes, activeSceneId, activeScene, collections, activeCollection, isLoggedIn } = useVuex(
() => ({
scenes: ScenesService.views.scenes.map(scene => ({
title: <TreeNode scene={scene} removeScene={removeScene} />,
key: scene.id,
selectable: true,
isLeaf: true,
})),
activeScene: ScenesService.views.activeScene,
activeSceneId: ScenesService.views.activeSceneId,
activeCollection: SceneCollectionsService.activeCollection,
collections: SceneCollectionsService.collections,
isLoggedIn: UserService.views.isLoggedIn,
}),
);

function showContextMenu(info: { event: React.MouseEvent }) {
info.event.preventDefault();
Expand Down Expand Up @@ -174,6 +180,16 @@ function SceneSelector() {
<i className="icon-add-circle icon-button icon-button--lg" onClick={addScene} />
</Tooltip>

{isLoggedIn && (
<Tooltip title={$t('Edit Automations.')} placement="bottomRight">
<span
className="icon-button icon-button--lg"
onClick={() => AutomationsService.actions.showAutomations()}
>
<AutomationsIcon style={{ width: '15px', height: '15px' }} />
</span>
</Tooltip>
)}
<Tooltip title={$t('Edit Scene Transitions.')} placement="bottomRight">
<i className="icon-transition icon-button icon-button--lg" onClick={showTransitions} />
</Tooltip>
Expand Down
1 change: 1 addition & 0 deletions app/components-react/editor/elements/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { RecordingPreview } from './RecordingPreview';
export { StreamPreview } from './StreamPreview';
export { Browser } from './Browser';
export { Display } from './Display';
export { AutomationsElement as Automations } from './AutomationsElement';
Loading
Loading