Files
kaptan/ground-suite-design-files/app.js
T
Onur Özkan 6e15063502 save
Signed-off-by: Onur Özkan <work@onurozkan.dev>
2026-08-17 18:51:40 +03:00

361 lines
46 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* Atlas Mission Control POC — all telemetry and operators are fictional. */
const groups = [
{ id: 'alpha', name: 'ALPHA', count: 12, task: 'Area Search', operator: 'Alex', progress: 82, health: 'nominal', color: 'cyan', icon: 'A', current: 'Search area north', next: 'Return', lease: 'Alex' },
{ id: 'bravo', name: 'BRAVO', count: 10, task: 'Route Recon', operator: 'Sara', progress: 71, health: 'nominal', color: 'blue', icon: 'B', current: 'Coastal route', next: 'Survey pass', lease: 'Sara' },
{ id: 'charlie', name: 'CHARLIE', count: 8, task: 'Relay', operator: 'David', progress: 64, health: 'nominal', color: 'violet', icon: 'C', current: 'Relay station', next: 'Hold', lease: 'David' },
{ id: 'delta', name: 'DELTA', count: 12, task: 'Reserve', operator: 'Maria', progress: 35, health: 'attention', color: 'amber', icon: 'D', current: 'Ready reserve', next: 'Awaiting task', lease: 'Maria' },
{ id: 'reserve', name: 'RESERVE', count: 8, task: 'Available', operator: 'Ken', progress: 100, health: 'nominal', color: 'violet', icon: 'R', current: 'Base service', next: 'Available', lease: 'Ken' }
];
const operators = [
{ id: 'alex', name: 'Alex', role: 'Commanding Alpha', color: 'cyan', activity: 'selected Alpha' },
{ id: 'sara', name: 'Sara', role: 'Commanding Bravo', color: 'green', activity: 'updated Bravo search area' },
{ id: 'david', name: 'David', role: 'Planning', color: 'amber', activity: 'drafted Task-144' },
{ id: 'maria', name: 'Maria', role: 'Monitoring', color: 'violet', activity: 'added monitoring note' },
{ id: 'ken', name: 'Ken', role: 'Engineering', color: 'yellow', activity: 'updated comms plan' }
];
const alphaVehicles = [
['UAV-01', 18, 32, 92], ['UAV-02', 25, 39, 86], ['UAV-03', 33, 28, 43],
['UAV-04', 39, 44, 80], ['UAV-05', 45, 35, 74], ['UAV-06', 53, 45, 88],
['UAV-07', 60, 30, 74], ['UAV-08', 66, 42, 83], ['UAV-09', 72, 35, 79],
['UAV-10', 76, 51, 90], ['UAV-11', 55, 57, 82], ['UAV-12', 34, 57, 76]
].map((item, index) => ({ id: item[0], x: item[1], y: item[2], energy: item[3], heading: 20 + index * 16, status: item[0] === 'UAV-03' ? 'critical' : 'nominal' }));
const availableVehicles = [
['ALP-01', 18, 50, 92], ['ALP-02', 31, 47, 71], ['ALP-03', 17, 35, 82], ['ALP-04', 72, 42, 65],
['ALP-05', 71, 55, 80], ['ALP-06', 64, 69, 74], ['ALP-07', 35, 18, 78], ['ALP-08', 38, 72, 61]
].map(item => ({ id: item[0], x: item[1], y: item[2], energy: item[3], heading: 30, status: 'nominal' }));
const requestedView = new URLSearchParams(window.location.search).get('view');
const validViews = ['overview', 'zoom', 'selection', 'collaboration', 'recovery', 'queue', 'system'];
let state = {
view: validViews.includes(requestedView) ? requestedView : 'overview',
selectedGroup: 'alpha',
selectedVehicle: 'UAV-07',
selectedCandidate: 'UAV-19',
modal: null,
toast: '',
layersOpen: false,
reassigned: false,
queueDraft: false,
queue: [
{ title: 'Transit', detail: 'to Northern Sector', duration: '8 min', status: 'completed' },
{ title: 'Search Area A', detail: 'coverage pattern', duration: '18 min', status: 'current' },
{ title: 'Loiter', detail: '6 min', duration: '6 min', status: 'queued' },
{ title: 'Return', detail: 'staging point', duration: '12 min', status: 'queued' },
{ title: 'Recharge', detail: 'base service', duration: '35 min', status: 'queued' },
{ title: 'Search Area B', detail: 'coverage pattern', duration: '20 min', status: 'queued' }
]
};
const icons = {
menu: '<path d="M4 7h16M4 12h16M4 17h16"/>',
map: '<path d="m4 6 5-2 6 2 5-2v14l-5 2-6-2-5 2V6Z"/><path d="M9 4v14m6-12v14"/>',
layers: '<path d="m12 3 8 4.5-8 4.5-8-4.5L12 3Z"/><path d="m4 12 8 4.5 8-4.5M4 16.5l8 4.5 8-4.5"/>',
plane: '<path d="m12 2 2.4 7.1 5.6 2.1v1.6l-6.2.5-1.1 7-1.5.1-1.2-7-6.2-.5v-1.6l5.6-2.1L12 2Z"/>',
people: '<circle cx="9" cy="8" r="3"/><circle cx="16.5" cy="9" r="2.5"/><path d="M3.5 20c.5-3.5 2.4-5.3 5.5-5.3s5 1.8 5.5 5.3M14.2 15.2c3.1-.6 5.5 1.1 6.1 4.8"/>',
task: '<rect x="5" y="4" width="14" height="17" rx="1"/><path d="M8 4.5h8v3H8zM8 12h8M8 16h5"/>',
alert: '<path d="M12 3 21 20H3L12 3Z"/><path d="M12 9v5m0 3h.01"/>',
shield: '<path d="M12 3 20 6v5c0 5-3.2 8.2-8 10-4.8-1.8-8-5-8-10V6l8-3Z"/><path d="m8.5 12 2.2 2.2 4.8-5"/>',
chevron: '<path d="m8 10 4 4 4-4"/>',
plus: '<path d="M12 5v14M5 12h14"/>',
close: '<path d="m6 6 12 12M18 6 6 18"/>',
target: '<circle cx="12" cy="12" r="7"/><circle cx="12" cy="12" r="2"/><path d="M12 2v3m0 14v3M2 12h3m14 0h3"/>',
transfer: '<path d="M4 8h12l-3-3m3 3-3 3M20 16H8l3 3m-3-3 3-3"/>',
hold: '<path d="M8 5v14M16 5v14"/>',
move: '<path d="m4 11 16-7-7 16-2-7-7-2Z"/>',
clock: '<circle cx="12" cy="12" r="8"/><path d="M12 7v5l3 2"/>',
check: '<path d="m5 12 4.2 4.2L19 6.7"/>',
search: '<circle cx="10.5" cy="10.5" r="5.5"/><path d="m15 15 5 5"/>',
info: '<circle cx="12" cy="12" r="8"/><path d="M12 11v5m0-8h.01"/>',
bolt: '<path d="m13 2-8 12h6l-1 8 8-12h-6l1-8Z"/>',
route: '<path d="M5 18c3-8 6-12 14-12"/><circle cx="5" cy="18" r="2"/><path d="m16 4 3 2-3 2"/>',
settings: '<circle cx="12" cy="12" r="3"/><path d="M19 12a7.2 7.2 0 0 0-.1-1.2l2-1.5-2-3.4-2.4 1a7 7 0 0 0-2-1.1L14.2 3h-4.1l-.4 2.8a7 7 0 0 0-2 1.1l-2.4-1-2 3.4 2 1.5a7.2 7.2 0 0 0 0 2.4l-2 1.5 2 3.4 2.4-1a7 7 0 0 0 2 1.1l.4 2.8h4.1l.4-2.8a7 7 0 0 0 2-1.1l2.4 1 2-3.4-2-1.5c.1-.4.1-.8.1-1.2Z"/>'
};
function icon(name, extra = '') {
return '<svg class="icon ' + extra + '" viewBox="0 0 24 24" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round">' + (icons[name] || icons.info) + '</svg>';
}
function groupById(id) {
return groups.find(group => group.id === id) || groups[0];
}
function statusTag(status, label) {
const text = label || ({ nominal: 'Nominal', attention: 'Attention', critical: 'Critical', planned: 'Planned' }[status] || status);
return '<span class="status ' + status + '"><span class="status-dot"></span>' + text + '</span>';
}
function button(label, action, options = {}) {
const cls = 'button ' + (options.kind || 'secondary') + (options.iconOnly ? ' icon-button' : '') + (options.className ? ' ' + options.className : '');
const attrs = (options.disabled ? ' disabled' : '') + (options.label ? ' aria-label="' + options.label + '"' : '') + (options.value ? ' data-value="' + options.value + '"' : '');
return '<button class="' + cls + '" data-action="' + action + '"' + attrs + '>' + (options.icon ? icon(options.icon) : '') + (options.iconOnly ? '' : '<span>' + label + '</span>') + '</button>';
}
function navItem(id, label, iconName) {
return '<button class="nav-item ' + (state.view === id ? 'is-active' : '') + '" data-action="set-view" data-value="' + id + '">' + icon(iconName) + '<span>' + label + '</span></button>';
}
function topBar() {
return '<header class="topbar">' +
'<div class="brand">' + button('', 'toggle-menu', { icon: 'menu', iconOnly: true, label: 'Open navigation' }) +
'<div class="brand-mark">A</div><span>ATLAS</span><span class="brand-divider"></span><span class="mission-name">Mission Alpha-17</span></div>' +
'<nav class="view-nav" aria-label="POC screens">' +
navItem('overview', 'Mission', 'map') + navItem('zoom', 'Zoom', 'target') + navItem('selection', 'Selection', 'search') +
navItem('collaboration', 'Collaboration', 'people') + navItem('recovery', 'Recovery', 'alert') + navItem('queue', 'Intent queue', 'route') + navItem('system', 'System', 'settings') +
'</nav>' +
'<div class="top-metrics">' +
'<div><small>Fleet</small><strong class="green">48 / 50 active</strong></div><div><small>Tasks</small><strong>7 active</strong></div><div><small>Alerts</small><strong class="amber">3</strong></div>' +
'<div><small>Operators</small><strong class="green">5 online</strong></div><div class="hide-at-wide"><small>Network</small><strong class="green">Healthy</strong></div>' +
'<div class="operator-avatar"><span>AX</span><div><strong>Alex</strong><small>Mission operator</small></div></div>' +
'</div></header>';
}
function mapBase() {
return '<svg class="map-art" viewBox="0 0 1000 700" preserveAspectRatio="none" aria-hidden="true">' +
'<defs><filter id="soft"><feGaussianBlur stdDeviation="9"/></filter><pattern id="grid" width="64" height="64" patternUnits="userSpaceOnUse"><path d="M0 0H64V64" fill="none" stroke="#6993b0" stroke-opacity=".11"/></pattern></defs>' +
'<rect width="1000" height="700" fill="url(#grid)"/><path class="land" d="M0 0H352C397 63 392 126 447 179c48 47 30 105 87 162 35 35 30 116-11 151-47 40-126 53-169 109-42 55-8 102-54 99H0Z"/>' +
'<path class="coast" d="M352 0c45 63 40 126 95 179 48 47 30 105 87 162 35 35 30 116-11 151-47 40-126 53-169 109-42 55-8 102-54 99"/><path class="river" d="M55 12c92 74 95 150 153 213 48 52 81 73 87 143 5 58-31 87-25 155 4 42 34 85 44 135"/>' +
'<path class="road" d="M5 520C111 460 148 440 216 376s121-73 177-147c54-70 87-54 158-73 92-24 115-112 233-119"/><path class="road thin" d="M92 52c24 85 85 117 148 161 49 34 45 85 96 121M64 612c84-14 136-81 223-84 97-3 117-68 194-75"/>' +
'<path class="hill" d="M62 104c56-48 121-47 164-16m-143 55c59-49 115-37 156-14M49 587c63-42 116-33 159-7"/><path class="hill" d="M120 245c34-26 71-31 108-20m-67 76c41-34 93-25 131-1"/>' +
'<g class="town"><circle cx="208" cy="381" r="20"/><circle cx="208" cy="381" r="7"/><circle cx="114" cy="526" r="13"/><circle cx="114" cy="526" r="4"/></g>' +
'</svg>' +
'<span class="map-label north-point" style="left:14%;top:20%">North Point</span><span class="map-label cliffside" style="left:58%;top:26%">Cliffside</span><span class="map-label seabridge" style="left:42%;top:68%">Seabridge</span><span class="map-label west-cove" style="left:16%;top:74%">West Cove</span>' +
'<div class="scale"><i></i><span>2 km</span></div>';
}
function mapControls() {
return '<div class="map-controls">' + button('', 'toggle-layers', { icon: 'layers', iconOnly: true, label: 'Toggle map layers' }) + button('', 'map-zoom-in', { icon: 'plus', iconOnly: true, label: 'Zoom map in' }) + button('−', 'map-zoom-out', { iconOnly: true, label: 'Zoom map out' }) + '</div>' +
(state.layersOpen ? '<div class="layers-popover"><strong>Map layers</strong><label><input type="checkbox" checked> Search areas</label><label><input type="checkbox" checked> Planned routes</label><label><input type="checkbox" checked> Constraints</label><label><input type="checkbox"> Map labels</label></div>' : '');
}
function taskArea(label = 'ALPHA\nSEARCH AREA', classes = '') {
return '<div class="task-area ' + classes + '"><div class="task-shape"></div><span>' + label.replace('\n', '<br>') + '</span></div>';
}
function groupMarker(group, x, y, active = false) {
return '<button class="group-marker ' + group.color + (active ? ' is-selected' : '') + '" style="left:' + x + '%;top:' + y + '%" data-action="select-group" data-value="' + group.id + '">' +
'<span class="marker-dot"><b>' + group.icon + '</b></span><span class="marker-card"><strong>' + group.name + '</strong><small>' + group.count + ' UAV · ' + group.task + ' · ' + group.progress + '%</small></span></button>';
}
function vehicleMarker(vehicle, selected = false, label = false) {
const critical = vehicle.status === 'critical';
return '<button class="vehicle-marker ' + (critical ? 'critical' : '') + (selected ? ' selected' : '') + '" style="left:' + vehicle.x + '%;top:' + vehicle.y + '%;--heading:' + vehicle.heading + 'deg" data-action="select-vehicle" data-value="' + vehicle.id + '" aria-label="' + vehicle.id + ', ' + vehicle.energy + '% energy">' +
icon('plane') + (label ? '<span>' + vehicle.id + '</span>' : '') + '</button>';
}
function routeLine(className = '') {
return '<svg class="route-line ' + className + '" viewBox="0 0 1000 700" preserveAspectRatio="none" aria-hidden="true"><path d="M180 535C195 463 238 445 282 384S367 280 433 256c76-27 100-91 179-102 82-11 124-55 217-37" fill="none"/><path class="planned-path" d="M610 142c86 55 127 80 166 168 27 59 72 67 151 104" fill="none"/></svg>';
}
function overviewMap() {
const g = groupById(state.selectedGroup);
return '<div class="map-canvas overview-map">' + mapBase() + mapControls() + routeLine() + taskArea(g.name + '\nAREA ' + (g.task === 'Area Search' ? 'SEARCH' : g.task.toUpperCase()), 'alpha-area') +
'<div class="coverage-ring"></div>' +
alphaVehicles.map(vehicle => vehicleMarker(vehicle, false, vehicle.id === 'UAV-03')).join('') +
groupMarker(groups[1], 65, 27) + groupMarker(groups[2], 69, 50) + groupMarker(groups[3], 73, 68) + groupMarker(groups[4], 57, 82) +
'<div class="geofence red-zone"></div>' +
'<div class="map-mini-exceptions"><strong>Exceptions (3)</strong><p>' + icon('alert') + ' UAV-27 · Navigation degraded</p><p>' + icon('info') + ' Alpha task behind schedule</p><a href="#" data-action="set-view" data-value="recovery">Review recovery</a></div>' +
'</div>';
}
function groupRail(compact = false) {
return '<aside class="group-rail ' + (compact ? 'compact' : '') + '"><div class="rail-heading"><span>Groups</span>' + button('', 'new-group', { icon: 'plus', iconOnly: true, label: 'Create a new group' }) + '</div>' +
'<div class="group-list">' + groups.map(group => {
const selected = state.selectedGroup === group.id;
return '<button class="group-card ' + (selected ? 'selected' : '') + '" data-action="select-group" data-value="' + group.id + '">' +
'<span class="group-badge ' + group.color + '">' + group.icon + '</span><span class="group-card-main"><strong>' + group.name + '</strong><small>' + group.count + ' UAV</small><small>' + group.task + '</small><span class="operator-line">' + group.operator + '</span></span>' +
'<span class="group-card-side">' + statusTag(group.health, '') + '<b>' + group.progress + '%</b><i class="mini-grid">' + Array(9).fill('<em></em>').join('') + '</i></span></button>';
}).join('') + '</div>' + button('New group', 'new-group', { icon: 'plus', className: 'full-width' }) + '</aside>';
}
function attentionRail() {
return '<aside class="attention-rail"><div class="rail-heading"><span>Attention</span>' + button('', 'view-alerts', { icon: 'alert', iconOnly: true, label: 'View all alerts' }) + '</div>' +
'<button class="attention-card warning" data-action="set-view" data-value="recovery">' + icon('alert') + '<span><strong>UAV-27</strong><small>Navigation degraded</small><small>Since 01:32:11</small></span><em>ALPHA</em></button>' +
'<button class="attention-card amber" data-action="set-view" data-value="recovery">' + icon('clock') + '<span><strong>Alpha task behind schedule</strong><small>ETA impact +00:08:45</small></span><em>ALPHA</em></button>' +
'<button class="attention-card amber" data-action="set-view" data-value="selection">' + icon('alert') + '<span><strong>Bravo approaching constraint</strong><small>R-210B coastal limit</small></span><em>BRAVO</em></button>' +
'<div class="fleet-nominal">' + icon('check') + '<div><b>47</b><span>vehicles nominal</span><small>No action required</small></div></div>' +
'</aside>';
}
function contextBar() {
const group = groupById(state.selectedGroup);
return '<section class="context-bar"><div class="context-summary"><span class="group-badge ' + group.color + '">' + group.icon + '</span><div><h2>' + group.name + '</h2><small>' + group.count + ' vehicles</small></div><dl><div><dt>Task</dt><dd>' + group.task + '</dd></div><div><dt>Progress</dt><dd>' + group.progress + '% complete <i class="progress"><b style="width:' + group.progress + '%"></b></i></dd></div><div><dt>Operator</dt><dd>' + group.operator + '</dd></div><div><dt>Status</dt><dd>' + statusTag(group.health) + '</dd></div><div class="wide-only"><dt>Current</dt><dd>' + group.current + '</dd></div><div class="wide-only"><dt>Next</dt><dd>' + group.next + '</dd></div></dl></div>' +
'<div class="context-actions">' + button('Task', 'open-preview', { icon: 'task' }) + button('Move', 'move-group', { icon: 'move' }) + button('Hold', 'hold-group', { icon: 'hold' }) + button('Group', 'group-vehicles', { icon: 'people' }) + button('Split', 'split-group', { icon: 'route' }) + button('Transfer', 'open-transfer', { icon: 'transfer' }) + button('Details', 'open-vehicle-detail', { icon: 'info' }) + '</div></section>';
}
function overviewView() {
return '<main class="workspace overview-workspace">' + groupRail() + '<section class="map-stage">' + overviewMap() + '</section>' + attentionRail() + contextBar() + '</main>';
}
function zoomSmallMap(kind) {
if (kind === 'strategic') {
return '<div class="zoom-map strategic">' + mapBase() + taskArea('TASK 143', 'small-task violet') + groupMarker(groups[0], 35, 33, true) + groupMarker(groups[1], 20, 57) + groupMarker(groups[2], 38, 75) + groupMarker(groups[3], 68, 61) + '<div class="zoom-fleet">' + groups.map(g => '<span><b>' + g.name + '</b><i style="width:' + g.progress + '%"></i></span>').join('') + '</div></div>';
}
if (kind === 'operational') {
return '<div class="zoom-map operational">' + mapBase() + taskArea('AREA SEARCH\n82%', 'wide alpha-area') + routeLine() + alphaVehicles.map(v => vehicleMarker(v)).join('') + '<div class="map-kpi"><small>Area search</small><b>82%</b><span class="ring"></span><em>Alex</em></div></div>';
}
return '<div class="zoom-map vehicle">' + mapBase() + taskArea('', 'wide alpha-area muted') + routeLine() + alphaVehicles.map(v => vehicleMarker(v, v.id === 'UAV-07')).join('') + '<aside class="vehicle-inspector"><div><strong>UAV-07</strong>' + button('', 'close-inspector', { icon: 'close', iconOnly: true, label: 'Close vehicle inspector' }) + '</div><small>ALPHA GROUP</small><dl><div><dt>Position</dt><dd>34.72245° N<br>18.28512° W</dd></div><div><dt>Heading</dt><dd>128°</dd></div><div><dt>Energy</dt><dd class="green">74%</dd></div><div><dt>Navigation</dt><dd class="green">Nominal</dd></div><div><dt>Communication</dt><dd class="green">Good</dd></div><div><dt>Payload</dt><dd>Imaging</dd></div></dl><hr><p><b>Current task</b>Search Area North</p><p><b>Next action</b>Sweep 4</p></aside></div>';
}
function zoomFooter(kind) {
const content = kind === 'strategic' ? '<h3>Groups and task intent</h3><p>High-level view of mission groups and task areas.</p>' : kind === 'operational' ? '<h3>Group composition and coverage</h3><p>Coordinated group view with coverage progress.</p>' : '<h3>Vehicle details on request</h3><p>Progressive disclosure for a selected vehicle.</p>';
return '<footer class="zoom-footer">' + content + '</footer>';
}
function zoomView() {
return '<main class="zoom-view"><header class="view-intro"><div><h1>Semantic zoom <span>— shared multi-UAV mission planner</span></h1><p>Seamless transition from strategic intent to operational control to individual vehicle detail.</p></div><div class="legend">' + statusTag('blue', 'Active') + statusTag('cyan', 'Selected') + statusTag('nominal', 'Nominal') + statusTag('attention', 'Attention') + statusTag('planned', 'Planned') + '</div></header><section class="zoom-grid">' +
'<article class="zoom-panel"><header><span class="letter">A</span><h2>Strategic</h2>' + button('', 'zoom-strategic', { icon: 'target', iconOnly: true, label: 'View strategic detail' }) + '</header>' + zoomSmallMap('strategic') + zoomFooter('strategic') + '</article>' +
'<article class="zoom-panel"><header><span class="letter">B</span><h2>Operational — Alpha</h2>' + button('', 'zoom-operational', { icon: 'target', iconOnly: true, label: 'View operational detail' }) + '</header>' + zoomSmallMap('operational') + zoomFooter('operational') + '</article>' +
'<article class="zoom-panel"><header><span class="letter">C</span><h2>Vehicle — UAV-07</h2>' + button('', 'zoom-vehicle', { icon: 'target', iconOnly: true, label: 'View vehicle detail' }) + '</header>' + zoomSmallMap('vehicle') + zoomFooter('vehicle') + '</article>' +
'</section></main>';
}
function filterChip(label, value) {
return '<div class="filter-chip"><span>' + label + '</span><b>' + value + '</b><button data-action="remove-filter" aria-label="Remove ' + label + ' filter">' + icon('close') + '</button></div>';
}
function selectionMap() {
return '<div class="map-canvas selection-map">' + mapBase() + '<div class="selection-task violet">' + taskArea('TASK-143\nAREA SEARCH', 'violet') + '<span class="task-pin"></span></div>' + routeLine('selection-routes') + availableVehicles.map(v => vehicleMarker(v, true, true)).join('') + '<div class="floating-task-card"><h3>TASK-143 <span>Area search</span></h3><dl><div><dt>Priority</dt><dd class="amber">High</dd></div><div><dt>Assigned</dt><dd class="blue">Alpha / 4</dd></div><div><dt>Area</dt><dd>Northern sector</dd></div><div><dt>Progress</dt><dd>63% <i class="progress"><b style="width:63%"></b></i></dd></div><div><dt>Required</dt><dd>4 UAV</dd></div><div><dt>Health</dt><dd class="green">Green</dd></div></dl><footer>' + icon('clock') + ' Estimated completion <b>18 min</b></footer></div>' + mapControls() + '</div>';
}
function smartSelectionView() {
return '<main class="selection-workspace"><aside class="smart-panel"><header><h1>Smart selection</h1>' + button('', 'show-filter-help', { icon: 'info', iconOnly: true, label: 'Selection help' }) + '</header><label class="form-label">Natural language query</label><div class="natural-query">' + icon('search') + '<p>Available ISR aircraft within 20 km of Task Bravo with more than 60% energy.</p></div><div class="filter-heading"><label class="form-label">Structured filters</label><button data-action="clear-filters">Clear all</button></div><div class="filter-stack">' + filterChip('Type', 'ISR') + filterChip('Availability', 'Available') + filterChip('Distance', '< 20 km') + filterChip('Energy', '> 60%') + filterChip('Task', 'None') + '</div><div class="selection-result"><small>Results</small><strong>8 <span>vehicles selected</span></strong><p>Filter distribution</p><dl><div><dt>Type (ISR)</dt><dd><i style="width:72%"></i>32</dd></div><div><dt>Availability</dt><dd><i style="width:42%"></i>12</dd></div><div><dt>Distance</dt><dd><i style="width:32%"></i>9</dd></div><div><dt>Energy</dt><dd><i style="width:28%"></i>8</dd></div></dl></div>' + button('Assign task', 'open-preview', { icon: 'task', kind: 'primary', className: 'full-width' }) + '<small class="updated">Last updated: 14:32:10 UTC</small></aside><section class="selection-map-stage">' + selectionMap() + '</section>' + assignPanel() + '</main>';
}
function validationRow(label, value, stateName = 'ok') {
return '<div class="validation-row">' + icon(stateName === 'warning' ? 'alert' : stateName === 'info' ? 'info' : 'check') + '<span>' + label + '</span><b class="' + (stateName === 'warning' ? 'amber' : '') + '">' + value + '</b>' + (stateName === 'ok' ? '<i class="round-check">' + icon('check') + '</i>' : '') + '</div>';
}
function assignPanel() {
return '<aside class="assign-panel"><header><div><h1>Assign task</h1><p>6 vehicles selected</p></div>' + button('', 'close-assign', { icon: 'close', iconOnly: true, label: 'Close assignment panel' }) + '</header><dl class="task-meta"><div><dt>Task</dt><dd class="violet">TASK-143 / AREA SEARCH</dd></div><div><dt>Area</dt><dd class="violet">Northern sector</dd></div></dl><h2>Validation summary</h2><div class="validation-list">' + validationRow('Estimated completion', '18 min') + validationRow('Energy reserve', 'All above minimum') + validationRow('Route conflicts', 'None detected') + validationRow('Constraint conflicts', 'None detected') + validationRow('Vehicle compatibility', '6 / 6 compatible') + validationRow('Communication link', 'All links good') + validationRow('Assignment considerations', '1 aircraft may require reassignment.', 'warning') + '</div><div class="info-callout">' + icon('info') + '<p>Review the preview to confirm routing, timing, and resource allocation before assigning.</p></div><footer>' + button('Cancel', 'cancel-modal') + button('Preview', 'open-preview', { kind: 'secondary' }) + button('Confirm', 'confirm-assignment', { icon: 'check', kind: 'primary' }) + '</footer></aside>';
}
function collabMap() {
return '<div class="map-canvas collaboration-map">' + mapBase() + '<div class="collab-alpha-boundary">' + taskArea('ALPHA\n3 UAV · 1 USV', 'alpha-area') + alphaVehicles.slice(0, 3).map(v => vehicleMarker(v)).join('') + '</div>' +
groupMarker(groups[1], 32, 23) + groupMarker(groups[2], 73, 23) + groupMarker(groups[3], 73, 53) + groupMarker(groups[4], 60, 75) +
operators.map((op, i) => '<span class="operator-cursor ' + op.color + '" style="left:' + [20, 36, 74, 38, 62][i] + '%;top:' + [69, 25, 20, 80, 80][i] + '%"><i>◢</i><b>' + op.name + '</b></span>').join('') +
'<article class="lease-card"><small>Command authority</small><h2>Alex</h2><p>lease expires <b>02:34</b></p><hr><small>Observers</small><p>' + statusTag('nominal', 'Sara') + statusTag('amber', 'David') + '</p></article>' +
'</div>';
}
function collaborationView() {
return '<main class="collaboration-workspace">' + groupRail(true) + '<section class="collab-map-stage">' + collabMap() + '</section><aside class="presence-panel"><header><h1>Mission presence</h1></header><div class="presence-list">' + operators.map(op => '<button class="presence-row" data-action="operator-detail" data-value="' + op.id + '"><i class="presence-dot ' + op.color + '"></i><span><strong>' + op.name + '</strong><small>' + op.role + '</small></span><b>⌄</b></button>').join('') + '</div><header class="subheading"><h2>Collaboration stream</h2><button data-action="view-history">View all</button></header><div class="collab-stream">' + operators.slice().reverse().map((op, i) => '<p><i class="presence-dot ' + op.color + '"></i><span>' + op.activity + '</span><small>' + (i * 2 + 2) + 'm ago</small></p>').join('') + '</div></aside><footer class="mission-events"><strong>Mission events</strong><div><span><i class="presence-dot cyan"></i>14:30:41Z&nbsp;&nbsp; Alex selected Alpha</span><span><i class="presence-dot amber"></i>14:28:15Z&nbsp;&nbsp; David drafted Task-144</span><span><i class="presence-dot green"></i>14:26:32Z&nbsp;&nbsp; Sara updated Bravo search area</span><span><i class="presence-dot violet"></i>14:24:10Z&nbsp;&nbsp; Maria added monitoring note</span></div><p>Shared awareness · authority changes are explicit and auditable.</p></footer></main>';
}
function recoveryMap() {
return '<div class="map-canvas recovery-map">' + mapBase() + taskArea('ALPHA\nSEARCH AREA', 'alpha-area') + routeLine() + alphaVehicles.slice(0, 4).map(v => vehicleMarker(v, false, true)).join('') + '<button class="failed-marker" style="left:71%;top:45%" data-action="show-failure"><span>!</span><b>UAV-03</b><small>Unavailable</small></button><article class="recovery-task-map-card">' + icon('people') + '<div><h2>Alpha Search</h2><p>Search and locate pattern</p></div><b class="amber">' + (state.reassigned ? '4 / 4' : '3 / 4') + '<small>assigned</small></b></article></div>';
}
function candidateCard(id, energy, distance, compatibility, selected) {
return '<button class="candidate-card ' + (selected ? 'selected' : '') + '" data-action="select-candidate" data-value="' + id + '"><header>' + icon('people') + '<span><strong>' + id + '</strong>' + statusTag('nominal', 'Ready') + '</span>' + (selected ? '<i class="round-check">' + icon('check') + '</i>' : '') + '</header><dl><div><dt>Energy</dt><dd>' + energy + '% ' + statusTag('nominal', '') + '</dd></div><div><dt>Distance to task</dt><dd>' + distance + ' km</dd></div><div><dt>Task compatibility</dt><dd class="' + (compatibility === 'Medium' ? 'amber' : 'green') + '">' + compatibility + '</dd></div><div><dt>Communication</dt><dd class="green">Strong</dd></div></dl>' + (selected ? '<footer>Recommended</footer>' : '') + '</button>';
}
function recoveryView() {
const active = state.selectedCandidate;
return '<main class="recovery-workspace"><aside class="recovery-left"><header><h1>Attention</h1></header><article class="recovery-alert critical">' + icon('alert') + '<div><h2>UAV-03</h2><p>Navigation unavailable</p><small>Since 14:33:02</small><b>High priority</b></div></article><article class="recovery-alert amber">' + icon('alert') + '<div><h2>Alpha search coverage below required</h2><p>Coverage gap detected</p><b>Requires attention</b></div></article><div class="fleet-summary"><h2>Fleet summary</h2><strong>47</strong><p>vehicles nominal</p><small>All other vehicles operating normally.</small>' + button('View fleet status', 'view-fleet', { icon: 'task' }) + '</div></aside><section class="recovery-map-stage">' + recoveryMap() + '</section><aside class="recovery-right"><header><h1>' + icon('shield') + ' Keep TASK-143 on plan</h1></header><section class="current-state"><h2>Current state</h2><dl><div><dt>Task</dt><dd>Alpha Search</dd></div><div><dt>Required</dt><dd>4 vehicles</dd></div><div><dt>Current</dt><dd class="amber">' + (state.reassigned ? '4 / 4' : '3 / 4') + '<small>assigned</small></dd></div><div><dt>Completion impact</dt><dd class="amber">' + (state.reassigned ? 'None' : '+11 minutes') + '</dd></div></dl></section><section class="recommendations"><h2>Recommended replacements</h2><p>' + icon('info') + ' Select a vehicle to review. This is a system recommendation for operator approval.</p><div class="candidate-grid">' + candidateCard('UAV-19', 78, 6.2, 'High', active === 'UAV-19') + candidateCard('UAV-27', 64, 9.8, 'High', active === 'UAV-27') + candidateCard('UAV-31', 82, 12.4, 'Medium', active === 'UAV-31') + '</div><p class="recommendation-note">' + icon('info') + ' Recommended because it can join before coverage gap exceeds threshold.</p></section><section class="local-preview"><h2>Local preview of reassignment</h2><div><span>' + icon('plane') + ' ' + active + '</span><i></i><b>ALPHA SEARCH AREA</b></div></section><footer>' + button('Cancel', 'cancel-modal') + button('Compare candidates', 'compare-candidates') + button(state.reassigned ? 'Recommendation accepted' : 'Accept recommendation', 'accept-replacement', { icon: 'shield', kind: 'primary', disabled: state.reassigned }) + '</footer></aside><footer class="recovery-events"><span>' + icon('alert') + ' <b>14:33:02</b> system detected UAV-03 unavailable</span><span>' + icon('shield') + ' <b>14:33:04</b> system evaluated replacement options</span><span>' + icon('info') + ' <b>14:33:17</b> operator reviewing recommendation</span></footer></main>';
}
function queueMap() {
return '<div class="queue-map">' + mapBase() + routeLine() + '<div class="base-service">⌾<span>Base<br>Service</span></div>' + taskArea('Search Area A', 'queue-search') + taskArea('Search Area B', 'queue-search future') + '<div class="queue-group-label"><span class="group-badge blue">A</span><strong>ALPHA</strong><small>12</small></div><article class="queue-group-card"><span class="group-badge blue">A</span><h2>Alpha</h2><p>' + icon('people') + '12 vehicles</p><p>' + icon('search') + 'Search</p><p>' + icon('people') + 'Operator&nbsp; Alex</p>' + statusTag('nominal') + '</article></div>';
}
function queueStep(step, index) {
return '<article class="queue-step ' + step.status + '"><button class="drag-handle" aria-label="Drag to reorder">⠿</button><span class="step-number">' + (index + 1) + '</span><div><h2>' + step.title + '</h2><p>' + step.detail + '</p>' + (step.status === 'current' ? '<i class="progress"><b style="width:82%"></b></i><small>82% complete</small>' : '') + '</div><aside><b>Alex</b><small>' + step.duration + '</small>' + statusTag(step.status === 'completed' ? 'nominal' : step.status === 'current' ? 'blue' : 'planned', step.status) + '</aside><div class="step-arrows">' + button('', 'queue-up', { icon: 'chevron', iconOnly: true, label: 'Move step up', value: index }) + button('', 'queue-down', { icon: 'chevron', iconOnly: true, label: 'Move step down', value: index }) + '</div></article>';
}
function queueView() {
const current = state.queue[1];
return '<main class="queue-workspace"><header class="queue-topline"><div><span>Mission</span><b>Coastal Survey</b></div><div><span>Time (local)</span><b>14:35:22</b></div><div><span>Weather</span><b>Overcast 18°C</b></div><div><span>Wind</span><b>12 kt NNE</b></div><div><span>Global status</span>' + statusTag('nominal') + '</div><div><span>Sync</span><b>3s ago</b></div></header><section class="queue-body"><section class="queue-map-stage">' + queueMap() + '</section><section class="queue-panel"><header><h1><span>Alpha</span> · Intent queue</h1><div>' + statusTag('planned', 'Draft changes') + button('', 'queue-menu', { icon: 'settings', iconOnly: true, label: 'Queue options' }) + '</div></header><div class="queue-content"><div class="step-list">' + state.queue.map(queueStep).join('') + '<div class="drop-zone">⠿&nbsp;&nbsp; Drop step here to reorder</div></div><aside class="step-inspector"><header><b>Step 2 of 6</b>' + button('', 'close-inspector', { icon: 'close', iconOnly: true, label: 'Close step inspector' }) + '</header><h2>Goal</h2><p>Search Area A with coverage pattern to identify objects of interest.</p><h2>Owner</h2><p>' + icon('people') + ' Alex</p><h2>Resources</h2><p>' + icon('people') + ' 12 vehicles</p><h2>Est. duration</h2><p>' + icon('clock') + ' 18 min</p><h2>Expected finish</h2><p>⚑ &nbsp;14:53 (local)</p><h2>Linked task</h2><p>⛓ &nbsp;TASK-143</p><h2>Local plan status</h2>' + statusTag('nominal', 'Plan valid') + '</aside></div><footer class="queue-actions">' + button('Add step', 'add-step', { icon: 'plus' }) + button('Reorder', 'reorder-queue', { icon: 'transfer' }) + button('Simulate timeline', 'simulate-queue', { icon: 'clock' }) + button('Review changes', 'review-queue', { kind: 'primary' }) + '</footer></section></section><footer class="queue-events"><strong>Activity timeline</strong><span><i class="presence-dot blue"></i>14:32&nbsp;&nbsp; <b>Alex</b>&nbsp;&nbsp; reordered Return after Search Area A</span><span><i class="presence-dot blue"></i>14:33&nbsp;&nbsp; <b>System</b>&nbsp;&nbsp; recomputed completion estimate</span></footer></main>';
}
function systemView() {
const color = (name, code, cls) => '<div class="swatch"><i class="' + cls + '"></i><b>' + name + '</b><small>' + code + '</small></div>';
return '<main class="system-view"><header class="system-title"><h1>Design system</h1><p>Multi-UAV Mission Control <span>· Screen 7</span></p></header><section class="system-grid"><article class="system-card colors"><h2>1. Color and status</h2><p>Surface (Graphite / Navy)</p><div class="swatches">' + color('Surface 0', '#0B0F14', 's0') + color('Surface 1', '#111724', 's1') + color('Surface 2', '#171D2B', 's2') + color('Surface 3', '#1D2536', 's3') + color('Border', '#2A3447', 'border') + '</div><p>Semantic status (meaning over decoration)</p><div class="swatches semantic">' + color('Blue', 'Active / Primary', 'blue') + color('Cyan', 'Selected', 'cyan') + color('Green', 'Nominal', 'green') + color('Amber', 'Attention', 'amber') + color('Red', 'Critical', 'red') + color('Violet', 'Planned', 'violet') + '</div><ul><li>Color communicates state and priority, not category.</li><li>Do not rely on color alone—use icon, text, and shape.</li><li>Maintain WCAG AA contrast for all text.</li></ul></article><article class="system-card type"><h2>2. Typography</h2><h1>H1 <b>Mission / Group Heading</b></h1><h2>H2 <b>Task Heading</b></h2><p>Body / UI Text <b>Readable action and mission information</b></p><small>Metadata / Secondary</small><pre>UAV-3F7A2C 08:43:21Z ALT 3250 ft</pre></article><article class="system-card components"><h2>3. Components</h2><div class="component-columns"><section><p>Buttons (44px height)</p>' + button('Create group', 'new-group', { icon: 'plus', kind: 'primary' }) + button('Assign task', 'open-preview', { icon: 'task' }) + button('Remove', 'remove', { icon: 'close', kind: 'danger' }) + '</section><section><p>Status chips</p><div class="chip-wrap">' + statusTag('nominal') + statusTag('attention') + statusTag('critical') + statusTag('cyan', 'Selected') + statusTag('planned') + '</div></section><section><p>Authority</p><div class="authority-demo">' + icon('shield') + '<span>Authority<br><b>Operator</b></span></div><div class="authority-demo muted">▣&nbsp;&nbsp; Read-only observer</div></section></div><div class="mini-components"><article class="mini-task"><b>ISR Sweep North</b>' + statusTag('nominal') + '<p>Type ISR</p><p>Area Sector 12</p><p>Assets 14 UAV</p></article><article class="mini-group"><span class="group-badge cyan">27</span><b>Group Alpha</b>' + statusTag('cyan', 'Selected') + '<p>Members 27 UAVs</p><p>Lead UAV-3F7A2C</p></article><article class="mini-alert">' + icon('alert') + '<span>' + statusTag('critical') + '<b>Link degraded on UAV-7B1C</b></span></article></div></article><article class="system-card interactions"><h2>4. Interaction states</h2><div class="multi-select-demo"><div class="select-map">' + alphaVehicles.slice(0, 9).map(v => vehicleMarker(v, v.id !== 'UAV-03')).join('') + '<i></i></div><div class="multi-summary">' + icon('check') + '<strong>27 vehicles selected</strong><span>ISR <b>14</b></span><span>Relay <b>7</b></span><span>Transport <b>6</b></span>' + button('Create group', 'new-group', { icon: 'plus', kind: 'primary' }) + button('Assign task', 'open-preview') + button('Add to group', 'group-vehicles') + '</div></div></article><article class="system-card state-cards"><h2>Selection state cards</h2><div class="selection-state-card selected"><span class="group-badge cyan">27</span><b>Group Alpha</b>' + statusTag('cyan', 'Selected') + '<small>Members · 27 UAVs</small></div><div class="selection-state-card"><span class="group-badge green">◈</span><b>ISR Sweep North</b>' + statusTag('nominal') + '<small>Start · 09:00Z · Assets 14 UAVs</small></div><div class="selection-state-card"><span class="group-badge blue">✈</span><b>UAV-3F7A2C</b>' + statusTag('cyan', 'Selected') + '<small>Type · ISR · Alt 3,250 ft</small></div><div class="selection-state-card muted"><span class="group-badge">□</span><b>No selection</b><small>Use selection tools to choose vehicles or groups.</small></div></article><article class="system-card responsive"><h2>5. Responsive composition</h2><p>Map-first layouts adapt by collapsing / expanding secondary panels.</p><div class="responsive-samples"><div><b>Laptop</b><i class="laptop"></i></div><div><b>27-inch</b><i class="monitor"></i></div><div><b>Ultrawide</b><i class="ultrawide"></i></div><div><b>4K wall</b><i class="wall"></i></div></div><h2>6. Density and accessibility</h2><div class="accessibility"><p><b>44px</b> minimum tap / click target</p><p class="focus-example">Assign task <kbd>Tab</kbd></p><p><b>Aa</b> high-contrast UI text</p><p>' + icon('info') + ' Semantic label and tooltip</p></div></article></section></main>';
}
function modal() {
if (!state.modal) return '';
let content = '';
if (state.modal === 'preview') {
content = '<div class="modal-dialog command-preview" role="dialog" aria-modal="true" aria-labelledby="preview-title"><header><div><small>Command preview</small><h1 id="preview-title">Assign TASK-143</h1></div>' + button('', 'cancel-modal', { icon: 'close', iconOnly: true, label: 'Close command preview' }) + '</header><p class="preview-lede">6 selected vehicles will be assigned to <b>Area Search — Northern Sector</b>.</p><div class="preview-route"><div><i></i><b>Current assignment</b></div><span>' + icon('route') + '</span><div class="target"><i></i><b>Proposed task</b></div></div><div class="preview-validation">' + validationRow('Estimated completion', '18 min') + validationRow('Energy reserve', 'All above minimum') + validationRow('Route conflicts', 'None detected') + validationRow('Geofence conflicts', 'None detected') + validationRow('Vehicle compatibility', '6 / 6') + validationRow('Potential issue', '1 aircraft may require reassignment', 'warning') + '</div><footer>' + button('Cancel', 'cancel-modal') + button('Confirm assignment', 'confirm-assignment', { icon: 'check', kind: 'primary' }) + '</footer></div>';
} else if (state.modal === 'transfer') {
content = '<div class="modal-dialog transfer-dialog" role="dialog" aria-modal="true" aria-labelledby="transfer-title"><header><div><small>Command lease</small><h1 id="transfer-title">Operator B requests command</h1></div>' + button('', 'cancel-modal', { icon: 'close', iconOnly: true, label: 'Close request dialog' }) + '</header><div class="transfer-authority"><span class="group-badge cyan">A</span><div><b>Alpha · command authority</b><p>Held by Alex · lease expires 02:34</p></div></div><label>Reason<textarea readonly>Emergency intervention</textarea></label><p class="notice">' + icon('info') + ' This action will transfer command authority from <b>Alex</b> to <b>Sara</b>. The change is logged for all operators.</p><footer>' + button('Deny', 'deny-transfer') + button('Transfer control', 'confirm-transfer', { icon: 'transfer', kind: 'danger' }) + '</footer></div>';
} else if (state.modal === 'help') {
content = '<div class="modal-dialog help-dialog" role="dialog" aria-modal="true"><header><h1>Smart selection</h1>' + button('', 'cancel-modal', { icon: 'close', iconOnly: true, label: 'Close help' }) + '</header><p>Use a natural-language request or structured filters to select a resource-ready group of aircraft. The POC derives the result from fictional vehicles and always validates the task before confirmation.</p><footer>' + button('Got it', 'cancel-modal', { kind: 'primary' }) + '</footer></div>';
}
return '<div class="modal-backdrop" data-action="cancel-modal"><div data-stop-propagation>' + content + '</div></div>';
}
function toast() {
return state.toast ? '<div class="toast">' + icon('check') + '<span>' + state.toast + '</span><button data-action="clear-toast" aria-label="Dismiss notification">' + icon('close') + '</button></div>' : '';
}
function app() {
const views = { overview: overviewView, zoom: zoomView, selection: smartSelectionView, collaboration: collaborationView, recovery: recoveryView, queue: queueView, system: systemView };
document.getElementById('app').innerHTML = topBar() + views[state.view]() + modal() + toast();
}
function setToast(message) {
state.toast = message;
window.clearTimeout(window.__atlasToast);
window.__atlasToast = window.setTimeout(() => { state.toast = ''; app(); }, 3600);
}
function clickAction(action, value) {
if (action === 'set-view') { state.view = value; state.modal = null; }
if (action === 'select-group') { state.selectedGroup = value; state.view = state.view === 'collaboration' ? 'collaboration' : 'overview'; setToast(groupById(value).name + ' selected — contextual controls updated.'); }
if (action === 'select-vehicle') { state.selectedVehicle = value; state.view = 'zoom'; setToast(value + ' selected — vehicle detail is shown on request.'); }
if (action === 'toggle-layers') state.layersOpen = !state.layersOpen;
if (action === 'open-preview') state.modal = 'preview';
if (action === 'open-transfer') state.modal = 'transfer';
if (action === 'cancel-modal' || action === 'close-assign' || action === 'close-inspector') state.modal = null;
if (action === 'show-filter-help') state.modal = 'help';
if (action === 'confirm-assignment') { state.modal = null; setToast('TASK-143 assigned after route, energy, and constraint validation.'); }
if (action === 'confirm-transfer') { state.modal = null; groups[0].lease = 'Sara'; groups[0].operator = 'Sara'; setToast('Command lease transferred to Sara. The audit event is visible to all operators.'); }
if (action === 'deny-transfer') { state.modal = null; setToast('Command request denied. Alex retains the Alpha command lease.'); }
if (action === 'move-group') setToast('Move intent opened. A route preview would be required before confirmation.');
if (action === 'hold-group') setToast('Hold intent queued for ' + groupById(state.selectedGroup).name + '.');
if (action === 'group-vehicles') setToast('Create / add to group workflow opened for the current selection.');
if (action === 'split-group') setToast('Split group is available after selecting a destination task.');
if (action === 'open-vehicle-detail') { state.view = 'zoom'; state.selectedVehicle = 'UAV-07'; }
if (action === 'select-candidate') state.selectedCandidate = value;
if (action === 'accept-replacement') { state.reassigned = true; setToast(state.selectedCandidate + ' assigned to Alpha Search. Task coverage returned to 4 / 4.'); }
if (action === 'show-failure') setToast('UAV-03 is unavailable. A replacement recommendation needs operator approval.');
if (action === 'queue-up' || action === 'queue-down') {
const index = Number(value); const direction = action === 'queue-up' ? -1 : 1; const next = index + direction;
if (next >= 0 && next < state.queue.length) { const copy = state.queue.slice(); [copy[index], copy[next]] = [copy[next], copy[index]]; state.queue = copy; state.queueDraft = true; setToast('Intent queue reordered locally. Review changes to commit.'); }
}
if (action === 'review-queue') { state.queueDraft = false; setToast('Queue review recorded. The plan remains valid.'); }
if (action === 'reorder-queue') { state.queueDraft = true; setToast('Reorder mode active — use the arrows on each intent step.'); }
if (action === 'simulate-queue') setToast('Simulation complete: no constraints conflict with the proposed intent queue.');
if (action === 'add-step') { state.queue.push({ title: 'New intent', detail: 'Configure task', duration: '—', status: 'queued' }); state.queueDraft = true; setToast('New draft intent added to the queue.'); }
if (action === 'new-group') setToast('New group creation starts with a multi-vehicle selection.');
if (action === 'remove-filter') setToast('Filter removed. The fictional result set has been recalculated.');
if (action === 'clear-filters') setToast('Structured filters cleared. Use a query to build a new selection.');
if (action === 'map-zoom-in') setToast('Map zoomed in — semantic detail increases as you select groups and vehicles.');
if (action === 'map-zoom-out') setToast('Map zoomed out — groups aggregate to protect situational awareness.');
if (action === 'view-alerts' || action === 'view-fleet' || action === 'view-history') setToast('This POC surfaces exceptions first; the nominal fleet stays intentionally quiet.');
if (action === 'compare-candidates') setToast('Candidate comparison keeps energy, task compatibility, and link quality visible.');
if (action === 'clear-toast') state.toast = '';
app();
}
document.addEventListener('click', event => {
const el = event.target.closest('[data-action]');
if (el) {
event.preventDefault();
clickAction(el.dataset.action, el.dataset.value);
return;
}
if (event.target.closest('[data-stop-propagation]')) event.stopPropagation();
});
document.addEventListener('keydown', event => {
if (event.key === 'Escape' && state.modal) { state.modal = null; app(); }
});
app();