diff --git a/integration_test/app_test.dart b/integration_test/app_test.dart index 01eb345b..e3df09e0 100644 --- a/integration_test/app_test.dart +++ b/integration_test/app_test.dart @@ -5,28 +5,28 @@ import 'package:integration_test/integration_test.dart'; void main() { IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('Download model from HF', (tester) async { - const app = App(); - await tester.pumpWidget(app); + //testWidgets('Download model from HF', (tester) async { + // const app = App(); + // await tester.pumpWidget(app); - await tester.tap(find.text('Import model')); - await tester.pumpAndSettle(); + // await tester.tap(find.text('Import model')); + // await tester.pumpAndSettle(); - await tester.tap(find.text('Hugging Face')); - await tester.pumpAndSettle(); + // await tester.tap(find.text('Hugging Face')); + // await tester.pumpAndSettle(); - final searchBarFinder = find.bySemanticsLabel('Find a model').first; - await tester.tap(searchBarFinder, warnIfMissed: false); - await tester.pumpAndSettle(); - await tester.enterText(searchBarFinder, 'tiny'); - await tester.pumpAndSettle(); + // final searchBarFinder = find.bySemanticsLabel('Find a model').first; + // await tester.tap(searchBarFinder, warnIfMissed: false); + // await tester.pumpAndSettle(); + // await tester.enterText(searchBarFinder, 'tiny'); + // await tester.pumpAndSettle(); - await tester.tap(find.text('TinyLlama 1.1B Chat V1.0').first); - await tester.pumpAndSettle(); - await tester.tap(find.text('Import selected model')); - await tester.pumpFrames(app, const Duration(seconds: 1)); - expect(find.textContaining(RegExp(r'^[1-9][\d,]* MB$')), findsNWidgets(2)); + // await tester.tap(find.text('TinyLlama 1.1B Chat V1.0').first); + // await tester.pumpAndSettle(); + // await tester.tap(find.text('Import selected model')); + // await tester.pumpFrames(app, const Duration(seconds: 1)); + // expect(find.textContaining(RegExp(r'^[1-9][\d,]* MB$')), findsNWidgets(2)); - await tester.pumpAndSettle(); - }); -} \ No newline at end of file + // await tester.pumpAndSettle(); + //}); +} diff --git a/lib/example_graph.dart b/lib/example_graph.dart deleted file mode 100644 index 6b2b588e..00000000 --- a/lib/example_graph.dart +++ /dev/null @@ -1,46 +0,0 @@ -const String faceDetectionExampleGraph = """ -output_stream : "output" - -node { - calculator : "OpenVINOInferenceAdapterCalculator" - output_side_packet : "INFERENCE_ADAPTER:adapter" - node_options: { - [type.googleapis.com/mediapipe.OpenVINOInferenceAdapterCalculatorOptions] { - model_path: "/data/omz_models/intel/face-detection-retail-0004/face-detection-retail-0004.xml" - } - } -} - -node { - calculator : "VideoInputCalculator" - output_stream: "IMAGE:input_image" -} - -node { - calculator : "DetectionCalculator" - input_side_packet : "INFERENCE_ADAPTER:adapter" - input_stream : "IMAGE:input_image" - output_stream: "INFERENCE_RESULT:inference_detections" -} - -node { - calculator : "OverlayCalculator" - input_stream : "IMAGE:input_image" - input_stream : "INFERENCE_RESULT:inference_detections" - output_stream: "IMAGE:output" - node_options: { - [type.googleapis.com/mediapipe.OverlayCalculatorOptions] { - labels: { - id: "face" - name: "face" - color: "#f7dab3ff" - is_empty: false - } - - stroke_width: 2 - opacity: 0.4 - font_size: 1.0 - } - } -} -"""; diff --git a/lib/header.dart b/lib/header.dart deleted file mode 100644 index bede01c4..00000000 --- a/lib/header.dart +++ /dev/null @@ -1,61 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:flutter_svg/svg.dart'; - -class Header extends StatelessWidget implements PreferredSizeWidget { - final bool showBack; - final void Function(BuildContext context)? onBack; - const Header(this.showBack, {this.onBack, super.key}); - - - @override - Widget build(BuildContext context) { - return Column( - children: [ - SizedBox( - height: 88, - child: Stack( - children: [ - Container( - padding: const EdgeInsets.only(left: 30), - height: 64, - color: Theme.of(context).colorScheme.secondary, - child: Row( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - if(showBack) Padding( - padding: const EdgeInsets.only(right: 5), - child: IconButton( - color: Colors.white, - icon: const Icon(Icons.arrow_back), - onPressed: () { - if(onBack != null) { - onBack!(context); - } else { - context.go('/'); - } - } - ), - ), - Padding( - padding: const EdgeInsets.only(right: 10.0, top: 2), - child: SvgPicture.asset("images/openvino_logo.svg"), - ), - const Text("Test Drive", style: TextStyle( - color: Colors.white, - fontSize: 20, - )), - ], - ), - ), - SvgPicture.asset("images/bit.svg"), - ] - ), - ), - ], - ); - } - - @override - Size get preferredSize => const Size.fromHeight(88); -} diff --git a/lib/import/import_page.dart b/lib/import/import_page.dart deleted file mode 100644 index 5eaab894..00000000 --- a/lib/import/import_page.dart +++ /dev/null @@ -1,37 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/header.dart'; -import 'package:inference/import/public_model.dart'; - -class ImportPage extends StatefulWidget { - const ImportPage({super.key}); - - @override - State createState() => _ImportPageState(); -} - -class _ImportPageState extends State with TickerProviderStateMixin { - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 3, animationDuration: Duration.zero, vsync: this); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return const Scaffold( - appBar: Header(false), - body: Padding( - padding: EdgeInsets.symmetric(horizontal: 30.0), - child: PublicModelPage() - ), - ); - } -} diff --git a/lib/import/optimization_filter_button.dart b/lib/import/optimization_filter_button.dart deleted file mode 100644 index 98efce6f..00000000 --- a/lib/import/optimization_filter_button.dart +++ /dev/null @@ -1,30 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/providers/project_filter_provider.dart'; - -class OptimizationFilterButton extends StatelessWidget { - - final String name; - final ProjectFilterProvider filter; - const OptimizationFilterButton(this.name, this.filter, {super.key}); - - @override - Widget build(BuildContext context) { - return Card( - child: Padding( - padding: const EdgeInsets.only(right: 12.0), - child: Row( - children: [ - Checkbox(value: filter.optimizations.contains(name), onChanged: (val) { - if (val ?? false) { - filter.addOptimization(name); - } else { - filter.removeOptimization(name); - } - }), - Text(name), - ], - ), - ), - ); - } -} diff --git a/lib/import/public_model.dart b/lib/import/public_model.dart deleted file mode 100644 index ce2bd065..00000000 --- a/lib/import/public_model.dart +++ /dev/null @@ -1,323 +0,0 @@ -import 'package:dio/dio.dart'; -import 'package:flutter/material.dart'; -import 'package:inference/import/optimization_filter_button.dart'; -import 'package:inference/projects/task_type_filter.dart'; -import 'package:inference/providers/project_filter_provider.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/public_model_info.dart'; -import 'package:inference/public_models.dart'; -import 'package:inference/searchbar.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/utils/dialogs.dart'; -import 'package:provider/provider.dart'; -import 'package:shimmer/shimmer.dart'; -import 'package:go_router/go_router.dart'; - -class PublicModelPage extends StatefulWidget { - const PublicModelPage({super.key}); - - @override - State createState() => _PublicModelPageState(); -} - -class _PublicModelPageState extends State { - List? models; - - PublicModelInfo? selectedModelForImport; - - void loadModels() async { - try { - final publicModels = await getPublicModels(); - setState(() { - models = publicModels; - }); - } on DioException catch(ex) { - if (ex.type == DioExceptionType.connectionError) { - if (context.mounted) { - errorDialog(context, "Connection error","Unable to connect to HuggingFace to load the OpenVINO model collections.\nPlease disable your proxy or VPN and try again."); - } - } - } - - } - - @override - void initState() { - super.initState(); - loadModels(); - } - - List buildList(ProjectFilterProvider filter) { - if (models == null) { - return const [ - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - GhostProjectItem(), - ]; - } - - final filteredModels = filter.applyFilterOnPublicModelInfo(models!); - - return filteredModels.map((model) { - return PublicModelItem(model, - key: ValueKey(model), - isSelected: selectedModelForImport == model, - onToggle: (selected) { - setState(() { - if (selected) { - selectedModelForImport = model; - } else { - selectedModelForImport = null; - } - }); - }, - ); - }).toList(); - } - - @override - Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (_) => ProjectFilterProvider(), - child: Consumer(builder: (context, filter, child) { - return Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Row( - children: [ - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: const EdgeInsets.only(left: 3, bottom: 8.0, top: 8.0, right: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - children: [ - GetiSearchBar( - onChange: (value) => filter.name = value, - ), - const Padding( - padding: EdgeInsets.only(left: 32.0), - child: Text("Optimization: "), - ), - OptimizationFilterButton("int4", filter), - OptimizationFilterButton("int8", filter), - OptimizationFilterButton("fp16", filter), - ], - ), - ), - Expanded( - child: CustomScrollView( - primary: false, - slivers: [ - SliverPadding( - padding: const EdgeInsets.all(3), - sliver: SliverGrid.extent( - crossAxisSpacing: 10, - mainAxisSpacing: 10, - maxCrossAxisExtent: 310, - childAspectRatio: 1.5, - children: buildList(filter) - ) - ) - ] - ), - ), - Padding( - padding: const EdgeInsets.symmetric(vertical: 30.0, horizontal: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: ElevatedButton( - style: const ButtonStyle( - elevation: WidgetStatePropertyAll(0), - backgroundColor: WidgetStatePropertyAll(intelGray) - ), - onPressed: () { - setState(() { - context.pop(); - }); - }, - child: const Text("Cancel") - ), - ), - - ElevatedButton( - style: ButtonStyle( - elevation: const WidgetStatePropertyAll(0), - backgroundColor: (selectedModelForImport == null - ? const WidgetStatePropertyAll(intelGray) - : null - ) - ), - onPressed: () { - if (selectedModelForImport != null) { - final projectsProvider = Provider.of(context, listen: false); - PublicModelInfo.convertToProject(selectedModelForImport!).then((project) { - projectsProvider.addProject(project); - context.go('/inference', extra: project); - }); - } - }, - child: const Text("Add model") - ), - ], - ), - ), - ], - ), - ), - ], - ), - ); - } - ), - ); - } -} - - -class GhostProjectItem extends StatelessWidget { - const GhostProjectItem({super.key}); - - @override - Widget build(BuildContext context) { - return ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: Opacity( - opacity: 1.0, - child: ConstrainedBox( - constraints: const BoxConstraints.expand(height: 136), - child: Shimmer.fromColors( - baseColor: Theme.of(context).colorScheme.surface, - highlightColor: Theme.of(context).colorScheme.onSurfaceVariant, - child: Container( - color: Theme.of(context).colorScheme.surface, - ) - ), - ), - ) - ); - } -} - -class PublicModelItem extends StatefulWidget { - final PublicModelInfo model; - final Function onToggle; - final bool isSelected; - const PublicModelItem(this.model, {required this.onToggle, this.isSelected = false, super.key}); - - @override - State createState() => _PublicModelItemState(); -} - -class _PublicModelItemState extends State { - bool hovered = false; - - void onHover(bool? val) { - setState(() { - hovered = val ?? false; - }); - } - - void onTap() { - widget.onToggle(!widget.isSelected); - } - - Color backgroundColor(BuildContext context) { - final scheme = Theme.of(context).colorScheme; - if (hovered) { - return scheme.onSurfaceVariant; - } else { - return scheme.surface; - } - } - - Color borderColor(BuildContext context) { - if (widget.isSelected) { - return intelBlue; - } else { - return backgroundColor(context); - } - } - - double borderWidth(BuildContext context) { - if (widget.isSelected) { - return 2; - } else { - return 0; - } - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 8, bottom: 8), - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8.0), - side: BorderSide(width: borderWidth(context), color: borderColor(context)) - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: InkWell( - splashFactory: NoSplash.splashFactory, - borderRadius: BorderRadius.circular(8.0), - onHover: (val) => onHover(val), - onTap: () => onTap(), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - height: 110, - decoration: BoxDecoration( - image: DecorationImage( - image: widget.model.thumbnail.image, - fit: BoxFit.cover), - ), - ), - Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text( - widget.model.name, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - fontSize: 12, - ) - ), - //Text(formatter.format(DateTime.parse(widget.model.lastModified))), - //Column( - // crossAxisAlignment: CrossAxisAlignment.start, - // children: [ - // Text("Likes: ${widget.model.likes}"), - // Text("Downloads: ${widget.model.downloads}"), - // ], - //), - ] - ), - ), - ) - ], - ), - ), - ) - ) - ); - } -} diff --git a/lib/inference/batch_inference.dart b/lib/inference/batch_inference.dart deleted file mode 100644 index e0a59d60..00000000 --- a/lib/inference/batch_inference.dart +++ /dev/null @@ -1,507 +0,0 @@ -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; - -import 'package:csv/csv.dart'; -import 'package:desktop_drop/desktop_drop.dart'; -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:inference/inference/device_selector.dart'; -import 'package:inference/interop/openvino_bindings.dart'; -import 'package:inference/providers/image_inference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:mime/mime.dart'; -import 'package:path/path.dart'; -import 'package:provider/provider.dart'; - -bool isImage(String path) { - final mimeType = lookupMimeType(path); - if (mimeType == null) { - return false; - } - return mimeType.startsWith('image/'); -} - - -bool pathIsValid(String path) { - // Directory.exists will return false if the file is not a folder. - return Directory(path).existsSync(); -} - -class Progress { - int current = 0; - final int total; - - Progress(this.total); - - double percentage() { - return current.toDouble() / total; - } -} - - -class BatchInference extends StatefulWidget { - const BatchInference({super.key}); - - @override - State createState() => _BatchInferenceState(); -} - -class _BatchInferenceState extends State { - - String sourceFolder = ""; - String destinationFolder = ""; - bool forceStop = false; - Progress? progress; - - bool get isRunning { - if (progress == null) { - return false; - } - - if (forceStop) { - return false; - } - - return progress!.current < progress!.total; - } - - bool get canProcess => - pathIsValid(sourceFolder) && pathIsValid(destinationFolder) && - serializationOutput.any(); - - SerializationOutput serializationOutput = SerializationOutput(overlay: true); - - void processFolder(BuildContext context, ImageInferenceProvider inference) async { - final platformContext = Context(style: Style.platform); - final dir = Directory(sourceFolder); - final listener = dir.list(recursive: true); - - final list = await listener.toList(); - setState(() { - progress = Progress(list.where((item) => isImage(item.path)).length); - forceStop = false; - }); - - List> rows = []; - const encoder = JsonEncoder.withIndent(" "); - const converter = CsvToListConverter(); - inference.lock(); - - for (final file in list) { - if (forceStop) { - break; - } - if (isImage(file.path)) { - final outputFilename = platformContext.basename(file.path); - Uint8List imageData = File(file.path).readAsBytesSync(); - final inferenceResult = await inference.infer(imageData, serializationOutput); - await Future.delayed(Duration.zero); // For some reason ui does not update even though it's running in Isolate. This gives the UI time to run that code. - final outputPath = platformContext.join(destinationFolder, outputFilename); - if (serializationOutput.overlay) { - final outputFile = File(outputPath); - final decodedImage = base64Decode(inferenceResult.overlay!); - outputFile.writeAsBytes(decodedImage); - } - if (serializationOutput.csv) { - var csvOutput = converter.convert(inferenceResult.csv); - rows.addAll(csvOutput.map((row) { - row.insert(0, outputFilename); - return row; - })); - } - if (serializationOutput.json) { - final outputFile = File(setExtension(outputPath, ".json")); - outputFile.writeAsString(encoder.convert(inferenceResult.json)); - } - setState(() { - progress!.current += 1; - }); - } - } - inference.unlock(); - - if (serializationOutput.csv) { - List columns = ["filename", "label_name", "label_id", "probability", "shape_type", "x", "y", "width", "height", "area", "angle"]; - rows.insert(0, columns); - const converter = ListToCsvConverter(); - final outputPath = platformContext.join(destinationFolder, "predictions.csv"); - File(outputPath).writeAsStringSync(converter.convert(rows)); - } - } - - void stop() { - setState((){ - forceStop = true; - }); - } - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, inference, child) { - return Padding( - padding: const EdgeInsets.only(top: 48.0), - child: Container( - color: Theme.of(context).colorScheme.surfaceContainer, - padding: const EdgeInsets.all(30), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - FolderRow("Source folder", - initialValue: sourceFolder, - enabled: !isRunning, - onSubmit: (path) { - setState(() { - sourceFolder = path; - }); - }), - FolderRow("Destination folder", - initialValue: destinationFolder, - enabled: !isRunning, - onSubmit: (path) { - setState(() { - destinationFolder = path; - }); - }), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const Padding( - padding: EdgeInsets.only(bottom: 3.0), - child: Text("Outputs"), - ), - SwitchRow("Overlay image", - onChange: (value) { - setState(() { - serializationOutput.overlay = value; - }); - }, - initialValue: serializationOutput.overlay, - ), - SwitchRow("CSV", - onChange: (value) { - setState(() { - serializationOutput.csv = value; - }); - }, - initialValue: serializationOutput.csv, - ), - SwitchRow("JSON", - onChange: (value) { - setState(() { - serializationOutput.json = value; - }); - }, - initialValue: serializationOutput.json, - ), - ], - ), - const DeviceSelector(), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20), - child: BatchProgressButton( - isRunning: isRunning, - enabled: canProcess && inference.isReady, - onStart: () { - processFolder(context, inference); - }, - onStop: () => stop(), - ), - ), - - BatchProgress(progress), - ], - ), - ), - ); - } - ); - } -} - -class BatchProgressButton extends StatelessWidget { - final bool isRunning; - final bool enabled; - final void Function() onStart; - final void Function() onStop; - - const BatchProgressButton({required this.onStart, required this.onStop, this.isRunning = false, this.enabled = true, super.key}); - - @override - Widget build(BuildContext context) { - if (isRunning) { - return ElevatedButton( - onPressed: onStop, - child: const Text("Stop")); - } - - if (!enabled) { - return ElevatedButton( - style: const ButtonStyle( - backgroundColor: MaterialStatePropertyAll(intelGrayLight), - elevation: MaterialStatePropertyAll(0), - ), - onPressed: () {}, - child: const Text("Start")); - } - - return ElevatedButton( - onPressed: onStart, - child: const Text("Start")); - } - -} - -class BatchProgress extends StatelessWidget { - final Progress? progress; - const BatchProgress(this.progress, {super.key}); - - @override - Widget build(BuildContext context) { - return Builder( - builder: (context) { - if (progress == null) { - return const SizedBox.shrink(); - } - - return SizedBox( - width: 300, - child: Column( - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text("Inference progress"), - Text("${progress!.current}/${progress!.total}"), - ]), - ), - LinearProgressIndicator( - value: progress!.percentage(), - color: intelBlueLight, - backgroundColor: intelGrayLight, - minHeight: 8, - borderRadius: BorderRadius.circular(4), - ) - ] - ) - ); - } - ); - } -} - -class SwitchRow extends StatefulWidget { - final String label; - final void Function(bool) onChange; - final bool initialValue; - const SwitchRow(this.label, {required this.onChange, this.initialValue = false, super.key}); - - @override - State createState() => _SwitchRowState(); -} - -class _SwitchRowState extends State { - bool switchState = false; - - @override - void initState() { - // TODO: implement initState - super.initState(); - switchState = widget.initialValue; - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Row( - children: [ - SizedBox( - width: 30, - height: 20, - child: FittedBox( - fit: BoxFit.fill, - child: Switch.adaptive( - value: switchState, - activeColor: intelGrayReallyDark, - activeTrackColor: lightGray, - onChanged: (value) { - setState(() { switchState = value; }); - widget.onChange(value); - }, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text(widget.label), - ), - ], - ), - ); - } -} - -class FolderRow extends StatefulWidget { - final String label; - final bool enabled; - final String initialValue; - final void Function(String) onSubmit; - const FolderRow(this.label, - {required this.onSubmit, this.initialValue = "", this.enabled = true, super.key}); - - @override - State createState() => _FolderRowState(); -} - -class _FolderRowState extends State { - bool _showReleaseMessage = false; - bool pathSet = false; - bool isValid = false; - - final controller = TextEditingController(); - final platformContext = Context(style: Style.platform); - - @override - void initState() { - super.initState(); - controller.text = widget.initialValue; - isValid = pathIsValid(controller.text); - } - - void showReleaseMessage() { - setState(() => _showReleaseMessage = true); - } - - void hideReleaseMessage() { - setState(() => _showReleaseMessage = false); - } - - void handleDrop(DropDoneDetails details) { - if (details.files.isNotEmpty) { - setPath(details.files[0].path); - } - } - - void setPath(String path) { - if (pathIsValid(path)) { - controller.text = path; - widget.onSubmit(path); - setState(() { - pathSet = true; - isValid = true; - }); - } else { - //TODO show user error that a folder must be selected - } - } - - @override - void dispose() { - // TODO: implement dispose - super.dispose(); - controller.dispose(); - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: DropTarget( - onDragDone: (details) => handleDrop(details), - onDragExited: (_) => hideReleaseMessage(), - onDragEntered: (_) => showReleaseMessage(), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 3.0), - child: Text(widget.label), - ), - Row( - children: [ - SizedBox( - width: 300, - height: 30, - child: TextField( - onChanged: (path) { - setState(() { - isValid = pathIsValid(path); - //Submit regardless. Path can be invalid, but that's less confusing to the user. - widget.onSubmit(path); - pathSet = isValid; - }); - }, - enabled: widget.enabled, - controller: controller, - textAlign: TextAlign.start, - style: const TextStyle ( - color: textColor, - fontSize: 10, - ), - decoration: InputDecoration( - contentPadding: const EdgeInsets.only(left: 10, bottom: 0), - hintText: (_showReleaseMessage - ? "Release..." - : "Drop ${widget.label.toLowerCase()} in"), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide( - color: (isValid ? Colors.green : Colors.red), - )), - ), - ), - ), - Padding( - padding: const EdgeInsets.symmetric(horizontal: 20.0), - child: FolderButton( - enabled: widget.enabled, - text: (pathSet ? "Change" : "Select"), - onSelect: setPath, - ), - ) - ], - ), - ], - ), - ), - ); - } -} - -class FolderButton extends StatelessWidget { - final String text; - final void Function(String) onSelect; - final bool enabled; - const FolderButton({required this.onSelect, required this.text, this.enabled = true, super.key}); - - void showUploadMenu() async { - final result = await FilePicker.platform.getDirectoryPath(); - if (result != null) { - onSelect(result.toString()); - } - } - - @override - Widget build(BuildContext context) { - if (!enabled) { - return ElevatedButton.icon( - icon: const Icon(Icons.folder), - onPressed: () {}, - style: const ButtonStyle( - elevation: MaterialStatePropertyAll(0), - backgroundColor: MaterialStatePropertyAll(intelGrayLight), - ), - label: Text(text), - ); - } - - return OutlinedButton.icon( - icon: const Icon(Icons.folder), - onPressed: () => showUploadMenu(), - label: Text(text), - ); - } -} diff --git a/lib/inference/camera_page.dart b/lib/inference/camera_page.dart deleted file mode 100644 index 0bd26cdc..00000000 --- a/lib/inference/camera_page.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; -import 'dart:ui' as ui; -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:inference/providers/image_inference_provider.dart'; -import 'package:provider/provider.dart'; - -Future createImage(Uint8List bytes) async { - final Completer completer = Completer(); - ui.decodeImageFromList(bytes, - (ui.Image img) { - completer.complete(img); - }, - ); - return completer.future; -} -class CameraPage extends StatefulWidget { - final ImageInferenceProvider inferenceProvider; - - const CameraPage(this.inferenceProvider, {super.key}); - @override - State createState() => _CameraPageState(); -} - - -class _CameraPageState extends State { - ui.Image? image; - - ImageInferenceProvider get inferenceProvider => widget.inferenceProvider; - - void openCamera() { - inferenceProvider.openCamera(0); - inferenceProvider.setListener((output) { - print(output); - createImage(base64Decode(output.overlay!)).then((frame) { - if (mounted) { - setState(() { - image = frame; - }); - } - }); - }); - } - - - @override - void initState() { - super.initState(); - openCamera(); - } - - @override - void dispose() { - inferenceProvider.closeCamera(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Expanded( - child: Padding( - padding: const EdgeInsets.all(8.0), - child: LayoutBuilder( - builder: (context, constraints) { - if (image == null) { - return Container(); - } - - - return SizedBox( - width: 200, - height: 200, - child: CustomPaint( - painter: ImagePainter(image!), - size: Size(100, 200), - child: Container(), - ) -, - ); - } - ), - ), - ); - } - -} -class ImagePainter extends CustomPainter { - final ui.Image image; - ImagePainter(this.image); - - @override - void paint(Canvas canvas, Size size) { - paintImage( - canvas: canvas, - rect: Rect.fromLTWH(0, 0, size.width, size.height), - image: image, - ); - //canvas.drawImage(image, Offset.zero, Paint()); - } - - @override - bool shouldRepaint(ImagePainter oldDelegate) { - return true; - } - @override - bool shouldRebuildSemantics(ImagePainter oldDelegate) => false; -} diff --git a/lib/inference/device_selector.dart b/lib/inference/device_selector.dart deleted file mode 100644 index 8ca8e53f..00000000 --- a/lib/inference/device_selector.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/providers/preference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:provider/provider.dart'; - -class DeviceSelector extends StatelessWidget { - const DeviceSelector({super.key}); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - const Text("Device: "), - Consumer(builder: (context, preferences, child) { - return DropdownButton( - onChanged: (value) { - preferences.device = value!; - }, - underline: Container( - height: 0, - ), - style: const TextStyle( - fontSize: 12.0, - ), - focusColor: intelGrayDark, - padding: const EdgeInsets.symmetric(horizontal: 10), - value: preferences.device, - items: PreferenceProvider.availableDevices.map>((value) { - return DropdownMenuItem( - value: value.id, - child: Text(value.name), - ); - }).toList() - ); - } - ), - ], - ); - } -} diff --git a/lib/inference/download_page.dart b/lib/inference/download_page.dart deleted file mode 100644 index c228c99e..00000000 --- a/lib/inference/download_page.dart +++ /dev/null @@ -1,124 +0,0 @@ -import 'dart:math'; - -import 'package:go_router/go_router.dart'; -import 'package:flutter/material.dart'; -import 'package:inference/header.dart'; -import 'package:inference/inference/device_selector.dart'; -import 'package:inference/inference/model_info.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/download_provider.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/public_models.dart'; -import 'package:inference/theme.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; - -String formatBytes(int bytes) { - return "${NumberFormat("#,##0").format(bytes / pow(1024, 2))} MB"; -} - -class DownloadPage extends StatefulWidget { - final PublicProject project; - final Function() onDone; - const DownloadPage(this.project, {required this.onDone, super.key}); - - @override - State createState() => _DownloadPageState(); -} - -class _DownloadPageState extends State { - @override - void initState() { - super.initState(); - - startDownload(); - } - - void startDownload() async { - final downloadProvider = Provider.of(context, listen: false); - final projectProvider = Provider.of(context, listen: false); - - final files = await listDownloadFiles(widget.project); - - try { - await downloadProvider.queue(files, widget.project.modelInfo?.collection.token); - await getAdditionalModelInfo(widget.project); - projectProvider.completeLoading(widget.project); - widget.onDone(); - } catch(e) { - if (mounted) { - showDialog(context: context, builder: (BuildContext context) => AlertDialog( - title: Text('An error occured trying to download ${widget.project.name}'), - content: Text(e.toString()), - actions: [ - TextButton( - onPressed: () => context.go('/'), - child: const Text('Close'), - ), - ] - ), - - ); - } - } - } - - @override - Widget build(BuildContext context) { - return Scaffold( - appBar: const Header(true), - body: Padding( - padding: const EdgeInsets.only(left: 58, right: 58, bottom: 30), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - SizedBox( - width: 250, - child: ModelInfo(widget.project), - ), - Consumer(builder: (context, downloads, child) { - final stats = downloads.stats; - return Expanded( - child: Column( - children: [ - const DeviceSelector(), - Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - CircularProgressIndicator( - backgroundColor: textColor, - value: stats.percentage - ), - SizedBox( - width: 140, - child: Padding( - padding: const EdgeInsets.only(top: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Text(formatBytes(stats.received), textAlign: TextAlign.end,), - const Text("/"), - Text(formatBytes(stats.total)) - ], - ), - ), - ), - const Padding( - padding: EdgeInsets.all(8.0), - child: Text("Downloading model weights"), - ) - ], - ), - ), - ], - ), - ); - }) - ], - ), - ), - ); - } -} diff --git a/lib/inference/image_inference_page.dart b/lib/inference/image_inference_page.dart deleted file mode 100644 index e9b035c5..00000000 --- a/lib/inference/image_inference_page.dart +++ /dev/null @@ -1,126 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:inference/header.dart'; -import 'package:inference/inference/batch_inference.dart'; -import 'package:inference/inference/live_inference.dart'; -import 'package:inference/inference/model_info.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/image_inference_provider.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/providers/preference_provider.dart'; -import 'package:inference/utils/dialogs.dart'; -import 'package:provider/provider.dart'; - -class ImageInferencePage extends StatefulWidget { - final Project project; - const ImageInferencePage(this.project, {super.key}); - - @override - State createState() => _ImageInferencePageState(); -} - -class _ImageInferencePageState extends State - with TickerProviderStateMixin { - late TabController _tabController; - - - void onBack(BuildContext context) { - final inference = Provider.of(context, listen: false); - if (inference.isLocked) { - showDialog( - context: context, - builder: (BuildContext context) => AlertDialog( - title: const Text('Batch inference'), - content: const Text('Batch inference is running currently.'), - actions: [ - TextButton( - onPressed: () => Navigator.pop(context, 'Cancel'), - child: const Text('Cancel'), - ), - ], - ) - ); - } else { - context.go('/'); - } - } - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 2, animationDuration: Duration.zero, vsync: this); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return ChangeNotifierProxyProvider( - lazy: false, - create: (_) { - final device = Provider.of(context, listen: false).device; - return ImageInferenceProvider(widget.project, device)..init().catchError(onExceptionDialog(context)); - }, - update: (_, preferences, imageInferenceProvider) { - if (imageInferenceProvider != null && imageInferenceProvider.device == preferences.device) { - return imageInferenceProvider; - } - return ImageInferenceProvider(widget.project, preferences.device)..init().catchError(onExceptionDialog(context)); - }, - child: Scaffold( - appBar: Header(true, onBack: (context) => onBack(context)), - body: Padding( - padding: const EdgeInsets.only(left: 58, right: 58, bottom: 30), - child: Consumer(builder: (context, projects, child) { - return Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - ModelInfo(widget.project, children: [ - PropertyItem( - name: "Task", - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: widget.project.tasks.map((task) => PropertyValue(task.name)).toList() - ) - ), - ]), - ( - Expanded( - child: Padding( - padding: const EdgeInsets.only(right: 35, left: 35), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( - isScrollable: true, - tabAlignment: TabAlignment.start, - controller: _tabController, - tabs: const [ - Tab(text: "Live inference"), - Tab(text: "Batch inference"), - ], - ), - Expanded( - child: TabBarView( - controller: _tabController, - children: [ - LiveInference(widget.project), - const BatchInference(), - ]), - ) - ], - ), - ), - ) - ) - ], - ); - }), - ), - )); - } -} diff --git a/lib/inference/inference_page.dart b/lib/inference/inference_page.dart deleted file mode 100644 index a9ef3970..00000000 --- a/lib/inference/inference_page.dart +++ /dev/null @@ -1,40 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/inference/download_page.dart'; -import 'package:inference/inference/image_inference_page.dart'; -import 'package:inference/inference/text_inference_page.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/download_provider.dart'; -import 'package:provider/provider.dart'; - -class InferencePage extends StatefulWidget { - final Project project; - const InferencePage(this.project, {super.key}); - - @override - State createState() => _InferencePageState(); -} - -class _InferencePageState extends State { - @override - Widget build(BuildContext context) { - if (widget.project.isDownloaded) { - switch(widget.project.type){ - case ProjectType.image: - return ImageInferencePage(widget.project); - case ProjectType.text: - return TextInferencePage(widget.project); - case ProjectType.textToImage: - return Container(); - case ProjectType.speech: - return Container(); - } - } else { - return ChangeNotifierProvider( - create: (_) => DownloadProvider(), - child: DownloadPage(widget.project as PublicProject, - onDone: () => setState(() {}), //trigger rerender. - ) - ); - } - } -} diff --git a/lib/inference/live_inference.dart b/lib/inference/live_inference.dart deleted file mode 100644 index 0b464165..00000000 --- a/lib/inference/live_inference.dart +++ /dev/null @@ -1,232 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:typed_data'; -import 'dart:ui' as ui; - -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/material.dart'; -import 'package:inference/inference/camera_page.dart'; -import 'package:inference/canvas/canvas.dart'; -import 'package:inference/inference/device_selector.dart'; -import 'package:inference/interop/image_inference.dart'; -import 'package:inference/interop/openvino_bindings.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/image_inference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/utils/drop_area.dart'; -import 'package:provider/provider.dart'; - - -Future createImage(Uint8List bytes) async { - return await decodeImageFromList(bytes); -} - -class LiveInference extends StatefulWidget { - final Project project; - const LiveInference(this.project, {super.key}); - - @override - State createState() => _LiveInferenceState(); -} - -enum MenuButtons { camera, sample, upload } - -class _LiveInferenceState extends State { - - bool loading = false; - bool cameraMode = false; - ImageInferenceResult? inferenceResult; - ui.Image? image; - - void showUploadMenu() async { - FilePickerResult? result = await FilePicker.platform.pickFiles(type: FileType.image); - - if (result != null) { - uploadFile(result.files.single.path!); - } - } - - void uploadFile(String path) async { - Uint8List imageData = File(path).readAsBytesSync(); - setState(() { - inferenceResult = null; - loading = true; - }); - - final inferenceProvider = Provider.of(context, listen: false); - - inferenceProvider.loaded.future.then((_) async{ - final output = await inferenceProvider.infer(imageData, SerializationOutput(json: true)); - final uiImage = await decodeImageFromList(imageData); - setState(() { - loading = false; - inferenceResult = output; - image = uiImage; - }); - }); - - } - - void handleMenu(MenuButtons option) { - switch(option) { - case MenuButtons.camera: - setState(() { - cameraMode = true; - }); - break; - case MenuButtons.sample: - uploadFile(widget.project.samplePath()); - break; - case MenuButtons.upload: - showUploadMenu(); - break; - } - } - - @override - Widget build(BuildContext context) { - return Consumer( - builder: (context, inference, child) { - final isLoading = loading || inference.inference == null; - return Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const DeviceSelector(), - PopupMenuButton( - onSelected: (val) => handleMenu(val), - child: ClipRRect( - borderRadius: BorderRadius.circular(4.0), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: textColor), - borderRadius: BorderRadius.circular(4.0), - color: intelGrayReallyDark, - //color: intelGrayLight, - ), - width: 168, - child: Padding( - padding: const EdgeInsets.only(top: 3, bottom: 3, left: 10, right: 5), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text("Upload image"), - const Icon( - Icons.expand_more, - color: Colors.white, - ), - ], - ), - ) - ), - ), - elevation: 0, - offset: const Offset(0, 35), - shape: RoundedRectangleBorder ( - borderRadius: BorderRadius.circular(4), - side: const BorderSide( - color: textColor, - width: 1, - ) - ), - color: intelGrayReallyDark, - itemBuilder: (BuildContext context) { - if (widget.project.hasSample) { - return >[ - const PopupMenuItem( - height: 20, - value: MenuButtons.sample, - child: MenuButton('Sample image'), - ), - const PopupMenuItem( - height: 20, - value: MenuButtons.upload, - child: MenuButton('Choose an image file'), - ), - //const PopupMenuItem( - // height: 20, - // value: MenuButtons.camera, - // child: MenuButton('Camera'), - //), - ]; - } - return >[ - const PopupMenuItem( - height: 20, - value: MenuButtons.upload, - child: MenuButton('Choose an image file'), - ), - ]; - }, - ), - ], - ), - ), - (isLoading - ? Expanded( - child: Center( - child: Image.asset('images/intel-loading.gif', width: 100) - ) - ) - : Builder( - builder: (context) { - if (cameraMode) { - return CameraPage(inference); - } - return DropArea( - type: "image", - extensions: const ["jpg, jpeg, bmp, png, tif, tiff"], - onUpload: (String path) => uploadFile(path), - showChild: inferenceResult != null, - child: Padding( - padding: const EdgeInsets.all(8.0), - child: Builder( - builder: (context) { - if (inferenceResult == null) { - return Container(); - } - return Canvas( - image: image!, - annotations: inferenceResult!.parseAnnotations(), - labelDefinitions: widget.project.labelDefinitions, - ); - } - ) - ) - ); - } - ) - ) - ], - ); - } - ); - } -} - -class MenuButton extends StatelessWidget { - final String name; - const MenuButton(this.name, { - super.key, - }); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), - child: Text(name, - style: const TextStyle( - fontSize: 10, - ), - ), - ); - - } -} - diff --git a/lib/inference/model_info.dart b/lib/inference/model_info.dart deleted file mode 100644 index 3838834f..00000000 --- a/lib/inference/model_info.dart +++ /dev/null @@ -1,114 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:provider/provider.dart'; - -class ModelInfo extends StatelessWidget { - final List children; - final Project project; - const ModelInfo(this.project, {this.children = const [], super.key}); - - // This widget is the root of your application. - @override - Widget build(BuildContext context) { - return Consumer(builder: (context, projects, child) { - return Padding( - padding: const EdgeInsets.all(8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - PropertyItem( - name: "Model name", - child: PropertyValue(project.name) - ), - PropertyItem( - name: "Architecture", - enabled: project.tasks.firstWhereOrNull((t) => t.architecture.isNotEmpty) != null, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: project.tasks.map((task) => PropertyValue(task.architecture)).toList() - ) - ), - PropertyItem( - name: "Optimization", - enabled: project.tasks.firstWhereOrNull((t) => t.optimization.isNotEmpty) != null, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: project.tasks.map((task) => PropertyValue(task.optimization)).toList() - ) - ), - ...children - ], - ), - ); - }); - } -} - -class PropertyItem extends StatelessWidget { - final String name; - final Widget child; - final bool enabled; - const PropertyItem({required this.name, required this.child, this.enabled = true, super.key}); - - @override - Widget build(BuildContext context) { - if (!enabled) { - return Container(); - } - return Padding( - padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: PropertyHeader(name), - ), - child - ] - ), - ); - } -} - -class PropertyHeader extends StatelessWidget { - final String text; - const PropertyHeader(this.text, {super.key}); - - @override - Widget build(BuildContext context) { - return Text(text, style: const TextStyle( - color: Colors.white, - fontSize: 19, - )); - } - -} - -class PropertyValue extends StatelessWidget { - final String text; - const PropertyValue(this.text, {super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 8.0, right: 8.0), - child: Container( - decoration: BoxDecoration( - border: Border.all(color: textColor), - borderRadius: BorderRadius.circular(4.0), - color: intelGrayReallyDark, - ), - child: Padding( - padding: const EdgeInsets.symmetric(vertical: 3, horizontal: 6), - child: Text(text, style: const TextStyle( - fontSize: 12, - )), - ) - ), - ); - } -} diff --git a/lib/inference/text/metric_widgets.dart b/lib/inference/text/metric_widgets.dart deleted file mode 100644 index 830be84f..00000000 --- a/lib/inference/text/metric_widgets.dart +++ /dev/null @@ -1,147 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/interop/openvino_bindings.dart'; -import 'package:inference/theme.dart'; -import 'package:intl/intl.dart'; - -class CirclePropRow extends StatelessWidget { - final Metrics metrics; - const CirclePropRow({super.key, required this.metrics}); - - @override - Widget build(BuildContext context) { - Locale locale = Localizations.localeOf(context); - final nf = NumberFormat.decimalPatternDigits( - locale: locale.languageCode, decimalDigits: 0); - - return Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CircleProp( - header: "Time to first token (ttft)", - value: nf.format(metrics.ttft), - unit: "ms", - ), - CircleProp( - header: "Time per output token (tpot)", - value: nf.format(metrics.tpot), - unit: "ms", - ), - CircleProp( - header: "Generate total duration", - value: nf.format(metrics.generate_time), - unit: "ms", - ) - ], - ); - } -} - -class CircleProp extends StatelessWidget { - final String header; - final String value; - final String unit; - - const CircleProp({super.key, required this.header, required this.value, required this.unit}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(16.0), - child: Container( - width: 250.0, - height: 250.0, - decoration: BoxDecoration( - shape: BoxShape.circle, - color: intelGrayDark, - border: Border.all( - color: intelBlueDark, - width: 10, - ), - ), - child: Column( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Padding( - padding: const EdgeInsets.only(top: 36.0), - child: SizedBox( - width: 170, - child: Text(header, - style: const TextStyle( - fontSize: 20, - ), - textAlign: TextAlign.center, - ), - ), - ), - Padding( - padding: const EdgeInsets.only(bottom: 46.0), - child: Row( - textBaseline: TextBaseline.alphabetic, - crossAxisAlignment: CrossAxisAlignment.baseline, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Text(value, - style: const TextStyle( - fontSize: 30, - ), - ), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text(unit, - style: const TextStyle( - fontSize: 12, - ), - ), - ), - ], - ), - ), - ], - ) - - ), - ); - } -} - -class Statistic extends StatelessWidget { - const Statistic({ - super.key, - required this.header, - required this.value, - required this.unit, - }); - - final String header; - final String value; - final String unit; - - @override - Widget build(BuildContext context) { - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text(header), - Row( - textBaseline: TextBaseline.alphabetic, - crossAxisAlignment: CrossAxisAlignment.baseline, - children: [ - Text(value, - style: const TextStyle( - fontSize: 30, - ) - ), - Padding( - padding: const EdgeInsets.only(left: 8.0), - child: Text(unit, - style: const TextStyle( - fontSize: 12, - ) - ), - ), - ], - ), - ] - ); - } -} diff --git a/lib/inference/text/performance_metrics.dart b/lib/inference/text/performance_metrics.dart deleted file mode 100644 index 6fde59e2..00000000 --- a/lib/inference/text/performance_metrics.dart +++ /dev/null @@ -1,97 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/inference/text/metric_widgets.dart'; -import 'package:inference/providers/text_inference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/utils/dialogs.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; - -class PerformanceMetricsPage extends StatefulWidget { - const PerformanceMetricsPage({super.key}); - - @override - State createState() => _PerformanceMetricsPageState(); -} - -class _PerformanceMetricsPageState extends State { - - @override - void initState() { - super.initState(); - final provider = Provider.of(context, listen: false); - if (provider.metrics == null) { - provider.loaded.future.then((_) { - provider.message("What is the purpose of OpenVINO?").catchError(onExceptionDialog(context)); - }); - } - } - - @override - Widget build(BuildContext context) { - return Consumer(builder: (context, inference, child) { - if (inference.metrics == null) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset('images/intel-loading.gif', width: 100), - const Text("Running benchmark prompt...") - ], - ) - ); - } - - Locale locale = Localizations.localeOf(context); - final nf = NumberFormat.decimalPatternDigits( - locale: locale.languageCode, decimalDigits: 0); - - final metrics = inference.metrics!; - - return Container( - decoration: BoxDecoration( - shape: BoxShape.rectangle, - borderRadius: const BorderRadius.all(Radius.circular(8)), - color: intelGray, - ), - child: Align( - alignment: Alignment.topLeft, - child: ConstrainedBox( - constraints: const BoxConstraints(maxWidth: 1000), - child: Padding( - padding: const EdgeInsets.all(30.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - CirclePropRow(metrics: metrics), - Expanded( - child: Padding( - padding: const EdgeInsets.only(top: 20.0), - child: GridView.count( - physics: const NeverScrollableScrollPhysics(), - childAspectRatio: 2.0, - padding: const EdgeInsets.only(right: 20.0), - crossAxisSpacing: 4.0, - crossAxisCount: 3, - children: [ - Statistic(header: "Tokenization duration", value: nf.format(metrics.tokenization_time), unit: "ms"), - Statistic(header: "Detokenization duration", value: nf.format(metrics.detokenization_time), unit: "ms"), - Statistic(header: "Generated tokens", value: nf.format(metrics.number_of_generated_tokens), unit: ""), - Statistic(header: "Load time", value: nf.format(metrics.load_time), unit: "ms"), - Statistic(header: "Tokens in the input prompt", value: nf.format(metrics.number_of_input_tokens), unit: ""), - Statistic(header: "Throughput", value: nf.format(metrics.throughput), unit: "tokens/sec"), - ] - ), - ), - ) - ], - ), - ), - ), - ), - ); - }); - } -} - - diff --git a/lib/inference/text/playground.dart b/lib/inference/text/playground.dart deleted file mode 100644 index 6c64dec8..00000000 --- a/lib/inference/text/playground.dart +++ /dev/null @@ -1,426 +0,0 @@ - -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:inference/config.dart'; -import 'package:inference/hint.dart'; -import 'package:inference/inference/device_selector.dart'; -import 'package:inference/inference/text/metric_widgets.dart'; -import 'package:inference/interop/openvino_bindings.dart'; -import 'package:inference/providers/text_inference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/utils/dialogs.dart'; -import 'package:provider/provider.dart'; - -class Playground extends StatefulWidget { - const Playground({super.key}); - - @override - State createState() => _PlaygroundState(); -} - -class _PlaygroundState extends State { - final _controller = TextEditingController(); - final _scrollController = ScrollController(); - bool attachedToBottom = true; - - void jumpToBottom({ offset = 0 }) { - if (_scrollController.hasClients) { - _scrollController.jumpTo(_scrollController.position.maxScrollExtent + offset); - } - } - - void message(String message) async { - if (message.isEmpty) { - return; - } - final llm = provider(); - if (!llm.initialized) { - return; - } - - if (llm.response != null) { - return; - } - _controller.text = ""; - jumpToBottom(offset: 110); //move to bottom including both - llm.message(message).catchError(onExceptionDialog(context)); - } - - TextInferenceProvider provider() => Provider.of(context, listen: false); - - @override - void initState() { - super.initState(); - _scrollController.addListener(() { - setState(() { - attachedToBottom = _scrollController.position.pixels + 0.001 >= _scrollController.position.maxScrollExtent; - }); - - }); - } - - @override - void dispose() { - super.dispose(); - _controller.dispose(); - _scrollController.dispose(); - } - - - @override - Widget build(BuildContext context) { - return Consumer(builder: (context, inference, child) { - WidgetsBinding.instance.addPostFrameCallback((_) { - if (attachedToBottom) { - jumpToBottom(); - } - }); - - return Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Padding( - padding: const EdgeInsets.only(left: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: const [ - DeviceSelector(), - Hint(hint: HintsEnum.intelCoreLLMPerformanceSuggestion), - ] - ), - ), - Builder( - builder: (context) { - if (!inference.initialized){ - return Expanded( - child: Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Image.asset('images/intel-loading.gif', width: 100), - const Text("Loading model...") - ], - ) - ), - ); - } - return Expanded( - child: Container( - decoration: BoxDecoration( - shape: BoxShape.rectangle, - borderRadius: const BorderRadius.all(Radius.circular(8)), - color: intelGray, - ), - child: Column( - children: [ - Expanded( - child: Builder(builder: (context) { - if (inference.messages.isEmpty) { - return Center( - child: Text("Type a message to ${inference.project?.name ?? "assistant"}")); - } - return Stack( - alignment: Alignment.bottomCenter, - children: [ - SingleChildScrollView( - controller: _scrollController, - child: Padding( - padding: const EdgeInsets.all(20), - child: Column( - //mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: inference.messages.map((message) { - switch (message.speaker) { - case Speaker.system: - throw UnimplementedError(); - case Speaker.user: - return UserMessage(message); - case Speaker.assistant: - return AssistantMessage(message, inference.project!.name); - } - }).toList()), - ), - ), - Positioned( - bottom: 10, - child: Builder( - builder: (context) { - if (attachedToBottom) { - return Container(); - } - return Center( - child: Padding( - padding: const EdgeInsets.only(top: 2.0), - child: SizedBox( - width: 200, - height: 20, - child: FloatingActionButton( - backgroundColor: intelGray, - child: const Text("Jump to bottom"), - onPressed: () { - jumpToBottom(); - setState(() { - attachedToBottom = true; - }); - } - ), - ), - ), - ); - } - ), - ), - - ], - ); - }), - ), - - SizedBox( - height: 30, - child: Builder( - builder: (context) { - if (inference.interimResponse == null){ - return Container(); - } - return Center( - child: OutlinedButton.icon( - onPressed: () => inference.forceStop(), - icon: const Icon(Icons.stop), - label: const Text("Stop responding") - ), - ); - } - ), - ), - Padding( - padding: const EdgeInsets.only(left: 45, right: 45, top: 10, bottom: 25), - child: SizedBox( - height: 40, - child: Row( - crossAxisAlignment: CrossAxisAlignment.end, - children: [ - Padding( - padding: const EdgeInsets.only(right: 8), - child: IconButton( - icon: SvgPicture.asset("images/clear.svg", - colorFilter: const ColorFilter.mode(textColor, BlendMode.srcIn), - width: 20, - ), - tooltip: "Clear chat", - onPressed: () => inference.reset(), - style: IconButton.styleFrom( - backgroundColor: intelGrayReallyDark, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - side: BorderSide( - color: intelGrayLight, - width: 2, - ) - ) - ) - ), - ), - Expanded( - child: TextField( - maxLines: null, - keyboardType: TextInputType.text, - decoration: InputDecoration( - hintText: "Ask me anything...", - suffixIcon: IconButton( - icon: Icon(Icons.send, color: (inference.interimResponse == null ? Colors.white : intelGray)), - onPressed: () => message(_controller.text), - ), - enabledBorder: const OutlineInputBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - borderSide: BorderSide( - color: intelGrayLight, - width: 2, - ) - ), - ), - style: const TextStyle( - fontSize: 14, - ), - controller: _controller, - onSubmitted: message, - ), - ), - ], - ), - ), - ), - ], - ), - ), - ); - } - ), - ], - ); - }); - } -} - -class UserMessage extends StatelessWidget { - final Message message; - const UserMessage(this.message, {super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - NameRowWidget(name: "You", icon: SvgPicture.asset("images/user.svg", - colorFilter: const ColorFilter.mode(textColor, BlendMode.srcIn), - width: 20, - ), - ), - MessageWidget(message: message.message), - ], - ), - ); - } -} - -class AssistantMessage extends StatelessWidget { - final Message message; - final String name; - const AssistantMessage(this.message, this.name, {super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(bottom: 20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - NameRowWidget( - name: name, - icon: SvgPicture.asset("images/network.svg", - colorFilter: const ColorFilter.mode(textColor, BlendMode.srcIn), - width: 20, - ), - ), - MessageWidget(message: message.message), - Padding( - padding: const EdgeInsets.only(left: 28, top: 5), - child: Builder( - builder: (context) { - if (message.metrics == null) { - return Container(); - } - return Row( - children: [ - IconButton.filled( - icon: SvgPicture.asset("images/copy.svg", - colorFilter: const ColorFilter.mode(textColor, BlendMode.srcIn), - width: 20, - ), - style: IconButton.styleFrom( - backgroundColor: intelGrayLight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - ), - padding: const EdgeInsets.all(4), - constraints: const BoxConstraints(), - tooltip: "Copy to clipboard", - onPressed: () { - Clipboard.setData(ClipboardData(text: message.message)); - }, - ), - Padding( - padding: const EdgeInsets.only(left: 8), - child: IconButton( - style: IconButton.styleFrom( - backgroundColor: intelGrayLight, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.all(Radius.circular(4)), - ), - ), - padding: const EdgeInsets.all(4), - constraints: const BoxConstraints(), - icon: SvgPicture.asset("images/stats.svg", - colorFilter: const ColorFilter.mode(textColor, BlendMode.srcIn), - width: 20, - ), - tooltip: "Show stats", - onPressed: () { - showMetricsDialog(context, message.metrics!); - }, - ), - ), - ], - ); - } - ), - ), - ], - ), - ); - } -} - -void showMetricsDialog(BuildContext context, Metrics metrics) { - showDialog( - context: context, - builder: (BuildContext context) { - return AlertDialog( - content: CirclePropRow( - metrics: metrics - ) - ); - } - ); -} - -class NameRowWidget extends StatelessWidget { - final String name; - final Widget icon; - const NameRowWidget({super.key, required this.name, required this.icon}); - - @override - Widget build(BuildContext context) { - return Row( - children: [ - Container( - padding: const EdgeInsets.all(2), - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(4.0), - color: intelBlueVibrant, - //color: intelGrayLight, - ), - child: icon - ), - Padding( - padding: const EdgeInsets.only(left: 10.0), - child: Text(name), - ) - ] - ); - } -} - -class MessageWidget extends StatelessWidget { - final String message; - const MessageWidget({super.key, required this.message}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(left: 34.0, top: 10, right: 26), - child: SelectableText( - message, - style: const TextStyle( - color: textColor, - fontSize: 12, - ), - ), - ); - } - -} diff --git a/lib/inference/text_inference_page.dart b/lib/inference/text_inference_page.dart deleted file mode 100644 index afe0e5cd..00000000 --- a/lib/inference/text_inference_page.dart +++ /dev/null @@ -1,165 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/header.dart'; -import 'package:inference/inference/model_info.dart'; -import 'package:inference/inference/text/performance_metrics.dart'; -import 'package:inference/inference/text/playground.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/preference_provider.dart'; -import 'package:inference/providers/text_inference_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/utils/dialogs.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; - -class TextInferencePage extends StatefulWidget { - final Project project; - const TextInferencePage(this.project, {super.key}); - - @override - State createState() => _TextInferencePageState(); -} - -class _TextInferencePageState extends State with TickerProviderStateMixin { - - late TabController _tabController; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 2, animationDuration: Duration.zero, vsync: this); - } - - @override - void dispose() { - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - Locale locale = Localizations.localeOf(context); - - return ChangeNotifierProxyProvider( - create: (_) { - return TextInferenceProvider(widget.project, null); - }, - update: (_, preferences, textInferenceProvider) { - final init = textInferenceProvider == null || - !textInferenceProvider.sameProps(widget.project, preferences.device); - if (init) { - final textInferenceProvider = TextInferenceProvider(widget.project, preferences.device); - textInferenceProvider.loadModel().catchError(onExceptionDialog(context)); - return textInferenceProvider; - } - if (!textInferenceProvider.sameProps(widget.project, preferences.device)) { - return TextInferenceProvider(widget.project, preferences.device); - } - return textInferenceProvider; - }, - child: Scaffold( - appBar: const Header(true), - body: Padding( - padding: const EdgeInsets.only(left: 58, right: 58, bottom: 30), - child: Row( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Consumer( - builder: (context, inference, child) { - final nf = NumberFormat.decimalPatternDigits( - locale: locale.languageCode, decimalDigits: 2); - - return SizedBox( - width: 250, - child: ModelInfo( - widget.project, - children: [ - PropertyItem( - name: "Task", - child: PropertyValue(inference.task), - ), - Padding( - padding: const EdgeInsets.only(left: 12, top: 12, right: 20.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text("Temperature"), - Text(nf.format(inference.temperature)) - ] - ), - Slider( - value: inference.temperature, - max: 2.0, - onChanged: (double value) { - inference.temperature = value; - }, - - ), - ], - ), - ), - Padding( - padding: const EdgeInsets.only(left: 12, top: 12, right: 20.0), - child: Column( - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - const Text("Top P"), - Text(nf.format(inference.topP)) - ] - ), - Slider( - value: inference.topP, - max: 1.0, - onChanged: (double value) { - inference.topP = value; - }, - - ), - ], - ), - ), - ] - ), - ); - }), - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - TabBar( - isScrollable: true, - tabAlignment: TabAlignment.start, - controller: _tabController, - tabs: const [ - Tab(text: "Playground"), - Tab(text: "Performance metrics"), - //Tab(text: "Deploy"), - ] - ), - Expanded( - child: Padding( - padding: const EdgeInsets.only(top: 15.0), - child: TabBarView( - controller: _tabController, - children: [ - const Playground(), - const PerformanceMetricsPage(), - //Container(), - ] - ), - ) - ), - ], - ), - ) - ], - ), - ), - ), - ); - } -} - diff --git a/lib/openvino_console_app.dart b/lib/openvino_console_app.dart index 4cc19a85..8b3b97fe 100644 --- a/lib/openvino_console_app.dart +++ b/lib/openvino_console_app.dart @@ -1,12 +1,7 @@ import 'package:go_router/go_router.dart'; -import 'package:flutter/material.dart'; import 'package:inference/deployment_processor.dart'; -import 'package:inference/import/import_page.dart'; -import 'package:inference/inference/inference_page.dart'; import 'package:inference/interop/device.dart'; import 'package:inference/interop/image_inference.dart'; -import 'package:inference/project.dart'; -import 'package:inference/projects/projects_page.dart'; import 'package:inference/providers/preference_provider.dart'; import 'package:inference/providers/project_provider.dart'; import 'package:inference/theme_fluent.dart'; @@ -14,25 +9,6 @@ import 'package:inference/utils.dart'; import 'package:provider/provider.dart'; import 'package:fluent_ui/fluent_ui.dart'; -final GoRouter _router = GoRouter( - routes: [ - GoRoute( - path: '/', - pageBuilder: (context, state) => const NoTransitionPage(child: ProjectsPage()), - routes: [ - GoRoute( - path: 'inference', - pageBuilder: (context, state) => NoTransitionPage(child: InferencePage(state.extra! as Project)), - ), - GoRoute( - path: 'import', - pageBuilder: (context, state) => const NoTransitionPage(child: ImportPage()), - ), - ], - ), - ], -); - class OpenVINOTestDriveApp extends StatefulWidget { const OpenVINOTestDriveApp({ @@ -63,7 +39,6 @@ class _OpenVINOTestDriveAppState extends State { setupErrors(); Device.getDevices().then((devices) { - devices.forEach((p) => print("${p.id}, ${p.name}")); PreferenceProvider.availableDevices = devices; }); diff --git a/lib/pages/computer_vision/live_inference.dart b/lib/pages/computer_vision/live_inference.dart index 9c78f25c..c779c4dd 100644 --- a/lib/pages/computer_vision/live_inference.dart +++ b/lib/pages/computer_vision/live_inference.dart @@ -13,7 +13,7 @@ import 'package:inference/theme_fluent.dart'; import 'package:inference/widgets/device_selector.dart'; import 'package:inference/widgets/controls/drop_area.dart'; import 'package:inference/widgets/controls/no_outline_button.dart'; -import 'package:inference/canvas/canvas.dart'; +import 'package:inference/widgets/canvas/canvas.dart'; import 'package:provider/provider.dart'; import 'dart:ui' as ui; diff --git a/lib/projects/project_item.dart b/lib/projects/project_item.dart deleted file mode 100644 index f735fa6b..00000000 --- a/lib/projects/project_item.dart +++ /dev/null @@ -1,244 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/download_provider.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/public_models.dart'; -import 'package:inference/theme.dart'; -import 'package:inference/color.dart'; -import 'package:intl/intl.dart'; -import 'package:provider/provider.dart'; - -enum MenuButtons { delete } - -class ProjectItem extends StatefulWidget { - final Project project; - const ProjectItem(this.project, {super.key}); - - @override - State createState() => _ProjectItemState(); -} - -class _ProjectItemState extends State { - final DateFormat formatter = DateFormat('dd MMMM y | h:mm a'); - bool hovered = false; - - void onHover(bool? val) { - setState(() { - hovered = val ?? false; - }); - } - - Color backgroundColor(BuildContext context) { - final scheme = Theme.of(context).colorScheme; - if (hovered) { - return scheme.onSurfaceVariant; - } else { - return scheme.surfaceContainer; - } - } - - void onTap(Project project) async { - if (project.isPublic || project.loaded.isCompleted) { - goToProject(project); - } - } - - void goToProject(Project project) { - // uncomment this to check the other downloading style... - //if (project.isDownloaded) { // or something. - context.go('/inference', extra: project); - //} else { - // createDirectory(project as PublicProject); - // writeProjectJson(project); - - // Provider.of(context, listen: false).queue(llmDownloadFiles(project)).then((_) { - // getAdditionalModelInfo(project).then((_) { - // Provider.of(context, listen: false).completeLoading(project); - // }); - // }); - //} - } - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 8, bottom: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: Consumer(builder: (context, projects, child) { - return InkWell( - borderRadius: BorderRadius.circular(8.0), - onHover: (val) => onHover(val), - onTap: () => onTap(widget.project), - child: Opacity( - opacity: (widget.project is PublicProject || - widget.project.loaded.isCompleted - ? 1.0 - : 0.5), - child: Stack( - alignment: Alignment.bottomLeft, - children: [ - ConstrainedBox( - constraints: const BoxConstraints.expand(height: 136), - child: Row( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Container( - width: 288, - decoration: BoxDecoration( - image: DecorationImage( - image: widget.project.thumbnailImage(), - fit: BoxFit.cover), - ), - ), - Expanded( - child: Container( - color: backgroundColor(context), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Row( - children: [ - Text(widget.project.name, - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 16, - )), - Text(" @ ${widget.project.taskName()}") - ], - ), - (widget.project.isDownloaded - ? Row( - children: [ - (widget.project is GetiProject && widget.project.type != ProjectType.text - ? Row(children: (widget.project as GetiProject) - .scores() - .map((p) { - if (p == null) { - return Container(); - } - return ScoreItem(p); - }).toList()) - : Container()), - PopupMenuButton( - iconColor: textColor, - iconSize: 16, - onSelected: (MenuButtons _) => projects.removeProject(widget .project), - color: intelGrayDark, - elevation: 1, - itemBuilder: (BuildContext context) { - return >[ - const PopupMenuItem( - value: MenuButtons .delete, - child: Text('Delete'), - ), - ]; - }, - ) - ], - ) - : Container()) - ], - ), - Text( - "Created: ${formatter.format(DateTime.parse(widget.project.creationTime))}"), - const Divider(color: intelGrayLight), - Row( - children: (widget.project.labels().length > 4 - ? [ - ...(widget.project - .labels() - .sublist(0, 4)) - .map((label) => - LabelItem(label)), - const Text(" ...and more") - ] - : widget.project - .labels() - .map((label) => - LabelItem(label)) - .toList())) - ], - ), - ), - ), - ), - ], - ), - ), - ], - ), - ), - ); - })), - ); - } -} - -class ScoreItem extends StatelessWidget { - final Score score; - const ScoreItem(this.score, {super.key}); - - @override - Widget build(BuildContext context) { - Locale locale = Localizations.localeOf(context); - return SizedBox( - width: 40, - child: Column(children: [ - Text(NumberFormat.percentPattern(locale.languageCode) - .format(score.score)), - Padding( - padding: const EdgeInsets.only(left: 7.0, right: 7.0), - child: Stack( - children: [ - const Divider( - color: Colors.white, - height: 2, - thickness: 2, - ), - SizedBox( - width: (score.score * (26)), - child: Divider( - color: getScoreColor(score.score), - height: 2, - thickness: 2)), - ], - ), - ) - ]), - ); - } -} - -class LabelItem extends StatelessWidget { - final Label label; - const LabelItem(this.label, {super.key}); - - @override - Widget build(BuildContext context) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Padding( - padding: const EdgeInsets.only(left: 8.0, right: 8.0), - child: ClipRRect( - borderRadius: BorderRadius.circular(2), - child: Container( - color: HexColor.fromHex(label.color.substring(0, 7)), - width: 10, - height: 10, - )), - ), - Text(label.name), - ], - ); - } -} diff --git a/lib/projects/projects_page.dart b/lib/projects/projects_page.dart deleted file mode 100644 index ac6daf5d..00000000 --- a/lib/projects/projects_page.dart +++ /dev/null @@ -1,299 +0,0 @@ -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:go_router/go_router.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter_svg/svg.dart'; -import 'package:inference/config.dart'; -import 'package:inference/header.dart'; -import 'package:inference/importers/importer.dart'; -import 'package:inference/project.dart'; -import 'package:inference/providers/project_filter_provider.dart'; -import 'package:inference/providers/project_provider.dart'; -import 'package:inference/searchbar.dart'; -import 'package:inference/projects/project_item.dart'; -import 'package:inference/theme.dart'; -import 'package:provider/provider.dart'; -import 'package:shimmer/shimmer.dart'; - -class ProjectsPage extends StatefulWidget { - const ProjectsPage({super.key}); - - @override - State createState() => _ProjectPageState(); -} - -class _ProjectPageState extends State with TickerProviderStateMixin { - void showErrorDialog(String error, String stack) { - showDialog(context: context, builder: (BuildContext context) => AlertDialog( - title: Text('A critical error occured: $error'), - content: Text(stack), - actions: [ - TextButton( - onPressed: () => Navigator.of(context).pop(), - child: const Text('Close'), - ), - ] - ) - ); - } - - @override - void initState() { - super.initState(); - - FlutterError.onError = (details) { - FlutterError.presentError(details); - //showErrorDialog(details.exception.toString(), details.stack.toString()); - }; - //PlatformDispatcher.instance.onError = (error, stack) { - // showErrorDialog(error.toString(), stack.toString()); - // return true; - //}; - } - - void addProject(BuildContext context) async { - - final result = await FilePicker.platform.pickFiles(allowMultiple: true); - - if (result != null) { - for (final file in result.files) { - final importer = selectMatchingImporter(file.path!); - if (importer == null) { - print("unable to process file"); - return; - } - if (context.mounted) { - if (!await importer.askUser(context)) { - print("cancelling due to user input"); - return; - } - } - importer.generateProject().then((project) async { - await importer.setupFiles(); - project.loaded.future.then((_) { - if (context.mounted) { - Provider.of(context, listen: false) - .completeLoading(project); - } - }); - if (context.mounted) { - final projectsProvider = Provider.of(context, listen: false); - projectsProvider.addProject(project); - } - }); - } - } else { - // User canceled the picker - } - } - - @override - Widget build(BuildContext context) { - return ChangeNotifierProvider( - create: (context) => ProjectFilterProvider(), - child: Scaffold( - appBar: const Header(false), - body: Consumer(builder: (context, projects, child) { - return Padding( - padding: const EdgeInsets.only(bottom: 10), - child: Consumer(builder: (context, filter, child) { - final selectedProjects = filter.applyFilter(projects.projects); - return Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - Padding( - padding: const EdgeInsets.symmetric(horizontal: 60, vertical: 8), - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - GetiSearchBar( - onChange: (value) => filter.name = value, - ), - (Config.geti - ? ElevatedButton( - onPressed: () => addProject(context), - child: const Text("Import Model") - ) - : ImportModelButton( - onHuggingFace: () => context.go("/import"), - onLocal: () => addProject(context), - ) - ) - ]), - ), - Expanded( - child: GetiProjectsList(selectedProjects.toList()) - ) - ] - ); - }), - ); - })), - ); - } -} - -enum MenuButtons { huggingface, local } - -class ImportModelButton extends StatelessWidget { - final Function() onHuggingFace; - final Function() onLocal; - const ImportModelButton({required this.onHuggingFace, required this.onLocal, super.key}); - - @override - Widget build(BuildContext context) { - return PopupMenuButton( - onSelected: (MenuButtons btn) { - switch(btn.name) { - case "huggingface": onHuggingFace(); - case "local": onLocal(); - default: throw UnimplementedError(); - } - }, - offset: const Offset(0, 40), - color: intelGrayVariant, - elevation: 20, - itemBuilder: (BuildContext context) { - return >[ - PopupMenuItem( - value: MenuButtons.huggingface, - child: Row( - children: [ - Padding( - padding: const EdgeInsets.only(right: 8.0), - child: SvgPicture.asset('images/hf-logo.svg', width: 18), - ), - const Text('Huggingface'), - ], - ), - ), - const PopupMenuItem( - value: MenuButtons.local, - child: Row( - children: [ - Padding( - padding: EdgeInsets.only(right: 8.0), - child: Icon(Icons.folder_open, color: Color.fromRGBO(248, 212, 78, 1.0), size: 18), - ), - Text('Local disk'), - ], - ), - ), - ]; - }, - child: Card( - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.0), - ), - color: intelBlueVibrant, - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8.0), - child: const Text("Import Model", - style: TextStyle( - color: Colors.white - ), - ), - ) - ), - ); - } - -} - -class GetiProjectsList extends StatelessWidget { - final List projects; - const GetiProjectsList(this.projects, {super.key}); - - @override - Widget build(BuildContext context) { - if (projects.isEmpty) { - return Center( - child: SizedBox( - height: 450, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.start, - children: [ - SvgPicture.asset('images/upload_art.svg'), - const Text("No imported models", style: TextStyle( - fontSize: 20, - )), - const Text("Import a Geti deployment using the import model button") - ], - ), - ) - ); - } - - return SingleChildScrollView( - child: Column( - children: projects.map((project) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 60), - child: ProjectItem(project), - ); - }).toList() - ) - ); - } -} - -class PublicProjectsList extends StatelessWidget { - final List projects; - final bool publicLoaded; - const PublicProjectsList(this.projects, this.publicLoaded, {super.key}); - - @override - Widget build(BuildContext context) { - if (projects.isEmpty) { - return Container(); - } - - return SingleChildScrollView( - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ...projects.map((project) => ProjectItem(project)), - ...(publicLoaded - ? [Container()] - : [ - const GhostProjectItem(), - const GhostProjectItem(), - const GhostProjectItem() - ] - ) - ] - ) - ); - } -} - -class GhostProjectItem extends StatelessWidget { - const GhostProjectItem({super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(top: 8), - child: ClipRRect( - borderRadius: BorderRadius.circular(8.0), - child: Opacity( - opacity: 1.0, - child: ConstrainedBox( - constraints: const BoxConstraints.expand(height: 136), - child: Shimmer.fromColors( - baseColor: Theme.of(context).colorScheme.surface, - highlightColor: Theme.of(context).colorScheme.onSurfaceVariant, - child: Container( - color: Theme.of(context).colorScheme.surface, - ) - ), - ), - ) - ) - ); - } -} diff --git a/lib/projects/task_type_filter.dart b/lib/projects/task_type_filter.dart deleted file mode 100644 index 46ab909f..00000000 --- a/lib/projects/task_type_filter.dart +++ /dev/null @@ -1,133 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:inference/providers/project_filter_provider.dart'; -import 'package:inference/theme.dart'; -import 'package:provider/provider.dart'; - -class TaskTypeFilter extends StatelessWidget { - const TaskTypeFilter({super.key}); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.only(right: 50.0), - child: SizedBox( - width: 200, - child: Column( - mainAxisAlignment: MainAxisAlignment.start, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - ...Option.filterOptions.keys.map((key) { - return Group(key, Option.filterOptions[key]!); - - }), //options.map((e) { - ] - ), - ), - ); - } -} - -class Group extends StatefulWidget { - final String name; - final List