No deployment has been sent to this device. Check the setup, then choose
diff --git a/editor/src/components/DevicesPanel.tsx b/editor/src/components/DevicesPanel.tsx
index e8af63d..84bf0c1 100644
--- a/editor/src/components/DevicesPanel.tsx
+++ b/editor/src/components/DevicesPanel.tsx
@@ -222,7 +222,7 @@ export default function DevicesPanel() {
const stopDeployment = async (device: HardwareDevice, deploymentId: string) => {
if (!window.confirm(
- `Stop the deployment using "${device.name}" and reconnect its hardware monitor?`,
+ `Stop the deployment using "${device.name}" and return control to Devices?`,
)) return
setBusy(true)
setError(null)
@@ -250,7 +250,9 @@ export default function DevicesPanel() {
+ Deployment controls this robot
+
+ {status?.notice || (
+ `“${deploymentLease.name}” is running. Device checks pause here `
+ + 'to prevent two processes from opening the same hardware connection.'
+ )}
+
+
+ )}
+ {status?.error && !deploymentLease && (
+
+ {status.error}
)}
{runtime && !runtime.ok && (
@@ -483,7 +489,7 @@ function DeviceRow({
{state?.loading
? 'Checking hardware and runtime…'
: deploymentLease
- ? `Running deployment: ${deploymentLease.name}`
+ ? `“${deploymentLease.name}” controls this robot`
: ready
? 'Hardware and runtime ready'
: serviceOnline
@@ -494,12 +500,16 @@ function DeviceRow({
-
+
-
+ {deploymentLease?.id && (
+
+ )}
+
@@ -527,7 +548,7 @@ function hardwareRecoveryHint(error?: string): string | null {
return 'The latest check reached the device service, but the servo bus did not answer. Check servo power, bus wiring, the configured serial port, baud rate, and servo IDs. On the device, run ./probe.sh --servos 6.'
}
if (normalized.includes('leased') && normalized.includes('deployment')) {
- return 'The hardware monitor is paused for a deployment. Restore the deployment runtime connection, then press Check so Blacknode can identify the owner or recover a stale lease.'
+ return 'Device checks pause while a deployment owns this robot. If no deployment is running, restore the deployment runtime connection and refresh the status to recover the stale reservation.'
}
return null
}
diff --git a/editor/src/components/NodeSearch.tsx b/editor/src/components/NodeSearch.tsx
index 657cba0..67f88a5 100644
--- a/editor/src/components/NodeSearch.tsx
+++ b/editor/src/components/NodeSearch.tsx
@@ -97,8 +97,17 @@ export default function NodeSearch({
const flatItems = grouped.flatMap(g => g.nodes)
- const left = screenPos.x + 260 > window.innerWidth ? screenPos.x - 260 : screenPos.x
- const top = screenPos.y + 420 > window.innerHeight ? screenPos.y - Math.min(420, screenPos.y) : screenPos.y
+ const viewportMargin = 8
+ const menuWidth = Math.min(260, Math.max(180, window.innerWidth - viewportMargin * 2))
+ const menuHeight = Math.min(420, Math.max(160, window.innerHeight - viewportMargin * 2))
+ const left = Math.max(
+ viewportMargin,
+ Math.min(screenPos.x, window.innerWidth - menuWidth - viewportMargin),
+ )
+ const top = Math.max(
+ viewportMargin,
+ Math.min(screenPos.y, window.innerHeight - menuHeight - viewportMargin),
+ )
return (
<>
@@ -112,7 +121,10 @@ export default function NodeSearch({
style={{
position: 'fixed',
left, top,
- width: 260,
+ width: menuWidth,
+ maxHeight: menuHeight,
+ display: 'flex',
+ flexDirection: 'column',
background: 'var(--panel)',
border: '1px solid var(--line2)',
borderRadius: 10,
@@ -129,6 +141,7 @@ export default function NodeSearch({
display: 'flex',
alignItems: 'center',
gap: 8,
+ flexShrink: 0,
}}>
⌕
{title && (
@@ -162,7 +175,7 @@ export default function NodeSearch({