[Overview] Сделать XunCode стабильно работающим на Android 10+, исправить фризы терминала, обойти noexec, переписать README под полностью открытый проект и написать полную документацию по Plugin API.
Проект — Flutter-редактор с Monaco Editor, плагинами, маркетплейсом и встроенным терминалом на proot+Alpine. Текущие проблемы: (1) терминал фризит UI из-за небуферизованного потока событий из Kotlin; (2) WebSocket-handshake к AXS написан вручную и нестабилен; (3) AXS-бинарник копируется в filesDir, где на Android 10+ может срабатывать noexec/W^X; (4) нет дебаунса при накоплении терминального вывода; (5) README и лицензия проприетарные; (6) документация Plugin API неполная и содержит устаревшие ссылки. Этот план исправляет всё перечисленное.
[Types] Никаких новых публичных моделей не требуется — все изменения внутри существующих сервисов и виджетов.
- Внутри
TerminalBridgeдобавляется приватное полеstatic DateTime? _axsStartTime;для таймаута старта AXS. - Внутри
_Tabвterminal_panel.dartдобавляетсяStringBuilder _pendingBufferиTimer? _flushTimerдля batching. - В
TerminalSessionдобавляетсяbool _isClosing = falseдля предотвращения двойного закрытия.
[Files] Изменения коснуться терминального стека, README, лицензии и документации.
-
Новые файлы:
docs/PLUGIN_API_FULL.md— полная документация по Plugin API (жизненный цикл, все методыctx.*, примеры, ограничения, публикация).LICENSE_NEW— временный файл с Apache-2.0 текстом (затем переименуется вLICENSE).
-
Изменения существующих:
android/app/src/main/kotlin/com/xunkal1/xuncode/TerminalService.kt— буферизацияpumpOutput, batching черезStringBuilder+Handler, убратьsink.endOfStream()изfinally, добавитьrunCatchingвокругsink.success().lib/services/terminal_service.dart— заменить ручной WebSocket handshake наWebSocket.connect()изdart:io; добавить таймаут 10 секунд на ожидание порта AXS; переписать_axsBinary()чтобы primary путь былlibaxs.soизnativeLibraryDir, а fallback — копирование из assets; убратьchmod(не нужен для .so).lib/widgets/terminal_panel.dart— добавитьTimer-based debounce (50 мс) на обновлениеValueNotifier; вынести_stripAnsiв статическийfinal RegExpи применять один раз; заменитьjumpToнаanimateToсDuration.zeroтолько если пользователь не скроллил вверх; сделать_handleCtrlсинхронным (убратьasync/await).lib/screens/editor_screen.dart— исправить порядок вызова: при открытии терминала сначала подписаться наEventChannel, потом вызыватьcreateUnsandboxed/create.README.md— полностью переписать под open-source проект с призывом к контрибьюторам, инструкциями по сборке, ссылками на Telegram и GitHub.docs/plugin-api.md— обновить существующий файл, добавить разделыctx.terminal,ctx.fs,ctx.fs.read,ctx.fs.write,ctx.fs.readdir, обновить все ссылки на@H4F8вместо@Hinderchik.pubspec.yaml— обновитьdescriptionиversion(например,1.1.0+2).LICENSE— заменить на Apache-2.0.
[Functions] Функции и методы будут изменены или добавлены для устранения фризов и noexec.
-
Новые функции:
TerminalBridge._axsBinaryFromNativeLib()→Future<File>— возвращаетFile('$nativeDir/libaxs.so'), проверяетexists()иlength() > 0.TerminalSession._connectWs(int port)→Future<WebSocket>— обёрткаWebSocket.connect('ws://127.0.0.1:$port/terminals/new')с таймаутом 5 секунд.TerminalService.pumpOutputBatched(Process, EventSink)→void(private) — читает вStringBuilderи отправляет каждые 50 мс или при._Tab._flushBuffer()→void(private) — синхронно применяет_pendingBufferкValueNotifier.
-
Изменённые функции:
TerminalBridge._axsBinary()— сначала пытаться_axsBinaryFromNativeLib(), если не найден — fallback на копирование из assets. Убратьchmod.TerminalBridge._ensureAxs()— добавитьawait Future.any([...stdout.firstWhere..., Future.delayed(Duration(seconds: 10), () => throw TimeoutException)]).TerminalSession._open()— заменитьHttpClient.openUrl+WebSocket.fromUpgradedSocketнаawait _connectWs(port).TerminalService.pumpOutput()→pumpOutputBatched()— рефакторинг полностью.TerminalSession.startSystemSh()— добавить таймаут 5 секунд на ожидание sink._TerminalViewState._handleCtrl(String)— убратьasync, убратьawait _send(), использоватьwidget.tab.session.write(...)напрямую (он и так async, но вызывать без await). УбратьsetStateизнутриonChanged— перенести модификацию_ctrlModвsetStateпослеif (last >= ...)._Tab._stripAnsi— убрать из конструктора и перенести во_flushBuffer.
-
Удалённые функции:
TerminalBridge._silent()— оставить, но внутри_ensureAxsубрать лишние вызовы (нет). Не удалять.
[Classes] Классы модифицируются минимально, чтобы не ломать архитектуру.
- Изменённые классы:
TerminalService(TerminalService.kt) — добавить полеprivate val outputHandler = android.os.Handler(android.os.Looper.getMainLooper())для batched posting. МетодpumpOutputзаменяется наpumpOutputBatched.TerminalBridge(terminal_service.dart) — добавитьstatic const _axsTimeout = Duration(seconds: 10);._Tab(terminal_panel.dart) — добавить поляStringBuffer _pending = StringBuffer();,Timer? _flushTimer;. В конструкторе заменитьlistenнаlistenс_onChunk._TerminalViewState— добавитьbool _userScrolledUp = false;и логику в_scrollToBottom(не скроллить если_userScrolledUp == true).
[Dependencies] Новые зависимости не нужны. Все изменения используют stdlib Flutter/Dart/Kotlin.
- Проверить в
pubspec.yamlчтоflutter_inappwebview: ^6.0.0совместим с текущим Flutter SDK (>=3.3.0). Если нет — обновить до^6.1.0или^6.0.0оставить. - Убедиться, что
dioиhttpприсутствуют (уже есть). - Никаких новых пакетов добавлять не требуется.
[Testing] Ручное тестирование на устройстве/эмуляторе Android 10+ (API 29+). Unit-тесты для batching-логики не пишем, т.к. проект не содержит тестового фреймворка для Kotlin.
- Проверить сборку:
flutter build apk --debug. - Проверить терминал: открытие, ввод
ls,whoami,apk add, отсутствие фризов приcat /proc/cpuinfo. - Проверить noexec: на Android 13 (API 33) эмуляторе убедиться, что AXS запускается через
libaxs.soбезchmod. - Проверить WebSocket handshake: убедиться, что
/terminals/newвозвращает 101 и не падает. - Проверить README: отображение на GitHub, корректность бейджей.
- Проверить plugin docs: открытие
plugin-docs.htmlв редакторе и отображение Markdown.
[Implementation Order] Логическая последовательность: сначала Kotlin-side (источник фризов), потом Dart-side (потребитель), потом noexec, потом документация и README.
- Обновить
TerminalService.kt— внедритьpumpOutputBatchedсHandlerиStringBuilder, удалитьsink.endOfStream()изfinally. Проверить, что проект компилируется. - Обновить
terminal_service.dart— рефакторинг_axsBinary()для использованияlibaxs.so, добавить_connectWs(), добавить таймаут в_ensureAxs(). - Обновить
terminal_panel.dart— добавить_pending/_flushTimerв_Tab, добавить_userScrolledUpв_TerminalViewState, убратьasyncиз_handleCtrl. - Обновить
editor_screen.dart— поправить порядок подписки наEventChannelперед вызовомcreate/createUnsandboxed. - Проверить noexec — запустить на эмуляторе Android 13+, убедиться что
libaxs.soзапускается. Если нет — добавить fallback через/system/bin/linker64. - Написать
docs/PLUGIN_API_FULL.md— полная документация. - Обновить
docs/plugin-api.md— добавить новые разделы, обновить ссылки. - Переписать
README.md— open-source, контрибьютеры, инструкции по сборке. - Заменить
LICENSEна Apache-2.0. - Обновить
pubspec.yaml— description и version. - Сборка и ручное тестирование —
flutter build apk --debug.