part of 'command_executor.dart'; extension _CommandExecutorToast on CommandExecutor { Future<_CommandResult> _toast( RuntimeCommand command, _CommandContext context, RuntimeCommandHandle? handle, ) { final text = _toastText(command); final duration = _duration(command, defaultValue: 1.8); final scope = _scopeFor(command, context, defaultTarget: false); final scopeEpoch = _scopeEpochFor(scope, context); final task = _registerTask(scope, handle); final toastId = 'runtime_toast_${++_toastSerial}'; final toastTextId = '${toastId}_text'; _renderTree.apply( NodeDiff( creates: _toastNodes(id: toastId, textId: toastTextId, text: text), ), ); task.addCancelCallback(() => _renderTree.removeById(toastId)); _schedule(duration, task, () { if ((handle?.isCancelled ?? false) || !_scopeIsAlive(scope)) { _renderTree.removeById(toastId); task.cancel(); return; } _renderTree.removeById(toastId); _emitCommandCompletion( command, context.copyWith(scope: scope, scopeEpoch: scopeEpoch), ); task.complete(_CommandResult.completed); }); return task.future; } List _toastNodes({ required String id, required String textId, required String text, }) { const width = 360.0; const minHeight = 38.0; final lineCount = text.split('\n').length; final height = (minHeight + (lineCount - 1) * 16).clamp(38.0, 92.0); final x = ((_overlaySize.x - width) / 2).clamp(12.0, _overlaySize.x); final y = (_overlaySize.y - height - 58).clamp(12.0, _overlaySize.y); return [ RuntimeNode( id: id, type: RuntimeNodeType.panel, x: x, y: y, width: width, height: height, color: const Color(0xee020617), radius: 12, layer: 10000, ), RuntimeNode( id: textId, type: RuntimeNodeType.text, parent: id, text: text, x: 14, y: 0, width: width - 28, height: height, color: const Color(0xfff8fafc), fontSize: 13, textAlign: RuntimeTextAlignValue.center, layer: 10001, ), ]; } String _toastText(RuntimeCommand command) { final text = _optionalString(command.payload['text'], 'toast.text'); final message = _optionalString( command.payload['message'], 'toast.message', ); if (text != null) { return text; } if (message != null) { return message; } throw const FormatException('toast.text or toast.message is required'); } }