From 08fe4eef4e274934405a9db37742d6e36ec885d5 Mon Sep 17 00:00:00 2001 From: Levi Date: Fri, 11 Sep 2026 08:00:03 +0800 Subject: [PATCH] fix(ui): refine create task actions and relocate icon sources (#1500) --- .../gopeed-file-category-icons/.gitignore | 0 .../gopeed-file-category-icons/SKILL.md | 4 +- .../agents/openai.yaml | 0 .../assets/folder-symbol-template.svg | 0 .../assets/manifest.example.json | 0 .../references/style-guide.md | 0 .../scripts/build_icon_font.py | 0 .../scripts/preview_icon_font.py | 0 .../scripts/requirements.txt | 0 .../design}/file-category-icons/README.md | 6 +- .../design}/file-category-icons/manifest.json | 4 +- .../preview/gopeed_icons_preview.png | Bin .../file-category-icons/svg/file-android.svg | 0 .../file-category-icons/svg/file-archive.svg | 0 .../file-category-icons/svg/file-audio.svg | 0 .../file-category-icons/svg/file-code.svg | 0 .../file-category-icons/svg/file-database.svg | 0 .../svg/file-disk-image.svg | 0 .../file-category-icons/svg/file-document.svg | 0 .../file-category-icons/svg/file-ebook.svg | 0 .../file-category-icons/svg/file-font.svg | 0 .../file-category-icons/svg/file-image.svg | 0 .../svg/file-installer.svg | 0 .../file-category-icons/svg/file-ios.svg | 0 .../file-category-icons/svg/file-pdf.svg | 0 .../svg/file-presentation.svg | 0 .../svg/file-spreadsheet.svg | 0 .../file-category-icons/svg/file-text.svg | 0 .../file-category-icons/svg/file-video.svg | 0 .../file-category-icons/svg/file-web.svg | 0 .../design}/file-category-icons/svg/file.svg | 0 .../file-category-icons/svg/folder-bt.svg | 0 .../file-category-icons/svg/folder.svg | 0 .../file-category-icons/svg/protocol-bt.svg | 0 .../file-category-icons/svg/protocol-ed2k.svg | 0 .../pages/create_task_window_page.dart | 32 ++++---- .../widgets/create_task_action_buttons.dart | 56 +++++++++++++ .../widgets/resolve_file_tree.dart | 76 ++++++++++-------- .../widgets/resolve_task_actions.dart | 50 ------------ ui/flutter/lib/l10n/app_ar.arb | 2 +- ui/flutter/lib/l10n/app_az.arb | 2 +- ui/flutter/lib/l10n/app_ca.arb | 2 +- ui/flutter/lib/l10n/app_de.arb | 2 +- ui/flutter/lib/l10n/app_en.arb | 2 +- ui/flutter/lib/l10n/app_es.arb | 2 +- ui/flutter/lib/l10n/app_fa.arb | 2 +- ui/flutter/lib/l10n/app_fr.arb | 2 +- ui/flutter/lib/l10n/app_hu.arb | 2 +- ui/flutter/lib/l10n/app_id.arb | 2 +- ui/flutter/lib/l10n/app_it.arb | 2 +- ui/flutter/lib/l10n/app_ja.arb | 2 +- ui/flutter/lib/l10n/app_ko.arb | 2 +- ui/flutter/lib/l10n/app_pl.arb | 2 +- ui/flutter/lib/l10n/app_pt.arb | 2 +- ui/flutter/lib/l10n/app_ru.arb | 2 +- ui/flutter/lib/l10n/app_ta.arb | 2 +- ui/flutter/lib/l10n/app_tr.arb | 2 +- ui/flutter/lib/l10n/app_uk.arb | 2 +- ui/flutter/lib/l10n/app_vi.arb | 2 +- ui/flutter/lib/l10n/app_zh.arb | 2 +- ui/flutter/lib/l10n/app_zh_TW.arb | 2 +- .../create_task_window_page_test.dart | 12 +-- .../resolve_task_actions_test.dart | 13 ++- ui/flutter/test/widget_test.dart | 35 +++++++- 64 files changed, 193 insertions(+), 139 deletions(-) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/.gitignore (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/SKILL.md (94%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/agents/openai.yaml (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/assets/folder-symbol-template.svg (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/assets/manifest.example.json (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/references/style-guide.md (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/scripts/build_icon_font.py (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/scripts/preview_icon_font.py (100%) rename {.agent => ui/flutter/.agent}/skills/gopeed-file-category-icons/scripts/requirements.txt (100%) rename {design => ui/flutter/design}/file-category-icons/README.md (75%) rename {design => ui/flutter/design}/file-category-icons/manifest.json (95%) rename {design => ui/flutter/design}/file-category-icons/preview/gopeed_icons_preview.png (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-android.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-archive.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-audio.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-code.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-database.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-disk-image.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-document.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-ebook.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-font.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-image.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-installer.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-ios.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-pdf.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-presentation.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-spreadsheet.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-text.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-video.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file-web.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/file.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/folder-bt.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/folder.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/protocol-bt.svg (100%) rename {design => ui/flutter/design}/file-category-icons/svg/protocol-ed2k.svg (100%) create mode 100644 ui/flutter/lib/features/tasks/presentation/widgets/create_task_action_buttons.dart delete mode 100644 ui/flutter/lib/features/tasks/presentation/widgets/resolve_task_actions.dart diff --git a/.agent/skills/gopeed-file-category-icons/.gitignore b/ui/flutter/.agent/skills/gopeed-file-category-icons/.gitignore similarity index 100% rename from .agent/skills/gopeed-file-category-icons/.gitignore rename to ui/flutter/.agent/skills/gopeed-file-category-icons/.gitignore diff --git a/.agent/skills/gopeed-file-category-icons/SKILL.md b/ui/flutter/.agent/skills/gopeed-file-category-icons/SKILL.md similarity index 94% rename from .agent/skills/gopeed-file-category-icons/SKILL.md rename to ui/flutter/.agent/skills/gopeed-file-category-icons/SKILL.md index 02bf3027..f4fbaf9a 100644 --- a/.agent/skills/gopeed-file-category-icons/SKILL.md +++ b/ui/flutter/.agent/skills/gopeed-file-category-icons/SKILL.md @@ -7,7 +7,7 @@ description: Design or revise Gopeed file-classification icons as one cohesive S Maintain Gopeed's file-classification visual language and its deterministic SVG → TTF → Flutter `IconData` pipeline. -The canonical editable materials live in `design/file-category-icons/`, outside the Flutter project. Read its `README.md` before changing any source. Do not add that directory to Flutter assets. +The canonical editable materials live in `design/file-category-icons/` as tooling-only sources inside the Flutter project. Read its `README.md` before changing any source. Do not add that directory to Flutter assets. ## Classification grammar @@ -49,7 +49,7 @@ Use [assets/manifest.example.json](assets/manifest.example.json) and [assets/fol ## Invariants - Existing codepoints are API: never renumber or reuse one unless the user explicitly approves a breaking migration. -- `design/file-category-icons/` is the sole editable source of truth. Flutter keeps only the generated runtime TTF and Dart constants. +- `design/file-category-icons/` is the sole editable source of truth. The packaged Flutter runtime uses only the generated TTF and Dart constants. - Never add the design-material directory to `pubspec.yaml`; it must remain outside the packaged Flutter asset graph. - The manifest is the source of truth. Do not hand-edit generated Dart constants or patch the binary font. - Keep icon names lowercase `snake_case`; the generator emits lowerCamelCase Dart fields. diff --git a/.agent/skills/gopeed-file-category-icons/agents/openai.yaml b/ui/flutter/.agent/skills/gopeed-file-category-icons/agents/openai.yaml similarity index 100% rename from .agent/skills/gopeed-file-category-icons/agents/openai.yaml rename to ui/flutter/.agent/skills/gopeed-file-category-icons/agents/openai.yaml diff --git a/.agent/skills/gopeed-file-category-icons/assets/folder-symbol-template.svg b/ui/flutter/.agent/skills/gopeed-file-category-icons/assets/folder-symbol-template.svg similarity index 100% rename from .agent/skills/gopeed-file-category-icons/assets/folder-symbol-template.svg rename to ui/flutter/.agent/skills/gopeed-file-category-icons/assets/folder-symbol-template.svg diff --git a/.agent/skills/gopeed-file-category-icons/assets/manifest.example.json b/ui/flutter/.agent/skills/gopeed-file-category-icons/assets/manifest.example.json similarity index 100% rename from .agent/skills/gopeed-file-category-icons/assets/manifest.example.json rename to ui/flutter/.agent/skills/gopeed-file-category-icons/assets/manifest.example.json diff --git a/.agent/skills/gopeed-file-category-icons/references/style-guide.md b/ui/flutter/.agent/skills/gopeed-file-category-icons/references/style-guide.md similarity index 100% rename from .agent/skills/gopeed-file-category-icons/references/style-guide.md rename to ui/flutter/.agent/skills/gopeed-file-category-icons/references/style-guide.md diff --git a/.agent/skills/gopeed-file-category-icons/scripts/build_icon_font.py b/ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/build_icon_font.py similarity index 100% rename from .agent/skills/gopeed-file-category-icons/scripts/build_icon_font.py rename to ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/build_icon_font.py diff --git a/.agent/skills/gopeed-file-category-icons/scripts/preview_icon_font.py b/ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/preview_icon_font.py similarity index 100% rename from .agent/skills/gopeed-file-category-icons/scripts/preview_icon_font.py rename to ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/preview_icon_font.py diff --git a/.agent/skills/gopeed-file-category-icons/scripts/requirements.txt b/ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/requirements.txt similarity index 100% rename from .agent/skills/gopeed-file-category-icons/scripts/requirements.txt rename to ui/flutter/.agent/skills/gopeed-file-category-icons/scripts/requirements.txt diff --git a/design/file-category-icons/README.md b/ui/flutter/design/file-category-icons/README.md similarity index 75% rename from design/file-category-icons/README.md rename to ui/flutter/design/file-category-icons/README.md index 821a8513..3f806947 100644 --- a/design/file-category-icons/README.md +++ b/ui/flutter/design/file-category-icons/README.md @@ -1,6 +1,6 @@ # 文件分类图标源物料 -此目录是 Gopeed 文件分类图标的唯一源物料目录,位于 Flutter 工程之外,不得加入 `ui/flutter/pubspec.yaml` 的 `assets` 配置。 +此目录是 Gopeed 文件分类图标的唯一源物料目录。它位于 Flutter 工程内,但只用于设计和生成流程,不得加入 `pubspec.yaml` 的 `assets` 配置。 ## 维护限制 @@ -17,8 +17,8 @@ ## 生成产物 -- Flutter 字体:`ui/flutter/assets/fonts/GopeedIcons.ttf` -- Flutter 常量:`ui/flutter/lib/core/icons/gopeed_icons.dart` +- Flutter 字体:`assets/fonts/GopeedIcons.ttf` +- Flutter 常量:`lib/core/icons/gopeed_icons.dart` 所有修改都应遵循: diff --git a/design/file-category-icons/manifest.json b/ui/flutter/design/file-category-icons/manifest.json similarity index 95% rename from design/file-category-icons/manifest.json rename to ui/flutter/design/file-category-icons/manifest.json index 50c95c9f..60457fd8 100644 --- a/design/file-category-icons/manifest.json +++ b/ui/flutter/design/file-category-icons/manifest.json @@ -1,7 +1,7 @@ { "fontFamily": "GopeedIcons", - "fontOutput": "../../ui/flutter/assets/fonts/GopeedIcons.ttf", - "dartOutput": "../../ui/flutter/lib/core/icons/gopeed_icons.dart", + "fontOutput": "../../assets/fonts/GopeedIcons.ttf", + "dartOutput": "../../lib/core/icons/gopeed_icons.dart", "previewOutput": "preview/gopeed_icons_preview.png", "dartClass": "GopeedIcons", "codepointStart": "0xE900", diff --git a/design/file-category-icons/preview/gopeed_icons_preview.png b/ui/flutter/design/file-category-icons/preview/gopeed_icons_preview.png similarity index 100% rename from design/file-category-icons/preview/gopeed_icons_preview.png rename to ui/flutter/design/file-category-icons/preview/gopeed_icons_preview.png diff --git a/design/file-category-icons/svg/file-android.svg b/ui/flutter/design/file-category-icons/svg/file-android.svg similarity index 100% rename from design/file-category-icons/svg/file-android.svg rename to ui/flutter/design/file-category-icons/svg/file-android.svg diff --git a/design/file-category-icons/svg/file-archive.svg b/ui/flutter/design/file-category-icons/svg/file-archive.svg similarity index 100% rename from design/file-category-icons/svg/file-archive.svg rename to ui/flutter/design/file-category-icons/svg/file-archive.svg diff --git a/design/file-category-icons/svg/file-audio.svg b/ui/flutter/design/file-category-icons/svg/file-audio.svg similarity index 100% rename from design/file-category-icons/svg/file-audio.svg rename to ui/flutter/design/file-category-icons/svg/file-audio.svg diff --git a/design/file-category-icons/svg/file-code.svg b/ui/flutter/design/file-category-icons/svg/file-code.svg similarity index 100% rename from design/file-category-icons/svg/file-code.svg rename to ui/flutter/design/file-category-icons/svg/file-code.svg diff --git a/design/file-category-icons/svg/file-database.svg b/ui/flutter/design/file-category-icons/svg/file-database.svg similarity index 100% rename from design/file-category-icons/svg/file-database.svg rename to ui/flutter/design/file-category-icons/svg/file-database.svg diff --git a/design/file-category-icons/svg/file-disk-image.svg b/ui/flutter/design/file-category-icons/svg/file-disk-image.svg similarity index 100% rename from design/file-category-icons/svg/file-disk-image.svg rename to ui/flutter/design/file-category-icons/svg/file-disk-image.svg diff --git a/design/file-category-icons/svg/file-document.svg b/ui/flutter/design/file-category-icons/svg/file-document.svg similarity index 100% rename from design/file-category-icons/svg/file-document.svg rename to ui/flutter/design/file-category-icons/svg/file-document.svg diff --git a/design/file-category-icons/svg/file-ebook.svg b/ui/flutter/design/file-category-icons/svg/file-ebook.svg similarity index 100% rename from design/file-category-icons/svg/file-ebook.svg rename to ui/flutter/design/file-category-icons/svg/file-ebook.svg diff --git a/design/file-category-icons/svg/file-font.svg b/ui/flutter/design/file-category-icons/svg/file-font.svg similarity index 100% rename from design/file-category-icons/svg/file-font.svg rename to ui/flutter/design/file-category-icons/svg/file-font.svg diff --git a/design/file-category-icons/svg/file-image.svg b/ui/flutter/design/file-category-icons/svg/file-image.svg similarity index 100% rename from design/file-category-icons/svg/file-image.svg rename to ui/flutter/design/file-category-icons/svg/file-image.svg diff --git a/design/file-category-icons/svg/file-installer.svg b/ui/flutter/design/file-category-icons/svg/file-installer.svg similarity index 100% rename from design/file-category-icons/svg/file-installer.svg rename to ui/flutter/design/file-category-icons/svg/file-installer.svg diff --git a/design/file-category-icons/svg/file-ios.svg b/ui/flutter/design/file-category-icons/svg/file-ios.svg similarity index 100% rename from design/file-category-icons/svg/file-ios.svg rename to ui/flutter/design/file-category-icons/svg/file-ios.svg diff --git a/design/file-category-icons/svg/file-pdf.svg b/ui/flutter/design/file-category-icons/svg/file-pdf.svg similarity index 100% rename from design/file-category-icons/svg/file-pdf.svg rename to ui/flutter/design/file-category-icons/svg/file-pdf.svg diff --git a/design/file-category-icons/svg/file-presentation.svg b/ui/flutter/design/file-category-icons/svg/file-presentation.svg similarity index 100% rename from design/file-category-icons/svg/file-presentation.svg rename to ui/flutter/design/file-category-icons/svg/file-presentation.svg diff --git a/design/file-category-icons/svg/file-spreadsheet.svg b/ui/flutter/design/file-category-icons/svg/file-spreadsheet.svg similarity index 100% rename from design/file-category-icons/svg/file-spreadsheet.svg rename to ui/flutter/design/file-category-icons/svg/file-spreadsheet.svg diff --git a/design/file-category-icons/svg/file-text.svg b/ui/flutter/design/file-category-icons/svg/file-text.svg similarity index 100% rename from design/file-category-icons/svg/file-text.svg rename to ui/flutter/design/file-category-icons/svg/file-text.svg diff --git a/design/file-category-icons/svg/file-video.svg b/ui/flutter/design/file-category-icons/svg/file-video.svg similarity index 100% rename from design/file-category-icons/svg/file-video.svg rename to ui/flutter/design/file-category-icons/svg/file-video.svg diff --git a/design/file-category-icons/svg/file-web.svg b/ui/flutter/design/file-category-icons/svg/file-web.svg similarity index 100% rename from design/file-category-icons/svg/file-web.svg rename to ui/flutter/design/file-category-icons/svg/file-web.svg diff --git a/design/file-category-icons/svg/file.svg b/ui/flutter/design/file-category-icons/svg/file.svg similarity index 100% rename from design/file-category-icons/svg/file.svg rename to ui/flutter/design/file-category-icons/svg/file.svg diff --git a/design/file-category-icons/svg/folder-bt.svg b/ui/flutter/design/file-category-icons/svg/folder-bt.svg similarity index 100% rename from design/file-category-icons/svg/folder-bt.svg rename to ui/flutter/design/file-category-icons/svg/folder-bt.svg diff --git a/design/file-category-icons/svg/folder.svg b/ui/flutter/design/file-category-icons/svg/folder.svg similarity index 100% rename from design/file-category-icons/svg/folder.svg rename to ui/flutter/design/file-category-icons/svg/folder.svg diff --git a/design/file-category-icons/svg/protocol-bt.svg b/ui/flutter/design/file-category-icons/svg/protocol-bt.svg similarity index 100% rename from design/file-category-icons/svg/protocol-bt.svg rename to ui/flutter/design/file-category-icons/svg/protocol-bt.svg diff --git a/design/file-category-icons/svg/protocol-ed2k.svg b/ui/flutter/design/file-category-icons/svg/protocol-ed2k.svg similarity index 100% rename from design/file-category-icons/svg/protocol-ed2k.svg rename to ui/flutter/design/file-category-icons/svg/protocol-ed2k.svg diff --git a/ui/flutter/lib/features/tasks/presentation/pages/create_task_window_page.dart b/ui/flutter/lib/features/tasks/presentation/pages/create_task_window_page.dart index 05d37d9b..55fb3ad1 100644 --- a/ui/flutter/lib/features/tasks/presentation/pages/create_task_window_page.dart +++ b/ui/flutter/lib/features/tasks/presentation/pages/create_task_window_page.dart @@ -29,7 +29,6 @@ import '../../../../shared/theme/app_design_tokens.dart'; import '../../../../shared/theme/app_palette.dart'; import '../../../../shared/widgets/app_choice_segmented_control.dart'; import '../../../../shared/widgets/app_http_headers_editor.dart'; -import '../../../../shared/widgets/app_loading_button.dart'; import '../../../../shared/widgets/app_number_input.dart'; import '../../../../shared/widgets/app_path_picker_field.dart'; import '../../../../shared/widgets/app_text_field.dart'; @@ -38,8 +37,8 @@ import '../../../../shared/widgets/app_toast.dart'; import '../../../../l10n/l10n.dart'; import '../../../../util/util.dart'; import '../../application/pending_create_task.dart'; +import '../widgets/create_task_action_buttons.dart'; import '../widgets/resolve_file_tree.dart'; -import '../widgets/resolve_task_actions.dart'; class CreateTaskWindowPage extends ConsumerStatefulWidget { const CreateTaskWindowPage({super.key, this.windowController, this.initialTask}); @@ -696,17 +695,14 @@ class _CreateTaskWindowPageState extends ConsumerState { child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ - SecondaryButton( - onPressed: _creating ? null : _closeWindow, - child: SizedBox(width: 68, child: Center(child: Text(context.l10n.cancel))), - ), - const SizedBox(width: 12), - AppLoadingButton( - key: const ValueKey('create-task-confirm-button'), - onPressed: _confirm, - loading: _creating, - variant: AppLoadingButtonVariant.primary, - child: SizedBox(width: 68, child: Center(child: Text(context.l10n.confirm))), + CreateTaskActionButtons( + submitting: _creating, + onCancel: _closeWindow, + onSubmit: _confirm, + cancelLabel: context.l10n.cancel, + submitLabel: context.l10n.confirm, + cancelButtonKey: const ValueKey('create-task-cancel-button'), + submitButtonKey: const ValueKey('create-task-confirm-button'), ), ], ), @@ -1159,10 +1155,14 @@ class _CreateTaskWindowPageState extends ConsumerState { ), actions: [ flutter.Flexible( - child: ResolveTaskActions( + child: CreateTaskActionButtons( submitting: submitting, onCancel: () => closeOverlay(dialogContext, false), - onCreate: submit, + onSubmit: submit, + cancelLabel: dialogContext.l10n.cancel, + submitLabel: dialogContext.l10n.createAction, + cancelButtonKey: const ValueKey('resolve-cancel-button'), + submitButtonKey: const ValueKey('resolve-create-button'), ), ), ], @@ -1205,7 +1205,7 @@ class _CreateTaskWindowPageState extends ConsumerState { width: contentWidth, child: Row( children: [ - Expanded(child: Text(dialogContext.l10n.createHistory)), + Expanded(child: Text(dialogContext.l10n.historyLinks)), AppTooltip( message: dialogContext.l10n.clearHistory, child: GhostButton( diff --git a/ui/flutter/lib/features/tasks/presentation/widgets/create_task_action_buttons.dart b/ui/flutter/lib/features/tasks/presentation/widgets/create_task_action_buttons.dart new file mode 100644 index 00000000..6de2ce88 --- /dev/null +++ b/ui/flutter/lib/features/tasks/presentation/widgets/create_task_action_buttons.dart @@ -0,0 +1,56 @@ +import 'package:flutter/widgets.dart' as flutter; +import 'package:shadcn_flutter/shadcn_flutter.dart'; + +import '../../../../shared/theme/app_design_tokens.dart'; +import '../../../../shared/widgets/app_loading_button.dart'; + +class CreateTaskActionButtons extends StatelessWidget { + const CreateTaskActionButtons({ + super.key, + required this.submitting, + required this.onCancel, + required this.onSubmit, + required this.cancelLabel, + required this.submitLabel, + this.cancelButtonKey, + this.submitButtonKey, + }); + + final bool submitting; + final VoidCallback onCancel; + final VoidCallback onSubmit; + final String cancelLabel; + final String submitLabel; + final Key? cancelButtonKey; + final Key? submitButtonKey; + + @override + Widget build(BuildContext context) { + return flutter.Row( + mainAxisSize: MainAxisSize.min, + children: [ + SecondaryButton( + key: cancelButtonKey, + onPressed: submitting ? null : onCancel, + child: SizedBox( + width: _actionContentWidth, + child: Center(child: Text(cancelLabel)), + ), + ), + const SizedBox(width: AppDesignTokens.space12), + AppLoadingButton( + key: submitButtonKey, + onPressed: onSubmit, + loading: submitting, + variant: AppLoadingButtonVariant.primary, + child: SizedBox( + width: _actionContentWidth, + child: Center(child: Text(submitLabel)), + ), + ), + ], + ); + } +} + +const _actionContentWidth = 68.0; diff --git a/ui/flutter/lib/features/tasks/presentation/widgets/resolve_file_tree.dart b/ui/flutter/lib/features/tasks/presentation/widgets/resolve_file_tree.dart index d5f8af8e..14eeccf7 100644 --- a/ui/flutter/lib/features/tasks/presentation/widgets/resolve_file_tree.dart +++ b/ui/flutter/lib/features/tasks/presentation/widgets/resolve_file_tree.dart @@ -189,42 +189,48 @@ class _TreeFooter extends StatelessWidget { @override Widget build(BuildContext context) { final palette = AppPalette.of(context); - final filters = _FileTypeFilterGroup( - videoActive: activeTypeFilters.contains(_FileTypeFilter.video), - audioActive: activeTypeFilters.contains(_FileTypeFilter.audio), - imageActive: activeTypeFilters.contains(_FileTypeFilter.image), - onSelectVideo: onSelectVideo, - onSelectAudio: onSelectAudio, - onSelectImage: onSelectImage, + final filters = KeyedSubtree( + key: const ValueKey('resolve-tree-filters'), + child: _FileTypeFilterGroup( + videoActive: activeTypeFilters.contains(_FileTypeFilter.video), + audioActive: activeTypeFilters.contains(_FileTypeFilter.audio), + imageActive: activeTypeFilters.contains(_FileTypeFilter.image), + onSelectVideo: onSelectVideo, + onSelectAudio: onSelectAudio, + onSelectImage: onSelectImage, + ), ); - final stats = Row( - mainAxisSize: MainAxisSize.min, + final statsStyle = TextStyle(color: palette.textSecondary, fontSize: 12, fontWeight: FontWeight.w600); + final selectedLabel = context.l10n.selectedCount(selectedCount, totalCount); + final sizeLabel = unknownSize ? context.l10n.unknownSize : Util.fmtByte(selectedSize); + final baseline = MediaQuery.textScalerOf(context).scale(statsStyle.fontSize!); + final stats = Wrap( + key: const ValueKey('resolve-tree-selection-stats'), + alignment: WrapAlignment.end, + spacing: AppDesignTokens.space8, + runSpacing: AppDesignTokens.space4, + crossAxisAlignment: WrapCrossAlignment.start, children: [ - Text( - context.l10n.selectedCount(selectedCount, totalCount), - style: TextStyle(color: palette.textSecondary, fontSize: 12, fontWeight: FontWeight.w600), + Baseline( + baseline: baseline, + baselineType: TextBaseline.alphabetic, + child: Text(key: const ValueKey('resolve-tree-selected-count'), selectedLabel, style: statsStyle), ), - const SizedBox(width: 8), - Text( - unknownSize ? context.l10n.unknownSize : Util.fmtByte(selectedSize), - style: TextStyle(color: palette.textSecondary, fontSize: 12, fontWeight: FontWeight.w600), + Baseline( + baseline: baseline, + baselineType: TextBaseline.alphabetic, + child: Text(key: const ValueKey('resolve-tree-selected-size'), sizeLabel, style: statsStyle), ), ], ); - return LayoutBuilder( - builder: (context, constraints) { - if (constraints.maxWidth < 360) { - return Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Align(alignment: Alignment.centerLeft, child: filters), - const SizedBox(height: 4), - Align(alignment: Alignment.centerRight, child: stats), - ], - ); - } - return Row(crossAxisAlignment: CrossAxisAlignment.center, children: [filters, const Spacer(), stats]); - }, + + return OverflowBar( + key: const ValueKey('resolve-tree-footer'), + spacing: AppDesignTokens.space8, + alignment: MainAxisAlignment.spaceBetween, + overflowAlignment: OverflowBarAlignment.end, + overflowSpacing: AppDesignTokens.space4, + children: [filters, stats], ); } } @@ -290,7 +296,11 @@ class _FilterDivider extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox(width: 1, height: 18, child: ColoredBox(color: color)); + return SizedBox( + width: _fileTypeFilterDividerWidth, + height: 18, + child: ColoredBox(color: color), + ); } } @@ -305,7 +315,7 @@ class _FilterButton extends StatelessWidget { Widget build(BuildContext context) { final palette = AppPalette.of(context); return SizedBox( - width: 40, + width: _fileTypeFilterButtonWidth, height: 28, child: DecoratedBox( decoration: BoxDecoration(color: active ? palette.filterActiveBg : null), @@ -340,3 +350,5 @@ const _audioExts = {'mp3', 'flac', 'wav', 'aac', 'm4a', 'ogg', 'ape'}; const _imageExts = {'jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'}; const _resolveTreeRowHeight = 36.0; const _resolveFileNameStyle = TextStyle(fontSize: 14, height: 1.25); +const _fileTypeFilterButtonWidth = 40.0; +const _fileTypeFilterDividerWidth = 1.0; diff --git a/ui/flutter/lib/features/tasks/presentation/widgets/resolve_task_actions.dart b/ui/flutter/lib/features/tasks/presentation/widgets/resolve_task_actions.dart deleted file mode 100644 index e9b6dc37..00000000 --- a/ui/flutter/lib/features/tasks/presentation/widgets/resolve_task_actions.dart +++ /dev/null @@ -1,50 +0,0 @@ -import 'package:shadcn_flutter/shadcn_flutter.dart'; -import 'package:flutter/widgets.dart' as flutter; - -import '../../../../l10n/l10n.dart'; -import '../../../../shared/theme/app_design_tokens.dart'; -import '../../../../shared/widgets/app_loading_button.dart'; - -class ResolveTaskActions extends StatelessWidget { - const ResolveTaskActions({super.key, required this.submitting, required this.onCancel, required this.onCreate}); - - final bool submitting; - final VoidCallback onCancel; - final VoidCallback onCreate; - - @override - Widget build(BuildContext context) { - // Size both actions from the wider translated label, including while the - // create button replaces its label with a loader. - return IntrinsicWidth( - child: flutter.Row( - mainAxisSize: MainAxisSize.min, - children: [ - flutter.Expanded( - child: ConstrainedBox( - constraints: const BoxConstraints(minWidth: AppDesignTokens.dialogActionMinWidth), - child: SecondaryButton( - key: const ValueKey('resolve-cancel-button'), - onPressed: submitting ? null : onCancel, - child: Text(context.l10n.cancel, maxLines: 1, softWrap: false), - ), - ), - ), - const SizedBox(width: AppDesignTokens.space8), - flutter.Expanded( - child: ConstrainedBox( - constraints: const BoxConstraints(minWidth: AppDesignTokens.dialogActionMinWidth), - child: AppLoadingButton( - key: const ValueKey('resolve-create-button'), - onPressed: onCreate, - loading: submitting, - variant: AppLoadingButtonVariant.primary, - child: Text(context.l10n.createAction, maxLines: 1, softWrap: false), - ), - ), - ), - ], - ), - ); - } -} diff --git a/ui/flutter/lib/l10n/app_ar.arb b/ui/flutter/lib/l10n/app_ar.arb index 830d6eb1..3b7c407d 100644 --- a/ui/flutter/lib/l10n/app_ar.arb +++ b/ui/flutter/lib/l10n/app_ar.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count}", "resourceHasNoFiles": "ولا توجد في هذا المورد قائمة ملف قابلة للاختيار", "noFiles": "لا توجد ملفات", - "createHistory": "تاريخ الخلق", + "historyLinks": "روابط السجل", "noHistory": "لا تاريخ", "directDownloadDescription": "حل المشاكل وخلق المهام على الفور", "taskProgress": "التقدم المحرز", diff --git a/ui/flutter/lib/l10n/app_az.arb b/ui/flutter/lib/l10n/app_az.arb index d9d5bcfd..4fccb3de 100644 --- a/ui/flutter/lib/l10n/app_az.arb +++ b/ui/flutter/lib/l10n/app_az.arb @@ -367,7 +367,7 @@ "resolvedFileCount": "{count} fayl tapıldı", "resourceHasNoFiles": "Bu resursda seçilə bilən fayl siyahısı yoxdur", "noFiles": "Fayl yoxdur", - "createHistory": "Tarixçə Yarat", + "historyLinks": "Tarixçə keçidləri", "noHistory": "Tarixçə yoxdur", "directDownloadDescription": "Təhlili keç və tapşırıqları dərhal yarat", "taskProgress": "Tapşırığın irəliləyişi", diff --git a/ui/flutter/lib/l10n/app_ca.arb b/ui/flutter/lib/l10n/app_ca.arb index 1daea2e6..bb030cf5 100644 --- a/ui/flutter/lib/l10n/app_ca.arb +++ b/ui/flutter/lib/l10n/app_ca.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count}", "resourceHasNoFiles": "El recurs no té cap llista de fitxers per seleccionar", "noFiles": "Sense fitxers", - "createHistory": "Crea un historial", + "historyLinks": "Enllaços de l'historial", "noHistory": "Sense historial", "directDownloadDescription": "Omet la resolució i crea les tasques immediatament", "taskProgress": "Progrés de la tasca", diff --git a/ui/flutter/lib/l10n/app_de.arb b/ui/flutter/lib/l10n/app_de.arb index fdd924ff..64d7683c 100644 --- a/ui/flutter/lib/l10n/app_de.arb +++ b/ui/flutter/lib/l10n/app_de.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count}-Dateien behoben", "resourceHasNoFiles": "Die Ressource verfügt über keine auswählbare Dateiliste", "noFiles": "Keine Dateien", - "createHistory": "Verlauf erstellen", + "historyLinks": "Verlaufslinks", "noHistory": "Keine Historie", "directDownloadDescription": "Überspringen Sie die Lösung und erstellen Sie sofort Aufgaben", "taskProgress": "Aufgabenfortschritt", diff --git a/ui/flutter/lib/l10n/app_en.arb b/ui/flutter/lib/l10n/app_en.arb index 1a0fcfa2..5aab7de5 100644 --- a/ui/flutter/lib/l10n/app_en.arb +++ b/ui/flutter/lib/l10n/app_en.arb @@ -367,7 +367,7 @@ "resolvedFileCount": "{count, plural, =1{1 file resolved} other{{count} files resolved}}", "resourceHasNoFiles": "The resource has no selectable file list", "noFiles": "No files", - "createHistory": "Create History", + "historyLinks": "History Links", "noHistory": "No history", "directDownloadDescription": "Skip resolving and create tasks immediately", "taskProgress": "Task progress", diff --git a/ui/flutter/lib/l10n/app_es.arb b/ui/flutter/lib/l10n/app_es.arb index c93209d1..89ed2ad0 100644 --- a/ui/flutter/lib/l10n/app_es.arb +++ b/ui/flutter/lib/l10n/app_es.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} resueltos", "resourceHasNoFiles": "El recurso no tiene lista de archivos seleccionable", "noFiles": "No hay archivos", - "createHistory": "Crear historia", + "historyLinks": "Enlaces del historial", "noHistory": "No hay historia", "directDownloadDescription": "Saltar a resolver y crear tareas inmediatamente", "taskProgress": "Progresos en la tarea", diff --git a/ui/flutter/lib/l10n/app_fa.arb b/ui/flutter/lib/l10n/app_fa.arb index 3943a61d..6486d00d 100644 --- a/ui/flutter/lib/l10n/app_fa.arb +++ b/ui/flutter/lib/l10n/app_fa.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} فایل شناسایی شد", "resourceHasNoFiles": "منبع هیچ لیست فایل قابل انتخاب ندارد", "noFiles": "فایل ها", - "createHistory": "ایجاد تاریخ", + "historyLinks": "پیوندهای تاریخچه", "noHistory": "تاریخ", "directDownloadDescription": "پیاده سازی و ایجاد وظایف بلافاصله", "taskProgress": "پیشرفت شغلی", diff --git a/ui/flutter/lib/l10n/app_fr.arb b/ui/flutter/lib/l10n/app_fr.arb index 4af83656..cd764419 100644 --- a/ui/flutter/lib/l10n/app_fr.arb +++ b/ui/flutter/lib/l10n/app_fr.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} résolus", "resourceHasNoFiles": "La ressource n'a pas de liste de fichiers sélectionnable", "noFiles": "Aucun fichier", - "createHistory": "Créer l'historique", + "historyLinks": "Liens de l'historique", "noHistory": "Pas d'histoire", "directDownloadDescription": "Sautez la résolution et créez immédiatement des tâches", "taskProgress": "Progrès accomplis dans la tâche", diff --git a/ui/flutter/lib/l10n/app_hu.arb b/ui/flutter/lib/l10n/app_hu.arb index 9c2fd4c8..034eff81 100644 --- a/ui/flutter/lib/l10n/app_hu.arb +++ b/ui/flutter/lib/l10n/app_hu.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} fájlok megoldva", "resourceHasNoFiles": "Az erőforrásnak nincs kiválasztható fájllistája", "noFiles": "Nincsenek fájlok", - "createHistory": "Készítsen előzményeket", + "historyLinks": "Előzményhivatkozások", "noHistory": "Nincs előzmény", "directDownloadDescription": "Hagyja ki a megoldást, és azonnal hozzon létre feladatokat", "taskProgress": "A feladat előrehaladása", diff --git a/ui/flutter/lib/l10n/app_id.arb b/ui/flutter/lib/l10n/app_id.arb index 857ec774..40acc714 100644 --- a/ui/flutter/lib/l10n/app_id.arb +++ b/ui/flutter/lib/l10n/app_id.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} terselesaikan", "resourceHasNoFiles": "Sumber daya tidak memiliki daftar berkas yang dapat dipilih", "noFiles": "Tidak ada berkas", - "createHistory": "Buat Riwayat", + "historyLinks": "Tautan Riwayat", "noHistory": "Tidak ada sejarah", "directDownloadDescription": "Lewatkan pemecahan dan buat tugas segera", "taskProgress": "Kemajuan tugas", diff --git a/ui/flutter/lib/l10n/app_it.arb b/ui/flutter/lib/l10n/app_it.arb index 01263794..aba9620f 100644 --- a/ui/flutter/lib/l10n/app_it.arb +++ b/ui/flutter/lib/l10n/app_it.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} risolti", "resourceHasNoFiles": "La risorsa non ha un elenco di file selezionabile", "noFiles": "Nessun file", - "createHistory": "Creare la storia", + "historyLinks": "Link della cronologia", "noHistory": "Nessuna storia", "directDownloadDescription": "Salta la risoluzione e crea immediatamente i compiti", "taskProgress": "Prospettive di attività", diff --git a/ui/flutter/lib/l10n/app_ja.arb b/ui/flutter/lib/l10n/app_ja.arb index d816b478..8f15b2d1 100644 --- a/ui/flutter/lib/l10n/app_ja.arb +++ b/ui/flutter/lib/l10n/app_ja.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} ファイルが解決しました", "resourceHasNoFiles": "リソースには、選択可能なファイルリストはありません", "noFiles": "ファイルがない場合", - "createHistory": "歴史を創造する", + "historyLinks": "履歴リンク", "noHistory": "歴史なし", "directDownloadDescription": "解決をスキップし、すぐにタスクを作成", "taskProgress": "タスクの進捗", diff --git a/ui/flutter/lib/l10n/app_ko.arb b/ui/flutter/lib/l10n/app_ko.arb index a4d3f9da..23c42a68 100644 --- a/ui/flutter/lib/l10n/app_ko.arb +++ b/ui/flutter/lib/l10n/app_ko.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} 파일 해결", "resourceHasNoFiles": "리소스에는 선택 가능한 파일 목록이 없습니다", "noFiles": "파일 없음", - "createHistory": "회사연혁", + "historyLinks": "기록 링크", "noHistory": "의 역사", "directDownloadDescription": "재해를 드리며 즉시 업무를 만듭니다", "taskProgress": "작업 진행", diff --git a/ui/flutter/lib/l10n/app_pl.arb b/ui/flutter/lib/l10n/app_pl.arb index 6aea60ce..85b61144 100644 --- a/ui/flutter/lib/l10n/app_pl.arb +++ b/ui/flutter/lib/l10n/app_pl.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} plików rozwiązanych", "resourceHasNoFiles": "Zasób nie ma listy plików do wyboru", "noFiles": "Brak plików", - "createHistory": "Utwórz historię", + "historyLinks": "Linki historii", "noHistory": "Brak historii", "directDownloadDescription": "Pomiń rozwiązywanie i tworzyć zadania natychmiast", "taskProgress": "Postępy w realizacji zadań", diff --git a/ui/flutter/lib/l10n/app_pt.arb b/ui/flutter/lib/l10n/app_pt.arb index 39b3a81a..46674d2c 100644 --- a/ui/flutter/lib/l10n/app_pt.arb +++ b/ui/flutter/lib/l10n/app_pt.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} ficheiros resolvidos", "resourceHasNoFiles": "O recurso não tem lista de arquivos selecionáveis", "noFiles": "Sem arquivos", - "createHistory": "Criar Histórico", + "historyLinks": "Links do histórico", "noHistory": "Sem histórico", "directDownloadDescription": "Ignorar resolver e criar tarefas imediatamente", "taskProgress": "Progresso da tarefa", diff --git a/ui/flutter/lib/l10n/app_ru.arb b/ui/flutter/lib/l10n/app_ru.arb index 782a5c9e..53f5f2f6 100644 --- a/ui/flutter/lib/l10n/app_ru.arb +++ b/ui/flutter/lib/l10n/app_ru.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} файлов, разрешенных", "resourceHasNoFiles": "Ресурс не имеет выбираемого списка файлов", "noFiles": "Нет файлов", - "createHistory": "Создаем историю", + "historyLinks": "Ссылки в истории", "noHistory": "Нет истории", "directDownloadDescription": "Немедленно пропустите решение и создайте задачи", "taskProgress": "Прогресс в решении задач", diff --git a/ui/flutter/lib/l10n/app_ta.arb b/ui/flutter/lib/l10n/app_ta.arb index 00d06c0c..bccefc16 100644 --- a/ui/flutter/lib/l10n/app_ta.arb +++ b/ui/flutter/lib/l10n/app_ta.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} கோப்புகள் தீர்க்கப்பட்டன", "resourceHasNoFiles": "வளத்தில் தேர்ந்தெடுக்கக்கூடிய கோப்பு பட்டியல் இல்லை", "noFiles": "(% 1 மொத்தம்) Items (Folders, Files), Size", - "createHistory": "வரலாற்றை உருவாக்கு", + "historyLinks": "வரலாற்று இணைப்புகள்", "noHistory": "& வரலாற்றில் கண்டுபிடி... Unlimited (number of lines)", "directDownloadDescription": "தீர்க்கப்படுவதைத் தவிர்த்து, உடனடியாகப் பணிகளை உருவாக்கவும்", "taskProgress": "பணி முன்னேற்றம்", diff --git a/ui/flutter/lib/l10n/app_tr.arb b/ui/flutter/lib/l10n/app_tr.arb index a753215d..9b3b4954 100644 --- a/ui/flutter/lib/l10n/app_tr.arb +++ b/ui/flutter/lib/l10n/app_tr.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} dosyaları çözüldü", "resourceHasNoFiles": "Kaynak, uygun olmayan dosya listesine sahiptir", "noFiles": "Hiçbir dosya yok", - "createHistory": "Create History Create History", + "historyLinks": "Geçmiş Bağlantıları", "noHistory": "Geçmiş yok", "directDownloadDescription": "Hemen çözümle ve görevleri oluşturun", "taskProgress": "Görev ilerlemesi", diff --git a/ui/flutter/lib/l10n/app_uk.arb b/ui/flutter/lib/l10n/app_uk.arb index 82c6cc34..e4b3e923 100644 --- a/ui/flutter/lib/l10n/app_uk.arb +++ b/ui/flutter/lib/l10n/app_uk.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} файли вирішені", "resourceHasNoFiles": "Ресурс не має вибраного списку файлів", "noFiles": "Немає файлів", - "createHistory": "Історія", + "historyLinks": "Посилання історії", "noHistory": "Історія", "directDownloadDescription": "Вирішити і створювати завдання відразу", "taskProgress": "Завдання", diff --git a/ui/flutter/lib/l10n/app_vi.arb b/ui/flutter/lib/l10n/app_vi.arb index ccd43cb7..f7a6db52 100644 --- a/ui/flutter/lib/l10n/app_vi.arb +++ b/ui/flutter/lib/l10n/app_vi.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "{count} tập tin được giải quyết", "resourceHasNoFiles": "Không thể chọn danh sách tập tin", "noFiles": "Không có tập tin", - "createHistory": "Tạo ra lịch sử", + "historyLinks": "Liên kết lịch sử", "noHistory": "Không có lịch sử", "directDownloadDescription": "Bỏ qua việc giải quyết và tạo tác vụ ngay lập tức", "taskProgress": "Tiến trình tác vụ", diff --git a/ui/flutter/lib/l10n/app_zh.arb b/ui/flutter/lib/l10n/app_zh.arb index ddc6e16d..0bd33ecd 100644 --- a/ui/flutter/lib/l10n/app_zh.arb +++ b/ui/flutter/lib/l10n/app_zh.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "已解析 {count} 个文件", "resourceHasNoFiles": "该资源没有可选择的文件列表", "noFiles": "没有文件", - "createHistory": "创建历史", + "historyLinks": "历史链接", "noHistory": "暂无历史记录", "directDownloadDescription": "跳过解析并立即创建任务", "taskProgress": "任务进度", diff --git a/ui/flutter/lib/l10n/app_zh_TW.arb b/ui/flutter/lib/l10n/app_zh_TW.arb index db5bb6a2..8220df52 100644 --- a/ui/flutter/lib/l10n/app_zh_TW.arb +++ b/ui/flutter/lib/l10n/app_zh_TW.arb @@ -353,7 +353,7 @@ "resolvedFileCount": "已解析 {count} 個檔案", "resourceHasNoFiles": "該資源沒有可選擇的檔案列表", "noFiles": "沒有檔案", - "createHistory": "建立歷史記錄", + "historyLinks": "歷史連結", "noHistory": "暫無歷史記錄", "directDownloadDescription": "跳過解析並立即建立任務", "taskProgress": "任務進度", diff --git a/ui/flutter/test/features/tasks/presentation/create_task_window_page_test.dart b/ui/flutter/test/features/tasks/presentation/create_task_window_page_test.dart index 8b05f599..2ca8e88f 100644 --- a/ui/flutter/test/features/tasks/presentation/create_task_window_page_test.dart +++ b/ui/flutter/test/features/tasks/presentation/create_task_window_page_test.dart @@ -499,11 +499,12 @@ void main() { expect(_fieldText(tester, 'create-task-rename-input'), isEmpty); final createButton = tester.widget(find.byKey(const ValueKey('resolve-create-button'))); - expect(createButton.child, isA()); - final createText = createButton.child as Text; - expect(createText.data, 'Create'); - expect(createText.maxLines, 1); - expect(createText.softWrap, isFalse); + expect(createButton.child, isA()); + expect((createButton.child as SizedBox).width, 68); + expect( + find.descendant(of: find.byKey(const ValueKey('resolve-create-button')), matching: find.text('Create')), + findsOneWidget, + ); expect( tester.getSize(find.byKey(const ValueKey('resolve-cancel-button'))), tester.getSize(find.byKey(const ValueKey('resolve-create-button'))), @@ -555,6 +556,7 @@ void main() { await tester.tap(find.byIcon(Icons.history)); await tester.pumpAndSettle(); + expect(find.text('History Links'), findsOneWidget); expect( tester.getSize(find.byKey(const ValueKey('create-history-title-bar'))).width, tester.getSize(find.byKey(const ValueKey('create-history-content'))).width, diff --git a/ui/flutter/test/features/tasks/presentation/resolve_task_actions_test.dart b/ui/flutter/test/features/tasks/presentation/resolve_task_actions_test.dart index 4a8d5940..8095bdf0 100644 --- a/ui/flutter/test/features/tasks/presentation/resolve_task_actions_test.dart +++ b/ui/flutter/test/features/tasks/presentation/resolve_task_actions_test.dart @@ -1,6 +1,6 @@ import 'package:flutter/widgets.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:gopeed/features/tasks/presentation/widgets/resolve_task_actions.dart'; +import 'package:gopeed/features/tasks/presentation/widgets/create_task_action_buttons.dart'; import 'package:gopeed/l10n/l10n.dart'; import 'package:gopeed/shared/theme/app_component_themes.dart'; import 'package:gopeed/shared/theme/app_theme.dart'; @@ -36,10 +36,14 @@ void main() { content: SizedBox(width: width < 760 ? width - 32 : 720, height: 100), actions: [ Flexible( - child: ResolveTaskActions( + child: CreateTaskActionButtons( submitting: submitting, onCancel: () => cancelled = true, - onCreate: () => setState(() => submitting = true), + onSubmit: () => setState(() => submitting = true), + cancelLabel: context.l10n.cancel, + submitLabel: context.l10n.createAction, + cancelButtonKey: const ValueKey('resolve-cancel-button'), + submitButtonKey: const ValueKey('resolve-create-button'), ), ), ], @@ -58,6 +62,9 @@ void main() { final createRect = tester.getRect(create); expect(cancelRect.size, createRect.size); expect(cancelRect.center.dy, createRect.center.dy); + expect(createRect.left - cancelRect.right, 12); + expect((tester.widget(cancel).child as SizedBox).width, 68); + expect((tester.widget(create).child as SizedBox).width, 68); expect(cancelRect.left, greaterThanOrEqualTo(0)); expect(createRect.right, lessThanOrEqualTo(width)); expect(find.text(appLocalizationsFor(language).createAction), findsOneWidget); diff --git a/ui/flutter/test/widget_test.dart b/ui/flutter/test/widget_test.dart index b90329cc..cf6098e4 100644 --- a/ui/flutter/test/widget_test.dart +++ b/ui/flutter/test/widget_test.dart @@ -2669,9 +2669,9 @@ void main() { padding: const EdgeInsets.all(24), child: ResolveFileTree( files: [ - FileInfo(path: 'videos', name: 'clip.mp4', size: 1024), - FileInfo(path: 'docs', name: 'notes.txt', size: 128), - FileInfo(path: 'docs', name: 'manual.pdf', size: 256), + FileInfo(path: 'videos', name: 'clip.mp4', size: 0), + FileInfo(path: 'docs', name: 'notes.txt', size: 0), + FileInfo(path: 'docs', name: 'manual.pdf', size: 0), ], initialSelection: const [0, 1, 2], onSelectionChanged: (values) => selected = values, @@ -2708,7 +2708,26 @@ void main() { expect(find.byKey(const ValueKey('resolve-tree-expand-toggle')), findsOneWidget); expect(find.byType(shad.OutlinedContainer), findsOneWidget); expect(find.text('clip.mp4'), findsOneWidget); + expect(find.text('Unknown size'), findsOneWidget); expect(tester.widget(find.text('clip.mp4')).style?.color, AppPalette.light.textPrimary); + final baselines = tester + .widgetList( + find.descendant( + of: find.byKey(const ValueKey('resolve-tree-selection-stats')), + matching: find.byType(Baseline), + ), + ) + .toList(); + expect(baselines, hasLength(2)); + expect(baselines.first.baseline, baselines.last.baseline); + expect(baselines.every((widget) => widget.baselineType == TextBaseline.alphabetic), isTrue); + final filtersRect = tester.getRect(find.byKey(const ValueKey('resolve-tree-filters'))); + final statsRect = tester.getRect(find.byKey(const ValueKey('resolve-tree-selection-stats'))); + final selectedCountRect = tester.getRect(find.byKey(const ValueKey('resolve-tree-selected-count'))); + final selectedSizeRect = tester.getRect(find.byKey(const ValueKey('resolve-tree-selected-size'))); + expect(statsRect.center.dy, closeTo(filtersRect.center.dy, 0.01)); + expect(selectedCountRect.center.dy, closeTo(selectedSizeRect.center.dy, 0.01)); + expect(selectedSizeRect.left, greaterThan(selectedCountRect.right)); await tester.tap(find.byKey(const ValueKey('resolve-tree-expand-toggle'))); await tester.pumpAndSettle(); @@ -2770,8 +2789,16 @@ void main() { expect(find.text('clip.mp4'), findsOneWidget); expect(selected, [0, 1, 2]); - tester.view.physicalSize = const Size(360, 620); + tester.view.physicalSize = const Size(280, 620); await tester.pumpAndSettle(); + expect( + tester.getRect(find.byKey(const ValueKey('resolve-tree-selection-stats'))).top, + greaterThanOrEqualTo(tester.getRect(find.byKey(const ValueKey('resolve-tree-filters'))).bottom), + ); + expect( + tester.getRect(find.byKey(const ValueKey('resolve-tree-selected-size'))).top, + greaterThanOrEqualTo(tester.getRect(find.byKey(const ValueKey('resolve-tree-selected-count'))).bottom), + ); expect(tester.takeException(), isNull); });