diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb index 8153459..d4f9030 100644 --- a/lib/l10n/app_de.arb +++ b/lib/l10n/app_de.arb @@ -408,6 +408,31 @@ }, "flowsMissingModulesLabel": "Braucht:", "flowsRunDisabledTooltip": "Erst die fehlenden Module installieren.", + "flowsInstallAllButton": "Alle installieren ({n})", + "@flowsInstallAllButton": { "placeholders": { "n": { "type": "int" } } }, + "flowsInstallPillTooltip": "Zum Installieren klicken", + "flowsInstallDepsTitle": "Abhängigkeiten installieren", + "flowsInstallDepsBody": "Die fehlenden Module werden der Reihe nach installiert. Vorhandene Installationen bleiben unberührt.", + "flowsInstallStatusPending": "Wartet", + "flowsInstallStatusInstalling": "Wird installiert…", + "flowsInstallStatusDone": "Installiert v{version}", + "@flowsInstallStatusDone": { "placeholders": { "version": { "type": "String" } } }, + "flowsInstallStatusFailed": "Fehlgeschlagen", + "flowsInstallStatusFailedDetail": "{error}", + "@flowsInstallStatusFailedDetail": { "placeholders": { "error": { "type": "String" } } }, + "flowsInstallDepsAllDone": "Alle Abhängigkeiten installiert.", + "flowsInstallDepsAnyFailed": "{ok} installiert, {failed} fehlgeschlagen.", + "@flowsInstallDepsAnyFailed": { + "placeholders": { + "ok": { "type": "int" }, + "failed": { "type": "int" } + } + }, + "flowsOpenInEditorTooltip": "YAML-Datei im Standard-Editor öffnen", + "flowsOpenInEditorFailed": "Konnte Datei nicht öffnen: {error}", + "@flowsOpenInEditorFailed": { + "placeholders": { "error": { "type": "String" } } + }, "welcomeChecklistAllSetTitle": "Du bist eingerichtet.", "welcomeChecklistAllSetBody": "Drei Stränge, an denen du als nächstes ziehen kannst:", "welcomeChecklistNextAuditTitle": "Audit-Log lesen", diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index eb06d30..8ff24a5 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -409,6 +409,31 @@ }, "flowsMissingModulesLabel": "Needs:", "flowsRunDisabledTooltip": "Install the missing modules first.", + "flowsInstallAllButton": "Install all ({n})", + "@flowsInstallAllButton": { "placeholders": { "n": { "type": "int" } } }, + "flowsInstallPillTooltip": "Click to install", + "flowsInstallDepsTitle": "Install dependencies", + "flowsInstallDepsBody": "Walking through the missing modules in order. Existing installs are left alone.", + "flowsInstallStatusPending": "Pending", + "flowsInstallStatusInstalling": "Installing…", + "flowsInstallStatusDone": "Installed v{version}", + "@flowsInstallStatusDone": { "placeholders": { "version": { "type": "String" } } }, + "flowsInstallStatusFailed": "Failed", + "flowsInstallStatusFailedDetail": "{error}", + "@flowsInstallStatusFailedDetail": { "placeholders": { "error": { "type": "String" } } }, + "flowsInstallDepsAllDone": "All dependencies installed.", + "flowsInstallDepsAnyFailed": "{ok} installed, {failed} failed.", + "@flowsInstallDepsAnyFailed": { + "placeholders": { + "ok": { "type": "int" }, + "failed": { "type": "int" } + } + }, + "flowsOpenInEditorTooltip": "Open the YAML file in your default editor", + "flowsOpenInEditorFailed": "Could not open file: {error}", + "@flowsOpenInEditorFailed": { + "placeholders": { "error": { "type": "String" } } + }, "welcomeChecklistAllSetTitle": "You're set up.", "welcomeChecklistAllSetBody": "Three threads to pull on next:", "welcomeChecklistNextAuditTitle": "Read the audit log", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index e509158..c5984f3 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1892,6 +1892,84 @@ abstract class AppLocalizations { /// **'Install the missing modules first.'** String get flowsRunDisabledTooltip; + /// No description provided for @flowsInstallAllButton. + /// + /// In en, this message translates to: + /// **'Install all ({n})'** + String flowsInstallAllButton(int n); + + /// No description provided for @flowsInstallPillTooltip. + /// + /// In en, this message translates to: + /// **'Click to install'** + String get flowsInstallPillTooltip; + + /// No description provided for @flowsInstallDepsTitle. + /// + /// In en, this message translates to: + /// **'Install dependencies'** + String get flowsInstallDepsTitle; + + /// No description provided for @flowsInstallDepsBody. + /// + /// In en, this message translates to: + /// **'Walking through the missing modules in order. Existing installs are left alone.'** + String get flowsInstallDepsBody; + + /// No description provided for @flowsInstallStatusPending. + /// + /// In en, this message translates to: + /// **'Pending'** + String get flowsInstallStatusPending; + + /// No description provided for @flowsInstallStatusInstalling. + /// + /// In en, this message translates to: + /// **'Installing…'** + String get flowsInstallStatusInstalling; + + /// No description provided for @flowsInstallStatusDone. + /// + /// In en, this message translates to: + /// **'Installed v{version}'** + String flowsInstallStatusDone(String version); + + /// No description provided for @flowsInstallStatusFailed. + /// + /// In en, this message translates to: + /// **'Failed'** + String get flowsInstallStatusFailed; + + /// No description provided for @flowsInstallStatusFailedDetail. + /// + /// In en, this message translates to: + /// **'{error}'** + String flowsInstallStatusFailedDetail(String error); + + /// No description provided for @flowsInstallDepsAllDone. + /// + /// In en, this message translates to: + /// **'All dependencies installed.'** + String get flowsInstallDepsAllDone; + + /// No description provided for @flowsInstallDepsAnyFailed. + /// + /// In en, this message translates to: + /// **'{ok} installed, {failed} failed.'** + String flowsInstallDepsAnyFailed(int ok, int failed); + + /// No description provided for @flowsOpenInEditorTooltip. + /// + /// In en, this message translates to: + /// **'Open the YAML file in your default editor'** + String get flowsOpenInEditorTooltip; + + /// No description provided for @flowsOpenInEditorFailed. + /// + /// In en, this message translates to: + /// **'Could not open file: {error}'** + String flowsOpenInEditorFailed(String error); + /// No description provided for @welcomeChecklistAllSetTitle. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index a396de7..d126c58 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -1067,6 +1067,56 @@ class AppLocalizationsDe extends AppLocalizations { String get flowsRunDisabledTooltip => 'Erst die fehlenden Module installieren.'; + @override + String flowsInstallAllButton(int n) { + return 'Alle installieren ($n)'; + } + + @override + String get flowsInstallPillTooltip => 'Zum Installieren klicken'; + + @override + String get flowsInstallDepsTitle => 'Abhängigkeiten installieren'; + + @override + String get flowsInstallDepsBody => + 'Die fehlenden Module werden der Reihe nach installiert. Vorhandene Installationen bleiben unberührt.'; + + @override + String get flowsInstallStatusPending => 'Wartet'; + + @override + String get flowsInstallStatusInstalling => 'Wird installiert…'; + + @override + String flowsInstallStatusDone(String version) { + return 'Installiert v$version'; + } + + @override + String get flowsInstallStatusFailed => 'Fehlgeschlagen'; + + @override + String flowsInstallStatusFailedDetail(String error) { + return '$error'; + } + + @override + String get flowsInstallDepsAllDone => 'Alle Abhängigkeiten installiert.'; + + @override + String flowsInstallDepsAnyFailed(int ok, int failed) { + return '$ok installiert, $failed fehlgeschlagen.'; + } + + @override + String get flowsOpenInEditorTooltip => 'YAML-Datei im Standard-Editor öffnen'; + + @override + String flowsOpenInEditorFailed(String error) { + return 'Konnte Datei nicht öffnen: $error'; + } + @override String get welcomeChecklistAllSetTitle => 'Du bist eingerichtet.'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 3f7407c..c2f0d59 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -1080,6 +1080,57 @@ class AppLocalizationsEn extends AppLocalizations { @override String get flowsRunDisabledTooltip => 'Install the missing modules first.'; + @override + String flowsInstallAllButton(int n) { + return 'Install all ($n)'; + } + + @override + String get flowsInstallPillTooltip => 'Click to install'; + + @override + String get flowsInstallDepsTitle => 'Install dependencies'; + + @override + String get flowsInstallDepsBody => + 'Walking through the missing modules in order. Existing installs are left alone.'; + + @override + String get flowsInstallStatusPending => 'Pending'; + + @override + String get flowsInstallStatusInstalling => 'Installing…'; + + @override + String flowsInstallStatusDone(String version) { + return 'Installed v$version'; + } + + @override + String get flowsInstallStatusFailed => 'Failed'; + + @override + String flowsInstallStatusFailedDetail(String error) { + return '$error'; + } + + @override + String get flowsInstallDepsAllDone => 'All dependencies installed.'; + + @override + String flowsInstallDepsAnyFailed(int ok, int failed) { + return '$ok installed, $failed failed.'; + } + + @override + String get flowsOpenInEditorTooltip => + 'Open the YAML file in your default editor'; + + @override + String flowsOpenInEditorFailed(String error) { + return 'Could not open file: $error'; + } + @override String get welcomeChecklistAllSetTitle => 'You\'re set up.'; diff --git a/lib/pages/flows.dart b/lib/pages/flows.dart index 271739c..7dbe938 100644 --- a/lib/pages/flows.dart +++ b/lib/pages/flows.dart @@ -6,6 +6,7 @@ import 'package:file_picker/file_picker.dart'; import 'package:flutter/material.dart'; import '../data/hub.dart'; +import '../data/system_actions.dart'; import '../l10n/app_localizations.dart'; import '../theme/theme.dart'; import '../theme/tokens.dart'; @@ -77,6 +78,31 @@ class _FlowsPageState extends State { ); } + /// Walk through one or more capability specs (e.g. + /// `text.extract@^0`) sequentially, showing per-item progress. + /// Refreshes the flows page on dismiss so the row updates if + /// any install actually changed the installed-set. + Future _installDeps(List specs) async { + final changed = await showDialog( + context: context, + barrierDismissible: false, + builder: (_) => _InstallDependenciesDialog(specs: specs), + ); + if (changed == true && mounted) _refresh(); + } + + /// Hand the YAML file to the OS — `open` on macOS, `xdg-open` + /// on Linux, `start` on Windows. The operator's default editor + /// for `.yaml` decides what actually opens. + Future _openInEditor(SavedFlow flow) async { + final r = await SystemActions.openInOs(flow.path); + if (!mounted || r.ok) return; + final l = AppLocalizations.of(context)!; + ScaffoldMessenger.of(context).showSnackBar( + SnackBar(content: Text(l.flowsOpenInEditorFailed(r.stderr))), + ); + } + @override Widget build(BuildContext context) { final l = AppLocalizations.of(context)!; @@ -131,6 +157,11 @@ class _FlowsPageState extends State { flow: flow, missingCapabilities: missing, onRun: missing.isEmpty ? () => _runFlow(flow) : null, + onInstallSingle: (spec) => _installDeps([spec]), + onInstallAll: missing.length > 1 + ? () => _installDeps(missing) + : null, + onOpenInEditor: () => _openInEditor(flow), ); }, ); @@ -164,11 +195,24 @@ class _FlowCard extends StatelessWidget { /// Run button greys out + a tooltip points the operator /// at the install path. Non-null otherwise. final VoidCallback? onRun; + /// Install a single capability spec (verbatim `use:` string, + /// version constraint included — the dialog strips the `@` + /// suffix before calling installModule). + final ValueChanged onInstallSingle; + /// Install every missing capability in sequence. Null when + /// only one is missing (the single-install pill is enough). + final VoidCallback? onInstallAll; + /// Hand the flow YAML to the OS so the operator's default + /// editor opens it. + final VoidCallback onOpenInEditor; const _FlowCard({ required this.flow, required this.missingCapabilities, required this.onRun, + required this.onInstallSingle, + required this.onInstallAll, + required this.onOpenInEditor, }); @override @@ -177,73 +221,142 @@ class _FlowCard extends StatelessWidget { final l = AppLocalizations.of(context)!; final isRunnable = missingCapabilities.isEmpty; return FaiCard( - child: Row( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon( - Icons.account_tree_outlined, - size: 20, - color: theme.colorScheme.primary, + Row( + children: [ + Icon( + Icons.account_tree_outlined, + size: 20, + color: theme.colorScheme.primary, + ), + const SizedBox(width: FaiSpace.md), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + flow.name, + style: theme.textTheme.titleMedium?.copyWith( + fontWeight: FontWeight.w600, + ), + ), + const SizedBox(height: 2), + Text( + flow.path, + style: FaiTheme.mono( + size: 11, + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), + FaiPill( + label: '${flow.sizeBytes} B', + tone: FaiPillTone.neutral, + monospace: true, + ), + const SizedBox(width: FaiSpace.sm), + IconButton( + icon: const Icon(Icons.edit_outlined, size: 18), + tooltip: l.flowsOpenInEditorTooltip, + onPressed: onOpenInEditor, + ), + const SizedBox(width: FaiSpace.xs), + Tooltip( + message: isRunnable ? '' : l.flowsRunDisabledTooltip, + child: FilledButton.icon( + onPressed: onRun, + icon: const Icon(Icons.play_arrow, size: 16), + label: Text(l.flowsRunButton), + ), + ), + ], ), - const SizedBox(width: FaiSpace.md), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - flow.name, - style: theme.textTheme.titleMedium?.copyWith( - fontWeight: FontWeight.w600, + if (!isRunnable) ...[ + const SizedBox(height: FaiSpace.sm), + Padding( + padding: const EdgeInsets.only(left: 32), + child: Wrap( + spacing: FaiSpace.xs, + runSpacing: FaiSpace.xs, + crossAxisAlignment: WrapCrossAlignment.center, + children: [ + Text( + l.flowsMissingModulesLabel, + style: theme.textTheme.labelSmall?.copyWith( + color: theme.colorScheme.error, + letterSpacing: 0.4, + ), ), - ), - const SizedBox(height: 2), - Text( - flow.path, - style: FaiTheme.mono( - size: 11, - color: theme.colorScheme.onSurfaceVariant, - ), - ), - if (!isRunnable) ...[ - const SizedBox(height: FaiSpace.sm), - Wrap( - spacing: FaiSpace.xs, - runSpacing: FaiSpace.xs, - crossAxisAlignment: WrapCrossAlignment.center, - children: [ - Text( - l.flowsMissingModulesLabel, - style: theme.textTheme.labelSmall?.copyWith( - color: theme.colorScheme.error, - letterSpacing: 0.4, + for (final cap in missingCapabilities) + _ClickablePill( + label: cap, + tooltip: l.flowsInstallPillTooltip, + onTap: () => onInstallSingle(cap), + ), + if (onInstallAll != null) + Padding( + padding: const EdgeInsets.only(left: FaiSpace.sm), + child: FilledButton.tonalIcon( + onPressed: onInstallAll, + icon: const Icon(Icons.download_outlined, size: 14), + label: Text( + l.flowsInstallAllButton(missingCapabilities.length), + ), + style: FilledButton.styleFrom( + padding: const EdgeInsets.symmetric( + horizontal: FaiSpace.md, + vertical: 0, + ), + minimumSize: const Size(0, 28), + tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), ), - for (final cap in missingCapabilities) - FaiPill( - label: cap, - tone: FaiPillTone.danger, - monospace: true, - ), - ], - ), + ), ], - ], + ), ), - ), - FaiPill( - label: '${flow.sizeBytes} B', - tone: FaiPillTone.neutral, + ], + ], + ), + ); + } +} + +/// Tappable variant of `FaiPill` used for the missing-capability +/// row. Wraps the pill in `InkWell` + `Tooltip` so the operator +/// gets a click affordance and a "Click to install" hint. +class _ClickablePill extends StatelessWidget { + final String label; + final String tooltip; + final VoidCallback onTap; + + const _ClickablePill({ + required this.label, + required this.tooltip, + required this.onTap, + }); + + @override + Widget build(BuildContext context) { + return Tooltip( + message: tooltip, + child: Material( + color: Colors.transparent, + borderRadius: BorderRadius.circular(FaiRadius.sm), + child: InkWell( + borderRadius: BorderRadius.circular(FaiRadius.sm), + onTap: onTap, + child: FaiPill( + label: label, + tone: FaiPillTone.danger, + icon: Icons.download_outlined, monospace: true, ), - const SizedBox(width: FaiSpace.md), - Tooltip( - message: isRunnable ? '' : l.flowsRunDisabledTooltip, - child: FilledButton.icon( - onPressed: onRun, - icon: const Icon(Icons.play_arrow, size: 16), - label: Text(l.flowsRunButton), - ), - ), - ], + ), ), ); } @@ -731,3 +844,256 @@ class _FlowRunDialogState extends State<_FlowRunDialog> { ); } } + +/// Per-spec install state for the dependencies dialog. The +/// dialog walks the list sequentially, advancing each row from +/// `pending` → `installing` → `done` | `failed` so the operator +/// sees one row light up at a time. +enum _InstallStatus { pending, installing, done, failed } + +class _InstallItemState { + /// Verbatim spec from the flow's `use:` line, e.g. + /// `text.extract@^0`. Shown as-is so the operator recognises + /// the same string they saw on the flow card. + final String spec; + + /// Bare capability name (everything left of `@`). What the + /// hub's `installModule` actually resolves through the store + /// index — the version constraint is dropped here because + /// the index already picks a matching version. + final String bareName; + + _InstallStatus status; + String? installedVersion; + String? error; + + _InstallItemState(this.spec) + : bareName = _stripVersion(spec), + status = _InstallStatus.pending; + + static String _stripVersion(String spec) { + final at = spec.indexOf('@'); + return at >= 0 ? spec.substring(0, at) : spec; + } +} + +/// Sequential installer for a list of capability specs. Pops +/// with `true` if at least one install completed successfully +/// (so the parent knows to refresh its installed-modules view), +/// `false` otherwise. +class _InstallDependenciesDialog extends StatefulWidget { + final List specs; + + const _InstallDependenciesDialog({required this.specs}); + + @override + State<_InstallDependenciesDialog> createState() => + _InstallDependenciesDialogState(); +} + +class _InstallDependenciesDialogState + extends State<_InstallDependenciesDialog> { + late final List<_InstallItemState> _items; + bool _running = true; + + @override + void initState() { + super.initState(); + _items = widget.specs.map(_InstallItemState.new).toList(); + // Kick off the install sequence after first frame so any + // initState-time setState calls inside the chain hit a + // mounted widget. + WidgetsBinding.instance.addPostFrameCallback((_) => _runAll()); + } + + Future _runAll() async { + for (final item in _items) { + if (!mounted) return; + setState(() => item.status = _InstallStatus.installing); + try { + final r = await HubService.instance.installModule( + source: item.bareName, + ); + if (!mounted) return; + setState(() { + item.status = _InstallStatus.done; + item.installedVersion = r.version; + }); + } catch (e) { + if (!mounted) return; + setState(() { + item.status = _InstallStatus.failed; + item.error = e.toString(); + }); + } + } + if (!mounted) return; + setState(() => _running = false); + } + + bool get _anySucceeded => + _items.any((i) => i.status == _InstallStatus.done); + + int get _doneCount => + _items.where((i) => i.status == _InstallStatus.done).length; + + int get _failedCount => + _items.where((i) => i.status == _InstallStatus.failed).length; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l = AppLocalizations.of(context)!; + return AlertDialog( + title: Text(l.flowsInstallDepsTitle), + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(FaiRadius.md), + ), + content: ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 520, maxHeight: 480), + child: SingleChildScrollView( + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + l.flowsInstallDepsBody, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + const SizedBox(height: FaiSpace.md), + for (final item in _items) ...[ + _InstallRow(item: item), + const SizedBox(height: FaiSpace.sm), + ], + if (!_running) ...[ + const SizedBox(height: FaiSpace.xs), + Text( + _failedCount == 0 + ? l.flowsInstallDepsAllDone + : l.flowsInstallDepsAnyFailed(_doneCount, _failedCount), + style: theme.textTheme.bodySmall?.copyWith( + color: _failedCount == 0 + ? null + : theme.colorScheme.error, + fontWeight: FontWeight.w500, + ), + ), + ], + ], + ), + ), + ), + actions: [ + TextButton( + onPressed: _running + ? null + : () => Navigator.pop(context, _anySucceeded), + child: Text(l.buttonClose), + ), + ], + ); + } +} + +/// One row in the install-dependencies dialog. Status icon + +/// capability name + per-state detail line (version on success, +/// truncated error on failure). +class _InstallRow extends StatelessWidget { + final _InstallItemState item; + + const _InstallRow({required this.item}); + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + final l = AppLocalizations.of(context)!; + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + SizedBox( + width: 20, + height: 20, + child: Center(child: _statusIcon(theme)), + ), + const SizedBox(width: FaiSpace.sm), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + item.spec, + style: FaiTheme.mono( + size: 12, + weight: FontWeight.w600, + color: theme.colorScheme.onSurface, + ), + ), + const SizedBox(height: 2), + Text( + _detailLine(l), + style: theme.textTheme.bodySmall?.copyWith( + color: item.status == _InstallStatus.failed + ? theme.colorScheme.error + : theme.colorScheme.onSurfaceVariant, + ), + ), + if (item.status == _InstallStatus.failed && + item.error != null) ...[ + const SizedBox(height: FaiSpace.xs), + FaiErrorBox( + text: item.error!, + isError: true, + maxHeight: 120, + ), + ], + ], + ), + ), + ], + ); + } + + Widget _statusIcon(ThemeData theme) { + switch (item.status) { + case _InstallStatus.pending: + return Icon( + Icons.radio_button_unchecked, + size: 16, + color: theme.colorScheme.onSurfaceVariant, + ); + case _InstallStatus.installing: + return const SizedBox( + width: 14, + height: 14, + child: CircularProgressIndicator(strokeWidth: 2), + ); + case _InstallStatus.done: + return Icon( + Icons.check_circle, + size: 16, + color: theme.colorScheme.primary, + ); + case _InstallStatus.failed: + return Icon( + Icons.error_outline, + size: 16, + color: theme.colorScheme.error, + ); + } + } + + String _detailLine(AppLocalizations l) { + switch (item.status) { + case _InstallStatus.pending: + return l.flowsInstallStatusPending; + case _InstallStatus.installing: + return l.flowsInstallStatusInstalling; + case _InstallStatus.done: + return l.flowsInstallStatusDone(item.installedVersion ?? '?'); + case _InstallStatus.failed: + return l.flowsInstallStatusFailed; + } + } +} diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 724bb2a..438e7e5 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,8 +5,10 @@ import FlutterMacOS import Foundation +import file_picker import shared_preferences_foundation func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 1385d0f..6ebac85 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -1,20 +1,26 @@ PODS: + - file_picker (0.0.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS DEPENDENCIES: + - file_picker (from `Flutter/ephemeral/.symlinks/plugins/file_picker/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) EXTERNAL SOURCES: + file_picker: + :path: Flutter/ephemeral/.symlinks/plugins/file_picker/macos FlutterMacOS: :path: Flutter/ephemeral shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin SPEC CHECKSUMS: + file_picker: 7584aae6fa07a041af2b36a2655122d42f578c1a FlutterMacOS: d0db08ddef1a9af05a5ec4b724367152bb0500b1 shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb diff --git a/pubspec.yaml b/pubspec.yaml index 73ec444..0aa64ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: fai_studio description: "F∆I Studio — desktop GUI for the F∆I hub" publish_to: 'none' -version: 0.42.0 +version: 0.43.0 environment: sdk: ^3.11.0-200.1.beta