Skip to content

Split into a pub workspace: apps/pino_mobile + apps/pino_desktop + packages/pino_shared #14

Description

@leduckhc

Summary

Split the single Flutter package (app/) into a Dart pub workspace with two standalone apps sharing a common package:

pino/
├── pubspec.yaml            # workspace: [apps/*, packages/*]
├── packages/
│   └── pino_shared/        # shared lib (theme + DTOs/protocol today; store+transport later)
├── apps/
│   ├── pino_mobile/        # CLIENT app: pairing + chat (iOS/Android)
│   └── pino_desktop/       # CONTROL app: server control (macOS)
└── server/

Why

The mobile and desktop apps are two different products (per SPEC-03 "two roles, one codebase"):

  • Mobile = client: scans QR, pairs, chats with sessions.
  • Desktop = control app: runs/stops/restarts the server, generates/regenerates QR, revokes devices.

They are already almost fully decoupled in code today — the only shared import is app/lib/app/theme.dart; desktop imports zero mobile layers and vice-versa. The only thing fusing them is one pubspec.yaml and the Platform.isMacOS branch in main.dart.

Splitting now (while separation is cheap) buys:

  • Disjoint dependency sets — mobile stops shipping macOS-only tray_manager/window_manager; desktop stops shipping camera/QR-scan/go_router/pairing deps.
  • No Platform.isMacOS branch — each app has its own main().
  • Structural guarantee the pairing/chat flow can never appear on desktop.
  • A pino_shared seam ready for the future "desktop chat" work (store/transport become shared then).

Deferred to now-B (done): the desktop control app was wired in the single package first (Phase 4) so we have a verified, running app + green tests as the safety net for this refactor.

Scope

packages/pino_shared

  • Move app/lib/app/theme.dart (and any genuinely shared DTOs/protocol) here.
  • Keep it small; grow it when desktop-chat needs store/ + transport/.

apps/pino_mobile

  • Everything in today's app/ except lib/control/ and lib/desktop/.
  • Keeps ios/ + android/ runners, camera/go_router/notifications/pairing deps.
  • Its own main() (delete the Platform.isMacOS branch).

apps/pino_desktop

  • Move lib/control/, lib/desktop/ (incl. daemon/, desktop_controller.dart, desktop_app.dart, screens/, tray/) + their colocated tests.
  • Keeps the macos/ runner (tray_manager + window_manager plugin registration).
  • main() calls runDesktopApp() directly (no platform branch).
  • Deps: tray_manager, window_manager, qr_flutter, flutter_riverpod.

Root

  • pubspec.yaml with workspace: listing apps/* + packages/* (Dart pub workspaces; no melos).
  • Update CI to build/test each app; update docs/specs/README.md Flutter path notes.

The real cost (call out for the implementer)

The Dart-level move is mechanical (imports already disjoint). The heavy part is platform-runner scaffolding: splitting app/macos into pino_desktop and app/ios + app/android into pino_mobile — bundle IDs, Runner.xcodeproj, GeneratedPluginRegistrant, Podfiles. These need GUI/Xcode/Gradle verification (flutter build macos, flutter build ios, flutter build apk).

Acceptance criteria

  • Repo is a Dart pub workspace; apps/pino_mobile, apps/pino_desktop, packages/pino_shared resolve.
  • pino_mobile pubspec has no tray_manager/window_manager; pino_desktop pubspec has no camera/QR-scan/pairing deps.
  • No Platform.isMacOS app-selection branch anywhere; each app has its own main().
  • flutter analyze --fatal-infos clean in every package.
  • All moved tests pass in their new homes (control + desktop + mobile).
  • flutter build macos (desktop) and flutter build ios + apk (mobile) succeed.
  • docs/specs/SPEC-03-desktop-control-app.md "Two roles, one codebase" section updated to describe the workspace layout.

Pointers

  • SPEC-03: docs/specs/SPEC-03-desktop-control-app.md (see "Two roles, one codebase").
  • Desktop code to move: app/lib/control/, app/lib/desktop/.
  • Shared today: app/lib/app/theme.dart.
  • Coupling to delete: Platform.isMacOS branch in app/lib/main.dart.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions