mirror of
https://github.com/legop3/MultiRoombaRover.git
synced 2026-09-16 09:31:20 -04:00
prompand
This commit is contained in:
@@ -3,7 +3,6 @@ You are The Overseer, an unserious collaborative rover co-host in chat.
|
|||||||
Output contract:
|
Output contract:
|
||||||
- Return exactly one line.
|
- Return exactly one line.
|
||||||
- Output must be either SKIP or one chat message.
|
- Output must be either SKIP or one chat message.
|
||||||
- Default length is 140 chars or less.
|
|
||||||
- If directly addressed with a request that clearly needs more detail, you may use up to 280 chars.
|
- If directly addressed with a request that clearly needs more detail, you may use up to 280 chars.
|
||||||
- No emojis, no markdown, no extra lines, no assistant framing.
|
- No emojis, no markdown, no extra lines, no assistant framing.
|
||||||
- Don't talk to the same person with a generic message more than once.
|
- Don't talk to the same person with a generic message more than once.
|
||||||
@@ -29,7 +28,6 @@ Environment brief (stable facts):
|
|||||||
- On the carpet side, three docks are mounted on a white wooden beam in front of the TV stand.
|
- On the carpet side, three docks are mounted on a white wooden beam in front of the TV stand.
|
||||||
- A phone button to "call Carpet" is mounted on that same beam.
|
- A phone button to "call Carpet" is mounted on that same beam.
|
||||||
- Near the carpet-side shelves: a small TV/laptop plays live TV.
|
- Near the carpet-side shelves: a small TV/laptop plays live TV.
|
||||||
- To the right is a Roomba-accessible controller station where users can play Peggle.
|
|
||||||
- On the concrete side, a workbench has an additional dock.
|
- On the concrete side, a workbench has an additional dock.
|
||||||
- Common room objects users reference:
|
- Common room objects users reference:
|
||||||
- large green cardboard "minecraft slime" box
|
- large green cardboard "minecraft slime" box
|
||||||
@@ -38,7 +36,7 @@ Environment brief (stable facts):
|
|||||||
- two blue balls (one very large, one smaller)
|
- two blue balls (one very large, one smaller)
|
||||||
- long snake plushie
|
- long snake plushie
|
||||||
- laptop that can be run over
|
- laptop that can be run over
|
||||||
- monitor usually showing a Chromecast screensaver
|
- monitor with a broken screen
|
||||||
|
|
||||||
Rover context hints:
|
Rover context hints:
|
||||||
- CHAT `rover_now` and SNAPSHOT FINAL include qualitative tags:
|
- CHAT `rover_now` and SNAPSHOT FINAL include qualitative tags:
|
||||||
@@ -60,6 +58,7 @@ When to skip:
|
|||||||
- If your line is generic and reusable across many ticks, output SKIP.
|
- If your line is generic and reusable across many ticks, output SKIP.
|
||||||
- If you would repeat the same topic with no new angle, output SKIP.
|
- If you would repeat the same topic with no new angle, output SKIP.
|
||||||
- Quiet periods with no active chat should mostly be SKIP.
|
- Quiet periods with no active chat should mostly be SKIP.
|
||||||
|
- If you are talking about the same rover + person combo, output SKIP.
|
||||||
|
|
||||||
Freshness and anti-repeat:
|
Freshness and anti-repeat:
|
||||||
- Check prior assistant messages in the timeline before speaking.
|
- Check prior assistant messages in the timeline before speaking.
|
||||||
@@ -71,7 +70,6 @@ Freshness and anti-repeat:
|
|||||||
Grounding:
|
Grounding:
|
||||||
- Use timeline for flow.
|
- Use timeline for flow.
|
||||||
- Use SNAPSHOT FINAL as current truth.
|
- Use SNAPSHOT FINAL as current truth.
|
||||||
- Do not invent facts.
|
|
||||||
- Do not assume user intent or next actions.
|
- Do not assume user intent or next actions.
|
||||||
|
|
||||||
Anti-announcer rule:
|
Anti-announcer rule:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -11,7 +11,7 @@
|
|||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
||||||
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
<meta name="apple-mobile-web-app-title" content="Multi Roomba Rover" />
|
||||||
<title>Multi Roomba Rover</title>
|
<title>Multi Roomba Rover</title>
|
||||||
<script type="module" crossorigin src="/assets/index-DW9KJfni.js"></script>
|
<script type="module" crossorigin src="/assets/index-7t2kb-aG.js"></script>
|
||||||
<link rel="stylesheet" crossorigin href="/assets/index-CGvUXLso.css">
|
<link rel="stylesheet" crossorigin href="/assets/index-CGvUXLso.css">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -38,21 +38,49 @@ function StatusTile({ label, value, tone = 'muted', valueClass = '', hideLabel =
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildLidarDots(points = []) {
|
function buildLidarRenderPoints(points = []) {
|
||||||
const center = 110;
|
const center = 110;
|
||||||
const radius = 92;
|
const plotRadius = 102;
|
||||||
const maxDistanceMm = 4000;
|
const validPoints = points.filter(
|
||||||
return points
|
(point) => point && point.valid && Number.isFinite(point.angleDeg) && Number.isFinite(point.distanceMm),
|
||||||
.filter((point) => point && point.valid && Number.isFinite(point.angleDeg) && Number.isFinite(point.distanceMm))
|
);
|
||||||
|
const sortedPoints = [...validPoints].sort((a, b) => Number(a.angleDeg) - Number(b.angleDeg));
|
||||||
|
const filteredPoints = sortedPoints.filter((point, index, list) => {
|
||||||
|
if (list.length < 3) return true;
|
||||||
|
const distanceMm = Math.max(0, Number(point.distanceMm));
|
||||||
|
const prev = list[(index - 1 + list.length) % list.length];
|
||||||
|
const next = list[(index + 1) % list.length];
|
||||||
|
const prevDistanceMm = Math.max(0, Number(prev.distanceMm));
|
||||||
|
const nextDistanceMm = Math.max(0, Number(next.distanceMm));
|
||||||
|
const neighborDistanceMm = Math.max(prevDistanceMm, nextDistanceMm);
|
||||||
|
|
||||||
|
// Drop single-point radial spikes that sit far beyond both adjacent angle samples.
|
||||||
|
if (neighborDistanceMm <= 0) return true;
|
||||||
|
const muchFartherThanNeighbors = distanceMm > neighborDistanceMm * 1.85;
|
||||||
|
const largeAbsoluteGap = distanceMm - neighborDistanceMm > 1400;
|
||||||
|
return !(muchFartherThanNeighbors && largeAbsoluteGap);
|
||||||
|
});
|
||||||
|
const scaleDistances = filteredPoints.map((point) => Math.max(0, Number(point.distanceMm))).sort((a, b) => a - b);
|
||||||
|
const percentileIndex = Math.max(0, Math.floor((scaleDistances.length - 1) * 0.95));
|
||||||
|
const maxDistanceMm = Math.max(1000, scaleDistances[percentileIndex] || 1000);
|
||||||
|
|
||||||
|
return filteredPoints
|
||||||
.map((point) => {
|
.map((point) => {
|
||||||
const angleRad = ((Number(point.angleDeg) - 90) * Math.PI) / 180;
|
const angleRad = ((Number(point.angleDeg) - 90) * Math.PI) / 180;
|
||||||
const normalized = Math.max(0, Math.min(1, Number(point.distanceMm) / maxDistanceMm));
|
const distanceMm = Math.max(0, Number(point.distanceMm));
|
||||||
const scaledRadius = normalized * radius;
|
const normalizedDistance = Math.max(0, Math.min(1, distanceMm / maxDistanceMm));
|
||||||
|
const scaledRadius = normalizedDistance * plotRadius;
|
||||||
const x = center + Math.cos(angleRad) * scaledRadius;
|
const x = center + Math.cos(angleRad) * scaledRadius;
|
||||||
const y = center + Math.sin(angleRad) * scaledRadius;
|
const y = center + Math.sin(angleRad) * scaledRadius;
|
||||||
return `${x},${y}`;
|
const intensity = Math.max(0, Math.min(255, Number(point.intensity) || 0));
|
||||||
})
|
const colorLightness = 38 + (intensity / 255) * 38;
|
||||||
.join(' ');
|
return {
|
||||||
|
key: `${point.angleDeg}-${point.distanceMm}-${point.intensity}`,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
fill: `hsl(198 100% ${colorLightness}%)`,
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function VipNeatoCard({
|
export default function VipNeatoCard({
|
||||||
@@ -84,7 +112,7 @@ export default function VipNeatoCard({
|
|||||||
const robotError = normalizeState(neato?.telemetry?.robotError) || '--';
|
const robotError = normalizeState(neato?.telemetry?.robotError) || '--';
|
||||||
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
const robotAlert = normalizeState(neato?.telemetry?.robotAlert) || '--';
|
||||||
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
const lidarPoints = Array.isArray(lidar?.points) ? lidar.points : [];
|
||||||
const lidarDots = buildLidarDots(lidarPoints);
|
const lidarRenderPoints = buildLidarRenderPoints(lidarPoints);
|
||||||
const lidarStatus = normalizeState(lidar?.status) || '--';
|
const lidarStatus = normalizeState(lidar?.status) || '--';
|
||||||
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
const lidarDebug = lidar?.debug && typeof lidar.debug === 'object' ? lidar.debug : null;
|
||||||
const lidarReason = normalizeState(lidarDebug?.reason) || '--';
|
const lidarReason = normalizeState(lidarDebug?.reason) || '--';
|
||||||
@@ -249,30 +277,20 @@ export default function VipNeatoCard({
|
|||||||
Lidar
|
Lidar
|
||||||
</button>
|
</button>
|
||||||
<div className="mt-0.25 text-center text-[0.68rem] text-slate-400">Front of robot</div>
|
<div className="mt-0.25 text-center text-[0.68rem] text-slate-400">Front of robot</div>
|
||||||
<div className="mt-0.25 aspect-square rounded-md bg-slate-900 p-0.25">
|
<div className="mt-0.25 aspect-square rounded-md bg-slate-800 p-0">
|
||||||
{lidarDots ? (
|
{lidarRenderPoints.length ? (
|
||||||
<svg viewBox="0 0 220 220" className="h-full w-full">
|
<svg viewBox="0 0 220 220" className="h-full w-full">
|
||||||
<rect x="0" y="0" width="220" height="220" fill="#0f172a" />
|
<rect x="0" y="0" width="220" height="220" fill="#1e293b" />
|
||||||
<circle cx="110" cy="110" r="23" fill="none" stroke="#1e293b" strokeWidth="1" />
|
<rect x="7" y="7" width="206" height="206" fill="none" stroke="#64748b" strokeWidth="1" />
|
||||||
<circle cx="110" cy="110" r="46" fill="none" stroke="#334155" strokeWidth="1" />
|
<rect x="33" y="33" width="154" height="154" fill="none" stroke="#475569" strokeWidth="1" />
|
||||||
<circle cx="110" cy="110" r="69" fill="none" stroke="#334155" strokeWidth="1" />
|
<rect x="59" y="59" width="102" height="102" fill="none" stroke="#475569" strokeWidth="1" />
|
||||||
<circle cx="110" cy="110" r="92" fill="none" stroke="#475569" strokeWidth="1" />
|
<rect x="85" y="85" width="50" height="50" fill="none" stroke="#334155" strokeWidth="1" />
|
||||||
<line x1="110" y1="18" x2="110" y2="202" stroke="#334155" strokeWidth="1" />
|
<line x1="110" y1="7" x2="110" y2="213" stroke="#475569" strokeWidth="1" />
|
||||||
<line x1="18" y1="110" x2="202" y2="110" stroke="#334155" strokeWidth="1" />
|
<line x1="7" y1="110" x2="213" y2="110" stroke="#475569" strokeWidth="1" />
|
||||||
<circle cx="110" cy="110" r="4" fill="#e2e8f0" />
|
<circle cx="110" cy="110" r="4" fill="#e2e8f0" />
|
||||||
{lidarPoints
|
{lidarRenderPoints.map((point) => (
|
||||||
.filter(
|
<circle key={point.key} cx={point.x} cy={point.y} r="1.8" fill={point.fill} />
|
||||||
(point) =>
|
))}
|
||||||
point && point.valid && Number.isFinite(point.angleDeg) && Number.isFinite(point.distanceMm),
|
|
||||||
)
|
|
||||||
.map((point) => {
|
|
||||||
const angleRad = ((Number(point.angleDeg) - 90) * Math.PI) / 180;
|
|
||||||
const normalized = Math.max(0, Math.min(1, Number(point.distanceMm) / 4000));
|
|
||||||
const scaledRadius = normalized * 92;
|
|
||||||
const x = 110 + Math.cos(angleRad) * scaledRadius;
|
|
||||||
const y = 110 + Math.sin(angleRad) * scaledRadius;
|
|
||||||
return <circle key={`${point.angleDeg}-${point.distanceMm}-${point.intensity}`} cx={x} cy={y} r="1.6" fill="#38bdf8" />;
|
|
||||||
})}
|
|
||||||
</svg>
|
</svg>
|
||||||
) : (
|
) : (
|
||||||
<div className="flex h-full items-center justify-center text-center text-xs text-slate-400">
|
<div className="flex h-full items-center justify-center text-center text-xs text-slate-400">
|
||||||
|
|||||||
Reference in New Issue
Block a user