Player: quick chat box, danmaku notifications, the player panel, smooth subtitle lift

- Quick chat (Enter in the full player, whatever plays): a box at the
  bottom centre in the control bar's place, compact and widening with
  the text, sharing its draft with the chat panel; Enter sends, Esc or an
  empty Enter closes, it closes by itself after 8 s without typing (the
  draft stays). Keys go to it; the Enter that opened it no longer reaches
  a focused button. Typing never shows the cursor or the controls: the
  frame wakes only on real mouse movement and holds the controls hidden
  while the box or its echo is up. Your sent line shows as an echo (or
  flies as your own danmaku line); your own chat toast is not repeated.
- Subtitle lift: always for the box, for the control bar as a setting
  (off); 4 eased-out sub-pos steps 42 ms apart instead of one per frame.
- Notifications: Toasts / Danmaku (off by default): chat and short notices
  fly over the full player, problems stay toasts; every knob of the look
  plus boldness, persisted, with a live preview strip in Settings.
- Player panel: ⚙ → Player settings docks a foldable glass panel at the
  right (tracks, subtitle size and lift, picture, volume, notifications,
  controls) in place of the card; Esc closes it.
- Danmaku preview: the mock dialogue follows the lift; the stand-in page
  has the chat box too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
foxyzandClaude Opus 5.5 committed 2026-10-18 17:29:13 +03:00
1 parent 6048fbe7d1
commit 69fae18fdc
29 files changed
+2755 -581

No files matched your search

+6 -1
View File
@@ -58,7 +58,11 @@ ShellToast shellToastFor(ToastItem t, AvatarInfo Function(String name) avatar) {
/// all (chat pop-ups, sitting out, alone) is the Notifier's policy.
class PartyToasts extends StatefulWidget {
final VoidCallback? onOpenChat;
const PartyToasts({super.key, this.onOpenChat});
/// Toasts shown another way (flying as danmaku, your quick chat echo):
/// left out when they come.
final bool Function(ToastItem t)? skip;
const PartyToasts({super.key, this.onOpenChat, this.skip});
@override
State<PartyToasts> createState() => _PartyToastsState();
@@ -86,6 +90,7 @@ class _PartyToastsState extends State<PartyToasts> {
// Only new toasts re-run this, not the party they mention.
untracked(() {
for (final t in fresh) {
if (widget.skip?.call(t) ?? false) continue;
_add(shellToastFor(t, (n) => avatarForName(n, coord, t.actorId)));
}
if (gone.isNotEmpty) _leave(gone);