From 8e67ac30c15683acc4e70722cb2e4332a2167d27 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Wed, 24 Jun 2026 15:19:30 +0300 Subject: [PATCH 1/3] docs(planning): add 0.4.8 milestone docs and bump version --- docs/planned-0.4.8.md | 14 ++++++++++++++ pubspec.yaml | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 docs/planned-0.4.8.md diff --git a/docs/planned-0.4.8.md b/docs/planned-0.4.8.md new file mode 100644 index 0000000..9689855 --- /dev/null +++ b/docs/planned-0.4.8.md @@ -0,0 +1,14 @@ +# Milestone 0.4.8: Extension Manager UI + +**Theme**: Разработка пользовательского интерфейса менеджера расширений (Marketplace Client). Мы создаем вкладку в настройках или отдельное окно, где пользователь сможет просматривать установленные расширения, искать новые в маркетплейсе, скачивать, обновлять и удалять их. Бэкенд маркетплейса пока будет замокан или использоваться статический JSON для тестов. + +| ID | Scope | Summary | +|----|-------|---------| +| **UI-EXT-1** | `ui`, `extensions` | **Extension Manager View** — Создать страницу/диалог для менеджера расширений. Вкладки: Installed, Marketplace, Updates. | +| **UI-EXT-2** | `ui`, `extensions` | **Extension Card Component** — Виджет карточки расширения (иконка, название, автор, версия, описание, кнопка Install/Uninstall). | +| **UI-EXT-3** | `core`, `extensions` | **MarketplaceRepository Mock** — Реализовать моковый репозиторий для симуляции запросов к API маркетплейса (пока бэкенд не готов). | +| **UI-EXT-4** | `core`, `extensions` | **Download & Install Flow** — Интеграция UI с процессом скачивания (прогресс-бар), валидации sha256 и распаковки в `~/.querya/extensions/`. | +| **UI-EXT-5** | `core`, `extensions` | **Uninstall & Update** — Механизмы удаления и обновления локальных расширений через UI менеджера. | + +## Заметки +Этот релиз фокусируется на пользовательском опыте (UX/UI) взаимодействия с расширениями. Мы не реализуем сам Marketplace API (он будет в 0.5.0), но закладываем сетевой слой клиента (`MarketplaceRepository`) и мокаем данные для того, чтобы UI можно было использовать и тестировать. Под капотом используются механизмы локального обнаружения, заложенные в релизе 0.4.7. diff --git a/pubspec.yaml b/pubspec.yaml index 835024d..e258577 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: querya_desktop description: Lightweight desktop SQL/NoSQL client. Flutter (Dart). -version: 0.4.7-a +version: 0.4.8 From 2d4e66f6abe13877c0e3b4e9735e4cf9adb273ac Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Wed, 24 Jun 2026 15:21:04 +0300 Subject: [PATCH 2/3] feat(ui): add Extension Manager View UI-EXT-1 --- .../pages/extension_manager_dialog.dart | 131 ++++++++++++++++++ .../main_screen/querya_window_title_bar.dart | 10 ++ 2 files changed, 141 insertions(+) create mode 100644 lib/features/extensions/presentation/pages/extension_manager_dialog.dart diff --git a/lib/features/extensions/presentation/pages/extension_manager_dialog.dart b/lib/features/extensions/presentation/pages/extension_manager_dialog.dart new file mode 100644 index 0000000..eb1adc5 --- /dev/null +++ b/lib/features/extensions/presentation/pages/extension_manager_dialog.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart' as material; +import 'package:shadcn_flutter/shadcn_flutter.dart'; +import 'package:querya_desktop/core/layout/window_layout.dart'; +import 'package:querya_desktop/shared/widgets/widgets.dart'; + +void showExtensionManagerDialog(material.BuildContext context) { + showAppDialog( + context: context, + builder: (ctx) => material.Dialog( + backgroundColor: material.Colors.transparent, + insetPadding: WindowLayout.dialogSymmetricInsets(ctx), + child: const _ExtensionManagerContent(), + ), + ); +} + +class _ExtensionManagerContent extends material.StatefulWidget { + const _ExtensionManagerContent(); + + @override + material.State<_ExtensionManagerContent> createState() => + _ExtensionManagerContentState(); +} + +class _ExtensionManagerContentState extends material.State<_ExtensionManagerContent> { + int _tabIndex = 0; + + @override + material.Widget build(material.BuildContext context) { + final theme = material.Theme.of(context).colorScheme; + final radius = material.Theme.of(context).radiusXxl; + final onPopover = theme.popoverForeground; + + return material.DefaultTextStyle( + style: material.TextStyle(color: onPopover), + child: material.IconTheme( + data: material.IconThemeData(color: onPopover), + child: material.Container( + constraints: WindowLayout.dialogConstraints( + context, + maxWidth: 800, + minWidth: 600, + maxHeight: 700, + ), + decoration: material.BoxDecoration( + color: theme.popover, + borderRadius: material.BorderRadius.circular(radius), + border: material.Border.all(color: theme.border), + ), + child: material.ClipRRect( + borderRadius: material.BorderRadius.circular(radius), + child: material.Column( + crossAxisAlignment: material.CrossAxisAlignment.stretch, + children: [ + material.Padding( + padding: const material.EdgeInsets.fromLTRB(24, 24, 24, 8), + child: material.Row( + mainAxisAlignment: material.MainAxisAlignment.spaceBetween, + crossAxisAlignment: material.CrossAxisAlignment.center, + children: [ + material.Column( + crossAxisAlignment: material.CrossAxisAlignment.start, + children: [ + const Text('Extensions').large().semiBold().foreground(), + const material.SizedBox(height: 6), + const Text('Manage local and marketplace extensions') + .muted() + .small(), + ], + ), + PrimaryButton( + onPressed: () => material.Navigator.of(context).pop(), + child: const Text('Close'), + ), + ], + ), + ), + material.Padding( + padding: const material.EdgeInsets.symmetric( + horizontal: 24.0, vertical: 8.0), + child: material.Row( + children: [ + _buildTabButton(0, 'Installed'), + const material.SizedBox(width: 8), + _buildTabButton(1, 'Marketplace'), + const material.SizedBox(width: 8), + _buildTabButton(2, 'Updates'), + ], + ), + ), + material.Divider(height: 1, color: theme.border), + material.Expanded( + child: material.IndexedStack( + index: _tabIndex, + children: [ + _buildInstalledTab(), + _buildMarketplaceTab(), + _buildUpdatesTab(), + ], + ), + ), + ], + ), + ), + ), + ), + ); + } + + material.Widget _buildTabButton(int index, String label) { + final isSelected = _tabIndex == index; + return SecondaryButton( + onPressed: () => setState(() => _tabIndex = index), + child: Text(label).withColor( + isSelected ? material.Theme.of(context).colorScheme.primary : null, + ), + ); + } + + material.Widget _buildInstalledTab() { + return const material.Center(child: Text('Installed extensions will appear here.')); + } + + material.Widget _buildMarketplaceTab() { + return const material.Center(child: Text('Marketplace extensions will appear here.')); + } + + material.Widget _buildUpdatesTab() { + return const material.Center(child: Text('Extension updates will appear here.')); + } +} diff --git a/lib/features/main_screen/querya_window_title_bar.dart b/lib/features/main_screen/querya_window_title_bar.dart index bba9a8e..ef07294 100644 --- a/lib/features/main_screen/querya_window_title_bar.dart +++ b/lib/features/main_screen/querya_window_title_bar.dart @@ -5,6 +5,7 @@ import 'package:querya_desktop/core/storage/local_db.dart'; import 'package:querya_desktop/core/theme/querya_theme_scope.dart'; import 'package:querya_desktop/features/connections/driver_manager_dialog.dart'; import 'package:querya_desktop/features/settings/preferences_dialog.dart'; +import 'package:querya_desktop/features/extensions/presentation/pages/extension_manager_dialog.dart'; import 'package:querya_desktop/core/actions/sql_editor_actions.dart'; import 'package:shadcn_flutter/shadcn_flutter.dart'; @@ -134,6 +135,15 @@ class QueryaWindowTitleBar extends StatelessWidget { onPressed: (ctx) => showPreferencesDialog(ctx), child: const Text('Preferences…'), ), + const MenuDivider(), + MenuButton( + leading: const material.Icon( + material.Icons.extension_rounded, + size: 18, + ), + onPressed: (ctx) => showExtensionManagerDialog(ctx), + child: const Text('Extensions…'), + ), ], child: const Text('Edit'), ), From f91741bc597f4b7cc99c9b6164c483426b303892 Mon Sep 17 00:00:00 2001 From: ZhuchkaTriplesix Date: Wed, 24 Jun 2026 15:23:58 +0300 Subject: [PATCH 3/3] fix(ui): resolve flutter analyze errors in extension manager dialog --- .../presentation/pages/extension_manager_dialog.dart | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/lib/features/extensions/presentation/pages/extension_manager_dialog.dart b/lib/features/extensions/presentation/pages/extension_manager_dialog.dart index eb1adc5..8c87346 100644 --- a/lib/features/extensions/presentation/pages/extension_manager_dialog.dart +++ b/lib/features/extensions/presentation/pages/extension_manager_dialog.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart' as material; -import 'package:shadcn_flutter/shadcn_flutter.dart'; import 'package:querya_desktop/core/layout/window_layout.dart'; import 'package:querya_desktop/shared/widgets/widgets.dart'; @@ -27,8 +26,8 @@ class _ExtensionManagerContentState extends material.State<_ExtensionManagerCont @override material.Widget build(material.BuildContext context) { - final theme = material.Theme.of(context).colorScheme; - final radius = material.Theme.of(context).radiusXxl; + final theme = Theme.of(context).colorScheme; + final radius = Theme.of(context).radiusXxl; final onPopover = theme.popoverForeground; return material.DefaultTextStyle( @@ -111,8 +110,11 @@ class _ExtensionManagerContentState extends material.State<_ExtensionManagerCont final isSelected = _tabIndex == index; return SecondaryButton( onPressed: () => setState(() => _tabIndex = index), - child: Text(label).withColor( - isSelected ? material.Theme.of(context).colorScheme.primary : null, + child: material.Text( + label, + style: material.TextStyle( + color: isSelected ? Theme.of(context).colorScheme.primary : null, + ), ), ); }