Files
upstream-tldraw/templates/agent/client/components/GoToAgentButton.tsx
upstream-mirror a774ac673a
Some checks failed
Deploy analytics worker / Deploy analytics worker to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
i18n upload strings / Upload latest strings (push) Failing after 1s
Deploy bemo / Deploy bemo to ${{ (github.ref == 'refs/heads/bemo-production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Failing after 1s
Deploy grafana resources / ${{ github.event_name == 'push' && 'Deploy grafana resources' || 'Validate grafana resources (dry run)' }} (push) Failing after 0s
End to end tests (dotcom) / Check for relevant changes (push) Failing after 0s
End to end tests (examples) / Check for relevant changes (push) Failing after 0s
End to end tests (mermaid readability) / Check for relevant changes (push) Failing after 1s
End to end tests (perf) / Check for relevant changes (push) Failing after 0s
Publish editor extensions / Publish editor extensions (push) Failing after 1s
Publish SDK packages / Remove the publish-packages label (push) Has been skipped
Checks / Tests & checks (push) Has been cancelled
Checks / Build all projects (push) Has been cancelled
Deploy templates / Deploy templates (push) Has been cancelled
End to end tests (dotcom) / End to end tests (dotcom) (push) Has been cancelled
End to end tests (mermaid readability) / End to end tests (mermaid readability) (push) Has been cancelled
End to end tests (perf) / End to end tests (perf) (push) Has been cancelled
Deploy .com / Deploy dotcom to ${{ (github.ref == 'refs/heads/production' && 'production') || (github.ref == 'refs/heads/main' && 'staging') || 'preview' }} (push) Has been cancelled
End to end tests (examples) / End to end tests (examples) (push) Has been cancelled
Publish SDK packages / Publish SDK packages (push) Has been cancelled
Publish SDK packages / Tell user released version (push) Has been cancelled
Publish SDK packages / Publishes code templates to separate repositories (push) Has been cancelled
Publish SDK packages / Deploys the template demos with the new SDK version (push) Has been cancelled
snapshot of https://github.com/tldraw/tldraw.git (main, 2026-09-23; blobs > 131072B omitted)
2026-09-23 16:12:52 +05:00

109 lines
2.8 KiB
TypeScript

import { useCallback } from 'react'
import {
Box,
TldrawUiButton,
TldrawUiButtonIcon,
TldrawUiButtonLabel,
useEditor,
useValue,
Vec,
} from 'tldraw'
import { TldrawAgent } from '../agent/TldrawAgent'
import { useAgents } from '../agent/TldrawAgentAppProvider'
/**
* Renders GoToAgentButton for all agents.
*/
export function GoToAgentButtons() {
const agents = useAgents()
return (
<>
{agents.map((agent) => (
<GoToAgentButton key={agent.id} agent={agent} />
))}
</>
)
}
/**
* A button that zooms the viewport to show where an agent is working.
*/
export function GoToAgentButton({ agent }: { agent: TldrawAgent }) {
const editor = useEditor()
const currentRequest = useValue('activeRequest', () => agent.requests.getActiveRequest(), [agent])
const agentViewport = currentRequest?.bounds
// We only show the button if the agent is offscreen
const agentIsOffscreen = useValue(
'agentIsOffscreen',
() => {
if (!agentViewport) return false
const screenBounds = editor.getViewportScreenBounds()
const agentViewportScreenCorners = Box.From(agentViewport).corners.map((corner) =>
editor.pageToViewport(corner)
)
return !Box.FromPoints(agentViewportScreenCorners).collides(screenBounds)
},
[agentViewport]
)
// The button's arrow points towards the agent
const angleToAgent = useValue(
'angleToAgent',
() => {
if (!agentViewport) return
if (agentIsOffscreen) return
const agentCenter = Box.From(agentViewport).center
const agentScreenCenter = editor.pageToViewport(agentCenter)
const screenBounds = editor.getViewportScreenBounds()
const screenCenter = Box.From(screenBounds).center
const displacement = Vec.From(agentScreenCenter).sub(screenCenter)
return Math.atan2(displacement.y, displacement.x) * (180 / Math.PI)
},
[agentViewport]
)
const handleClick = useCallback(() => {
if (agentViewport) {
const bounds = Box.From(agentViewport).expandBy(50)
editor.zoomToBounds(bounds, { animation: { duration: 220 } })
}
}, [agentViewport, editor])
if (!agentViewport) return null
if (!agentIsOffscreen) return null
return (
<TldrawUiButton type="low" onClick={handleClick}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', gap: 8 }}>
<TldrawUiButtonIcon
icon={
<svg
width="16"
height="16"
viewBox="0 0 24 24"
style={{
transform: `rotate(${angleToAgent}deg)`,
transition: 'transform 0.2s ease',
}}
>
<path
d="M8 6l6 6-6 6"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
fill="none"
/>
</svg>
}
/>
<TldrawUiButtonLabel>Go to {agent.id}</TldrawUiButtonLabel>
</div>
</TldrawUiButton>
)
}