- Add responsive breakpoints (compact <600, medium 600-1023, expanded >=1024) - Add ViewportInfo InheritedWidget for layout-aware descendants - Add inline ChatPanel (380dp right column) for expanded desktop layout - Add channel right-click context menu with Chat option for in-place switching - Add per-target draft persistence via restoredDraft/onDraftChanged callbacks - Fix header chat button to switch to current voice channel when panel open - Fix close = dismiss (preserves last target and draft for reopen) - Add unread dot indicator on channel tiles when chat is closed - Fix audio regression: decimate dBFS computation to every 3rd callback (~31 Hz) to avoid buffer underruns on macOS CoreAudio real-time thread - Add tools/build-macos.sh release build script (7-step process) - Add chat panel switching implementation plan and 3-panel design spec Tests: 183 passed, 2 skipped. Flutter analyze clean.
70 lines
2.4 KiB
Dart
70 lines
2.4 KiB
Dart
// SPDX-License-Identifier: Apache-2.0
|
||
// Canonical layout breakpoints for Chanora.
|
||
//
|
||
// Aligned with Material 3 adaptive layout guidance:
|
||
// compact < 600dp — phone, narrow tablet
|
||
// medium 600–1023 — tablet portrait, small desktop window
|
||
// expanded ≥ 1024dp — desktop, tablet landscape
|
||
//
|
||
// 1024dp was chosen as the expanded threshold based on production app
|
||
// research: Discord (member list at 1024px), Mattermost (RHS docked at
|
||
// ≥ 1024px), and Rocket.Chat (contextual bar persistent at lg/1024px).
|
||
|
||
/// Canonical breakpoint thresholds in logical pixels.
|
||
///
|
||
/// Use these instead of hardcoded pixel values in layout decisions.
|
||
/// Migrate existing `_wideBreakpoint` / `_chatMobileBreakpoint` references
|
||
/// to these named constants.
|
||
class ChanoraBreakpoints {
|
||
ChanoraBreakpoints._();
|
||
|
||
/// Width at which the layout switches from compact to medium.
|
||
/// Below this: single-column mobile layout.
|
||
/// At/above: two-panel side-by-side layout.
|
||
static const double medium = 600;
|
||
|
||
/// Width at which the layout switches from medium to expanded.
|
||
/// Below this: chat opens as a pushed route.
|
||
/// At/above: three-panel layout with inline chat panel.
|
||
static const double expanded = 1024;
|
||
|
||
// Panel sizing constants.
|
||
|
||
/// Fixed width of the left voice/control panel.
|
||
static const double voicePanelWidth = 320;
|
||
|
||
/// Fixed width of the right chat panel (expanded layout only).
|
||
static const double chatPanelWidth = 380;
|
||
|
||
/// Horizontal gap between panels.
|
||
static const double panelGap = 12;
|
||
|
||
/// Desktop snackbar width cap (used when width ≥ [medium]).
|
||
static const double snackBarDesktopCap = 560;
|
||
|
||
/// Connect form action buttons switch from row to column below this width.
|
||
static const double connectActionsStackMaxWidth = 400;
|
||
|
||
/// Modal bottom sheet max height as fraction of screen height.
|
||
static const double modalSheetHeightFraction = 0.72;
|
||
}
|
||
|
||
/// Semantic layout class derived from viewport width.
|
||
enum LayoutClass {
|
||
/// < 600dp — single-column mobile layout.
|
||
compact,
|
||
|
||
/// 600–1023dp — two-panel side-by-side layout.
|
||
medium,
|
||
|
||
/// ≥ 1024dp — three-panel layout with inline chat.
|
||
expanded,
|
||
}
|
||
|
||
/// Computes the current [LayoutClass] from viewport [width].
|
||
LayoutClass layoutClassFromWidth(double width) {
|
||
if (width >= ChanoraBreakpoints.expanded) return LayoutClass.expanded;
|
||
if (width >= ChanoraBreakpoints.medium) return LayoutClass.medium;
|
||
return LayoutClass.compact;
|
||
}
|