Protocol 15: one shared session log on the server, chat never logged, Mark a problem

Every app streams its redacted diagnostic lines (server time, batched
every 10 s, gzip, buffered while offline) to POST /l/; the server merges
them with its own into session-YYYY-MM-DD.log in time order, tagged by
who. Opt-out in Settings > About; Mark a problem (Ctrl+Shift+B, the
player menus, About) writes a marker with an optional note. Copy
diagnostics can include the server's session log. Chat text never
reaches any log.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-12 19:41:06 +03:00
1 parent 4ab37c55cf
commit 7487b4deb1
29 files changed
+1380 -47

No files matched your search

+9 -2
View File
@@ -1,6 +1,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../shell/mark_problem.dart';
import '../state/session_state.dart';
import 'text_focus.dart';
@@ -15,11 +16,12 @@ Map<ShortcutActivator, Intent> get appShortcuts => {
const SingleActivator(LogicalKeyboardKey.space): const DoNothingIntent(),
};
/// The window's two app-wide keys, ahead of whatever has focus: while in a
/// The window's app-wide keys, ahead of whatever has focus: while in a
/// server, Space is ready / not ready and Tab opens or closes the chat,
/// on every tab and in every player mode. Not while a text field has
/// focus (the chat field handles Tab and Esc itself) nor while a dialog or
/// menu is in front of [child].
/// menu is in front of [child]. Ctrl+Shift+B ([markProblemKey]) marks a
/// problem anywhere, a text field focused or not.
class AppKeys extends StatefulWidget {
/// Who toggles the chat, latest first: the strip while it is shown, the
/// full player in fullscreen (both hold the shell's toggle).
@@ -53,6 +55,11 @@ class _AppKeysState extends State<AppKeys> {
bool _key(KeyEvent event) {
if (event is! KeyDownEvent || !mounted) return false;
final key = event.logicalKey;
if (markProblemKey.accepts(event, HardwareKeyboard.instance)) {
if (ModalRoute.of(context)?.isCurrent == false) return false;
showMarkProblem(context);
return true;
}
if (key != LogicalKeyboardKey.space && key != LogicalKeyboardKey.tab) return false;
if (SessionState.instance.route.peek() != AppRoute.session) return false;
if (ModalRoute.of(context)?.isCurrent == false) return false;