mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
fix(ui) add embedded workflow notifications, switch tab on workflow run (#3618)
* Include notification view in embedded workflow view * fix(ui) fix workflow not showing up when mothership calls run * Wire up fix in mothership * Refresh events after workflow run * Fix so run workflow switches tabs as well --------- Co-authored-by: Theodore Li <theo@sim.ai>
This commit is contained in:
@@ -250,6 +250,15 @@ export function Home({ chatId }: HomeProps = {}) {
|
||||
[sendMessage]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
const handler = (e: Event) => {
|
||||
const message = (e as CustomEvent<{ message: string }>).detail?.message
|
||||
if (message) sendMessage(message)
|
||||
}
|
||||
window.addEventListener('mothership-send-message', handler)
|
||||
return () => window.removeEventListener('mothership-send-message', handler)
|
||||
}, [sendMessage])
|
||||
|
||||
const handleContextAdd = useCallback(
|
||||
(context: ChatContext) => {
|
||||
let resourceType: MothershipResourceType | null = null
|
||||
|
||||
@@ -651,6 +651,22 @@ export function useChat(
|
||||
) {
|
||||
clientExecutionStarted.add(id)
|
||||
const args = data?.arguments ?? data?.input ?? {}
|
||||
const targetWorkflowId =
|
||||
typeof (args as Record<string, unknown>).workflowId === 'string'
|
||||
? ((args as Record<string, unknown>).workflowId as string)
|
||||
: useWorkflowRegistry.getState().activeWorkflowId
|
||||
if (targetWorkflowId) {
|
||||
const meta = useWorkflowRegistry.getState().workflows[targetWorkflowId]
|
||||
const wasAdded = addResource({
|
||||
type: 'workflow',
|
||||
id: targetWorkflowId,
|
||||
title: meta?.name ?? 'Workflow',
|
||||
})
|
||||
if (!wasAdded && activeResourceIdRef.current !== targetWorkflowId) {
|
||||
setActiveResourceId(targetWorkflowId)
|
||||
}
|
||||
onResourceEventRef.current?.()
|
||||
}
|
||||
executeRunToolOnClient(id, name, args as Record<string, unknown>)
|
||||
}
|
||||
break
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
type Notification,
|
||||
type NotificationAction,
|
||||
openCopilotWithMessage,
|
||||
sendMothershipMessage,
|
||||
useNotificationStore,
|
||||
} from '@/stores/notifications'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
@@ -81,7 +82,11 @@ function CountdownRing({ onPause }: { onPause: () => void }) {
|
||||
* Workflow error notifications auto-dismiss after {@link AUTO_DISMISS_MS}ms with a countdown
|
||||
* ring. Clicking the ring pauses all timers until the notification stack clears.
|
||||
*/
|
||||
export const Notifications = memo(function Notifications() {
|
||||
interface NotificationsProps {
|
||||
embedded?: boolean
|
||||
}
|
||||
|
||||
export const Notifications = memo(function Notifications({ embedded }: NotificationsProps) {
|
||||
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
|
||||
|
||||
const allNotifications = useNotificationStore((state) => state.notifications)
|
||||
@@ -112,7 +117,11 @@ export const Notifications = memo(function Notifications() {
|
||||
|
||||
switch (action.type) {
|
||||
case 'copilot':
|
||||
openCopilotWithMessage(action.message)
|
||||
if (embedded) {
|
||||
sendMothershipMessage(action.message)
|
||||
} else {
|
||||
openCopilotWithMessage(action.message)
|
||||
}
|
||||
break
|
||||
case 'refresh':
|
||||
window.location.reload()
|
||||
@@ -133,7 +142,7 @@ export const Notifications = memo(function Notifications() {
|
||||
})
|
||||
}
|
||||
},
|
||||
[removeNotification]
|
||||
[embedded, removeNotification]
|
||||
)
|
||||
|
||||
useRegisterGlobalCommands(() =>
|
||||
@@ -281,7 +290,9 @@ export const Notifications = memo(function Notifications() {
|
||||
onClick={() => executeAction(notification.id, notification.action!)}
|
||||
className='w-full rounded-[5px] px-[8px] py-[4px] font-medium text-[12px]'
|
||||
>
|
||||
{ACTION_LABELS[notification.action!.type] ?? 'Take action'}
|
||||
{embedded && notification.action!.type === 'copilot'
|
||||
? 'Fix in Mothership'
|
||||
: (ACTION_LABELS[notification.action!.type] ?? 'Take action')}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -3922,7 +3922,7 @@ const WorkflowContent = React.memo(
|
||||
</>
|
||||
)}
|
||||
|
||||
{!embedded && <Notifications />}
|
||||
<Notifications embedded={embedded} />
|
||||
|
||||
{!embedded && isWorkflowReady && isWorkflowEmpty && effectivePermissions.canEdit && (
|
||||
<CommandList />
|
||||
|
||||
@@ -4,4 +4,4 @@ export type {
|
||||
Notification,
|
||||
NotificationAction,
|
||||
} from './types'
|
||||
export { openCopilotWithMessage } from './utils'
|
||||
export { openCopilotWithMessage, sendMothershipMessage } from './utils'
|
||||
|
||||
@@ -3,6 +3,20 @@ import { useCopilotStore, usePanelStore } from '@/stores/panel'
|
||||
|
||||
const logger = createLogger('NotificationUtils')
|
||||
|
||||
/**
|
||||
* Dispatches a message to the mothership chat via a custom window event.
|
||||
* The mothership `Home` component listens for this event and calls `sendMessage`.
|
||||
*/
|
||||
export function sendMothershipMessage(message: string): void {
|
||||
const trimmed = message.trim()
|
||||
if (!trimmed) {
|
||||
logger.warn('sendMothershipMessage called with empty message')
|
||||
return
|
||||
}
|
||||
window.dispatchEvent(new CustomEvent('mothership-send-message', { detail: { message: trimmed } }))
|
||||
logger.info('Dispatched mothership message event', { messageLength: trimmed.length })
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the copilot panel and directly sends the message.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user