23 Commits

Author SHA1 Message Date
ae96218d20 feat: add QR code scanner for media URLs 2026-06-23 09:09:23 +08:00
9cab9dd2cb feat: add URL input button to control toolbar 2026-06-23 09:06:32 +08:00
f303832a1b feat: fullscreen uses SwiftUI with icon+toolbar, double-click toggle, single-click play/pause 2026-06-23 09:02:08 +08:00
4879f56ef1 fix: set window background to black 2026-06-23 08:55:50 +08:00
f1eca64212 fix: compact toolbar with all controls (open/volume/track/playlist)
- Replaced text buttons with SF Symbol icon buttons (28x28)
- All features: folder/prev/play/next/volume+slider/track/repeat/fullscreen/playlist
- Removed Spacer() to prevent buttons from being clipped
- Single compact row layout, no overflow
2026-06-23 08:40:01 +08:00
ad1be14a0c refactor: restore Bricks .ui for video player + native overlay controls
- miniplayer.ui: VideoPlayer 100% fullscreen (Bricks renders video)
- BricksAppView: loads .ui via BricksView, ZStack overlays native controls
- ControlToolbar: semi-transparent (0.6 opacity), slides from bottom
- 30-second auto-hide timer with mouse-hover reset
- Left-top icon toggles toolbar visibility
2026-06-23 08:36:27 +08:00
20f4dec0b9 feat: fullscreen video + click icon to toggle control toolbar
- Video fills entire window via VideoPlayerRepresentable
- MiniPlayerIcon in top-left corner toggles ControlToolbar visibility
- ControlToolbar slides up from bottom with animation
- Removed Bricks JSON UI dependency for main view
- All toolbar features preserved: open/prev/play/next/volume/tracks/repeat/fullscreen/playlist
2026-06-22 20:47:27 +08:00
e7bca7269e feat: add i18n support and app icon in top-left corner
- Added i18n JSON files (zh.json, en.json) with auto language detection
- Updated miniplayer.ui to use i18n keys for all button labels
- Added MiniPlayerIcon overlay in top-left corner of main window
- Load i18n translations on app startup based on system locale
2026-06-22 20:43:13 +08:00
49bc681cf7 chore: rename miniplayer.json to miniplayer.ui 2026-06-22 20:16:32 +08:00
bdf834012a feat: SwiftBricks UI integration with shared AVPlayer
- Inject PlayerBridge AVPlayer into BricksStore for VideoPlayer widget
- Load miniplayer.json from SPM bundle resources
- Fix infinite render loop: pass JSON string instead of schema object
- Fix async closure mismatch in setupBridgeEvents
- Enhanced JSON: URL input, load/play/pause buttons, title bar
- Remove duplicate Resources/miniplayer.json (keep SPM-compliant path)
2026-06-22 09:52:28 +08:00
cae63c6588 fix: persistent fullscreen window — never destroy AVPlayerView
Root cause: every fullscreen exit destroyed the AVPlayerView, triggering
CoreMedia's FigNotificationCenterRemoveWeakListeners which races with
the main thread's autorelease pool drain. No amount of nil-ing the
player reference or delaying the close could prevent this race.

Solution: the fullscreen NSWindow + AVPlayerView are created ONCE on
first use and never destroyed. Toggle uses orderFront/orderOut instead
of makeKeyAndOrderFront/close. The AVPlayerView stays alive for the
entire app lifetime, so CoreMedia teardown never happens during toggle.

Also:
- Removed onDisappear { bridge.cleanup() } — cleanup during app
  termination races with autorelease pool drain. Process exit handles
  all teardown correctly.
- Double-click exit uses DispatchQueue.main.async to avoid calling
  toggleFullscreen from inside the event monitor callback.
- isReleasedWhenClosed = false on fullscreen window
2026-06-22 09:01:04 +08:00
85f5699878 fix: use AVPlayerView (AVKit) instead of custom AVPlayerLayer
Root cause: custom AVPlayerLayer lifecycle management causes CoreMedia
FigNotificationCenter weak listener race condition. Apple's AVPlayerView
handles all internal teardown correctly.

Changes:
- VideoPlayerView.swift: NSViewRepresentable wrapping AVPlayerView (macOS)
  and UIViewControllerRepresentable wrapping AVPlayerViewController (iOS)
- PlayerBridge.swift: fullscreen uses AVPlayerView, removed
  FullscreenPlayerView class entirely
- Fullscreen interaction via NSEvent.addLocalMonitorForEvents:
  double-click exits, single-click toggles play/pause, Escape exits
- cleanup() now closes fullscreen window and removes event monitor
- Removed ExitFullscreen NotificationCenter listener from MiniPlayerApp
2026-06-22 01:16:49 +08:00
0d63414214 fix: replace CoreMedia periodic time observer with pure Swift Timer to eliminate autorelease pool race
Root cause: addPeriodicTimeObserver's internal FigNotificationCenter weak
listener mechanism races with autorelease pool drain on main thread, causing
double-free of weak reference wrappers (KERN_INVALID_ADDRESS).

Changes:
- Replace addPeriodicTimeObserver with Timer.scheduledTimer (bypasses CoreMedia
  weak listener infrastructure entirely)
- Remove ALL Task { @MainActor in } from observer callbacks — these created
  unstructured tasks whose weak ref wrappers conflicted with CoreMedia internals
- Use DispatchQueue.main.async for KVO callbacks (may fire from non-main thread)
- Direct calls for queue: .main callbacks (NotificationCenter, end observer)
- Add isTearingDown flag to prevent callbacks firing during cleanup
- Fix cleanup() order: timer → KVO → notifications → replaceCurrentItem(nil)
- Fix FullscreenPlayerView: use addSublayer instead of replacing backing layer
- Add .onDisappear { bridge.cleanup() } to ensure cleanup before dealloc
- Remove Combine import (no longer needed)
2026-06-22 01:06:39 +08:00
aa19ab9799 feat: i18n support, tri-color icon, fix crash
- Add i18n: Localization.swift + zh-Hans/en Localizable.strings
- Add MiniPlayerIcon SwiftUI view (tri-color play button + ring)
- Fix crash: isInteracting/lastInteraction no longer @Published
- Fix crash: ExitFullscreen notification wrapped in DispatchQueue.main.async
- Auto-hide toolbar uses local @State + Timer (not @Published)
- Replace emoji logo with MiniPlayerIcon
- Move icon sets out of Resources/ to avoid SPM conflicts
- Package.swift: add defaultLocalization, process Resources
2026-06-22 00:40:20 +08:00
3e3a990f5e fix: replace Combine KVO with NSKeyValueObservation to prevent autorelease crash
- Remove Combine import and cancellables
- Use NSKeyValueObservation for timeControlStatus (controlled teardown)
- Invalidate itemStatusObserver before replacing playerItem
- Store endObserver token for proper removal
- Add cleanup() method called on view disappear
- Proper deinit with direct property cleanup (nonisolated-safe)
2026-06-22 00:36:18 +08:00
1fa6f6a4bb feat: toolbar auto-hides after 60s of no interaction 2026-06-22 00:12:43 +08:00
6811572b7e fix: rewrite UI as pure SwiftUI, fix crash/fullscreen/height issues
- Skip BricksView, render video directly in SwiftUI (fixes 1/3 height)
- Fullscreen uses plain NSView+AVPlayerLayer (fixes objc_release crash)
- Remove NSApp.hide(nil) (fixes fullscreen not showing)
- Add volume +/- buttons and volume slider indicator
- Add iOS/iPadOS support with #if os guards
- ProgressSlider decoupled from BricksEngine
- PlayerBridge no longer depends on player.ui JSON
2026-06-22 00:04:06 +08:00
e4ca9bc80a fix: fullscreen crash + video size - use system toggleFullScreen, direct VideoPlayer render 2026-06-21 23:50:54 +08:00
4b94f11664 feat: hidden toolbar with logo toggle, semi-transparent controls, playlist popup window 2026-06-21 23:43:20 +08:00
2a5e3b8ad7 feat: video fills entire window, compact control bar, single-tap play/pause 2026-06-21 23:27:17 +08:00
d7a819af27 fix: resource loading and schema decoding - change .copy to .process for Resources 2026-06-21 23:18:52 +08:00
Hermes Agent
c69ec38dc3 refactor: rewrite MiniPlayer using SwiftBricks framework
- UI defined in player.json (Bricks JSON schema)
- Custom widgets: VideoPlayer (AVPlayer layer), ProgressSlider (seek bar)
- PlayerBridge connects AVPlayer to BricksEngine event bus
- All interactions via binds/events (no imperative UI code)
- Depends on SwiftBricks SPM package
2026-06-21 17:48:06 +08:00
Hermes Agent
c700f27b16 MiniPlayer: multiplatform video/audio player (macOS/iOS/iPadOS) 2026-06-21 11:41:07 +08:00