From 129e27a7be4c3d9fe958a68f534797f5fc15c2a4 Mon Sep 17 00:00:00 2001 From: Alex Ayers Date: Tue, 3 Mar 2026 20:05:56 -0500 Subject: [PATCH 1/2] Fixes --- .env.mac.sample | 2 + .env.windows.sample | 2 + docker-compose.yml | 2 +- packages/pubspec.lock | 4 +- .../lib/widgets/genre_autocomplete.dart | 74 +++++++++++-------- 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/.env.mac.sample b/.env.mac.sample index b6d8f04..d1fbe60 100644 --- a/.env.mac.sample +++ b/.env.mac.sample @@ -19,6 +19,8 @@ BUILD_ENV=local BARK_VARIANT=cpu # ACE-Step: "cuda" for Linux/Windows with NVIDIA GPU, "cpu" for Mac ACESTEP_VARIANT=cpu +# ACE-Step LLM: "true" to enable built-in lyrics LLM, "false" to disable (saves memory) +ACESTEP_INIT_LLM=false # YuLan-Mini: "cuda" for NVIDIA GPU, "cpu" for Mac YULAN_VARIANT=cpu # Uncomment the next line on GPU machines to enable NVIDIA device passthrough: diff --git a/.env.windows.sample b/.env.windows.sample index fd45a68..51d0905 100644 --- a/.env.windows.sample +++ b/.env.windows.sample @@ -19,6 +19,8 @@ BUILD_ENV=local BARK_VARIANT=cuda # ACE-Step: "cuda" for Linux/Windows with NVIDIA GPU, "cpu" for Mac ACESTEP_VARIANT=cuda +# ACE-Step LLM: "true" to enable built-in lyrics LLM, "false" to disable (saves VRAM) +ACESTEP_INIT_LLM=true # YuLan-Mini: "cuda" for NVIDIA GPU, "cpu" for Mac YULAN_VARIANT=cuda # Uncomment the next line on GPU machines to enable NVIDIA device passthrough: diff --git a/docker-compose.yml b/docker-compose.yml index d1d9830..4cc51f6 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -169,7 +169,7 @@ services: ACESTEP_CONFIG_PATH: acestep-v15-turbo ACESTEP_LM_MODEL_PATH: acestep-5Hz-lm-0.6B ACESTEP_LM_BACKEND: pt - ACESTEP_INIT_LLM: "false" + ACESTEP_INIT_LLM: "${ACESTEP_INIT_LLM:-false}" ACESTEP_DEVICE: ${ACESTEP_VARIANT:-cpu} ACESTEP_DOWNLOAD_SOURCE: huggingface ACESTEP_API_HOST: 0.0.0.0 diff --git a/packages/pubspec.lock b/packages/pubspec.lock index f29cef4..7cad427 100644 --- a/packages/pubspec.lock +++ b/packages/pubspec.lock @@ -641,10 +641,10 @@ packages: dependency: transitive description: name: meta - sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349" + sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.17.0" mime: dependency: transitive description: diff --git a/packages/studio_ui/lib/widgets/genre_autocomplete.dart b/packages/studio_ui/lib/widgets/genre_autocomplete.dart index 122b0c7..d175df6 100644 --- a/packages/studio_ui/lib/widgets/genre_autocomplete.dart +++ b/packages/studio_ui/lib/widgets/genre_autocomplete.dart @@ -32,6 +32,7 @@ class _GenreAutocompleteState extends State { final _focusNode = FocusNode(); List> _filteredResults = []; bool _showOverlay = false; + bool _isPointerOverOverlay = false; final _layerLink = LayerLink(); OverlayEntry? _overlayEntry; int _highlightedIndex = 0; @@ -90,7 +91,7 @@ class _GenreAutocompleteState extends State { setState(() {}); // rebuild for border color if (!_focusNode.hasFocus) { Future.delayed(const Duration(milliseconds: 100), () { - if (mounted && !_focusNode.hasFocus) { + if (mounted && !_focusNode.hasFocus && !_isPointerOverOverlay) { _removeOverlay(); } }); @@ -204,29 +205,33 @@ class _GenreAutocompleteState extends State { link: _layerLink, showWhenUnlinked: false, offset: Offset(0, size.height + 4), - child: Material( - elevation: 0, - color: AppColors.surfaceHigh, - borderRadius: BorderRadius.circular(8), - child: Container( - constraints: const BoxConstraints(maxHeight: 200), - decoration: BoxDecoration( - border: Border.all(color: AppColors.border), - borderRadius: BorderRadius.circular(8), - ), - child: ListView.builder( - padding: const EdgeInsets.symmetric(vertical: 4), - shrinkWrap: true, - itemCount: _filteredResults.length, - itemBuilder: (context, index) { - final genre = _filteredResults[index]; - final isHighlighted = index == _highlightedIndex; - return _GenreOptionTile( - genre: genre, - highlighted: isHighlighted, - onTap: () => _selectGenre(genre), - ); - }, + child: MouseRegion( + onEnter: (_) => _isPointerOverOverlay = true, + onExit: (_) => _isPointerOverOverlay = false, + child: Material( + elevation: 0, + color: AppColors.surfaceHigh, + borderRadius: BorderRadius.circular(8), + child: Container( + constraints: const BoxConstraints(maxHeight: 200), + decoration: BoxDecoration( + border: Border.all(color: AppColors.border), + borderRadius: BorderRadius.circular(8), + ), + child: ListView.builder( + padding: const EdgeInsets.symmetric(vertical: 4), + shrinkWrap: true, + itemCount: _filteredResults.length, + itemBuilder: (context, index) { + final genre = _filteredResults[index]; + final isHighlighted = index == _highlightedIndex; + return _GenreOptionTile( + genre: genre, + highlighted: isHighlighted, + onTap: () => _selectGenre(genre), + ); + }, + ), ), ), ), @@ -355,7 +360,7 @@ class _GenreAutocompleteState extends State { } } -class _GenreOptionTile extends StatelessWidget { +class _GenreOptionTile extends StatefulWidget { const _GenreOptionTile({ required this.genre, required this.highlighted, @@ -366,16 +371,25 @@ class _GenreOptionTile extends StatelessWidget { final bool highlighted; final VoidCallback onTap; + @override + State<_GenreOptionTile> createState() => _GenreOptionTileState(); +} + +class _GenreOptionTileState extends State<_GenreOptionTile> { + bool _hovered = false; + @override Widget build(BuildContext context) { - final color = genre.value.color; + final color = widget.genre.value.color; return GestureDetector( - onTap: onTap, + onTap: widget.onTap, child: MouseRegion( cursor: SystemMouseCursors.click, + onEnter: (_) => setState(() => _hovered = true), + onExit: (_) => setState(() => _hovered = false), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6), - color: highlighted ? Colors.white10 : Colors.transparent, + color: (_hovered || widget.highlighted) ? Colors.white10 : Colors.transparent, child: Row( children: [ Container( @@ -389,7 +403,7 @@ class _GenreOptionTile extends StatelessWidget { const SizedBox(width: 8), Expanded( child: Text( - genre.key, + widget.genre.key, style: const TextStyle( color: AppColors.text, fontSize: 12, @@ -399,7 +413,7 @@ class _GenreOptionTile extends StatelessWidget { ), const SizedBox(width: 8), Text( - genre.value.name, + widget.genre.value.name, style: TextStyle( color: color.withValues(alpha: 0.6), fontSize: 10, From 950bf51eb8d3519f1bd02ac67c6dee0139bf12d2 Mon Sep 17 00:00:00 2001 From: Alex Ayers Date: Tue, 3 Mar 2026 20:10:03 -0500 Subject: [PATCH 2/2] force a build --- packages/studio_backend/bin/server.dart | 73 +++++++++++-------------- 1 file changed, 31 insertions(+), 42 deletions(-) diff --git a/packages/studio_backend/bin/server.dart b/packages/studio_backend/bin/server.dart index 944de3a..e1e954a 100644 --- a/packages/studio_backend/bin/server.dart +++ b/packages/studio_backend/bin/server.dart @@ -43,8 +43,10 @@ Future main(List args) async { await dependencySetup(database); // Generate and persist the server's external user ID on first start. - final externalUserId = - await di.get().ensureExternalUserId(); + + final externalUserId = await di + .get() + .ensureExternalUserId(); logger.d(message: 'External user ID: $externalUserId'); final port = int.parse(Platform.environment['PORT'] ?? '80'); @@ -53,7 +55,6 @@ Future main(List args) async { ..mount('/v1/users', di.get().router.call) ..mount('/v1/health', di.get().router.call); - if (di.isRegistered()) { rootRouter.mount('/v1/audio', di.get().router.call); } @@ -64,40 +65,22 @@ Future main(List args) async { if (di.isRegistered()) { final trainingProxy = di.get(); - rootRouter.mount( - '/v1/dataset', - trainingProxy.datasetRouter.call, - ); - rootRouter.mount( - '/v1/training', - trainingProxy.trainingRouter.call, - ); + rootRouter.mount('/v1/dataset', trainingProxy.datasetRouter.call); + rootRouter.mount('/v1/training', trainingProxy.trainingRouter.call); } - rootRouter.mount( - '/v1/settings', - di.get().router.call, - ); + rootRouter.mount('/v1/settings', di.get().router.call); rootRouter.mount( '/v1/server-backends', di.get().router.call, ); - rootRouter.mount( - '/v1/peers', - di.get().router.call, - ); + rootRouter.mount('/v1/peers', di.get().router.call); - rootRouter.mount( - '/v1/logs', - di.get().router.call, - ); + rootRouter.mount('/v1/logs', di.get().router.call); - rootRouter.mount( - '/v1/browse', - di.get().router.call, - ); + rootRouter.mount('/v1/browse', di.get().router.call); rootRouter.mount( '/v1/workspaces', @@ -113,21 +96,27 @@ Future main(List args) async { .addMiddleware(_ignoreFavicon()) .addMiddleware(_logUnhandledErrors()) .addMiddleware(logRequests()) - .addMiddleware(corsMiddleware( - allowedOrigins: Platform.environment['CORS_ALLOWED_ORIGINS'] - ?.split(',') - .map((s) => s.trim()) - .where((s) => s.isNotEmpty) - .toList(), - )) - .addMiddleware(signatureVerificationMiddleware( - peerRepository: di.get(), - settingsRepository: di.get(), - )) - .addMiddleware(forwardingMiddleware( - serverBackendRepository: di.get(), - settingsRepository: di.get(), - )) + .addMiddleware( + corsMiddleware( + allowedOrigins: Platform.environment['CORS_ALLOWED_ORIGINS'] + ?.split(',') + .map((s) => s.trim()) + .where((s) => s.isNotEmpty) + .toList(), + ), + ) + .addMiddleware( + signatureVerificationMiddleware( + peerRepository: di.get(), + settingsRepository: di.get(), + ), + ) + .addMiddleware( + forwardingMiddleware( + serverBackendRepository: di.get(), + settingsRepository: di.get(), + ), + ) .addMiddleware(userAutoCreateMiddleware(di.get())) .addHandler(rootRouter.call);