diff --git a/.vscode/settings.json b/.vscode/settings.json index 92479997..4cbc0787 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,23 +9,23 @@ "**/.fvm/flutter_sdk": true }, "workbench.colorCustomizations": { - "activityBar.activeBackground": "#2f7c47", - "activityBar.background": "#2f7c47", - "activityBar.foreground": "#e7e7e7", - "activityBar.inactiveForeground": "#e7e7e799", - "activityBarBadge.background": "#422c74", + "activityBar.activeBackground": "#3399ff", + "activityBar.background": "#3399ff", + "activityBar.foreground": "#15202b", + "activityBar.inactiveForeground": "#15202b99", + "activityBarBadge.background": "#bf0060", "activityBarBadge.foreground": "#e7e7e7", "commandCenter.border": "#e7e7e799", - "sash.hoverBorder": "#2f7c47", - "statusBar.background": "#215732", + "sash.hoverBorder": "#3399ff", + "statusBar.background": "#007fff", "statusBar.foreground": "#e7e7e7", - "statusBarItem.hoverBackground": "#2f7c47", - "statusBarItem.remoteBackground": "#215732", + "statusBarItem.hoverBackground": "#3399ff", + "statusBarItem.remoteBackground": "#007fff", "statusBarItem.remoteForeground": "#e7e7e7", - "titleBar.activeBackground": "#215732", + "titleBar.activeBackground": "#007fff", "titleBar.activeForeground": "#e7e7e7", - "titleBar.inactiveBackground": "#21573299", + "titleBar.inactiveBackground": "#007fff99", "titleBar.inactiveForeground": "#e7e7e799" }, - "peacock.color": "#215732" + "peacock.color": "#007fff" } \ No newline at end of file diff --git a/README.md b/README.md index ec6bc7c9..d4f992d3 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ -![Superdeck logo](./assets/logo.png) + +![Superdeck logo](./assets/logo-dark.png#gh-dark-mode-only) +![Superdeck logo](./assets/logo-light.png#gh-light-mode-only) SuperDeck enables you to craft visually appealing and interactive presentations directly within your Flutter apps, using the simplicity and power of Markdown. @@ -8,8 +10,6 @@ SuperDeck enables you to craft visually appealing and interactive presentations ### [Example code](https://github.com/leoafarias/superdeck/blob/main/example/slides.md) - - ## Getting Started Follow these steps to integrate SuperDeck into your Flutter project: @@ -26,7 +26,8 @@ Follow these steps to integrate SuperDeck into your Flutter project: import 'package:superdeck/superdeck.dart'; ``` -3. Initialize SuperDeck and run the app. +3. Initialize SuperDeck and run the app. + ```dart void main() { runApp(const SuperDeckApp()); @@ -44,49 +45,39 @@ Follow these steps to integrate SuperDeck into your Flutter project: - assets/images/ ``` -The `assets` directory is used to slide and asset references, while the `assets/images` directory is specifically used for storing images used in your presentations. + The `assets` directory is used to slide and asset references, while the `assets/images` directory is specifically used for storing images used in your presentations. 6. Configure your app -MacOS - -Change your `Release.entitlements` - -```xml - - com.apple.security.app-sandbox - - com.apple.security.network.client - - -``` - -Change `DebugProfile.entitlements` - -```xml - - com.apple.security.app-sandbox - - com.apple.security.cs.allow-jit - - com.apple.security.network.server - - com.apple.security.network.client - - -``` - - - - - - - + MacOS + Change your `Release.entitlements` + ```xml + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + ``` + Change `DebugProfile.entitlements` + + ```xml + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + ``` -6. Start building your slides in the `slides.md` file using Markdown syntax and SuperDeck's slide templates and configurations. +7. Start building your slides in the `slides.md` file using Markdown syntax and SuperDeck's slide templates and configurations. ### SuperDeck Options @@ -113,6 +104,7 @@ SuperDeckApp( ``` ### Shared Slide Options + Some shared options can be applied by adding them to a `superdeck.yaml` file in the root of your project. These options will be applied to all slides unless overridden by slide-specific options. ### Slide Options @@ -245,9 +237,9 @@ layout: two_column_header #### Sections -- `::header::`: The content that will be placed in the header section. -- `::left::`: The content that will be placed in the left column. -- `::right::`: The content that will be placed in the right column. +- `::header::`: The content that will be placed in the header section. +- `::left::`: The content that will be placed in the left column. +- `::right::`: The content that will be placed in the right column. If the first tag that is found is `::left::`, everything before this tag will be placed in the `::header::` section. @@ -269,6 +261,7 @@ sections: Keep in mind that you can also control the flex of the `left` and `right` sections by using the `content` property. Read more about it in the [content options](#content) section. + ### Image Template Display an image alongside the slide content. @@ -322,4 +315,3 @@ The `options` property specifies the widget to be embedded. It has the following - `position`: The position of the widget relative to the slide content. - `flex`: The flex value of the widget. - `args`: Additional arguments to be passed to the widget. - diff --git a/assets/logo-dark.png b/assets/logo-dark.png index e21fe1db..9091ef52 100644 Binary files a/assets/logo-dark.png and b/assets/logo-dark.png differ diff --git a/assets/logo-light.png b/assets/logo-light.png index 4a6365f7..08bbd7d1 100644 Binary files a/assets/logo-light.png and b/assets/logo-light.png differ diff --git a/assets/logo.png b/assets/logo.png index 60c19218..7e7881a3 100644 Binary files a/assets/logo.png and b/assets/logo.png differ diff --git a/example/.metadata b/example/.metadata deleted file mode 100644 index 009f8afe..00000000 --- a/example/.metadata +++ /dev/null @@ -1,45 +0,0 @@ -# This file tracks properties of this Flutter project. -# Used by Flutter tool to assess capabilities and perform upgrades etc. -# -# This file should be version controlled and should not be manually edited. - -version: - revision: "efbf63d9c66b9f6ec30e9ad4611189aa80003d31" - channel: "stable" - -project_type: app - -# Tracks metadata for the flutter migrate command -migration: - platforms: - - platform: root - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: android - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: ios - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: linux - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: macos - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: web - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - platform: windows - create_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - base_revision: efbf63d9c66b9f6ec30e9ad4611189aa80003d31 - - # User provided section - - # List of Local paths (relative to this file) that should be - # ignored by the migrate tool. - # - # Files that are not part of the templates will be ignored by default. - unmanaged_files: - - 'lib/main.dart' - - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle deleted file mode 100644 index 8f246bbd..00000000 --- a/example/android/app/build.gradle +++ /dev/null @@ -1,67 +0,0 @@ -plugins { - id "com.android.application" - id "kotlin-android" - id "dev.flutter.flutter-gradle-plugin" -} - -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -android { - namespace "com.example.superdeck_demo" - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.superdeck_demo" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - } - } -} - -flutter { - source '../..' -} - -dependencies {} diff --git a/example/android/app/src/main/kotlin/com/example/dash_deck_demo/MainActivity.kt b/example/android/app/src/main/kotlin/com/example/dash_deck_demo/MainActivity.kt deleted file mode 100644 index 942c6fe5..00000000 --- a/example/android/app/src/main/kotlin/com/example/dash_deck_demo/MainActivity.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.example.superdeck_demo - -import io.flutter.embedding.android.FlutterActivity - -class MainActivity: FlutterActivity() { -} diff --git a/example/android/build.gradle b/example/android/build.gradle deleted file mode 100644 index f7eb7f63..00000000 --- a/example/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.7.10' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.3.0' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/example/android/gradle.properties b/example/android/gradle.properties deleted file mode 100644 index 94adc3a3..00000000 --- a/example/android/gradle.properties +++ /dev/null @@ -1,3 +0,0 @@ -org.gradle.jvmargs=-Xmx1536M -android.useAndroidX=true -android.enableJetifier=true diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png deleted file mode 100644 index 3f71ab5c..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/100.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png deleted file mode 100644 index 0f9fe3f0..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/1024.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png deleted file mode 100644 index 81f8f3b7..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/114.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png deleted file mode 100644 index 58a8a4be..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/120.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png deleted file mode 100644 index f5648679..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/128.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png deleted file mode 100644 index c4c12a07..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/144.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png deleted file mode 100644 index 1578fbc5..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/152.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png deleted file mode 100644 index 5192436e..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/16.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png deleted file mode 100644 index c09435ae..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/167.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png deleted file mode 100644 index 300b39e1..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/180.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png deleted file mode 100644 index 8efe4931..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/20.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png deleted file mode 100644 index 8fc17e0c..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/256.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png deleted file mode 100644 index 75b677f2..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/29.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png deleted file mode 100644 index 5d7fa4a3..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/32.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png deleted file mode 100644 index e8372384..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/40.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png deleted file mode 100644 index 6eabe776..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/50.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png deleted file mode 100644 index 9cda5f61..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/512.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png deleted file mode 100644 index 620c3438..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/57.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png deleted file mode 100644 index 71c2b9f4..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/58.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png deleted file mode 100644 index 8fd73476..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/60.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png deleted file mode 100644 index 13c5156c..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/64.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png deleted file mode 100644 index 3ead7aaa..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/72.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png deleted file mode 100644 index b0bda75d..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/76.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png deleted file mode 100644 index 45a54efd..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/80.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png deleted file mode 100644 index 34de7c41..00000000 Binary files a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/87.png and /dev/null differ diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 5c82812f..00000000 --- a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1 +0,0 @@ -{"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"128x128","expected-size":"128","filename":"128.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"256x256","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"128x128","expected-size":"256","filename":"256.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"256x256","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"512x512","expected-size":"512","filename":"512.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"16","filename":"16.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"1x"},{"size":"16x16","expected-size":"32","filename":"32.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"32x32","expected-size":"64","filename":"64.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"},{"size":"512x512","expected-size":"1024","filename":"1024.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"mac","scale":"2x"}]} \ No newline at end of file diff --git a/example/lib/main.dart b/example/lib/main.dart deleted file mode 100644 index 1635572f..00000000 --- a/example/lib/main.dart +++ /dev/null @@ -1,38 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:superdeck/superdeck.dart'; - -import 'src/style.dart'; -import 'src/widget/mix_demo.dart'; - -void main() async { - await SuperDeckApp.initialize(); - runApp( - Builder(builder: (context) { - return MaterialApp( - title: 'Superdeck', - debugShowCheckedModeBanner: false, - home: SuperDeckApp( - style: style, - // ignore: prefer_const_literals_to_create_immutables - examples: [ - Example( - name: 'demo', - schema: ExampleOptions.schema, - builder: (args) { - return Center( - child: Container( - height: args.height, - width: args.width, - color: Colors.blue, - alignment: Alignment.center, - child: Text(args.text), - ), - ); - }, - ), - ], - ), - ); - }), - ); -} diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/100.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/100.png deleted file mode 100644 index 3f71ab5c..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/100.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/1024.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/1024.png deleted file mode 100644 index 0f9fe3f0..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/1024.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/114.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/114.png deleted file mode 100644 index 81f8f3b7..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/114.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/120.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/120.png deleted file mode 100644 index 58a8a4be..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/120.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/128.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/128.png deleted file mode 100644 index f5648679..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/128.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/144.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/144.png deleted file mode 100644 index c4c12a07..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/144.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/152.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/152.png deleted file mode 100644 index 1578fbc5..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/152.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/16.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/16.png deleted file mode 100644 index 5192436e..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/16.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/167.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/167.png deleted file mode 100644 index c09435ae..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/167.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/180.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/180.png deleted file mode 100644 index 300b39e1..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/180.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/20.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/20.png deleted file mode 100644 index 8efe4931..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/20.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/256.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/256.png deleted file mode 100644 index 8fc17e0c..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/256.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/29.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/29.png deleted file mode 100644 index 75b677f2..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/29.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/32.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/32.png deleted file mode 100644 index 5d7fa4a3..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/32.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/40.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/40.png deleted file mode 100644 index e8372384..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/40.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/50.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/50.png deleted file mode 100644 index 6eabe776..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/50.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/512.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/512.png deleted file mode 100644 index 9cda5f61..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/512.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/57.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/57.png deleted file mode 100644 index 620c3438..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/57.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/58.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/58.png deleted file mode 100644 index 71c2b9f4..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/58.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/60.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/60.png deleted file mode 100644 index 8fd73476..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/60.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/64.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/64.png deleted file mode 100644 index 13c5156c..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/64.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/72.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/72.png deleted file mode 100644 index 3ead7aaa..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/72.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/76.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/76.png deleted file mode 100644 index b0bda75d..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/76.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/80.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/80.png deleted file mode 100644 index 45a54efd..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/80.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/87.png b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/87.png deleted file mode 100644 index 34de7c41..00000000 Binary files a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/87.png and /dev/null differ diff --git a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/Contents.json b/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 871a77bc..00000000 --- a/example/macos/Runner/Assets.xcassets/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,284 +0,0 @@ -{ - "images": [ - { - "size": "60x60", - "expected-size": "180", - "filename": "180.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "40x40", - "expected-size": "80", - "filename": "80.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "40x40", - "expected-size": "120", - "filename": "120.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "60x60", - "expected-size": "120", - "filename": "120.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "57x57", - "expected-size": "57", - "filename": "57.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "58", - "filename": "58.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "29x29", - "expected-size": "29", - "filename": "29.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "87", - "filename": "87.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "57x57", - "expected-size": "114", - "filename": "114.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "60", - "filename": "60.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "iphone", - "scale": "3x" - }, - { - "size": "1024x1024", - "filename": "1024.png", - "expected-size": "1024", - "idiom": "ios-marketing", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "scale": "1x" - }, - { - "size": "40x40", - "expected-size": "80", - "filename": "80.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "72x72", - "expected-size": "72", - "filename": "72.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "76x76", - "expected-size": "152", - "filename": "152.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "50x50", - "expected-size": "100", - "filename": "100.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "29x29", - "expected-size": "58", - "filename": "58.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "76x76", - "expected-size": "76", - "filename": "76.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "29x29", - "expected-size": "29", - "filename": "29.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "50x50", - "expected-size": "50", - "filename": "50.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "72x72", - "expected-size": "144", - "filename": "144.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "40x40", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "83.5x83.5", - "expected-size": "167", - "filename": "167.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "20x20", - "expected-size": "20", - "filename": "20.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "1x" - }, - { - "size": "20x20", - "expected-size": "40", - "filename": "40.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "ipad", - "scale": "2x" - }, - { - "size": "128x128", - "expected-size": "128", - "filename": "128.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "256x256", - "expected-size": "256", - "filename": "256.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "128x128", - "expected-size": "256", - "filename": "256.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "256x256", - "expected-size": "512", - "filename": "512.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "32x32", - "expected-size": "32", - "filename": "32.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "512x512", - "expected-size": "512", - "filename": "512.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "16x16", - "expected-size": "16", - "filename": "16.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "1x" - }, - { - "size": "16x16", - "expected-size": "32", - "filename": "32.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "32x32", - "expected-size": "64", - "filename": "64.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - }, - { - "size": "512x512", - "expected-size": "1024", - "filename": "1024.png", - "folder": "Assets.xcassets/AppIcon.appiconset/", - "idiom": "mac", - "scale": "2x" - } - ] -} diff --git a/example/pubspec.lock b/example/pubspec.lock deleted file mode 100644 index 471c292d..00000000 --- a/example/pubspec.lock +++ /dev/null @@ -1,935 +0,0 @@ -# Generated by pub -# See https://dart.dev/tools/pub/glossary#lockfile -packages: - _fe_analyzer_shared: - dependency: transitive - description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" - url: "https://pub.dev" - source: hosted - version: "67.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - animate_do: - dependency: transitive - description: - name: animate_do - sha256: "7a3162729f0ea042f9dd84da217c5bde5472ad9cef644079929d4304a5dc4ca0" - url: "https://pub.dev" - source: hosted - version: "3.3.4" - archive: - dependency: transitive - description: - name: archive - sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265 - url: "https://pub.dev" - source: hosted - version: "3.5.1" - args: - dependency: transitive - description: - name: args - sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" - url: "https://pub.dev" - source: hosted - version: "2.5.0" - async: - dependency: transitive - description: - name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" - url: "https://pub.dev" - source: hosted - version: "2.11.0" - barcode: - dependency: transitive - description: - name: barcode - sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 - url: "https://pub.dev" - source: hosted - version: "2.2.8" - bidi: - dependency: transitive - description: - name: bidi - sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" - url: "https://pub.dev" - source: hosted - version: "2.0.10" - boolean_selector: - dependency: transitive - description: - name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" - url: "https://pub.dev" - source: hosted - version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - cached_network_image: - dependency: transitive - description: - name: cached_network_image - sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" - url: "https://pub.dev" - source: hosted - version: "3.3.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" - charcode: - dependency: transitive - description: - name: charcode - sha256: fb98c0f6d12c920a02ee2d998da788bca066ca5f148492b7085ee23372b12306 - url: "https://pub.dev" - source: hosted - version: "1.3.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - clock: - dependency: transitive - description: - name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf - url: "https://pub.dev" - source: hosted - version: "1.1.1" - collection: - dependency: transitive - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" - url: "https://pub.dev" - source: hosted - version: "0.3.4+1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - dart_mappable: - dependency: transitive - description: - name: dart_mappable - sha256: "47269caf2060533c29b823ff7fa9706502355ffcb61e7f2a374e3a0fb2f2c3f0" - url: "https://pub.dev" - source: hosted - version: "4.2.2" - dart_markdown: - dependency: transitive - description: - name: dart_markdown - sha256: b9b7e13733dd418bc83e6f4e68d79caebab6cd3a8b301e91d1bfd6ef5ef8e993 - url: "https://pub.dev" - source: hosted - version: "3.1.7" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" - url: "https://pub.dev" - source: hosted - version: "2.3.6" - fake_async: - dependency: transitive - description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" - url: "https://pub.dev" - source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - file: - dependency: transitive - description: - name: file - sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - file_picker: - dependency: transitive - description: - name: file_picker - sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" - url: "https://pub.dev" - source: hosted - version: "8.0.3" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544" - url: "https://pub.dev" - source: hosted - version: "3.3.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" - url: "https://pub.dev" - source: hosted - version: "2.0.19" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - glob: - dependency: transitive - description: - name: glob - sha256: "0e7014b3b7d4dac1ca4d6114f82bf1782ee86745b9b42a92c9289c23d8a0ab63" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - go_router: - dependency: transitive - description: - name: go_router - sha256: abec47eb8c8c36ebf41d0a4c64dbbe7f956e39a012b3aafc530e951bdc12fe3f - url: "https://pub.dev" - source: hosted - version: "14.1.4" - google_fonts: - dependency: "direct main" - description: - name: google_fonts - sha256: "5b1726fee554d1cc9db1baef8061b126567ff0a1140a03ed7de936e62f2ab98b" - url: "https://pub.dev" - source: hosted - version: "6.2.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" - http: - dependency: transitive - description: - name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - http_parser: - dependency: transitive - description: - name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" - url: "https://pub.dev" - source: hosted - version: "4.0.2" - image: - dependency: transitive - description: - name: image - sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" - url: "https://pub.dev" - source: hosted - version: "4.1.7" - json_annotation: - dependency: transitive - description: - name: json_annotation - sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" - source: hosted - version: "4.9.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290 - url: "https://pub.dev" - source: hosted - version: "3.0.0" - localstorage: - dependency: transitive - description: - name: localstorage - sha256: "6340acefdd3a969cceb044a69cde2dc5877c5b861b2e02d0803930ed483dbe91" - url: "https://pub.dev" - source: hosted - version: "5.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - markdown_viewer: - dependency: transitive - description: - name: markdown_viewer - sha256: e819ea779eaeca933bb38228e02888ad72409aba922efb2e65abb2f8b046dc8e - url: "https://pub.dev" - source: hosted - version: "0.6.2" - matcher: - dependency: transitive - description: - name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb - url: "https://pub.dev" - source: hosted - version: "0.12.16+1" - material_color_utilities: - dependency: transitive - description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" - url: "https://pub.dev" - source: hosted - version: "0.8.0" - meta: - dependency: transitive - description: - name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" - url: "https://pub.dev" - source: hosted - version: "1.12.0" - mix: - dependency: "direct overridden" - description: - path: "../../mix/packages/mix" - relative: true - source: path - version: "1.3.0" - mix_annotations: - dependency: transitive - description: - name: mix_annotations - sha256: ecad51935d9c422481ebc820e8d3edac001bfd8493ac3cf44d03c43cee196d7a - url: "https://pub.dev" - source: hosted - version: "0.2.0+1" - mix_generator: - dependency: "direct overridden" - description: - path: "../../mix/packages/mix_generator" - relative: true - source: path - version: "0.2.1" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" - url: "https://pub.dev" - source: hosted - version: "2.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" - path: - dependency: transitive - description: - name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" - url: "https://pub.dev" - source: hosted - version: "1.9.0" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: transitive - description: - name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 - url: "https://pub.dev" - source: hosted - version: "2.1.3" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d - url: "https://pub.dev" - source: hosted - version: "2.2.4" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - pdf: - dependency: transitive - description: - name: pdf - sha256: "243f05342fc0bdf140eba5b069398985cdbdd3dbb1d776cf43d5ea29cc570ba6" - url: "https://pub.dev" - source: hosted - version: "3.10.8" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" - platform: - dependency: transitive - description: - name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" - source: hosted - version: "2.1.8" - pub_semver: - dependency: transitive - description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - pubspec_parse: - dependency: transitive - description: - name: pubspec_parse - sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8 - url: "https://pub.dev" - source: hosted - version: "1.3.0" - qr: - dependency: transitive - description: - name: qr - sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - recase: - dependency: transitive - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - screen_retriever: - dependency: transitive - description: - name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" - url: "https://pub.dev" - source: hosted - version: "0.1.9" - scrollable_positioned_list: - dependency: transitive - description: - name: scrollable_positioned_list - sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" - url: "https://pub.dev" - source: hosted - version: "0.3.8" - signals: - dependency: transitive - description: - name: signals - sha256: "94927d4069a158a6a20ad98990e6c67faeeed4ae6af681c1696fb98f3fbd5d2e" - url: "https://pub.dev" - source: hosted - version: "5.2.2" - signals_core: - dependency: transitive - description: - name: signals_core - sha256: e5888685b9a3cff7c9814bd433195b1f76d0bfa3eae135e0a2f4ff5c48a56a1c - url: "https://pub.dev" - source: hosted - version: "5.2.2" - signals_flutter: - dependency: transitive - description: - name: signals_flutter - sha256: "1c202bc5136fbe1d8d71b66202e8f71bc10b957ca678c1f44d0d3af2aeb678cc" - url: "https://pub.dev" - source: hosted - version: "5.2.2" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: - dependency: transitive - description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" - url: "https://pub.dev" - source: hosted - version: "1.5.0" - source_span: - dependency: transitive - description: - name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" - url: "https://pub.dev" - source: hosted - version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d - url: "https://pub.dev" - source: hosted - version: "2.3.3+1" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" - url: "https://pub.dev" - source: hosted - version: "2.5.4" - stack_trace: - dependency: transitive - description: - name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" - url: "https://pub.dev" - source: hosted - version: "1.11.1" - stream_channel: - dependency: transitive - description: - name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 - url: "https://pub.dev" - source: hosted - version: "2.1.2" - string_scanner: - dependency: transitive - description: - name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - superdeck: - dependency: "direct main" - description: - path: ".." - relative: true - source: path - version: "0.0.4" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - syntax_highlight: - dependency: transitive - description: - name: syntax_highlight - sha256: ee33b6aa82cc722bb9b40152a792181dee222353b486c0255fde666a3e3a4997 - url: "https://pub.dev" - source: hosted - version: "0.4.0" - term_glyph: - dependency: transitive - description: - name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 - url: "https://pub.dev" - source: hosted - version: "1.2.1" - test_api: - dependency: transitive - description: - name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - type_plus: - dependency: transitive - description: - name: type_plus - sha256: d5d1019471f0d38b91603adb9b5fd4ce7ab903c879d2fbf1a3f80a630a03fcc9 - url: "https://pub.dev" - source: hosted - version: "2.1.1" - typed_data: - dependency: transitive - description: - name: typed_data - sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c - url: "https://pub.dev" - source: hosted - version: "1.3.2" - universal_html: - dependency: transitive - description: - name: universal_html - sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" - url: "https://pub.dev" - source: hosted - version: "2.2.4" - universal_io: - dependency: transitive - description: - name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - url_launcher: - dependency: transitive - description: - name: url_launcher - sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" - url: "https://pub.dev" - source: hosted - version: "6.2.6" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" - url: "https://pub.dev" - source: hosted - version: "6.3.1" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" - url: "https://pub.dev" - source: hosted - version: "6.2.5" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - uuid: - dependency: transitive - description: - name: uuid - sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" - url: "https://pub.dev" - source: hosted - version: "4.4.0" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://pub.dev" - source: hosted - version: "14.2.1" - watcher: - dependency: transitive - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - web: - dependency: transitive - description: - name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - win32: - dependency: transitive - description: - name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" - url: "https://pub.dev" - source: hosted - version: "5.5.0" - window_manager: - dependency: transitive - description: - name: window_manager - sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" - url: "https://pub.dev" - source: hosted - version: "0.3.9" - xdg_directories: - dependency: transitive - description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d - url: "https://pub.dev" - source: hosted - version: "1.0.4" - xml: - dependency: transitive - description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" - yaml: - dependency: transitive - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" - url: "https://pub.dev" - source: hosted - version: "3.1.2" -sdks: - dart: ">=3.3.0 <4.0.0" - flutter: ">=3.19.0" diff --git a/example/pubspec_overrides.yaml b/example/pubspec_overrides.yaml deleted file mode 100644 index e9e5b6a3..00000000 --- a/example/pubspec_overrides.yaml +++ /dev/null @@ -1,5 +0,0 @@ -dependency_overrides: - mix_generator: - path: ../../mix/packages/mix_generator - mix: - path: ../../mix/packages/mix \ No newline at end of file diff --git a/example/superdeck/assets.json b/example/superdeck/assets.json deleted file mode 100644 index 2068adb0..00000000 --- a/example/superdeck/assets.json +++ /dev/null @@ -1 +0,0 @@ -[{"file":"superdeck/generated/thumb_K0akgdDR.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_aShu2Vm6.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_SwkEymrr.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_2oTGEbqt.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_rM1XbfdO.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_bt3HCjyv.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_zXfqPfVD.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_qH3zHQwf.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_dBrctVs2.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/mermaid_wRACwsNb.png","dimensions":{"width":600.0,"height":860.0}},{"file":"superdeck/generated/thumb_Bp7o6RCC.png","dimensions":{"width":512.0,"height":288.0}},{"file":"superdeck/generated/thumb_IrZnYqye.png","dimensions":{"width":512.0,"height":288.0}}] \ No newline at end of file diff --git a/example/superdeck/config.json b/example/superdeck/config.json deleted file mode 100644 index 9e26dfee..00000000 --- a/example/superdeck/config.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/example/superdeck/generated/mermaid_wRACwsNb.png b/example/superdeck/generated/mermaid_wRACwsNb.png deleted file mode 100644 index 2e8a0cc8..00000000 Binary files a/example/superdeck/generated/mermaid_wRACwsNb.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_2oTGEbqt.png b/example/superdeck/generated/thumb_2oTGEbqt.png deleted file mode 100644 index f5df3510..00000000 Binary files a/example/superdeck/generated/thumb_2oTGEbqt.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_Bp7o6RCC.png b/example/superdeck/generated/thumb_Bp7o6RCC.png deleted file mode 100644 index 2a40ee3a..00000000 Binary files a/example/superdeck/generated/thumb_Bp7o6RCC.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_IrZnYqye.png b/example/superdeck/generated/thumb_IrZnYqye.png deleted file mode 100644 index 0ae204a4..00000000 Binary files a/example/superdeck/generated/thumb_IrZnYqye.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_K0akgdDR.png b/example/superdeck/generated/thumb_K0akgdDR.png deleted file mode 100644 index 559eb0b5..00000000 Binary files a/example/superdeck/generated/thumb_K0akgdDR.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_SwkEymrr.png b/example/superdeck/generated/thumb_SwkEymrr.png deleted file mode 100644 index abaada82..00000000 Binary files a/example/superdeck/generated/thumb_SwkEymrr.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_aShu2Vm6.png b/example/superdeck/generated/thumb_aShu2Vm6.png deleted file mode 100644 index d9993ed4..00000000 Binary files a/example/superdeck/generated/thumb_aShu2Vm6.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_bt3HCjyv.png b/example/superdeck/generated/thumb_bt3HCjyv.png deleted file mode 100644 index e0d3bce3..00000000 Binary files a/example/superdeck/generated/thumb_bt3HCjyv.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_dBrctVs2.png b/example/superdeck/generated/thumb_dBrctVs2.png deleted file mode 100644 index 3e4bef61..00000000 Binary files a/example/superdeck/generated/thumb_dBrctVs2.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_qH3zHQwf.png b/example/superdeck/generated/thumb_qH3zHQwf.png deleted file mode 100644 index 0e1aab1d..00000000 Binary files a/example/superdeck/generated/thumb_qH3zHQwf.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_rM1XbfdO.png b/example/superdeck/generated/thumb_rM1XbfdO.png deleted file mode 100644 index 91c2d14d..00000000 Binary files a/example/superdeck/generated/thumb_rM1XbfdO.png and /dev/null differ diff --git a/example/superdeck/generated/thumb_zXfqPfVD.png b/example/superdeck/generated/thumb_zXfqPfVD.png deleted file mode 100644 index c32bee57..00000000 Binary files a/example/superdeck/generated/thumb_zXfqPfVD.png and /dev/null differ diff --git a/example/superdeck/slides.json b/example/superdeck/slides.json deleted file mode 100644 index aaa41a35..00000000 --- a/example/superdeck/slides.json +++ /dev/null @@ -1 +0,0 @@ -[{"style":"quote","content":{"flex":1,"alignment":"bottom_right"},"data":"> Create your Flutter presentations faster and easier than ever.\n> You can quote me on that\n> ### Leo Farias","options":{"src":"https://source.unsplash.com/people-watching-concert-during-night-time-blgOFmPIlr0","fit":"cover","flex":1,"position":"right"},"raw":"---\nstyle: quote\nlayout: image\noptions:\n src: https://source.unsplash.com/people-watching-concert-during-night-time-blgOFmPIlr0\n fit: cover\ncontent:\n alignment: bottom_right\n---\n\n> Create your Flutter presentations faster and easier than ever.\n> You can quote me on that\n> ### Leo Farias","type":"Slide","layout":"image"},{"background":"https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif","style":"cover","raw":"---\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif\nstyle: cover\n---\n\n# Complex layout","data":"# Complex layout","type":"Slide"},{"style":"show_sections","data":"## Image Layout\n\nCreate beautiful slides with images that fit your content.\n\n##### Options\n```yaml\noptions:\n src: https//www.url.com/image.jpg\n fit: cover\n position: left\n flex: 1\n```\n\n> Define position fit and flex options for the image.","options":{"src":"https://source.unsplash.com/random/900×700/?waves","fit":"cover","flex":1,"position":"left"},"raw":"---\nlayout: image\nstyle: show_sections\noptions:\n src: https://source.unsplash.com/random/900×700/?waves\n fit: cover\n position: left\n flex: 1\n---\n\n## Image Layout\n\nCreate beautiful slides with images that fit your content.\n\n##### Options\n```yaml\noptions:\n src: https//www.url.com/image.jpg\n fit: cover\n position: left\n flex: 1\n```\n\n> Define position fit and flex options for the image.","type":"Slide","layout":"image"},{"style":"show_sections","data":"::left::\n\n# Two Column\n\nThis is a two-column layout. You can use it to compare two different concepts or ideas.\n\n::right::\n\n### Section Options\n\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n\n\n```yaml\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n```","sections":{"left":{"flex":2,"alignment":"center_left"},"right":{"flex":1,"alignment":"bottom_left"}},"raw":"---\nlayout: two_column\nstyle: show_sections\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n---\n\n::left::\n\n# Two Column\n\nThis is a two-column layout. You can use it to compare two different concepts or ideas.\n\n::right::\n\n### Section Options\n\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n\n\n```yaml\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n```","type":"Slide","layout":"two_column"},{"content":{"flex":2,"alignment":"center"},"style":"show_sections","data":"# Two Column + Header\n\n\n::left::\n\n### Left Section\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n::right::\n\n#### Section Options\n\n```yaml\nsections:\n left:\n alignment: bottom_right\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\n```","sections":{"left":{"flex":2,"alignment":"center_left"},"right":{"flex":1,"alignment":"bottom_left"},"header":{"flex":1,"alignment":"bottom_left"}},"raw":"---\nlayout: two_column_header\ncontent:\n alignment: center\n flex: 2\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\nstyle: show_sections\n---\n\n# Two Column + Header\n\n\n::left::\n\n### Left Section\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n::right::\n\n#### Section Options\n\n```yaml\nsections:\n left:\n alignment: bottom_right\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\n```","type":"Slide","layout":"two_column_header"},{"content":{"flex":1,"alignment":"center"},"style":"rad","data":"# Mix\n\nIntegration with Mix gives you complete control over all styling elements in your slides with a simple and intuitive API.\n\n::right::\n\n```dart\nVariantAttribute get radStyle {\n return const SlideVariant('rad')(\n $.h1.textStyle.as(GoogleFonts.poppins()),\n $.h1.textStyle.fontSize(140),\n $.code.decoration.border.all(\n color: Colors.red,\n width: 3,\n ),\n $.code.decoration(\n color: Colors.black54,\n ),\n $.code.padding.all(40),\n\n $.outerContainer.margin.all(60),\n\n $.innerContainer.borderRadius(25),\n $.innerContainer.shadow(\n blurRadius: 0,\n spreadRadius: 10,\n color: Colors.red.withOpacity(1),\n ),\n $.innerContainer.gradient.radial(\n stops: [0.0, 1.0],\n radius: 0.7,\n colors: [Colors.purple, Colors.deepPurple],\n ),\n\n // Events\n onMouseHover((event) {\n final position = event.position;\n final dx = position.x * 10;\n final dy = position.y * 10;\n\n return Style(\n $.innerContainer.transform(_transformMatrix(position)),\n $.innerContainer.shadow.offset(dx, dy),\n $.innerContainer.gradient.radial(\n center: position,\n ),\n );\n }),\n\n (onPressed | onLongPressed)(\n $.innerContainer.shadow(\n blurRadius: 5,\n spreadRadius: 1,\n offset: Offset.zero,\n color: Colors.purpleAccent,\n ),\n $.innerContainer.border.all(color: Colors.white, width: 1),\n $.innerContainer.gradient.radial\n .colors([Colors.purpleAccent, Colors.purpleAccent]),\n ),\n );\n}\n```","sections":{"left":null,"right":{"flex":2,"alignment":"bottom_left"}},"raw":"---\nstyle: rad\nlayout: two_column\ncontent:\n alignment: center\nsections:\n left:\n right:\n alignment: bottom_left\n flex: 2\n---\n\n# Mix\n\nIntegration with Mix gives you complete control over all styling elements in your slides with a simple and intuitive API.\n\n::right::\n\n```dart\nVariantAttribute get radStyle {\n return const SlideVariant('rad')(\n $.h1.textStyle.as(GoogleFonts.poppins()),\n $.h1.textStyle.fontSize(140),\n $.code.decoration.border.all(\n color: Colors.red,\n width: 3,\n ),\n $.code.decoration(\n color: Colors.black54,\n ),\n $.code.padding.all(40),\n\n $.outerContainer.margin.all(60),\n\n $.innerContainer.borderRadius(25),\n $.innerContainer.shadow(\n blurRadius: 0,\n spreadRadius: 10,\n color: Colors.red.withOpacity(1),\n ),\n $.innerContainer.gradient.radial(\n stops: [0.0, 1.0],\n radius: 0.7,\n colors: [Colors.purple, Colors.deepPurple],\n ),\n\n // Events\n onMouseHover((event) {\n final position = event.position;\n final dx = position.x * 10;\n final dy = position.y * 10;\n\n return Style(\n $.innerContainer.transform(_transformMatrix(position)),\n $.innerContainer.shadow.offset(dx, dy),\n $.innerContainer.gradient.radial(\n center: position,\n ),\n );\n }),\n\n (onPressed | onLongPressed)(\n $.innerContainer.shadow(\n blurRadius: 5,\n spreadRadius: 1,\n offset: Offset.zero,\n color: Colors.purpleAccent,\n ),\n $.innerContainer.border.all(color: Colors.white, width: 1),\n $.innerContainer.gradient.radial\n .colors([Colors.purpleAccent, Colors.purpleAccent]),\n ),\n );\n}\n```","type":"Slide","layout":"two_column"},{"background":"https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif","style":"cover","raw":"---\nstyle: cover\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif\n---\n\n# Markdown support","data":"# Markdown support","type":"Slide"},{"content":{"flex":4,"alignment":"center_left"},"style":"show_sections","data":"::left::\n\n\n**Bold Text**\n\n*Italic Text*\n\n~~Strikethrough~~\n\n`Inline Code`\n\n[Link here](https://github.com/leoafarias/superdeck)\n\n::right::\n\nLists\n\n1. Ordered list item 1\n2. Ordered list item 2\n\n- Unordered list item 1\n- Unordered list item 2\n\nQuotes\n\n> If you want to go fast, go alone. \n> If you want to go far, go together.\n> ### African Proverb","sections":{},"raw":"---\nstyle: show_sections\nlayout: two_column\nsections:\ncontent:\n flex: 4\n---\n\n::left::\n\n\n**Bold Text**\n\n*Italic Text*\n\n~~Strikethrough~~\n\n`Inline Code`\n\n[Link here](https://github.com/leoafarias/superdeck)\n\n::right::\n\nLists\n\n1. Ordered list item 1\n2. Ordered list item 2\n\n- Unordered list item 1\n- Unordered list item 2\n\nQuotes\n\n> If you want to go fast, go alone. \n> If you want to go far, go together.\n> ### African Proverb","type":"Slide","layout":"two_column"},{"data":"::left::\n\n\nCode\n```dart\nint factorial(int n) {\n return n == 0 ? 1 : n * factorial(n - 1);\n}\n```\n\nTasks\n- [ ] Item 1\n- [x] Item 2\n\nSubtasks\n\n- [x] Item 1\n - [ ] Subitem 1\n\n::right::\n\nImages\n![Unsplash Image](https://source.unsplash.com/random/300x200/?landscape)\n\n\nTable\n\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1A | Cell 1B |\n| Cell 2A | Cell 2B |\n\nDivider\n\n___","sections":{},"raw":"---\nlayout: two_column\n---\n\n::left::\n\n\nCode\n```dart\nint factorial(int n) {\n return n == 0 ? 1 : n * factorial(n - 1);\n}\n```\n\nTasks\n- [ ] Item 1\n- [x] Item 2\n\nSubtasks\n\n- [x] Item 1\n - [ ] Subitem 1\n\n::right::\n\nImages\n![Unsplash Image](https://source.unsplash.com/random/300x200/?landscape)\n\n\nTable\n\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1A | Cell 1B |\n| Cell 2A | Cell 2B |\n\nDivider\n\n___","type":"Slide","layout":"two_column"},{"title":"Mermaid example","data":"::left::\n\n![Mermaid Diagram](superdeck/generated/mermaid_wRACwsNb.png)\n \n\n::right::\n\n## Mermaid Support\n\nSuperdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation.","sections":{},"raw":"---\ntitle: \"Mermaid example\"\nlayout: two_column\n---\n\n::left::\n\n```mermaid\nflowchart TD\n A[This is crazy] -->|Get money| B(Go shopping)\n B --> C{Let me car}\n C -->|One| D[Laptop]\n C -->|Two| E[iPhone]\n C -->|Three| F[fa:fa-car Car]\n```\n \n\n::right::\n\n## Mermaid Support\n\nSuperdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation.","type":"Slide","layout":"two_column"},{"options":{"name":"demo","args":{"text":"Hello, Superdeck!","height":200.0,"width":300.0},"flex":1,"position":"right"},"data":"## Showcase your widgets","raw":"---\nlayout: widget\noptions:\n name: demo\n args:\n text: Hello, Superdeck!\n height: 200.0\n width: 300.0\n---\n\n## Showcase your widgets\n","type":"Slide","layout":"widget"}] \ No newline at end of file diff --git a/example/web/index.html b/example/web/index.html deleted file mode 100644 index 4b6ba3fd..00000000 --- a/example/web/index.html +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - - - - - - - - - - - - Presentation - - - - - - - - -
- - - - diff --git a/example/web/manifest.json b/example/web/manifest.json deleted file mode 100644 index c325d4ec..00000000 --- a/example/web/manifest.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "name": "superdeck_demo", - "short_name": "superdeck_demo", - "start_url": ".", - "display": "standalone", - "background_color": "#0175C2", - "theme_color": "#0175C2", - "description": "A new Flutter project.", - "orientation": "portrait-primary", - "prefer_related_applications": false, - "icons": [ - { - "src": "icons/Icon-192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "icons/Icon-512.png", - "sizes": "512x512", - "type": "image/png" - }, - { - "src": "icons/Icon-maskable-192.png", - "sizes": "192x192", - "type": "image/png", - "purpose": "maskable" - }, - { - "src": "icons/Icon-maskable-512.png", - "sizes": "512x512", - "type": "image/png", - "purpose": "maskable" - } - ] -} diff --git a/lib/builder/slide_parser.dart b/lib/builder/slide_parser.dart deleted file mode 100644 index e4b18ad9..00000000 --- a/lib/builder/slide_parser.dart +++ /dev/null @@ -1,78 +0,0 @@ -import '../helpers/config_model.dart'; -import '../helpers/deep_merge.dart'; -import '../helpers/utils.dart'; -import '../models/slide_model.dart'; - -class SlideParser { - final SDConfig config; - - SlideParser({required this.config}); - - final _frontMatterRegex = RegExp(r'---([\s\S]*?)---'); - - List _splitSlides(String content) { - final lines = content.split('\n'); - final slides = []; - final buffer = StringBuffer(); - bool inSlide = false; - - var isCodeBlock = false; - - for (var line in lines) { - if (line.trim().startsWith('```')) { - isCodeBlock = !isCodeBlock; - } - if (line.trim() == '---' && !isCodeBlock) { - if (buffer.isNotEmpty) { - if (inSlide) { - // Add the slide content to the list of slides - slides.add(buffer.toString().trim()); - inSlide = false; - buffer.clear(); - } else { - inSlide = true; - } - } - buffer.writeln(line); - } else { - buffer.writeln(line); - } - } - - // Capture any remaining content as a slide - if (buffer.isNotEmpty) { - slides.add(buffer.toString()); - } - - return slides; - } - - List run(String contents) { - final slidesRaw = _splitSlides(contents.trim()); - - final slides = []; - - for (final slideRaw in slidesRaw) { - slides.add(_runEach(slideRaw)); - } - - return slides; - } - - Slide _runEach(String slideRaw) { - final frontMatter = _frontMatterRegex.firstMatch(slideRaw)?.group(1) ?? ''; - - final options = converYamlToMap(frontMatter); - - final content = slideRaw - .substring(_frontMatterRegex.matchAsPrefix(slideRaw)?.end ?? 0) - .trim(); - - final mergedOptions = deepMerge( - config.toSlideMap(), - {...options, 'raw': slideRaw, 'data': content}, - ); - - return Slide.parse(mergedOptions); - } -} diff --git a/lib/builder/slides_loader.dart b/lib/builder/slides_loader.dart deleted file mode 100644 index 00a2f312..00000000 --- a/lib/builder/slides_loader.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; - -import 'package:watcher/watcher.dart'; - -import '../helpers/constants.dart'; -import '../services/project_service.dart'; -import 'slide_parser.dart'; -import 'slides_pipeline.dart'; - -class SlidesLoader { - SlidesLoader._(); - - static final instance = SlidesLoader._(); - - StreamSubscription? _listenerSub; - final _projectService = ProjectService.instance; - - Future _generate() async { - log('Generating slides...'); - await _projectService.ensureExists(); - - final presentationRaw = await _projectService.loadMarkdown(); - final deck = await _projectService.loadDeck(); - final files = await _projectService.loadGeneratedFiles(); - - final slideParser = SlideParser(config: deck.config); - - final slides = slideParser.run(presentationRaw); - - final pipeline = SlidesPipeline( - [ - // const ImageCachingTask(), - const MermaidConverterTask(), - const SlideThumbnailTask(), - ], - ); - - final result = await pipeline.run(slides, files); - - for (var file in files) { - if (!result.neededAssets.any((element) => element.path == file.path)) { - if (await file.exists()) { - await file.delete(); - } - } - } - - await Future.wait([ - _projectService.saveConfigRef(deck.config), - _projectService.saveSlidesRef(result.slides), - _projectService.saveAssetsRef(result.neededAssets), - ]); - } - - void listen( - FutureOr Function() onChange, - ) { - // _listenerSub?.cancel(); - - _listenerSub = _projectService.watcher.events.listen((_) => onChange()); - } - - Future loadDeck() async { - if (kCanRunProcess) { - await _generate(); - } - - return _projectService.loadDeck(); - } -} diff --git a/lib/builder/slides_pipeline.dart b/lib/builder/slides_pipeline.dart deleted file mode 100644 index a9869493..00000000 --- a/lib/builder/slides_pipeline.dart +++ /dev/null @@ -1,256 +0,0 @@ -import 'dart:async'; -import 'dart:developer'; -import 'dart:io'; -import 'dart:ui' as ui; - -import 'package:flutter/foundation.dart'; - -import '../services/mermaid_service.dart'; -import '../services/project_service.dart'; -import '../superdeck.dart'; - -final _mermaidBlockRegex = RegExp(r'```mermaid([\s\S]*?)```'); - -class PipelineAsset { - final File asset; - final Uint8List data; - - PipelineAsset({ - required this.asset, - required this.data, - }); -} - -typedef PipelineResult = ({ - List slides, - List neededAssets, -}); - -class TaskController { - final Slide slide; - final List _assets; - - TaskController({ - required this.slide, - required List assets, - }) : _assets = assets; - - List neededAssets = []; - - TaskController copyWith({ - Slide? slide, - List? assets, - }) { - return TaskController( - slide: slide ?? this.slide, - assets: assets ?? _assets, - )..neededAssets = neededAssets; - } - - void markNeeded(File asset) { - neededAssets.add(asset); - } -} - -class SlidesPipeline { - final List processors; - - SlidesPipeline(this.processors); - - Future run( - List slides, - List assets, - ) async { - final futures = >[]; - - Future runEachSlide(Slide slide) async { - var controller = TaskController(slide: slide, assets: assets); - for (var task in processors) { - controller = await task.run(controller); - } - return controller; - } - - for (var slide in slides) { - futures.add(runEachSlide(slide)); - } - - final controllers = await Future.wait(futures); - - final result = ( - slides: controllers.map((e) => e.slide).toList(), - neededAssets: controllers.expand((e) => e.neededAssets).toList(), - ); - - return result; - } -} - -abstract class Task { - const Task(); - - FutureOr run( - TaskController controller, - ); -} - -class SlideThumbnailTask extends Task { - const SlideThumbnailTask(); - - @override - FutureOr run(controller) async { - final file = SlideAsset.thumbnail(controller.slide); - - if (await file.exists()) { - controller.markNeeded(file); - } - - return controller; - } -} - -class MermaidConverterTask extends Task { - const MermaidConverterTask(); - - @override - FutureOr run(controller) async { - final slide = controller.slide; - - final matches = _mermaidBlockRegex.allMatches(slide.data); - - if (matches.isEmpty) return controller; - final replacements = <({int start, int end, String markdown})>[]; - - for (final Match match in matches) { - final mermaidSyntax = match.group(1); - - if (mermaidSyntax == null) continue; - - final mermaidFile = SlideAsset.mermaid(mermaidSyntax); - - if (!await mermaidFile.exists()) { - // Process the mermaid syntax to generate an image file - final imageData = await mermaidService.generateImage(mermaidSyntax); - - if (imageData != null) { - await mermaidFile.writeAsBytes(imageData); - } - } - - // If file existeed or was create it then replace it - if (await mermaidFile.exists()) { - controller.markNeeded(mermaidFile); - - replacements.add(( - start: match.start, - end: match.end, - markdown: '![Mermaid Diagram](${mermaidFile.path})', - )); - } - } - - var replacedData = slide.data; - - // Apply replacements in reverse order - for (var replacement in replacements.reversed) { - final ( - :start, - :end, - :markdown, - ) = replacement; - - replacedData = replacedData.replaceRange(start, end, markdown); - } - - return controller.copyWith( - slide: slide.copyWith(data: replacedData), - ); - } -} - -class ImageCachingTask extends Task { - const ImageCachingTask(); - - @override - Future run(controller) async { - final slide = controller.slide; - - var content = slide.data; - // Do not cache remot edata if cacheRemoteAssets is false - - // Get any url of images that are in the markdown - // Save it the local path on the device - // and replace the url with the local path - final imageRegex = RegExp(r'!\[.*?\]\((.*?)\)'); - - final matches = imageRegex.allMatches(content); - - Future saveAsset(String url) async { - if (ProjectService.instance.isAssetFile(File(url))) return; - // Look by hashcode to see if the asset is already cached - - final file = SlideAsset.cached(url); - - if (await file.exists()) { - controller.markNeeded(file); - return; - } - - final client = HttpClient(); - final request = await client.getUrl(Uri.parse(url)); - final response = await request.close(); - - final contentType = response.headers.contentType; - // Default to .jpg if no extension is found - var assetData = await consolidateHttpClientResponseBytes(response); - final extension = contentType?.subType ?? 'jpg'; - - final fileType = AssetFileType.tryParse(extension); - - if (fileType == null) { - log('Invalid file type: $extension'); - return; - } - - final codec = await ui.instantiateImageCodec(assetData); - - if (codec.frameCount > 1) { - // get half of the frame count - final frameCount = codec.frameCount ~/ 2; - - for (var i = 0; i < frameCount; i++) { - await codec.getNextFrame(); - } - final frame = await codec.getNextFrame(); - - final bytes = - await frame.image.toByteData(format: ui.ImageByteFormat.png); - assetData = bytes!.buffer.asUint8List(); - } - - await file.writeAsBytes(assetData); - - controller.markNeeded(file); - } - - for (final Match match in matches) { - final assetUri = match.group(1); - if (assetUri == null) continue; - - await saveAsset(assetUri); - } - - final background = slide.background; - - if (background != null) { - await saveAsset(background); - } - - if (slide is ImageSlide) { - final imageSource = slide.options.src; - await saveAsset(imageSource); - } - - return controller; - } -} diff --git a/lib/components/atoms/linear_progresss_indicator_widget.dart b/lib/components/atoms/linear_progresss_indicator_widget.dart deleted file mode 100644 index 4f51ae78..00000000 --- a/lib/components/atoms/linear_progresss_indicator_widget.dart +++ /dev/null @@ -1,70 +0,0 @@ -import 'package:flutter/material.dart'; - -class AnimatedLinearProgressIndicator extends StatefulWidget { - final double progress; - - const AnimatedLinearProgressIndicator({ - super.key, - required this.progress, - }); - - @override - _AnimatedLinearProgressIndicatorState createState() => - _AnimatedLinearProgressIndicatorState(); -} - -class _AnimatedLinearProgressIndicatorState - extends State - with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _animation; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - vsync: this, - duration: const Duration(milliseconds: 100), - ); - _animation = Tween(begin: 0.0, end: widget.progress) - .animate(_animationController); - _animationController.forward(); - } - - @override - void didUpdateWidget(covariant AnimatedLinearProgressIndicator oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.progress != oldWidget.progress) { - _animation = Tween( - begin: _animation.value, - end: widget.progress, - ).animate(_animationController); - - if (_animationController.isAnimating) { - _animationController.forward(); - } else { - _animationController.forward(from: 0.0); - } - } - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return LinearProgressIndicator( - minHeight: 10, - borderRadius: BorderRadius.circular(10), - value: _animation.value, - ); - }, - ); - } -} diff --git a/lib/components/atoms/markdown_viewer.dart b/lib/components/atoms/markdown_viewer.dart deleted file mode 100644 index 12f375c9..00000000 --- a/lib/components/atoms/markdown_viewer.dart +++ /dev/null @@ -1,169 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:markdown_viewer/markdown_viewer.dart'; -import 'package:url_launcher/url_launcher.dart'; - -import '../../helpers/measure_size.dart'; -import '../../helpers/syntax_highlighter.dart'; -import '../../helpers/utils.dart'; -import '../../providers/slide_provider.dart'; -import '../../superdeck.dart'; -import 'cache_image_widget.dart'; - -class AnimatedMarkdownViewer extends ImplicitlyAnimatedWidget { - final String content; - final SlideSpec spec; - - const AnimatedMarkdownViewer({ - super.key, - required this.content, - required this.spec, - required super.duration, - super.curve = Curves.linear, - }); - - @override - ImplicitlyAnimatedWidgetState createState() => - _AnimatedMarkdownViewerState(); -} - -class _AnimatedMarkdownViewerState - extends AnimatedWidgetBaseState { - SlideSpecTween? _styleTween; - - @override - void forEachTween(TweenVisitor visitor) { - _styleTween = visitor( - _styleTween, - widget.spec, - (dynamic value) => SlideSpecTween(begin: value), - ) as SlideSpecTween?; - } - - @override - Widget build(BuildContext context) { - return MarkdownViewer( - widget.content, - enableTaskList: true, - enableSuperscript: false, - enableSubscript: false, - enableFootnote: false, - enableImageSize: true, - enableKbd: false, - syntaxExtensions: const [], - elementBuilders: const [], - imageBuilder: _imageBuilder, - onTapLink: (href, title) async { - // open link in the browser - if (href == null || href.isEmpty) return; - final url = Uri.parse(href); - await launchUrl(url); - }, - highlightBuilder: (text, language, infoString) { - return [ - TextSpan( - style: _styleTween!.evaluate(animation).code?.codeSpan, - children: SyntaxHighlight.render(text, language), - ), - ]; - }, - copyIconBuilder: (bool copied) { - return const SizedBox(); - // return Padding( - // padding: const EdgeInsets.all(16.0), - // child: Icon( - // copied ? Icons.check : Icons.copy, - // color: _styleTween!.evaluate(animation).code?.copyIconColor ?? - // Colors.grey, - // size: 24, - // ), - // ); - }, - styleSheet: _styleTween!.evaluate(animation).toStyle(), - ); - } -} - -class SlideSpecTween extends Tween { - SlideSpecTween({super.begin, super.end}); - @override - SlideSpec lerp(double t) { - return begin?.lerp(end!, t) ?? end ?? SlideSpec(); - } -} - -Widget _imageBuilder( - Uri uri, - MarkdownImageInfo info, -) { - return Builder( - builder: (context) { - final size = SlideConstraints.of(context).biggest; - - final spec = SlideProvider.specOf(context); - final imageSpec = spec.image; - final constraints = calculateConstraints(size, spec.contentContainer); - return ConstrainedBox( - constraints: constraints, - child: CacheImage( - url: uri.toString(), - size: constraints.biggest, - spec: imageSpec.copyWith( - width: info.width ?? imageSpec.width, - height: info.height ?? imageSpec.height, - ), - ), - ); - }, - ); -} - -List updateTextColor( - List originalSpans, - List targetLines, - Color newColor, -) { - // Check if the target line is within the range of the list - if (targetLines.isEmpty) { - return originalSpans; - } - - // Clone the original list to avoid mutating it directly - List updatedSpans = List.from(originalSpans); - - // Detect line break from the list of text spans - // This is done by checking if the previous span is a line break - // If it is, then the current span is the start of a new line - int line = 1; - - for (int i = 0; i < updatedSpans.length; i++) { - if (i > 0) { - final currentValue = updatedSpans[i].text ?? ''; - - if (currentValue.startsWith('\n')) { - line++; - } - } - final originalSpan = originalSpans[i]; - - final textStyle = originalSpan.style ?? const TextStyle(); - - if (targetLines.contains(line)) { - updatedSpans[i] = TextSpan( - text: originalSpan.text, - children: originalSpan.children, - recognizer: originalSpan.recognizer, - mouseCursor: originalSpan.mouseCursor, - onEnter: originalSpan.onEnter, - onExit: originalSpan.onExit, - semanticsLabel: originalSpan.semanticsLabel, - locale: originalSpan.locale, - spellOut: originalSpan.spellOut, - style: textStyle.copyWith( - backgroundColor: newColor, - ), - ); - } - } - - return updatedSpans; -} diff --git a/lib/components/atoms/slide_thumbnail.dart b/lib/components/atoms/slide_thumbnail.dart deleted file mode 100644 index 5d0a2fa4..00000000 --- a/lib/components/atoms/slide_thumbnail.dart +++ /dev/null @@ -1,216 +0,0 @@ -import 'dart:async'; -import 'dart:io'; - -import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../../helpers/constants.dart'; -import '../../services/snapshot_service.dart'; -import '../../superdeck.dart'; -import '../molecules/scaled_app.dart'; -import 'cache_image_widget.dart'; -import 'loading_indicator.dart'; -import 'slide_view.dart'; - -final _previewStyle = AnimatedStyle( - Style( - $box.color.grey.shade900(), - $box.margin.all(8), - $box.border.all.width(2), - $box.shadow( - color: Colors.black.withOpacity(0.5), - blurRadius: 4, - spreadRadius: 1, - ), - ), - duration: const Duration(milliseconds: 300), - curve: Curves.ease, -); - -// ignore: non_constant_identifier_names -final PreviewBox = _previewStyle.box; - -class SlideThumbnail extends StatefulWidget { - final bool selected; - final VoidCallback onTap; - final int index; - final Slide slide; - - const SlideThumbnail({ - super.key, - required this.selected, - required this.index, - required this.onTap, - required this.slide, - }); - - @override - State createState() => _SlideThumbnailState(); -} - -class _SlideThumbnailState extends State { - late final imageGenerator = SnapshotService.instance; - late File _thumbnailFile = SlideAsset.thumbnail(widget.slide); - - late final _thumbnailLoader = futureSignal(() { - return kCanRunProcess - ? _generateThumbnail() - : Future.value(_getLocalAsset()); - }); - - @override - void dispose() { - super.dispose(); - - _thumbnailLoader.dispose(); - } - - @override - void didUpdateWidget(SlideThumbnail oldWidget) { - super.didUpdateWidget(oldWidget); - if (oldWidget.slide.hashKey != widget.slide.hashKey) { - _thumbnailFile = SlideAsset.thumbnail(widget.slide); - _thumbnailLoader.refresh(); - } - } - - String _getLocalAsset() { - return _thumbnailFile.path; - } - - Future _generateThumbnail() async { - if (await _thumbnailFile.exists()) { - return _getLocalAsset(); - } - - final imageData = await imageGenerator.generate( - // ignore: use_build_context_synchronously - quality: SnapshotQuality.low, - slide: widget.slide, - ); - - await _thumbnailFile.writeAsBytes(imageData); - - return _getLocalAsset(); - } - - @override - Widget build(BuildContext context) { - return LayoutBuilder(builder: (context, constraints) { - final selectedColor = widget.selected ? Colors.blue : Colors.transparent; - - final result = _thumbnailLoader.watch(context); - - final child = LoadingOverlay( - isLoading: result.isLoading, - child: result.map( - data: (path) { - return Image( - image: getImageProvider( - context: context, - url: path, - targetSize: constraints.biggest, - ), - ); - }, - loading: () { - return const Center( - child: CircularProgressIndicator(), - ); - }, - error: (error, _) { - return const Center( - child: Text('Error loading image'), - ); - }, - ), - ); - - return GestureDetector( - onTap: widget.onTap, - child: PreviewBox( - style: Style( - $box.border.all.color(selectedColor), - ), - child: AbsorbPointer( - child: AspectRatio( - aspectRatio: kAspectRatio, - child: Stack( - children: [ - child, - Positioned( - top: 0, - right: 0, - left: 0, - child: SizedBox( - child: result.isRefreshing - ? const LinearProgressIndicator( - minHeight: 3, - backgroundColor: Colors.transparent, - ) - : null, - ), - ), - Positioned( - right: 0, - bottom: 0, - child: Container( - padding: const EdgeInsets.fromLTRB(12, 4, 12, 4), - margin: const EdgeInsets.all(1), - color: Colors.black.withOpacity(0.5), - child: Text( - '${widget.index + 1}', - style: const TextStyle( - color: Colors.white, - fontWeight: FontWeight.bold, - ), - ), - ), - ), - ], - ), - ), - ), - ), - ); - }); - } -} - -class SlideThumbnailDynamic extends StatelessWidget { - final bool selected; - final VoidCallback onTap; - final int index; - final Slide slide; - - const SlideThumbnailDynamic({ - super.key, - required this.selected, - required this.index, - required this.onTap, - required this.slide, - }); - - @override - Widget build(BuildContext context) { - final selectedColor = selected ? Colors.blue : Colors.transparent; - - return GestureDetector( - onTap: onTap, - child: PreviewBox( - style: Style( - $box.border.all.color(selectedColor), - ), - child: AbsorbPointer( - child: AspectRatio( - aspectRatio: kAspectRatio, - child: ScaledWidget( - child: SlideView( - slide, - ), - )), - ), - ), - ); - } -} diff --git a/lib/components/atoms/slide_view.dart b/lib/components/atoms/slide_view.dart deleted file mode 100644 index 60a60b40..00000000 --- a/lib/components/atoms/slide_view.dart +++ /dev/null @@ -1,128 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../../helpers/constants.dart'; -import '../../helpers/layout_builder.dart'; -import '../../helpers/measure_size.dart'; -import '../../providers/slide_provider.dart'; -import '../../superdeck.dart'; -import 'cache_image_widget.dart'; -import 'transition_widget.dart'; - -class SlideView extends StatelessWidget { - // If SlideView is a snapshot for image generation - final bool _isSnapshot; - const SlideView( - this.slide, { - super.key, - }) : _isSnapshot = false; - - const SlideView.snapshot( - this.slide, { - super.key, - }) : _isSnapshot = true; - - final Slide slide; - - @override - Widget build(BuildContext context) { - final slide = this.slide; - final variant = slide.styleVariant; - final style = sdController.style.watch(context); - - final variantStyle = style.applyVariant(variant); - - final backgroundWidget = slide.background != null - ? CacheImage( - url: slide.background!, - fit: BoxFit.cover, - size: kResolution, - alignment: Alignment.center, - ) - : const SizedBox(); - - final duration = _isSnapshot ? Duration.zero : null; - - return TransitionWidget( - key: ValueKey(slide.transition?.copyWith(duration: duration)), - transition: slide.transition, - child: Pressable( - onPress: () {}, - autofocus: true, - child: SpecBuilder( - style: variantStyle, - builder: (context) { - final spec = SlideSpec.of(context); - return Builder(builder: (context) { - return AnimatedBoxSpecWidget( - spec: spec.outerContainer, - duration: duration ?? const Duration(milliseconds: 300), - child: Stack( - children: [ - Positioned.fill(child: backgroundWidget), - AnimatedBoxSpecWidget( - spec: spec.innerContainer, - duration: const Duration(milliseconds: 300), - child: SlideProvider( - slide: slide, - spec: spec, - examples: sdController.examples.watch(context), - assets: sdController.assets.watch(context), - isSnapshot: _isSnapshot, - child: SlideConstraints( - (_) { - if (slide is SimpleSlide) { - return SimpleSlideBuilder(config: slide); - } else if (slide is WidgetSlide) { - return WidgetSlideBuilder(config: slide); - } else if (slide is ImageSlide) { - return ImageSlideBuilder(config: slide); - } else if (slide is TwoColumnSlide) { - return TwoColumnSlideBuilder(config: slide); - } else if (slide is TwoColumnHeaderSlide) { - return TwoColumnHeaderSlideBuilder(config: slide); - } else if (slide is InvalidSlide) { - return InvalidSlideBuilder(config: slide); - } else { - throw UnimplementedError( - 'Slide config not implemented', - ); - } - }, - ), - ), - ), - ], - ), - ); - }); - }, - ), - ), - ); - } -} - -class SlideConstraintsProvider extends InheritedWidget { - const SlideConstraintsProvider({ - required this.constraints, - required super.child, - super.key, - }); - - final BoxConstraints constraints; - - static BoxConstraints of(BuildContext context) { - final slideConstraints = - context.dependOnInheritedWidgetOfExactType(); - if (slideConstraints == null) { - throw Exception('SlideConstraints not found in context'); - } - return slideConstraints.constraints; - } - - @override - bool updateShouldNotify(SlideConstraintsProvider oldWidget) { - return oldWidget.constraints != constraints; - } -} diff --git a/lib/components/molecules/code_preview.dart b/lib/components/molecules/code_preview.dart deleted file mode 100644 index 39f31fbe..00000000 --- a/lib/components/molecules/code_preview.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/material.dart'; - -class CodePreview extends StatelessWidget { - const CodePreview({ - this.child, - super.key, - }); - - final Widget? child; - - @override - Widget build(BuildContext context) { - // final options = SlideDataProvider.of(context); - return Center(child: child); - } -} diff --git a/lib/components/molecules/slide_preview.dart b/lib/components/molecules/slide_preview.dart deleted file mode 100644 index 9effa3e7..00000000 --- a/lib/components/molecules/slide_preview.dart +++ /dev/null @@ -1,77 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../../helpers/measure_size.dart'; -import '../../helpers/utils.dart'; -import '../../styles/style_util.dart'; -import '../../superdeck.dart'; -import '../atoms/markdown_viewer.dart'; -import '../atoms/slide_view.dart'; -import 'scaled_app.dart'; -import 'split_view.dart'; - -class SlidePreview extends StatelessWidget { - const SlidePreview( - this.slide, { - super.key, - }); - - final Slide slide; - - @override - Widget build(BuildContext context) { - final panelSize = SplitViewProvider.panelSizeOf(context); - var paddingSize = panelSize / (context.isSmall ? 5.0 : 20.0); - - return Center( - child: Container( - margin: EdgeInsets.all(paddingSize), - decoration: BoxDecoration( - color: const Color.fromARGB(144, 0, 0, 0), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.3), - blurRadius: 6, - spreadRadius: 3, - ), - ], - ), - child: ScaledWidget( - child: SlideView(slide), - ), - ), - ); - } -} - -class SlideMarkdownPreview extends StatelessWidget { - const SlideMarkdownPreview({ - super.key, - required this.slide, - }); - - final Slide slide; - - @override - Widget build(BuildContext context) { - final rawYaml = slide.raw; - final options = '#### Options\n```yaml\n${rawYaml.trim()}\n```\n'; - final data = '#### Content\n```markdown\n${slide.data}\n```\n'; - final s = SlideSpecUtility.self; - return SpecBuilder( - style: defaultStyle.merge( - Style( - s.code.span.fontSize(14), - ), - ), - builder: (mix) { - return SlideConstraints((_) { - return AnimatedMarkdownViewer( - content: "$options\n$data\n", - spec: SlideSpec.of(context), - duration: Duration.zero, - ); - }); - }, - ); - } -} diff --git a/lib/components/molecules/slide_thumbnail_list.dart b/lib/components/molecules/slide_thumbnail_list.dart deleted file mode 100644 index afa15bc7..00000000 --- a/lib/components/molecules/slide_thumbnail_list.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'package:collection/collection.dart'; -import 'package:flutter/material.dart'; -import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../../models/slide_model.dart'; -import '../../providers/controller.dart'; -import '../atoms/slide_thumbnail.dart'; - -class SlideThumbnailList extends StatefulWidget { - const SlideThumbnailList({ - super.key, - required this.slides, - required this.currentSlide, - required this.onSelect, - required this.scrollDirection, - }); - - final List slides; - final int currentSlide; - final void Function(int) onSelect; - final Axis scrollDirection; - - @override - State createState() => _SlideThumbnailListState(); -} - -class _SlideThumbnailListState extends State { - final _itemScrollController = ItemScrollController(); - final _itemPositionsListener = ItemPositionsListener.create(); - - var _visibleItems = []; - - @override - void didUpdateWidget(covariant SlideThumbnailList oldWidget) { - super.didUpdateWidget(oldWidget); - - if (widget.currentSlide != oldWidget.currentSlide) { - goToPage(widget.currentSlide); - } - } - - Future goToPage(int page, {bool animate = true}) async { - if (page < 0 || page >= widget.slides.length) return; - - const duration = Duration(milliseconds: 300); - const curve = Curves.easeInOutCubic; - - final visibleItem = _visibleItems.firstWhereOrNull((e) => e.index == page); - - double alignment; - - if (visibleItem == null) { - final isBeginning = _visibleItems.first.index > page; - - alignment = isBeginning ? 0 : 0.75; - } else { - if (visibleItem.itemTrailingEdge > 1) { - final totalSpace = - visibleItem.itemTrailingEdge - visibleItem.itemLeadingEdge; - alignment = 1 - totalSpace; - } else if (visibleItem.itemLeadingEdge < 0) { - alignment = 0; - } else { - alignment = visibleItem.itemLeadingEdge; - } - } - _itemScrollController.scrollTo( - index: page, - alignment: alignment, - duration: duration, - curve: curve, - ); - - widget.onSelect(page); - } - - @override - void initState() { - super.initState(); - _itemPositionsListener.itemPositions.addListener(() { - _visibleItems = _itemPositionsListener.itemPositions.value.toList(); - }); - } - - @override - Widget build(BuildContext context) { - return Container( - color: const Color.fromARGB(108, 0, 0, 0), - child: ScrollablePositionedList.builder( - scrollDirection: widget.scrollDirection, - itemCount: widget.slides.length, - itemPositionsListener: _itemPositionsListener, - itemScrollController: _itemScrollController, - padding: const EdgeInsets.all(20), - itemBuilder: (context, idx) { - final slide = widget.slides[idx]; - SDController.instance.style.watch(context); - - return SlideThumbnail( - index: idx, - selected: idx == widget.currentSlide, - onTap: () => goToPage(idx), - slide: slide, - ); - }), - ); - } -} diff --git a/lib/components/molecules/split_view.dart b/lib/components/molecules/split_view.dart deleted file mode 100644 index c2eeed9b..00000000 --- a/lib/components/molecules/split_view.dart +++ /dev/null @@ -1,213 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../../helpers/utils.dart'; -import '../../superdeck.dart'; -import 'slide_thumbnail_list.dart'; - -// ignore: non_constant_identifier_names -final SlidePreviewBox = Style( - $box.color.grey.shade900(), - $box.margin.all(8), - $box.maxHeight(140), - $box.shadow( - color: Colors.black.withOpacity(0.5), - blurRadius: 4, - spreadRadius: 1, - ), -).box; - -class SplitView extends StatefulWidget { - final Widget child; - - const SplitView({ - super.key, - required this.child, - }); - - @override - // ignore: library_private_types_in_public_api - _SplitViewState createState() => _SplitViewState(); -} - -class _SplitViewState extends State - with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _animation; - - late final navigation = NavigationProvider.instance; - late final superdeck = SDController.instance; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - vsync: this, - duration: Durations.medium1, - ); - _animation = Tween(begin: 0.0, end: 1.0).animate( - CurvedAnimation( - parent: _animationController, - curve: Curves.ease, - ), - ); - - if (navigation.sideIsOpen.value) { - _animationController.value = 1.0; - } - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - final slides = superdeck.slides.watch(context); - - navigation.sideIsOpen.listen(context, () { - if (navigation.sideIsOpen.value) { - _animationController.forward(); - } else { - _animationController.reverse(); - } - }); - - final sideWidth = context.isMobileLandscape ? 200.0 : 400.0; - const sideHeight = 200.0; - final currentSlide = navigation.currentSlide.watch(context); - final sideIsOpen = navigation.sideIsOpen.watch(context); - - final isSmall = context.isSmall || context.isMobileLandscape; - - final sidePanel = SlideThumbnailList( - scrollDirection: isSmall ? Axis.horizontal : Axis.vertical, - currentSlide: currentSlide, - onSelect: navigation.goToSlide, - slides: slides, - ); - - return LayoutBuilder( - builder: (context, constraints) { - return AnimatedBuilder( - animation: _animation, - builder: (context, child) { - final animatedWidth = _animation.value * sideWidth; - final animatedHeight = _animation.value * sideHeight; - - Offset offset; - if (isSmall) { - offset = Offset(0, -(animatedHeight - sideHeight)); - } else { - offset = Offset(animatedWidth - sideWidth, 0); - } - - EdgeInsets padding; - - if (isSmall) { - padding = EdgeInsets.only(bottom: animatedHeight); - } else { - padding = EdgeInsets.only(left: animatedWidth); - } - - final panelSize = isSmall ? animatedHeight : animatedWidth; - - Widget drawer = Transform.translate( - offset: offset, - child: SizedBox( - width: isSmall ? null : sideWidth, - height: isSmall ? sideHeight : null, - child: sidePanel, - ), - ); - - // Align at the bottom if its a small screen - if (isSmall) { - drawer = Align( - alignment: Alignment.bottomCenter, - child: drawer, - ); - } - - final child = SplitViewProvider( - panelSize: panelSize, - isOpen: sideIsOpen, - size: constraints.biggest, - child: Padding( - padding: padding, - child: widget.child, - ), - ); - - return Stack( - children: [child, drawer], - ); - }, - ); - }, - ); - } -} - -enum SplitViewProviderAspect { - panelSize, - isOpen, - size, -} - -class SplitViewProvider extends InheritedModel { - final double panelSize; - final bool isOpen; - final Size size; - - const SplitViewProvider({ - super.key, - required this.panelSize, - required this.isOpen, - required this.size, - required super.child, - }); - - @override - bool updateShouldNotify(covariant SplitViewProvider oldWidget) { - return panelSize != oldWidget.panelSize || - isOpen != oldWidget.isOpen || - size != oldWidget.size; - } - - @override - bool updateShouldNotifyDependent(covariant SplitViewProvider oldWidget, - Set dependencies) { - if (dependencies.contains(SplitViewProviderAspect.panelSize) && - panelSize != oldWidget.panelSize) { - return true; - } - if (dependencies.contains(SplitViewProviderAspect.isOpen) && - isOpen != oldWidget.isOpen) { - return true; - } - if (dependencies.contains(SplitViewProviderAspect.size) && - size != oldWidget.size) { - return true; - } - return false; - } - - static SplitViewProvider of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType()!; - } - - static Size sizeOf(BuildContext context) { - return SplitViewProvider.of(context).size; - } - - static double panelSizeOf(BuildContext context) { - return SplitViewProvider.of(context).panelSize; - } - - static bool isOpenOf(BuildContext context) { - return SplitViewProvider.of(context).isOpen; - } -} diff --git a/lib/components/organisms/app_shell.dart b/lib/components/organisms/app_shell.dart deleted file mode 100644 index a7f69559..00000000 --- a/lib/components/organisms/app_shell.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:go_router/go_router.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../../helpers/constants.dart'; -import '../../helpers/utils.dart'; -import '../../superdeck.dart'; -import 'drawer.dart'; - -final scaffoldKey = GlobalKey(); - -/// Builds the "shell" for the app by building a Scaffold with a -/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. -class ScaffoldWithNavBar extends StatefulWidget { - /// Constructs an [ScaffoldWithNavBar]. - const ScaffoldWithNavBar({ - required this.navigationShell, - Key? key, - }) : super(key: key ?? const ValueKey('ScaffoldWithNavBar')); - - /// The navigation shell and container for the branch Navigators. - final StatefulNavigationShell navigationShell; - - @override - State createState() => _ScaffoldWithNavBarState(); -} - -class _ScaffoldWithNavBarState extends State - with SingleTickerProviderStateMixin { - late AnimationController _animationController; - late Animation _animation; - - @override - void initState() { - super.initState(); - _animationController = AnimationController( - vsync: this, - duration: Durations.short3, - ); - _animation = CurvedAnimation( - parent: _animationController, - curve: Curves.ease, - ); - - if (navigation.sideIsOpen.value) { - _animationController.forward(); - } - } - - @override - void dispose() { - _animationController.dispose(); - super.dispose(); - } - - final navigation = NavigationProvider.instance; - - void _onTap(BuildContext context, int index) { - if (index == SideMenu.clearCache.index) { - sdController.clearGenerated(); - return; - } - // When navigating to a new branch, it's recommended to use the goBranch - // method, as doing so makes sure the last navigation state of the - // Navigator for the branch is restored. - widget.navigationShell.goBranch( - index, - // A common pattern when using bottom navigation bars is to support - // navigating to the initial location when tapping the item that is - // already active. This example demonstrates how to support this behavior, - // using the initialLocation parameter of goBranch. - // initialLocation: index == navigationShell.currentIndex, - ); - } - - @override - Widget build(BuildContext context) { - final slides = sdController.slides.watch(context); - - navigation.sideIsOpen.listen(context, () { - if (navigation.sideIsOpen.value) { - _animationController.forward(); - } else { - _animationController.reverse(); - } - }); - final isSmall = context.isSmall; - - final totalInvalid = slides.whereType().length; - - final bindings = { - const SingleActivator( - LogicalKeyboardKey.arrowRight, - ): navigation.nextSlide, - const SingleActivator( - LogicalKeyboardKey.arrowDown, - ): navigation.nextSlide, - const SingleActivator( - LogicalKeyboardKey.space, - ): navigation.nextSlide, - const SingleActivator( - LogicalKeyboardKey.arrowLeft, - ): navigation.previousSlide, - const SingleActivator( - LogicalKeyboardKey.arrowUp, - ): navigation.previousSlide, - }; - - void onTap(int index) { - _onTap(context, index); - } - - final menuItems = kCanRunProcess ? SideMenu.devMenu : SideMenu.prodMenu; - - final navigationRail = NavigationRail( - extended: false, - selectedIndex: widget.navigationShell.currentIndex, - onDestinationSelected: onTap, - minWidth: 80, - leading: const SizedBox(height: 20), - labelType: NavigationRailLabelType.none, - destinations: menuItems.map( - (e) { - return NavigationRailDestination( - icon: Icon(e.icon, size: 20), - label: Text(e.label), - ); - }, - ).toList(), - ); - - final sideNavBar = !isSmall - ? AnimatedBuilder( - animation: _animation, - builder: (context, child) { - return SizeTransition( - sizeFactor: _animation, - axis: Axis.horizontal, - child: navigationRail, - ); - }) - : null; - - return CallbackShortcuts( - bindings: bindings, - child: Scaffold( - bottomNavigationBar: null, - key: scaffoldKey, - floatingActionButtonLocation: isSmall - ? FloatingActionButtonLocation.miniEndFloat - : FloatingActionButtonLocation.miniStartFloat, - floatingActionButton: FloatingActionButton.small( - onPressed: navigation.toggleSide, - child: Badge( - label: Text(totalInvalid.toString()), - isLabelVisible: totalInvalid != 0, - child: const Icon(Icons.menu), - ), - ), - body: isSmall - ? widget.navigationShell - : Row( - children: [ - sideNavBar ?? Container(), - Expanded(child: widget.navigationShell), - ], - ), - ), - ); - } -} diff --git a/lib/components/superdeck_app.dart b/lib/components/superdeck_app.dart deleted file mode 100644 index e153cda0..00000000 --- a/lib/components/superdeck_app.dart +++ /dev/null @@ -1,187 +0,0 @@ -import 'dart:async'; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:go_router/go_router.dart'; -import 'package:localstorage/localstorage.dart'; -import 'package:signals/signals_flutter.dart'; -import 'package:window_manager/window_manager.dart'; - -import '../../helpers/syntax_highlighter.dart'; -import '../../superdeck.dart'; -import '../helpers/constants.dart'; -import '../helpers/theme.dart'; -import '../screens/export_screen.dart'; -import '../screens/home_screen.dart'; -import 'atoms/loading_indicator.dart'; -import 'molecules/exception_widget.dart'; - -final kAppKey = GlobalKey(); -final _uniqueKey = UniqueKey(); - -class SuperDeckApp extends StatefulWidget { - const SuperDeckApp({ - super.key, - this.style, - this.examples = const [], - }); - - final Style? style; - final List examples; - - static bool _isInitialized = false; - - static Future initialize() async { - // Return if its initialized - if (SuperDeckApp._isInitialized) return; - - WidgetsFlutterBinding.ensureInitialized(); - - SignalsObserver.instance = null; - - await Future.wait([ - initLocalStorage(), - SyntaxHighlight.initialize(), - _initializeWindowManager(), - ]); - - SuperDeckApp._isInitialized = true; - } - - @override - // ignore: library_private_types_in_public_api - _SuperDeckAppState createState() => _SuperDeckAppState(); -} - -class _SuperDeckAppState extends State { - late final _initialize = futureSignal(() async { - await SuperDeckApp.initialize(); - - return sdController.initialize( - style: widget.style, - examples: widget.examples, - ); - }); - - @override - void didUpdateWidget(SuperDeckApp oldWidget) { - super.didUpdateWidget(oldWidget); - if (widget.style != oldWidget.style || - !listEquals(widget.examples, oldWidget.examples)) { - _initialize.refresh(); - } - } - - @override - void dispose() { - _initialize.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return Theme( - data: theme, - child: Builder(builder: (context) { - return MixTheme( - data: MixThemeData.withMaterial(), - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Superdeck', - routerConfig: _router, - theme: Theme.of(context), - key: kAppKey, - builder: (context, child) { - final result = _initialize.watch(context); - - return LoadingOverlay( - isLoading: result.isLoading, - key: _uniqueKey, - child: result.map( - data: (_) => child!, - loading: () => const SizedBox(), - error: (error, _) { - return ExceptionWidget( - error, - onRetry: _initialize.reload, - ); - }, - ), - ); - }, - ), - ); - }), - ); - } -} - -Future _initializeWindowManager() async { - if (kIsWeb) return; - - // Must add this line. - await windowManager.ensureInitialized(); - - const windowOptions = WindowOptions( - size: kResolution, - backgroundColor: Colors.black, - skipTaskbar: false, - minimumSize: kResolution, - titleBarStyle: TitleBarStyle.hidden, - ); - - windowManager.waitUntilReadyToShow(windowOptions, () async { - await windowManager.show(); - await windowManager.focus(); - }); - - await windowManager.setAspectRatio(kAspectRatio); -} - -final _rootNavigatorKey = GlobalKey(debugLabel: 'root'); -final _sectionANavigatorKey = - GlobalKey(debugLabel: 'sectionANav'); - -final _router = GoRouter( - navigatorKey: _rootNavigatorKey, - initialLocation: '/', - routes: [ - StatefulShellRoute.indexedStack( - builder: ( - BuildContext context, - GoRouterState state, - StatefulNavigationShell navigationShell, - ) { - // Return the widget that implements the custom shell (in this case - // using a BottomNavigationBar). The StatefulNavigationShell is passed - // to be able access the state of the shell and to navigate to other - // branches in a stateful way. - return ScaffoldWithNavBar(navigationShell: navigationShell); - }, - branches: [ - // The route branch for the first tab of the bottom navigation bar. - StatefulShellBranch( - navigatorKey: _sectionANavigatorKey, - routes: [ - GoRoute( - path: '/', - builder: (BuildContext context, GoRouterState state) { - return const HomeScreen(); - }, - ), - ], - ), - StatefulShellBranch( - routes: [ - GoRoute( - path: '/export', - builder: (BuildContext context, GoRouterState state) { - return const ExportScreen(); - }, - ), - ], - ), - ], - ), - ], -); diff --git a/lib/helpers/layout_builder.dart b/lib/helpers/layout_builder.dart deleted file mode 100644 index 1701948d..00000000 --- a/lib/helpers/layout_builder.dart +++ /dev/null @@ -1,265 +0,0 @@ -import 'package:flutter/material.dart'; - -import '../components/atoms/cache_image_widget.dart'; -import '../components/molecules/code_preview.dart'; -import '../components/molecules/slide_content.dart'; -import '../providers/slide_provider.dart'; -import '../superdeck.dart'; -import 'constants.dart'; -import 'measure_size.dart'; - -abstract class SlideBuilder extends StatelessWidget { - final T config; - - const SlideBuilder({required this.config, super.key}); - - Widget buildContent() { - return _buildContent( - config.data, - config.contentOptions, - ); - } - - @protected - Widget _buildContent(String content, ContentOptions? options) { - return SlideContent(data: content, options: options); - } - - Widget buildContentSection(SectionData section) { - return Expanded( - flex: section.options.flex, - child: _buildContent(section.content, section.options), - ); - } -} - -class SimpleSlideBuilder extends SlideBuilder { - const SimpleSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) => buildContent(); -} - -class InvalidSlideBuilder extends SlideBuilder { - const InvalidSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) { - const red = Color.fromARGB(255, 166, 6, 6); - - final s = SlideSpecUtility.self; - - final style = Style( - s.textStyle.color(Colors.white), - s.h1.textStyle.color(const Color.fromARGB(255, 71, 1, 1)), - s.h1.textStyle.fontSize(36.0), - s.h1.textStyle.bold(), - s.h2.padding.top(0), - s.h2.textStyle.bold(), - s.h2.textStyle.color.yellow(), - s.code.span.color.yellow(), - s.code.span.backgroundColor(const Color.fromARGB(255, 84, 6, 6)), - ); - - return SpecBuilder( - style: style, - builder: (context) { - // Maybe there are no validation errors just return the content - return Container( - decoration: BoxDecoration( - color: red, - border: Border.all(color: red, width: 20), - ), - child: buildContent(), - ); - }); - } -} - -abstract class SplitSlideBuilder extends SlideBuilder { - const SplitSlideBuilder({required super.config, super.key}); - - Widget buildSplitSlide(Widget side) { - final position = config.options.position; - final flex = config.options.flex; - - List children = [ - buildContentSection(( - content: config.data, - options: config.contentOptions ?? const ContentOptions(), - )), - Expanded(flex: flex, child: side), - ]; - - if (position == LayoutPosition.left || position == LayoutPosition.top) { - children = children.reversed.toList(); - } - - final isVertical = - position == LayoutPosition.top || position == LayoutPosition.bottom; - - if (isVertical) { - return Column(children: children); - } else { - return Row(children: children); - } - } -} - -class WidgetSlideBuilder extends SplitSlideBuilder { - const WidgetSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) { - final options = config.options; - - final examples = SlideProvider.examplesOf(context); - - final builder = examples[options.name]; - - final side = SlideConstraints( - (size) { - return MediaQuery( - data: MediaQueryData(size: size), - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: size.width, - maxHeight: size.height, - ), - child: CodePreview( - child: builder?.call(options.args), - ), - ), - ); - }, - ); - - return buildSplitSlide(side); - } -} - -class ImageSlideBuilder extends SplitSlideBuilder { - const ImageSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) { - final spec = SlideProvider.specOf(context); - - final src = config.options.src; - final boxFit = config.options.fit?.toBoxFit() ?? spec.image.fit; - - // THis slide breaks in half and I want to calculate the size based on if its in top or bottom - // or left or right. Also there is a property called flex which is how much of the slide it takes - // so I can use that to calculate the size of the canvas - final firstHalf = - config.contentOptions?.flex ?? const ContentOptions().flex; - final imageHalf = config.options.flex; - -// available size const width = 1280.0; -//const height = 720.0; - - double width; - double height; - const availableSize = kResolution; - if (config.options.position.isHorizontal()) { - width = availableSize.width * firstHalf / (firstHalf + imageHalf); - height = availableSize.height; - } else { - width = availableSize.width; - height = availableSize.height * firstHalf / (firstHalf + imageHalf); - } - - final side = Container( - height: spec.image.height, - width: spec.image.width, - alignment: spec.image.alignment, - decoration: BoxDecoration( - image: DecorationImage( - image: getImageProvider( - context: context, - url: src, - targetSize: Size( - width, - height, - ), - ), - centerSlice: spec.image.centerSlice, - repeat: spec.image.repeat ?? ImageRepeat.noRepeat, - filterQuality: spec.image.filterQuality ?? FilterQuality.low, - fit: boxFit, - ), - ), - ); - - return buildSplitSlide(side); - } -} - -class TwoColumnSlideBuilder extends SlideBuilder { - const TwoColumnSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) { - final options = config.contentOptions ?? const ContentOptions(); - final alignment = options.alignment; - - return Container( - alignment: alignment.toAlignment(), - child: Row( - children: [ - buildContentSection(config.left), - buildContentSection(config.right), - ], - ), - ); - } -} - -class TwoColumnHeaderSlideBuilder extends SlideBuilder { - const TwoColumnHeaderSlideBuilder({required super.config, super.key}); - - @override - Widget build(BuildContext context) { - final options = config.contentOptions ?? const ContentOptions(); - final alignment = options.alignment; - final flex = options.flex; - - final header = config.header; - - final left = config.left; - final right = config.right; - return Column( - children: [ - Expanded( - flex: header.options.flex, - child: Row( - children: [ - buildContentSection(( - content: header.content, - options: options.merge(header.options), - )), - ], - ), - ), - Expanded( - flex: flex, - child: Container( - alignment: alignment.toAlignment(), - child: Row( - children: [ - buildContentSection(( - content: left.content, - options: options.merge(left.options), - )), - buildContentSection(( - content: right.content, - options: options.merge(right.options), - )), - ], - ), - ), - ), - ], - ); - } -} diff --git a/lib/helpers/measure_size.dart b/lib/helpers/measure_size.dart deleted file mode 100644 index 52eab9cb..00000000 --- a/lib/helpers/measure_size.dart +++ /dev/null @@ -1,113 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; - -import '../components/atoms/slide_view.dart'; - -typedef OnWidgetSizeChange = void Function(Size size); - -class MeasureSizeRenderObject extends RenderProxyBox { - Size? oldSize; - OnWidgetSizeChange onChange; - - MeasureSizeRenderObject(this.onChange); - - @override - void performLayout() { - super.performLayout(); - - Size newSize = child!.size; - if (oldSize == newSize) return; - - oldSize = newSize; - WidgetsBinding.instance.addPostFrameCallback((_) { - onChange(newSize); - }); - } -} - -class MeasureSize extends SingleChildRenderObjectWidget { - final OnWidgetSizeChange onChange; - - const MeasureSize({ - super.key, - required this.onChange, - required Widget super.child, - }); - - @override - RenderObject createRenderObject(BuildContext context) { - return MeasureSizeRenderObject(onChange); - } - - @override - void updateRenderObject( - BuildContext context, covariant MeasureSizeRenderObject renderObject) { - renderObject.onChange = onChange; - } -} - -class SlideConstraints extends StatefulWidget { - final Widget Function(Size) builder; - - const SlideConstraints( - this.builder, { - super.key, - }); - - static BoxConstraints of(BuildContext context) { - final provider = - context.dependOnInheritedWidgetOfExactType(); - if (provider == null) { - throw FlutterError('SlideConstraintsProvider not found in context'); - } - - return provider.constraints; - } - - static Size sizeOf(BuildContext context) { - final constraints = of(context); - return Size(constraints.maxWidth, constraints.maxHeight); - } - - @override - // ignore: library_private_types_in_public_api - _SlideConstraintsState createState() => _SlideConstraintsState(); -} - -class _SlideConstraintsState extends State { - Size? _widgetSize; - - void _onWidgetSizeChange(Size size) { - setState(() { - _widgetSize = size; - }); - } - - @override - Widget build(BuildContext context) { - return LayoutBuilder( - builder: ( - BuildContext context, - BoxConstraints constraints, - ) { - final size = _widgetSize == null ? constraints.biggest : _widgetSize!; - final constraintSize = BoxConstraints( - maxHeight: size.height, - maxWidth: size.width, - ); - - return MeasureSize( - onChange: _onWidgetSizeChange, - child: SlideConstraintsProvider( - constraints: constraintSize, - child: Builder( - builder: (context) { - return widget.builder(size); - }, - ), - ), - ); - }, - ); - } -} diff --git a/lib/helpers/theme.dart b/lib/helpers/theme.dart deleted file mode 100644 index 5363a1f0..00000000 --- a/lib/helpers/theme.dart +++ /dev/null @@ -1,10 +0,0 @@ -import 'package:flutter/material.dart'; - -ThemeData get theme => ThemeData( - colorScheme: ColorScheme.fromSeed( - seedColor: Colors.cyan, - error: Colors.red, - onTertiary: Colors.orange, - brightness: Brightness.dark, - ), - ); diff --git a/lib/helpers/utils.dart b/lib/helpers/utils.dart deleted file mode 100644 index c5ce0801..00000000 --- a/lib/helpers/utils.dart +++ /dev/null @@ -1,140 +0,0 @@ -import 'dart:convert'; - -import 'package:flutter/material.dart'; -import 'package:mix/mix.dart'; -import 'package:yaml/yaml.dart'; - -Map converYamlToMap(String yamlString) { - final yamlMap = loadYaml(yamlString) as YamlMap? ?? YamlMap(); - - final yaml = jsonEncode(yamlMap); - - return jsonDecode(yaml); -} - -/// Formats [json] -String prettyJson(dynamic json) { - var spaces = ' ' * 2; - var encoder = JsonEncoder.withIndent(spaces); - return encoder.convert(json); -} - -BoxConstraints calculateConstraints(Size size, BoxSpec spec) { - final padding = spec.padding ?? EdgeInsets.zero; - final margin = spec.margin ?? EdgeInsets.zero; - - double horizontalBorder = 0.0; - double verticalBorder = 0.0; - - if (spec.decoration is BoxDecoration) { - final border = (spec.decoration as BoxDecoration).border; - if (border != null) { - horizontalBorder = border.dimensions.horizontal; - verticalBorder = border.dimensions.vertical; - } - } - - final horizontalSpacing = - padding.horizontal + margin.horizontal + horizontalBorder; - final verticalSpacing = padding.vertical + margin.vertical + verticalBorder; - - return BoxConstraints( - maxHeight: size.height - verticalSpacing, - maxWidth: size.width - horizontalSpacing, - ); -} - -({List added, List removed}) compareListChanges( - List oldList, - List newList, -) { - final added = []; - final removed = []; - - final oldSet = oldList.toSet(); - final newSet = newList.toSet(); - - for (final item in newList) { - if (!oldSet.contains(item)) { - added.add(item); - } - } - - for (final item in oldList) { - if (!newSet.contains(item)) { - removed.add(item); - } - } - - return ( - added: added, - removed: removed, - ); -} - -extension BuildContextExt on BuildContext { - bool get isDarkMode => Theme.of(this).brightness == Brightness.dark; - bool get isSmall => size.width < 600; - - Size get size => MediaQuery.sizeOf(this); - bool get isMedium => size.width >= 600 && size.width < 1024; - - bool get isLarge => size.width >= 1024; - - bool get isExtraLarge => size.width >= 1440; - - bool get isMobileLandscape { - return size.shortestSide < 600 && isLandscape; - } - - bool get isLandscape => - MediaQuery.orientationOf(this) == Orientation.landscape; - - bool get isPortrait => MediaQuery.orientationOf(this) == Orientation.portrait; - - double get width => size.width; - double get height => size.height; -} - -/// Generates a short, unique identifier from a given string. -/// This is needed as hashCode for strings is not guaranteed to be unique across different platforms -/// -/// This function uses a hashing mechanism to transform the input string into -/// a unique, 8-character identifier. It is useful for creating compact and -/// unique keys for database entries, URLs, etc. -/// -/// [valueToHash] is the string input that you want to convert into a hash ID. -/// -/// Returns an 8-character string that represents the hashed ID. -String shortHashId(String valueToHash) { - // Define a string of possible characters that can appear in the output hash. - const characters = - 'aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789'; - - // Initialize hash to zero. - int hash = 0; - - // Calculate the hash value from each character in the input string. - for (int i = 0; i < valueToHash.length; i++) { - int charIndex = characters.indexOf(valueToHash[i]); - // Continue to next iteration if character is not found in the characters string. - if (charIndex == -1) { - continue; - } - // Update the hash value using the character's index and position. - hash = (hash * 31 + charIndex + i) % 2147483647; - } - - // Initialize the short ID as an empty string. - String shortId = ''; - int base = characters.length; - int remainingHash = hash; - - // Construct the short ID using the hash value. - for (int i = 0; i < 8; i++) { - shortId += characters[remainingHash % base]; - remainingHash = (remainingHash * 31 + hash) % 2147483647; - } - - return shortId; -} diff --git a/lib/models/asset_model.dart b/lib/models/asset_model.dart deleted file mode 100644 index dbde6e57..00000000 --- a/lib/models/asset_model.dart +++ /dev/null @@ -1,99 +0,0 @@ -import 'dart:io'; - -import 'package:collection/collection.dart'; -import 'package:dart_mappable/dart_mappable.dart'; -import 'package:flutter/services.dart'; -import 'package:path/path.dart' as p; - -import '../helpers/mappers.dart'; -import '../helpers/utils.dart'; -import '../services/project_service.dart'; -import 'slide_model.dart'; - -part 'asset_model.mapper.dart'; - -@MappableEnum() -enum AssetFileType { - png, - jpg, - jpeg, - gif, - webp; - - static AssetFileType parse(String value) { - return values.firstWhereOrNull((e) => e.name == value) ?? - (throw Exception('Invalid file type: $value')); - } - - static AssetFileType? tryParse(String value) { - return values.firstWhereOrNull((e) => value.startsWith(e.name)); - } - - bool isPng() => this == AssetFileType.png; - - bool isJpg() => this == AssetFileType.jpg || this == AssetFileType.jpeg; - - bool isGif() => this == AssetFileType.gif; -} - -@MappableEnum() -enum SlideAssetType { - cached, - thumb, - mermaid; - - static SlideAssetType parse(String value) { - return values.firstWhereOrNull((e) => e.name == value) ?? - (throw Exception('Invalid asset type: $value')); - } - - static SlideAssetType? tryParse(String value) { - return values.firstWhereOrNull((e) => value.startsWith(e.name)); - } -} - -@MappableClass( - includeCustomMappers: [ - FileMapper(), - SizeMapper(), - ], -) -final class SlideAsset with SlideAssetMappable { - final File file; - final Size dimensions; - - SlideAsset({ - required this.file, - required this.dimensions, - }); - - String get extension => p.extension(file.path); - - bool get isPortrait => dimensions.height > dimensions.width; - - bool get isLandscape => !isPortrait; - - static File thumbnail(Slide slide) { - return ProjectService.instance.buildAssetFile( - '${slide.hashKey}.png', - SlideAssetType.thumb, - ); - } - - static File cached(String uri) { - return ProjectService.instance.buildAssetFile( - '${shortHashId(uri)}.png', - SlideAssetType.cached, - ); - } - - static File mermaid(String mermaidSyntax) { - return ProjectService.instance.buildAssetFile( - '${shortHashId(mermaidSyntax)}.png', - SlideAssetType.mermaid, - ); - } - - static const fromMap = SlideAssetMapper.fromMap; - static const fromJson = SlideAssetMapper.fromJson; -} diff --git a/lib/providers/controller.dart b/lib/providers/controller.dart deleted file mode 100644 index 25a45983..00000000 --- a/lib/providers/controller.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; - -import 'package:flutter/foundation.dart'; -import 'package:localstorage/localstorage.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../builder/slides_loader.dart'; -import '../helpers/constants.dart'; -import '../services/project_service.dart'; -import '../styles/style_util.dart'; -import '../superdeck.dart'; - -final sdController = SDController.instance; - -class SDController { - SDController._(); - - static final instance = SDController._(); - - final _data = futureSignal(SlidesLoader.instance.loadDeck); - - final style = signal(const Style.empty(), debugLabel: 'Style'); - - late final loading = computed(() => _data.value is AsyncLoading); - - late final slides = computed(() => _data.value.value?.slides ?? []); - late final assets = computed(() => _data.value.value?.assets ?? []); - - final examples = mapSignal({}); - - late final error = computed( - () { - final data = _data.value; - return data is AsyncError ? data.error : null; - }, - ); - - Future initialize({ - List examples = const [], - Style? style, - }) async { - // Unsubscribe to listeners in case its a retry - batch(() { - this.style.value = defaultStyle.merge(style); - this.examples.assign({for (var e in examples) e.name: e}); - }); - - if (_data.isDone) { - await _data.reload(); - } else { - await _data.future; - } - - if (kCanRunProcess) { - SlidesLoader.instance.listen(_data.refresh); - } - } - - Future clearGenerated() async { - await ProjectService.instance.clearGeneratedDir(); - _data.refresh(); - } - - void dispose() { - style.dispose(); - error.dispose(); - assets.dispose(); - slides.dispose(); - _data.dispose(); - examples.dispose(); - } -} - -class NavigationProvider { - List? _cleanup; - NavigationProvider._() { - initialize(); - } - - static NavigationProvider get instance => _instance; - - static final _instance = NavigationProvider._(); - - late final currentSlide = signal(0); - late final sideIsOpen = signal(false); - late final currentScreen = signal(0); - - void initialize() { - currentSlide.value = _getItem('current-slide') ?? 0; - sideIsOpen.value = _getItem('side-is-open') ?? false; - currentScreen.value = _getItem('current-screen') ?? 0; - - _cleanup = [ - effect(() { - _setItem('side-is-open', sideIsOpen.value); - _setItem('current-slide', currentSlide.value); - _setItem('current-screen', currentScreen.value); - }) - ]; - } - - void toggleSide() { - batch(() { - if (sideIsOpen.value) { - currentScreen.value = 0; - } - sideIsOpen.value = !sideIsOpen.peek(); - }); - } - - void goToSlide(int slide) { - if (slide < 0 || slide >= sdController.slides.value.length) return; - currentSlide.value = slide; - } - - void nextSlide() { - goToSlide(currentSlide.peek() + 1); - } - - void previousSlide() { - goToSlide(currentSlide.peek() - 1); - } - - void goToScreen(int screen) { - currentScreen.value = screen; - } - - void _setItem(String key, T value) { - localStorage.setItem(key, jsonEncode(value)); - } - - T? _getItem(String key) { - final stringValue = localStorage.getItem(key); - return stringValue == null ? null : jsonDecode(stringValue) as T; - } - - void dispose() { - currentSlide.dispose(); - sideIsOpen.dispose(); - _cleanup?.forEach((e) => e.call()); - } -} - -extension ListSignalExtension on ListSignal { - void assign(List value) { - if (listEquals(peek(), value)) return; - this.value = value; - } -} - -extension MapSignalExtension on MapSignal { - void assign(Map value) { - if (mapEquals(peek(), value)) return; - this.value = value; - } -} diff --git a/lib/providers/slide_provider.dart b/lib/providers/slide_provider.dart deleted file mode 100644 index 58956371..00000000 --- a/lib/providers/slide_provider.dart +++ /dev/null @@ -1,94 +0,0 @@ -// Create a SlideProvider that extends an Inherited widget -import 'package:flutter/material.dart'; - -import '../models/asset_model.dart'; -import '../models/options_model.dart'; -import '../models/slide_model.dart'; -import '../styles/style_spec.dart'; - -enum SlideProviderAspect { - slide, - spec, - isSnapshot, - examples, - assets, -} - -class SlideProvider extends InheritedModel { - final Slide slide; - final SlideSpec spec; - - // If slide is a snapshot for image generation - final bool isSnapshot; - final Map examples; - final List assets; - - const SlideProvider({ - super.key, - required this.slide, - required this.spec, - required this.isSnapshot, - required this.examples, - required this.assets, - required super.child, - }); - - @override - bool updateShouldNotify(covariant SlideProvider oldWidget) { - return slide != oldWidget.slide || spec != oldWidget.spec; - } - - static SlideProvider of(BuildContext context) { - return context.dependOnInheritedWidgetOfExactType()!; - } - - @override - bool updateShouldNotifyDependent( - covariant SlideProvider oldWidget, - Set dependencies, - ) { - if (dependencies.contains(SlideProviderAspect.slide) && - slide != oldWidget.slide) { - return true; - } - if (dependencies.contains(SlideProviderAspect.spec) && - spec != oldWidget.spec) { - return true; - } - - if (dependencies.contains(SlideProviderAspect.isSnapshot) && - isSnapshot != oldWidget.isSnapshot) { - return true; - } - if (dependencies.contains(SlideProviderAspect.examples) && - examples != oldWidget.examples) { - return true; - } - if (dependencies.contains(SlideProviderAspect.assets) && - assets != oldWidget.assets) { - return true; - } - return false; - } - - static Slide slideOf(BuildContext context) { - return SlideProvider.of(context).slide; - } - - static SlideSpec specOf(BuildContext context) { - return SlideProvider.of(context).spec; - } - - static bool isSnapshotOf(BuildContext context) { - return SlideProvider.of(context).isSnapshot; - } - - static List assetsOf(BuildContext context) { - return SlideProvider.of(context).assets; - } - -// TODO: only get notified if the individual example changes - static Map examplesOf(BuildContext context) { - return SlideProvider.of(context).examples; - } -} diff --git a/lib/schema/schema_model.dart b/lib/schema/schema_model.dart deleted file mode 100644 index 282711b0..00000000 --- a/lib/schema/schema_model.dart +++ /dev/null @@ -1,296 +0,0 @@ -import 'package:dart_mappable/dart_mappable.dart'; - -import 'schema_values.dart'; -import 'validators.dart'; - -part 'schema_model.mapper.dart'; - -typedef JSON = Map; - -class SchemaValidationException implements Exception { - final SchemaValidationResult result; - - const SchemaValidationException(this.result); -} - -class SchemaError { - const SchemaError._(); - static const unallowedAdditionalProperty = 'unnalowed_additional_property'; - static const enumViolated = 'enum_violated'; - static const requiredPropMissing = 'required_property_missing'; - static const invalidType = 'invalid_type'; - static const constraints = 'constraints'; - static const unknown = 'unknown'; -} - -@MappableClass() -class SchemaValidationError with SchemaValidationErrorMappable { - final String type; - final String message; - const SchemaValidationError({ - required this.type, - required this.message, - }); - - const SchemaValidationError.unknown() - : type = SchemaError.unknown, - message = 'Unknown error'; - - const SchemaValidationError.constraints(this.message) - : type = SchemaError.constraints; - - const SchemaValidationError.unallowedAdditionalProperty(String property) - : type = SchemaError.unallowedAdditionalProperty, - message = 'Unallowed property: [$property]'; - - const SchemaValidationError.enumViolated( - String value, List possibleValues) - : type = SchemaError.enumViolated, - message = 'Wrong value: [$value] \n\n Possible values: $possibleValues'; - - const SchemaValidationError.requiredPropMissing(String property) - : type = SchemaError.requiredPropMissing, - message = 'Missing prop: [$property]'; - - const SchemaValidationError.invalidType(String value, String expectedType) - : type = SchemaError.invalidType, - message = 'Invalid type: [$expectedType] got [$value]'; - - @override - String toString() { - return 'SchemaValidationError{type: $type, message: $message}'; - } -} - -@MappableClass() -class SchemaValidationResult with SchemaValidationResultMappable { - final List key; - final List errors; - - const SchemaValidationResult({ - required this.key, - required this.errors, - }); - - const SchemaValidationResult.valid(this.key) : errors = const []; - - factory SchemaValidationResult.invalidType( - List path, - Object value, - String expectedType, - ) { - return SchemaValidationResult( - key: path, - errors: [ - SchemaValidationError.invalidType( - value.runtimeType.toString(), - expectedType, - ) - ], - ); - } - - factory SchemaValidationResult.unallowedAdditionalProperty( - List path, String property) { - return SchemaValidationResult( - key: path, - errors: [SchemaValidationError.unallowedAdditionalProperty(property)], - ); - } - - factory SchemaValidationResult.enumViolated( - List path, String value, List possibleValues) { - return SchemaValidationResult( - key: path, - errors: [SchemaValidationError.enumViolated(value, possibleValues)], - ); - } - - factory SchemaValidationResult.requiredPropMissing(List path) { - return SchemaValidationResult( - key: path, - errors: [SchemaValidationError.requiredPropMissing(path.last)], - ); - } - - factory SchemaValidationResult.constraints( - List path, String message) { - return SchemaValidationResult( - key: path, - errors: [SchemaValidationError.constraints(message)], - ); - } - - @override - String toString() { - return '${errors.isEmpty ? 'VALID' : 'INVALID'}${errors.isEmpty ? ', Errors: $errors' : ''}'; - } - - bool get isValid => errors.isEmpty; - - static const fromMap = SchemaValidationResultMapper.fromMap; - static const fromJson = SchemaValidationResultMapper.fromJson; -} - -class SchemaMap extends SchemaValue> { - final Map properties; - final bool additionalProperties; - const SchemaMap( - this.properties, { - super.optional = false, - this.additionalProperties = false, - super.validators = const [], - }); - - // optional constructor - const SchemaMap.optional( - this.properties, { - this.additionalProperties = false, - }) : super( - validators: const [], - optional: true, - ); - - @override - SchemaMap copyWith({ - bool? optional, - bool? additionalProperties, - Map? properties, - List>>? validators, - }) { - return SchemaMap( - properties ?? this.properties, - additionalProperties: additionalProperties ?? this.additionalProperties, - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - Map? tryParse(Object? value) { - return value is Map ? value : null; - } - - SchemaMap mergeSchema(SchemaMap schema) { - return merge( - schema.properties, - additionalProperties: schema.additionalProperties, - ); - } - - T? getSchemaValue(String key) { - return properties[key] as T?; - } - - SchemaMap merge( - Map properties, { - bool? additionalProperties, - bool? optional, - }) { - // if property SchemaValue is of SchemaMap, we need to merge them - final mergedProperties = {...this.properties}; - - for (final entry in properties.entries) { - final key = entry.key; - final prop = entry.value; - - final existingProp = mergedProperties[key]; - - if (existingProp is SchemaMap && prop is SchemaMap) { - mergedProperties[key] = existingProp.merge(prop.properties); - } else { - mergedProperties[key] = prop; - } - } - - return copyWith( - properties: mergedProperties, - optional: optional, - additionalProperties: additionalProperties, - ); - } - - @override - SchemaValidationResult validate(List path, Object? value) { - if (value == null) { - return optional - ? SchemaValidationResult.valid(path) - : SchemaValidationResult.requiredPropMissing(path); - } - - final parsedValue = tryParse(value); - - if (parsedValue == null) { - return SchemaValidationResult.invalidType( - path, - value, - {}.toString(), - ); - } - - final keys = parsedValue.keys.toSet(); - - final required = properties.entries - .where((entry) => !entry.value.optional) - .map((entry) => entry.key); - - final requiredKeys = required.toSet(); - - if (!keys.containsAll(requiredKeys)) { - return SchemaValidationResult( - key: path, - errors: requiredKeys - .difference(keys) - .map(SchemaValidationError.requiredPropMissing) - .toList()); - } - - if (additionalProperties == false) { - final extraKeys = keys.difference(properties.keys.toSet()); - if (extraKeys.isNotEmpty) { - return SchemaValidationResult( - key: path, - errors: extraKeys - .map(SchemaValidationError.unallowedAdditionalProperty) - .toList(), - ); - } - } - - for (final entry in parsedValue.entries) { - final key = entry.key; - final prop = properties[key]; - - if (prop == null) { - return additionalProperties == false - ? SchemaValidationResult(key: path, errors: [ - SchemaValidationError.unallowedAdditionalProperty(key) - ]) - : SchemaValidationResult.valid(path); - } - - final result = prop.validate([...path, key], entry.value); - if (!result.isValid) { - return result; - } - } - - return SchemaValidationResult.valid(path); - } -} - -class Schema extends SchemaMap { - const Schema( - super.properties, { - super.additionalProperties = false, - }); - - static const string = StringSchema(); - static const double = DoubleSchema(); - static const integer = IntegerSchema(); - static const boolean = BooleanSchema(); - - static const enumType = EnumSchema.new; - - static const any = Schema({}, additionalProperties: true); -} diff --git a/lib/schema/schema_values.dart b/lib/schema/schema_values.dart deleted file mode 100644 index 5a1844fe..00000000 --- a/lib/schema/schema_values.dart +++ /dev/null @@ -1,273 +0,0 @@ -import 'schema_model.dart'; -import 'validators.dart'; - -abstract class SchemaValue { - const SchemaValue({ - required this.optional, - required this.validators, - }); - - SchemaValue copyWith({ - bool? optional, - List>? validators, - }); - - SchemaValue isRequired() { - return copyWith(optional: false); - } - - SchemaValue isOptional() { - return copyWith(optional: true); - } - - final bool optional; - final List> validators; - - bool get required => !optional; - - V? tryParse(Object value) => value as V?; - - void validateOrThrow(Object value) { - final result = validate([], value); - if (!result.isValid) { - throw SchemaValidationException(result); - } - } - - SchemaValidationResult validate(List path, Object? value) { - if (value == null) { - return optional - ? SchemaValidationResult.valid(path) - : SchemaValidationResult.requiredPropMissing(path); - } - - final valueType = tryParse(value); - - if (valueType == null) { - return SchemaValidationResult.invalidType(path, value, V.toString()); - } - - for (final validator in validators) { - final error = validator.validate(valueType); - if (error != null) { - return SchemaValidationResult.constraints(path, error.message); - } - } - - return SchemaValidationResult.valid(path); - } -} - -/// Used to remove value from SchemaMap -class NullSchema extends SchemaValue { - const NullSchema({super.optional = false, super.validators = const []}); - - @override - NullSchema copyWith({ - bool? optional, - List>? validators, - }) { - return NullSchema( - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - Null tryParse(Object? value) { - return value == null ? null : null; - } -} - -class DoubleSchema extends SchemaValue { - const DoubleSchema({super.optional = false, super.validators = const []}); - - @override - DoubleSchema copyWith({ - bool? optional, - List>? validators, - }) { - return DoubleSchema( - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - double? tryParse(Object value) { - if (value is double) { - return value; - } - - if (value is int) { - return value.toDouble(); - } - - if (value is String) { - return double.tryParse(value); - } - - return null; - } -} - -class EnumSchema extends StringSchema { - final List values; - const EnumSchema({ - required this.values, - super.optional = false, - super.validators = const [], - }); - - @override - EnumSchema copyWith({ - List? values, - bool? optional, - List>? validators, - }) { - return EnumSchema( - values: values ?? this.values, - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - SchemaValidationResult validate(List path, Object? value) { - final result = super.validate(path, value); - - if (result.isValid) { - final parsedValue = tryParse(value); - - if (parsedValue != null && !values.contains(parsedValue)) { - return SchemaValidationResult.enumViolated(path, parsedValue, values); - } - } - - return result; - } -} - -class StringSchema extends SchemaValue { - const StringSchema({super.optional = false, super.validators = const []}); - - @override - StringSchema copyWith({ - bool? optional, - List>? validators, - }) { - return StringSchema( - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - String? tryParse(Object? value) { - return value is String ? value : null; - } -} - -class IntegerSchema extends SchemaValue { - const IntegerSchema({super.optional = false, super.validators = const []}); - - @override - IntegerSchema copyWith({ - bool? optional, - List>? validators, - }) { - return IntegerSchema( - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - int? tryParse(Object value) { - if (value is int) { - return value; - } - - if (value is String) { - return int.tryParse(value); - } - - return null; - } -} - -class BooleanSchema extends SchemaValue { - const BooleanSchema({super.optional = false, super.validators = const []}); - - @override - BooleanSchema copyWith({ - bool? optional, - List>? validators, - }) { - return BooleanSchema( - optional: optional ?? this.optional, - validators: validators ?? this.validators, - ); - } - - @override - bool? tryParse(Object? value) { - if (value is bool) { - return value; - } - - if (value is String) { - if (value.toLowerCase() == 'true') { - return true; - } else if (value.toLowerCase() == 'false') { - return false; - } - } - - return null; - } -} - -extension StringSchemaExt on SchemaValue { - SchemaValue isPosixPath() { - return copyWith(validators: [ - ...validators, - const PosixPathValidator(), - ]); - } - - SchemaValue isEmail() { - return copyWith(validators: [ - ...validators, - const EmailValidator(), - ]); - } - - SchemaValue isHexColor() { - return copyWith(validators: [ - ...validators, - const HexColorValidator(), - ]); - } - - SchemaValue isEmpty() { - return copyWith(validators: [ - ...validators, - const IsEmptyValidator(), - ]); - } - - SchemaValue minLength(int min) { - return copyWith(validators: [ - ...validators, - MinLengthValidator(min), - ]); - } - - SchemaValue maxLength(int max) { - return copyWith(validators: [ - ...validators, - MaxLengthValidator(max), - ]); - } -} diff --git a/lib/screens/export_screen.dart b/lib/screens/export_screen.dart deleted file mode 100644 index 20f2aeda..00000000 --- a/lib/screens/export_screen.dart +++ /dev/null @@ -1,357 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:file_picker/file_picker.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:pdf/pdf.dart'; -import 'package:pdf/widgets.dart' as pw; -import 'package:signals/signals_flutter.dart'; -import 'package:universal_html/html.dart' as html; - -import '../../helpers/constants.dart'; -import '../components/atoms/linear_progresss_indicator_widget.dart'; -import '../components/atoms/slide_view.dart'; -import '../components/molecules/scaled_app.dart'; -import '../services/snapshot_service.dart'; -import '../superdeck.dart'; - -enum ExportProcessStatus { - idle, - converting, - creatingPdf, - complete; - - const ExportProcessStatus(); - - bool get isComplete => this == ExportProcessStatus.complete; -} - -class ExportScreen extends StatefulWidget { - const ExportScreen({super.key}); - - @override - State createState() => _ExportScreenState(); -} - -class _ExportScreenState extends State { - final navigation = NavigationProvider.instance; - - late final _selectedQuality = createSignal(context, SnapshotQuality.good); - - Future convertToPdf(BuildContext context) async { - final lastState = navigation.sideIsOpen.value; - - navigation.sideIsOpen.value = false; - - await Future.delayed(Duration.zero); - - late OverlayEntry entry; - void handleOnComplete() { - entry.remove(); - navigation.sideIsOpen.value = lastState; - } - - entry = OverlayEntry( - maintainState: true, - builder: (context) { - return ExportingProcessScreen( - onComplete: handleOnComplete, - quality: _selectedQuality.value, - ); - }, - ); - if (!context.mounted) return; - Overlay.of(context).insert(entry); - } - - void setQuality(SnapshotQuality? quality) { - if (quality == null) throw Exception('Quality cannot be null'); - _selectedQuality.value = quality; - } - - @override - Widget build(BuildContext context) { - final selectedQuality = _selectedQuality.watch(context); - - List> buildRadioList() { - return SnapshotQuality.values.map((e) { - return RadioListTile.adaptive( - title: Text(e.label), - value: e, - groupValue: selectedQuality, - onChanged: setQuality, - ); - }).toList(); - } - - return Scaffold( - appBar: AppBar( - title: const Text('Export'), - ), - body: Center( - child: SizedBox( - width: 300, - child: Column( - crossAxisAlignment: CrossAxisAlignment.stretch, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Text( - 'Select Quality:', - style: TextStyle( - fontSize: 16.0, - ), - ), - ...buildRadioList(), - const SizedBox(height: 24.0), - ElevatedButton( - onPressed: () => convertToPdf(context), - child: const Text('Save'), - ), - ], - ), - ), - ), - ); - } -} - -class ExportingProcessScreen extends StatefulWidget { - const ExportingProcessScreen({ - super.key, - required this.onComplete, - required this.quality, - }); - - final void Function() onComplete; - final SnapshotQuality quality; - - @override - State createState() => _ExportingProcessScreenState(); -} - -class _ExportingProcessScreenState extends State { - late final _status = createSignal(context, ExportProcessStatus.idle); - final _pageController = PageController(); - late List _slides; - late final _images = listSignal([]); - late final superdeck = SDController.instance; - - @override - void initState() { - super.initState(); - - _slides = superdeck.slides.value; - - Future.delayed(Durations.medium1).then((value) => startConversion()); - } - - @override - void dispose() { - _pageController.dispose(); - super.dispose(); - } - - Future startConversion() async { - try { - final generator = SnapshotService.instance; - _status.value = ExportProcessStatus.converting; - - List> futures = []; - - Future convertSlide(Slide slide) async { - final convertedImage = await generator.generate( - quality: widget.quality, - slide: slide, - ); - _images.add(convertedImage); - - return convertedImage; - } - - for (var slide in _slides) { - futures.add(convertSlide(slide)); - } - - final images = await Future.wait(futures); - - await Future.delayed(Durations.short1); - _status.value = ExportProcessStatus.creatingPdf; - await Future.delayed(Durations.short1); - - final pdf = await buildPdf(images); - - _status.value = ExportProcessStatus.complete; - await Future.delayed(Durations.short1); - - if (kIsWeb) { - // Create a Blob from the PDF bytes - final blob = html.Blob([pdf], 'application/pdf'); - - // Create a URL for the Blob - final url = html.Url.createObjectUrlFromBlob(blob); - - html.AnchorElement(href: url) - ..setAttribute('download', 'superdeck.pdf') - ..click(); - - return; - } - - final outputFile = await FilePicker.platform.saveFile( - dialogTitle: 'Save PDF', - fileName: 'superdeck.pdf', - type: FileType.custom, - allowedExtensions: ['pdf'], - ); - - if (outputFile != null) { - File file = File(outputFile); - - await file.writeAsBytes(pdf); - } - } on Exception catch (e) { - log(e.toString()); - } finally { - widget.onComplete(); - } - } - - Future buildPdf(List images) async { - final pdf = pw.Document(); - - for (final imageData in images) { - final image = pw.MemoryImage(imageData); - - pdf.addPage( - pw.Page( - pageFormat: PdfPageFormat( - kResolution.width, - kResolution.height, - ), - build: (pw.Context context) { - return pw.Center( - child: pw.Image( - image, - width: kResolution.width, - height: kResolution.height, - ), - ); - }, - ), - ); - } - - return pdf.save(); - } - - @override - Widget build(BuildContext context) { - _images.listen( - context, - () { - int page; - if (_images.length == _slides.length) { - page = 0; - } else { - page = _images.length - 1; - } - _pageController.jumpToPage(page); - }, - ); - - return Watch.builder(builder: (context) { - final totalSlides = _slides.length; - final totalImages = _images.length; - - final statusLabel = switch (_status.value) { - ExportProcessStatus.idle => 'Idle', - ExportProcessStatus.converting => 'Converting', - ExportProcessStatus.creatingPdf => 'Creating PDF', - ExportProcessStatus.complete => 'Done', - }; - - List buildChildren() { - if (_status.value.isComplete) { - return [ - const Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - Icons.check_circle, - color: Colors.green, - size: 48, - ), - SizedBox(width: 16), - Text( - 'Done', - style: TextStyle( - fontSize: 24, - fontWeight: FontWeight.bold, - ), - ), - ], - ) - ]; - } - - return [ - Container( - decoration: const BoxDecoration( - color: Colors.black, - ), - height: 225, - width: 400, - child: PageView.builder( - controller: _pageController, - itemCount: _images.length, - itemBuilder: (context, index) { - return ScaledWidget( - child: SlideView( - _slides[index], - ), - ); - }, - ), - ), - const SizedBox(height: 26), - Text( - statusLabel, - style: Theme.of(context).textTheme.bodyLarge, - ), - const SizedBox(height: 16), - Row( - children: [ - Expanded( - child: AnimatedLinearProgressIndicator( - progress: totalImages / totalSlides, - ), - ), - const SizedBox(width: 16), - Text( - '$totalImages/$totalSlides', - style: Theme.of(context).textTheme.bodyLarge, - ), - ], - ) - ]; - } - - return Stack( - children: [ - Container( - color: Theme.of(context).colorScheme.surface, - ), - Container( - color: Colors.black.withOpacity(0.8), - padding: const EdgeInsets.all(200), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: buildChildren(), - ), - ), - ], - ); - }); - } -} diff --git a/lib/screens/home_screen.dart b/lib/screens/home_screen.dart deleted file mode 100644 index 275777af..00000000 --- a/lib/screens/home_screen.dart +++ /dev/null @@ -1,80 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; - -import '../components/molecules/slide_preview.dart'; -import '../components/molecules/split_view.dart'; -import '../superdeck.dart'; - -class HomeScreen extends StatefulWidget { - const HomeScreen({ - super.key, - }); - - @override - // ignore: library_private_types_in_public_api - _HomeScreenState createState() => _HomeScreenState(); -} - -class _HomeScreenState extends State { - late final PageController pageController; - final superdeck = SDController.instance; - final navigation = NavigationProvider.instance; - - @override - void initState() { - super.initState(); - pageController = PageController(initialPage: navigation.currentSlide.value); - } - - @override - void dispose() { - pageController.dispose(); - super.dispose(); - } - - Future goToPage(int page) async { - final slides = superdeck.slides; - if (page < 0 || page >= slides.value.length) return; - - const duration = Duration(milliseconds: 300); - const curve = Curves.easeInOutCubic; - - // Return if already paged - if (pageController.page == page) return; - - if (page != navigation.currentSlide.value) { - await pageController.animateToPage( - page, - duration: duration, - curve: curve, - ); - - navigation.currentSlide.value = page; - } else { - pageController.jumpToPage(page); - } - } - - @override - Widget build(BuildContext context) { - final slides = superdeck.slides.watch(context); - - navigation.currentSlide.listen(context, () { - goToPage(navigation.currentSlide.value); - }); - - return SplitView( - child: Container( - alignment: Alignment.center, - child: PageView.builder( - controller: pageController, - itemCount: slides.length, - itemBuilder: (context, idx) { - final slide = slides[idx]; - return SlidePreview(slide); - }, - ), - ), - ); - } -} diff --git a/lib/services/mermaid_service.dart b/lib/services/mermaid_service.dart deleted file mode 100644 index a7230389..00000000 --- a/lib/services/mermaid_service.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'dart:developer'; -import 'dart:io'; - -import 'package:flutter/services.dart'; -import 'package:path/path.dart' as p; - -const mermaidService = MermaidService.instance; - -class MermaidService { - const MermaidService._(); - - static const instance = MermaidService._(); - - Future generateImage(String mermaidSyntax) async { - final fileName = mermaidSyntax.hashCode; - - final tempDir = Directory('.tmp_superdeck'); - - final tempFile = File(p.join(tempDir.path, '$fileName.mmd')); - final outputFile = File(p.join(tempDir.path, '$fileName.png')); - - if (!await tempDir.exists()) { - await tempDir.create(recursive: true); - } - - try { - mermaidSyntax = mermaidSyntax.trim().replaceAll(r'\n', '\n'); - - await tempFile.writeAsString(mermaidSyntax); - - final imageSizeParams = '--scale 2'.split(' '); - final params = - '-t dark -b transparent -i ${tempFile.path} -o ${outputFile.path} ' - .split(' '); - - // Check if can execute mmdc before executing command - final mmdcResult = await Process.run('mmdc', ['--version']); - - if (mmdcResult.exitCode != 0) { - log( - '"mmdc" not found. You need mermaid cli installed to process mermaid syntax', - ); - - return null; - } - - final result = await Process.run('mmdc', [...params, ...imageSizeParams]); - - if (result.exitCode != 0) { - log('Error while processing mermaid syntax'); - log(result.stderr); - return null; - } - - return outputFile.readAsBytes(); - } catch (e) { - log('Error while processing mermaid syntax: $e'); - return null; - } finally { - if (await tempDir.exists()) { - await tempDir.delete(recursive: true); - } - } - } -} diff --git a/lib/services/project_service.dart b/lib/services/project_service.dart deleted file mode 100644 index dc2e5aba..00000000 --- a/lib/services/project_service.dart +++ /dev/null @@ -1,209 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:developer'; -import 'dart:io'; -import 'dart:ui' as ui; - -import 'package:flutter/services.dart'; -import 'package:path/path.dart' as p; -import 'package:watcher/watcher.dart'; - -import '../helpers/config_model.dart'; -import '../helpers/constants.dart'; -import '../helpers/extensions.dart'; -import '../superdeck.dart'; - -typedef DeckReferences = ({ - SDConfig config, - List slides, - List assets, -}); - -class ProjectService { - ProjectService._(); - - static final instance = ProjectService._(); - - File get markdownFile => File('slides.md'); - - Directory get assetsDir => Directory(p.join('superdeck')); - - Directory get generatedAssetsDir => - Directory(p.join(assetsDir.path, 'generated')); - File get projectConfigFile => File('superdeck.yaml'); - - File get slideRef => File(p.join(assetsDir.path, 'slides.json')); - File get configRef => File(p.join(assetsDir.path, 'config.json')); - File get assetsRef => File(p.join(assetsDir.path, 'assets.json')); - - late final watcher = FileWatcher(markdownFile.path); - - Future ensureExists() async { - await assetsDir.ensureExists(); - await generatedAssetsDir.ensureExists(); - await markdownFile.ensureExists(); - - if (!await slideRef.exists()) { - await slideRef.ensureWrite('[]'); - } - - if (!await configRef.exists()) { - await configRef.ensureWrite(const SDConfig.empty().toJson()); - } - - if (!await assetsRef.exists()) { - await assetsRef.ensureWrite('[]'); - } - } - - // String _getAssetRelativePath(String path) { - // return p.relative( - // path, - // from: _assetsRootDir.path, - // ); - // } - - Future clearGeneratedDir() async { - await generatedAssetsDir.delete(recursive: true); - await generatedAssetsDir.ensureExists(); - } - - File buildAssetFile(String fileName, SlideAssetType type) { - if (p.extension(fileName).isEmpty) { - throw Exception('File name must have an extension'); - } - final updatedFileName = ('${type.name}_$fileName'); - return File(p.join(generatedAssetsDir.path, updatedFileName)); - } - - SlideAssetType? getAssetTypeOfFile(File file) { - // check if filename starts with prefix - - return SlideAssetType.tryParse(p.basenameWithoutExtension(file.path)); - } - - Future getSlideAssetFromFile(File file) async { - final data = await file.readAsBytes(); - final size = await _getImageSize(data); - - return SlideAsset( - file: file, - dimensions: size, - ); - } - - Future loadString(String path) async { - if (kCanRunProcess) { - return File(path).readAsString(); - } else { - return rootBundle.loadString(path); - } - } - - Future loadDeck() async { - return ( - slides: await _loadSlidesRef(), - config: await _loadConfigRef(), - assets: await _loadAssetsRef(), - ); - } - - Future> _loadSlidesRef() async { - try { - final slidesJson = await loadString(slideRef.path); - return _parseList(slidesJson, Slide.fromMap); - } on Exception catch (err) { - log('Error loading ${slideRef.path}: $err'); - return []; - } - } - - Future _loadConfigRef() async { - try { - final configJson = await loadString(configRef.path); - return SDConfig.fromJson(configJson); - } on Exception catch (err) { - log('Error loading ${configRef.path}: $err'); - return const SDConfig.empty(); - } - } - - Future> _loadAssetsRef() async { - try { - final assetJson = await loadString(assetsRef.path); - return _parseList(assetJson, SlideAsset.fromMap); - } on Exception catch (err) { - log('Error loading ${assetsRef.path}: $err'); - return []; - } - } - - Future loadMarkdown() async { - try { - return await loadString(markdownFile.path); - } on Exception catch (err) { - log('Error loading ${markdownFile.path}: $err'); - return ''; - } - } - - Future saveConfigRef(SDConfig config) async { - await configRef.ensureWrite(config.toJson()); - } - - Future saveSlidesRef(List slides) async { - final map = slides.map((e) => e.toMap()).toList(); - await slideRef.ensureWrite(jsonEncode(map)); - } - - Future saveAssetsRef(List files) async { - final assets = await _convertToAssets(files); - final map = assets.map((e) => e.toMap()).toList(); - await assetsRef.ensureWrite(jsonEncode(map)); - } - - Future> loadGeneratedFiles() async { - final files = []; - - await for (var entity in generatedAssetsDir.list()) { - if (entity is File) { - files.add(entity); - } - } - - return files; - } - - bool isAssetFile(File file) => getAssetTypeOfFile(file) != null; - - Future> _convertToAssets(List assetFiles) async { - final assets = []; - - for (var file in assetFiles) { - if (isAssetFile(file)) { - assets.add( - await getSlideAssetFromFile(file), - ); - } - } - - return assets; - } -} - -List _parseList( - String contents, T Function(Map) fromMap) { - final jsonList = jsonDecode(contents) as List; - final mapList = List.castFrom>(jsonList); - - return mapList.map(fromMap).toList(); -} - -Future _getImageSize(Uint8List data) async { - final codec = await ui.instantiateImageCodec(data); - final frame = await codec.getNextFrame(); - return Size( - frame.image.width.toDouble(), - frame.image.height.toDouble(), - ); -} diff --git a/lib/styles/style_spec.dart b/lib/styles/style_spec.dart deleted file mode 100644 index 8c19a0e7..00000000 --- a/lib/styles/style_spec.dart +++ /dev/null @@ -1,314 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:markdown_viewer/markdown_viewer.dart'; -import 'package:mix/mix.dart'; -import 'package:mix_annotations/mix_annotations.dart'; - -part 'style_spec.g.dart'; - -const _mdTextStyle = MixableFieldDto(type: 'MdTextStyleAttribute'); -const _mdCode = MixableFieldDto(type: 'MdCodeAttribute'); -const _mdList = MixableFieldDto(type: 'MdListAttribute'); -const _mdTable = MixableFieldDto(type: 'MdTableAttribute'); -const _mdDivider = MixableFieldDto(type: 'MdDividerAttribute'); -const _mdBlockQuote = MixableFieldDto(type: 'MdBlockQuoteAttribute'); - -@MixableSpec() -final class MdDivider extends Spec with _$MdDivider { - @MixableProperty(utilities: [MixableUtility(alias: 'height')]) - final double? dividerHeight; - @MixableProperty(utilities: [MixableUtility(alias: 'color')]) - final Color? dividerColor; - @MixableProperty(utilities: [MixableUtility(alias: 'thickness')]) - final double? dividerThickness; - - const MdDivider({ - this.dividerHeight, - this.dividerColor, - this.dividerThickness, - }); -} - -@MixableSpec() -final class MdTextStyle extends Spec with _$MdTextStyle { - final TextStyle? textStyle; - - final EdgeInsets? padding; - - const MdTextStyle({ - this.textStyle, - this.padding, - }); -} - -@MixableSpec() -final class MdList extends Spec with _$MdList { - @MixableProperty(utilities: [MixableUtility(alias: 'textStyle')]) - final TextStyle? list; - @MixableProperty(utilities: [MixableUtility(alias: 'itemTextStyle')]) - final TextStyle? listItem; - @MixableProperty(utilities: [MixableUtility(alias: 'itemMarkerTextStyle')]) - final TextStyle? listItemMarker; - @MixableProperty( - utilities: [MixableUtility(alias: 'itemMarkerTrailingSpace')]) - final double? listItemMarkerTrailingSpace; - @MixableProperty(utilities: [MixableUtility(alias: 'itemMinIndent')]) - final double? listItemMinIndent; - - const MdList({ - this.list, - this.listItem, - this.listItemMarker, - this.listItemMarkerTrailingSpace, - this.listItemMinIndent, - }); -} - -@MixableSpec() -final class MdBlockQuote extends Spec with _$MdBlockQuote { - @MixableProperty(utilities: [MixableUtility(alias: 'textStyle')]) - final TextStyle? blockquote; - @MixableProperty(utilities: [MixableUtility(alias: 'decoration')]) - final BoxDecoration? blockquoteDecoration; - @MixableProperty(utilities: [MixableUtility(alias: 'padding')]) - final EdgeInsets? blockquotePadding; - @MixableProperty(utilities: [MixableUtility(alias: 'contentPadding')]) - final EdgeInsets? blockquoteContentPadding; - - const MdBlockQuote({ - this.blockquote, - this.blockquoteDecoration, - this.blockquotePadding, - this.blockquoteContentPadding, - }); -} - -@MixableSpec() -final class MdTable extends Spec with _$MdTable { - @MixableProperty(utilities: [MixableUtility(alias: 'textStyle')]) - final TextStyle? table; - @MixableProperty(utilities: [MixableUtility(alias: 'headTextStyle')]) - final TextStyle? tableHead; - @MixableProperty(utilities: [MixableUtility(alias: 'bodyTextStyle')]) - final TextStyle? tableBody; - @MixableProperty(utilities: [MixableUtility(alias: 'border')]) - final TableBorder? tableBorder; - @MixableProperty(utilities: [MixableUtility(alias: 'rowDecoration')]) - final BoxDecoration? tableRowDecoration; - @MixableProperty( - utilities: [MixableUtility(alias: 'rowDecorationAlternating')]) - final MarkdownAlternating? tableRowDecorationAlternating; - @MixableProperty( - utilities: [ - MixableUtility( - alias: 'cellPadding', - ) - ], - ) - final EdgeInsets? tableCellPadding; - @MixableProperty(utilities: [MixableUtility(alias: 'columnWidth')]) - final TableColumnWidth? tableColumnWidth; - - const MdTable({ - this.table, - this.tableHead, - this.tableBody, - this.tableBorder, - this.tableRowDecoration, - this.tableRowDecorationAlternating, - this.tableCellPadding, - this.tableColumnWidth, - }); -} - -@MixableSpec() -final class MdCode extends Spec with _$MdCode { - @MixableProperty(utilities: [MixableUtility(alias: 'span')]) - final TextStyle? codeSpan; - @MixableProperty(utilities: [ - MixableUtility( - alias: 'padding', - ) - ]) - final EdgeInsets? codeblockPadding; - @MixableProperty(utilities: [MixableUtility(alias: 'decoration')]) - final BoxDecoration? codeblockDecoration; - @MixableProperty(utilities: [MixableUtility(alias: 'block')]) - final TextStyle? codeBlock; - - final Color? copyIconColor; - - const MdCode({ - this.codeSpan, - this.codeblockPadding, - this.codeblockDecoration, - this.copyIconColor, - this.codeBlock, - }); -} - -@MixableSpec() -final class SlideSpec extends Spec with _$SlideSpec { - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h1')]) - final MdTextStyle? headline1; - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h2')]) - final MdTextStyle? headline2; - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h3')]) - final MdTextStyle? headline3; - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h4')]) - final MdTextStyle? headline4; - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h5')]) - final MdTextStyle? headline5; - @MixableProperty(dto: _mdTextStyle, utilities: [MixableUtility(alias: 'h6')]) - final MdTextStyle? headline6; - @MixableProperty(dto: _mdTextStyle) - final MdTextStyle? paragraph; - final TextStyle? link; - final double? blockSpacing; - @MixableProperty(dto: _mdDivider) - final MdDivider? divider; - @MixableProperty(dto: _mdBlockQuote) - final MdBlockQuote? blockquote; - @MixableProperty(dto: _mdList) - final MdList? list; - @MixableProperty(dto: _mdTable) - final MdTable? table; - @MixableProperty(dto: _mdCode) - final MdCode? code; - final BoxSpec innerContainer; - final BoxSpec outerContainer; - final BoxSpec contentContainer; - final ImageSpec image; - - static const of = _$SlideSpec.of; - static const from = _$SlideSpec.from; - - const SlideSpec({ - this.headline1, - this.headline2, - this.headline3, - this.headline4, - this.headline5, - this.headline6, - this.paragraph, - this.link, - this.blockSpacing, - this.divider, - this.blockquote, - this.list, - this.table, - this.code, - BoxSpec? innerContainer, - BoxSpec? outerContainer, - BoxSpec? contentContainer, - ImageSpec? image, - super.animated, - }) : outerContainer = outerContainer ?? const BoxSpec(), - innerContainer = innerContainer ?? const BoxSpec(), - contentContainer = contentContainer ?? const BoxSpec(), - image = image ?? const ImageSpec(); - - MarkdownStyle toStyle() { - return MarkdownStyle( - headline1: headline1?.textStyle, - h1Padding: headline1?.padding ?? EdgeInsets.zero, - headline2: headline2?.textStyle, - h2Padding: headline2?.padding ?? EdgeInsets.zero, - headline3: headline3?.textStyle, - h3Padding: headline3?.padding ?? EdgeInsets.zero, - headline4: headline4?.textStyle, - h4Padding: headline4?.padding ?? EdgeInsets.zero, - headline5: headline5?.textStyle, - h5Padding: headline5?.padding ?? EdgeInsets.zero, - headline6: headline6?.textStyle, - h6Padding: headline6?.padding ?? EdgeInsets.zero, - paragraph: paragraph?.textStyle, - paragraphPadding: paragraph?.padding ?? EdgeInsets.zero, - link: link, - dividerHeight: divider?.dividerHeight ?? 1.0, - dividerColor: divider?.dividerColor ?? Colors.black, - dividerThickness: divider?.dividerThickness ?? 1.0, - blockquote: blockquote?.blockquote, - blockquoteDecoration: blockquote?.blockquoteDecoration, - blockquotePadding: blockquote?.blockquotePadding ?? EdgeInsets.zero, - blockquoteContentPadding: - blockquote?.blockquoteContentPadding ?? EdgeInsets.zero, - list: list?.list, - listItem: list?.listItem, - listItemMarker: list?.listItemMarker, - listItemMarkerTrailingSpace: list?.listItemMarkerTrailingSpace ?? 4.0, - listItemMinIndent: list?.listItemMinIndent ?? 16.0, - table: table?.table, - tableHead: table?.tableHead, - tableBody: table?.tableBody, - tableBorder: table?.tableBorder, - tableRowDecoration: table?.tableRowDecoration, - tableRowDecorationAlternating: table?.tableRowDecorationAlternating, - tableCellPadding: table?.tableCellPadding, - tableColumnWidth: table?.tableColumnWidth, - codeSpan: code?.codeSpan, - codeBlock: code?.codeSpan, - codeblockPadding: code?.codeblockPadding, - codeblockDecoration: code?.codeblockDecoration, - blockSpacing: blockSpacing ?? 8, - copyIconColor: code?.copyIconColor ?? Colors.black, - ); - } -} - -extension on MixData { - Spec specOf>(Spec fallback) { - return resolvableOf() ?? fallback; - } -} - -extension SlideSpecUtilityX on SlideSpecUtility { - TextStyleUtility get textStyle { - return TextStyleUtility( - (value) => only( - paragraph: MdTextStyleAttribute(textStyle: value), - headline1: MdTextStyleAttribute(textStyle: value), - headline2: MdTextStyleAttribute(textStyle: value), - headline3: MdTextStyleAttribute(textStyle: value), - headline4: MdTextStyleAttribute(textStyle: value), - headline5: MdTextStyleAttribute(textStyle: value), - headline6: MdTextStyleAttribute(textStyle: value), - list: MdListAttribute(list: value), - table: MdTableAttribute(table: value), - code: MdCodeAttribute(codeBlock: value), - blockquote: MdBlockQuoteAttribute(blockquote: value), - ), - ); - } - - MdTextStyleUtility get headings { - return MdTextStyleUtility( - (value) => only( - headline1: value, - headline2: value, - headline3: value, - headline4: value, - headline5: value, - headline6: value, - ), - ); - } -} - -@MixableEnumUtility(generateCallMethod: false) -class MarkdownAlternatingUtility - extends MixUtility - with _$MarkdownAlternatingUtility { - const MarkdownAlternatingUtility(super.builder); -} - -@MixableClassUtility() -class TableColumnWidthUtility - extends MixUtility with _$TableColumnWidthUtility { - const TableColumnWidthUtility(super.builder); -} - -@MixableClassUtility() -class TableBorderUtility extends MixUtility - with _$TableBorderUtility { - const TableBorderUtility(super.builder); -} diff --git a/lib/styles/style_spec.g.dart b/lib/styles/style_spec.g.dart deleted file mode 100644 index ae8b6d21..00000000 --- a/lib/styles/style_spec.g.dart +++ /dev/null @@ -1,1903 +0,0 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND - -part of 'style_spec.dart'; - -// ************************************************************************** -// MixableClassUtilityGenerator -// ************************************************************************** - -/// {@template table_column_width_utility} -/// A utility class for creating [Attribute] instances from [TableColumnWidth] values. -/// -/// This class extends [MixUtility] and provides methods to create [Attribute] instances -/// from predefined [TableColumnWidth] values. -/// {@endtemplate} -mixin _$TableColumnWidthUtility - on MixUtility { - /// Creates an [Attribute] instance with the specified TableColumnWidth value. - T call(TableColumnWidth value) => builder(value); -} - -/// {@template table_border_utility} -/// A utility class for creating [Attribute] instances from [TableBorder] values. -/// -/// This class extends [MixUtility] and provides methods to create [Attribute] instances -/// from predefined [TableBorder] values. -/// {@endtemplate} -mixin _$TableBorderUtility on MixUtility { - /// Creates an [Attribute] instance using the [TableBorder.all] constructor. - T all( - {Color color = const Color(0xFF000000), - double width = 1.0, - BorderStyle style = BorderStyle.solid, - BorderRadius borderRadius = BorderRadius.zero}) { - return builder(TableBorder.all( - color: color, width: width, style: style, borderRadius: borderRadius)); - } - - /// Creates an [Attribute] instance using the [TableBorder.symmetric] constructor. - T symmetric( - {BorderSide inside = BorderSide.none, - BorderSide outside = BorderSide.none, - BorderRadius borderRadius = BorderRadius.zero}) { - return builder(TableBorder.symmetric( - inside: inside, outside: outside, borderRadius: borderRadius)); - } - - /// Creates an [Attribute] instance with the specified TableBorder value. - T call(TableBorder value) => builder(value); -} - -// ************************************************************************** -// MixableEnumUtilityGenerator -// ************************************************************************** - -/// {@template markdown_alternating_utility} -/// A utility class for creating [Attribute] instances from [MarkdownAlternating] values. -/// -/// This class extends [MixUtility] and provides methods to create [Attribute] instances -/// from predefined [MarkdownAlternating] values. -/// {@endtemplate} -mixin _$MarkdownAlternatingUtility - on MixUtility { - /// Creates an [Attribute] instance with [MarkdownAlternating.odd] value. - T odd() => builder(MarkdownAlternating.odd); - - /// Creates an [Attribute] instance with [MarkdownAlternating.even] value. - T even() => builder(MarkdownAlternating.even); -} - -// ************************************************************************** -// MixableSpecGenerator -// ************************************************************************** - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdDivider on Spec { - static MdDivider from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? - const MdDivider(); - } - - /// {@template md_divider_of} - /// Retrieves the [MdDivider] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdDivider] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdDivider]. - /// - /// Example: - /// - /// ```dart - /// final mdDivider = MdDivider.of(context); - /// ``` - /// {@endtemplate} - static MdDivider of(BuildContext context) { - return _$MdDivider.from(Mix.of(context)); - } - - /// Creates a copy of this [MdDivider] but with the given fields - /// replaced with the new values. - @override - MdDivider copyWith({ - double? dividerHeight, - Color? dividerColor, - double? dividerThickness, - }) { - return MdDivider( - dividerHeight: dividerHeight ?? _$this.dividerHeight, - dividerColor: dividerColor ?? _$this.dividerColor, - dividerThickness: dividerThickness ?? _$this.dividerThickness, - ); - } - - /// Linearly interpolates between this [MdDivider] and another [MdDivider] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdDivider] is returned. When [t] is 1.0, the [other] [MdDivider] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdDivider] is returned. - /// - /// If [other] is null, this method returns the current [MdDivider] instance. - /// - /// The interpolation is performed on each property of the [MdDivider] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpDouble] for [dividerHeight] and [dividerThickness]. - /// - [Color.lerp] for [dividerColor]. - - /// For , the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdDivider] is used. Otherwise, the value - /// from the [other] [MdDivider] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdDivider] configurations. - @override - MdDivider lerp(MdDivider? other, double t) { - if (other == null) return _$this; - - return MdDivider( - dividerHeight: - MixHelpers.lerpDouble(_$this.dividerHeight, other.dividerHeight, t), - dividerColor: Color.lerp(_$this.dividerColor, other.dividerColor, t), - dividerThickness: MixHelpers.lerpDouble( - _$this.dividerThickness, other.dividerThickness, t), - ); - } - - /// The list of properties that constitute the state of this [MdDivider]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdDivider] instances for equality. - @override - List get props => [ - _$this.dividerHeight, - _$this.dividerColor, - _$this.dividerThickness, - ]; - - MdDivider get _$this => this as MdDivider; -} - -/// Represents the attributes of a [MdDivider]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdDivider]. -/// -/// Use this class to configure the attributes of a [MdDivider] and pass it to -/// the [MdDivider] constructor. -final class MdDividerAttribute extends SpecAttribute { - final double? dividerHeight; - final ColorDto? dividerColor; - final double? dividerThickness; - - const MdDividerAttribute({ - this.dividerHeight, - this.dividerColor, - this.dividerThickness, - }); - - /// Resolves to [MdDivider] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdDivider = MdDividerAttribute(...).resolve(mix); - /// ``` - @override - MdDivider resolve(MixData mix) { - return MdDivider( - dividerHeight: dividerHeight, - dividerColor: dividerColor?.resolve(mix), - dividerThickness: dividerThickness, - ); - } - - /// Merges the properties of this [MdDividerAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdDividerAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdDividerAttribute merge(MdDividerAttribute? other) { - if (other == null) return this; - - return MdDividerAttribute( - dividerHeight: other.dividerHeight ?? dividerHeight, - dividerColor: - dividerColor?.merge(other.dividerColor) ?? other.dividerColor, - dividerThickness: other.dividerThickness ?? dividerThickness, - ); - } - - /// The list of properties that constitute the state of this [MdDividerAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdDividerAttribute] instances for equality. - @override - List get props => [ - dividerHeight, - dividerColor, - dividerThickness, - ]; -} - -/// Utility class for configuring [MdDividerAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdDividerAttribute]. -/// Use the methods of this class to configure specific properties of a [MdDividerAttribute]. -class MdDividerUtility - extends SpecUtility { - /// Utility for defining [MdDividerAttribute.dividerHeight] - late final height = DoubleUtility((v) => only(dividerHeight: v)); - - /// Utility for defining [MdDividerAttribute.dividerColor] - late final color = ColorUtility((v) => only(dividerColor: v)); - - /// Utility for defining [MdDividerAttribute.dividerThickness] - late final thickness = DoubleUtility((v) => only(dividerThickness: v)); - - MdDividerUtility(super.builder); - - static final self = MdDividerUtility((v) => v); - - /// Returns a new [MdDividerAttribute] with the specified properties. - @override - T only({ - double? dividerHeight, - ColorDto? dividerColor, - double? dividerThickness, - }) { - return builder(MdDividerAttribute( - dividerHeight: dividerHeight, - dividerColor: dividerColor, - dividerThickness: dividerThickness, - )); - } -} - -/// A tween that interpolates between two [MdDivider] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdDivider] specifications. -class MdDividerTween extends Tween { - MdDividerTween({ - super.begin, - super.end, - }); - - @override - MdDivider lerp(double t) { - if (begin == null && end == null) return const MdDivider(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdTextStyle on Spec { - static MdTextStyle from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? - const MdTextStyle(); - } - - /// {@template md_text_style_of} - /// Retrieves the [MdTextStyle] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdTextStyle] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdTextStyle]. - /// - /// Example: - /// - /// ```dart - /// final mdTextStyle = MdTextStyle.of(context); - /// ``` - /// {@endtemplate} - static MdTextStyle of(BuildContext context) { - return _$MdTextStyle.from(Mix.of(context)); - } - - /// Creates a copy of this [MdTextStyle] but with the given fields - /// replaced with the new values. - @override - MdTextStyle copyWith({ - TextStyle? textStyle, - EdgeInsets? padding, - }) { - return MdTextStyle( - textStyle: textStyle ?? _$this.textStyle, - padding: padding ?? _$this.padding, - ); - } - - /// Linearly interpolates between this [MdTextStyle] and another [MdTextStyle] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdTextStyle] is returned. When [t] is 1.0, the [other] [MdTextStyle] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdTextStyle] is returned. - /// - /// If [other] is null, this method returns the current [MdTextStyle] instance. - /// - /// The interpolation is performed on each property of the [MdTextStyle] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [textStyle]. - /// - [EdgeInsets.lerp] for [padding]. - - /// For , the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdTextStyle] is used. Otherwise, the value - /// from the [other] [MdTextStyle] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdTextStyle] configurations. - @override - MdTextStyle lerp(MdTextStyle? other, double t) { - if (other == null) return _$this; - - return MdTextStyle( - textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), - padding: EdgeInsets.lerp(_$this.padding, other.padding, t), - ); - } - - /// The list of properties that constitute the state of this [MdTextStyle]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdTextStyle] instances for equality. - @override - List get props => [ - _$this.textStyle, - _$this.padding, - ]; - - MdTextStyle get _$this => this as MdTextStyle; -} - -/// Represents the attributes of a [MdTextStyle]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdTextStyle]. -/// -/// Use this class to configure the attributes of a [MdTextStyle] and pass it to -/// the [MdTextStyle] constructor. -final class MdTextStyleAttribute extends SpecAttribute { - final TextStyleDto? textStyle; - final EdgeInsetsDto? padding; - - const MdTextStyleAttribute({ - this.textStyle, - this.padding, - }); - - /// Resolves to [MdTextStyle] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdTextStyle = MdTextStyleAttribute(...).resolve(mix); - /// ``` - @override - MdTextStyle resolve(MixData mix) { - return MdTextStyle( - textStyle: textStyle?.resolve(mix), - padding: padding?.resolve(mix), - ); - } - - /// Merges the properties of this [MdTextStyleAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdTextStyleAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdTextStyleAttribute merge(MdTextStyleAttribute? other) { - if (other == null) return this; - - return MdTextStyleAttribute( - textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, - padding: padding?.merge(other.padding) ?? other.padding, - ); - } - - /// The list of properties that constitute the state of this [MdTextStyleAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdTextStyleAttribute] instances for equality. - @override - List get props => [ - textStyle, - padding, - ]; -} - -/// Utility class for configuring [MdTextStyleAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdTextStyleAttribute]. -/// Use the methods of this class to configure specific properties of a [MdTextStyleAttribute]. -class MdTextStyleUtility - extends SpecUtility { - /// Utility for defining [MdTextStyleAttribute.textStyle] - late final textStyle = TextStyleUtility((v) => only(textStyle: v)); - - /// Utility for defining [MdTextStyleAttribute.padding] - late final padding = EdgeInsetsUtility((v) => only(padding: v)); - - MdTextStyleUtility(super.builder); - - static final self = MdTextStyleUtility((v) => v); - - /// Returns a new [MdTextStyleAttribute] with the specified properties. - @override - T only({ - TextStyleDto? textStyle, - EdgeInsetsDto? padding, - }) { - return builder(MdTextStyleAttribute( - textStyle: textStyle, - padding: padding, - )); - } -} - -/// A tween that interpolates between two [MdTextStyle] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdTextStyle] specifications. -class MdTextStyleTween extends Tween { - MdTextStyleTween({ - super.begin, - super.end, - }); - - @override - MdTextStyle lerp(double t) { - if (begin == null && end == null) return const MdTextStyle(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdList on Spec { - static MdList from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? const MdList(); - } - - /// {@template md_list_of} - /// Retrieves the [MdList] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdList] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdList]. - /// - /// Example: - /// - /// ```dart - /// final mdList = MdList.of(context); - /// ``` - /// {@endtemplate} - static MdList of(BuildContext context) { - return _$MdList.from(Mix.of(context)); - } - - /// Creates a copy of this [MdList] but with the given fields - /// replaced with the new values. - @override - MdList copyWith({ - TextStyle? list, - TextStyle? listItem, - TextStyle? listItemMarker, - double? listItemMarkerTrailingSpace, - double? listItemMinIndent, - }) { - return MdList( - list: list ?? _$this.list, - listItem: listItem ?? _$this.listItem, - listItemMarker: listItemMarker ?? _$this.listItemMarker, - listItemMarkerTrailingSpace: - listItemMarkerTrailingSpace ?? _$this.listItemMarkerTrailingSpace, - listItemMinIndent: listItemMinIndent ?? _$this.listItemMinIndent, - ); - } - - /// Linearly interpolates between this [MdList] and another [MdList] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdList] is returned. When [t] is 1.0, the [other] [MdList] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdList] is returned. - /// - /// If [other] is null, this method returns the current [MdList] instance. - /// - /// The interpolation is performed on each property of the [MdList] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [list] and [listItem] and [listItemMarker]. - /// - [MixHelpers.lerpDouble] for [listItemMarkerTrailingSpace] and [listItemMinIndent]. - - /// For , the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdList] is used. Otherwise, the value - /// from the [other] [MdList] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdList] configurations. - @override - MdList lerp(MdList? other, double t) { - if (other == null) return _$this; - - return MdList( - list: MixHelpers.lerpTextStyle(_$this.list, other.list, t), - listItem: MixHelpers.lerpTextStyle(_$this.listItem, other.listItem, t), - listItemMarker: MixHelpers.lerpTextStyle( - _$this.listItemMarker, other.listItemMarker, t), - listItemMarkerTrailingSpace: MixHelpers.lerpDouble( - _$this.listItemMarkerTrailingSpace, - other.listItemMarkerTrailingSpace, - t), - listItemMinIndent: MixHelpers.lerpDouble( - _$this.listItemMinIndent, other.listItemMinIndent, t), - ); - } - - /// The list of properties that constitute the state of this [MdList]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdList] instances for equality. - @override - List get props => [ - _$this.list, - _$this.listItem, - _$this.listItemMarker, - _$this.listItemMarkerTrailingSpace, - _$this.listItemMinIndent, - ]; - - MdList get _$this => this as MdList; -} - -/// Represents the attributes of a [MdList]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdList]. -/// -/// Use this class to configure the attributes of a [MdList] and pass it to -/// the [MdList] constructor. -final class MdListAttribute extends SpecAttribute { - final TextStyleDto? list; - final TextStyleDto? listItem; - final TextStyleDto? listItemMarker; - final double? listItemMarkerTrailingSpace; - final double? listItemMinIndent; - - const MdListAttribute({ - this.list, - this.listItem, - this.listItemMarker, - this.listItemMarkerTrailingSpace, - this.listItemMinIndent, - }); - - /// Resolves to [MdList] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdList = MdListAttribute(...).resolve(mix); - /// ``` - @override - MdList resolve(MixData mix) { - return MdList( - list: list?.resolve(mix), - listItem: listItem?.resolve(mix), - listItemMarker: listItemMarker?.resolve(mix), - listItemMarkerTrailingSpace: listItemMarkerTrailingSpace, - listItemMinIndent: listItemMinIndent, - ); - } - - /// Merges the properties of this [MdListAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdListAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdListAttribute merge(MdListAttribute? other) { - if (other == null) return this; - - return MdListAttribute( - list: list?.merge(other.list) ?? other.list, - listItem: listItem?.merge(other.listItem) ?? other.listItem, - listItemMarker: - listItemMarker?.merge(other.listItemMarker) ?? other.listItemMarker, - listItemMarkerTrailingSpace: - other.listItemMarkerTrailingSpace ?? listItemMarkerTrailingSpace, - listItemMinIndent: other.listItemMinIndent ?? listItemMinIndent, - ); - } - - /// The list of properties that constitute the state of this [MdListAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdListAttribute] instances for equality. - @override - List get props => [ - list, - listItem, - listItemMarker, - listItemMarkerTrailingSpace, - listItemMinIndent, - ]; -} - -/// Utility class for configuring [MdListAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdListAttribute]. -/// Use the methods of this class to configure specific properties of a [MdListAttribute]. -class MdListUtility - extends SpecUtility { - /// Utility for defining [MdListAttribute.list] - late final textStyle = TextStyleUtility((v) => only(list: v)); - - /// Utility for defining [MdListAttribute.listItem] - late final itemTextStyle = TextStyleUtility((v) => only(listItem: v)); - - /// Utility for defining [MdListAttribute.listItemMarker] - late final itemMarkerTextStyle = - TextStyleUtility((v) => only(listItemMarker: v)); - - /// Utility for defining [MdListAttribute.listItemMarkerTrailingSpace] - late final itemMarkerTrailingSpace = - DoubleUtility((v) => only(listItemMarkerTrailingSpace: v)); - - /// Utility for defining [MdListAttribute.listItemMinIndent] - late final itemMinIndent = DoubleUtility((v) => only(listItemMinIndent: v)); - - MdListUtility(super.builder); - - static final self = MdListUtility((v) => v); - - /// Returns a new [MdListAttribute] with the specified properties. - @override - T only({ - TextStyleDto? list, - TextStyleDto? listItem, - TextStyleDto? listItemMarker, - double? listItemMarkerTrailingSpace, - double? listItemMinIndent, - }) { - return builder(MdListAttribute( - list: list, - listItem: listItem, - listItemMarker: listItemMarker, - listItemMarkerTrailingSpace: listItemMarkerTrailingSpace, - listItemMinIndent: listItemMinIndent, - )); - } -} - -/// A tween that interpolates between two [MdList] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdList] specifications. -class MdListTween extends Tween { - MdListTween({ - super.begin, - super.end, - }); - - @override - MdList lerp(double t) { - if (begin == null && end == null) return const MdList(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdBlockQuote on Spec { - static MdBlockQuote from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? - const MdBlockQuote(); - } - - /// {@template md_block_quote_of} - /// Retrieves the [MdBlockQuote] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdBlockQuote] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdBlockQuote]. - /// - /// Example: - /// - /// ```dart - /// final mdBlockQuote = MdBlockQuote.of(context); - /// ``` - /// {@endtemplate} - static MdBlockQuote of(BuildContext context) { - return _$MdBlockQuote.from(Mix.of(context)); - } - - /// Creates a copy of this [MdBlockQuote] but with the given fields - /// replaced with the new values. - @override - MdBlockQuote copyWith({ - TextStyle? blockquote, - BoxDecoration? blockquoteDecoration, - EdgeInsets? blockquotePadding, - EdgeInsets? blockquoteContentPadding, - }) { - return MdBlockQuote( - blockquote: blockquote ?? _$this.blockquote, - blockquoteDecoration: blockquoteDecoration ?? _$this.blockquoteDecoration, - blockquotePadding: blockquotePadding ?? _$this.blockquotePadding, - blockquoteContentPadding: - blockquoteContentPadding ?? _$this.blockquoteContentPadding, - ); - } - - /// Linearly interpolates between this [MdBlockQuote] and another [MdBlockQuote] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdBlockQuote] is returned. When [t] is 1.0, the [other] [MdBlockQuote] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdBlockQuote] is returned. - /// - /// If [other] is null, this method returns the current [MdBlockQuote] instance. - /// - /// The interpolation is performed on each property of the [MdBlockQuote] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [blockquote]. - /// - [BoxDecoration.lerp] for [blockquoteDecoration]. - /// - [EdgeInsets.lerp] for [blockquotePadding] and [blockquoteContentPadding]. - - /// For , the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdBlockQuote] is used. Otherwise, the value - /// from the [other] [MdBlockQuote] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdBlockQuote] configurations. - @override - MdBlockQuote lerp(MdBlockQuote? other, double t) { - if (other == null) return _$this; - - return MdBlockQuote( - blockquote: - MixHelpers.lerpTextStyle(_$this.blockquote, other.blockquote, t), - blockquoteDecoration: BoxDecoration.lerp( - _$this.blockquoteDecoration, other.blockquoteDecoration, t), - blockquotePadding: - EdgeInsets.lerp(_$this.blockquotePadding, other.blockquotePadding, t), - blockquoteContentPadding: EdgeInsets.lerp( - _$this.blockquoteContentPadding, other.blockquoteContentPadding, t), - ); - } - - /// The list of properties that constitute the state of this [MdBlockQuote]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdBlockQuote] instances for equality. - @override - List get props => [ - _$this.blockquote, - _$this.blockquoteDecoration, - _$this.blockquotePadding, - _$this.blockquoteContentPadding, - ]; - - MdBlockQuote get _$this => this as MdBlockQuote; -} - -/// Represents the attributes of a [MdBlockQuote]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdBlockQuote]. -/// -/// Use this class to configure the attributes of a [MdBlockQuote] and pass it to -/// the [MdBlockQuote] constructor. -final class MdBlockQuoteAttribute extends SpecAttribute { - final TextStyleDto? blockquote; - final BoxDecorationDto? blockquoteDecoration; - final EdgeInsetsDto? blockquotePadding; - final EdgeInsetsDto? blockquoteContentPadding; - - const MdBlockQuoteAttribute({ - this.blockquote, - this.blockquoteDecoration, - this.blockquotePadding, - this.blockquoteContentPadding, - }); - - /// Resolves to [MdBlockQuote] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdBlockQuote = MdBlockQuoteAttribute(...).resolve(mix); - /// ``` - @override - MdBlockQuote resolve(MixData mix) { - return MdBlockQuote( - blockquote: blockquote?.resolve(mix), - blockquoteDecoration: blockquoteDecoration?.resolve(mix), - blockquotePadding: blockquotePadding?.resolve(mix), - blockquoteContentPadding: blockquoteContentPadding?.resolve(mix), - ); - } - - /// Merges the properties of this [MdBlockQuoteAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdBlockQuoteAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdBlockQuoteAttribute merge(MdBlockQuoteAttribute? other) { - if (other == null) return this; - - return MdBlockQuoteAttribute( - blockquote: blockquote?.merge(other.blockquote) ?? other.blockquote, - blockquoteDecoration: - blockquoteDecoration?.merge(other.blockquoteDecoration) ?? - other.blockquoteDecoration, - blockquotePadding: blockquotePadding?.merge(other.blockquotePadding) ?? - other.blockquotePadding, - blockquoteContentPadding: - blockquoteContentPadding?.merge(other.blockquoteContentPadding) ?? - other.blockquoteContentPadding, - ); - } - - /// The list of properties that constitute the state of this [MdBlockQuoteAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdBlockQuoteAttribute] instances for equality. - @override - List get props => [ - blockquote, - blockquoteDecoration, - blockquotePadding, - blockquoteContentPadding, - ]; -} - -/// Utility class for configuring [MdBlockQuoteAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdBlockQuoteAttribute]. -/// Use the methods of this class to configure specific properties of a [MdBlockQuoteAttribute]. -class MdBlockQuoteUtility - extends SpecUtility { - /// Utility for defining [MdBlockQuoteAttribute.blockquote] - late final textStyle = TextStyleUtility((v) => only(blockquote: v)); - - /// Utility for defining [MdBlockQuoteAttribute.blockquoteDecoration] - late final decoration = - BoxDecorationUtility((v) => only(blockquoteDecoration: v)); - - /// Utility for defining [MdBlockQuoteAttribute.blockquotePadding] - late final padding = EdgeInsetsUtility((v) => only(blockquotePadding: v)); - - /// Utility for defining [MdBlockQuoteAttribute.blockquoteContentPadding] - late final contentPadding = - EdgeInsetsUtility((v) => only(blockquoteContentPadding: v)); - - MdBlockQuoteUtility(super.builder); - - static final self = MdBlockQuoteUtility((v) => v); - - /// Returns a new [MdBlockQuoteAttribute] with the specified properties. - @override - T only({ - TextStyleDto? blockquote, - BoxDecorationDto? blockquoteDecoration, - EdgeInsetsDto? blockquotePadding, - EdgeInsetsDto? blockquoteContentPadding, - }) { - return builder(MdBlockQuoteAttribute( - blockquote: blockquote, - blockquoteDecoration: blockquoteDecoration, - blockquotePadding: blockquotePadding, - blockquoteContentPadding: blockquoteContentPadding, - )); - } -} - -/// A tween that interpolates between two [MdBlockQuote] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdBlockQuote] specifications. -class MdBlockQuoteTween extends Tween { - MdBlockQuoteTween({ - super.begin, - super.end, - }); - - @override - MdBlockQuote lerp(double t) { - if (begin == null && end == null) return const MdBlockQuote(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdTable on Spec { - static MdTable from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? const MdTable(); - } - - /// {@template md_table_of} - /// Retrieves the [MdTable] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdTable] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdTable]. - /// - /// Example: - /// - /// ```dart - /// final mdTable = MdTable.of(context); - /// ``` - /// {@endtemplate} - static MdTable of(BuildContext context) { - return _$MdTable.from(Mix.of(context)); - } - - /// Creates a copy of this [MdTable] but with the given fields - /// replaced with the new values. - @override - MdTable copyWith({ - TextStyle? table, - TextStyle? tableHead, - TextStyle? tableBody, - TableBorder? tableBorder, - BoxDecoration? tableRowDecoration, - MarkdownAlternating? tableRowDecorationAlternating, - EdgeInsets? tableCellPadding, - TableColumnWidth? tableColumnWidth, - }) { - return MdTable( - table: table ?? _$this.table, - tableHead: tableHead ?? _$this.tableHead, - tableBody: tableBody ?? _$this.tableBody, - tableBorder: tableBorder ?? _$this.tableBorder, - tableRowDecoration: tableRowDecoration ?? _$this.tableRowDecoration, - tableRowDecorationAlternating: - tableRowDecorationAlternating ?? _$this.tableRowDecorationAlternating, - tableCellPadding: tableCellPadding ?? _$this.tableCellPadding, - tableColumnWidth: tableColumnWidth ?? _$this.tableColumnWidth, - ); - } - - /// Linearly interpolates between this [MdTable] and another [MdTable] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdTable] is returned. When [t] is 1.0, the [other] [MdTable] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdTable] is returned. - /// - /// If [other] is null, this method returns the current [MdTable] instance. - /// - /// The interpolation is performed on each property of the [MdTable] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [table] and [tableHead] and [tableBody]. - /// - [TableBorder.lerp] for [tableBorder]. - /// - [BoxDecoration.lerp] for [tableRowDecoration]. - /// - [EdgeInsets.lerp] for [tableCellPadding]. - - /// For [tableRowDecorationAlternating] and [tableColumnWidth], the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdTable] is used. Otherwise, the value - /// from the [other] [MdTable] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdTable] configurations. - @override - MdTable lerp(MdTable? other, double t) { - if (other == null) return _$this; - - return MdTable( - table: MixHelpers.lerpTextStyle(_$this.table, other.table, t), - tableHead: MixHelpers.lerpTextStyle(_$this.tableHead, other.tableHead, t), - tableBody: MixHelpers.lerpTextStyle(_$this.tableBody, other.tableBody, t), - tableBorder: TableBorder.lerp(_$this.tableBorder, other.tableBorder, t), - tableRowDecoration: BoxDecoration.lerp( - _$this.tableRowDecoration, other.tableRowDecoration, t), - tableRowDecorationAlternating: t < 0.5 - ? _$this.tableRowDecorationAlternating - : other.tableRowDecorationAlternating, - tableCellPadding: - EdgeInsets.lerp(_$this.tableCellPadding, other.tableCellPadding, t), - tableColumnWidth: - t < 0.5 ? _$this.tableColumnWidth : other.tableColumnWidth, - ); - } - - /// The list of properties that constitute the state of this [MdTable]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdTable] instances for equality. - @override - List get props => [ - _$this.table, - _$this.tableHead, - _$this.tableBody, - _$this.tableBorder, - _$this.tableRowDecoration, - _$this.tableRowDecorationAlternating, - _$this.tableCellPadding, - _$this.tableColumnWidth, - ]; - - MdTable get _$this => this as MdTable; -} - -/// Represents the attributes of a [MdTable]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdTable]. -/// -/// Use this class to configure the attributes of a [MdTable] and pass it to -/// the [MdTable] constructor. -final class MdTableAttribute extends SpecAttribute { - final TextStyleDto? table; - final TextStyleDto? tableHead; - final TextStyleDto? tableBody; - final TableBorder? tableBorder; - final BoxDecorationDto? tableRowDecoration; - final MarkdownAlternating? tableRowDecorationAlternating; - final EdgeInsetsDto? tableCellPadding; - final TableColumnWidth? tableColumnWidth; - - const MdTableAttribute({ - this.table, - this.tableHead, - this.tableBody, - this.tableBorder, - this.tableRowDecoration, - this.tableRowDecorationAlternating, - this.tableCellPadding, - this.tableColumnWidth, - }); - - /// Resolves to [MdTable] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdTable = MdTableAttribute(...).resolve(mix); - /// ``` - @override - MdTable resolve(MixData mix) { - return MdTable( - table: table?.resolve(mix), - tableHead: tableHead?.resolve(mix), - tableBody: tableBody?.resolve(mix), - tableBorder: tableBorder, - tableRowDecoration: tableRowDecoration?.resolve(mix), - tableRowDecorationAlternating: tableRowDecorationAlternating, - tableCellPadding: tableCellPadding?.resolve(mix), - tableColumnWidth: tableColumnWidth, - ); - } - - /// Merges the properties of this [MdTableAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdTableAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdTableAttribute merge(MdTableAttribute? other) { - if (other == null) return this; - - return MdTableAttribute( - table: table?.merge(other.table) ?? other.table, - tableHead: tableHead?.merge(other.tableHead) ?? other.tableHead, - tableBody: tableBody?.merge(other.tableBody) ?? other.tableBody, - tableBorder: other.tableBorder ?? tableBorder, - tableRowDecoration: tableRowDecoration?.merge(other.tableRowDecoration) ?? - other.tableRowDecoration, - tableRowDecorationAlternating: - other.tableRowDecorationAlternating ?? tableRowDecorationAlternating, - tableCellPadding: tableCellPadding?.merge(other.tableCellPadding) ?? - other.tableCellPadding, - tableColumnWidth: other.tableColumnWidth ?? tableColumnWidth, - ); - } - - /// The list of properties that constitute the state of this [MdTableAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdTableAttribute] instances for equality. - @override - List get props => [ - table, - tableHead, - tableBody, - tableBorder, - tableRowDecoration, - tableRowDecorationAlternating, - tableCellPadding, - tableColumnWidth, - ]; -} - -/// Utility class for configuring [MdTableAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdTableAttribute]. -/// Use the methods of this class to configure specific properties of a [MdTableAttribute]. -class MdTableUtility - extends SpecUtility { - /// Utility for defining [MdTableAttribute.table] - late final textStyle = TextStyleUtility((v) => only(table: v)); - - /// Utility for defining [MdTableAttribute.tableHead] - late final headTextStyle = TextStyleUtility((v) => only(tableHead: v)); - - /// Utility for defining [MdTableAttribute.tableBody] - late final bodyTextStyle = TextStyleUtility((v) => only(tableBody: v)); - - /// Utility for defining [MdTableAttribute.tableBorder] - late final border = TableBorderUtility((v) => only(tableBorder: v)); - - /// Utility for defining [MdTableAttribute.tableRowDecoration] - late final rowDecoration = - BoxDecorationUtility((v) => only(tableRowDecoration: v)); - - /// Utility for defining [MdTableAttribute.tableRowDecorationAlternating] - late final rowDecorationAlternating = - MarkdownAlternatingUtility((v) => only(tableRowDecorationAlternating: v)); - - /// Utility for defining [MdTableAttribute.tableCellPadding] - late final cellPadding = EdgeInsetsUtility((v) => only(tableCellPadding: v)); - - /// Utility for defining [MdTableAttribute.tableColumnWidth] - late final columnWidth = - TableColumnWidthUtility((v) => only(tableColumnWidth: v)); - - MdTableUtility(super.builder); - - static final self = MdTableUtility((v) => v); - - /// Returns a new [MdTableAttribute] with the specified properties. - @override - T only({ - TextStyleDto? table, - TextStyleDto? tableHead, - TextStyleDto? tableBody, - TableBorder? tableBorder, - BoxDecorationDto? tableRowDecoration, - MarkdownAlternating? tableRowDecorationAlternating, - EdgeInsetsDto? tableCellPadding, - TableColumnWidth? tableColumnWidth, - }) { - return builder(MdTableAttribute( - table: table, - tableHead: tableHead, - tableBody: tableBody, - tableBorder: tableBorder, - tableRowDecoration: tableRowDecoration, - tableRowDecorationAlternating: tableRowDecorationAlternating, - tableCellPadding: tableCellPadding, - tableColumnWidth: tableColumnWidth, - )); - } -} - -/// A tween that interpolates between two [MdTable] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdTable] specifications. -class MdTableTween extends Tween { - MdTableTween({ - super.begin, - super.end, - }); - - @override - MdTable lerp(double t) { - if (begin == null && end == null) return const MdTable(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$MdCode on Spec { - static MdCode from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? const MdCode(); - } - - /// {@template md_code_of} - /// Retrieves the [MdCode] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [MdCode] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [MdCode]. - /// - /// Example: - /// - /// ```dart - /// final mdCode = MdCode.of(context); - /// ``` - /// {@endtemplate} - static MdCode of(BuildContext context) { - return _$MdCode.from(Mix.of(context)); - } - - /// Creates a copy of this [MdCode] but with the given fields - /// replaced with the new values. - @override - MdCode copyWith({ - TextStyle? codeSpan, - EdgeInsets? codeblockPadding, - BoxDecoration? codeblockDecoration, - Color? copyIconColor, - TextStyle? codeBlock, - }) { - return MdCode( - codeSpan: codeSpan ?? _$this.codeSpan, - codeblockPadding: codeblockPadding ?? _$this.codeblockPadding, - codeblockDecoration: codeblockDecoration ?? _$this.codeblockDecoration, - copyIconColor: copyIconColor ?? _$this.copyIconColor, - codeBlock: codeBlock ?? _$this.codeBlock, - ); - } - - /// Linearly interpolates between this [MdCode] and another [MdCode] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [MdCode] is returned. When [t] is 1.0, the [other] [MdCode] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [MdCode] is returned. - /// - /// If [other] is null, this method returns the current [MdCode] instance. - /// - /// The interpolation is performed on each property of the [MdCode] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [codeSpan] and [codeBlock]. - /// - [EdgeInsets.lerp] for [codeblockPadding]. - /// - [BoxDecoration.lerp] for [codeblockDecoration]. - /// - [Color.lerp] for [copyIconColor]. - - /// For , the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [MdCode] is used. Otherwise, the value - /// from the [other] [MdCode] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [MdCode] configurations. - @override - MdCode lerp(MdCode? other, double t) { - if (other == null) return _$this; - - return MdCode( - codeSpan: MixHelpers.lerpTextStyle(_$this.codeSpan, other.codeSpan, t), - codeblockPadding: - EdgeInsets.lerp(_$this.codeblockPadding, other.codeblockPadding, t), - codeblockDecoration: BoxDecoration.lerp( - _$this.codeblockDecoration, other.codeblockDecoration, t), - copyIconColor: Color.lerp(_$this.copyIconColor, other.copyIconColor, t), - codeBlock: MixHelpers.lerpTextStyle(_$this.codeBlock, other.codeBlock, t), - ); - } - - /// The list of properties that constitute the state of this [MdCode]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdCode] instances for equality. - @override - List get props => [ - _$this.codeSpan, - _$this.codeblockPadding, - _$this.codeblockDecoration, - _$this.copyIconColor, - _$this.codeBlock, - ]; - - MdCode get _$this => this as MdCode; -} - -/// Represents the attributes of a [MdCode]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [MdCode]. -/// -/// Use this class to configure the attributes of a [MdCode] and pass it to -/// the [MdCode] constructor. -final class MdCodeAttribute extends SpecAttribute { - final TextStyleDto? codeSpan; - final EdgeInsetsDto? codeblockPadding; - final BoxDecorationDto? codeblockDecoration; - final ColorDto? copyIconColor; - final TextStyleDto? codeBlock; - - const MdCodeAttribute({ - this.codeSpan, - this.codeblockPadding, - this.codeblockDecoration, - this.copyIconColor, - this.codeBlock, - }); - - /// Resolves to [MdCode] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final mdCode = MdCodeAttribute(...).resolve(mix); - /// ``` - @override - MdCode resolve(MixData mix) { - return MdCode( - codeSpan: codeSpan?.resolve(mix), - codeblockPadding: codeblockPadding?.resolve(mix), - codeblockDecoration: codeblockDecoration?.resolve(mix), - copyIconColor: copyIconColor?.resolve(mix), - codeBlock: codeBlock?.resolve(mix), - ); - } - - /// Merges the properties of this [MdCodeAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [MdCodeAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - MdCodeAttribute merge(MdCodeAttribute? other) { - if (other == null) return this; - - return MdCodeAttribute( - codeSpan: codeSpan?.merge(other.codeSpan) ?? other.codeSpan, - codeblockPadding: codeblockPadding?.merge(other.codeblockPadding) ?? - other.codeblockPadding, - codeblockDecoration: - codeblockDecoration?.merge(other.codeblockDecoration) ?? - other.codeblockDecoration, - copyIconColor: - copyIconColor?.merge(other.copyIconColor) ?? other.copyIconColor, - codeBlock: codeBlock?.merge(other.codeBlock) ?? other.codeBlock, - ); - } - - /// The list of properties that constitute the state of this [MdCodeAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [MdCodeAttribute] instances for equality. - @override - List get props => [ - codeSpan, - codeblockPadding, - codeblockDecoration, - copyIconColor, - codeBlock, - ]; -} - -/// Utility class for configuring [MdCodeAttribute] properties. -/// -/// This class provides methods to set individual properties of a [MdCodeAttribute]. -/// Use the methods of this class to configure specific properties of a [MdCodeAttribute]. -class MdCodeUtility - extends SpecUtility { - /// Utility for defining [MdCodeAttribute.codeSpan] - late final span = TextStyleUtility((v) => only(codeSpan: v)); - - /// Utility for defining [MdCodeAttribute.codeblockPadding] - late final padding = EdgeInsetsUtility((v) => only(codeblockPadding: v)); - - /// Utility for defining [MdCodeAttribute.codeblockDecoration] - late final decoration = - BoxDecorationUtility((v) => only(codeblockDecoration: v)); - - /// Utility for defining [MdCodeAttribute.copyIconColor] - late final copyIconColor = ColorUtility((v) => only(copyIconColor: v)); - - /// Utility for defining [MdCodeAttribute.codeBlock] - late final block = TextStyleUtility((v) => only(codeBlock: v)); - - MdCodeUtility(super.builder); - - static final self = MdCodeUtility((v) => v); - - /// Returns a new [MdCodeAttribute] with the specified properties. - @override - T only({ - TextStyleDto? codeSpan, - EdgeInsetsDto? codeblockPadding, - BoxDecorationDto? codeblockDecoration, - ColorDto? copyIconColor, - TextStyleDto? codeBlock, - }) { - return builder(MdCodeAttribute( - codeSpan: codeSpan, - codeblockPadding: codeblockPadding, - codeblockDecoration: codeblockDecoration, - copyIconColor: copyIconColor, - codeBlock: codeBlock, - )); - } -} - -/// A tween that interpolates between two [MdCode] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [MdCode] specifications. -class MdCodeTween extends Tween { - MdCodeTween({ - super.begin, - super.end, - }); - - @override - MdCode lerp(double t) { - if (begin == null && end == null) return const MdCode(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} - -// ignore_for_file: deprecated_member_use_from_same_package - -mixin _$SlideSpec on Spec { - static SlideSpec from(MixData mix) { - return mix.attributeOf()?.resolve(mix) ?? - const SlideSpec(); - } - - /// {@template slide_spec_of} - /// Retrieves the [SlideSpec] from the nearest [Mix] ancestor in the widget tree. - /// - /// This method uses [Mix.of] to obtain the [Mix] instance associated with the - /// given [BuildContext], and then retrieves the [SlideSpec] from that [Mix]. - /// If no ancestor [Mix] is found, this method returns an empty [SlideSpec]. - /// - /// Example: - /// - /// ```dart - /// final slideSpec = SlideSpec.of(context); - /// ``` - /// {@endtemplate} - static SlideSpec of(BuildContext context) { - return _$SlideSpec.from(Mix.of(context)); - } - - /// Creates a copy of this [SlideSpec] but with the given fields - /// replaced with the new values. - @override - SlideSpec copyWith({ - MdTextStyle? headline1, - MdTextStyle? headline2, - MdTextStyle? headline3, - MdTextStyle? headline4, - MdTextStyle? headline5, - MdTextStyle? headline6, - MdTextStyle? paragraph, - TextStyle? link, - double? blockSpacing, - MdDivider? divider, - MdBlockQuote? blockquote, - MdList? list, - MdTable? table, - MdCode? code, - BoxSpec? innerContainer, - BoxSpec? outerContainer, - BoxSpec? contentContainer, - ImageSpec? image, - AnimatedData? animated, - }) { - return SlideSpec( - headline1: headline1 ?? _$this.headline1, - headline2: headline2 ?? _$this.headline2, - headline3: headline3 ?? _$this.headline3, - headline4: headline4 ?? _$this.headline4, - headline5: headline5 ?? _$this.headline5, - headline6: headline6 ?? _$this.headline6, - paragraph: paragraph ?? _$this.paragraph, - link: link ?? _$this.link, - blockSpacing: blockSpacing ?? _$this.blockSpacing, - divider: divider ?? _$this.divider, - blockquote: blockquote ?? _$this.blockquote, - list: list ?? _$this.list, - table: table ?? _$this.table, - code: code ?? _$this.code, - innerContainer: innerContainer ?? _$this.innerContainer, - outerContainer: outerContainer ?? _$this.outerContainer, - contentContainer: contentContainer ?? _$this.contentContainer, - image: image ?? _$this.image, - animated: animated ?? _$this.animated, - ); - } - - /// Linearly interpolates between this [SlideSpec] and another [SlideSpec] based on the given parameter [t]. - /// - /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. - /// When [t] is 0.0, the current [SlideSpec] is returned. When [t] is 1.0, the [other] [SlideSpec] is returned. - /// For values of [t] between 0.0 and 1.0, an interpolated [SlideSpec] is returned. - /// - /// If [other] is null, this method returns the current [SlideSpec] instance. - /// - /// The interpolation is performed on each property of the [SlideSpec] using the appropriate - /// interpolation method: - /// - /// - [MixHelpers.lerpTextStyle] for [link]. - /// - [MixHelpers.lerpDouble] for [blockSpacing]. - /// - [BoxSpec.lerp] for [innerContainer] and [outerContainer] and [contentContainer]. - /// - [ImageSpec.lerp] for [image]. - - /// For [headline1] and [headline2] and [headline3] and [headline4] and [headline5] and [headline6] and [paragraph] and [divider] and [blockquote] and [list] and [table] and [code] and [animated], the interpolation is performed using a step function. - /// If [t] is less than 0.5, the value from the current [SlideSpec] is used. Otherwise, the value - /// from the [other] [SlideSpec] is used. - /// - /// This method is typically used in animations to smoothly transition between - /// different [SlideSpec] configurations. - @override - SlideSpec lerp(SlideSpec? other, double t) { - if (other == null) return _$this; - - return SlideSpec( - headline1: _$this.headline1?.lerp(other.headline1, t) ?? other.headline1, - headline2: _$this.headline2?.lerp(other.headline2, t) ?? other.headline2, - headline3: _$this.headline3?.lerp(other.headline3, t) ?? other.headline3, - headline4: _$this.headline4?.lerp(other.headline4, t) ?? other.headline4, - headline5: _$this.headline5?.lerp(other.headline5, t) ?? other.headline5, - headline6: _$this.headline6?.lerp(other.headline6, t) ?? other.headline6, - paragraph: _$this.paragraph?.lerp(other.paragraph, t) ?? other.paragraph, - link: MixHelpers.lerpTextStyle(_$this.link, other.link, t), - blockSpacing: - MixHelpers.lerpDouble(_$this.blockSpacing, other.blockSpacing, t), - divider: _$this.divider?.lerp(other.divider, t) ?? other.divider, - blockquote: - _$this.blockquote?.lerp(other.blockquote, t) ?? other.blockquote, - list: _$this.list?.lerp(other.list, t) ?? other.list, - table: _$this.table?.lerp(other.table, t) ?? other.table, - code: _$this.code?.lerp(other.code, t) ?? other.code, - innerContainer: _$this.innerContainer.lerp(other.innerContainer, t), - outerContainer: _$this.outerContainer.lerp(other.outerContainer, t), - contentContainer: _$this.contentContainer.lerp(other.contentContainer, t), - image: _$this.image.lerp(other.image, t), - animated: t < 0.5 ? _$this.animated : other.animated, - ); - } - - /// The list of properties that constitute the state of this [SlideSpec]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [SlideSpec] instances for equality. - @override - List get props => [ - _$this.headline1, - _$this.headline2, - _$this.headline3, - _$this.headline4, - _$this.headline5, - _$this.headline6, - _$this.paragraph, - _$this.link, - _$this.blockSpacing, - _$this.divider, - _$this.blockquote, - _$this.list, - _$this.table, - _$this.code, - _$this.innerContainer, - _$this.outerContainer, - _$this.contentContainer, - _$this.image, - _$this.animated, - ]; - - SlideSpec get _$this => this as SlideSpec; -} - -/// Represents the attributes of a [SlideSpec]. -/// -/// This class encapsulates properties defining the layout and -/// appearance of a [SlideSpec]. -/// -/// Use this class to configure the attributes of a [SlideSpec] and pass it to -/// the [SlideSpec] constructor. -final class SlideSpecAttribute extends SpecAttribute { - final MdTextStyleAttribute? headline1; - final MdTextStyleAttribute? headline2; - final MdTextStyleAttribute? headline3; - final MdTextStyleAttribute? headline4; - final MdTextStyleAttribute? headline5; - final MdTextStyleAttribute? headline6; - final MdTextStyleAttribute? paragraph; - final TextStyleDto? link; - final double? blockSpacing; - final MdDividerAttribute? divider; - final MdBlockQuoteAttribute? blockquote; - final MdListAttribute? list; - final MdTableAttribute? table; - final MdCodeAttribute? code; - final BoxSpecAttribute? innerContainer; - final BoxSpecAttribute? outerContainer; - final BoxSpecAttribute? contentContainer; - final ImageSpecAttribute? image; - - const SlideSpecAttribute({ - this.headline1, - this.headline2, - this.headline3, - this.headline4, - this.headline5, - this.headline6, - this.paragraph, - this.link, - this.blockSpacing, - this.divider, - this.blockquote, - this.list, - this.table, - this.code, - this.innerContainer, - this.outerContainer, - this.contentContainer, - this.image, - super.animated, - }); - - /// Resolves to [SlideSpec] using the provided [MixData]. - /// - /// If a property is null in the [MixData], it falls back to the - /// default value defined in the `defaultValue` for that property. - /// - /// ```dart - /// final slideSpec = SlideSpecAttribute(...).resolve(mix); - /// ``` - @override - SlideSpec resolve(MixData mix) { - return SlideSpec( - headline1: headline1?.resolve(mix), - headline2: headline2?.resolve(mix), - headline3: headline3?.resolve(mix), - headline4: headline4?.resolve(mix), - headline5: headline5?.resolve(mix), - headline6: headline6?.resolve(mix), - paragraph: paragraph?.resolve(mix), - link: link?.resolve(mix), - blockSpacing: blockSpacing, - divider: divider?.resolve(mix), - blockquote: blockquote?.resolve(mix), - list: list?.resolve(mix), - table: table?.resolve(mix), - code: code?.resolve(mix), - innerContainer: innerContainer?.resolve(mix), - outerContainer: outerContainer?.resolve(mix), - contentContainer: contentContainer?.resolve(mix), - image: image?.resolve(mix), - animated: animated?.resolve(mix) ?? mix.animation, - ); - } - - /// Merges the properties of this [SlideSpecAttribute] with the properties of [other]. - /// - /// If [other] is null, returns this instance unchanged. Otherwise, returns a new - /// [SlideSpecAttribute] with the properties of [other] taking precedence over - /// the corresponding properties of this instance. - /// - /// Properties from [other] that are null will fall back - /// to the values from this instance. - @override - SlideSpecAttribute merge(SlideSpecAttribute? other) { - if (other == null) return this; - - return SlideSpecAttribute( - headline1: headline1?.merge(other.headline1) ?? other.headline1, - headline2: headline2?.merge(other.headline2) ?? other.headline2, - headline3: headline3?.merge(other.headline3) ?? other.headline3, - headline4: headline4?.merge(other.headline4) ?? other.headline4, - headline5: headline5?.merge(other.headline5) ?? other.headline5, - headline6: headline6?.merge(other.headline6) ?? other.headline6, - paragraph: paragraph?.merge(other.paragraph) ?? other.paragraph, - link: link?.merge(other.link) ?? other.link, - blockSpacing: other.blockSpacing ?? blockSpacing, - divider: divider?.merge(other.divider) ?? other.divider, - blockquote: blockquote?.merge(other.blockquote) ?? other.blockquote, - list: list?.merge(other.list) ?? other.list, - table: table?.merge(other.table) ?? other.table, - code: code?.merge(other.code) ?? other.code, - innerContainer: - innerContainer?.merge(other.innerContainer) ?? other.innerContainer, - outerContainer: - outerContainer?.merge(other.outerContainer) ?? other.outerContainer, - contentContainer: contentContainer?.merge(other.contentContainer) ?? - other.contentContainer, - image: image?.merge(other.image) ?? other.image, - animated: animated?.merge(other.animated) ?? other.animated, - ); - } - - /// The list of properties that constitute the state of this [SlideSpecAttribute]. - /// - /// This property is used by the [==] operator and the [hashCode] getter to - /// compare two [SlideSpecAttribute] instances for equality. - @override - List get props => [ - headline1, - headline2, - headline3, - headline4, - headline5, - headline6, - paragraph, - link, - blockSpacing, - divider, - blockquote, - list, - table, - code, - innerContainer, - outerContainer, - contentContainer, - image, - animated, - ]; -} - -/// Utility class for configuring [SlideSpecAttribute] properties. -/// -/// This class provides methods to set individual properties of a [SlideSpecAttribute]. -/// Use the methods of this class to configure specific properties of a [SlideSpecAttribute]. -class SlideSpecUtility - extends SpecUtility { - /// Utility for defining [SlideSpecAttribute.headline1] - late final h1 = MdTextStyleUtility((v) => only(headline1: v)); - - /// Utility for defining [SlideSpecAttribute.headline2] - late final h2 = MdTextStyleUtility((v) => only(headline2: v)); - - /// Utility for defining [SlideSpecAttribute.headline3] - late final h3 = MdTextStyleUtility((v) => only(headline3: v)); - - /// Utility for defining [SlideSpecAttribute.headline4] - late final h4 = MdTextStyleUtility((v) => only(headline4: v)); - - /// Utility for defining [SlideSpecAttribute.headline5] - late final h5 = MdTextStyleUtility((v) => only(headline5: v)); - - /// Utility for defining [SlideSpecAttribute.headline6] - late final h6 = MdTextStyleUtility((v) => only(headline6: v)); - - /// Utility for defining [SlideSpecAttribute.paragraph] - late final paragraph = MdTextStyleUtility((v) => only(paragraph: v)); - - /// Utility for defining [SlideSpecAttribute.link] - late final link = TextStyleUtility((v) => only(link: v)); - - /// Utility for defining [SlideSpecAttribute.blockSpacing] - late final blockSpacing = DoubleUtility((v) => only(blockSpacing: v)); - - /// Utility for defining [SlideSpecAttribute.divider] - late final divider = MdDividerUtility((v) => only(divider: v)); - - /// Utility for defining [SlideSpecAttribute.blockquote] - late final blockquote = MdBlockQuoteUtility((v) => only(blockquote: v)); - - /// Utility for defining [SlideSpecAttribute.list] - late final list = MdListUtility((v) => only(list: v)); - - /// Utility for defining [SlideSpecAttribute.table] - late final table = MdTableUtility((v) => only(table: v)); - - /// Utility for defining [SlideSpecAttribute.code] - late final code = MdCodeUtility((v) => only(code: v)); - - /// Utility for defining [SlideSpecAttribute.innerContainer] - late final innerContainer = BoxSpecUtility((v) => only(innerContainer: v)); - - /// Utility for defining [SlideSpecAttribute.outerContainer] - late final outerContainer = BoxSpecUtility((v) => only(outerContainer: v)); - - /// Utility for defining [SlideSpecAttribute.contentContainer] - late final contentContainer = - BoxSpecUtility((v) => only(contentContainer: v)); - - /// Utility for defining [SlideSpecAttribute.image] - late final image = ImageSpecUtility((v) => only(image: v)); - - /// Utility for defining [SlideSpecAttribute.animated] - late final animated = AnimatedUtility((v) => only(animated: v)); - - SlideSpecUtility(super.builder); - - static final self = SlideSpecUtility((v) => v); - - /// Returns a new [SlideSpecAttribute] with the specified properties. - @override - T only({ - MdTextStyleAttribute? headline1, - MdTextStyleAttribute? headline2, - MdTextStyleAttribute? headline3, - MdTextStyleAttribute? headline4, - MdTextStyleAttribute? headline5, - MdTextStyleAttribute? headline6, - MdTextStyleAttribute? paragraph, - TextStyleDto? link, - double? blockSpacing, - MdDividerAttribute? divider, - MdBlockQuoteAttribute? blockquote, - MdListAttribute? list, - MdTableAttribute? table, - MdCodeAttribute? code, - BoxSpecAttribute? innerContainer, - BoxSpecAttribute? outerContainer, - BoxSpecAttribute? contentContainer, - ImageSpecAttribute? image, - AnimatedDataDto? animated, - }) { - return builder(SlideSpecAttribute( - headline1: headline1, - headline2: headline2, - headline3: headline3, - headline4: headline4, - headline5: headline5, - headline6: headline6, - paragraph: paragraph, - link: link, - blockSpacing: blockSpacing, - divider: divider, - blockquote: blockquote, - list: list, - table: table, - code: code, - innerContainer: innerContainer, - outerContainer: outerContainer, - contentContainer: contentContainer, - image: image, - animated: animated, - )); - } -} - -/// A tween that interpolates between two [SlideSpec] instances. -/// -/// This class can be used in animations to smoothly transition between -/// different [SlideSpec] specifications. -class SlideSpecTween extends Tween { - SlideSpecTween({ - super.begin, - super.end, - }); - - @override - SlideSpec lerp(double t) { - if (begin == null && end == null) return const SlideSpec(); - if (begin == null) return end!; - - return begin!.lerp(end!, t); - } -} diff --git a/melos.yaml b/melos.yaml new file mode 100644 index 00000000..91051863 --- /dev/null +++ b/melos.yaml @@ -0,0 +1,89 @@ +name: superdeck_workspace + +sdkPath: .fvm/flutter_sdk +packages: + - packages/* + - packages/*/example + +command: + bootstrap: + environment: + sdk: ">=3.3.0 <4.0.0" + flutter: ">=3.19.0" + dependencies: + collection: ^1.18.0 + # publish: + # hooks: + # pre: melos run gen:build + +scripts: + # ANALYSIS + analyze: + run: melos run analyze:dart && melos run analyze:dcm + description: Run all static analysis checks. + exec: + failFast: true + + analyze:dart: + run: melos exec -c 10 -- dart analyze --fatal-infos + description: Run Dart static analysis checks. + + analyze:dcm: + run: melos exec -c 10 -- dcm analyze . --fatal-style --fatal-performance --fatal-warnings + description: Run DCM static analysis checks. + packageFilters: + dependsOn: "dart_code_metrics_presets" + + lint:fix:all: + run: melos run lint:dart:fix && melos run lint:dcm:fix + description: Run all static analysis checks and apply fixes. + + lint:dart:fix: + run: melos exec -- dart fix --apply . + description: Run Dart static analysis checks. + + lint:dcm:fix: + run: melos exec -- dcm fix . + description: Run DCM static analysis checks. + packageFilters: + dependsOn: "dart_code_metrics_presets" + + gen:watch: + run: melos exec --order-dependents -- dart run build_runner watch --delete-conflicting-outputs + description: Generate code for all packages + packageFilters: + dependsOn: "build_runner" + + gen:build: + run: melos run gen:clean && melos exec --order-dependents -- dart run build_runner build --delete-conflicting-outputs + description: Generate code for all packages + packageFilters: + dependsOn: "build_runner" + + gen:clean: + run: melos exec --order-dependents -- dart run build_runner clean + description: Clean generated code for all packages + packageFilters: + dependsOn: "build_runner" + + test: + run: melos exec -- flutter test + description: Run flutter test + packageFilters: + dirExists: test + + test:coverage: + run: melos exec -- flutter test --coverage + description: Run flutter test with coverage + packageFilters: + dirExists: test + + brb: + run: melos run gen:build + + brbc: + run: melos run gen:clean + fix: + run: melos run lint:fix:all + custom_lint_analyze: + run: dart pub global activate custom_lint && melos exec --depends-on="mix_lint" custom_lint \ No newline at end of file diff --git a/.vscode/launch.json b/packages/superdeck/.vscode/launch.json similarity index 100% rename from .vscode/launch.json rename to packages/superdeck/.vscode/launch.json diff --git a/packages/superdeck/README.md b/packages/superdeck/README.md new file mode 100644 index 00000000..74e35cd7 --- /dev/null +++ b/packages/superdeck/README.md @@ -0,0 +1,315 @@ +![Superdeck logo](./assets/logo.png) + +SuperDeck enables you to craft visually appealing and interactive presentations directly within your Flutter apps, using the simplicity and power of Markdown. + +![Screenshot](https://github.com/leoafarias/superdeck/assets/435833/42ec88e9-d3d9-4c52-bbf9-5a2809cca257) + +### [View demo here](https://superdeck-dev.web.app) + +### [Example code](https://github.com/leoafarias/superdeck/blob/main/example/slides.md) + +## Getting Started + +Follow these steps to integrate SuperDeck into your Flutter project: + +1. Install the `superdeck` package by running the following command: + + ```bash + flutter pub add superdeck + ``` + +2. Import the `superdeck` package in your Dart code: + + ```dart + import 'package:superdeck/superdeck.dart'; + ``` + +3. Initialize SuperDeck and run the app. + + ```dart + void main() { + runApp(const SuperDeckApp()); + } + ``` + +4. Create a `slides.md` file at the root of your project. + +5. Configure your `pubspec.yaml` file to include the necessary assets: + + ```yaml + flutter: + assets: + - assets/ + - assets/images/ + ``` + + The `assets` directory is used to slide and asset references, while the `assets/images` directory is specifically used for storing images used in your presentations. + +6. Configure your app + + MacOS + + Change your `Release.entitlements` + + ```xml + + com.apple.security.app-sandbox + + com.apple.security.network.client + + + ``` + + Change `DebugProfile.entitlements` + + ```xml + + com.apple.security.app-sandbox + + com.apple.security.cs.allow-jit + + com.apple.security.network.server + + com.apple.security.network.client + + + ``` + +7. Start building your slides in the `slides.md` file using Markdown syntax and SuperDeck's slide templates and configurations. + +### SuperDeck Options + +#### `style` + +SuperDeck provides a robust styling system by leveraging [Mix](https://fluttermix.com), This allows a complete control by creating endless possibility of defining styling every element in teh slide, and also creating `style variants`. + +#### `examples` + +These are widget examples that can be referenced in the slide. You can read more about how to reference these examples in the [Widget Template](#widget-template) section. + +```dart +SuperDeckApp( + style: style, + examples: [ + Example( + name: 'demo', + builder: (args) { + return CustomWidget(); + }, + ), + ], +); +``` + +### Shared Slide Options + +Some shared options can be applied by adding them to a `superdeck.yaml` file in the root of your project. These options will be applied to all slides unless overridden by slide-specific options. + +### Slide Options + +The following options are available for configuring each slide. All options are optional. + +**Shared Options** +Some shared options can be applied by adding them to a `superdeck.yaml` file in the root of your project. These options will be applied to all slides unless overridden by slide-specific options. + +#### `title` + +The title of the slide. + +#### `background` + +The background image that will be displayed on the slide. You can use a URL or a local asset path. + +#### `content` + +The markdown content of the slide. This is where you write the main content of the slide using Markdown syntax. + +- `alignment`: The alignment of the slide content. +- `flex`: The flex value of the slide content. This determines how much space the content occupies relative to other content on the slide. + +#### `style` + +This is the style variants that will be applied to the slide. You can define style variants by using `Mix` and pass them to the `SuperDeck` constructor. + +#### `transition` + +The transition effect to be applied when navigating to the slide. + +- `type`: The type of transition effect. +- `duration`: The duration of the transition effect in milliseconds. +- `delay`: The delay before the transition effect starts in milliseconds. +- `curve`: The curve of the transition effect. + +#### `layout` + +Selects a slide layout template. Available options include: `simple`, `two_column`, `two_column_header`, `image`, and `widget`. + +## Templates + +SuperDeck provides a diverse collection of templates, each designed to meet different presentation needs. From simple text slides to complex layouts with images and columns, you can easily find the right template for your content. + +### Simple Template (default) + +A straightforward template for your presentations. + +```markdown +--- +background: https://source.unsplash.com/random/900×700/?landscape +--- + +# Introduction to SuperDeck + +Create **engaging**, **customizable** presentations within your Flutter app. +``` + +### Two-Column Template + +Ideal for presenting comparative or complementary information side by side. + +```markdown +--- +layout: two_column +--- + +::left:: + +# Product A +- Feature 1 +- Feature 2 + +::right:: + +# Product B +- Feature X +- Feature Y +``` + +#### Sections + +- `::left::`: The content that will be placed in the left column. +- `::right::`: The content that will be placed in the right column. + +If the first tag that is found is `::right::` everything before this tag will be placed on the `::left::` section. + +You can control the content options for each section on the front matter of the slide. + +```markdown +sections: + left: + flex: 2 + alignment: center_left + right: + flex: 1 + alignment: center_right +``` + +Read more about on the [content options](#content) section. + +### Two-Column Header Template + +Similar to the two-column template, but with an additional header section at the top of the slide. + +```markdown +--- +layout: two_column_header +--- + +::header:: + +# Product Comparison + +::left:: + +## Product A +- Feature 1 +- Feature 2 +- Feature 3 + +::right:: + +## Product B +- Feature X +- Feature Y +- Feature Z +``` + +#### Sections + +- `::header::`: The content that will be placed in the header section. +- `::left::`: The content that will be placed in the left column. +- `::right::`: The content that will be placed in the right column. + +If the first tag that is found is `::left::`, everything before this tag will be placed in the `::header::` section. + +You can control the content options for each section in the front matter of the slide. + +```markdown +sections: + header: + flex: 2 + alignment: top_right + left: + flex: 2 + alignment: center_left + right: + flex: 1 + alignment: center_right +``` + +Keep in mind that you can also control the flex of the `left` and `right` sections by using the `content` property. + +Read more about it in the [content options](#content) section. + +### Image Template + +Display an image alongside the slide content. + +```markdown +--- +layout: image +options: + src: https://source.unsplash.com/random/900×700/?nature + fit: cover + position: left +--- + +# Key Features + +- Innovative design +- User-friendly +- Energy-efficient +``` + +The `options` property specifies the image to be displayed. It has the following sub-options: + +- `src`: The URL or path to the image file. +- `fit`: How the image should be fitted within the slide. +- `position`: The position of the image relative to the slide content. + +### Widget Template + +Embed a custom widget within the slide. + +```markdown +--- +layout: widget +options: + name: demo + position: center + flex: 1 + args: + customArg: value + customArg2: value2 +--- + +# Custom Widget + +This slide contains a custom widget. +``` + +The `options` property specifies the widget to be embedded. It has the following sub-options: + +- `name`: The name of the widget. +- `position`: The position of the widget relative to the slide content. +- `flex`: The flex value of the widget. +- `args`: Additional arguments to be passed to the widget. diff --git a/analysis_options.yaml b/packages/superdeck/analysis_options.yaml similarity index 82% rename from analysis_options.yaml rename to packages/superdeck/analysis_options.yaml index b1d3be9f..0e5cf807 100644 --- a/analysis_options.yaml +++ b/packages/superdeck/analysis_options.yaml @@ -5,6 +5,7 @@ include: package:flutter_lints/flutter.yaml analyzer: errors: invalid_annotation_target: ignore + body_might_complete_normally_nullable: ignore plugins: - custom_lint exclude: @@ -15,5 +16,4 @@ linter: public_member_api_docs: false always_use_package_imports: false prefer_relative_imports: true - library_private_types_in_public_api: false - + library_private_types_in_public_api: false \ No newline at end of file diff --git a/build.yaml b/packages/superdeck/build.yaml similarity index 76% rename from build.yaml rename to packages/superdeck/build.yaml index 07b08278..a77b33df 100644 --- a/build.yaml +++ b/packages/superdeck/build.yaml @@ -3,19 +3,19 @@ targets: builders: dart_mappable_builder: generate_for: - - lib/**/*_model.dart + - lib/**/*.dart mix_generator|spec: generate_for: - - lib/**/*_spec.dart + - lib/**/*.dart mix_generator|dto: generate_for: - - lib/**/*_spec.dart + - lib/**/*.dart mix_generator|enum_utility: generate_for: - - lib/**/*_spec.dart + - lib/**/*.dart mix_generator|class_utility: generate_for: - - lib/**/*_spec.dart + - lib/**/*.dart global_options: dart_mappable_builder: diff --git a/coverage/lcov.info b/packages/superdeck/coverage/lcov.info similarity index 71% rename from coverage/lcov.info rename to packages/superdeck/coverage/lcov.info index 4e6a4c34..a3f0f2bc 100644 --- a/coverage/lcov.info +++ b/packages/superdeck/coverage/lcov.info @@ -25,36 +25,133 @@ DA:62,1 LF:23 LH:22 end_of_record -SF:lib/builder/slides_loader.dart -DA:12,0 -DA:14,0 -DA:19,0 -DA:20,0 -DA:21,0 +SF:lib/models/deck_reference_model.dart +DA:15,1 +LF:1 +LH:1 +end_of_record +SF:lib/helpers/utils.dart +DA:7,2 +DA:8,4 +DA:10,2 +DA:12,2 +DA:16,1 +DA:17,1 +DA:18,1 +DA:19,1 +DA:22,0 DA:23,0 DA:24,0 -DA:25,0 -DA:27,0 DA:29,0 -DA:31,0 +DA:30,0 DA:32,0 +DA:33,0 +DA:38,0 DA:39,0 DA:41,0 DA:42,0 DA:43,0 -DA:44,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:56,0 -DA:59,0 -DA:61,0 -DA:64,0 -DA:66,0 -DA:69,0 -LF:27 -LH:0 +DA:47,1 +DA:51,1 +DA:52,1 +DA:54,1 +DA:55,1 +DA:57,2 +DA:58,1 +DA:59,1 +DA:63,2 +DA:64,1 +DA:65,1 +DA:76,0 +DA:77,0 +DA:79,0 +DA:80,0 +DA:82,0 +DA:84,0 +DA:86,0 +DA:87,0 +DA:90,0 +DA:91,0 +DA:93,0 +DA:95,0 +DA:96,0 +DA:109,3 +DA:118,9 +DA:119,6 +DA:121,6 +DA:125,12 +DA:130,3 +DA:134,6 +DA:135,9 +DA:136,9 +LF:53 +LH:28 +end_of_record +SF:lib/helpers/syntax_highlighter.dart +DA:6,0 +DA:8,0 +DA:10,0 +DA:16,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:28,0 +DA:29,0 +DA:30,0 +DA:32,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:42,1 +DA:44,1 +DA:45,1 +DA:48,1 +DA:51,1 +DA:57,1 +DA:60,2 +DA:61,1 +DA:62,1 +DA:66,1 +DA:68,1 +DA:69,3 +DA:70,3 +DA:73,2 +DA:74,1 +DA:78,2 +DA:83,1 +DA:84,1 +LF:32 +LH:18 +end_of_record +SF:lib/helpers/extensions.dart +DA:4,1 +DA:5,1 +DA:6,1 +DA:9,1 +DA:12,1 +DA:13,1 +DA:14,1 +DA:20,1 +DA:21,1 +DA:22,1 +LF:10 +LH:10 +end_of_record +SF:lib/helpers/deep_merge.dart +DA:1,1 +DA:3,1 +DA:5,1 +DA:6,2 +DA:7,1 +DA:8,2 +DA:9,1 +DA:10,2 +DA:12,1 +DA:15,1 +DA:20,1 +DA:21,1 +LF:12 +LH:12 end_of_record SF:lib/builder/slide_parser.dart DA:9,0 @@ -89,20 +186,20 @@ DA:76,0 LF:29 LH:0 end_of_record -SF:lib/helpers/config.dart -DA:18,3 +SF:lib/models/config_model.dart +DA:18,8 DA:24,0 DA:25,0 DA:26,0 DA:27,0 DA:28,0 -DA:38,3 -DA:45,3 +DA:38,7 +DA:45,5 DA:46,0 -DA:55,0 -DA:57,0 -DA:58,0 -DA:59,0 +DA:55,3 +DA:57,1 +DA:58,1 +DA:59,1 DA:62,0 DA:63,0 DA:64,0 @@ -112,88 +209,13 @@ DA:70,0 DA:71,0 DA:72,0 LF:21 -LH:3 -end_of_record -SF:lib/helpers/deep_merge.dart -DA:1,0 -DA:3,0 -DA:5,0 -DA:6,0 -DA:7,0 -DA:8,0 -DA:9,0 -DA:10,0 -DA:12,0 -DA:15,0 -DA:20,0 -DA:21,0 -LF:12 -LH:0 -end_of_record -SF:lib/helpers/utils.dart -DA:7,1 -DA:8,2 -DA:10,1 -DA:12,1 -DA:16,1 -DA:17,1 -DA:18,1 -DA:19,1 -DA:22,0 -DA:23,0 -DA:24,0 -DA:29,0 -DA:30,0 -DA:32,0 -DA:33,0 -DA:38,0 -DA:39,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:47,1 -DA:51,1 -DA:52,1 -DA:54,1 -DA:55,1 -DA:57,2 -DA:58,1 -DA:59,1 -DA:63,2 -DA:64,1 -DA:65,1 -DA:76,0 -DA:77,0 -DA:79,0 -DA:80,0 -DA:82,0 -DA:84,0 -DA:86,0 -DA:88,0 -DA:89,0 -DA:92,0 -DA:93,0 -DA:95,0 -DA:97,0 -DA:98,0 -DA:111,1 -DA:120,3 -DA:121,2 -DA:123,2 -DA:127,4 -DA:132,1 -DA:136,2 -DA:137,3 -DA:138,3 -LF:54 -LH:28 +LH:7 end_of_record SF:lib/models/slide_model.dart -DA:22,0 -DA:31,0 -DA:33,0 +DA:23,2 +DA:32,2 DA:34,0 -DA:38,0 +DA:35,0 DA:39,0 DA:40,0 DA:41,0 @@ -209,177 +231,178 @@ DA:50,0 DA:51,0 DA:52,0 DA:53,0 -DA:55,0 -DA:57,0 +DA:54,0 +DA:56,0 DA:58,0 DA:59,0 DA:60,0 -DA:62,0 -DA:66,0 +DA:61,0 +DA:63,0 DA:67,0 -DA:74,0 +DA:68,0 DA:75,0 DA:76,0 DA:77,0 DA:78,0 DA:79,0 DA:80,0 -DA:87,0 -DA:95,0 -DA:101,0 -DA:109,0 -DA:124,0 -DA:133,0 -DA:139,0 +DA:81,0 +DA:88,2 +DA:96,2 +DA:102,0 +DA:110,1 +DA:125,1 +DA:134,1 DA:140,0 DA:141,0 -DA:148,0 -DA:157,0 -DA:163,0 +DA:142,0 +DA:149,1 +DA:158,1 DA:164,0 DA:165,0 -DA:178,0 -DA:192,0 +DA:166,0 +DA:179,1 DA:193,0 -DA:195,0 -DA:198,0 +DA:194,0 +DA:196,0 DA:199,0 -DA:201,0 -DA:205,0 -DA:214,0 -DA:223,0 -DA:225,0 -DA:227,0 -DA:233,0 +DA:200,0 +DA:202,0 +DA:206,0 +DA:215,1 +DA:224,1 +DA:226,0 +DA:228,0 DA:234,0 DA:235,0 DA:236,0 -DA:244,0 -DA:253,0 -DA:255,0 -DA:257,0 -DA:259,0 -DA:265,0 +DA:237,0 +DA:245,1 +DA:254,1 +DA:256,0 +DA:258,0 +DA:260,0 DA:266,0 DA:267,0 DA:268,0 -DA:276,0 -DA:284,0 -DA:286,0 +DA:269,0 +DA:277,0 +DA:285,0 DA:287,0 -DA:298,0 +DA:288,0 DA:299,0 -DA:301,0 +DA:300,0 DA:302,0 -DA:305,0 -DA:309,0 +DA:303,0 +DA:306,0 DA:310,0 DA:311,0 -DA:316,0 +DA:312,0 DA:317,0 -DA:326,0 -DA:328,0 -DA:331,0 +DA:318,0 +DA:327,0 +DA:329,0 DA:332,0 +DA:333,0 LF:92 -LH:0 +LH:14 end_of_record -SF:lib/services/project_service.dart -DA:23,0 -DA:25,0 -DA:27,0 -DA:29,0 -DA:31,0 +SF:lib/builder/slides_loader.dart +DA:13,0 +DA:15,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:24,0 +DA:25,0 +DA:26,0 +DA:28,0 +DA:30,0 DA:32,0 DA:33,0 +DA:40,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:50,0 +DA:51,0 +DA:52,0 +DA:54,0 +DA:59,0 +DA:64,0 +DA:67,0 +DA:69,0 +DA:72,0 +LF:26 +LH:0 +end_of_record +SF:lib/services/project_service.dart +DA:18,1 +DA:20,3 +DA:22,0 +DA:24,3 +DA:26,1 +DA:27,4 +DA:28,0 +DA:30,0 +DA:34,0 DA:35,0 DA:36,0 DA:37,0 DA:39,0 DA:40,0 -DA:41,0 -DA:42,0 -DA:44,0 -DA:45,0 -DA:48,0 -DA:49,0 +DA:51,0 DA:52,0 DA:53,0 +DA:56,1 +DA:57,2 +DA:58,0 +DA:60,2 +DA:61,4 DA:64,0 -DA:65,0 DA:66,0 -DA:69,0 -DA:70,0 -DA:71,0 +DA:68,0 +DA:72,0 DA:73,0 -DA:74,0 +DA:75,0 DA:77,0 +DA:78,0 +DA:79,0 DA:80,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:87,0 -DA:93,0 +DA:86,0 +DA:88,0 +DA:89,0 +DA:90,0 DA:95,0 -DA:97,0 -DA:101,0 +DA:96,0 +DA:99,0 +DA:100,0 +DA:102,0 DA:103,0 DA:104,0 -DA:105,0 -DA:109,0 -DA:111,0 DA:112,0 -DA:113,0 DA:114,0 DA:115,0 +DA:117,0 +DA:118,0 DA:119,0 -DA:121,0 -DA:122,0 -DA:123,0 -DA:124,0 +DA:120,0 +DA:125,0 +DA:128,0 DA:129,0 -DA:131,0 +DA:130,0 DA:132,0 -DA:133,0 -DA:134,0 -DA:135,0 -DA:139,0 +DA:138,0 DA:141,0 -DA:142,0 -DA:143,0 +DA:144,0 +DA:145,0 +DA:146,0 +DA:147,0 DA:148,0 DA:149,0 -DA:152,0 -DA:153,0 -DA:154,0 -DA:157,0 -DA:158,0 -DA:159,0 -DA:160,0 -DA:163,0 -DA:164,0 -DA:166,0 -DA:167,0 -DA:168,0 -DA:175,0 -DA:177,0 -DA:178,0 -DA:180,0 -DA:181,0 -DA:182,0 -DA:183,0 -DA:194,0 -DA:196,0 -DA:197,0 -DA:199,0 -DA:202,0 -DA:203,0 -DA:204,0 -DA:205,0 -DA:206,0 -DA:207,0 -LF:93 -LH:0 +LF:63 +LH:9 end_of_record SF:lib/builder/slides_pipeline.dart DA:12,0 @@ -404,13 +427,13 @@ DA:75,0 DA:78,0 DA:81,0 DA:82,0 -DA:90,3 -DA:98,3 +DA:90,5 +DA:98,5 DA:100,0 DA:102,0 DA:104,0 DA:105,0 -DA:113,3 +DA:113,5 DA:115,0 DA:117,0 DA:119,0 @@ -474,7 +497,7 @@ LF:88 LH:3 end_of_record SF:lib/services/mermaid_service.dart -DA:10,3 +DA:10,5 DA:14,0 DA:15,0 DA:17,0 @@ -502,37 +525,36 @@ LF:24 LH:1 end_of_record SF:lib/components/atoms/cache_image_widget.dart -DA:20,0 -DA:29,0 -DA:31,0 +DA:21,0 +DA:30,0 DA:32,0 -DA:34,0 +DA:33,0 DA:35,0 -DA:37,0 +DA:36,0 DA:38,0 DA:39,0 -DA:45,0 -DA:52,0 +DA:40,0 +DA:46,0 DA:53,0 -DA:55,0 -DA:59,0 -DA:63,0 -DA:65,0 -DA:72,0 -DA:77,0 +DA:54,0 +DA:56,0 +DA:60,0 +DA:64,0 +DA:66,0 +DA:73,0 DA:82,0 DA:84,0 DA:85,0 -DA:87,0 -DA:90,0 -DA:93,0 +DA:88,0 +DA:91,0 DA:94,0 -DA:97,0 +DA:95,0 DA:98,0 -DA:100,0 -DA:104,0 -DA:106,0 -LF:30 +DA:99,0 +DA:101,0 +DA:105,0 +DA:107,0 +LF:29 LH:0 end_of_record SF:lib/components/atoms/linear_progresss_indicator_widget.dart @@ -568,56 +590,67 @@ DA:65,0 LF:29 LH:0 end_of_record -SF:lib/components/atoms/markdown_viewer.dart -DA:16,0 +SF:lib/components/atoms/loading_indicator.dart +DA:7,0 +DA:13,0 +DA:14,0 +DA:22,0 DA:24,0 -DA:26,0 +DA:25,0 +DA:29,0 +DA:30,0 DA:33,0 DA:35,0 DA:36,0 DA:37,0 DA:38,0 +DA:40,0 +DA:41,0 DA:42,0 -DA:44,0 -DA:45,0 +DA:47,0 +DA:49,0 +DA:50,0 +DA:53,0 DA:55,0 +DA:56,0 DA:57,0 DA:58,0 DA:59,0 +DA:60,0 DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:69,0 -DA:81,0 -DA:87,0 -DA:88,0 -DA:90,0 +DA:74,5 +DA:77,0 +DA:78,0 +DA:92,0 DA:94,0 +DA:95,0 DA:98,0 DA:99,0 DA:100,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 +DA:101,0 DA:107,0 -DA:108,0 DA:109,0 DA:110,0 -DA:111,0 -DA:112,0 +DA:113,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:118,0 DA:120,0 +DA:121,0 +DA:122,0 +DA:124,0 +DA:125,0 DA:126,0 +DA:128,0 +DA:129,0 +DA:130,0 DA:131,0 -DA:138,0 -DA:139,0 -DA:140,0 -DA:142,0 -DA:143,0 +DA:132,0 +DA:144,0 DA:146,0 DA:148,0 +DA:149,0 DA:150,0 DA:151,0 DA:152,0 @@ -630,87 +663,183 @@ DA:158,0 DA:159,0 DA:160,0 DA:161,0 -LF:61 -LH:0 -end_of_record -SF:lib/helpers/measure_size.dart -DA:12,0 -DA:14,0 -DA:16,0 -DA:18,0 -DA:19,0 -DA:21,0 -DA:22,0 -DA:23,0 -DA:31,0 -DA:37,0 -DA:39,0 -DA:42,0 -DA:45,0 -DA:52,0 -DA:57,0 -DA:59,0 -DA:61,0 -DA:64,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:72,0 -DA:74,0 -DA:80,0 -DA:81,0 -DA:82,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:166,0 +DA:167,0 +DA:168,0 +DA:169,0 +DA:170,0 +DA:172,0 +DA:173,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:178,0 +DA:179,0 +DA:181,0 +DA:182,0 +DA:183,0 +DA:184,0 +DA:185,0 +DA:186,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:191,0 +DA:192,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:199,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:210,0 +DA:212,0 +LF:115 +LH:1 +end_of_record +SF:lib/components/atoms/markdown_viewer.dart +DA:16,0 +DA:24,0 +DA:26,0 +DA:33,0 +DA:35,0 +DA:36,0 +DA:37,0 +DA:38,0 +DA:42,0 +DA:44,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:48,0 +DA:49,0 +DA:50,0 +DA:51,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:59,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:63,0 +DA:66,0 +DA:69,0 +DA:74,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:83,0 +DA:84,0 +DA:85,0 DA:86,0 DA:88,0 DA:89,0 +DA:90,0 +DA:91,0 +DA:92,0 DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:99,0 -DA:100,0 DA:101,0 -DA:103,0 -DA:104,0 -DA:105,0 -LF:39 +DA:107,0 +DA:112,0 +DA:119,0 +DA:120,0 +DA:121,0 +DA:123,0 +DA:124,0 +DA:127,0 +DA:129,0 +DA:131,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:139,0 +DA:140,0 +DA:141,0 +DA:142,0 +DA:154,0 +DA:155,0 +DA:157,0 +DA:159,0 +DA:160,0 +DA:166,0 +DA:191,0 +DA:192,0 +DA:196,0 +DA:197,0 +DA:198,0 +DA:200,0 +DA:201,0 +DA:202,0 +DA:203,0 +DA:204,0 +DA:205,0 +DA:206,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:210,0 +LF:85 LH:0 end_of_record -SF:lib/helpers/syntax_highlighter.dart -DA:6,0 -DA:8,0 -DA:10,0 -DA:16,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:28,0 -DA:29,0 -DA:30,0 -DA:32,0 -DA:36,0 -DA:37,0 -DA:38,0 +SF:lib/helpers/measure_size.dart +DA:12,1 +DA:14,1 +DA:16,1 +DA:18,2 +DA:19,2 +DA:21,1 +DA:22,3 +DA:23,2 +DA:31,1 +DA:37,1 +DA:39,2 DA:42,1 -DA:44,1 -DA:45,1 -DA:48,1 -DA:51,1 +DA:45,2 +DA:52,1 DA:57,1 -DA:60,2 +DA:59,1 DA:61,1 -DA:62,1 -DA:66,1 -DA:68,1 -DA:69,3 -DA:70,3 -DA:73,2 -DA:74,1 -DA:78,2 -DA:83,1 -DA:84,1 -LF:32 -LH:18 +DA:64,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:72,1 +DA:73,1 +DA:79,1 +DA:80,2 +DA:81,1 +DA:85,1 +DA:87,1 +DA:88,1 +DA:92,3 +DA:94,1 +DA:95,1 +DA:96,1 +DA:97,1 +DA:98,1 +DA:99,1 +DA:101,1 +DA:102,1 +DA:103,3 +LF:39 +LH:35 end_of_record SF:lib/providers/slide_provider.dart DA:26,0 @@ -742,319 +871,79 @@ DA:92,0 LF:26 LH:0 end_of_record -SF:lib/styles/style_spec.dart -DA:14,0 +SF:lib/components/atoms/slide_thumbnail.dart +DA:15,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 DA:20,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:29,0 -DA:33,0 +DA:21,0 +DA:31,0 DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:46,0 DA:47,0 -DA:54,0 -DA:59,0 -DA:64,0 +DA:48,0 +DA:61,0 +DA:63,0 DA:65,0 -DA:66,0 +DA:68,0 DA:70,0 -DA:75,0 -DA:76,0 +DA:71,0 +DA:72,0 +DA:73,0 DA:77,0 +DA:78,0 DA:81,0 DA:82,0 +DA:83,0 +DA:86,0 +DA:89,0 DA:92,0 +DA:94,0 +DA:97,0 +DA:99,0 DA:100,0 +DA:102,0 +DA:104,0 DA:105,0 DA:106,0 DA:107,0 DA:108,0 -DA:110,0 -DA:111,0 +DA:109,0 DA:112,0 DA:116,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:128,0 +DA:121,0 +DA:129,0 DA:130,0 -DA:134,0 +DA:131,0 +DA:132,0 +DA:133,0 DA:135,0 DA:136,0 -DA:137,0 DA:138,0 DA:139,0 -DA:140,0 -DA:150,0 +DA:141,0 +DA:145,0 +DA:146,0 +DA:154,0 DA:157,0 +DA:160,0 +DA:161,0 DA:162,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:167,0 -DA:168,0 -DA:169,0 -DA:173,0 -DA:180,0 -DA:181,0 -DA:182,0 -DA:183,0 -DA:185,0 -DA:189,0 -DA:190,0 -DA:191,0 -DA:192,0 -DA:193,0 -DA:194,0 -DA:208,0 -DA:219,0 -DA:224,0 -DA:225,0 -DA:226,0 -DA:227,0 -DA:228,0 -DA:230,0 -DA:232,0 -DA:234,0 -DA:235,0 -DA:239,0 -DA:250,0 -DA:251,0 -DA:252,0 -DA:253,0 -DA:254,0 -DA:255,0 -DA:257,0 -DA:258,0 -DA:259,0 -DA:263,0 -DA:264,0 -DA:265,0 -DA:266,0 -DA:267,0 -DA:268,0 -DA:269,0 -DA:270,0 -DA:271,0 -DA:272,0 -DA:284,0 -DA:292,0 -DA:297,0 -DA:298,0 -DA:300,0 -DA:301,0 -DA:302,0 -DA:303,0 -DA:304,0 -DA:308,0 -DA:316,0 -DA:317,0 -DA:318,0 -DA:319,0 -DA:320,0 -DA:321,0 -DA:325,0 -DA:326,0 -DA:327,0 -DA:328,0 -DA:329,0 -DA:330,0 -DA:331,0 -DA:355,0 -DA:379,6 -DA:399,0 -DA:400,0 -DA:401,0 -DA:404,0 -DA:405,0 -DA:408,0 -DA:410,0 -DA:412,0 -DA:414,0 -DA:416,0 -DA:423,0 -DA:424,0 -DA:425,0 -DA:426,0 -DA:427,0 -DA:428,0 -DA:429,0 -DA:430,0 -DA:431,0 -DA:432,0 -DA:433,0 -DA:434,0 -DA:435,0 -DA:436,0 -DA:437,0 -DA:438,0 -DA:439,0 -DA:440,0 -DA:441,0 -DA:445,0 -DA:446,0 -DA:447,0 -DA:448,0 -DA:449,0 -DA:450,0 -DA:451,0 -DA:452,0 -DA:453,0 -DA:454,0 -DA:455,0 -DA:456,0 -DA:457,0 -DA:458,0 -DA:459,0 -DA:460,0 -DA:461,0 -DA:462,0 -DA:463,0 -DA:464,0 -DA:465,0 -DA:466,0 -DA:467,0 -DA:469,0 -DA:470,0 -DA:471,0 -DA:472,0 -DA:473,0 -DA:474,0 -DA:475,0 -DA:476,0 -DA:477,0 -DA:478,0 -DA:479,0 -DA:480,0 -DA:481,0 -DA:482,0 -DA:483,0 -DA:484,0 -DA:485,0 -DA:486,0 -DA:487,0 -DA:488,0 -DA:492,0 -DA:514,0 -DA:515,0 -DA:516,0 -DA:517,0 -DA:518,0 -DA:519,0 -DA:520,0 -DA:521,0 -DA:522,0 -DA:523,0 -DA:524,0 -DA:525,0 -DA:526,0 -DA:527,0 -DA:528,0 -DA:529,0 -DA:530,0 -DA:531,0 -DA:532,0 -DA:536,0 -DA:537,0 -DA:538,0 -DA:539,0 -DA:540,0 -DA:541,0 -DA:542,0 -DA:543,0 -DA:544,0 -DA:545,0 -DA:546,0 -DA:547,0 -DA:548,0 -DA:549,0 -DA:550,0 -DA:551,0 -DA:552,0 -DA:553,0 -DA:554,0 -DA:555,0 -DA:560,0 -DA:561,0 -LF:238 -LH:1 -end_of_record -SF:lib/components/atoms/slide_thumbnail.dart -DA:14,0 -DA:15,0 -DA:16,0 -DA:17,0 -DA:18,0 -DA:19,0 -DA:20,0 -DA:30,0 -DA:38,0 -DA:46,0 -DA:47,0 -DA:60,0 -DA:62,0 -DA:64,0 -DA:67,0 -DA:69,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:76,0 -DA:77,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:85,0 -DA:88,0 -DA:91,0 -DA:93,0 -DA:96,0 -DA:98,0 -DA:99,0 -DA:101,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:106,0 -DA:109,0 -DA:113,0 -DA:118,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:128,0 -DA:129,0 -DA:131,0 -DA:132,0 -DA:134,0 -DA:135,0 -DA:137,0 -DA:141,0 -DA:142,0 -DA:150,0 -DA:153,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:182,0 -DA:190,0 -DA:192,0 +DA:186,0 DA:194,0 -DA:195,0 DA:196,0 -DA:197,0 DA:198,0 +DA:199,0 DA:200,0 DA:201,0 -DA:203,0 +DA:202,0 DA:204,0 DA:205,0 -LF:69 +DA:207,0 +DA:208,0 +DA:209,0 +LF:71 LH:0 end_of_record SF:lib/services/snapshot_service.dart @@ -1095,30 +984,33 @@ DA:106,0 DA:111,0 DA:113,0 DA:117,0 -DA:123,0 -DA:124,0 -DA:129,0 -DA:130,0 -DA:131,0 -DA:136,0 -DA:137,0 +DA:118,0 +DA:119,0 +DA:120,0 +DA:126,0 +DA:127,0 +DA:132,0 +DA:133,0 +DA:134,0 DA:139,0 -DA:141,0 -DA:145,0 -DA:147,0 -DA:149,0 +DA:140,0 +DA:142,0 +DA:144,0 +DA:148,0 DA:150,0 -DA:151,0 DA:152,0 DA:153,0 +DA:154,0 DA:155,0 +DA:156,0 DA:158,0 -DA:162,0 -DA:164,0 +DA:161,0 +DA:165,0 DA:167,0 -DA:169,0 -DA:173,0 -LF:60 +DA:170,0 +DA:172,0 +DA:176,0 +LF:63 LH:0 end_of_record SF:lib/components/molecules/scaled_app.dart @@ -1166,27 +1058,28 @@ DA:30,0 DA:31,0 DA:33,0 DA:35,0 +DA:36,0 DA:37,0 -DA:38,0 -DA:39,0 +DA:44,0 DA:46,0 +DA:47,0 DA:48,0 DA:49,0 -DA:50,0 DA:51,0 DA:52,0 DA:53,0 +DA:54,0 DA:55,0 -DA:56,0 DA:57,0 DA:58,0 DA:59,0 +DA:60,0 DA:61,0 -DA:62,0 DA:63,0 -DA:64,0 -DA:65,0 +DA:66,0 DA:67,0 +DA:68,0 +DA:69,0 DA:70,0 DA:71,0 DA:72,0 @@ -1200,144 +1093,141 @@ DA:79,0 DA:80,0 DA:81,0 DA:82,0 -DA:83,0 DA:84,0 -DA:85,0 -DA:86,0 -DA:88,0 -DA:108,0 -DA:116,0 -DA:118,0 -DA:120,0 -DA:122,0 -DA:125,0 -DA:127,0 -LF:54 -LH:0 +DA:103,1 +DA:111,0 +DA:113,0 +DA:115,0 +DA:117,0 +DA:120,1 +DA:122,3 +LF:51 +LH:3 end_of_record SF:lib/helpers/layout_builder.dart -DA:14,0 DA:16,0 -DA:17,0 DA:18,0 DA:19,0 -DA:23,0 +DA:20,0 +DA:21,0 DA:25,0 -DA:28,0 -DA:29,0 +DA:27,0 DA:30,0 DA:31,0 -DA:37,0 +DA:32,0 +DA:33,0 DA:39,0 -DA:40,0 -DA:44,0 +DA:41,0 +DA:42,0 DA:46,0 -DA:50,0 -DA:51,0 +DA:48,0 DA:52,0 -DA:53,0 DA:54,0 DA:55,0 DA:56,0 DA:57,0 DA:58,0 DA:59,0 +DA:60,0 +DA:61,0 DA:62,0 -DA:64,0 +DA:63,0 DA:66,0 -DA:67,0 -DA:69,0 +DA:68,0 +DA:70,0 DA:71,0 -DA:78,0 -DA:80,0 -DA:81,0 +DA:73,0 +DA:75,0 DA:82,0 DA:84,0 DA:85,0 DA:86,0 -DA:87,0 +DA:88,0 DA:89,0 -DA:92,0 +DA:90,0 +DA:91,0 DA:93,0 +DA:96,0 DA:97,0 -DA:100,0 -DA:102,0 -DA:108,0 -DA:110,0 +DA:101,0 +DA:104,0 +DA:106,0 DA:112,0 DA:114,0 DA:116,0 DA:118,0 -DA:119,0 DA:120,0 -DA:121,0 DA:122,0 DA:123,0 DA:124,0 DA:125,0 +DA:126,0 DA:127,0 DA:128,0 -DA:135,0 -DA:140,0 -DA:142,0 +DA:129,0 +DA:131,0 +DA:132,0 +DA:139,0 DA:144,0 DA:146,0 -DA:147,0 -DA:153,0 -DA:154,0 -DA:162,0 -DA:163,0 -DA:164,0 +DA:148,0 +DA:150,0 +DA:151,0 +DA:156,0 +DA:157,0 +DA:165,0 DA:166,0 DA:167,0 +DA:169,0 DA:170,0 -DA:171,0 -DA:172,0 DA:173,0 DA:174,0 DA:175,0 DA:176,0 +DA:177,0 +DA:178,0 DA:179,0 -DA:184,0 -DA:185,0 -DA:186,0 -DA:192,0 -DA:197,0 -DA:199,0 -DA:201,0 +DA:182,0 +DA:187,0 +DA:188,0 +DA:189,0 +DA:195,0 +DA:200,0 DA:202,0 DA:204,0 DA:205,0 -DA:206,0 DA:207,0 DA:208,0 DA:209,0 -DA:217,0 -DA:219,0 -DA:221,0 +DA:210,0 +DA:211,0 +DA:212,0 +DA:220,0 DA:222,0 -DA:223,0 +DA:224,0 DA:225,0 -DA:227,0 +DA:226,0 DA:228,0 -DA:229,0 DA:230,0 DA:231,0 DA:232,0 DA:233,0 DA:234,0 DA:235,0 +DA:236,0 DA:237,0 -DA:242,0 -DA:244,0 +DA:238,0 +DA:240,0 DA:245,0 -DA:246,0 DA:247,0 DA:248,0 +DA:249,0 DA:250,0 -DA:252,0 -DA:254,0 -LF:121 +DA:251,0 +DA:253,0 +DA:255,0 +DA:257,0 +LF:122 LH:0 end_of_record SF:lib/components/atoms/transition_widget.dart @@ -1419,107 +1309,110 @@ LF:74 LH:0 end_of_record SF:lib/models/options_model.dart -DA:18,6 -DA:23,0 -DA:25,0 +DA:18,14 +DA:23,1 +DA:25,2 DA:28,0 DA:29,0 DA:30,0 DA:31,0 -DA:41,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -DA:59,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:79,0 -DA:86,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:103,0 -DA:112,0 -DA:114,0 -DA:116,0 -DA:119,0 -DA:120,0 -DA:121,0 -DA:122,0 -DA:123,0 -DA:124,0 -DA:131,0 -DA:139,0 -DA:151,0 +DA:41,2 +DA:47,2 +DA:49,2 +DA:51,0 +DA:52,0 +DA:53,0 +DA:54,0 +DA:64,2 +DA:71,0 +DA:72,0 +DA:73,0 +DA:74,0 +DA:84,2 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:108,3 +DA:117,1 +DA:118,3 +DA:120,1 +DA:122,2 +DA:125,0 +DA:126,0 +DA:127,0 +DA:128,0 +DA:129,0 +DA:130,0 +DA:137,0 +DA:145,0 DA:157,0 -DA:162,0 -DA:165,0 -DA:166,0 +DA:163,0 DA:168,0 +DA:171,0 +DA:172,0 DA:174,0 -DA:179,0 -DA:186,0 -DA:188,0 -DA:193,0 +DA:180,0 +DA:185,0 +DA:192,0 +DA:194,0 DA:199,0 -DA:200,0 -DA:203,0 -DA:207,0 +DA:205,0 +DA:206,0 DA:209,0 -DA:210,0 -DA:211,0 DA:213,0 +DA:215,0 +DA:216,0 DA:217,0 -DA:218,0 DA:219,0 -DA:220,0 -DA:221,0 -DA:222,0 DA:223,0 +DA:224,0 DA:225,0 DA:226,0 -DA:233,0 -DA:249,0 -DA:250,0 -DA:253,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:231,0 +DA:232,0 +DA:239,0 DA:255,0 DA:256,0 -DA:257,0 -DA:258,0 DA:259,0 -DA:260,0 DA:261,0 +DA:262,0 +DA:263,0 +DA:264,0 +DA:265,0 +DA:266,0 DA:267,0 -DA:342,0 -DA:343,0 -DA:365,0 -DA:366,0 -DA:377,0 -DA:378,0 -DA:381,0 +DA:273,0 +DA:348,0 +DA:349,0 +DA:371,0 +DA:372,0 DA:383,0 DA:384,0 -DA:385,0 -DA:386,0 +DA:387,0 +DA:389,0 DA:390,0 DA:391,0 -DA:407,0 -DA:408,0 -DA:411,0 +DA:392,0 +DA:396,0 +DA:397,0 DA:413,0 DA:414,0 -DA:415,0 -DA:416,0 DA:417,0 -DA:418,0 DA:419,0 DA:420,0 DA:421,0 -LF:99 -LH:1 +DA:422,0 +DA:423,0 +DA:424,0 +DA:425,0 +DA:426,0 +DA:427,0 +LF:102 +LH:13 end_of_record SF:lib/components/molecules/code_preview.dart DA:4,0 @@ -1549,1379 +1442,1615 @@ DA:23,0 DA:25,0 DA:27,0 DA:28,0 -DA:29,0 -DA:36,0 -DA:40,0 -DA:42,0 -DA:48,0 -DA:50,0 -DA:51,0 -LF:14 -LH:0 -end_of_record -SF:lib/components/molecules/slide_preview.dart -DA:12,0 -DA:19,0 -DA:21,0 -DA:22,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:29,0 -DA:30,0 -DA:31,0 -DA:37,0 +DA:34,0 DA:38,0 +DA:40,0 DA:46,0 -DA:53,0 -DA:55,0 -DA:56,0 -DA:57,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:61,0 -DA:64,0 -DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 -LF:27 -LH:0 -end_of_record -SF:lib/components/molecules/split_view.dart -DA:9,0 -DA:10,0 -DA:11,0 -DA:12,0 -DA:13,0 -DA:14,0 -DA:18,0 -DA:23,0 -DA:28,0 -DA:30,0 -DA:41,0 -DA:43,0 -DA:44,0 DA:48,0 DA:49,0 -DA:50,0 -DA:55,0 -DA:56,0 -DA:60,0 -DA:62,0 -DA:63,0 -DA:66,0 -DA:68,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:74,0 -DA:78,0 -DA:80,0 -DA:81,0 -DA:83,0 -DA:85,0 -DA:88,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:102,0 -DA:104,0 -DA:110,0 -DA:112,0 -DA:117,0 -DA:119,0 -DA:128,0 -DA:134,0 -DA:137,0 -DA:138,0 -DA:140,0 -DA:144,0 -DA:145,0 -DA:165,0 -DA:173,0 -DA:175,0 -DA:176,0 -DA:177,0 -DA:180,0 -DA:183,0 -DA:184,0 -DA:187,0 -DA:188,0 -DA:191,0 -DA:192,0 -DA:198,0 -DA:199,0 -DA:202,0 -DA:203,0 -DA:206,0 -DA:207,0 -DA:210,0 -DA:211,0 -LF:73 +LF:13 LH:0 end_of_record -SF:lib/components/molecules/slide_thumbnail_list.dart +SF:lib/styles/style_spec.dart +DA:22,5 +DA:35,5 +DA:48,5 +DA:62,5 +DA:84,5 +DA:105,5 +DA:121,5 +DA:182,10 +DA:215,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:219,0 +DA:220,0 +DA:221,0 +DA:222,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:228,0 +DA:229,0 +DA:230,0 +DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:236,0 +DA:237,0 +DA:238,0 +DA:239,0 +DA:240,0 +DA:241,0 +DA:242,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:246,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:252,0 +DA:253,0 +DA:254,0 +DA:255,0 +DA:256,0 +DA:258,0 +DA:259,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:263,0 +DA:269,0 +DA:270,0 +DA:275,0 +DA:276,0 +DA:277,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:282,0 +DA:283,0 +DA:284,0 +DA:285,0 +DA:286,0 +DA:287,0 +DA:288,0 +DA:293,0 +DA:294,0 +DA:295,0 +LF:75 +LH:8 +end_of_record +SF:lib/styles/style_spec.g.dart +DA:10,0 DA:11,0 -DA:24,0 -DA:25,0 +DA:28,0 +DA:29,0 DA:34,0 -DA:35,0 DA:40,0 -DA:45,0 -DA:49,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:56,0 -DA:59,0 -DA:66,0 +DA:41,0 +DA:42,0 +DA:43,0 +DA:67,0 DA:69,0 DA:71,0 DA:72,0 DA:73,0 -DA:77,0 -DA:79,0 -DA:81,0 +DA:74,0 DA:82,0 DA:83,0 DA:84,0 DA:85,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:91,0 -DA:93,0 -DA:94,0 -LF:31 -LH:0 -end_of_record -SF:lib/providers/controller.dart -DA:14,0 -DA:17,0 -DA:19,0 -DA:48,0 -DA:53,0 -DA:54,0 -DA:55,0 -DA:59,0 -DA:63,0 -DA:64,0 -DA:65,0 -DA:68,0 -DA:69,0 -DA:70,0 -DA:71,0 -DA:72,0 -DA:73,0 -DA:74,0 -DA:80,0 -DA:81,0 -DA:84,0 DA:86,0 -DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:97,0 -DA:98,0 -DA:99,0 -DA:100,0 -DA:101,0 -DA:106,0 -DA:107,0 -DA:108,0 -DA:109,0 -DA:111,0 -DA:115,0 -DA:116,0 -DA:119,0 +DA:89,0 +DA:104,0 +DA:118,0 DA:120,0 +DA:121,0 +DA:122,0 DA:123,0 -DA:124,0 -DA:127,0 -DA:128,0 -DA:131,0 -DA:132,0 DA:135,0 -DA:136,0 -DA:137,0 +DA:139,0 DA:140,0 DA:141,0 DA:142,0 -DA:143,0 -DA:148,0 -DA:149,0 DA:150,0 -DA:155,0 -DA:156,0 -DA:157,0 -LF:59 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:173,0 +DA:175,0 +DA:178,0 +DA:184,0 +DA:197,0 +DA:202,0 +DA:204,0 +DA:208,0 +DA:209,0 +DA:212,0 +DA:217,0 +DA:218,0 +DA:235,0 +DA:236,0 +DA:241,0 +DA:247,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:274,0 +DA:276,0 +DA:278,0 +DA:279,0 +DA:280,0 +DA:281,0 +DA:289,0 +DA:290,0 +DA:291,0 +DA:292,0 +DA:293,0 +DA:296,0 +DA:311,0 +DA:325,0 +DA:327,0 +DA:328,0 +DA:329,0 +DA:330,0 +DA:342,0 +DA:346,0 +DA:347,0 +DA:348,0 +DA:349,0 +DA:357,0 +DA:358,0 +DA:359,0 +DA:360,0 +DA:361,0 +DA:380,0 +DA:382,0 +DA:385,0 +DA:391,0 +DA:404,0 +DA:409,0 +DA:411,0 +DA:415,0 +DA:416,0 +DA:419,0 +DA:424,0 +DA:425,0 +DA:442,0 +DA:443,0 +DA:448,0 +DA:453,0 +DA:454,0 +DA:455,0 +DA:479,0 +DA:481,0 +DA:483,0 +DA:484,0 +DA:485,0 +DA:493,0 +DA:494,0 +DA:495,0 +DA:496,0 +DA:499,0 +DA:513,0 +DA:526,0 +DA:528,0 +DA:529,0 +DA:530,0 +DA:542,0 +DA:546,0 +DA:547,0 +DA:548,0 +DA:556,0 +DA:557,0 +DA:558,0 +DA:559,0 +DA:575,0 +DA:577,0 +DA:580,0 +DA:585,0 +DA:597,0 +DA:602,0 +DA:604,0 +DA:608,0 +DA:609,0 +DA:612,0 +DA:617,0 +DA:618,0 +DA:635,0 +DA:636,0 +DA:641,0 +DA:649,0 +DA:650,0 +DA:651,0 +DA:652,0 +DA:653,0 +DA:654,0 +DA:679,0 +DA:681,0 +DA:683,0 +DA:684,0 +DA:686,0 +DA:688,0 +DA:690,0 +DA:692,0 +DA:700,0 +DA:701,0 +DA:702,0 +DA:703,0 +DA:704,0 +DA:705,0 +DA:706,0 +DA:709,0 +DA:726,0 +DA:742,0 +DA:744,0 +DA:745,0 +DA:746,0 +DA:747,0 +DA:748,0 +DA:749,0 +DA:761,0 +DA:765,0 +DA:766,0 +DA:767,0 +DA:769,0 +DA:770,0 +DA:771,0 +DA:779,0 +DA:780,0 +DA:781,0 +DA:782,0 +DA:783,0 +DA:784,0 +DA:785,0 +DA:812,0 +DA:814,0 +DA:817,0 +DA:825,0 +DA:840,0 +DA:845,0 +DA:847,0 +DA:851,0 +DA:852,0 +DA:855,0 +DA:860,0 +DA:861,0 +DA:878,0 +DA:879,0 +DA:884,0 +DA:896,0 +DA:897,0 +DA:898,0 +DA:899,0 +DA:900,0 +DA:901,0 +DA:902,0 +DA:903,0 +DA:904,0 +DA:905,0 +DA:931,0 +DA:933,0 +DA:935,0 +DA:936,0 +DA:937,0 +DA:938,0 +DA:939,0 +DA:940,0 +DA:941,0 +DA:942,0 +DA:944,0 +DA:946,0 +DA:954,0 +DA:955,0 +DA:956,0 +DA:957,0 +DA:958,0 +DA:959,0 +DA:960,0 +DA:961,0 +DA:962,0 +DA:963,0 +DA:964,0 +DA:967,0 +DA:988,0 +DA:1008,0 +DA:1010,0 +DA:1011,0 +DA:1012,0 +DA:1013,0 +DA:1014,0 +DA:1015,0 +DA:1016,0 +DA:1017,0 +DA:1018,0 +DA:1019,0 +DA:1031,0 +DA:1035,0 +DA:1036,0 +DA:1037,0 +DA:1038,0 +DA:1039,0 +DA:1040,0 +DA:1041,0 +DA:1042,0 +DA:1044,0 +DA:1045,0 +DA:1053,0 +DA:1054,0 +DA:1055,0 +DA:1056,0 +DA:1057,0 +DA:1058,0 +DA:1059,0 +DA:1060,0 +DA:1061,0 +DA:1062,0 +DA:1063,0 +DA:1102,0 +DA:1104,0 +DA:1107,0 +DA:1119,0 +DA:1138,0 +DA:1143,0 +DA:1145,0 +DA:1149,0 +DA:1150,0 +DA:1153,0 +DA:1158,0 +DA:1159,0 +DA:1176,0 +DA:1177,0 +DA:1182,0 +DA:1189,0 +DA:1190,0 +DA:1191,0 +DA:1192,0 +DA:1193,0 +DA:1218,0 +DA:1220,0 +DA:1222,0 +DA:1223,0 +DA:1224,0 +DA:1225,0 +DA:1226,0 +DA:1234,0 +DA:1235,0 +DA:1236,0 +DA:1237,0 +DA:1238,0 +DA:1239,0 +DA:1242,0 +DA:1258,0 +DA:1273,0 +DA:1275,0 +DA:1276,0 +DA:1277,0 +DA:1278,0 +DA:1279,0 +DA:1291,0 +DA:1295,0 +DA:1296,0 +DA:1297,0 +DA:1298,0 +DA:1299,0 +DA:1307,0 +DA:1308,0 +DA:1309,0 +DA:1310,0 +DA:1311,0 +DA:1312,0 +DA:1334,0 +DA:1336,0 +DA:1339,0 +DA:1346,0 +DA:1360,0 +DA:1365,0 +DA:1367,0 +DA:1371,0 +DA:1372,0 +DA:1375,0 +DA:1380,0 +DA:1381,0 +DA:1398,0 +DA:1399,0 +DA:1404,0 +DA:1411,0 +DA:1412,0 +DA:1413,0 +DA:1414,0 +DA:1415,0 +DA:1440,0 +DA:1442,0 +DA:1444,0 +DA:1445,0 +DA:1446,0 +DA:1447,0 +DA:1448,0 +DA:1456,0 +DA:1457,0 +DA:1458,0 +DA:1459,0 +DA:1460,0 +DA:1461,0 +DA:1464,0 +DA:1480,0 +DA:1495,0 +DA:1497,0 +DA:1498,0 +DA:1499,0 +DA:1500,0 +DA:1501,0 +DA:1513,0 +DA:1517,0 +DA:1518,0 +DA:1519,0 +DA:1520,0 +DA:1521,0 +DA:1529,0 +DA:1530,0 +DA:1531,0 +DA:1532,0 +DA:1533,0 +DA:1534,0 +DA:1556,0 +DA:1558,0 +DA:1561,0 +DA:1568,0 +DA:1582,0 +DA:1587,0 +DA:1589,0 +DA:1593,0 +DA:1594,0 +DA:1597,0 +DA:1602,0 +DA:1603,0 +DA:1620,0 +DA:1621,0 +DA:1626,0 +DA:1656,0 +DA:1657,0 +DA:1658,0 +DA:1659,0 +DA:1660,0 +DA:1661,0 +DA:1662,0 +DA:1663,0 +DA:1664,0 +DA:1665,0 +DA:1666,0 +DA:1667,0 +DA:1668,0 +DA:1669,0 +DA:1670,0 +DA:1671,0 +DA:1672,0 +DA:1673,0 +DA:1674,0 +DA:1675,0 +DA:1676,0 +DA:1678,0 +DA:1679,0 +DA:1680,0 +DA:1681,0 +DA:1682,0 +DA:1683,0 +DA:1684,0 +DA:1711,0 +DA:1713,0 +DA:1715,0 +DA:1716,0 +DA:1717,0 +DA:1718,0 +DA:1719,0 +DA:1720,0 +DA:1721,0 +DA:1722,0 +DA:1723,0 +DA:1725,0 +DA:1727,0 +DA:1728,0 +DA:1729,0 +DA:1730,0 +DA:1731,0 +DA:1732,0 +DA:1733,0 +DA:1734,0 +DA:1735,0 +DA:1736,0 +DA:1737,0 +DA:1738,0 +DA:1739,0 +DA:1740,0 +DA:1741,0 +DA:1742,0 +DA:1743,0 +DA:1744,0 +DA:1745,0 +DA:1753,0 +DA:1754,0 +DA:1755,0 +DA:1756,0 +DA:1757,0 +DA:1758,0 +DA:1759,0 +DA:1760,0 +DA:1761,0 +DA:1762,0 +DA:1763,0 +DA:1764,0 +DA:1765,0 +DA:1766,0 +DA:1767,0 +DA:1768,0 +DA:1769,0 +DA:1770,0 +DA:1771,0 +DA:1772,0 +DA:1773,0 +DA:1774,0 +DA:1775,0 +DA:1776,0 +DA:1777,0 +DA:1778,0 +DA:1779,0 +DA:1780,0 +DA:1781,0 +DA:1784,0 +DA:1822,0 +DA:1860,0 +DA:1862,0 +DA:1863,0 +DA:1864,0 +DA:1865,0 +DA:1866,0 +DA:1867,0 +DA:1868,0 +DA:1869,0 +DA:1870,0 +DA:1871,0 +DA:1872,0 +DA:1873,0 +DA:1874,0 +DA:1875,0 +DA:1876,0 +DA:1877,0 +DA:1878,0 +DA:1879,0 +DA:1880,0 +DA:1881,0 +DA:1882,0 +DA:1883,0 +DA:1884,0 +DA:1885,0 +DA:1886,0 +DA:1887,0 +DA:1888,0 +DA:1889,0 +DA:1901,0 +DA:1905,0 +DA:1906,0 +DA:1907,0 +DA:1908,0 +DA:1909,0 +DA:1910,0 +DA:1911,0 +DA:1912,0 +DA:1913,0 +DA:1914,0 +DA:1915,0 +DA:1916,0 +DA:1917,0 +DA:1918,0 +DA:1919,0 +DA:1920,0 +DA:1921,0 +DA:1922,0 +DA:1923,0 +DA:1924,0 +DA:1925,0 +DA:1927,0 +DA:1928,0 +DA:1929,0 +DA:1931,0 +DA:1933,0 +DA:1934,0 +DA:1935,0 +DA:1936,0 +DA:1937,0 +DA:1945,0 +DA:1946,0 +DA:1947,0 +DA:1948,0 +DA:1949,0 +DA:1950,0 +DA:1951,0 +DA:1952,0 +DA:1953,0 +DA:1954,0 +DA:1955,0 +DA:1956,0 +DA:1957,0 +DA:1958,0 +DA:1959,0 +DA:1960,0 +DA:1961,0 +DA:1962,0 +DA:1963,0 +DA:1964,0 +DA:1965,0 +DA:1966,0 +DA:1967,0 +DA:1968,0 +DA:1969,0 +DA:1970,0 +DA:1971,0 +DA:1972,0 +DA:1973,0 +DA:2067,0 +DA:2069,0 +DA:2072,0 +DA:2102,0 +DA:2139,0 +DA:2144,0 +DA:2146,0 +DA:2150,0 +DA:2151,0 +DA:2154,0 +LF:577 LH:0 end_of_record -SF:lib/components/organisms/app_shell.dart -DA:11,0 -DA:17,0 +SF:lib/components/molecules/slide_preview.dart +DA:13,0 DA:20,0 +DA:22,0 +DA:23,0 DA:25,0 DA:26,0 -DA:34,0 -DA:36,0 -DA:37,0 -DA:41,0 -DA:42,0 -DA:46,0 +DA:27,0 +DA:28,0 +DA:30,0 +DA:31,0 +DA:32,0 +DA:38,0 +DA:39,0 DA:47,0 -DA:51,0 -DA:53,0 DA:54,0 +DA:56,0 +DA:57,0 +DA:58,0 DA:59,0 DA:60,0 DA:61,0 -DA:67,0 -DA:77,0 -DA:79,0 -DA:81,0 -DA:82,0 -DA:83,0 -DA:85,0 -DA:88,0 -DA:90,0 -DA:92,0 -DA:95,0 -DA:98,0 -DA:101,0 -DA:104,0 -DA:107,0 -DA:110,0 -DA:111,0 -DA:114,0 -DA:116,0 -DA:118,0 -DA:123,0 -DA:124,0 -DA:125,0 -DA:126,0 -DA:127,0 -DA:130,0 -DA:134,0 -DA:135,0 -DA:136,0 -DA:137,0 -DA:138,0 -DA:145,0 -DA:147,0 -DA:149,0 -DA:153,0 -DA:154,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:162,0 -DA:163,0 -DA:164,0 -DA:165,0 -DA:166,0 -LF:62 -LH:0 -end_of_record -SF:lib/components/organisms/drawer.dart -DA:16,0 -DA:20,0 -LF:2 -LH:0 -end_of_record -SF:lib/components/superdeck_app.dart -DA:18,0 -DA:21,0 -DA:32,0 -DA:36,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:49,0 -DA:51,0 -DA:64,0 -DA:66,0 -DA:67,0 -DA:68,0 -DA:69,0 -DA:73,0 -DA:75,0 -DA:76,0 -DA:79,0 -DA:81,0 -DA:89,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:96,0 -DA:97,0 -DA:98,0 -DA:101,0 -DA:102,0 -DA:103,0 -DA:104,0 -DA:105,0 -DA:107,0 -DA:108,0 -DA:109,0 -DA:110,0 -DA:111,0 -DA:113,0 -DA:126,0 -DA:130,0 -DA:140,0 -DA:141,0 -DA:142,0 -DA:145,0 -DA:148,0 -DA:149,0 -DA:150,0 -DA:152,0 -DA:153,0 -DA:155,0 -DA:156,0 -DA:157,0 -DA:166,0 -DA:168,0 -DA:170,0 -DA:171,0 -DA:172,0 -DA:173,0 -DA:175,0 -DA:181,0 -DA:182,0 -DA:183,0 -DA:185,0 -LF:63 -LH:0 -end_of_record -SF:lib/helpers/theme.dart -DA:3,0 -DA:4,0 -LF:2 +DA:62,0 +DA:63,0 +DA:66,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:70,0 +LF:28 LH:0 end_of_record -SF:lib/screens/export_screen.dart +SF:lib/styles/style_util.dart +DA:6,0 +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:14,0 +DA:16,0 +DA:17,0 +DA:18,0 +DA:19,0 +DA:20,0 +DA:21,0 +DA:22,0 +DA:23,0 +DA:24,0 +DA:25,0 +DA:26,0 DA:27,0 -DA:31,3 +DA:28,0 +DA:29,0 +DA:30,0 +DA:31,0 +DA:32,0 DA:33,0 DA:34,0 -DA:42,0 +DA:35,0 +DA:36,0 +DA:38,0 +DA:39,0 +DA:41,0 DA:43,0 +DA:44,0 DA:45,0 +DA:46,0 DA:47,0 +DA:48,0 +DA:49,0 DA:50,0 DA:51,0 DA:52,0 +DA:53,0 +DA:54,0 DA:55,0 +DA:56,0 DA:57,0 DA:58,0 +DA:59,0 DA:60,0 +DA:61,0 +DA:63,0 DA:64,0 DA:65,0 +DA:66,0 +DA:67,0 DA:68,0 DA:69,0 DA:70,0 +DA:71,0 +DA:72,0 DA:73,0 +DA:74,0 DA:75,0 +DA:76,0 DA:77,0 DA:78,0 DA:79,0 DA:80,0 -DA:83,0 +DA:82,0 +LF:68 +LH:0 +end_of_record +SF:lib/components/molecules/split_view.dart +DA:9,0 +DA:10,0 +DA:11,0 +DA:12,0 +DA:13,0 +DA:14,0 +DA:18,0 +DA:23,0 +DA:28,0 +DA:30,0 +DA:38,0 +DA:40,0 +DA:41,0 +DA:45,0 +DA:46,0 +DA:47,0 +DA:52,0 +DA:53,0 +DA:57,0 +DA:59,0 +DA:60,0 +DA:63,0 +DA:65,0 +DA:67,0 +DA:68,0 +DA:69,0 +DA:71,0 +DA:75,0 +DA:77,0 +DA:78,0 +DA:80,0 +DA:82,0 DA:85,0 -DA:88,0 DA:89,0 +DA:90,0 +DA:91,0 DA:92,0 DA:93,0 +DA:94,0 DA:95,0 -DA:98,0 -DA:105,0 -DA:106,0 +DA:99,0 +DA:101,0 DA:107,0 -DA:108,0 -DA:120,0 -DA:129,0 -DA:130,0 -DA:140,0 +DA:109,0 +DA:114,0 +DA:116,0 +DA:125,0 +DA:131,0 +DA:134,0 +DA:135,0 +DA:137,0 +DA:141,0 DA:142,0 -DA:144,0 -DA:146,0 -DA:149,0 -DA:151,0 -DA:152,0 -DA:155,0 -DA:157,0 -DA:158,0 -DA:160,0 DA:162,0 -DA:163,0 -DA:164,0 -DA:167,0 +DA:170,0 DA:172,0 DA:173,0 -DA:176,0 -DA:178,0 -DA:179,0 +DA:174,0 +DA:177,0 DA:180,0 -DA:182,0 +DA:181,0 DA:184,0 DA:185,0 +DA:188,0 DA:189,0 -DA:192,0 -DA:194,0 DA:195,0 DA:196,0 -DA:201,0 -DA:205,0 -DA:209,0 -DA:211,0 -DA:213,0 -DA:214,0 -DA:216,0 -DA:220,0 -DA:221,0 -DA:223,0 -DA:224,0 -DA:226,0 -DA:227,0 -DA:228,0 -DA:229,0 -DA:230,0 -DA:232,0 -DA:233,0 -DA:234,0 -DA:236,0 -DA:237,0 -DA:245,0 -DA:248,0 -DA:250,0 -DA:252,0 -DA:254,0 -DA:257,0 -DA:259,0 -DA:263,0 -DA:264,0 -DA:265,0 -DA:267,0 -DA:268,0 -DA:269,0 -DA:270,0 -DA:271,0 -DA:274,0 -DA:275,0 -DA:276,0 -DA:298,0 -DA:299,0 -DA:305,0 -DA:306,0 -DA:307,0 -DA:308,0 -DA:309,0 -DA:310,0 -DA:311,0 -DA:318,0 -DA:320,0 -DA:323,0 -DA:324,0 -DA:325,0 -DA:326,0 -DA:327,0 -DA:331,0 -DA:332,0 -DA:333,0 -DA:340,0 -DA:341,0 -DA:342,0 -DA:343,0 -DA:345,0 -DA:346,0 -DA:348,0 -DA:350,0 -LF:136 -LH:1 +DA:199,0 +DA:200,0 +DA:203,0 +DA:204,0 +DA:207,0 +DA:208,0 +LF:73 +LH:0 end_of_record -SF:lib/screens/home_screen.dart -DA:9,3 -DA:13,0 -DA:15,0 -DA:23,0 +SF:lib/components/molecules/slide_thumbnail_list.dart +DA:11,0 +DA:24,0 DA:25,0 -DA:26,0 -DA:29,0 -DA:31,0 -DA:32,0 -DA:35,0 +DA:34,0 DA:36,0 -DA:37,0 +DA:38,0 +DA:39,0 DA:43,0 -DA:45,0 -DA:46,0 +DA:44,0 +DA:49,0 +DA:54,0 +DA:58,0 +DA:60,0 +DA:61,0 +DA:62,0 +DA:65,0 +DA:68,0 +DA:75,0 +DA:78,0 +DA:80,0 +DA:81,0 +DA:82,0 +DA:86,0 +DA:88,0 +DA:90,0 +DA:91,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:96,0 +DA:97,0 +DA:98,0 +DA:100,0 +DA:102,0 +DA:103,0 +LF:35 +LH:0 +end_of_record +SF:lib/providers/controller.dart +DA:14,0 +DA:15,0 +DA:18,0 +DA:20,0 +DA:22,0 +DA:42,0 +DA:47,0 +DA:48,0 +DA:49,0 DA:52,0 -DA:54,0 -DA:58,0 -DA:60,0 -DA:62,0 +DA:53,0 +DA:55,0 +DA:59,0 DA:63,0 -DA:66,0 -DA:67,0 +DA:64,0 +DA:65,0 +DA:68,0 DA:69,0 DA:70,0 DA:71,0 DA:72,0 DA:73,0 DA:74,0 -LF:29 -LH:1 +DA:80,0 +DA:81,0 +DA:84,0 +DA:86,0 +DA:92,0 +DA:93,0 +DA:94,0 +DA:95,0 +DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 +DA:101,0 +DA:106,0 +DA:107,0 +DA:108,0 +DA:109,0 +DA:111,0 +DA:115,0 +DA:116,0 +DA:117,0 +DA:120,0 +DA:121,0 +DA:124,0 +DA:125,0 +DA:128,0 +DA:129,0 +DA:132,0 +DA:133,0 +DA:136,0 +DA:137,0 +DA:138,0 +DA:141,0 +DA:142,0 +DA:143,0 +DA:144,0 +DA:149,0 +DA:150,0 +DA:151,0 +DA:156,0 +DA:157,0 +DA:158,0 +LF:65 +LH:0 end_of_record -SF:lib/schema/schema.dart -DA:13,0 +SF:lib/components/organisms/app_shell.dart +DA:11,0 DA:17,0 -DA:30,0 -DA:35,0 -DA:39,3 +DA:20,0 +DA:25,0 +DA:26,0 +DA:34,0 +DA:36,0 +DA:37,0 +DA:41,0 DA:42,0 -DA:44,0 DA:46,0 -DA:49,0 +DA:47,0 DA:51,0 DA:53,0 -DA:55,0 +DA:54,0 DA:57,0 +DA:58,0 DA:59,0 -DA:61,0 +DA:65,0 +DA:75,0 +DA:77,0 +DA:79,0 +DA:80,0 +DA:81,0 +DA:83,0 +DA:86,0 +DA:88,0 +DA:90,0 +DA:93,0 +DA:96,0 +DA:99,0 +DA:102,0 +DA:105,0 +DA:108,0 +DA:109,0 +DA:112,0 +DA:114,0 +DA:116,0 +DA:121,0 +DA:122,0 +DA:123,0 +DA:124,0 +DA:125,0 +DA:128,0 +DA:132,0 +DA:133,0 +DA:134,0 +DA:135,0 +DA:136,0 +DA:143,0 +DA:145,0 +DA:147,0 +DA:151,0 +DA:152,0 +DA:153,0 +DA:154,0 +DA:155,0 +DA:160,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +LF:62 +LH:0 +end_of_record +SF:lib/components/organisms/drawer.dart +DA:16,0 +DA:20,0 +LF:2 +LH:0 +end_of_record +SF:lib/components/superdeck_app.dart +DA:19,0 +DA:20,0 +DA:23,0 +DA:34,0 +DA:38,0 +DA:42,0 +DA:43,0 +DA:44,0 +DA:45,0 +DA:51,0 +DA:53,0 +DA:66,0 +DA:68,0 +DA:69,0 DA:70,0 +DA:71,0 DA:75,0 DA:77,0 -DA:82,0 +DA:78,0 +DA:81,0 +DA:83,0 DA:84,0 DA:85,0 DA:86,0 +DA:87,0 +DA:88,0 +DA:91,0 +DA:92,0 DA:93,0 +DA:94,0 DA:95,0 DA:97,0 +DA:98,0 +DA:99,0 +DA:100,0 DA:101,0 +DA:102,0 DA:103,0 -DA:105,0 -DA:109,0 -DA:110,0 -DA:112,0 -DA:116,0 -DA:118,0 DA:120,0 DA:124,0 -DA:126,0 -DA:129,0 -DA:138,3 +DA:134,0 +DA:135,0 +DA:136,0 +DA:139,0 +DA:142,0 +DA:143,0 +DA:144,0 DA:146,0 +DA:147,0 DA:149,0 -DA:154,0 -DA:161,0 +DA:150,0 +DA:151,0 +DA:160,0 DA:162,0 -DA:163,0 DA:164,0 DA:165,0 +DA:166,0 +DA:167,0 DA:169,0 -DA:171,0 -DA:174,0 DA:175,0 DA:176,0 DA:177,0 -DA:181,0 -DA:182,0 -DA:185,0 -DA:191,0 -DA:193,0 -DA:194,0 -DA:195,0 -DA:197,0 -DA:199,0 -DA:200,0 -DA:202,0 -DA:206,0 -DA:213,0 -DA:216,0 -DA:217,0 -DA:218,0 -DA:221,0 -DA:224,0 -DA:227,0 -DA:231,0 -DA:233,0 -DA:234,0 -DA:235,0 -DA:237,0 -DA:239,0 -DA:240,0 -DA:243,0 -DA:244,0 -DA:245,0 -DA:248,0 -DA:249,0 -DA:250,0 -DA:251,0 -DA:254,0 -DA:255,0 -DA:260,0 -DA:261,0 -DA:262,0 -DA:265,0 -DA:266,0 -DA:267,0 -DA:269,0 -DA:272,0 -DA:273,0 -DA:278,0 -DA:283,3 -LF:98 -LH:3 +DA:179,0 +LF:63 +LH:0 end_of_record -SF:lib/schema/schema_values.dart -DA:5,3 -DA:15,0 -DA:16,0 -DA:19,0 -DA:20,0 -DA:26,0 -DA:28,0 -DA:30,0 -DA:31,0 -DA:32,0 +SF:lib/helpers/theme.dart +DA:3,0 +DA:4,0 +LF:2 +LH:0 +end_of_record +SF:lib/screens/export_screen.dart +DA:27,0 +DA:31,5 DA:33,0 -DA:37,0 -DA:39,0 +DA:34,0 DA:40,0 DA:41,0 -DA:44,0 -DA:47,0 +DA:43,0 +DA:45,0 +DA:48,0 +DA:49,0 DA:50,0 -DA:51,0 DA:53,0 -DA:57,0 +DA:55,0 +DA:56,0 +DA:58,0 +DA:62,0 DA:63,0 -DA:65,0 -DA:70,0 +DA:66,0 +DA:67,0 +DA:68,0 DA:71,0 -DA:72,0 +DA:73,0 +DA:75,0 DA:76,0 -DA:83,3 -DA:85,0 +DA:77,0 +DA:78,0 +DA:81,0 +DA:83,0 +DA:86,0 +DA:87,0 DA:90,0 DA:91,0 -DA:92,0 +DA:93,0 DA:96,0 -DA:98,0 -DA:102,0 DA:103,0 +DA:104,0 +DA:105,0 DA:106,0 -DA:107,0 -DA:116,0 -DA:122,0 +DA:118,0 +DA:127,0 DA:128,0 -DA:129,0 -DA:130,0 -DA:131,0 -DA:135,0 DA:137,0 DA:139,0 -DA:140,0 -DA:142,0 +DA:141,0 DA:143,0 -DA:152,3 +DA:146,0 +DA:148,0 +DA:149,0 +DA:152,0 DA:154,0 +DA:155,0 +DA:157,0 DA:159,0 DA:160,0 DA:161,0 -DA:165,0 -DA:167,0 -DA:172,3 -DA:174,0 +DA:164,0 +DA:169,0 +DA:170,0 +DA:173,0 +DA:175,0 +DA:176,0 +DA:177,0 DA:179,0 -DA:180,0 DA:181,0 -DA:185,0 -DA:187,0 +DA:182,0 +DA:186,0 +DA:189,0 DA:191,0 DA:192,0 -DA:200,3 +DA:193,0 +DA:198,0 DA:202,0 -DA:207,0 +DA:206,0 DA:208,0 -DA:209,0 +DA:210,0 +DA:211,0 DA:213,0 -DA:215,0 -DA:219,0 +DA:217,0 +DA:218,0 DA:220,0 -DA:222,0 -DA:232,0 +DA:221,0 +DA:223,0 +DA:224,0 +DA:225,0 +DA:226,0 +DA:227,0 +DA:229,0 +DA:230,0 +DA:231,0 DA:233,0 DA:234,0 -DA:235,0 -DA:239,0 -DA:240,0 -DA:241,0 DA:242,0 -DA:246,0 +DA:245,0 DA:247,0 -DA:248,0 DA:249,0 -DA:253,0 +DA:251,0 DA:254,0 -DA:255,0 DA:256,0 DA:260,0 DA:261,0 DA:262,0 -DA:263,0 +DA:264,0 +DA:265,0 +DA:266,0 DA:267,0 DA:268,0 -DA:269,0 -DA:270,0 -LF:100 -LH:5 +DA:271,0 +DA:272,0 +DA:273,0 +DA:295,0 +DA:296,0 +DA:302,0 +DA:303,0 +DA:304,0 +DA:305,0 +DA:306,0 +DA:307,0 +DA:308,0 +DA:315,0 +DA:317,0 +DA:320,0 +DA:321,0 +DA:322,0 +DA:323,0 +DA:324,0 +DA:328,0 +DA:329,0 +DA:330,0 +DA:337,0 +DA:338,0 +DA:339,0 +DA:340,0 +DA:342,0 +DA:343,0 +DA:345,0 +DA:347,0 +LF:136 +LH:1 end_of_record -SF:lib/helpers/extensions.dart -DA:4,0 -DA:5,0 -DA:6,0 -DA:9,0 -DA:12,0 +SF:lib/screens/home_screen.dart +DA:9,5 DA:13,0 -DA:14,0 -DA:20,0 +DA:15,0 DA:21,0 -DA:22,0 -LF:10 -LH:0 -end_of_record -SF:lib/helpers/mappers.dart -DA:7,3 -DA:9,0 -DA:11,0 -DA:14,0 -DA:16,0 -DA:21,3 -DA:23,0 -DA:25,0 -DA:28,0 -DA:30,0 -DA:35,3 -DA:37,0 -DA:41,0 -DA:42,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:50,0 -DA:52,0 -LF:19 -LH:3 -end_of_record -SF:lib/models/asset_model.dart DA:23,0 DA:24,0 DA:25,0 DA:28,0 -DA:29,0 -DA:32,0 -DA:34,0 -DA:36,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:50,0 -DA:51,0 -DA:65,0 -DA:70,0 -DA:72,0 -DA:74,0 -DA:76,0 -DA:77,0 -DA:78,0 -DA:83,0 -DA:84,0 -DA:85,0 -DA:90,0 -DA:91,0 -DA:92,0 -LF:26 -LH:0 -end_of_record -SF:lib/schema/validators.dart -DA:4,3 -DA:10,3 -DA:11,0 -DA:19,0 -DA:20,0 -DA:29,3 DA:30,0 -DA:38,3 -DA:39,0 -DA:50,3 -DA:56,0 -DA:58,0 -DA:59,0 -DA:60,0 -DA:69,3 -DA:71,0 -DA:73,0 -DA:81,0 -DA:83,0 -DA:85,0 -DA:87,0 -DA:88,0 -DA:95,0 -DA:97,0 -DA:99,0 -DA:101,0 -DA:102,0 -DA:109,0 -DA:111,0 -DA:113,0 -DA:115,0 -DA:116,0 -DA:123,0 -DA:125,0 -DA:127,0 -DA:129,0 -DA:130,0 -DA:138,0 -DA:140,0 -DA:142,0 -DA:144,0 -DA:145,0 -DA:151,0 -DA:153,0 -DA:163,0 -DA:165,0 -DA:167,0 -DA:168,0 -DA:177,0 -DA:179,0 -DA:181,0 -DA:183,0 -DA:184,0 -DA:192,0 -DA:194,0 -DA:196,0 -DA:198,0 -DA:199,0 -LF:58 -LH:6 -end_of_record -SF:lib/styles/style_attribute.dart -DA:26,0 -DA:47,0 -DA:49,0 -DA:50,0 +DA:31,0 +DA:34,0 +DA:35,0 +DA:36,0 +DA:42,0 +DA:44,0 +DA:45,0 DA:51,0 -DA:52,0 DA:53,0 -DA:54,0 -DA:55,0 -DA:56,0 DA:57,0 -DA:58,0 DA:59,0 -DA:60,0 DA:61,0 DA:62,0 DA:63,0 -DA:65,0 DA:67,0 DA:68,0 -DA:69,0 DA:70,0 +DA:71,0 +DA:72,0 +DA:73,0 DA:74,0 -DA:76,0 +DA:75,0 +LF:31 +LH:1 +end_of_record +SF:lib/helpers/mappers.dart +DA:7,6 +DA:9,1 +DA:11,1 +DA:14,0 +DA:16,0 +DA:21,8 +DA:23,1 +DA:25,1 +DA:28,0 +DA:30,0 +DA:35,6 +DA:37,1 +DA:41,1 +DA:42,1 +DA:44,1 +DA:45,1 +DA:46,1 +DA:50,0 +DA:52,0 +LF:19 +LH:13 +end_of_record +SF:lib/models/asset_model.dart +DA:23,1 +DA:24,4 +DA:25,2 +DA:28,1 +DA:29,4 +DA:32,2 +DA:34,3 +DA:36,2 +DA:45,1 +DA:46,4 +DA:47,2 +DA:50,1 +DA:51,4 +DA:65,2 +DA:70,4 +DA:72,6 +DA:74,2 +DA:76,1 +DA:77,2 +DA:78,2 +DA:83,1 +DA:84,2 +DA:85,2 +DA:90,1 +DA:91,2 +DA:92,2 +LF:26 +LH:26 +end_of_record +SF:lib/schema/schema_model.dart +DA:13,0 +DA:17,0 +DA:30,0 +DA:35,0 +DA:39,5 +DA:42,0 +DA:44,0 +DA:46,0 +DA:49,0 +DA:51,0 +DA:53,0 +DA:55,0 +DA:57,0 +DA:59,0 +DA:61,0 +DA:70,0 +DA:75,0 DA:77,0 -DA:78,0 -DA:79,0 -DA:80,0 -DA:81,0 DA:82,0 -DA:83,0 DA:84,0 DA:85,0 DA:86,0 -DA:87,0 -DA:88,0 -DA:89,0 -DA:90,0 -DA:91,0 -DA:92,0 DA:93,0 -DA:94,0 -DA:98,0 -DA:99,0 -DA:100,0 +DA:95,0 +DA:97,0 DA:101,0 -DA:102,0 DA:103,0 -DA:104,0 DA:105,0 -DA:106,0 -DA:107,0 -DA:108,0 DA:109,0 DA:110,0 -DA:111,0 DA:112,0 -DA:113,0 -DA:114,0 -DA:115,0 DA:116,0 -DA:117,0 -LF:62 -LH:0 +DA:118,0 +DA:120,0 +DA:124,0 +DA:126,0 +DA:129,0 +DA:138,5 +DA:146,0 +DA:149,0 +DA:154,0 +DA:161,0 +DA:162,0 +DA:163,0 +DA:164,0 +DA:165,0 +DA:169,0 +DA:171,0 +DA:174,0 +DA:175,0 +DA:176,0 +DA:177,0 +DA:181,0 +DA:182,0 +DA:185,0 +DA:191,0 +DA:193,0 +DA:194,0 +DA:195,0 +DA:197,0 +DA:199,0 +DA:200,0 +DA:202,0 +DA:206,0 +DA:213,0 +DA:216,0 +DA:217,0 +DA:218,0 +DA:221,0 +DA:224,0 +DA:227,0 +DA:231,0 +DA:233,0 +DA:234,0 +DA:235,0 +DA:237,0 +DA:239,0 +DA:240,0 +DA:243,0 +DA:244,0 +DA:245,0 +DA:248,0 +DA:249,0 +DA:250,0 +DA:251,0 +DA:254,0 +DA:255,0 +DA:260,0 +DA:261,0 +DA:262,0 +DA:265,0 +DA:266,0 +DA:267,0 +DA:269,0 +DA:272,0 +DA:273,0 +DA:278,0 +DA:283,5 +LF:98 +LH:3 end_of_record -SF:lib/styles/style_dto.dart -DA:11,0 +SF:lib/schema/schema_values.dart +DA:5,5 +DA:15,0 DA:16,0 -DA:17,0 -DA:18,0 DA:19,0 -DA:23,0 -DA:24,0 -DA:27,0 -DA:29,0 +DA:20,0 +DA:26,0 +DA:28,0 DA:30,0 DA:31,0 -DA:35,0 +DA:32,0 +DA:33,0 DA:37,0 -DA:38,0 DA:39,0 -DA:43,0 +DA:40,0 +DA:41,0 DA:44,0 -DA:54,0 -DA:62,0 +DA:47,0 +DA:50,0 +DA:51,0 +DA:53,0 +DA:57,0 DA:63,0 -DA:64,0 DA:65,0 -DA:66,0 -DA:67,0 -DA:68,0 +DA:70,0 +DA:71,0 DA:72,0 -DA:73,0 DA:76,0 -DA:78,0 -DA:79,0 -DA:80,0 -DA:81,0 -DA:82,0 -DA:84,0 -DA:88,0 +DA:83,5 +DA:85,0 DA:90,0 DA:91,0 DA:92,0 -DA:93,0 -DA:94,0 -DA:95,0 -DA:99,0 -DA:101,0 -DA:109,0 -DA:115,0 +DA:96,0 +DA:98,0 +DA:102,0 +DA:103,0 +DA:106,0 +DA:107,0 DA:116,0 -DA:117,0 -DA:118,0 -DA:119,0 -DA:123,0 -DA:124,0 -DA:127,0 +DA:122,0 +DA:128,0 DA:129,0 DA:130,0 DA:131,0 -DA:132,0 -DA:136,0 -DA:138,0 +DA:135,0 +DA:137,0 DA:139,0 DA:140,0 -DA:141,0 -DA:145,0 -DA:146,0 -DA:155,0 -DA:162,0 -DA:163,0 -DA:164,0 +DA:142,0 +DA:143,0 +DA:152,5 +DA:154,0 +DA:159,0 +DA:160,0 +DA:161,0 DA:165,0 -DA:166,0 DA:167,0 -DA:171,0 -DA:172,0 -DA:175,0 -DA:177,0 -DA:178,0 +DA:172,5 +DA:174,0 DA:179,0 DA:180,0 -DA:182,0 -DA:186,0 -DA:188,0 -DA:189,0 -DA:190,0 +DA:181,0 +DA:185,0 +DA:187,0 DA:191,0 DA:192,0 -DA:196,0 -DA:197,0 -DA:211,0 +DA:200,5 +DA:202,0 +DA:207,0 +DA:208,0 +DA:209,0 +DA:213,0 +DA:215,0 +DA:219,0 +DA:220,0 DA:222,0 -DA:223,0 -DA:224,0 -DA:225,0 -DA:226,0 -DA:227,0 -DA:228,0 -DA:229,0 -DA:230,0 -DA:231,0 +DA:232,0 +DA:233,0 +DA:234,0 DA:235,0 -DA:236,0 DA:239,0 +DA:240,0 DA:241,0 DA:242,0 -DA:243,0 -DA:244,0 -DA:245,0 +DA:246,0 DA:247,0 +DA:248,0 DA:249,0 -DA:250,0 -DA:251,0 +DA:253,0 +DA:254,0 DA:255,0 -DA:257,0 -DA:258,0 -DA:259,0 +DA:256,0 DA:260,0 DA:261,0 DA:262,0 DA:263,0 -DA:264,0 -DA:265,0 +DA:267,0 +DA:268,0 DA:269,0 DA:270,0 -DA:271,0 -DA:272,0 -DA:273,0 -DA:274,0 -DA:275,0 -DA:276,0 -DA:277,0 -DA:278,0 -DA:291,0 -DA:299,0 -DA:300,0 -DA:301,0 -DA:302,0 -DA:303,0 -DA:304,0 -DA:305,0 -DA:309,0 -DA:310,0 -DA:313,0 -DA:315,0 -DA:316,0 -DA:317,0 -DA:318,0 -DA:320,0 -DA:321,0 -DA:325,0 -DA:327,0 -DA:328,0 -DA:329,0 -DA:330,0 -DA:331,0 -DA:332,0 -DA:336,0 -DA:337,0 -DA:349,0 -DA:350,0 -DA:351,0 -DA:352,0 -DA:353,0 -DA:354,0 -DA:355,0 -DA:356,0 -DA:357,0 -DA:361,0 -DA:367,0 -DA:368,0 -DA:372,0 -DA:379,0 -DA:383,0 -DA:384,0 -DA:387,0 -DA:397,0 -DA:399,0 -DA:400,0 -DA:401,0 -DA:402,0 -DA:403,0 -DA:404,0 -DA:405,0 -DA:407,0 -DA:409,0 -DA:413,0 -DA:415,0 -DA:416,0 -DA:417,0 -DA:418,0 -DA:419,0 -DA:420,0 -DA:421,0 -DA:423,0 -DA:427,0 -DA:428,0 -DA:429,0 -DA:430,0 -DA:431,0 -DA:432,0 -DA:433,0 -DA:434,0 -DA:435,0 -LF:200 -LH:0 +LF:100 +LH:5 end_of_record -SF:lib/styles/style_util.dart -DA:7,0 -DA:10,0 +SF:lib/schema/validators.dart +DA:4,5 +DA:10,5 DA:11,0 -DA:12,0 -DA:13,0 -DA:15,0 -DA:17,0 -DA:18,0 +DA:19,0 DA:20,0 -DA:22,0 -DA:23,0 -DA:24,0 -DA:25,0 -DA:26,0 -DA:27,0 -DA:28,0 -DA:29,0 +DA:29,5 DA:30,0 -DA:31,0 -DA:32,0 -DA:33,0 -DA:34,0 -DA:35,0 -DA:36,0 -DA:37,0 -DA:38,0 +DA:38,5 DA:39,0 -DA:40,0 -DA:41,0 -DA:42,0 -DA:43,0 -DA:44,0 -DA:45,0 -DA:46,0 -DA:47,0 -DA:48,0 -DA:49,0 -DA:50,0 -DA:51,0 -DA:52,0 -DA:53,0 -DA:54,0 -DA:55,0 +DA:50,5 DA:56,0 -DA:57,0 DA:58,0 DA:59,0 DA:60,0 -DA:61,0 -DA:62,0 -DA:63,0 -DA:64,0 -DA:69,0 -DA:75,3 -DA:77,0 -DA:78,0 -DA:79,0 -DA:80,0 +DA:69,5 +DA:71,0 +DA:73,0 DA:81,0 -DA:82,0 DA:83,0 -DA:84,0 DA:85,0 -DA:86,0 DA:87,0 DA:88,0 -DA:89,0 -DA:90,0 DA:95,0 -DA:96,0 DA:97,0 -DA:108,0 +DA:99,0 +DA:101,0 +DA:102,0 DA:109,0 -DA:110,0 -DA:114,0 +DA:111,0 +DA:113,0 DA:115,0 DA:116,0 -DA:120,0 -DA:121,0 -DA:122,0 -DA:126,0 +DA:123,0 +DA:125,0 DA:127,0 -DA:128,0 -DA:132,0 -DA:133,0 -DA:134,0 +DA:129,0 +DA:130,0 DA:138,0 -DA:139,0 DA:140,0 +DA:142,0 DA:144,0 DA:145,0 -DA:146,0 -DA:150,0 DA:151,0 -DA:152,0 -DA:156,0 -DA:157,0 -DA:158,0 -DA:162,0 +DA:153,0 DA:163,0 -DA:164,0 +DA:165,0 +DA:167,0 DA:168,0 -DA:169,0 -DA:170,0 -DA:174,0 -DA:175,0 -DA:176,0 -DA:180,0 +DA:177,0 +DA:179,0 DA:181,0 -DA:182,0 -DA:186,0 -DA:187,0 -DA:188,0 +DA:183,0 +DA:184,0 DA:192,0 -DA:193,0 DA:194,0 +DA:196,0 DA:198,0 DA:199,0 -DA:200,0 -DA:204,0 -DA:205,0 -DA:206,0 -DA:210,0 -DA:211,0 -DA:214,0 -DA:235,0 -DA:236,0 -DA:262,0 -DA:264,0 -DA:265,0 -DA:268,0 -DA:269,0 -DA:272,0 -DA:277,0 -DA:278,0 -DA:285,0 -DA:289,0 -DA:290,0 -DA:291,0 -DA:298,0 -DA:300,0 -DA:301,0 -DA:304,0 -DA:305,0 -DA:308,0 -DA:309,0 -DA:312,0 -DA:318,0 -DA:319,0 -DA:330,0 -DA:332,0 -DA:333,0 -DA:336,0 -DA:337,0 -DA:340,0 -DA:341,0 -DA:344,0 -DA:345,0 -DA:348,0 -DA:349,0 -DA:352,0 -DA:360,0 -DA:361,0 -DA:374,0 -DA:375,0 -DA:377,0 -DA:378,0 -DA:381,0 -DA:382,0 -DA:385,0 -DA:386,0 -DA:389,0 -DA:390,0 -DA:393,0 -DA:394,0 -DA:397,0 -DA:398,0 -DA:401,0 -DA:402,0 -DA:412,0 -DA:422,0 -DA:423,0 -DA:438,0 -DA:440,0 -DA:441,0 -DA:446,0 -DA:448,0 -DA:449,0 -DA:452,0 -DA:453,0 -DA:456,0 -DA:457,0 -DA:460,0 -DA:461,0 -DA:464,0 -DA:465,0 -DA:468,0 -DA:469,0 -DA:470,0 -DA:474,0 -DA:475,0 -DA:478,0 -DA:479,0 -DA:482,0 -DA:493,0 -DA:494,0 -DA:510,0 -DA:512,0 -DA:513,0 -DA:516,0 -DA:517,0 -DA:520,0 -DA:521,0 -DA:524,0 -DA:525,0 -DA:529,0 -DA:530,0 -DA:533,0 -DA:541,0 -DA:542,0 -DA:555,0 -DA:556,0 -DA:558,0 -DA:559,0 -DA:562,0 -DA:563,0 -DA:566,0 -DA:567,0 -DA:570,0 -DA:571,0 -DA:574,0 -DA:581,0 -DA:582,0 -LF:233 -LH:1 +LF:58 +LH:6 +end_of_record +SF:lib/helpers/memory_image_provider.dart +DA:12,1 +DA:20,1 +DA:22,2 +DA:23,1 +DA:25,1 +DA:27,1 +DA:28,1 +DA:29,1 +DA:30,2 +LF:9 +LH:9 end_of_record diff --git a/example/.firebaserc b/packages/superdeck/example/.firebaserc similarity index 100% rename from example/.firebaserc rename to packages/superdeck/example/.firebaserc diff --git a/example/.gitignore b/packages/superdeck/example/.gitignore similarity index 100% rename from example/.gitignore rename to packages/superdeck/example/.gitignore diff --git a/.markdownlint.yaml b/packages/superdeck/example/.markdownlint.yaml similarity index 100% rename from .markdownlint.yaml rename to packages/superdeck/example/.markdownlint.yaml diff --git a/packages/superdeck/example/.metadata b/packages/superdeck/example/.metadata new file mode 100644 index 00000000..90eabcff --- /dev/null +++ b/packages/superdeck/example/.metadata @@ -0,0 +1,45 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819" + channel: "stable" + +project_type: app + +# Tracks metadata for the flutter migrate command +migration: + platforms: + - platform: root + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: android + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: ios + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: linux + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: macos + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: web + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + - platform: windows + create_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + base_revision: 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819 + + # User provided section + + # List of Local paths (relative to this file) that should be + # ignored by the migrate tool. + # + # Files that are not part of the templates will be ignored by default. + unmanaged_files: + - 'lib/main.dart' + - 'ios/Runner.xcodeproj/project.pbxproj' diff --git a/packages/superdeck/example/.superdeck/generated/image_caching_c5tnibJL.gif b/packages/superdeck/example/.superdeck/generated/image_caching_c5tnibJL.gif new file mode 100644 index 00000000..87935e86 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/image_caching_c5tnibJL.gif differ diff --git a/packages/superdeck/example/.superdeck/generated/image_caching_woxVKegn.gif b/packages/superdeck/example/.superdeck/generated/image_caching_woxVKegn.gif new file mode 100644 index 00000000..aaabb82d Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/image_caching_woxVKegn.gif differ diff --git a/packages/superdeck/example/.superdeck/generated/mermaid_s3Iic43G.png b/packages/superdeck/example/.superdeck/generated/mermaid_s3Iic43G.png new file mode 100644 index 00000000..44cc94d2 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/mermaid_s3Iic43G.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_9aLrluQc.png b/packages/superdeck/example/.superdeck/generated/thumbnail_9aLrluQc.png new file mode 100644 index 00000000..31336ca3 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_9aLrluQc.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_AbUkCAD1.png b/packages/superdeck/example/.superdeck/generated/thumbnail_AbUkCAD1.png new file mode 100644 index 00000000..8907df88 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_AbUkCAD1.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_HR8xUsF0.png b/packages/superdeck/example/.superdeck/generated/thumbnail_HR8xUsF0.png new file mode 100644 index 00000000..3d0120a3 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_HR8xUsF0.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_RiJb4GGG.png b/packages/superdeck/example/.superdeck/generated/thumbnail_RiJb4GGG.png new file mode 100644 index 00000000..80a38e8e Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_RiJb4GGG.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_bztq1AdA.png b/packages/superdeck/example/.superdeck/generated/thumbnail_bztq1AdA.png new file mode 100644 index 00000000..64f04f2d Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_bztq1AdA.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_hhnMdIWj.png b/packages/superdeck/example/.superdeck/generated/thumbnail_hhnMdIWj.png new file mode 100644 index 00000000..9f452379 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_hhnMdIWj.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_kXnyZsjq.png b/packages/superdeck/example/.superdeck/generated/thumbnail_kXnyZsjq.png new file mode 100644 index 00000000..baf4100c Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_kXnyZsjq.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_nuiio8i0.png b/packages/superdeck/example/.superdeck/generated/thumbnail_nuiio8i0.png new file mode 100644 index 00000000..62ce9e2f Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_nuiio8i0.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_q6arHVVz.png b/packages/superdeck/example/.superdeck/generated/thumbnail_q6arHVVz.png new file mode 100644 index 00000000..2f0e3424 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_q6arHVVz.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_shPxXVHO.png b/packages/superdeck/example/.superdeck/generated/thumbnail_shPxXVHO.png new file mode 100644 index 00000000..51e344c6 Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_shPxXVHO.png differ diff --git a/packages/superdeck/example/.superdeck/generated/thumbnail_xRHNCnNN.png b/packages/superdeck/example/.superdeck/generated/thumbnail_xRHNCnNN.png new file mode 100644 index 00000000..46baf94e Binary files /dev/null and b/packages/superdeck/example/.superdeck/generated/thumbnail_xRHNCnNN.png differ diff --git a/packages/superdeck/example/.superdeck/slides.json b/packages/superdeck/example/.superdeck/slides.json new file mode 100644 index 00000000..a2fa57e5 --- /dev/null +++ b/packages/superdeck/example/.superdeck/slides.json @@ -0,0 +1,205 @@ +{ + "config": { + "transition": { + "type": "fade_in", + "duration": 0 + } + }, + "slides": [ + { + "style": "quote", + "layout": "image", + "options": { + "src": "https://picsum.photos/600/600.webp", + "fit": "cover" + }, + "content": "> Create your Flutter presentations faster and easier than ever.\n> You can quote me on that\n> ### Leo Farias", + "content_options": { + "alignment": "bottom_right" + }, + "key": "HR8xUsF0" + }, + { + "background": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif", + "style": "cover", + "content": "# Complex layout", + "key": "RiJb4GGG" + }, + { + "layout": "image", + "content": "## Image Layout\n\nCreate beautiful slides with images that fit your content.\n\n##### Options\n```yaml\ncontent:\noptions:\n src: https//www.url.com/image.jpg\n fit: cover\n position: left\n flex: 1\n```\n\n> Define position fit and flex options for the image.", + "style": "show_sections", + "options": { + "src": "https://picsum.photos/900/700?waves", + "fit": "cover", + "position": "left", + "flex": 1 + }, + "content_options": { + "alignment": "bottom_right", + "flex": 1 + }, + "key": "hhnMdIWj" + }, + { + "layout": "two_column", + "style": "show_sections", + "sections": { + "left": { + "flex": 2 + }, + "right": { + "alignment": "bottom_left" + } + }, + "content": "::left::\n\n# Two Column\n\nThis is a two-column layout. You can use it to compare two different concepts or ideas.\n\n::right::\n\n### Section Options\n\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n\n\n```yaml\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n```", + "key": "kXnyZsjq" + }, + { + "layout": "two_column_header", + "content": "# Two Column + Header\n\n\n::left::\n\n### Left Section\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n::right::\n\n#### Section Options\n\n```yaml\nsections:\n left:\n alignment: bottom_right\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\n```", + "sections": { + "left": { + "flex": 2 + }, + "right": { + "alignment": "bottom_left" + }, + "header": { + "alignment": "bottom_left" + } + }, + "style": "show_sections", + "content_options": { + "alignment": "center", + "flex": 2 + }, + "key": "bztq1AdA" + }, + { + "style": "rad", + "layout": "two_column", + "content": "# Mix\n\nIntegration with Mix gives you complete control over all styling elements in your slides with a simple and intuitive API.\n\n::right::\n\n```dart\nVariantAttribute get radStyle {\n return const SlideVariant('rad')(\n $.h1.textStyle.as(GoogleFonts.poppins()),\n $.h1.textStyle.fontSize(140),\n $.code.decoration.border.all(\n color: Colors.red,\n width: 3,\n ),\n $.code.decoration(\n color: Colors.black54,\n ),\n $.code.padding.all(40),\n\n $.outerContainer.margin.all(60),\n\n $.innerContainer.borderRadius(25),\n $.innerContainer.shadow(\n blurRadius: 0,\n spreadRadius: 10,\n color: Colors.red.withOpacity(1),\n ),\n $.innerContainer.gradient.radial(\n stops: [0.0, 1.0],\n radius: 0.7,\n colors: [Colors.purple, Colors.deepPurple],\n ),\n\n // Events\n onMouseHover((event) {\n final position = event.position;\n final dx = position.x * 10;\n final dy = position.y * 10;\n\n return Style(\n $.innerContainer.transform(_transformMatrix(position)),\n $.innerContainer.shadow.offset(dx, dy),\n $.innerContainer.gradient.radial(\n center: position,\n ),\n );\n }),\n\n (onPressed | onLongPressed)(\n $.innerContainer.shadow(\n blurRadius: 5,\n spreadRadius: 1,\n offset: Offset.zero,\n color: Colors.purpleAccent,\n ),\n $.innerContainer.border.all(color: Colors.white, width: 1),\n $.innerContainer.gradient.radial\n .colors([Colors.purpleAccent, Colors.purpleAccent]),\n ),\n );\n}\n\n```", + "sections": { + "left": null, + "right": { + "alignment": "bottom_left", + "flex": 2 + } + }, + "content_options": { + "alignment": "center" + }, + "key": "AbUkCAD1" + }, + { + "style": "cover", + "background": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif", + "content": "# Markdown support", + "key": "q6arHVVz" + }, + { + "style": "show_sections", + "layout": "two_column", + "sections": null, + "content": "::left::\n\n\n**Bold Text**\n\n*Italic Text*\n\n~~Strikethrough~~\n\n`Inline Code`\n\n[Link here](https://github.com/leoafarias/superdeck)\n\n::right::\n\nLists\n\n1. Ordered list item 1\n2. Ordered list item 2\n\n- Unordered list item 1\n- Unordered list item 2\n\nQuotes\n\n> If you want to go fast, go alone. \n> If you want to go far, go together.\n> ### African Proverb", + "content_options": { + "flex": 4 + }, + "key": "shPxXVHO" + }, + { + "layout": "two_column", + "content": "::left::\n\n\nCode\n```dart\nint factorial(int n) {\n return n == 0 ? 1 : n * factorial(n - 1);\n}\n\n```\n\nTasks\n- [ ] Item 1\n- [x] Item 2\n\nSubtasks\n\n- [x] Item 1\n - [ ] Subitem 1\n\n::right::\n\nImages\n![Unsplash Image](https://picsum.photos/300/200?landscape)\n\n\nTable\n\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1A | Cell 1B |\n| Cell 2A | Cell 2B |\n\nDivider\n\n___", + "key": "nuiio8i0" + }, + { + "title": "Mermaid example", + "layout": "two_column", + "content": "::left::\n\n![Mermaid Diagram](.superdeck/generated/mermaid_s3Iic43G.png)\n \n\n::right::\n\n## Mermaid Support\n\nSuperdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation.", + "key": "9aLrluQc" + }, + { + "layout": "widget", + "options": { + "name": "demo", + "args": { + "text": "Hello, Superdeck!", + "height": 200.0, + "width": 300.0 + } + }, + "content": "## Showcase your widgets", + "key": "xRHNCnNN" + } + ], + "assets": [ + { + "path": ".superdeck/generated/thumbnail_RiJb4GGG.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/image_caching_woxVKegn.gif", + "width": 500, + "height": 500, + "reference": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif" + }, + { + "path": ".superdeck/generated/thumbnail_hhnMdIWj.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_kXnyZsjq.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_bztq1AdA.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_AbUkCAD1.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_q6arHVVz.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/image_caching_c5tnibJL.gif", + "width": 270, + "height": 480, + "reference": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif" + }, + { + "path": ".superdeck/generated/thumbnail_shPxXVHO.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_nuiio8i0.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/mermaid_s3Iic43G.png", + "width": 600, + "height": 866 + }, + { + "path": ".superdeck/generated/thumbnail_9aLrluQc.png", + "width": 512, + "height": 288 + }, + { + "path": ".superdeck/generated/thumbnail_xRHNCnNN.png", + "width": 512, + "height": 288 + } + ] +} \ No newline at end of file diff --git a/example/.vscode/launch.json b/packages/superdeck/example/.vscode/launch.json similarity index 100% rename from example/.vscode/launch.json rename to packages/superdeck/example/.vscode/launch.json diff --git a/example/README.md b/packages/superdeck/example/README.md similarity index 100% rename from example/README.md rename to packages/superdeck/example/README.md diff --git a/example/analysis_options.yaml b/packages/superdeck/example/analysis_options.yaml similarity index 100% rename from example/analysis_options.yaml rename to packages/superdeck/example/analysis_options.yaml diff --git a/example/android/.gitignore b/packages/superdeck/example/android/.gitignore similarity index 100% rename from example/android/.gitignore rename to packages/superdeck/example/android/.gitignore diff --git a/packages/superdeck/example/android/app/build.gradle b/packages/superdeck/example/android/app/build.gradle new file mode 100644 index 00000000..2a2d082b --- /dev/null +++ b/packages/superdeck/example/android/app/build.gradle @@ -0,0 +1,58 @@ +plugins { + id "com.android.application" + id "kotlin-android" + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id "dev.flutter.flutter-gradle-plugin" +} + +def localProperties = new Properties() +def localPropertiesFile = rootProject.file("local.properties") +if (localPropertiesFile.exists()) { + localPropertiesFile.withReader("UTF-8") { reader -> + localProperties.load(reader) + } +} + +def flutterVersionCode = localProperties.getProperty("flutter.versionCode") +if (flutterVersionCode == null) { + flutterVersionCode = "1" +} + +def flutterVersionName = localProperties.getProperty("flutter.versionName") +if (flutterVersionName == null) { + flutterVersionName = "1.0" +} + +android { + namespace = "com.example.example" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.example" + // You can update the following values to match your application needs. + // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. + minSdk = flutter.minSdkVersion + targetSdk = flutter.targetSdkVersion + versionCode = flutterVersionCode.toInteger() + versionName = flutterVersionName + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.debug + } + } +} + +flutter { + source = "../.." +} diff --git a/example/android/app/src/debug/AndroidManifest.xml b/packages/superdeck/example/android/app/src/debug/AndroidManifest.xml similarity index 100% rename from example/android/app/src/debug/AndroidManifest.xml rename to packages/superdeck/example/android/app/src/debug/AndroidManifest.xml diff --git a/example/android/app/src/main/AndroidManifest.xml b/packages/superdeck/example/android/app/src/main/AndroidManifest.xml similarity index 72% rename from example/android/app/src/main/AndroidManifest.xml rename to packages/superdeck/example/android/app/src/main/AndroidManifest.xml index 27080fd4..74a78b93 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/packages/superdeck/example/android/app/src/main/AndroidManifest.xml @@ -1,12 +1,13 @@ + + + + + + + diff --git a/packages/superdeck/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt b/packages/superdeck/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt new file mode 100644 index 00000000..70f8f08f --- /dev/null +++ b/packages/superdeck/example/android/app/src/main/kotlin/com/example/example/MainActivity.kt @@ -0,0 +1,5 @@ +package com.example.example + +import io.flutter.embedding.android.FlutterActivity + +class MainActivity: FlutterActivity() diff --git a/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/superdeck/example/android/app/src/main/res/drawable-v21/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable-v21/launch_background.xml rename to packages/superdeck/example/android/app/src/main/res/drawable-v21/launch_background.xml diff --git a/example/android/app/src/main/res/drawable/launch_background.xml b/packages/superdeck/example/android/app/src/main/res/drawable/launch_background.xml similarity index 100% rename from example/android/app/src/main/res/drawable/launch_background.xml rename to packages/superdeck/example/android/app/src/main/res/drawable/launch_background.xml diff --git a/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/superdeck/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png rename to packages/superdeck/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/superdeck/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png rename to packages/superdeck/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/superdeck/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png rename to packages/superdeck/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/superdeck/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png rename to packages/superdeck/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/superdeck/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png similarity index 100% rename from example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png rename to packages/superdeck/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png diff --git a/example/android/app/src/main/res/values-night/styles.xml b/packages/superdeck/example/android/app/src/main/res/values-night/styles.xml similarity index 100% rename from example/android/app/src/main/res/values-night/styles.xml rename to packages/superdeck/example/android/app/src/main/res/values-night/styles.xml diff --git a/example/android/app/src/main/res/values/styles.xml b/packages/superdeck/example/android/app/src/main/res/values/styles.xml similarity index 100% rename from example/android/app/src/main/res/values/styles.xml rename to packages/superdeck/example/android/app/src/main/res/values/styles.xml diff --git a/example/android/app/src/profile/AndroidManifest.xml b/packages/superdeck/example/android/app/src/profile/AndroidManifest.xml similarity index 100% rename from example/android/app/src/profile/AndroidManifest.xml rename to packages/superdeck/example/android/app/src/profile/AndroidManifest.xml diff --git a/packages/superdeck/example/android/build.gradle b/packages/superdeck/example/android/build.gradle new file mode 100644 index 00000000..d2ffbffa --- /dev/null +++ b/packages/superdeck/example/android/build.gradle @@ -0,0 +1,18 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +rootProject.buildDir = "../build" +subprojects { + project.buildDir = "${rootProject.buildDir}/${project.name}" +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean", Delete) { + delete rootProject.buildDir +} diff --git a/packages/superdeck/example/android/gradle.properties b/packages/superdeck/example/android/gradle.properties new file mode 100644 index 00000000..3b5b324f --- /dev/null +++ b/packages/superdeck/example/android/gradle.properties @@ -0,0 +1,3 @@ +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError +android.useAndroidX=true +android.enableJetifier=true diff --git a/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/superdeck/example/android/gradle/wrapper/gradle-wrapper.properties similarity index 92% rename from example/android/gradle/wrapper/gradle-wrapper.properties rename to packages/superdeck/example/android/gradle/wrapper/gradle-wrapper.properties index 3c472b99..e1ca574e 100644 --- a/example/android/gradle/wrapper/gradle-wrapper.properties +++ b/packages/superdeck/example/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip diff --git a/example/android/settings.gradle b/packages/superdeck/example/android/settings.gradle similarity index 50% rename from example/android/settings.gradle rename to packages/superdeck/example/android/settings.gradle index 55c4ca8b..536165d3 100644 --- a/example/android/settings.gradle +++ b/packages/superdeck/example/android/settings.gradle @@ -5,16 +5,21 @@ pluginManagement { def flutterSdkPath = properties.getProperty("flutter.sdk") assert flutterSdkPath != null, "flutter.sdk not set in local.properties" return flutterSdkPath - } - settings.ext.flutterSdkPath = flutterSdkPath() + }() - includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") - plugins { - id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false + repositories { + google() + mavenCentral() + gradlePluginPortal() } } -include ":app" +plugins { + id "dev.flutter.flutter-plugin-loader" version "1.0.0" + id "com.android.application" version "7.3.0" apply false + id "org.jetbrains.kotlin.android" version "1.7.10" apply false +} -apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" +include ":app" diff --git a/example/devtools_options.yaml b/packages/superdeck/example/devtools_options.yaml similarity index 100% rename from example/devtools_options.yaml rename to packages/superdeck/example/devtools_options.yaml diff --git a/example/firebase.json b/packages/superdeck/example/firebase.json similarity index 100% rename from example/firebase.json rename to packages/superdeck/example/firebase.json diff --git a/example/ios/.gitignore b/packages/superdeck/example/ios/.gitignore similarity index 100% rename from example/ios/.gitignore rename to packages/superdeck/example/ios/.gitignore diff --git a/example/ios/Flutter/AppFrameworkInfo.plist b/packages/superdeck/example/ios/Flutter/AppFrameworkInfo.plist similarity index 100% rename from example/ios/Flutter/AppFrameworkInfo.plist rename to packages/superdeck/example/ios/Flutter/AppFrameworkInfo.plist diff --git a/example/ios/Flutter/Debug.xcconfig b/packages/superdeck/example/ios/Flutter/Debug.xcconfig similarity index 100% rename from example/ios/Flutter/Debug.xcconfig rename to packages/superdeck/example/ios/Flutter/Debug.xcconfig diff --git a/example/ios/Flutter/Release.xcconfig b/packages/superdeck/example/ios/Flutter/Release.xcconfig similarity index 100% rename from example/ios/Flutter/Release.xcconfig rename to packages/superdeck/example/ios/Flutter/Release.xcconfig diff --git a/example/ios/Podfile b/packages/superdeck/example/ios/Podfile similarity index 100% rename from example/ios/Podfile rename to packages/superdeck/example/ios/Podfile diff --git a/example/ios/Podfile.lock b/packages/superdeck/example/ios/Podfile.lock similarity index 88% rename from example/ios/Podfile.lock rename to packages/superdeck/example/ios/Podfile.lock index a624b610..84eed3eb 100644 --- a/example/ios/Podfile.lock +++ b/packages/superdeck/example/ios/Podfile.lock @@ -33,6 +33,8 @@ PODS: - file_picker (0.0.1): - DKImagePickerController/PhotoGallery - Flutter + - file_saver (0.0.1): + - Flutter - Flutter (1.0.0) - path_provider_foundation (0.0.1): - Flutter @@ -49,6 +51,7 @@ PODS: DEPENDENCIES: - file_picker (from `.symlinks/plugins/file_picker/ios`) + - file_saver (from `.symlinks/plugins/file_saver/ios`) - Flutter (from `Flutter`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/darwin`) @@ -64,6 +67,8 @@ SPEC REPOS: EXTERNAL SOURCES: file_picker: :path: ".symlinks/plugins/file_picker/ios" + file_saver: + :path: ".symlinks/plugins/file_saver/ios" Flutter: :path: Flutter path_provider_foundation: @@ -77,12 +82,13 @@ SPEC CHECKSUMS: DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60 file_picker: 09aa5ec1ab24135ccd7a1621c46c84134bfd6655 + file_saver: 503e386464dbe118f630e17b4c2e1190fa0cf808 Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 SDWebImage: 981fd7e860af070920f249fd092420006014c3eb sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4 - url_launcher_ios: 6116280ddcfe98ab8820085d8d76ae7449447586 + url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/packages/superdeck/example/ios/Runner.xcodeproj/project.pbxproj similarity index 89% rename from example/ios/Runner.xcodeproj/project.pbxproj rename to packages/superdeck/example/ios/Runner.xcodeproj/project.pbxproj index a71eaa3e..bc8f4877 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/packages/superdeck/example/ios/Runner.xcodeproj/project.pbxproj @@ -10,12 +10,12 @@ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; + 670C29666A950342EB28E12D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 16308D384DED0ADBA35F6B73 /* Pods_Runner.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 78D5B995AF42B8CA0FB6C453 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4A1B4336BF3609AC3A0E65B2 /* Pods_RunnerTests.framework */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - 9AD202AF3B7879A13266BF57 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B13CDD2AFAEA374BB3E54BA7 /* Pods_RunnerTests.framework */; }; - EC290F340C4325E803F74921 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6548D2F36EF977C733FCC0E3 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -44,15 +44,16 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 1DC2D703FE037C9843F06135 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + 16308D384DED0ADBA35F6B73 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 22528DAC27A9B2F331ECBD8D /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; - 380EE12C7C16BB182FE293F1 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + 386C428226C5D7E3E0E095C9 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; - 491DA14EC605BBC27519F135 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; - 6548D2F36EF977C733FCC0E3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 4A1B4336BF3609AC3A0E65B2 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 79AFD802B83878E2675FBFCF /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; @@ -61,18 +62,17 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - B13CDD2AFAEA374BB3E54BA7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - EF9F23793DED80D17AF8253F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - FAC3E5F67F51DF220D231E02 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - FD014F9ACD120C75B11FCA3E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + A0B45CE0EFE389E99FC51BF1 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; + B08EDFBD01FC3FDB59280474 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + CA026C14BC6F4F3C81DD48A0 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ - 1D0F012EA9FCD89C8466327D /* Frameworks */ = { + 334F36450A62B78C93AE3299 /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 9AD202AF3B7879A13266BF57 /* Pods_RunnerTests.framework in Frameworks */, + 78D5B995AF42B8CA0FB6C453 /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -80,27 +80,13 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - EC290F340C4325E803F74921 /* Pods_Runner.framework in Frameworks */, + 670C29666A950342EB28E12D /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 0BB19CF16B93C9B578B08B7B /* Pods */ = { - isa = PBXGroup; - children = ( - EF9F23793DED80D17AF8253F /* Pods-Runner.debug.xcconfig */, - 380EE12C7C16BB182FE293F1 /* Pods-Runner.release.xcconfig */, - 1DC2D703FE037C9843F06135 /* Pods-Runner.profile.xcconfig */, - FD014F9ACD120C75B11FCA3E /* Pods-RunnerTests.debug.xcconfig */, - FAC3E5F67F51DF220D231E02 /* Pods-RunnerTests.release.xcconfig */, - 491DA14EC605BBC27519F135 /* Pods-RunnerTests.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; 331C8082294A63A400263BE5 /* RunnerTests */ = { isa = PBXGroup; children = ( @@ -109,15 +95,6 @@ path = RunnerTests; sourceTree = ""; }; - 5ED6059EED7157F0D5C37E2F /* Frameworks */ = { - isa = PBXGroup; - children = ( - 6548D2F36EF977C733FCC0E3 /* Pods_Runner.framework */, - B13CDD2AFAEA374BB3E54BA7 /* Pods_RunnerTests.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -136,8 +113,8 @@ 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, 331C8082294A63A400263BE5 /* RunnerTests */, - 0BB19CF16B93C9B578B08B7B /* Pods */, - 5ED6059EED7157F0D5C37E2F /* Frameworks */, + D64547297FA8EA027BF2B50F /* Pods */, + D7B51951A935828BF40EB5E8 /* Frameworks */, ); sourceTree = ""; }; @@ -165,6 +142,29 @@ path = Runner; sourceTree = ""; }; + D64547297FA8EA027BF2B50F /* Pods */ = { + isa = PBXGroup; + children = ( + CA026C14BC6F4F3C81DD48A0 /* Pods-Runner.debug.xcconfig */, + B08EDFBD01FC3FDB59280474 /* Pods-Runner.release.xcconfig */, + 79AFD802B83878E2675FBFCF /* Pods-Runner.profile.xcconfig */, + A0B45CE0EFE389E99FC51BF1 /* Pods-RunnerTests.debug.xcconfig */, + 386C428226C5D7E3E0E095C9 /* Pods-RunnerTests.release.xcconfig */, + 22528DAC27A9B2F331ECBD8D /* Pods-RunnerTests.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; + D7B51951A935828BF40EB5E8 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 16308D384DED0ADBA35F6B73 /* Pods_Runner.framework */, + 4A1B4336BF3609AC3A0E65B2 /* Pods_RunnerTests.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -172,10 +172,10 @@ isa = PBXNativeTarget; buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - 5E4E946CBCB5251AFDF2915C /* [CP] Check Pods Manifest.lock */, + 955165415BDE0B6FC2A9C44B /* [CP] Check Pods Manifest.lock */, 331C807D294A63A400263BE5 /* Sources */, 331C807F294A63A400263BE5 /* Resources */, - 1D0F012EA9FCD89C8466327D /* Frameworks */, + 334F36450A62B78C93AE3299 /* Frameworks */, ); buildRules = ( ); @@ -191,14 +191,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - FF6D87DC0BCAF554672374D2 /* [CP] Check Pods Manifest.lock */, + 4C750108331A8394CD426D96 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, - E685C8D1D6C544059EE9B3D6 /* [CP] Embed Pods Frameworks */, + F6F22B48ABA57741F0FE11D5 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -286,7 +286,7 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 5E4E946CBCB5251AFDF2915C /* [CP] Check Pods Manifest.lock */ = { + 4C750108331A8394CD426D96 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -301,65 +301,65 @@ outputFileListPaths = ( ); outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - 9740EEB61CF901F6004384FC /* Run Script */ = { + 955165415BDE0B6FC2A9C44B /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; - alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); + inputFileListPaths = ( + ); inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( ); - name = "Run Script"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; }; - E685C8D1D6C544059EE9B3D6 /* [CP] Embed Pods Frameworks */ = { + 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", + inputPaths = ( ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", + name = "Run Script"; + outputPaths = ( ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; + shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; }; - FF6D87DC0BCAF554672374D2 /* [CP] Check Pods Manifest.lock */ = { + F6F22B48ABA57741F0FE11D5 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; + name = "[CP] Embed Pods Frameworks"; outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; /* End PBXShellScriptBuildPhase section */ @@ -416,6 +416,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -445,6 +446,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -469,13 +471,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = RW3X256N25; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; @@ -485,14 +488,14 @@ }; 331C8088294A63A400263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FD014F9ACD120C75B11FCA3E /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = A0B45CE0EFE389E99FC51BF1 /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -503,14 +506,14 @@ }; 331C8089294A63A400263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = FAC3E5F67F51DF220D231E02 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = 386C428226C5D7E3E0E095C9 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -519,14 +522,14 @@ }; 331C808A294A63A400263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 491DA14EC605BBC27519F135 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = 22528DAC27A9B2F331ECBD8D /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; @@ -537,6 +540,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -566,6 +570,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -592,6 +597,7 @@ isa = XCBuildConfiguration; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; CLANG_CXX_LIBRARY = "libc++"; @@ -621,6 +627,7 @@ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -647,13 +654,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = RW3X256N25; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; @@ -669,13 +677,14 @@ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; CLANG_ENABLE_MODULES = YES; CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; + DEVELOPMENT_TEAM = RW3X256N25; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", ); - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_VERSION = 5.0; diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/superdeck/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/superdeck/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 100% rename from example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/superdeck/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/superdeck/example/ios/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/ios/Runner.xcworkspace/contents.xcworkspacedata rename to packages/superdeck/example/ios/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/superdeck/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/superdeck/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/superdeck/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to packages/superdeck/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/example/ios/Runner/AppDelegate.swift b/packages/superdeck/example/ios/Runner/AppDelegate.swift similarity index 95% rename from example/ios/Runner/AppDelegate.swift rename to packages/superdeck/example/ios/Runner/AppDelegate.swift index 70693e4a..62666446 100644 --- a/example/ios/Runner/AppDelegate.swift +++ b/packages/superdeck/example/ios/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ -import UIKit import Flutter +import UIKit -@UIApplicationMain +@main @objc class AppDelegate: FlutterAppDelegate { override func application( _ application: UIApplication, diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..d36b1fab --- /dev/null +++ b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "20x20", + "idiom" : "iphone", + "filename" : "Icon-App-20x20@3x.png", + "scale" : "3x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "iphone", + "filename" : "Icon-App-29x29@3x.png", + "scale" : "3x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "iphone", + "filename" : "Icon-App-40x40@3x.png", + "scale" : "3x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@2x.png", + "scale" : "2x" + }, + { + "size" : "60x60", + "idiom" : "iphone", + "filename" : "Icon-App-60x60@3x.png", + "scale" : "3x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@1x.png", + "scale" : "1x" + }, + { + "size" : "20x20", + "idiom" : "ipad", + "filename" : "Icon-App-20x20@2x.png", + "scale" : "2x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@1x.png", + "scale" : "1x" + }, + { + "size" : "29x29", + "idiom" : "ipad", + "filename" : "Icon-App-29x29@2x.png", + "scale" : "2x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@1x.png", + "scale" : "1x" + }, + { + "size" : "40x40", + "idiom" : "ipad", + "filename" : "Icon-App-40x40@2x.png", + "scale" : "2x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@1x.png", + "scale" : "1x" + }, + { + "size" : "76x76", + "idiom" : "ipad", + "filename" : "Icon-App-76x76@2x.png", + "scale" : "2x" + }, + { + "size" : "83.5x83.5", + "idiom" : "ipad", + "filename" : "Icon-App-83.5x83.5@2x.png", + "scale" : "2x" + }, + { + "size" : "1024x1024", + "idiom" : "ios-marketing", + "filename" : "Icon-App-1024x1024@1x.png", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png new file mode 100644 index 00000000..dc9ada47 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..7353c41e Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..797d452e Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..6ed2d933 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..4cd7b009 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..fe730945 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..321773cd Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..797d452e Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..502f463a Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..0ec30343 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..0ec30343 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..e9f5fea2 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..84ac32ae Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..8953cba0 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..0467bf12 Binary files /dev/null and b/packages/superdeck/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json rename to packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png rename to packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png rename to packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png rename to packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md similarity index 100% rename from example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md rename to packages/superdeck/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/superdeck/example/ios/Runner/Base.lproj/LaunchScreen.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/LaunchScreen.storyboard rename to packages/superdeck/example/ios/Runner/Base.lproj/LaunchScreen.storyboard diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/packages/superdeck/example/ios/Runner/Base.lproj/Main.storyboard similarity index 100% rename from example/ios/Runner/Base.lproj/Main.storyboard rename to packages/superdeck/example/ios/Runner/Base.lproj/Main.storyboard diff --git a/example/ios/Runner/Info.plist b/packages/superdeck/example/ios/Runner/Info.plist similarity index 96% rename from example/ios/Runner/Info.plist rename to packages/superdeck/example/ios/Runner/Info.plist index 742978e8..5458fc41 100644 --- a/example/ios/Runner/Info.plist +++ b/packages/superdeck/example/ios/Runner/Info.plist @@ -5,7 +5,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Dash Deck Demo + Example CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier @@ -13,7 +13,7 @@ CFBundleInfoDictionaryVersion 6.0 CFBundleName - superdeck_demo + example CFBundlePackageType APPL CFBundleShortVersionString diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/packages/superdeck/example/ios/Runner/Runner-Bridging-Header.h similarity index 100% rename from example/ios/Runner/Runner-Bridging-Header.h rename to packages/superdeck/example/ios/Runner/Runner-Bridging-Header.h diff --git a/example/ios/RunnerTests/RunnerTests.swift b/packages/superdeck/example/ios/RunnerTests/RunnerTests.swift similarity index 100% rename from example/ios/RunnerTests/RunnerTests.swift rename to packages/superdeck/example/ios/RunnerTests/RunnerTests.swift diff --git a/packages/superdeck/example/lib/main.dart b/packages/superdeck/example/lib/main.dart new file mode 100644 index 00000000..d1c5004c --- /dev/null +++ b/packages/superdeck/example/lib/main.dart @@ -0,0 +1,29 @@ +import 'package:flutter/material.dart'; +import 'package:superdeck/superdeck.dart'; + +import 'src/style.dart'; +import 'src/widget/mix_demo.dart'; + +void main() async { + await SuperDeckApp.initialize(); + runApp( + Builder(builder: (context) { + return MaterialApp( + title: 'Superdeck', + debugShowCheckedModeBanner: false, + home: SuperDeckApp( + styles: { + 'rad': radStyle, + 'custom': customStyle, + 'cover': coverStyle, + 'announcement': announcementStyle, + 'quote': quoteStyle, + 'show_sections': showSectionsStyle, + }, + // ignore: prefer_const_literals_to_create_immutables + examples: {'demo': mixExampleBuilder}, + ), + ); + }), + ); +} diff --git a/example/lib/src/style.dart b/packages/superdeck/example/lib/src/style.dart similarity index 86% rename from example/lib/src/style.dart rename to packages/superdeck/example/lib/src/style.dart index a1066fd3..6ee08e0c 100644 --- a/example/lib/src/style.dart +++ b/packages/superdeck/example/lib/src/style.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; -import 'package:superdeck/styles/style_util.dart'; import 'package:superdeck/superdeck.dart'; final _util = SlideSpecUtility.self; @@ -21,8 +20,8 @@ final _contentContainer = _util.contentContainer; final _textStyle = _util.textStyle; final _innerContainer = _util.innerContainer; -VariantAttribute get radStyle { - return const SlideVariant('rad')( +Style get radStyle { + return Style( _h1.textStyle.as(GoogleFonts.poppins()), _h1.textStyle.fontSize(140), _code.decoration.border.all( @@ -74,8 +73,8 @@ VariantAttribute get radStyle { ); } -VariantAttribute get customStyle { - return const SlideVariant('custom')( +Style get customStyle { + return Style( _textStyle.as(GoogleFonts.poppins()), _h1.textStyle.as(GoogleFonts.smooch()), _h1.textStyle.fontSize(200), @@ -115,8 +114,8 @@ VariantAttribute get customStyle { ); } -VariantAttribute get coverStyle { - return const SlideVariant('cover')( +Style get coverStyle { + return Style( _h1.textStyle.as(GoogleFonts.poppins()), _h1.textStyle.fontSize(100), _contentContainer.gradient.linear( @@ -130,8 +129,8 @@ VariantAttribute get coverStyle { ); } -VariantAttribute get announcementStyle { - return const SlideVariant('announcement')( +Style get announcementStyle { + return Style( _textStyle.height(0.6), _h1.textStyle.fontSize(140), _h1.textStyle.bold(), @@ -151,8 +150,8 @@ VariantAttribute get announcementStyle { ); } -VariantAttribute get quoteStyle { - return const SlideVariant('quote')( +Style get quoteStyle { + return Style( _blockquote.textStyle.as(GoogleFonts.notoSerif()), _blockquote.decoration.border.left( width: 4, @@ -164,8 +163,8 @@ VariantAttribute get quoteStyle { ); } -VariantAttribute get showSectionsStyle { - return const SlideVariant('show_sections')( +Style get showSectionsStyle { + return Style( _contentContainer.border.all( color: Colors.blue, width: 2, @@ -173,18 +172,6 @@ VariantAttribute get showSectionsStyle { ); } -Style get style { - return Style( - _textStyle.as(GoogleFonts.poppins()), - customStyle, - quoteStyle, - showSectionsStyle, - coverStyle, - announcementStyle, - radStyle, - ).animate(); -} - Matrix4 _transformMatrix(Alignment alignment) { final double rotateX = alignment.y * 0.2; final double rotateY = -alignment.x * 0.2; diff --git a/example/lib/src/version.dart b/packages/superdeck/example/lib/src/version.dart similarity index 100% rename from example/lib/src/version.dart rename to packages/superdeck/example/lib/src/version.dart diff --git a/example/lib/src/widget/mix_demo.dart b/packages/superdeck/example/lib/src/widget/mix_demo.dart similarity index 82% rename from example/lib/src/widget/mix_demo.dart rename to packages/superdeck/example/lib/src/widget/mix_demo.dart index f13fcb2a..0f8a2ffd 100644 --- a/example/lib/src/widget/mix_demo.dart +++ b/packages/superdeck/example/lib/src/widget/mix_demo.dart @@ -1,6 +1,7 @@ import 'dart:math' as math; import 'package:flutter/material.dart'; +import 'package:superdeck/components/molecules/code_preview.dart'; import 'package:superdeck/schema/schema_model.dart'; import 'package:superdeck/superdeck.dart'; @@ -88,43 +89,35 @@ class ExampleOptions { } static final schema = ArgsSchema( - validator: Schema( + validator: SchemaShape( { - 'height': Schema.double.isRequired(), - 'width': Schema.double.isRequired(), - 'text': Schema.string.isRequired(), + 'height': Schema.double.required(), + 'width': Schema.double.required(), + 'text': Schema.string.required(), }, ), decoder: fromMap, ); } -class MixExample extends ExampleWidget { - MixExample() : super(name: 'mix'); - - @override - ExampleOptions decode(Map args) { - return ExampleOptions.fromMap(args); - } - - @override - final schema = ExampleOptions.schema; - - @override - Widget build(ExampleOptions args) { - return Builder(builder: (context) { +Widget mixExampleBuilder(BuildContext context) { + final options = ExampleOptions.fromMap(context.args); + return Builder( + builder: (context) { return Center( child: Box( style: Style( _style(), - $box.height(args.height), - $box.width(args.width), + $box.height(options.height), + $box.width(options.width), ).animate(), - child: StyledText(args.text ?? 'Mix'), + child: StyledText( + options.text ?? 'Mix', + ), ), ); - }); - } + }, + ); } double _calculateDistance(Alignment alignment) { diff --git a/example/linux/.gitignore b/packages/superdeck/example/linux/.gitignore similarity index 100% rename from example/linux/.gitignore rename to packages/superdeck/example/linux/.gitignore diff --git a/example/linux/CMakeLists.txt b/packages/superdeck/example/linux/CMakeLists.txt similarity index 94% rename from example/linux/CMakeLists.txt rename to packages/superdeck/example/linux/CMakeLists.txt index 3046bd67..9cb0d1dd 100644 --- a/example/linux/CMakeLists.txt +++ b/packages/superdeck/example/linux/CMakeLists.txt @@ -4,10 +4,10 @@ project(runner LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "superdeck_demo") +set(BINARY_NAME "example") # The unique GTK application identifier for this application. See: # https://wiki.gnome.org/HowDoI/ChooseApplicationID -set(APPLICATION_ID "com.example.superdeck_demo") +set(APPLICATION_ID "com.example.example") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -123,6 +123,12 @@ foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) COMPONENT Runtime) endforeach(bundled_library) +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/example/linux/flutter/CMakeLists.txt b/packages/superdeck/example/linux/flutter/CMakeLists.txt similarity index 100% rename from example/linux/flutter/CMakeLists.txt rename to packages/superdeck/example/linux/flutter/CMakeLists.txt diff --git a/example/linux/flutter/generated_plugin_registrant.cc b/packages/superdeck/example/linux/flutter/generated_plugin_registrant.cc similarity index 65% rename from example/linux/flutter/generated_plugin_registrant.cc rename to packages/superdeck/example/linux/flutter/generated_plugin_registrant.cc index 0cacc750..c84268e3 100644 --- a/example/linux/flutter/generated_plugin_registrant.cc +++ b/packages/superdeck/example/linux/flutter/generated_plugin_registrant.cc @@ -6,11 +6,19 @@ #include "generated_plugin_registrant.h" +#include +#include #include #include #include void fl_register_plugins(FlPluginRegistry* registry) { + g_autoptr(FlPluginRegistrar) file_saver_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSaverPlugin"); + file_saver_plugin_register_with_registrar(file_saver_registrar); + g_autoptr(FlPluginRegistrar) file_selector_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin"); + file_selector_plugin_register_with_registrar(file_selector_linux_registrar); g_autoptr(FlPluginRegistrar) screen_retriever_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin"); screen_retriever_plugin_register_with_registrar(screen_retriever_registrar); diff --git a/example/linux/flutter/generated_plugin_registrant.h b/packages/superdeck/example/linux/flutter/generated_plugin_registrant.h similarity index 100% rename from example/linux/flutter/generated_plugin_registrant.h rename to packages/superdeck/example/linux/flutter/generated_plugin_registrant.h diff --git a/example/linux/flutter/generated_plugins.cmake b/packages/superdeck/example/linux/flutter/generated_plugins.cmake similarity index 95% rename from example/linux/flutter/generated_plugins.cmake rename to packages/superdeck/example/linux/flutter/generated_plugins.cmake index 62f151fd..66f0a374 100644 --- a/example/linux/flutter/generated_plugins.cmake +++ b/packages/superdeck/example/linux/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_saver + file_selector_linux screen_retriever url_launcher_linux window_manager diff --git a/example/linux/main.cc b/packages/superdeck/example/linux/main.cc similarity index 100% rename from example/linux/main.cc rename to packages/superdeck/example/linux/main.cc diff --git a/example/linux/my_application.cc b/packages/superdeck/example/linux/my_application.cc similarity index 81% rename from example/linux/my_application.cc rename to packages/superdeck/example/linux/my_application.cc index cda779c1..c0530d42 100644 --- a/example/linux/my_application.cc +++ b/packages/superdeck/example/linux/my_application.cc @@ -40,11 +40,11 @@ static void my_application_activate(GApplication* application) { if (use_header_bar) { GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); gtk_widget_show(GTK_WIDGET(header_bar)); - gtk_header_bar_set_title(header_bar, "superdeck_demo"); + gtk_header_bar_set_title(header_bar, "example"); gtk_header_bar_set_show_close_button(header_bar, TRUE); gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); } else { - gtk_window_set_title(window, "superdeck_demo"); + gtk_window_set_title(window, "example"); } gtk_window_set_default_size(window, 1280, 720); @@ -81,6 +81,24 @@ static gboolean my_application_local_command_line(GApplication* application, gch return TRUE; } +// Implements GApplication::startup. +static void my_application_startup(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application startup. + + G_APPLICATION_CLASS(my_application_parent_class)->startup(application); +} + +// Implements GApplication::shutdown. +static void my_application_shutdown(GApplication* application) { + //MyApplication* self = MY_APPLICATION(object); + + // Perform any actions required at application shutdown. + + G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); +} + // Implements GObject::dispose. static void my_application_dispose(GObject* object) { MyApplication* self = MY_APPLICATION(object); @@ -91,6 +109,8 @@ static void my_application_dispose(GObject* object) { static void my_application_class_init(MyApplicationClass* klass) { G_APPLICATION_CLASS(klass)->activate = my_application_activate; G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; + G_APPLICATION_CLASS(klass)->startup = my_application_startup; + G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; G_OBJECT_CLASS(klass)->dispose = my_application_dispose; } diff --git a/example/linux/my_application.h b/packages/superdeck/example/linux/my_application.h similarity index 100% rename from example/linux/my_application.h rename to packages/superdeck/example/linux/my_application.h diff --git a/example/macos/.gitignore b/packages/superdeck/example/macos/.gitignore similarity index 100% rename from example/macos/.gitignore rename to packages/superdeck/example/macos/.gitignore diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/superdeck/example/macos/Flutter/Flutter-Debug.xcconfig similarity index 100% rename from example/macos/Flutter/Flutter-Debug.xcconfig rename to packages/superdeck/example/macos/Flutter/Flutter-Debug.xcconfig diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/packages/superdeck/example/macos/Flutter/Flutter-Release.xcconfig similarity index 100% rename from example/macos/Flutter/Flutter-Release.xcconfig rename to packages/superdeck/example/macos/Flutter/Flutter-Release.xcconfig diff --git a/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/superdeck/example/macos/Flutter/GeneratedPluginRegistrant.swift similarity index 76% rename from example/macos/Flutter/GeneratedPluginRegistrant.swift rename to packages/superdeck/example/macos/Flutter/GeneratedPluginRegistrant.swift index 0370eb95..811a3726 100644 --- a/example/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/packages/superdeck/example/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,8 @@ import FlutterMacOS import Foundation +import file_saver +import file_selector_macos import path_provider_foundation import screen_retriever import sqflite @@ -12,6 +14,8 @@ import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + FileSaverPlugin.register(with: registry.registrar(forPlugin: "FileSaverPlugin")) + FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) diff --git a/example/macos/Podfile b/packages/superdeck/example/macos/Podfile similarity index 100% rename from example/macos/Podfile rename to packages/superdeck/example/macos/Podfile diff --git a/example/macos/Podfile.lock b/packages/superdeck/example/macos/Podfile.lock similarity index 70% rename from example/macos/Podfile.lock rename to packages/superdeck/example/macos/Podfile.lock index 07031a0f..4365112a 100644 --- a/example/macos/Podfile.lock +++ b/packages/superdeck/example/macos/Podfile.lock @@ -1,4 +1,8 @@ PODS: + - file_saver (0.0.1): + - FlutterMacOS + - file_selector_macos (0.0.1): + - FlutterMacOS - FlutterMacOS (1.0.0) - path_provider_foundation (0.0.1): - Flutter @@ -14,6 +18,8 @@ PODS: - FlutterMacOS DEPENDENCIES: + - file_saver (from `Flutter/ephemeral/.symlinks/plugins/file_saver/macos`) + - file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`) - FlutterMacOS (from `Flutter/ephemeral`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) @@ -22,6 +28,10 @@ DEPENDENCIES: - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) EXTERNAL SOURCES: + file_saver: + :path: Flutter/ephemeral/.symlinks/plugins/file_saver/macos + file_selector_macos: + :path: Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos FlutterMacOS: :path: Flutter/ephemeral path_provider_foundation: @@ -36,11 +46,13 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: + file_saver: 44e6fbf666677faf097302460e214e977fdd977b + file_selector_macos: 54fdab7caa3ac3fc43c9fac4d7d8d231277f8cf2 FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - path_provider_foundation: 3784922295ac71e43754bd15e0653ccfd36a147c + path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec - url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95 + url_launcher_macos: 5f437abeda8c85500ceb03f5c1938a8c5a705399 window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/packages/superdeck/example/macos/Runner.xcodeproj/project.pbxproj similarity index 89% rename from example/macos/Runner.xcodeproj/project.pbxproj rename to packages/superdeck/example/macos/Runner.xcodeproj/project.pbxproj index 1da676c9..652a4230 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/packages/superdeck/example/macos/Runner.xcodeproj/project.pbxproj @@ -27,8 +27,8 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 46B3065E3DC9E36DFAA7E4E5 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1D5B6EE3E4C25EF9B9DC26AB /* Pods_Runner.framework */; }; - CF480946466E3948C153CB7B /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A3EEB72D1DBF34484D4B571 /* Pods_RunnerTests.framework */; }; + D33EC936C6603F092BE164F8 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58206CAA5C9CAE5E71ACD682 /* Pods_Runner.framework */; }; + DC6D6673B8F35A47BEC03AAD /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = C6840468B539A3E04B679E96 /* Pods_RunnerTests.framework */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -62,14 +62,11 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 0B89690350CB7C0BFA12F997 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 1D5B6EE3E4C25EF9B9DC26AB /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 27F821420410E0459CD3AF93 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* superdeck_demo.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = superdeck_demo.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -81,13 +78,16 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; + 58206CAA5C9CAE5E71ACD682 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 8A3EEB72D1DBF34484D4B571 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; - 8E55C3AF8DBE8C0B642392C5 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; - 95E586B6E592454913723390 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + 96D02405CAB3337A8B12E29B /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; - B3893DEC9C056E32B2FFF5D5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; - E6DC9400EA13E1E381531DBA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + B9852E5A6795285C786AC0A5 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; }; + C6840468B539A3E04B679E96 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + D549EC79AC8D2DF1C27D4870 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; }; + DA04FC470987408FF8C57926 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; + E2BD03C4940642B10DF984FF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + FEF4F6D79728354A6C21CACE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -95,7 +95,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - CF480946466E3948C153CB7B /* Pods_RunnerTests.framework in Frameworks */, + DC6D6673B8F35A47BEC03AAD /* Pods_RunnerTests.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -103,7 +103,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 46B3065E3DC9E36DFAA7E4E5 /* Pods_Runner.framework in Frameworks */, + D33EC936C6603F092BE164F8 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -137,14 +137,14 @@ 331C80D6294CF71000263BE5 /* RunnerTests */, 33CC10EE2044A3C60003C045 /* Products */, D73912EC22F37F3D000D13A0 /* Frameworks */, - 6765858902A57DC5511CDB91 /* Pods */, + D5950B1D48D0D7CEF81BA26B /* Pods */, ); sourceTree = ""; }; 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* superdeck_demo.app */, + 33CC10ED2044A3C60003C045 /* example.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; @@ -185,15 +185,15 @@ path = Runner; sourceTree = ""; }; - 6765858902A57DC5511CDB91 /* Pods */ = { + D5950B1D48D0D7CEF81BA26B /* Pods */ = { isa = PBXGroup; children = ( - 0B89690350CB7C0BFA12F997 /* Pods-Runner.debug.xcconfig */, - 8E55C3AF8DBE8C0B642392C5 /* Pods-Runner.release.xcconfig */, - E6DC9400EA13E1E381531DBA /* Pods-Runner.profile.xcconfig */, - 27F821420410E0459CD3AF93 /* Pods-RunnerTests.debug.xcconfig */, - B3893DEC9C056E32B2FFF5D5 /* Pods-RunnerTests.release.xcconfig */, - 95E586B6E592454913723390 /* Pods-RunnerTests.profile.xcconfig */, + FEF4F6D79728354A6C21CACE /* Pods-Runner.debug.xcconfig */, + E2BD03C4940642B10DF984FF /* Pods-Runner.release.xcconfig */, + DA04FC470987408FF8C57926 /* Pods-Runner.profile.xcconfig */, + 96D02405CAB3337A8B12E29B /* Pods-RunnerTests.debug.xcconfig */, + D549EC79AC8D2DF1C27D4870 /* Pods-RunnerTests.release.xcconfig */, + B9852E5A6795285C786AC0A5 /* Pods-RunnerTests.profile.xcconfig */, ); name = Pods; path = Pods; @@ -202,8 +202,8 @@ D73912EC22F37F3D000D13A0 /* Frameworks */ = { isa = PBXGroup; children = ( - 1D5B6EE3E4C25EF9B9DC26AB /* Pods_Runner.framework */, - 8A3EEB72D1DBF34484D4B571 /* Pods_RunnerTests.framework */, + 58206CAA5C9CAE5E71ACD682 /* Pods_Runner.framework */, + C6840468B539A3E04B679E96 /* Pods_RunnerTests.framework */, ); name = Frameworks; sourceTree = ""; @@ -215,7 +215,7 @@ isa = PBXNativeTarget; buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; buildPhases = ( - E72CA2529ABC203BD0C16508 /* [CP] Check Pods Manifest.lock */, + 19EEF7D0A3B09786CF680AFC /* [CP] Check Pods Manifest.lock */, 331C80D1294CF70F00263BE5 /* Sources */, 331C80D2294CF70F00263BE5 /* Frameworks */, 331C80D3294CF70F00263BE5 /* Resources */, @@ -234,13 +234,13 @@ isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - FBC2DC12B48F181C56A79EFE /* [CP] Check Pods Manifest.lock */, + D54F2E16B80C97964B7F5826 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - 6E983C0F7EEBE33CAC03B365 /* [CP] Embed Pods Frameworks */, + C22D8E44EE0F21092537C376 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -249,7 +249,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* superdeck_demo.app */; + productReference = 33CC10ED2044A3C60003C045 /* example.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -258,6 +258,7 @@ 33CC10E52044A3C60003C045 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0920; LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; @@ -322,6 +323,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 19EEF7D0A3B09786CF680AFC /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -360,7 +383,7 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 6E983C0F7EEBE33CAC03B365 /* [CP] Embed Pods Frameworks */ = { + C22D8E44EE0F21092537C376 /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -377,29 +400,7 @@ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; showEnvVarsInLog = 0; }; - E72CA2529ABC203BD0C16508 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - FBC2DC12B48F181C56A79EFE /* [CP] Check Pods Manifest.lock */ = { + D54F2E16B80C97964B7F5826 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -472,46 +473,46 @@ /* Begin XCBuildConfiguration section */ 331C80DB294CF71000263BE5 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 27F821420410E0459CD3AF93 /* Pods-RunnerTests.debug.xcconfig */; + baseConfigurationReference = 96D02405CAB3337A8B12E29B /* Pods-RunnerTests.debug.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/superdeck_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/superdeck_demo"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Debug; }; 331C80DC294CF71000263BE5 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B3893DEC9C056E32B2FFF5D5 /* Pods-RunnerTests.release.xcconfig */; + baseConfigurationReference = D549EC79AC8D2DF1C27D4870 /* Pods-RunnerTests.release.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/superdeck_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/superdeck_demo"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Release; }; 331C80DD294CF71000263BE5 /* Profile */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 95E586B6E592454913723390 /* Pods-RunnerTests.profile.xcconfig */; + baseConfigurationReference = B9852E5A6795285C786AC0A5 /* Pods-RunnerTests.profile.xcconfig */; buildSettings = { BUNDLE_LOADER = "$(TEST_HOST)"; CURRENT_PROJECT_VERSION = 1; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo.RunnerTests; + PRODUCT_BUNDLE_IDENTIFIER = com.example.example.RunnerTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_VERSION = 5.0; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/superdeck_demo.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/superdeck_demo"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/example"; }; name = Profile; }; @@ -520,6 +521,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -543,9 +545,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -593,6 +597,7 @@ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -616,9 +621,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -646,6 +653,7 @@ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; @@ -669,9 +677,11 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = NO; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; diff --git a/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/superdeck/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/superdeck/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/superdeck/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme similarity index 94% rename from example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme rename to packages/superdeck/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 5473b244..15368ecc 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/packages/superdeck/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -65,7 +65,7 @@ @@ -82,7 +82,7 @@ diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/superdeck/example/macos/Runner.xcworkspace/contents.xcworkspacedata similarity index 100% rename from example/macos/Runner.xcworkspace/contents.xcworkspacedata rename to packages/superdeck/example/macos/Runner.xcworkspace/contents.xcworkspacedata diff --git a/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/superdeck/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to packages/superdeck/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/example/macos/Runner/AppDelegate.swift b/packages/superdeck/example/macos/Runner/AppDelegate.swift similarity index 91% rename from example/macos/Runner/AppDelegate.swift rename to packages/superdeck/example/macos/Runner/AppDelegate.swift index d53ef643..8e02df28 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/packages/superdeck/example/macos/Runner/AppDelegate.swift @@ -1,7 +1,7 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png diff --git a/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png similarity index 100% rename from example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png rename to packages/superdeck/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png diff --git a/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/superdeck/example/macos/Runner/Base.lproj/MainMenu.xib similarity index 100% rename from example/macos/Runner/Base.lproj/MainMenu.xib rename to packages/superdeck/example/macos/Runner/Base.lproj/MainMenu.xib diff --git a/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/superdeck/example/macos/Runner/Configs/AppInfo.xcconfig similarity index 76% rename from example/macos/Runner/Configs/AppInfo.xcconfig rename to packages/superdeck/example/macos/Runner/Configs/AppInfo.xcconfig index 726f1ae8..92fb3cd5 100644 --- a/example/macos/Runner/Configs/AppInfo.xcconfig +++ b/packages/superdeck/example/macos/Runner/Configs/AppInfo.xcconfig @@ -5,10 +5,10 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = superdeck_demo +PRODUCT_NAME = example // The application's bundle identifier -PRODUCT_BUNDLE_IDENTIFIER = com.example.dashDeckDemo +PRODUCT_BUNDLE_IDENTIFIER = com.example.example // The copyright displayed in application information -PRODUCT_COPYRIGHT = Copyright © 2023 com.example. All rights reserved. +PRODUCT_COPYRIGHT = Copyright © 2024 com.example. All rights reserved. diff --git a/example/macos/Runner/Configs/Debug.xcconfig b/packages/superdeck/example/macos/Runner/Configs/Debug.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Debug.xcconfig rename to packages/superdeck/example/macos/Runner/Configs/Debug.xcconfig diff --git a/example/macos/Runner/Configs/Release.xcconfig b/packages/superdeck/example/macos/Runner/Configs/Release.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Release.xcconfig rename to packages/superdeck/example/macos/Runner/Configs/Release.xcconfig diff --git a/example/macos/Runner/Configs/Warnings.xcconfig b/packages/superdeck/example/macos/Runner/Configs/Warnings.xcconfig similarity index 100% rename from example/macos/Runner/Configs/Warnings.xcconfig rename to packages/superdeck/example/macos/Runner/Configs/Warnings.xcconfig diff --git a/example/macos/Runner/DebugProfile.entitlements b/packages/superdeck/example/macos/Runner/DebugProfile.entitlements similarity index 100% rename from example/macos/Runner/DebugProfile.entitlements rename to packages/superdeck/example/macos/Runner/DebugProfile.entitlements diff --git a/example/macos/Runner/Info.plist b/packages/superdeck/example/macos/Runner/Info.plist similarity index 100% rename from example/macos/Runner/Info.plist rename to packages/superdeck/example/macos/Runner/Info.plist diff --git a/example/macos/Runner/MainFlutterWindow.swift b/packages/superdeck/example/macos/Runner/MainFlutterWindow.swift similarity index 66% rename from example/macos/Runner/MainFlutterWindow.swift rename to packages/superdeck/example/macos/Runner/MainFlutterWindow.swift index 896ceae6..3cc05eb2 100644 --- a/example/macos/Runner/MainFlutterWindow.swift +++ b/packages/superdeck/example/macos/Runner/MainFlutterWindow.swift @@ -1,6 +1,5 @@ import Cocoa import FlutterMacOS -import window_manager class MainFlutterWindow: NSWindow { override func awakeFromNib() { @@ -13,9 +12,4 @@ class MainFlutterWindow: NSWindow { super.awakeFromNib() } - - override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { - super.order(place, relativeTo: otherWin) - hiddenWindowAtLaunch() - } } diff --git a/example/macos/Runner/Release.entitlements b/packages/superdeck/example/macos/Runner/Release.entitlements similarity index 100% rename from example/macos/Runner/Release.entitlements rename to packages/superdeck/example/macos/Runner/Release.entitlements diff --git a/example/macos/RunnerTests/RunnerTests.swift b/packages/superdeck/example/macos/RunnerTests/RunnerTests.swift similarity index 100% rename from example/macos/RunnerTests/RunnerTests.swift rename to packages/superdeck/example/macos/RunnerTests/RunnerTests.swift index 5418c9f5..61f3bd1f 100644 --- a/example/macos/RunnerTests/RunnerTests.swift +++ b/packages/superdeck/example/macos/RunnerTests/RunnerTests.swift @@ -1,5 +1,5 @@ -import FlutterMacOS import Cocoa +import FlutterMacOS import XCTest class RunnerTests: XCTestCase { diff --git a/example/pubspec.yaml b/packages/superdeck/example/pubspec.yaml similarity index 77% rename from example/pubspec.yaml rename to packages/superdeck/example/pubspec.yaml index be84edb0..0bdfffe6 100644 --- a/example/pubspec.yaml +++ b/packages/superdeck/example/pubspec.yaml @@ -6,7 +6,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" dependencies: flutter: @@ -19,9 +19,11 @@ dev_dependencies: flutter_test: sdk: flutter flutter_lints: ^3.0.2 + superdeck_cli: + path: ../../superdeck_cli flutter: uses-material-design: true assets: - - superdeck/ - - superdeck/generated/ \ No newline at end of file + - .superdeck/ + - .superdeck/generated/ \ No newline at end of file diff --git a/packages/superdeck/example/pubspec_overrides.yaml b/packages/superdeck/example/pubspec_overrides.yaml new file mode 100644 index 00000000..32c9384f --- /dev/null +++ b/packages/superdeck/example/pubspec_overrides.yaml @@ -0,0 +1,10 @@ +# melos_managed_dependency_overrides: superdeck,superdeck_cli +dependency_overrides: + # mix_generator: + # path: ../../mix/packages/mix_generator + # mix: + # path: ../../mix/packages/mix + superdeck: + path: .. + superdeck_cli: + path: ../../superdeck_cli diff --git a/example/slides.md b/packages/superdeck/example/slides.md similarity index 94% rename from example/slides.md rename to packages/superdeck/example/slides.md index aae4742b..d1b447f4 100644 --- a/example/slides.md +++ b/packages/superdeck/example/slides.md @@ -2,7 +2,7 @@ style: quote layout: image options: - src: https://source.unsplash.com/people-watching-concert-during-night-time-blgOFmPIlr0 + src: https://picsum.photos/600/600.webp fit: cover content: alignment: bottom_right @@ -21,9 +21,12 @@ style: cover --- layout: image +content: + alignment: bottom_right + flex: 1 style: show_sections options: - src: https://source.unsplash.com/random/900×700/?waves + src: https://picsum.photos/900/700?waves fit: cover position: left flex: 1 @@ -35,6 +38,7 @@ Create beautiful slides with images that fit your content. ##### Options ```yaml +content: options: src: https//www.url.com/image.jpg fit: cover @@ -193,6 +197,7 @@ VariantAttribute get radStyle { ), ); } + ``` --- @@ -250,8 +255,9 @@ layout: two_column Code ```dart int factorial(int n) { - return n == 0 ? 1 : n * factorial(n - 1); + return n == 0 ? 1 : n * factorial(n - 1); } + ``` Tasks @@ -266,7 +272,7 @@ Subtasks ::right:: Images -![Unsplash Image](https://source.unsplash.com/random/300x200/?landscape) +![Unsplash Image](https://picsum.photos/300/200?landscape) Table diff --git a/example/superdeck.yaml b/packages/superdeck/example/superdeck.yaml similarity index 100% rename from example/superdeck.yaml rename to packages/superdeck/example/superdeck.yaml diff --git a/example/test/widget_test.dart b/packages/superdeck/example/test/widget_test.dart similarity index 100% rename from example/test/widget_test.dart rename to packages/superdeck/example/test/widget_test.dart diff --git a/example/web/favicon.png b/packages/superdeck/example/web/favicon.png similarity index 100% rename from example/web/favicon.png rename to packages/superdeck/example/web/favicon.png diff --git a/example/web/icons/Icon-192.png b/packages/superdeck/example/web/icons/Icon-192.png similarity index 100% rename from example/web/icons/Icon-192.png rename to packages/superdeck/example/web/icons/Icon-192.png diff --git a/example/web/icons/Icon-512.png b/packages/superdeck/example/web/icons/Icon-512.png similarity index 100% rename from example/web/icons/Icon-512.png rename to packages/superdeck/example/web/icons/Icon-512.png diff --git a/example/web/icons/Icon-maskable-192.png b/packages/superdeck/example/web/icons/Icon-maskable-192.png similarity index 100% rename from example/web/icons/Icon-maskable-192.png rename to packages/superdeck/example/web/icons/Icon-maskable-192.png diff --git a/example/web/icons/Icon-maskable-512.png b/packages/superdeck/example/web/icons/Icon-maskable-512.png similarity index 100% rename from example/web/icons/Icon-maskable-512.png rename to packages/superdeck/example/web/icons/Icon-maskable-512.png diff --git a/packages/superdeck/example/web/index.html b/packages/superdeck/example/web/index.html new file mode 100644 index 00000000..ef6b75a9 --- /dev/null +++ b/packages/superdeck/example/web/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + Superdeck + + + + + + diff --git a/packages/superdeck/example/web/manifest.json b/packages/superdeck/example/web/manifest.json new file mode 100644 index 00000000..096edf8f --- /dev/null +++ b/packages/superdeck/example/web/manifest.json @@ -0,0 +1,35 @@ +{ + "name": "example", + "short_name": "example", + "start_url": ".", + "display": "standalone", + "background_color": "#0175C2", + "theme_color": "#0175C2", + "description": "A new Flutter project.", + "orientation": "portrait-primary", + "prefer_related_applications": false, + "icons": [ + { + "src": "icons/Icon-192.png", + "sizes": "192x192", + "type": "image/png" + }, + { + "src": "icons/Icon-512.png", + "sizes": "512x512", + "type": "image/png" + }, + { + "src": "icons/Icon-maskable-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "maskable" + }, + { + "src": "icons/Icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" + } + ] +} diff --git a/example/windows/.gitignore b/packages/superdeck/example/windows/.gitignore similarity index 100% rename from example/windows/.gitignore rename to packages/superdeck/example/windows/.gitignore diff --git a/example/windows/CMakeLists.txt b/packages/superdeck/example/windows/CMakeLists.txt similarity index 92% rename from example/windows/CMakeLists.txt rename to packages/superdeck/example/windows/CMakeLists.txt index d427e88b..d960948a 100644 --- a/example/windows/CMakeLists.txt +++ b/packages/superdeck/example/windows/CMakeLists.txt @@ -1,10 +1,10 @@ # Project-level configuration. cmake_minimum_required(VERSION 3.14) -project(superdeck_demo LANGUAGES CXX) +project(example LANGUAGES CXX) # The name of the executable created for the application. Change this to change # the on-disk name of your application. -set(BINARY_NAME "superdeck_demo") +set(BINARY_NAME "example") # Explicitly opt in to modern CMake behaviors to avoid warnings with recent # versions of CMake. @@ -87,6 +87,12 @@ if(PLUGIN_BUNDLED_LIBRARIES) COMPONENT Runtime) endif() +# Copy the native assets provided by the build.dart from all packages. +set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") +install(DIRECTORY "${NATIVE_ASSETS_DIR}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + # Fully re-copy the assets directory on each build to avoid having stale files # from a previous install. set(FLUTTER_ASSET_DIR_NAME "flutter_assets") diff --git a/example/windows/flutter/CMakeLists.txt b/packages/superdeck/example/windows/flutter/CMakeLists.txt similarity index 94% rename from example/windows/flutter/CMakeLists.txt rename to packages/superdeck/example/windows/flutter/CMakeLists.txt index 930d2071..903f4899 100644 --- a/example/windows/flutter/CMakeLists.txt +++ b/packages/superdeck/example/windows/flutter/CMakeLists.txt @@ -10,6 +10,11 @@ include(${EPHEMERAL_DIR}/generated_config.cmake) # https://github.com/flutter/flutter/issues/57146. set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") +# Set fallback configurations for older versions of the flutter tool. +if (NOT DEFINED FLUTTER_TARGET_PLATFORM) + set(FLUTTER_TARGET_PLATFORM "windows-x64") +endif() + # === Flutter Library === set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") @@ -92,7 +97,7 @@ add_custom_command( COMMAND ${CMAKE_COMMAND} -E env ${FLUTTER_TOOL_ENVIRONMENT} "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" - windows-x64 $ + ${FLUTTER_TARGET_PLATFORM} $ VERBATIM ) add_custom_target(flutter_assemble DEPENDS diff --git a/example/windows/flutter/generated_plugin_registrant.cc b/packages/superdeck/example/windows/flutter/generated_plugin_registrant.cc similarity index 67% rename from example/windows/flutter/generated_plugin_registrant.cc rename to packages/superdeck/example/windows/flutter/generated_plugin_registrant.cc index fe7ec1cb..4176f5bc 100644 --- a/example/windows/flutter/generated_plugin_registrant.cc +++ b/packages/superdeck/example/windows/flutter/generated_plugin_registrant.cc @@ -6,11 +6,17 @@ #include "generated_plugin_registrant.h" +#include +#include #include #include #include void RegisterPlugins(flutter::PluginRegistry* registry) { + FileSaverPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSaverPlugin")); + FileSelectorWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FileSelectorWindows")); ScreenRetrieverPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("ScreenRetrieverPlugin")); UrlLauncherWindowsRegisterWithRegistrar( diff --git a/example/windows/flutter/generated_plugin_registrant.h b/packages/superdeck/example/windows/flutter/generated_plugin_registrant.h similarity index 100% rename from example/windows/flutter/generated_plugin_registrant.h rename to packages/superdeck/example/windows/flutter/generated_plugin_registrant.h diff --git a/example/windows/flutter/generated_plugins.cmake b/packages/superdeck/example/windows/flutter/generated_plugins.cmake similarity index 95% rename from example/windows/flutter/generated_plugins.cmake rename to packages/superdeck/example/windows/flutter/generated_plugins.cmake index fb2dea6b..cfabaeb0 100644 --- a/example/windows/flutter/generated_plugins.cmake +++ b/packages/superdeck/example/windows/flutter/generated_plugins.cmake @@ -3,6 +3,8 @@ # list(APPEND FLUTTER_PLUGIN_LIST + file_saver + file_selector_windows screen_retriever url_launcher_windows window_manager diff --git a/example/windows/runner/CMakeLists.txt b/packages/superdeck/example/windows/runner/CMakeLists.txt similarity index 100% rename from example/windows/runner/CMakeLists.txt rename to packages/superdeck/example/windows/runner/CMakeLists.txt diff --git a/example/windows/runner/Runner.rc b/packages/superdeck/example/windows/runner/Runner.rc similarity index 90% rename from example/windows/runner/Runner.rc rename to packages/superdeck/example/windows/runner/Runner.rc index 9d8047c4..687e6bd2 100644 --- a/example/windows/runner/Runner.rc +++ b/packages/superdeck/example/windows/runner/Runner.rc @@ -90,12 +90,12 @@ BEGIN BLOCK "040904e4" BEGIN VALUE "CompanyName", "com.example" "\0" - VALUE "FileDescription", "superdeck_demo" "\0" + VALUE "FileDescription", "example" "\0" VALUE "FileVersion", VERSION_AS_STRING "\0" - VALUE "InternalName", "superdeck_demo" "\0" - VALUE "LegalCopyright", "Copyright (C) 2023 com.example. All rights reserved." "\0" - VALUE "OriginalFilename", "superdeck_demo.exe" "\0" - VALUE "ProductName", "superdeck_demo" "\0" + VALUE "InternalName", "example" "\0" + VALUE "LegalCopyright", "Copyright (C) 2024 com.example. All rights reserved." "\0" + VALUE "OriginalFilename", "example.exe" "\0" + VALUE "ProductName", "example" "\0" VALUE "ProductVersion", VERSION_AS_STRING "\0" END END diff --git a/example/windows/runner/flutter_window.cpp b/packages/superdeck/example/windows/runner/flutter_window.cpp similarity index 100% rename from example/windows/runner/flutter_window.cpp rename to packages/superdeck/example/windows/runner/flutter_window.cpp diff --git a/example/windows/runner/flutter_window.h b/packages/superdeck/example/windows/runner/flutter_window.h similarity index 100% rename from example/windows/runner/flutter_window.h rename to packages/superdeck/example/windows/runner/flutter_window.h diff --git a/example/windows/runner/main.cpp b/packages/superdeck/example/windows/runner/main.cpp similarity index 95% rename from example/windows/runner/main.cpp rename to packages/superdeck/example/windows/runner/main.cpp index d69be0a3..a61bf80d 100644 --- a/example/windows/runner/main.cpp +++ b/packages/superdeck/example/windows/runner/main.cpp @@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, FlutterWindow window(project); Win32Window::Point origin(10, 10); Win32Window::Size size(1280, 720); - if (!window.Create(L"superdeck_demo", origin, size)) { + if (!window.Create(L"example", origin, size)) { return EXIT_FAILURE; } window.SetQuitOnClose(true); diff --git a/example/windows/runner/resource.h b/packages/superdeck/example/windows/runner/resource.h similarity index 100% rename from example/windows/runner/resource.h rename to packages/superdeck/example/windows/runner/resource.h diff --git a/example/windows/runner/resources/app_icon.ico b/packages/superdeck/example/windows/runner/resources/app_icon.ico similarity index 100% rename from example/windows/runner/resources/app_icon.ico rename to packages/superdeck/example/windows/runner/resources/app_icon.ico diff --git a/example/windows/runner/runner.exe.manifest b/packages/superdeck/example/windows/runner/runner.exe.manifest similarity index 100% rename from example/windows/runner/runner.exe.manifest rename to packages/superdeck/example/windows/runner/runner.exe.manifest diff --git a/example/windows/runner/utils.cpp b/packages/superdeck/example/windows/runner/utils.cpp similarity index 93% rename from example/windows/runner/utils.cpp rename to packages/superdeck/example/windows/runner/utils.cpp index b2b08734..3a0b4651 100644 --- a/example/windows/runner/utils.cpp +++ b/packages/superdeck/example/windows/runner/utils.cpp @@ -45,13 +45,13 @@ std::string Utf8FromUtf16(const wchar_t* utf16_string) { if (utf16_string == nullptr) { return std::string(); } - int target_length = ::WideCharToMultiByte( + unsigned int target_length = ::WideCharToMultiByte( CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr, 0, nullptr, nullptr) -1; // remove the trailing null character int input_length = (int)wcslen(utf16_string); std::string utf8_string; - if (target_length <= 0 || target_length > utf8_string.max_size()) { + if (target_length == 0 || target_length > utf8_string.max_size()) { return utf8_string; } utf8_string.resize(target_length); diff --git a/example/windows/runner/utils.h b/packages/superdeck/example/windows/runner/utils.h similarity index 100% rename from example/windows/runner/utils.h rename to packages/superdeck/example/windows/runner/utils.h diff --git a/example/windows/runner/win32_window.cpp b/packages/superdeck/example/windows/runner/win32_window.cpp similarity index 100% rename from example/windows/runner/win32_window.cpp rename to packages/superdeck/example/windows/runner/win32_window.cpp diff --git a/example/windows/runner/win32_window.h b/packages/superdeck/example/windows/runner/win32_window.h similarity index 100% rename from example/windows/runner/win32_window.h rename to packages/superdeck/example/windows/runner/win32_window.h diff --git a/grammars/markdown.json b/packages/superdeck/grammars/markdown.json similarity index 100% rename from grammars/markdown.json rename to packages/superdeck/grammars/markdown.json diff --git a/grammars/mermaid.json b/packages/superdeck/grammars/mermaid.json similarity index 100% rename from grammars/mermaid.json rename to packages/superdeck/grammars/mermaid.json diff --git a/grammars/python.json b/packages/superdeck/grammars/python.json similarity index 100% rename from grammars/python.json rename to packages/superdeck/grammars/python.json diff --git a/packages/superdeck/lib/chat/chat_theme.dart b/packages/superdeck/lib/chat/chat_theme.dart new file mode 100644 index 00000000..0fbb4e6e --- /dev/null +++ b/packages/superdeck/lib/chat/chat_theme.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_chat_ui/flutter_chat_ui.dart'; + +import '../helpers/extensions.dart'; + +final _baseTheme = DarkChatTheme(); + +ChatTheme buildChatTheme(BuildContext context) { + return DarkChatTheme( + backgroundColor: context.colorScheme.surface, + inputSurfaceTintColor: Colors.black, + dateDividerTextStyle: TextStyle( + color: context.colorScheme.onSurface, + fontSize: 12, + fontWeight: FontWeight.w800, + height: 1.333, + ), + emptyChatPlaceholderTextStyle: TextStyle( + color: context.colorScheme.onSurface.withOpacity(0.6), + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.5, + ), + errorColor: context.colorScheme.error, + inputBackgroundColor: Colors.black, + inputBorderRadius: BorderRadius.zero, + inputTextColor: context.colorScheme.onSurface, + primaryColor: context.colorScheme.primary, + receivedMessageBodyTextStyle: TextStyle( + color: context.colorScheme.onSecondary, + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.5, + ), + receivedMessageCaptionTextStyle: TextStyle( + color: context.colorScheme.onSecondary.withOpacity(0.6), + fontSize: 12, + fontWeight: FontWeight.w500, + height: 1.333, + ), + receivedMessageDocumentIconColor: context.colorScheme.primary, + receivedMessageLinkDescriptionTextStyle: TextStyle( + color: context.colorScheme.onSecondary, + fontSize: 14, + fontWeight: FontWeight.w400, + height: 1.428, + ), + receivedMessageLinkTitleTextStyle: TextStyle( + color: context.colorScheme.onSecondary, + fontSize: 16, + fontWeight: FontWeight.w800, + height: 1.375, + ), + secondaryColor: context.colorScheme.secondary, + sentMessageBodyTextStyle: TextStyle( + color: context.colorScheme.onPrimary, + fontSize: 16, + fontWeight: FontWeight.w500, + height: 1.5, + ), + sentMessageCaptionTextStyle: TextStyle( + color: context.colorScheme.onPrimary.withOpacity(0.6), + fontSize: 12, + fontWeight: FontWeight.w500, + height: 1.333, + ), + sentMessageDocumentIconColor: context.colorScheme.onPrimary, + sentMessageLinkDescriptionTextStyle: TextStyle( + color: context.colorScheme.onPrimary, + fontSize: 14, + fontWeight: FontWeight.w400, + height: 1.428, + ), + sentMessageLinkTitleTextStyle: TextStyle( + color: context.colorScheme.onPrimary, + fontSize: 16, + fontWeight: FontWeight.w800, + height: 1.375, + ), + // systemMessageTheme: SystemMessageTheme( + // textStyle: TextStyle( + // color: context.colorScheme.onSurface, + // fontSize: 12, + // fontWeight: FontWeight.w800, + // height: 1.333, + // ), + // ), + // typingIndicatorTheme: TypingIndicatorTheme( + // animatedCirclesColor: context.colorScheme.onSurface, + // bubbleColor: context.colorScheme.surface, + // countTextColor: context.colorScheme.onPrimary, + // animatedCircleSize: _baseTheme.typingIndicatorTheme.animatedCircleSize, + // bubbleBorder: _baseTheme.typingIndicatorTheme.bubbleBorder, + // countAvatarColor: context.colorScheme.primary, + // multipleUserTextStyle: context.textTheme.bodySmall!, + // ), + unreadHeaderTheme: UnreadHeaderTheme( + color: context.colorScheme.secondary, + textStyle: TextStyle( + color: context.colorScheme.onSecondary.withOpacity(0.6), + fontSize: 12, + fontWeight: FontWeight.w500, + height: 1.333, + ), + ), + userAvatarTextStyle: TextStyle( + color: context.colorScheme.onPrimary, + fontSize: 12, + fontWeight: FontWeight.w800, + height: 1.333, + ), + ); +} diff --git a/packages/superdeck/lib/chat/components/typing_indicator.dart b/packages/superdeck/lib/chat/components/typing_indicator.dart new file mode 100644 index 00000000..14d3c0c2 --- /dev/null +++ b/packages/superdeck/lib/chat/components/typing_indicator.dart @@ -0,0 +1,66 @@ +import 'dart:async'; + +import 'package:flutter/material.dart'; + +class WaitingIndicator extends StatefulWidget { + final bool isTyping; + + const WaitingIndicator({Key? key, required this.isTyping}) : super(key: key); + + @override + State createState() => _WaitingIndicatorState(); +} + +class _WaitingIndicatorState extends State { + late Timer _timer; + final List _dotSizes = [6, 8, 10]; + + @override + void initState() { + super.initState(); + _startTimer(); + } + + @override + void dispose() { + _stopTimer(); + super.dispose(); + } + + void _startTimer() { + _timer = Timer.periodic(const Duration(milliseconds: 500), (_) { + setState(() { + _dotSizes.insert(0, _dotSizes.removeLast()); + }); + }); + } + + void _stopTimer() { + _timer.cancel(); + } + + @override + Widget build(BuildContext context) { + return widget.isTyping + ? Row( + mainAxisSize: MainAxisSize.min, + children: [ + for (final size in _dotSizes) + Padding( + padding: const EdgeInsets.only(right: 4), + child: AnimatedContainer( + duration: const Duration(milliseconds: 500), + curve: Curves.easeInOut, + height: size, + width: size, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.grey[400], + ), + ), + ), + ], + ) + : const SizedBox.shrink(); + } +} diff --git a/packages/superdeck/lib/chat/prompt.dart b/packages/superdeck/lib/chat/prompt.dart new file mode 100644 index 00000000..feb031d7 --- /dev/null +++ b/packages/superdeck/lib/chat/prompt.dart @@ -0,0 +1,64 @@ +final presentationAssistantPrompt = ''' + + + +**CONTEXT:** + +You are a markdown presentation assistance bot designed to help users create, refine, and optimize markdown-based slides. Users often use frontmatter to define styles, layouts, and various options within their presentations. Your task is to assist in adjusting these elements to improve the overall presentation while providing feedback and suggestions on specific slide numbers. + +**OBJECTIVE:** + +To guide users in enhancing their markdown presentations by suggesting improvements to the structure, layout, and style. When proposing changes, you should clearly reference the slide number where the change is suggested. Additionally, ensure that the user is aware of available frontmatter options that can be utilized for better customization. + +**STYLE:** +Never change the frontmatter structure, or any of the image urls. +Always focus on one specific slide and fixes before moving to the next changes. +Provide feedback in a concise and instructional manner, ensuring that each suggestion is actionable. The feedback should focus on optimizing the presentation's readability, visual appeal, and effectiveness in conveying the intended message. + +**TONE:** + +Professional and supportive. Encourage the user by acknowledging their current efforts and offering constructive advice to elevate the presentation. + +**AUDIENCE:** + +The audience comprises users who are actively working on creating markdown-based presentations. They may have varying levels of experience with markdown and frontmatter customization, so explanations should be clear and accessible. + +**REQUIRED FORMAT:** + +The suggestions are always conversational, you should should not provide code snippets. +Feedback and suggestions should be provided as text responses within the markdown file or as separate comments. Each suggestion must include a reference to the specific slide number being discussed. + +**STEP-BACK PROMPTING:** + +Before making specific suggestions, review the overall structure and style of the presentation. Consider the flow and coherence of the slides, as well as the effective use of frontmatter for layout and style consistency. Are there opportunities to better utilize frontmatter options to enhance the presentation's impact? + +**END RESULT PROMPTING:** + +Envision the final version of the markdown presentation after all suggested changes have been implemented. Describe how the presentation has improved in terms of clarity, visual appeal, and audience engagement. What specific changes have made the most significant impact, and how does the presentation now meet the user's original goals? +'''; + +final provideMarkdownEdits = ''' +CONTEXT: +You need a finalized Markdown document incorporating all feedback and ready for immediate use, without extra lines or spaces, or extra content in the response. + +OBJECTIVE: +Deliver a fully edited, complete Markdown file, ready to save and use with no further adjustments. + +STYLE: +Clear, concise, and well-structured, with a focus on precision. + +TONE: +Professional and straightforward, reflecting previous feedback. + +AUDIENCE: +Intended for immediate use by professionals needing the finalized content. + +REQUIRED FORMAT: +A complete and polished Markdown file, ready for saving. + +STEP-BACK PROMPTING: +Review the Markdown file to ensure all feedback is integrated and the content is complete and aligned with clarity goals. + +END RESULT PROMPTING: +Envision saving the file as the final version, reflecting all edits and ready for immediate use with no further modifications needed. +'''; diff --git a/lib/components/atoms/cache_image_widget.dart b/packages/superdeck/lib/components/atoms/cache_image_widget.dart similarity index 65% rename from lib/components/atoms/cache_image_widget.dart rename to packages/superdeck/lib/components/atoms/cache_image_widget.dart index 4de8f1ff..fd9ba9fe 100644 --- a/lib/components/atoms/cache_image_widget.dart +++ b/packages/superdeck/lib/components/atoms/cache_image_widget.dart @@ -4,11 +4,8 @@ import 'dart:math'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; import '../../helpers/constants.dart'; -import '../../helpers/utils.dart'; -import '../../services/project_service.dart'; import '../../superdeck.dart'; class CacheImage extends StatelessWidget { @@ -16,25 +13,19 @@ class CacheImage extends StatelessWidget { final BoxFit? fit; final ImageSpec spec; final Alignment? alignment; - final Size size; const CacheImage({ required this.url, this.fit = BoxFit.cover, this.alignment = Alignment.center, this.spec = const ImageSpec(), - required this.size, super.key, }); @override Widget build(BuildContext context) { return AnimatedImageSpecWidget( - image: getImageProvider( - context: context, - url: url, - targetSize: size, - ), + image: getImageProvider(url), spec: spec.copyWith( fit: fit, alignment: alignment, @@ -51,9 +42,9 @@ class CacheImage extends StatelessWidget { // cache the smallest dimension of the image // So set the other dimension to null if (asset.isPortrait) { - cacheHeight = min(size.height, asset.dimensions.height).toInt(); + cacheHeight = min(size.height, asset.height).toInt(); } else { - cacheWidth = min(size.width, asset.dimensions.width).toInt(); + cacheWidth = min(size.width, asset.width).toInt(); } } else { // If no asset is available, set both cacheWidth and cacheHeight @@ -70,25 +61,24 @@ class CacheImage extends StatelessWidget { return (width: cacheWidth, height: cacheHeight); } -ImageProvider getImageProvider({ - required BuildContext context, - required String url, - required Size targetSize, -}) { - final controller = SDController.instance; +ImageProvider getImageProvider(String url, {Size? targetSize}) { ImageProvider provider; - SlideAsset? asset; + final assets = $superdeck.assets; - final firstOrNull = controller.assets.watch(context).firstWhereOrNull; + final assetUrl = assets.firstWhereOrNull((e) { + if (e.path == url) { + return true; + } - if (ProjectService.instance.isAssetFile(File(url))) { - asset = firstOrNull((e) => e.file.path == url); - } else { - asset = firstOrNull((e) => e.file.path.contains(shortHashId(url))); - } + if (e.reference == url) { + return true; + } + + return false; + }); - url = asset?.file.path ?? url; + url = assetUrl?.path ?? url; // check if its a local path or a network path if (url.startsWith('http')) { @@ -102,7 +92,8 @@ ImageProvider getImageProvider({ } } - final (:width, :height) = calculateImageSize(targetSize, asset); + final (:width, :height) = + calculateImageSize(targetSize ?? kResolution, assetUrl); return ResizeImage.resizeIfNeeded( width, diff --git a/packages/superdeck/lib/components/atoms/linear_progresss_indicator_widget.dart b/packages/superdeck/lib/components/atoms/linear_progresss_indicator_widget.dart new file mode 100644 index 00000000..f2f9564d --- /dev/null +++ b/packages/superdeck/lib/components/atoms/linear_progresss_indicator_widget.dart @@ -0,0 +1,37 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +class AnimatedLinearProgressIndicator extends HookWidget { + final double progress; + + const AnimatedLinearProgressIndicator({ + super.key, + required this.progress, + }); + + @override + Widget build(BuildContext context) { + final animationController = useAnimationController( + duration: const Duration(milliseconds: 100), + ); + + final animation = + Tween(begin: 0.0, end: progress).animate(animationController); + + useEffect(() { + animationController.forward(); + return null; + }, []); + + return AnimatedBuilder( + animation: animation, + builder: (context, child) { + return LinearProgressIndicator( + minHeight: 10, + borderRadius: BorderRadius.circular(10), + value: animation.value, + ); + }, + ); + } +} diff --git a/lib/components/atoms/loading_indicator.dart b/packages/superdeck/lib/components/atoms/loading_indicator.dart similarity index 73% rename from lib/components/atoms/loading_indicator.dart rename to packages/superdeck/lib/components/atoms/loading_indicator.dart index fefaf777..2a92cfb1 100644 --- a/lib/components/atoms/loading_indicator.dart +++ b/packages/superdeck/lib/components/atoms/loading_indicator.dart @@ -38,9 +38,7 @@ class _LoadingOverlayState extends State setState(() {}); }); } else if (widget.isLoading && !oldWidget.isLoading) { - _animationController.reverse().then((_) { - setState(() {}); - }); + _animationController.reverse().then((_) => setState(() {})); } } @@ -71,9 +69,13 @@ class _LoadingOverlayState extends State } class IsometricLoading extends StatefulWidget { - const IsometricLoading({super.key, this.color = Colors.white}); + const IsometricLoading({ + super.key, + this.color = Colors.white, + }); final Color color; + @override _IsometricLoadingState createState() => _IsometricLoadingState(); } @@ -118,20 +120,26 @@ class _IsometricLoadingState extends State return Transform.scale( scale: 0.2, child: CustomPaint( - painter: IsometricLoadingPainter( - colors: List.generate(4, (index) { - final startColorIndex = - ((_animation.value * _colors.length).floor() + index) % - _colors.length; - final endColorIndex = (startColorIndex == _colors.length - 1) - ? 0 - : startColorIndex + 1; - final startColor = _colors[startColorIndex]; - final endColor = _colors[endColorIndex]; - final colorProgress = (_animation.value * _colors.length) % 1.0; - return Color.lerp(startColor, endColor, colorProgress)!; - }), - )), + painter: IsometricLoadingPainter( + colors: List.generate( + 4, + (index) { + final startColorIndex = + ((_animation.value * _colors.length).floor() + index) % + _colors.length; + final endColorIndex = (startColorIndex == _colors.length - 1) + ? 0 + : startColorIndex + 1; + final startColor = _colors[startColorIndex]; + final endColor = _colors[endColorIndex]; + final colorProgress = + (_animation.value * _colors.length) % 1.0; + return Color.lerp(startColor, endColor, colorProgress)!; + }, + ), + ), + child: const SizedBox(width: 200, height: 200), + ), ); }, ); @@ -145,6 +153,27 @@ class IsometricLoadingPainter extends CustomPainter { @override void paint(Canvas canvas, Size size) { + final canvasHeight = size.height; + final canvasWidth = size.width; + + // Calculate the minimum and maximum Y-coordinates from the path data + // Assuming the minimum Y-coordinate is 0 + final minY = 0.0; + // Based on the maximum Y-coordinate value in the path data + final maxY = 226.45; + + // Calculate the height of the paths + final pathHeight = maxY - minY; + + // Calculate the scale factor based on the canvas size and path height + final scaleFactor = canvasHeight / pathHeight; + + // Translate the canvas to center the paths + canvas.translate(canvasWidth / 2 - 100.003 * scaleFactor, 0); + + // Scale the canvas based on the calculated scale factor + canvas.scale(scaleFactor); + final path1 = Path() ..moveTo(92.2116, 119.9706) ..lineTo(0, 66.7358) diff --git a/packages/superdeck/lib/components/atoms/markdown_viewer.dart b/packages/superdeck/lib/components/atoms/markdown_viewer.dart new file mode 100644 index 00000000..a23b7957 --- /dev/null +++ b/packages/superdeck/lib/components/atoms/markdown_viewer.dart @@ -0,0 +1,230 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:markdown/markdown.dart' as md; + +import '../../helpers/constants.dart'; +import '../../helpers/measure_size.dart'; +import '../../helpers/syntax_highlighter.dart'; +import '../../helpers/utils.dart'; +import '../../superdeck.dart'; +import 'cache_image_widget.dart'; + +class AnimatedMarkdownViewer extends ImplicitlyAnimatedWidget { + final String content; + final SlideSpec spec; + + const AnimatedMarkdownViewer({ + super.key, + required this.content, + required this.spec, + required super.duration, + super.curve = Curves.linear, + }); + + @override + ImplicitlyAnimatedWidgetState createState() => + _AnimatedMarkdownViewerState(); +} + +class _AnimatedMarkdownViewerState + extends AnimatedWidgetBaseState { + SlideSpecTween? _styleTween; + Size? _size; + + @override + void forEachTween(TweenVisitor visitor) { + _styleTween = visitor( + _styleTween, + widget.spec, + (dynamic value) => SlideSpecTween(begin: value), + ) as SlideSpecTween?; + } + + @override + Widget build(BuildContext context) { + final spec = _styleTween!.evaluate(animation) ?? const SlideSpec(); + return MeasureSingleWidgetSize( + onChange: (size) { + setState(() { + _size = size; + }); + }, + child: MarkdownBody( + data: widget.content, + extensionSet: md.ExtensionSet( + md.ExtensionSet.gitHubFlavored.blockSyntaxes, + [ + md.EmojiSyntax(), + ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes + ], + ), + imageBuilder: (uri, title, alt) { + return _imageBuilder(uri, title, alt, size: _size ?? kResolution); + }, + builders: { + 'code': CodeElementBuilder(spec.code), + }, + bulletBuilder: (parameters) { + if (parameters.style == BulletStyle.orderedList) { + final index = parameters.index + 1; + return Text( + '$index .', + style: spec.list?.bulletStyle, + ); + } else { + return Text('•', style: spec.list?.bulletStyle); + } + }, + styleSheet: _styleTween!.evaluate(animation)?.toStyle(), + ), + ); + } +} + +List updateTextColor( + List originalSpans, + List targetLines, + Color newColor, +) { + // Check if the target line is within the range of the list + if (targetLines.isEmpty) { + return originalSpans; + } + + // Clone the original list to avoid mutating it directly + List updatedSpans = List.from(originalSpans); + + // Detect line break from the list of text spans + // This is done by checking if the previous span is a line break + // If it is, then the current span is the start of a new line + int line = 1; + + for (int i = 0; i < updatedSpans.length; i++) { + if (i > 0) { + final currentValue = updatedSpans[i].text ?? ''; + + if (currentValue.startsWith('\n')) { + line++; + } + } + final originalSpan = originalSpans[i]; + + final textStyle = originalSpan.style ?? const TextStyle(); + + if (targetLines.contains(line)) { + updatedSpans[i] = TextSpan( + text: originalSpan.text, + children: originalSpan.children, + recognizer: originalSpan.recognizer, + mouseCursor: originalSpan.mouseCursor, + onEnter: originalSpan.onEnter, + onExit: originalSpan.onExit, + semanticsLabel: originalSpan.semanticsLabel, + locale: originalSpan.locale, + spellOut: originalSpan.spellOut, + style: textStyle.copyWith( + backgroundColor: newColor, + ), + ); + } + } + + return updatedSpans; +} + +Widget _imageBuilder( + Uri uri, + String? title, + String? alt, { + required Size size, +}) { + return Builder(builder: (context) { + final slideSpec = SlideSpec.of(context); + + return ConstrainedBox( + constraints: calculateConstraints(size, slideSpec), + child: CacheImage( + url: uri.toString(), + spec: slideSpec.image, + ), + ); + }); +} + +class TextBuilder extends MarkdownElementBuilder { + final TextSpec? spec; + TextBuilder(this.spec); + @override + Widget visitText(md.Text text, TextStyle? preferredStyle) { + return TextSpecWidget(text.text, spec: spec); + } +} + +class CodeElementBuilder extends MarkdownElementBuilder { + final MdCodeblockSpec? spec; + CodeElementBuilder(this.spec); + @override + Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) { + var language = 'dart'; + + if (element.attributes['class'] != null) { + String lg = element.attributes['class'] as String; + language = lg.substring(9); + } + return Row( + children: [ + Expanded( + child: Container( + padding: spec?.padding, + decoration: spec?.decoration, + child: RichText( + text: TextSpan( + style: spec?.textStyle, + children: SyntaxHighlight.render( + element.textContent.trim(), + language, + ), + ), + ), + ), + ), + ], + ); + } +} + +class SampleCodeElementBuilder extends MarkdownElementBuilder { + final MdCodeblockSpec? spec; + SampleCodeElementBuilder(this.spec); + @override + Widget? visitElementAfter(md.Element element, TextStyle? preferredStyle) { + var language = 'dart'; + + if (element.attributes['class'] != null) { + String lg = element.attributes['class'] as String; + language = lg.substring(9); + } + return Row( + children: [ + Expanded( + child: Container( + padding: EdgeInsets.all(8), + decoration: BoxDecoration( + color: Colors.transparent, + borderRadius: BorderRadius.circular(8), + ), + child: RichText( + text: TextSpan( + style: spec?.textStyle, + children: SyntaxHighlight.render( + element.textContent.trim(), + language, + ), + ), + ), + ), + ), + ], + ); + } +} diff --git a/packages/superdeck/lib/components/atoms/sized_transition.dart b/packages/superdeck/lib/components/atoms/sized_transition.dart new file mode 100644 index 00000000..9f203265 --- /dev/null +++ b/packages/superdeck/lib/components/atoms/sized_transition.dart @@ -0,0 +1,37 @@ +import 'dart:math' as math; + +import 'package:flutter/widgets.dart'; + +class SizedTransition extends StatelessWidget { + const SizedTransition({ + super.key, + required this.sizeFactor, + this.child, + this.direction = Axis.horizontal, + }); + + final double sizeFactor; + final Axis direction; + + final Widget? child; + + @override + Widget build(BuildContext context) { + AlignmentDirectional alignment; + if (direction == Axis.horizontal) { + alignment = const AlignmentDirectional(0.0, -1.0); + } else { + alignment = const AlignmentDirectional(-1.0, 0.0); + } + return ClipRect( + child: Align( + alignment: alignment, + heightFactor: + direction == Axis.vertical ? math.max(sizeFactor, 0.0) : 1.0, + widthFactor: + direction == Axis.horizontal ? math.max(sizeFactor, 0.0) : 1.0, + child: child, + ), + ); + } +} diff --git a/packages/superdeck/lib/components/atoms/slide_thumbnail.dart b/packages/superdeck/lib/components/atoms/slide_thumbnail.dart new file mode 100644 index 00000000..ab338f65 --- /dev/null +++ b/packages/superdeck/lib/components/atoms/slide_thumbnail.dart @@ -0,0 +1,153 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:remix/remix.dart'; + +import '../../helpers/constants.dart'; +import '../../helpers/extensions.dart'; +import '../../services/reference_service.dart'; +import '../../services/snapshot_service.dart'; +import '../../superdeck.dart'; +import 'cache_image_widget.dart'; +import 'loading_indicator.dart'; + +class SlideThumbnail extends HookWidget { + final VoidCallback onTap; + final bool selected; + final Slide slide; + final int page; + + const SlideThumbnail({ + super.key, + required this.selected, + required this.onTap, + required this.slide, + required this.page, + }); + + @override + Widget build(BuildContext context) { + final processThumbnail = useFuture( + useMemoized(() => _generateThumbnail(slide, context), [slide]), + ); + return LayoutBuilder(builder: (context, constraints) { + final child = processThumbnail.when( + data: (file) { + return Image( + gaplessPlayback: true, + image: getImageProvider(file.path), + ); + }, + loading: () { + return IsometricLoading(); + }, + error: (error, _) { + return const Center( + child: Text('Error loading image'), + ); + }, + ); + + return GestureDetector( + onTap: onTap, + child: _PreviewContainer( + selected: selected, + child: AspectRatio( + aspectRatio: kAspectRatio, + child: Stack( + children: [ + AspectRatio( + aspectRatio: kAspectRatio, + child: child, + ), + Positioned( + top: 0, + right: 0, + left: 0, + child: SizedBox( + child: processThumbnail.isRefreshing + ? const LinearProgressIndicator( + minHeight: 3, + backgroundColor: Colors.transparent, + ) + : null, + ), + ), + Positioned( + right: 0, + bottom: 0, + child: Container( + padding: const EdgeInsets.fromLTRB(12, 4, 12, 4), + color: Colors.black.withOpacity(0.9), + child: Text( + '$page', + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + ), + ], + ), + ), + ), + ); + }); + } +} + +class _PreviewContainer extends StatelessWidget { + final Widget child; + final bool selected; + + const _PreviewContainer({ + required this.selected, + required this.child, + }); + + @override + Widget build(BuildContext context) { + final style = Style( + $box.color.$neutral(2), + $box.margin.all(8), + $box.border.width(2), + $box.shadow( + blurRadius: 4, + spreadRadius: 1, + ), + + selected ? $box.wrap.scale(1.05) : $box.wrap.scale(1), + selected ? $box.wrap.opacity(1) : $box.wrap.opacity(0.5), + selected ? $box.border.color.$accent() : $box.border.color.transparent(), + // $on.hover( + // $box.wrap.opacity(1), + // ), + ).animate(); + + return Box( + style: style, + child: child, + ); + } +} + +Future _generateThumbnail(Slide slide, BuildContext context) async { + final thumbnailFile = + ReferenceService.instance.getAssetFile('thumbnail_${slide.key}.png'); + + if (!kCanRunProcess || await thumbnailFile.exists()) { + return thumbnailFile; + } + + final imageData = await SnapshotService.instance.generate( + quality: SnapshotQuality.low, + slide: slide, + ); + + await thumbnailFile.writeAsBytes(imageData); + + return thumbnailFile; +} diff --git a/packages/superdeck/lib/components/atoms/slide_view.dart b/packages/superdeck/lib/components/atoms/slide_view.dart new file mode 100644 index 00000000..1561225c --- /dev/null +++ b/packages/superdeck/lib/components/atoms/slide_view.dart @@ -0,0 +1,63 @@ +import 'package:flutter/material.dart'; + +import '../../providers/slide_provider.dart'; +import '../../providers/snapshot_provider.dart'; +import '../../providers/style_provider.dart'; +import '../../superdeck.dart'; +import 'cache_image_widget.dart'; +import 'transition_widget.dart'; + +class SlideView extends StatelessWidget { + const SlideView( + this.slide, { + super.key, + }); + + final T slide; + + @override + Widget build(BuildContext context) { + final slide = this.slide; + + final variantStyle = StyleProvider.of(context, slide.style); + + final isCapturing = SnapshotProvider.isCapturingOf(context); + final duration = + isCapturing ? Duration.zero : const Duration(milliseconds: 300); + + final backgroundWidget = slide.background != null + ? CacheImage( + url: slide.background!, + fit: BoxFit.cover, + alignment: Alignment.center, + ) + : const SizedBox(); + + return TransitionWidget( + key: ValueKey(slide.transition), + transition: slide.transition, + child: SpecBuilder( + style: variantStyle, + builder: (context) { + final spec = SlideSpec.of(context); + return Builder(builder: (context) { + return AnimatedBoxSpecWidget( + spec: spec.outerContainer, + duration: duration, + child: Stack( + children: [ + Positioned.fill(child: backgroundWidget), + AnimatedBoxSpecWidget( + spec: spec.innerContainer, + duration: duration, + child: SlideBuilder(slide), + ), + ], + ), + ); + }); + }, + ), + ); + } +} diff --git a/lib/components/atoms/transition_widget.dart b/packages/superdeck/lib/components/atoms/transition_widget.dart similarity index 100% rename from lib/components/atoms/transition_widget.dart rename to packages/superdeck/lib/components/atoms/transition_widget.dart diff --git a/packages/superdeck/lib/components/molecules/code_preview.dart b/packages/superdeck/lib/components/molecules/code_preview.dart new file mode 100644 index 00000000..453a4ee8 --- /dev/null +++ b/packages/superdeck/lib/components/molecules/code_preview.dart @@ -0,0 +1,51 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +class ExamplePreview extends StatelessWidget { + const ExamplePreview({ + super.key, + required this.args, + required this.builder, + }); + + final Map args; + + final Widget Function(BuildContext) builder; + + @override + Widget build(BuildContext context) { + return ExampleArgsProvider( + args: args, + child: Center( + child: Builder(builder: builder), + ), + ); + } +} + +extension BuildContextExampleX on BuildContext { + Map get args { + return ExampleArgsProvider.of(this); + } +} + +class ExampleArgsProvider extends InheritedWidget { + const ExampleArgsProvider({ + required this.args, + required super.child, + super.key, + }); + + final Map args; + + static Map of(BuildContext context) { + final provider = + context.dependOnInheritedWidgetOfExactType(); + return provider?.args ?? {}; + } + + @override + bool updateShouldNotify(ExampleArgsProvider oldWidget) { + return !mapEquals(args, oldWidget.args); + } +} diff --git a/lib/components/molecules/exception_widget.dart b/packages/superdeck/lib/components/molecules/exception_widget.dart similarity index 100% rename from lib/components/molecules/exception_widget.dart rename to packages/superdeck/lib/components/molecules/exception_widget.dart diff --git a/packages/superdeck/lib/components/molecules/navigation_rail.dart b/packages/superdeck/lib/components/molecules/navigation_rail.dart new file mode 100644 index 00000000..fc65f8dd --- /dev/null +++ b/packages/superdeck/lib/components/molecules/navigation_rail.dart @@ -0,0 +1,68 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:mix/mix.dart'; + +import '../remix/button.dart'; + +class CustomNavigationRail extends HookWidget { + final int selectedIndex; + final ValueChanged? onDestinationSelected; + final List destinations; + final bool displayLabel; + final double? leading; + final double? trailing; + + CustomNavigationRail({ + required this.selectedIndex, + this.onDestinationSelected, + required this.destinations, + this.displayLabel = false, + this.leading, + this.trailing, + }); + + @override + Widget build(BuildContext context) { + final _buildDestination = useCallback((int index) { + final destination = destinations[index]; + final isSelected = selectedIndex == index; + + return Padding( + padding: const EdgeInsets.symmetric(vertical: 4), + child: SDIconButton( + icon: destination.icon, + onPressed: () => onDestinationSelected?.call(index), + selected: isSelected, + ), + ); + }, [selectedIndex, destinations]); + + return VBox( + style: _containerStyle, + children: [ + if (leading != null) SizedBox(height: leading), + for (int i = 0; i < destinations.length; i++) _buildDestination(i), + if (trailing != null) SizedBox(height: trailing), + ], + ); + } +} + +get _containerStyle => Style( + $box.color.black(), + $box.padding(16), + $box.border.right( + color: Colors.white10, + width: 1, + ), + ); + +class CustomNavigationRailDestination { + final IconData icon; + final String label; + + CustomNavigationRailDestination({ + required this.icon, + required this.label, + }); +} diff --git a/lib/components/molecules/scaled_app.dart b/packages/superdeck/lib/components/molecules/scaled_app.dart similarity index 100% rename from lib/components/molecules/scaled_app.dart rename to packages/superdeck/lib/components/molecules/scaled_app.dart diff --git a/lib/components/molecules/slide_content.dart b/packages/superdeck/lib/components/molecules/slide_content.dart similarity index 73% rename from lib/components/molecules/slide_content.dart rename to packages/superdeck/lib/components/molecules/slide_content.dart index 6a2d94af..da50a9ef 100644 --- a/lib/components/molecules/slide_content.dart +++ b/packages/superdeck/lib/components/molecules/slide_content.dart @@ -2,37 +2,34 @@ import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; import '../../models/options_model.dart'; -import '../../providers/slide_provider.dart'; +import '../../providers/snapshot_provider.dart'; import '../../styles/style_spec.dart'; import '../atoms/markdown_viewer.dart'; class SlideContent extends StatelessWidget { const SlideContent({ - required this.data, + required this.content, required this.options, super.key, }); - final String data; + final String content; + final ContentOptions? options; @override Widget build(context) { - final spec = SlideSpec.of(context); - final alignment = options?.alignment ?? ContentAlignment.center; + final spec = SlideSpec.of(context); + final isCapturing = SnapshotProvider.isCapturingOf(context); - final isExporting = SlideProvider.isSnapshotOf(context); - - Widget child = IntrinsicWidth( - child: AnimatedMarkdownViewer( - content: data, - spec: spec, - duration: Durations.medium1, - ), + Widget child = AnimatedMarkdownViewer( + content: content, + spec: spec, + duration: Durations.medium1, ); - if (!isExporting) { + if (!isCapturing) { child = SingleChildScrollView( child: child, ); @@ -44,7 +41,6 @@ class SlideContent extends StatelessWidget { ], ); } - return AnimatedBoxSpecWidget( duration: const Duration(milliseconds: 300), spec: spec.contentContainer.copyWith( diff --git a/packages/superdeck/lib/components/molecules/slide_preview.dart b/packages/superdeck/lib/components/molecules/slide_preview.dart new file mode 100644 index 00000000..c3fef6ec --- /dev/null +++ b/packages/superdeck/lib/components/molecules/slide_preview.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +import '../../superdeck.dart'; +import '../atoms/slide_view.dart'; +import 'scaled_app.dart'; + +class SlidePreview extends StatelessWidget { + const SlidePreview( + this.slide, { + super.key, + }); + + final T slide; + + @override + Widget build(BuildContext context) { + return Center( + child: Container( + decoration: BoxDecoration( + color: const Color.fromARGB(255, 68, 60, 60), + boxShadow: [ + BoxShadow( + color: Colors.black.withOpacity(0.3), + blurRadius: 6, + spreadRadius: 3, + ), + ], + ), + child: ScaledWidget( + child: SlideView(slide), + ), + ), + ); + } +} diff --git a/packages/superdeck/lib/components/molecules/split_view.dart b/packages/superdeck/lib/components/molecules/split_view.dart new file mode 100644 index 00000000..147b6e13 --- /dev/null +++ b/packages/superdeck/lib/components/molecules/split_view.dart @@ -0,0 +1,106 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:go_router/go_router.dart'; + +import '../../helpers/hooks.dart'; +import '../../helpers/routes.dart'; +import '../../helpers/utils.dart'; +import '../atoms/sized_transition.dart'; +import '../organisms/chat_panel.dart'; +import '../organisms/presentation_side_panel.dart'; +import 'navigation_rail.dart'; + +class SplitView extends HookWidget { + final StatefulNavigationShell navigationShell; + + const SplitView({ + super.key, + required this.navigationShell, + }); + + final _maxWidth = 400.0; + final _thumbnailWidth = 300.0; + + @override + Widget build(BuildContext context) { + final sideSize = useState(0.0); + + useEffect(() { + if (context.isMobileLandscape) { + sideSize.value = 200.0; + } else { + sideSize.value = _maxWidth; + } + }, [navigationShell.currentIndex]); + + final animationController = useAnimationController( + duration: Durations.medium1, + ); + + final animation = useAnimation(CurvedAnimation( + parent: animationController, + curve: Curves.ease, + )); + + usePostFrameEffect(() { + if (context.isDrawerOpen) { + animationController.forward(); + } else { + animationController.reverse(); + } + }, [context.isDrawerOpen]); + + return LayoutBuilder( + builder: (context, constraints) { + final sidebar = switch (navigationShell.currentIndex) { + 0 => SizedBox( + width: _thumbnailWidth, + child: const PresentationSidePanel(), + ), + 1 => SizedBox( + width: _thumbnailWidth, + child: const ChatScreen(), + ), + _ => const SizedBox.shrink(), + }; + + return Row( + children: [ + SizedTransition( + sizeFactor: animation, + child: Row( + children: [ + CustomNavigationRail( + selectedIndex: navigationShell.currentIndex, + onDestinationSelected: (value) { + navigationShell.goBranch(value); + }, + leading: 20, + destinations: [ + CustomNavigationRailDestination( + icon: Icons.view_carousel, + label: 'Home', + ), + CustomNavigationRailDestination( + icon: Icons.chat, + label: 'Chat', + ), + if (!kIsWeb) + CustomNavigationRailDestination( + icon: Icons.picture_as_pdf, + label: 'Export', + ), + ], + ), + sidebar + ], + ), + ), + Expanded(child: navigationShell) + ], + ); + }, + ); + } +} diff --git a/packages/superdeck/lib/components/organisms/app_shell.dart b/packages/superdeck/lib/components/organisms/app_shell.dart new file mode 100644 index 00000000..ff797bbb --- /dev/null +++ b/packages/superdeck/lib/components/organisms/app_shell.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:go_router/go_router.dart'; + +import '../../helpers/routes.dart'; +import '../../helpers/utils.dart'; +import '../../superdeck.dart'; +import '../molecules/split_view.dart'; + +final kScaffoldKey = GlobalKey(); + +/// Builds the "shell" for the app by building a Scaffold with a +/// BottomNavigationBar, where [child] is placed in the body of the Scaffold. +class AppShell extends HookWidget { + const AppShell({ + required this.navigationShell, + super.key = const ValueKey('app_shell'), + }); + + /// The navigation shell and container for the branch Navigators. + final StatefulNavigationShell navigationShell; + + @override + Widget build(BuildContext context) { + final isSmall = context.isSmall; + + final slides = useSlides(); + + final invalidSlides = slides.whereType().toList(); + final handleNext = useCallback(() { + if (context.currentSlidePage < slides.length) { + context.nextSlide(); + } + }, [slides]); + + final handlePrevious = useCallback(() { + if (context.currentSlidePage > 1) { + context.previousSlide(); + } + }, [slides]); + + final bindings = { + const SingleActivator( + LogicalKeyboardKey.arrowRight, + meta: true, + ): handleNext, + const SingleActivator( + LogicalKeyboardKey.arrowDown, + meta: true, + ): handleNext, + const SingleActivator( + LogicalKeyboardKey.space, + meta: true, + ): handleNext, + const SingleActivator( + LogicalKeyboardKey.arrowLeft, + meta: true, + ): handlePrevious, + const SingleActivator( + LogicalKeyboardKey.arrowUp, + meta: true, + ): handlePrevious, + }; + + return CallbackShortcuts( + bindings: bindings, + child: Scaffold( + backgroundColor: const Color.fromARGB(255, 9, 9, 9), + bottomNavigationBar: null, + extendBodyBehindAppBar: true, + extendBody: true, + key: kScaffoldKey, + floatingActionButtonLocation: isSmall + ? FloatingActionButtonLocation.miniEndFloat + : FloatingActionButtonLocation.miniStartFloat, + floatingActionButton: FloatingActionButton.small( + onPressed: context.toggleDrawer, + child: Badge( + label: Text(invalidSlides.length.toString()), + isLabelVisible: invalidSlides.isNotEmpty, + child: const Icon(Icons.menu), + ), + ), + body: SplitView( + navigationShell: navigationShell, + ), + ), + ); + } +} diff --git a/packages/superdeck/lib/components/organisms/chat_panel.dart b/packages/superdeck/lib/components/organisms/chat_panel.dart new file mode 100644 index 00000000..258fef96 --- /dev/null +++ b/packages/superdeck/lib/components/organisms/chat_panel.dart @@ -0,0 +1,356 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:google_generative_ai/google_generative_ai.dart'; + +import '../../chat/components/typing_indicator.dart'; +import '../../chat/prompt.dart'; +import '../../helpers/constants.dart'; +import '../../helpers/extensions.dart'; +import '../../services/reference_service.dart'; +import '../atoms/markdown_viewer.dart'; + +const _apiKey = ''; + +final _geminiFlash = 'gemini-1.5-flash-latest'; +final _geminiPro = 'gemini-1.5-pro'; + +class ChatScreen extends StatelessWidget { + const ChatScreen({super.key}); + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: Text( + 'Gemini', + style: context.textTheme.bodySmall, + ), + ), + body: const ChatWidget(apiKey: _apiKey), + ); + } +} + +class ChatWidget extends StatefulHookWidget { + const ChatWidget({ + required this.apiKey, + super.key, + }); + + final String apiKey; + + @override + State createState() => _ChatWidgetState(); +} + +typedef GeneratedContent = ({Image? image, String? text, bool fromUser}); + +class _ChatWidgetState extends State { + late final GenerativeModel _model; + late final ChatSession _chat; + + final _generatedContent = []; + + @override + void initState() { + super.initState(); + _model = GenerativeModel( + model: _geminiPro, + apiKey: widget.apiKey, + systemInstruction: Content.system( + presentationAssistantPrompt, + )); + if (kCanRunProcess) { + ReferenceService.instance.loadMarkdown().then((value) { + final content = '\n$value\n'; + _chat = _model.startChat(history: [ + // add around value + Content.text(content), + ]); + }); + } else { + _chat = _model.startChat(); + } + } + + @override + Widget build(BuildContext context) { + final _textController = useTextEditingController(); + final _scrollController = useScrollController(); + final _loading = useState(false); + final _applyChanges = useState(false); + + void _scrollDown() { + WidgetsBinding.instance.addPostFrameCallback( + (_) => _scrollController.animateTo( + _scrollController.position.maxScrollExtent, + duration: const Duration( + milliseconds: 200, + ), + curve: Curves.easeOutCirc, + ), + ); + } + + final _sendChatMessage = useCallback((String message) async { + _loading.value = true; + + _textController.clear(); + + try { + if (!_applyChanges.value) { + _generatedContent.add((image: null, text: message, fromUser: true)); + } + final response = await _chat.sendMessage( + Content.text(message), + ); + final text = response.text; + if (!_applyChanges.value) { + _generatedContent.add((image: null, text: text, fromUser: false)); + } else { + // Replace and with empty string + final provideMarkdownEdits = text! + .replaceAll('', '') + .replaceAll('', ''); + await ReferenceService.instance.saveMarkdown(provideMarkdownEdits); + _generatedContent.add(( + image: null, + text: 'I have applied the changes.', + fromUser: false + )); + } + + if (text == null) { + _showError('No response from API.'); + return; + } else { + _loading.value = false; + _scrollDown(); + } + } catch (e) { + _showError(e.toString()); + } finally { + _loading.value = false; + } + }, []); + + final _textFieldFocus = useFocusNode( + onKeyEvent: (node, event) { + if (event is KeyUpEvent) { + return KeyEventResult.ignored; + } + + final isEnterKey = event.logicalKey == LogicalKeyboardKey.enter || + event.logicalKey == LogicalKeyboardKey.numpadEnter; + + if (HardwareKeyboard.instance.isShiftPressed && isEnterKey) { + _textController.value = TextEditingValue( + text: _textController.text + '\n', + selection: TextSelection.collapsed( + offset: _textController.text.length + 1), + ); + return KeyEventResult.handled; + } + + if (isEnterKey) { + if (event is KeyDownEvent) { + _sendChatMessage(_textController.text); + return KeyEventResult.handled; + } + } + return KeyEventResult.ignored; + }, + ); + useEffect(() { + if (!_loading.value) { + _textFieldFocus.requestFocus(); + } + }, [_loading.value]); + + Widget _buildSendButton() { + if (!_loading.value) { + return IconButton( + onPressed: () async { + _sendChatMessage(_textController.text); + }, + icon: Icon( + Icons.send, + color: Theme.of(context).colorScheme.primary, + ), + ); + } + + return Padding( + padding: EdgeInsets.only(right: 16.0), + child: WaitingIndicator(isTyping: true), + ); + } + + Widget _buildApplyChangesButton() { + return Padding( + padding: const EdgeInsets.all(8.0), + child: IconButton( + onPressed: () async { + _applyChanges.value = true; + _sendChatMessage(provideMarkdownEdits); + }, + icon: Icon( + Icons.bolt, + color: Theme.of(context).colorScheme.primary, + ), + ), + ); + } + + final textFieldDecoration = InputDecoration( + filled: true, + fillColor: Colors.black45, + hoverColor: Colors.black45, + hintText: _loading.value ? '' : 'Type a message', + enabled: !_loading.value, + hintStyle: TextStyle(color: Colors.white), + suffixIcon: _buildSendButton(), + prefixIcon: _buildApplyChangesButton(), + suffixIconConstraints: const BoxConstraints( + minWidth: 20, + minHeight: 20, + ), + prefixIconConstraints: const BoxConstraints( + minWidth: 20, + minHeight: 20, + ), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(16), + borderSide: BorderSide.none, + ), + contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 16), + ); + + return Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: _apiKey.isNotEmpty + ? ListView.builder( + controller: _scrollController, + itemBuilder: (context, idx) { + final content = _generatedContent[idx]; + return MessageWidget( + text: content.text, + image: content.image, + isFromUser: content.fromUser, + ); + }, + itemCount: _generatedContent.length, + ) + : ListView( + children: const [ + Text( + 'No API key found. Please provide an API Key using ' + "'--dart-define' to set the 'API_KEY' declaration.", + ), + ], + ), + ), + Row( + children: [ + Expanded( + child: ConstrainedBox( + constraints: const BoxConstraints(maxHeight: 80), + child: Padding( + padding: EdgeInsets.symmetric(horizontal: 8, vertical: 8), + child: TextField( + enabled: !_loading.value, + keyboardType: TextInputType.multiline, + autofocus: true, + expands: true, + style: context.textTheme.bodyMedium, + focusNode: _textFieldFocus, + decoration: textFieldDecoration, + maxLines: null, + controller: _textController, + ), + ), + ), + ), + ], + ), + ], + ), + ); + } + + void _showError(String message) { + showDialog( + context: context, + builder: (context) { + return AlertDialog( + title: const Text('Something went wrong'), + content: SingleChildScrollView( + child: SelectableText(message), + ), + actions: [ + TextButton( + onPressed: () { + Navigator.of(context).pop(); + }, + child: const Text('OK'), + ) + ], + ); + }, + ); + } +} + +class MessageWidget extends StatelessWidget { + const MessageWidget({ + super.key, + this.image, + this.text, + required this.isFromUser, + }); + + final Image? image; + final String? text; + final bool isFromUser; + + @override + Widget build(BuildContext context) { + return Row( + mainAxisAlignment: + isFromUser ? MainAxisAlignment.end : MainAxisAlignment.start, + children: [ + Flexible( + child: Container( + constraints: const BoxConstraints(maxWidth: 520), + decoration: BoxDecoration( + color: isFromUser + ? Theme.of(context).colorScheme.primaryContainer + : Theme.of(context).colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(18), + ), + padding: const EdgeInsets.symmetric( + vertical: 15, + horizontal: 20, + ), + margin: const EdgeInsets.symmetric(vertical: 8, horizontal: 8), + child: Column(children: [ + if (text case final text?) + MarkdownBody( + data: text, + builders: { + 'code': SampleCodeElementBuilder(null), + }, + ), + if (image case final image?) image, + ]))), + ], + ); + } +} diff --git a/lib/components/organisms/drawer.dart b/packages/superdeck/lib/components/organisms/drawer.dart similarity index 87% rename from lib/components/organisms/drawer.dart rename to packages/superdeck/lib/components/organisms/drawer.dart index 13b8911e..b258e5ff 100644 --- a/lib/components/organisms/drawer.dart +++ b/packages/superdeck/lib/components/organisms/drawer.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; enum SideMenu { preview(icon: Icons.play_arrow, label: 'Preview'), - export(icon: Icons.save, label: 'Export'), + clearCache(icon: Icons.cached, label: 'Clear Cache'); const SideMenu({ @@ -19,6 +19,5 @@ enum SideMenu { static List prodMenu = [ preview, - export, ]; } diff --git a/packages/superdeck/lib/components/organisms/presentation_side_panel.dart b/packages/superdeck/lib/components/organisms/presentation_side_panel.dart new file mode 100644 index 00000000..2e1f81d9 --- /dev/null +++ b/packages/superdeck/lib/components/organisms/presentation_side_panel.dart @@ -0,0 +1,85 @@ +import 'package:collection/collection.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +import '../../helpers/hooks.dart'; +import '../../helpers/routes.dart'; +import '../../helpers/utils.dart'; +import '../../providers/controller.dart'; +import '../atoms/slide_thumbnail.dart'; + +class PresentationSidePanel extends HookWidget { + const PresentationSidePanel({ + super.key, + }); + + final _duration = const Duration(milliseconds: 300); + final _curve = Curves.easeInOutCubic; + + @override + Widget build(BuildContext context) { + final currentSlideIndex = context.currentSlidePage - 1; + + final slides = useSlides(); + final controller = useScrollVisibleController(); + final visibleItems = controller.visibleItems; + + usePostFrameEffect(() { + if (visibleItems.isEmpty) return; + + final visibleItem = + visibleItems.firstWhereOrNull((e) => e.index == currentSlideIndex); + + double alignment; + + if (visibleItem == null) { + final isBeginning = visibleItems.first.index > currentSlideIndex; + + alignment = isBeginning ? 0 : 0.7; + } else { + if (visibleItem.itemTrailingEdge > 1) { + final totalSpace = + visibleItem.itemTrailingEdge - visibleItem.itemLeadingEdge; + alignment = 1 - totalSpace; + } else if (visibleItem.itemLeadingEdge < 0) { + alignment = 0; + } else { + alignment = visibleItem.itemLeadingEdge; + } + } + controller.itemScrollController.scrollTo( + index: currentSlideIndex, + alignment: alignment, + duration: _duration, + curve: _curve, + ); + + return; + }, [currentSlideIndex, slides]); + + return Container( + color: Colors.black, + child: ScrollablePositionedList.builder( + scrollDirection: context.isSmall ? Axis.horizontal : Axis.vertical, + itemCount: slides.length, + itemPositionsListener: controller.itemPositionsListener, + itemScrollController: controller.itemScrollController, + padding: const EdgeInsets.all(20), + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric( + vertical: 8, + horizontal: 10, + ), + child: SlideThumbnail( + page: index + 1, + selected: currentSlideIndex == index, + onTap: () => context.goToSlide(index + 1), + slide: slides[index], + ), + ); + }), + ); + } +} diff --git a/packages/superdeck/lib/components/remix/button.dart b/packages/superdeck/lib/components/remix/button.dart new file mode 100644 index 00000000..89c271ce --- /dev/null +++ b/packages/superdeck/lib/components/remix/button.dart @@ -0,0 +1,100 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/widgets.dart'; +import 'package:remix/remix.dart'; + +class SDButton extends StatelessWidget { + const SDButton({ + required this.onPressed, + super.key, + required this.label, + this.icon, + }); + + final VoidCallback onPressed; + final String label; + + final IconData? icon; + + @override + Widget build(BuildContext context) { + return RxButton( + onPressed: onPressed, + type: ButtonVariant.surface, + iconLeft: icon, + label: label, + ); + } +} + +class SDButtonSolid extends StatelessWidget { + const SDButtonSolid({ + required this.onPressed, + super.key, + required this.label, + this.icon, + }); + + final VoidCallback onPressed; + final String label; + + final IconData? icon; + + @override + Widget build(BuildContext context) { + return RxButton( + onPressed: onPressed, + type: ButtonVariant.solid, + iconLeft: icon, + label: label, + ); + } +} + +class SDOutlinedButton extends StatelessWidget { + const SDOutlinedButton({ + required this.onPressed, + super.key, + required this.label, + this.icon, + }); + + final VoidCallback onPressed; + final String label; + + final IconData? icon; + + @override + Widget build(BuildContext context) { + return RxButton( + onPressed: onPressed, + type: ButtonVariant.outline, + iconLeft: icon, + label: label, + ); + } +} + +class SDIconButton extends StatelessWidget { + const SDIconButton({ + required this.onPressed, + super.key, + required this.icon, + this.selected = false, + }); + + final VoidCallback onPressed; + final bool selected; + + final IconData icon; + + @override + Widget build(BuildContext context) { + return RxButton( + onPressed: onPressed, + type: selected ? ButtonVariant.surface : ButtonVariant.ghost, + iconLeft: icon, + size: ButtonSize.large, + label: '', + ); + } +} diff --git a/packages/superdeck/lib/components/remix/button_style.dart b/packages/superdeck/lib/components/remix/button_style.dart new file mode 100644 index 00000000..e69de29b diff --git a/packages/superdeck/lib/components/superdeck_app.dart b/packages/superdeck/lib/components/superdeck_app.dart new file mode 100644 index 00000000..53c8ec23 --- /dev/null +++ b/packages/superdeck/lib/components/superdeck_app.dart @@ -0,0 +1,111 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:remix/remix.dart'; +import 'package:window_manager/window_manager.dart'; + +import '../../helpers/syntax_highlighter.dart'; +import '../../superdeck.dart'; +import '../helpers/constants.dart'; +import '../helpers/routes.dart'; +import '../helpers/theme.dart'; +import '../providers/examples_provider.dart'; +import '../providers/snapshot_provider.dart'; +import '../providers/style_provider.dart'; +import 'atoms/loading_indicator.dart'; + +final _uniqueKey = UniqueKey(); +var _initialized = false; + +class SuperDeckApp extends HookWidget { + const SuperDeckApp({ + super.key, + this.baseStyle = const Style.empty(), + this.styles = const {}, + this.examples = const {}, + }); + + final Style baseStyle; + final Map examples; + final Map styles; + + static Future initialize() async { + // Return if its initialized + if (_initialized) return; + + _initialized = true; + + WidgetsFlutterBinding.ensureInitialized(); + + await Future.wait([ + SuperDeckController.initialize(), + SyntaxHighlight.initialize(), + _initializeWindowManager(), + ]); + } + + @override + Widget build(BuildContext context) { + return FutureBuilder( + future: SuperDeckApp.initialize(), + builder: (context, snapshot) { + return StyleProvider( + baseStyle: baseStyle, + styles: styles, + child: ExamplesProvider( + examples: examples, + child: ListenableBuilder( + listenable: $superdeck, + builder: (context, snapshot) { + return RemixTokens( + data: RemixTokens.dark, + child: MaterialApp.router( + debugShowCheckedModeBanner: true, + title: 'Superdeck', + routerConfig: goRouterConfig, + theme: theme, + builder: (context, child) { + return SnapshotProvider( + isCapturing: true, + child: LoadingOverlay( + isLoading: $superdeck.loading, + key: _uniqueKey, + child: $superdeck.completed + ? child! + : const SizedBox(), + ), + ); + }, + ), + ); + }), + ), + ); + }, + ); + } +} + +Future _initializeWindowManager() async { + if (kIsWeb) return; + + // Must add this line. + await windowManager.ensureInitialized(); + + const windowOptions = WindowOptions( + size: kResolution, + backgroundColor: Colors.black, + skipTaskbar: false, + minimumSize: kResolution, + titleBarStyle: TitleBarStyle.hidden, + ); + + windowManager.waitUntilReadyToShow(windowOptions, () async { + await windowManager.show(); + await windowManager.focus(); + }); + + await windowManager.setAspectRatio(kAspectRatio); +} diff --git a/lib/helpers/memory_image_provider.dart b/packages/superdeck/lib/helpers/cache_memory_image.dart similarity index 100% rename from lib/helpers/memory_image_provider.dart rename to packages/superdeck/lib/helpers/cache_memory_image.dart diff --git a/lib/helpers/constants.dart b/packages/superdeck/lib/helpers/constants.dart similarity index 100% rename from lib/helpers/constants.dart rename to packages/superdeck/lib/helpers/constants.dart diff --git a/lib/helpers/deep_merge.dart b/packages/superdeck/lib/helpers/deep_merge.dart similarity index 100% rename from lib/helpers/deep_merge.dart rename to packages/superdeck/lib/helpers/deep_merge.dart diff --git a/packages/superdeck/lib/helpers/dialog_page.dart b/packages/superdeck/lib/helpers/dialog_page.dart new file mode 100644 index 00000000..4db74ae2 --- /dev/null +++ b/packages/superdeck/lib/helpers/dialog_page.dart @@ -0,0 +1,57 @@ +import 'package:flutter/material.dart'; + +import '../superdeck.dart'; + +/// A dialog page with Material entrance and exit animations, modal barrier color, +/// and modal barrier behavior (dialog is dismissible with a tap on the barrier). +class DialogPage extends Page { + final Offset? anchorPoint; + final Color? barrierColor; + final bool barrierDismissible; + final String? barrierLabel; + final bool useSafeArea; + final CapturedThemes? themes; + final WidgetBuilder builder; + + const DialogPage({ + required this.builder, + this.anchorPoint, + this.barrierColor = Colors.black54, + this.barrierDismissible = true, + this.barrierLabel, + this.useSafeArea = true, + this.themes, + super.key, + super.name, + super.arguments, + super.restorationId, + }); + + @override + Route createRoute(BuildContext context) { + return DialogRoute( + context: kScaffoldKey.currentContext!, + settings: this, + builder: builder, + anchorPoint: anchorPoint, + barrierColor: barrierColor, + barrierDismissible: barrierDismissible, + barrierLabel: barrierLabel, + useSafeArea: useSafeArea, + themes: themes, + ); + } +} + +class ModalPage extends Page { + const ModalPage({required this.child}); + + final Widget child; + + @override + Route createRoute(BuildContext context) => ModalBottomSheetRoute( + settings: this, + builder: (context) => child, + isScrollControlled: false, + ); +} diff --git a/packages/superdeck/lib/helpers/extensions.dart b/packages/superdeck/lib/helpers/extensions.dart new file mode 100644 index 00000000..1efeeb9a --- /dev/null +++ b/packages/superdeck/lib/helpers/extensions.dart @@ -0,0 +1,45 @@ +import 'package:flutter/material.dart'; + +extension AsyncSnapshotX on AsyncSnapshot { + bool get isLoading => connectionState == ConnectionState.waiting; + + bool get isRefreshing => connectionState == ConnectionState.active; + Widget when({ + required Widget Function(T data) data, + required Widget Function(Object? error, StackTrace? stackTrace) error, + required Widget Function() loading, + }) { + if (hasError) { + return error(error, stackTrace); + } + + if (isLoading) { + return loading(); + } + + return data(this.data as T); + } +} + +extension BuildContextX on BuildContext { + ThemeData get theme => Theme.of(this); + TextTheme get textTheme => theme.textTheme; + ColorScheme get colorScheme => theme.colorScheme; +} + +extension StringX on String { + String capitalize() { + return "${this[0].toUpperCase()}${this.substring(1)}"; + } + + String snakeCase() { + return this + .replaceAll(RegExp(r'\s+'), '_') + .replaceAllMapped( + RegExp( + r'[A-Z]{2,}(?=[A-Z][a-z]+[0-9]*|\b)|[A-Z]?[a-z]+[0-9]*|[A-Z]|[0-9]+'), + (match) => "${match.group(0)!.toLowerCase()}_") + .replaceAll(RegExp(r'(_)\1+'), '_') + .replaceAll(RegExp(r'^_|_$'), ''); + } +} diff --git a/packages/superdeck/lib/helpers/hooks.dart b/packages/superdeck/lib/helpers/hooks.dart new file mode 100644 index 00000000..a5a3b83b --- /dev/null +++ b/packages/superdeck/lib/helpers/hooks.dart @@ -0,0 +1,139 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:scrollable_positioned_list/scrollable_positioned_list.dart'; + +void useEffectOnce(void Function()? Function() effect) { + useEffect(effect, []); +} + +void usePostFrameEffect(void Function() effect, + [List keys = const []]) { + useEffect(() { + WidgetsBinding.instance.addPostFrameCallback((_) { + effect(); + }); + }, keys); +} + +typedef ScrollVisibleControllerData = ({ + ItemScrollController itemScrollController, + ItemPositionsListener itemPositionsListener, + List visibleItems, +}); + +ScrollVisibleControllerData useScrollVisibleController({ + List? keys, +}) { + return use( + _ScrollVisibleController(keys: keys), + ); +} + +class _ScrollVisibleController extends Hook { + const _ScrollVisibleController({ + super.keys, + }); + + @override + HookState> + createState() => _ScrollVisibleControllerState(); +} + +class _ScrollVisibleControllerState + extends HookState { + late final controller = ItemScrollController(); + late final itemPositionsListener = ItemPositionsListener.create(); + var visibleItems = []; + + void _listener() { + visibleItems = itemPositionsListener.itemPositions.value.toList(); + } + + @override + void initHook() { + super.initHook(); + itemPositionsListener.itemPositions.addListener(_listener); + } + + @override + ScrollVisibleControllerData build(BuildContext context) => ( + itemScrollController: controller, + itemPositionsListener: itemPositionsListener, + visibleItems: visibleItems, + ); + + @override + void dispose() { + super.dispose(); + itemPositionsListener.itemPositions.removeListener(_listener); + } + + @override + String get debugLabel => 'useScrollVisibleController'; +} + +bool useIsFirstMount() { + final first = useRef(true); + + if (first.value) { + first.value = false; + + return true; + } + + return first.value; +} + +void useUpdateEffect(Dispose? Function() effect, [List? keys]) { + final isFirstMount = useIsFirstMount(); + + useEffect(() { + if (!isFirstMount) { + return effect(); + } + }, keys); +} + +T? useDistinct(T value, [Predicate? compare]) { + compare ??= (prev, next) => prev == next; + + final valueRef = useRef(value); + + if (valueRef.value == null || !compare(valueRef.value, value)) { + valueRef.value = value; + } + + return valueRef.value; +} + +typedef Predicate = bool Function(T prev, T next); + +OverlayPortalController useOverlayPortalController() { + return use( + _OverlayPortalController(), + ); +} + +class _OverlayPortalController extends Hook { + const _OverlayPortalController(); + + @override + HookState> + createState() => _OverlayPortalControllerState(); +} + +class _OverlayPortalControllerState + extends HookState { + late final controller = OverlayPortalController(); + + @override + OverlayPortalController build(BuildContext context) => controller; + + @override + void dispose() { + super.dispose(); + } + + @override + String get debugLabel => 'useOverlayPortalController'; +} diff --git a/lib/helpers/mappers.dart b/packages/superdeck/lib/helpers/mappers.dart similarity index 100% rename from lib/helpers/mappers.dart rename to packages/superdeck/lib/helpers/mappers.dart diff --git a/packages/superdeck/lib/helpers/measure_size.dart b/packages/superdeck/lib/helpers/measure_size.dart new file mode 100644 index 00000000..27dffbe9 --- /dev/null +++ b/packages/superdeck/lib/helpers/measure_size.dart @@ -0,0 +1,88 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; + +typedef OnWidgetSizeChange = void Function(Size size); + +class MeasureSizeRenderObject extends RenderProxyBox { + Size? oldSize; + OnWidgetSizeChange onChange; + + MeasureSizeRenderObject(this.onChange); + + @override + void performLayout() { + super.performLayout(); + + Size newSize = child!.size; + if (oldSize == newSize) return; + + oldSize = newSize; + WidgetsBinding.instance.addPostFrameCallback((_) { + onChange(newSize); + }); + } +} + +class MeasureSize extends SingleChildRenderObjectWidget { + final OnWidgetSizeChange onChange; + + const MeasureSize({ + super.key, + required this.onChange, + required Widget super.child, + }); + + @override + RenderObject createRenderObject(BuildContext context) { + return MeasureSizeRenderObject(onChange); + } + + @override + void updateRenderObject( + BuildContext context, covariant MeasureSizeRenderObject renderObject) { + renderObject.onChange = onChange; + } +} + +class MeasureSingleWidgetSize extends StatefulWidget { + final Widget child; + final void Function(Size) onChange; + + const MeasureSingleWidgetSize( + {super.key, required this.child, required this.onChange}); + + @override + _MeasureSingleWidgetSizeState createState() => + _MeasureSingleWidgetSizeState(); +} + +class _MeasureSingleWidgetSizeState extends State { + final GlobalKey _childKey = GlobalKey(); + + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addPostFrameCallback(_measureHeight); + } + + void _measureHeight(Duration _) async { + final renderBox = + _childKey.currentContext?.findRenderObject() as RenderBox?; + final size = renderBox?.size; + + if (size == null || size == Size.zero) { + WidgetsBinding.instance.addPostFrameCallback(_measureHeight); + return; + } + + widget.onChange(size); + } + + @override + Widget build(BuildContext context) { + return Container( + key: _childKey, + child: widget.child, + ); + } +} diff --git a/packages/superdeck/lib/helpers/routes.dart b/packages/superdeck/lib/helpers/routes.dart new file mode 100644 index 00000000..e2f07ce3 --- /dev/null +++ b/packages/superdeck/lib/helpers/routes.dart @@ -0,0 +1,165 @@ +import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; +import 'package:go_router_paths/go_router_paths.dart'; + +import '../../superdeck.dart'; +import '../screens/export_screen.dart'; +import '../screens/presentation_screen.dart'; +import 'dialog_page.dart'; + +class SDPaths { + static Path get root => Path('/'); + static ExportPath get export => ExportPath(); + static ChatPath get chat => ChatPath(); +} + +class ExportPath extends Path { + ExportPath() : super('export'); +} + +class ChatPath extends Path { + ChatPath() : super('chat'); +} + +class QueryParams { + static const drawer = 'drawer'; + static const slide = 'slide'; +} + +final kRootNavigatorKey = GlobalKey(debugLabel: 'root'); + +Map _previousQueryParams = {}; + +final goRouterConfig = GoRouter( + navigatorKey: kRootNavigatorKey, + initialLocation: SDPaths.root.goRoute, + restorationScopeId: 'root', + routes: [ + StatefulShellRoute.indexedStack( + redirect: (context, state) { + // Get the previous route's state from the navigation history + + // Extract current query parameters + final currentQueryParams = state.uri.queryParameters; + + final openDrawerParam = _previousQueryParams[QueryParams.drawer]; + final slideParam = _previousQueryParams[QueryParams.slide]; + + // Add any additional query parameters if needed + final allQueryParams = { + if (openDrawerParam != null) QueryParams.drawer: openDrawerParam, + if (slideParam != null) QueryParams.slide: slideParam, + ...currentQueryParams, + }; + + _previousQueryParams = allQueryParams; + + // Construct the target URI with query parameters + final uri = state.uri.replace( + queryParameters: allQueryParams.isEmpty ? null : allQueryParams, + ); + + return uri.toString(); + }, + restorationScopeId: 'shell1', + parentNavigatorKey: kRootNavigatorKey, + builder: (context, state, navigationShell) { + return AppShell(navigationShell: navigationShell); + }, + branches: [ + StatefulShellBranch( + routes: [ + GoRoute( + path: SDPaths.root.goRoute, + pageBuilder: (context, state) => _getPage( + PresentationScreen(), + state, + ), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: SDPaths.chat.goRoute, + pageBuilder: (context, state) => _getPage( + PresentationScreen(), + state, + ), + ), + ], + ), + StatefulShellBranch( + routes: [ + GoRoute( + path: SDPaths.export.goRoute, + pageBuilder: (context, state) { + return _getPage(ExportScreen(), state); + }, + ), + ], + ), + ], + ), + ], +); + +MaterialPage _getPage(Widget child, GoRouterState state) { + return MaterialPage(key: state.pageKey, child: child, maintainState: false); +} + +DialogPage _getDialogPage(Widget child, GoRouterState state) { + return DialogPage(key: state.pageKey, builder: (_) => Dialog(child: child)); +} + +extension BuildContextRoutesX on BuildContext { + int get currentSlidePage => int.parse(_slidePage ?? '1'); + int get currentSlideIndex => currentSlidePage - 1; + + void goToSlide(int page) => go(_replaceQueryParam('slide', '$page')); + + String get currentPath { + return GoRouterState.of(this).uri.toString(); + } + + void nextSlide() => goToSlide(currentSlidePage + 1); + + void previousSlide() => goToSlide(currentSlidePage - 1); + + String _replaceQueryParam(String key, String value) { + final uri = GoRouterState.of(this).uri; + final queryParameters = Map.from(uri.queryParameters); + queryParameters[key] = value; + return uri.replace(queryParameters: queryParameters).toString(); + } + + void openDrawer() => go(_replaceQueryParam(QueryParams.drawer, '1')); + + void closeDrawer() => go(_replaceQueryParam(QueryParams.drawer, '0')); + + void toggleDrawer() { + if (isDrawerOpen) { + closeDrawer(); + } else { + openDrawer(); + } + } + + Map get _queryParams { + return GoRouterState.of(this).uri.queryParameters; + } + + String? get _drawerParam => _queryParams[QueryParams.drawer]; + + String? get _slidePage => _queryParams[QueryParams.slide]; + + bool get isDrawerOpen => _drawerParam == '1'; + + void goPath(Path path) { + go(path.path); + } + + void pushPath(Path path) { + push(path.path); + } +} diff --git a/lib/helpers/section_tag.dart b/packages/superdeck/lib/helpers/section_tag.dart similarity index 100% rename from lib/helpers/section_tag.dart rename to packages/superdeck/lib/helpers/section_tag.dart diff --git a/lib/helpers/syntax_highlighter.dart b/packages/superdeck/lib/helpers/syntax_highlighter.dart similarity index 100% rename from lib/helpers/syntax_highlighter.dart rename to packages/superdeck/lib/helpers/syntax_highlighter.dart diff --git a/packages/superdeck/lib/helpers/theme.dart b/packages/superdeck/lib/helpers/theme.dart new file mode 100644 index 00000000..455c0bfd --- /dev/null +++ b/packages/superdeck/lib/helpers/theme.dart @@ -0,0 +1,14 @@ +import 'package:flutter/material.dart'; + +ThemeData get theme => ThemeData.dark().copyWith( + colorScheme: ColorScheme.fromSeed( + dynamicSchemeVariant: DynamicSchemeVariant.fidelity, + seedColor: Colors.indigo, + brightness: Brightness.dark, + ), + dialogBackgroundColor: Colors.black, + visualDensity: VisualDensity.adaptivePlatformDensity, + ); + + +// dark cupertino theme \ No newline at end of file diff --git a/packages/superdeck/lib/helpers/universal.dart b/packages/superdeck/lib/helpers/universal.dart new file mode 100644 index 00000000..2a1a0f57 --- /dev/null +++ b/packages/superdeck/lib/helpers/universal.dart @@ -0,0 +1,285 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; + +extension TargetPlatformX on TargetPlatform { + bool get isCupertino => + this == TargetPlatform.iOS || this == TargetPlatform.macOS; + bool get isMaterial => !isCupertino; +} + +class XPlatform extends InheritedWidget { + final TargetPlatform? overridePlatform; + + const XPlatform({ + super.key, + required super.child, + this.overridePlatform, + }); + + static TargetPlatform of(BuildContext context) { + final XPlatform? inherited = + context.dependOnInheritedWidgetOfExactType(); + return inherited?.overridePlatform ?? defaultTargetPlatform; + } + + @override + bool updateShouldNotify(XPlatform oldWidget) { + return overridePlatform != oldWidget.overridePlatform; + } +} + +class XButton extends StatelessWidget { + final VoidCallback onPressed; + final Widget child; + final Color? color; + + XButton({ + required this.onPressed, + required this.child, + this.color, + }); + + @override + Widget build(BuildContext context) { + final platform = XPlatform.of(context); + + if (platform.isCupertino) { + return CupertinoButton( + color: color, + onPressed: onPressed, + child: child, + ); + } else { + return ElevatedButton( + style: ElevatedButton.styleFrom( + backgroundColor: color, + ), + onPressed: onPressed, + child: child, + ); + } + } +} + +class XSwitch extends StatelessWidget { + final bool value; + final ValueChanged onChanged; + + XSwitch({ + required this.value, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final platform = XPlatform.of(context); + + if (platform.isCupertino) { + return CupertinoSwitch( + value: value, + onChanged: onChanged, + ); + } else { + return Switch( + value: value, + onChanged: onChanged, + ); + } + } +} + +class XDropdownButton extends StatefulWidget { + final bool isDense; + final Widget? underline; + final T? value; + final ValueChanged onChanged; + final List> items; + + XDropdownButton({ + super.key, + this.isDense = false, + this.underline, + required this.value, + required this.onChanged, + required this.items, + }); + + @override + _XDropdownButtonState createState() => _XDropdownButtonState(); +} + +class _XDropdownButtonState extends State> { + void _showCupertinoPicker(BuildContext context) { + showCupertinoModalPopup( + context: context, + builder: (BuildContext context) { + return Container( + height: 250, + color: CupertinoTheme.of(context).scaffoldBackgroundColor, + child: CupertinoPicker( + itemExtent: 32.0, + onSelectedItemChanged: (int index) { + widget.onChanged(widget.items[index].value); + }, + children: widget.items.map((item) => item.toCupertino()).toList(), + ), + ); + }, + ); + } + + @override + Widget build(BuildContext context) { + final platform = XPlatform.of(context); + + if (platform.isCupertino) { + Widget getSelectedWidget() { + final item = widget.items.firstWhere( + (item) => item.value == widget.value, + orElse: () => widget.items.first, + ); + + return item.toCupertino(); + } + + ; + return GestureDetector( + onTap: () => _showCupertinoPicker(context), + child: Container( + padding: widget.isDense + ? EdgeInsets.symmetric(vertical: 8.0, horizontal: 12.0) + : EdgeInsets.symmetric(vertical: 12.0, horizontal: 16.0), + decoration: BoxDecoration( + border: Border.all(color: CupertinoColors.inactiveGray), + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + getSelectedWidget(), + Icon( + CupertinoIcons.chevron_down, + color: CupertinoTheme.of(context).primaryColor, + ), + ], + ), + ), + ); + } else { + return DropdownButton( + isDense: widget.isDense, + underline: widget.underline, + value: widget.value, + onChanged: widget.onChanged, + items: widget.items.map((item) => item.toMaterial()).toList(), + ); + } + } +} + +class XDropdownMenuItem { + final T value; + final Widget child; + + XDropdownMenuItem({ + required this.value, + required this.child, + }); + + DropdownMenuItem toMaterial() { + return DropdownMenuItem( + value: value, + child: child, + ); + } + + Widget toCupertino() { + return child; + } +} + +class XAlertDialog extends StatelessWidget { + final Widget? title; + final Widget? content; + final List actions; + + const XAlertDialog({ + super.key, + this.title, + this.content, + this.actions = const [], + }); + + @override + Widget build(BuildContext context) { + final platform = XPlatform.of(context); + + if (platform.isCupertino) { + return CupertinoAlertDialog( + title: title, + content: content, + actions: actions.map((action) => action.toCupertino()).toList(), + ); + } else { + return AlertDialog( + title: title, + content: content, + actions: actions.map((action) => action.toMaterial()).toList(), + ); + } + } + + static Future show({ + required BuildContext context, + Widget? title, + Widget? content, + required List actions, + }) { + final platform = XPlatform.of(context); + + if (platform == TargetPlatform.iOS) { + return showCupertinoDialog( + context: context, + builder: (context) => XAlertDialog( + title: title, + content: content, + actions: actions, + ), + ); + } else { + return showDialog( + context: context, + builder: (context) => XAlertDialog( + title: title, + content: content, + actions: actions, + ), + ); + } + } +} + +class XDialogAction { + final Widget child; + final VoidCallback onPressed; + + XDialogAction({ + required this.child, + required this.onPressed, + }); + + CupertinoDialogAction toCupertino() { + return CupertinoDialogAction( + child: child, + onPressed: onPressed, + ); + } + + Widget toMaterial() { + return TextButton( + onPressed: onPressed, + child: child, + ); + } +} diff --git a/packages/superdeck/lib/helpers/utils.dart b/packages/superdeck/lib/helpers/utils.dart new file mode 100644 index 00000000..006cdd74 --- /dev/null +++ b/packages/superdeck/lib/helpers/utils.dart @@ -0,0 +1,89 @@ +import 'package:flutter/material.dart'; + +import '../styles/style_spec.dart'; + +BoxConstraints calculateConstraints(Size size, SlideSpec spec) { + // final outerContainer = spec.outerContainer; + // final innerContainer = spec.innerContainer; + final contentContainer = spec.contentContainer; + + double horizontalSpacing = 0.0; + double verticalSpacing = 0.0; + + for (final container in [contentContainer]) { + final padding = container.padding ?? EdgeInsets.zero; + final margin = container.margin ?? EdgeInsets.zero; + + double horizontalBorder = 0.0; + double verticalBorder = 0.0; + + if (container.decoration is BoxDecoration) { + final border = (container.decoration as BoxDecoration).border; + if (border != null) { + horizontalBorder = border.dimensions.horizontal; + verticalBorder = border.dimensions.vertical; + } + } + + horizontalSpacing += + padding.horizontal + margin.horizontal + horizontalBorder; + verticalSpacing += padding.vertical + margin.vertical + verticalBorder; + } + + return BoxConstraints( + maxHeight: size.height - verticalSpacing, + maxWidth: size.width - horizontalSpacing, + ); +} + +({List added, List removed}) compareListChanges( + List oldList, + List newList, +) { + final added = []; + final removed = []; + + final oldSet = oldList.toSet(); + final newSet = newList.toSet(); + + for (final item in newList) { + if (!oldSet.contains(item)) { + added.add(item); + } + } + + for (final item in oldList) { + if (!newSet.contains(item)) { + removed.add(item); + } + } + + return ( + added: added, + removed: removed, + ); +} + +extension BuildContextExt on BuildContext { + bool get isDarkMode => Theme.of(this).brightness == Brightness.dark; + bool get isSmall => size.width < 600; + + Size get size => MediaQuery.sizeOf(this); + bool get isMedium => size.width >= 600 && size.width < 1024; + + bool get isLarge => size.width >= 1024; + + bool get isExtraLarge => size.width >= 1440; + + bool get isMobileLandscape { + return size.shortestSide < 600 && isLandscape; + } + + bool get isLandscape => + MediaQuery.orientationOf(this) == Orientation.landscape; + + bool get isPortrait => MediaQuery.orientationOf(this) == Orientation.portrait; + + double get width => size.width; + double get height => size.height; +} diff --git a/packages/superdeck/lib/helpers/value_notifiers.dart b/packages/superdeck/lib/helpers/value_notifiers.dart new file mode 100644 index 00000000..a9a85081 --- /dev/null +++ b/packages/superdeck/lib/helpers/value_notifiers.dart @@ -0,0 +1,55 @@ +import 'dart:convert'; + +import 'package:flutter/foundation.dart'; +import 'package:localstorage/localstorage.dart'; + +// Create a ListNotifier like ValueNotifier that does comparison before setting the value +class ListNotifier extends ChangeNotifier { + ListNotifier(this._value); + + List _value; + + List get value => _value; + + set value(List newValue) { + if (listEquals(_value, newValue)) { + return; + } + _value = newValue; + notifyListeners(); + } + + int get length => _value.length; + + T operator [](int index) => _value[index]; + + void operator []=(int index, T newValue) { + _value[index] = newValue; + notifyListeners(); + } + + void add(T newValue) { + _value.add(newValue); + notifyListeners(); + } +} + +class StoredValueNotifier extends ValueNotifier { + final String key; + + StoredValueNotifier(this.key, T defaultValue) + : super(_getStoredValue(key, defaultValue)) { + addListener(() { + _setStoredValue(key, value); + }); + } + + static T _getStoredValue(String key, T defaultValue) { + final stringValue = localStorage.getItem(key); + return stringValue == null ? defaultValue : jsonDecode(stringValue) as T; + } + + static void _setStoredValue(String key, T value) { + localStorage.setItem(key, jsonEncode(value)); + } +} diff --git a/packages/superdeck/lib/helpers/watcher.dart b/packages/superdeck/lib/helpers/watcher.dart new file mode 100644 index 00000000..5a0fafed --- /dev/null +++ b/packages/superdeck/lib/helpers/watcher.dart @@ -0,0 +1,44 @@ +import 'dart:async'; +import 'dart:io'; + +class FileWatcher { + final File file; + Timer? _timer; + DateTime? _lastModified; + + FileWatcher(this.file); + + /// Starts watching the file for changes + void start(void Function() onFileChange) { + _timer = Timer.periodic(const Duration(seconds: 2), (_) async { + final hasChanged = await _checkFileChanges(file); + + if (hasChanged) { + onFileChange(); + } + }); + } + + /// Stops watching the file + void stop() { + _timer?.cancel(); + } + + /// Checks if the file is currently being watched + bool get isWatching => _timer != null; + + Future _checkFileChanges(File file) async { + final currentLastModified = await file.lastModified(); + + if (_lastModified == null) { + _lastModified = currentLastModified; + return false; + } + + final result = currentLastModified != _lastModified; + + _lastModified = currentLastModified; + + return result; + } +} diff --git a/packages/superdeck/lib/models/asset_model.dart b/packages/superdeck/lib/models/asset_model.dart new file mode 100644 index 00000000..2d990a2b --- /dev/null +++ b/packages/superdeck/lib/models/asset_model.dart @@ -0,0 +1,60 @@ +import 'package:collection/collection.dart'; +import 'package:dart_mappable/dart_mappable.dart'; +import 'package:path/path.dart' as p; + +import '../helpers/mappers.dart'; + +part 'asset_model.mapper.dart'; + +@MappableEnum() +enum AssetFileType { + png, + jpg, + jpeg, + gif, + webp; + + static AssetFileType parse(String value) { + return values.firstWhereOrNull((e) => e.name == value) ?? + (throw Exception('Invalid file type: $value')); + } + + static AssetFileType? tryParse(String value) { + return values.firstWhereOrNull((e) => value.startsWith(e.name)); + } + + bool isPng() => this == AssetFileType.png; + + bool isJpg() => this == AssetFileType.jpg || this == AssetFileType.jpeg; + + bool isGif() => this == AssetFileType.gif; +} + +@MappableClass( + includeCustomMappers: [ + FileMapper(), + SizeMapper(), + ], +) +final class SlideAsset with SlideAssetMappable { + final String path; + final int width; + final int height; + final String? reference; + + SlideAsset({ + required this.path, + required this.width, + required this.height, + required this.reference, + }); + + String get extension => p.extension(path); + + bool get isPortrait => height >= width; + + bool get isLandscape => !isPortrait; + + static const fromMap = SlideAssetMapper.fromMap; + static const fromJson = SlideAssetMapper.fromJson; +} diff --git a/lib/models/asset_model.mapper.dart b/packages/superdeck/lib/models/asset_model.mapper.dart similarity index 69% rename from lib/models/asset_model.mapper.dart rename to packages/superdeck/lib/models/asset_model.mapper.dart index b130de62..96a62dd2 100644 --- a/lib/models/asset_model.mapper.dart +++ b/packages/superdeck/lib/models/asset_model.mapper.dart @@ -64,56 +64,6 @@ extension AssetFileTypeMapperExtension on AssetFileType { } } -class SlideAssetTypeMapper extends EnumMapper { - SlideAssetTypeMapper._(); - - static SlideAssetTypeMapper? _instance; - static SlideAssetTypeMapper ensureInitialized() { - if (_instance == null) { - MapperContainer.globals.use(_instance = SlideAssetTypeMapper._()); - } - return _instance!; - } - - static SlideAssetType fromValue(dynamic value) { - ensureInitialized(); - return MapperContainer.globals.fromValue(value); - } - - @override - SlideAssetType decode(dynamic value) { - switch (value) { - case 'cached': - return SlideAssetType.cached; - case 'thumb': - return SlideAssetType.thumb; - case 'mermaid': - return SlideAssetType.mermaid; - default: - throw MapperException.unknownEnumValue(value); - } - } - - @override - dynamic encode(SlideAssetType self) { - switch (self) { - case SlideAssetType.cached: - return 'cached'; - case SlideAssetType.thumb: - return 'thumb'; - case SlideAssetType.mermaid: - return 'mermaid'; - } - } -} - -extension SlideAssetTypeMapperExtension on SlideAssetType { - String toValue() { - SlideAssetTypeMapper.ensureInitialized(); - return MapperContainer.globals.toValue(this) as String; - } -} - class SlideAssetMapper extends ClassMapperBase { SlideAssetMapper._(); @@ -129,23 +79,32 @@ class SlideAssetMapper extends ClassMapperBase { @override final String id = 'SlideAsset'; - static File _$file(SlideAsset v) => v.file; - static const Field _f$file = Field('file', _$file); - static Size _$dimensions(SlideAsset v) => v.dimensions; - static const Field _f$dimensions = - Field('dimensions', _$dimensions); + static String _$path(SlideAsset v) => v.path; + static const Field _f$path = Field('path', _$path); + static int _$width(SlideAsset v) => v.width; + static const Field _f$width = Field('width', _$width); + static int _$height(SlideAsset v) => v.height; + static const Field _f$height = Field('height', _$height); + static String? _$reference(SlideAsset v) => v.reference; + static const Field _f$reference = + Field('reference', _$reference); @override final MappableFields fields = const { - #file: _f$file, - #dimensions: _f$dimensions, + #path: _f$path, + #width: _f$width, + #height: _f$height, + #reference: _f$reference, }; @override final bool ignoreNull = true; static SlideAsset _instantiate(DecodingData data) { return SlideAsset( - file: data.dec(_f$file), dimensions: data.dec(_f$dimensions)); + path: data.dec(_f$path), + width: data.dec(_f$width), + height: data.dec(_f$height), + reference: data.dec(_f$reference)); } @override @@ -199,7 +158,7 @@ extension SlideAssetValueCopy<$R, $Out> abstract class SlideAssetCopyWith<$R, $In extends SlideAsset, $Out> implements ClassCopyWith<$R, $In, $Out> { - $R call({File? file, Size? dimensions}); + $R call({String? path, int? width, int? height, String? reference}); SlideAssetCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -212,14 +171,19 @@ class _SlideAssetCopyWithImpl<$R, $Out> late final ClassMapperBase $mapper = SlideAssetMapper.ensureInitialized(); @override - $R call({File? file, Size? dimensions}) => $apply(FieldCopyWithData({ - if (file != null) #file: file, - if (dimensions != null) #dimensions: dimensions + $R call({String? path, int? width, int? height, Object? reference = $none}) => + $apply(FieldCopyWithData({ + if (path != null) #path: path, + if (width != null) #width: width, + if (height != null) #height: height, + if (reference != $none) #reference: reference })); @override SlideAsset $make(CopyWithData data) => SlideAsset( - file: data.get(#file, or: $value.file), - dimensions: data.get(#dimensions, or: $value.dimensions)); + path: data.get(#path, or: $value.path), + width: data.get(#width, or: $value.width), + height: data.get(#height, or: $value.height), + reference: data.get(#reference, or: $value.reference)); @override SlideAssetCopyWith<$R2, SlideAsset, $Out2> $chain<$R2, $Out2>( diff --git a/lib/helpers/config_model.dart b/packages/superdeck/lib/models/config_model.dart similarity index 51% rename from lib/helpers/config_model.dart rename to packages/superdeck/lib/models/config_model.dart index 39d42e15..e184ddc4 100644 --- a/lib/helpers/config_model.dart +++ b/packages/superdeck/lib/models/config_model.dart @@ -1,48 +1,45 @@ -import 'dart:io'; - import 'package:dart_mappable/dart_mappable.dart'; import '../models/options_model.dart'; import '../schema/schema_model.dart'; import '../schema/schema_values.dart'; -import 'utils.dart'; part 'config_model.mapper.dart'; @MappableClass() -abstract class Config with ConfigMappable { +abstract class BaseConfig with BaseConfigMappable { final String? background; final String? style; final TransitionOptions? transition; - const Config({ + const BaseConfig({ required this.background, required this.style, required this.transition, }); - static final schema = Schema( + static final schema = SchemaShape( { - "background": Schema.string.isOptional(), - "style": Schema.string.isOptional(), - "transition": TransitionOptions.schema.isOptional(), + "background": Schema.string, + "style": Schema.string, + "transition": TransitionOptions.schema.optional(), }, additionalProperties: false, ); } @MappableClass() -class SDConfig extends Config with SDConfigMappable { +class Config extends BaseConfig with ConfigMappable { final bool? cacheRemoteAssets; - const SDConfig({ + const Config({ required super.background, required super.style, required super.transition, this.cacheRemoteAssets, }); - const SDConfig.empty() + const Config.empty() : this( cacheRemoteAssets: null, background: null, @@ -50,14 +47,8 @@ class SDConfig extends Config with SDConfigMappable { transition: null, ); - static const fromMap = SDConfigMapper.fromMap; - static const fromJson = SDConfigMapper.fromJson; - static SDConfig fromYaml(String yaml) => fromMap(converYamlToMap(yaml)); - - static Future load(File file) async { - final contents = await file.readAsString(); - return fromYaml(contents); - } + static const fromMap = ConfigMapper.fromMap; + static const fromJson = ConfigMapper.fromJson; Map toSlideMap() { final config = toMap(); @@ -65,11 +56,11 @@ class SDConfig extends Config with SDConfigMappable { return config; } - static final schema = Config.schema.merge( + static final schema = BaseConfig.schema.merge( { - "cache_remote_assets": Schema.boolean.isOptional(), - "markdown_file": Schema.string.isRequired().isPosixPath(), - "assets_dir": Schema.string.isRequired().isPosixPath(), + "cache_remote_assets": Schema.boolean.optional(), + "markdown_file": Schema.string.required().isPosixPath(), + "assets_dir": Schema.string.required().isPosixPath(), }, ); } diff --git a/lib/helpers/config_model.mapper.dart b/packages/superdeck/lib/models/config_model.mapper.dart similarity index 61% rename from lib/helpers/config_model.mapper.dart rename to packages/superdeck/lib/models/config_model.mapper.dart index 69731fa0..daedab98 100644 --- a/lib/helpers/config_model.mapper.dart +++ b/packages/superdeck/lib/models/config_model.mapper.dart @@ -6,33 +6,33 @@ part of 'config_model.dart'; -class ConfigMapper extends ClassMapperBase { - ConfigMapper._(); +class BaseConfigMapper extends ClassMapperBase { + BaseConfigMapper._(); - static ConfigMapper? _instance; - static ConfigMapper ensureInitialized() { + static BaseConfigMapper? _instance; + static BaseConfigMapper ensureInitialized() { if (_instance == null) { - MapperContainer.globals.use(_instance = ConfigMapper._()); - SDConfigMapper.ensureInitialized(); + MapperContainer.globals.use(_instance = BaseConfigMapper._()); + ConfigMapper.ensureInitialized(); TransitionOptionsMapper.ensureInitialized(); } return _instance!; } @override - final String id = 'Config'; + final String id = 'BaseConfig'; - static String? _$background(Config v) => v.background; - static const Field _f$background = + static String? _$background(BaseConfig v) => v.background; + static const Field _f$background = Field('background', _$background); - static String? _$style(Config v) => v.style; - static const Field _f$style = Field('style', _$style); - static TransitionOptions? _$transition(Config v) => v.transition; - static const Field _f$transition = + static String? _$style(BaseConfig v) => v.style; + static const Field _f$style = Field('style', _$style); + static TransitionOptions? _$transition(BaseConfig v) => v.transition; + static const Field _f$transition = Field('transition', _$transition); @override - final MappableFields fields = const { + final MappableFields fields = const { #background: _f$background, #style: _f$style, #transition: _f$transition, @@ -40,67 +40,67 @@ class ConfigMapper extends ClassMapperBase { @override final bool ignoreNull = true; - static Config _instantiate(DecodingData data) { - throw MapperException.missingConstructor('Config'); + static BaseConfig _instantiate(DecodingData data) { + throw MapperException.missingConstructor('BaseConfig'); } @override final Function instantiate = _instantiate; - static Config fromMap(Map map) { - return ensureInitialized().decodeMap(map); + static BaseConfig fromMap(Map map) { + return ensureInitialized().decodeMap(map); } - static Config fromJson(String json) { - return ensureInitialized().decodeJson(json); + static BaseConfig fromJson(String json) { + return ensureInitialized().decodeJson(json); } } -mixin ConfigMappable { +mixin BaseConfigMappable { String toJson(); Map toMap(); - ConfigCopyWith get copyWith; + BaseConfigCopyWith get copyWith; } -abstract class ConfigCopyWith<$R, $In extends Config, $Out> +abstract class BaseConfigCopyWith<$R, $In extends BaseConfig, $Out> implements ClassCopyWith<$R, $In, $Out> { TransitionOptionsCopyWith<$R, TransitionOptions, TransitionOptions>? get transition; $R call({String? background, String? style, TransitionOptions? transition}); - ConfigCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); + BaseConfigCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } -class SDConfigMapper extends ClassMapperBase { - SDConfigMapper._(); +class ConfigMapper extends ClassMapperBase { + ConfigMapper._(); - static SDConfigMapper? _instance; - static SDConfigMapper ensureInitialized() { + static ConfigMapper? _instance; + static ConfigMapper ensureInitialized() { if (_instance == null) { - MapperContainer.globals.use(_instance = SDConfigMapper._()); - ConfigMapper.ensureInitialized(); + MapperContainer.globals.use(_instance = ConfigMapper._()); + BaseConfigMapper.ensureInitialized(); TransitionOptionsMapper.ensureInitialized(); } return _instance!; } @override - final String id = 'SDConfig'; + final String id = 'Config'; - static String? _$background(SDConfig v) => v.background; - static const Field _f$background = + static String? _$background(Config v) => v.background; + static const Field _f$background = Field('background', _$background); - static String? _$style(SDConfig v) => v.style; - static const Field _f$style = Field('style', _$style); - static TransitionOptions? _$transition(SDConfig v) => v.transition; - static const Field _f$transition = + static String? _$style(Config v) => v.style; + static const Field _f$style = Field('style', _$style); + static TransitionOptions? _$transition(Config v) => v.transition; + static const Field _f$transition = Field('transition', _$transition); - static bool? _$cacheRemoteAssets(SDConfig v) => v.cacheRemoteAssets; - static const Field _f$cacheRemoteAssets = Field( + static bool? _$cacheRemoteAssets(Config v) => v.cacheRemoteAssets; + static const Field _f$cacheRemoteAssets = Field( 'cacheRemoteAssets', _$cacheRemoteAssets, key: 'cache_remote_assets', opt: true); @override - final MappableFields fields = const { + final MappableFields fields = const { #background: _f$background, #style: _f$style, #transition: _f$transition, @@ -109,8 +109,8 @@ class SDConfigMapper extends ClassMapperBase { @override final bool ignoreNull = true; - static SDConfig _instantiate(DecodingData data) { - return SDConfig( + static Config _instantiate(DecodingData data) { + return Config( background: data.dec(_f$background), style: data.dec(_f$style), transition: data.dec(_f$transition), @@ -120,52 +120,49 @@ class SDConfigMapper extends ClassMapperBase { @override final Function instantiate = _instantiate; - static SDConfig fromMap(Map map) { - return ensureInitialized().decodeMap(map); + static Config fromMap(Map map) { + return ensureInitialized().decodeMap(map); } - static SDConfig fromJson(String json) { - return ensureInitialized().decodeJson(json); + static Config fromJson(String json) { + return ensureInitialized().decodeJson(json); } } -mixin SDConfigMappable { +mixin ConfigMappable { String toJson() { - return SDConfigMapper.ensureInitialized() - .encodeJson(this as SDConfig); + return ConfigMapper.ensureInitialized().encodeJson(this as Config); } Map toMap() { - return SDConfigMapper.ensureInitialized() - .encodeMap(this as SDConfig); + return ConfigMapper.ensureInitialized().encodeMap(this as Config); } - SDConfigCopyWith get copyWith => - _SDConfigCopyWithImpl(this as SDConfig, $identity, $identity); + ConfigCopyWith get copyWith => + _ConfigCopyWithImpl(this as Config, $identity, $identity); @override String toString() { - return SDConfigMapper.ensureInitialized().stringifyValue(this as SDConfig); + return ConfigMapper.ensureInitialized().stringifyValue(this as Config); } @override bool operator ==(Object other) { - return SDConfigMapper.ensureInitialized() - .equalsValue(this as SDConfig, other); + return ConfigMapper.ensureInitialized().equalsValue(this as Config, other); } @override int get hashCode { - return SDConfigMapper.ensureInitialized().hashValue(this as SDConfig); + return ConfigMapper.ensureInitialized().hashValue(this as Config); } } -extension SDConfigValueCopy<$R, $Out> on ObjectCopyWith<$R, SDConfig, $Out> { - SDConfigCopyWith<$R, SDConfig, $Out> get $asSDConfig => - $base.as((v, t, t2) => _SDConfigCopyWithImpl(v, t, t2)); +extension ConfigValueCopy<$R, $Out> on ObjectCopyWith<$R, Config, $Out> { + ConfigCopyWith<$R, Config, $Out> get $asConfig => + $base.as((v, t, t2) => _ConfigCopyWithImpl(v, t, t2)); } -abstract class SDConfigCopyWith<$R, $In extends SDConfig, $Out> - implements ConfigCopyWith<$R, $In, $Out> { +abstract class ConfigCopyWith<$R, $In extends Config, $Out> + implements BaseConfigCopyWith<$R, $In, $Out> { @override TransitionOptionsCopyWith<$R, TransitionOptions, TransitionOptions>? get transition; @@ -175,17 +172,15 @@ abstract class SDConfigCopyWith<$R, $In extends SDConfig, $Out> String? style, TransitionOptions? transition, bool? cacheRemoteAssets}); - SDConfigCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); + ConfigCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } -class _SDConfigCopyWithImpl<$R, $Out> - extends ClassCopyWithBase<$R, SDConfig, $Out> - implements SDConfigCopyWith<$R, SDConfig, $Out> { - _SDConfigCopyWithImpl(super.value, super.then, super.then2); +class _ConfigCopyWithImpl<$R, $Out> extends ClassCopyWithBase<$R, Config, $Out> + implements ConfigCopyWith<$R, Config, $Out> { + _ConfigCopyWithImpl(super.value, super.then, super.then2); @override - late final ClassMapperBase $mapper = - SDConfigMapper.ensureInitialized(); + late final ClassMapperBase $mapper = ConfigMapper.ensureInitialized(); @override TransitionOptionsCopyWith<$R, TransitionOptions, TransitionOptions>? get transition => @@ -203,7 +198,7 @@ class _SDConfigCopyWithImpl<$R, $Out> if (cacheRemoteAssets != $none) #cacheRemoteAssets: cacheRemoteAssets })); @override - SDConfig $make(CopyWithData data) => SDConfig( + Config $make(CopyWithData data) => Config( background: data.get(#background, or: $value.background), style: data.get(#style, or: $value.style), transition: data.get(#transition, or: $value.transition), @@ -211,7 +206,6 @@ class _SDConfigCopyWithImpl<$R, $Out> data.get(#cacheRemoteAssets, or: $value.cacheRemoteAssets)); @override - SDConfigCopyWith<$R2, SDConfig, $Out2> $chain<$R2, $Out2>( - Then<$Out2, $R2> t) => - _SDConfigCopyWithImpl($value, $cast, t); + ConfigCopyWith<$R2, Config, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t) => + _ConfigCopyWithImpl($value, $cast, t); } diff --git a/lib/models/options_model.dart b/packages/superdeck/lib/models/options_model.dart similarity index 56% rename from lib/models/options_model.dart rename to packages/superdeck/lib/models/options_model.dart index bf5a0fb1..a1a76fc9 100644 --- a/lib/models/options_model.dart +++ b/packages/superdeck/lib/models/options_model.dart @@ -1,22 +1,21 @@ import 'package:dart_mappable/dart_mappable.dart'; import 'package:flutter/material.dart'; -import 'package:recase/recase.dart'; -import '../helpers/layout_builder.dart'; +import '../helpers/extensions.dart'; import '../helpers/mappers.dart'; import '../schema/schema_model.dart'; import '../schema/schema_values.dart'; -import 'slide_model.dart'; +import '../schema/validators.dart'; part 'options_model.mapper.dart'; @MappableClass() class ContentOptions with ContentOptionsMappable { final ContentAlignment alignment; - final int flex; + final int? flex; const ContentOptions({ - this.flex = 1, + this.flex, this.alignment = ContentAlignment.centerLeft, }); @@ -25,28 +24,33 @@ class ContentOptions with ContentOptionsMappable { return copyWith.$merge(other); } - static final schema = Schema( + static final schema = SchemaShape( { - "alignment": ContentAlignment.schema.isOptional(), - "flex": Schema.integer.isOptional(), + "alignment": ContentAlignment.schema.optional(), + "flex": Schema.integer.optional(), }, ); } @MappableClass() abstract class SplitOptions with SplitOptionsMappable { - final int flex; - final LayoutPosition position; + final int? _flex; + final LayoutPosition? _position; const SplitOptions({ - this.flex = 1, - this.position = LayoutPosition.right, - }); + int? flex, + LayoutPosition? position, + }) : _flex = flex, + _position = position; + + int get flex => _flex ?? 1; + + LayoutPosition get position => _position ?? LayoutPosition.left; - static final schema = Schema( + static final schema = SchemaShape( { - "flex": Schema.integer.isOptional(), - "position": LayoutPosition.schema.isOptional(), + "flex": Schema.integer, + "position": LayoutPosition.schema, }, ); } @@ -65,14 +69,14 @@ class ImageOptions extends SplitOptions with ImageOptionsMappable { static final schema = SplitOptions.schema.merge( { - "fit": ImageFit.schema.isOptional(), - "src": Schema.string.isRequired(), + "fit": ImageFit.schema, + "src": Schema.string.required(), }, ); } @MappableClass() -class WidgetOptions extends SplitOptions with WidgetOptionsMappable { +class WidgetOptions extends SplitOptions with WidgetOptionsMappable { final String name; final Map args; @@ -85,8 +89,8 @@ class WidgetOptions extends SplitOptions with WidgetOptionsMappable { static final schema = SplitOptions.schema.merge( { - "name": Schema.string.isRequired(), - "args": Schema.any.isOptional(), + "name": Schema.string.required(), + "args": Schema.any.optional(), }, ); } @@ -96,32 +100,33 @@ class WidgetOptions extends SplitOptions with WidgetOptionsMappable { ) class TransitionOptions with TransitionOptionsMappable { final TransitionType type; - final Duration duration; - final Duration delay; - final CurveType curve; + final Duration? duration; + final Duration? delay; + final CurveType? curve; const TransitionOptions({ required this.type, - this.duration = const Duration(milliseconds: 200), - this.delay = const Duration(milliseconds: 0), - this.curve = CurveType.ease, + this.duration, + this.delay, + this.curve, }); static const fromJson = TransitionOptionsMapper.fromJson; - Duration get totalAnimationDuration => duration + delay; + Duration get totalAnimationDuration => + (duration ?? Duration.zero) + (delay ?? Duration.zero); TransitionOptions merge(TransitionOptions? other) { if (other == null) return this; return copyWith.$merge(other); } - static final schema = Schema( + static final schema = SchemaShape( { - "type": TransitionType.schema.isRequired(), - "duration": Schema.integer.isOptional(), - "delay": Schema.integer.isOptional(), - "curve": CurveType.schema.isOptional() + "type": TransitionType.schema.required(), + "duration": Schema.integer.optional(), + "delay": Schema.integer.optional(), + "curve": CurveType.schema.optional(), }, ); } @@ -133,7 +138,7 @@ T mapDecoder(Map args) { } class ArgsSchema { - final Schema validator; + final SchemaShape validator; final Decoder decoder; const ArgsSchema({ @@ -142,99 +147,7 @@ class ArgsSchema { }); } -class Example extends ExampleWidget { - final Widget Function(T) builder; - - @override - final ArgsSchema? schema; - - const Example._({ - required super.name, - required this.builder, - this.schema, - }); - - static Example device({ - required String name, - required Widget Function(T) builder, - ArgsSchema? schema, - }) { - return Example._( - name: name, - schema: schema, - builder: (args) { - return Padding( - padding: const EdgeInsets.all(40.0), - child: builder(args), - ); - }, - ); - } - - factory Example({ - required String name, - required Widget Function(T) builder, - ArgsSchema? schema, - }) { - return Example._( - name: name, - schema: schema, - builder: builder, - ); - } - - @override - Widget build(args) { - return builder(args); - } -} - -abstract class ExampleWidget { - const ExampleWidget({ - required this.name, - }); - - final String name; - - T decode(Map args) { - if (schema?.decoder == null) { - return args as T; - } else { - return schema!.decoder(args); - } - } - - ArgsSchema? get schema => null; - - SchemaValidationResult _validate(Map args) { - if (schema?.validator == null) { - return SchemaValidationResult.valid(['widget', name]); - } else { - return schema!.validator.validate(['widget', name], args); - } - } - - Widget call(Map args) { - final result = _validate(args); - if (!result.isValid) { - return Row( - children: [ - Expanded( - child: Container( - color: Colors.red, - child: InvalidSlideBuilder( - config: InvalidSlide.schemaError(result), - ), - ), - ), - ], - ); - } - return build(decode(args)); - } - - Widget build(T args); -} +typedef ExampleBuilder = Widget Function(BuildContext context); @MappableEnum() enum ImageFit { @@ -246,9 +159,7 @@ enum ImageFit { none, scaleDown; - static final schema = EnumSchema( - values: ImageFit.values.map((e) => e.name.snakeCase).toList(), - ); + static final schema = Schema.string.isEnum(values); BoxFit toBoxFit() { return switch (this) { @@ -339,9 +250,7 @@ enum TransitionType { spinPerfect, swing; - static final schema = EnumSchema( - values: TransitionType.values.map((e) => e.name.snakeCase).toList(), - ); + static final schema = Schema.string.isEnum(values); } @MappableEnum() @@ -362,9 +271,7 @@ enum CurveType { slowMiddle, linearToEaseOut; - static final schema = EnumSchema( - values: values.map((e) => e.name.snakeCase).toList(), - ); + static final schema = Schema.string.isEnum(values); } @MappableEnum() @@ -374,9 +281,7 @@ enum LayoutPosition { top, bottom; - static final schema = EnumSchema( - values: LayoutPosition.values.map((e) => e.name.snakeCase).toList(), - ); + static final schema = Schema.string.isEnum(values); bool isHorizontal() { return switch (this) { @@ -387,9 +292,7 @@ enum LayoutPosition { }; } - bool isVertical() { - return !isHorizontal(); - } + bool isVertical() => !isHorizontal(); } @MappableEnum() @@ -404,9 +307,7 @@ enum ContentAlignment { bottomCenter, bottomRight; - static final schema = EnumSchema( - values: ContentAlignment.values.map((e) => e.name.snakeCase).toList(), - ); + static final schema = Schema.string.isEnum(values); Alignment toAlignment() { return switch (this) { @@ -422,3 +323,12 @@ enum ContentAlignment { }; } } + +extension on SchemaValue { + SchemaValue isEnum(List values) { + return copyWith(validators: [ + ...validators, + ArrayValidator(values.map((e) => e.name.snakeCase()).toList()), + ]); + } +} diff --git a/lib/models/options_model.mapper.dart b/packages/superdeck/lib/models/options_model.mapper.dart similarity index 88% rename from lib/models/options_model.mapper.dart rename to packages/superdeck/lib/models/options_model.mapper.dart index 06570ed2..391f57ef 100644 --- a/lib/models/options_model.mapper.dart +++ b/packages/superdeck/lib/models/options_model.mapper.dart @@ -535,9 +535,9 @@ class ContentOptionsMapper extends ClassMapperBase { @override final String id = 'ContentOptions'; - static int _$flex(ContentOptions v) => v.flex; + static int? _$flex(ContentOptions v) => v.flex; static const Field _f$flex = - Field('flex', _$flex, opt: true, def: 1); + Field('flex', _$flex, opt: true); static ContentAlignment _$alignment(ContentOptions v) => v.alignment; static const Field _f$alignment = Field( 'alignment', _$alignment, @@ -623,9 +623,9 @@ class _ContentOptionsCopyWithImpl<$R, $Out> late final ClassMapperBase $mapper = ContentOptionsMapper.ensureInitialized(); @override - $R call({int? flex, ContentAlignment? alignment}) => + $R call({Object? flex = $none, ContentAlignment? alignment}) => $apply(FieldCopyWithData({ - if (flex != null) #flex: flex, + if (flex != $none) #flex: flex, if (alignment != null) #alignment: alignment })); @override @@ -656,17 +656,17 @@ class SplitOptionsMapper extends ClassMapperBase { @override final String id = 'SplitOptions'; - static int _$flex(SplitOptions v) => v.flex; - static const Field _f$flex = - Field('flex', _$flex, opt: true, def: 1); - static LayoutPosition _$position(SplitOptions v) => v.position; - static const Field _f$position = - Field('position', _$position, opt: true, def: LayoutPosition.right); + static int? _$_flex(SplitOptions v) => v._flex; + static const Field _f$_flex = + Field('_flex', _$_flex, key: 'flex', opt: true); + static LayoutPosition? _$_position(SplitOptions v) => v._position; + static const Field _f$_position = + Field('_position', _$_position, key: 'position', opt: true); @override final MappableFields fields = const { - #flex: _f$flex, - #position: _f$position, + #_flex: _f$_flex, + #_position: _f$_position, }; @override final bool ignoreNull = true; @@ -721,19 +721,19 @@ class ImageOptionsMapper extends ClassMapperBase { static ImageFit? _$fit(ImageOptions v) => v.fit; static const Field _f$fit = Field('fit', _$fit, opt: true); - static int _$flex(ImageOptions v) => v.flex; - static const Field _f$flex = - Field('flex', _$flex, opt: true, def: 1); - static LayoutPosition _$position(ImageOptions v) => v.position; - static const Field _f$position = - Field('position', _$position, opt: true, def: LayoutPosition.right); + static int? _$_flex(ImageOptions v) => v._flex; + static const Field _f$_flex = + Field('_flex', _$_flex, key: 'flex', opt: true); + static LayoutPosition? _$_position(ImageOptions v) => v._position; + static const Field _f$_position = + Field('_position', _$_position, key: 'position', opt: true); @override final MappableFields fields = const { #src: _f$src, #fit: _f$fit, - #flex: _f$flex, - #position: _f$position, + #_flex: _f$_flex, + #_position: _f$_position, }; @override final bool ignoreNull = true; @@ -742,8 +742,8 @@ class ImageOptionsMapper extends ClassMapperBase { return ImageOptions( src: data.dec(_f$src), fit: data.dec(_f$fit), - flex: data.dec(_f$flex), - position: data.dec(_f$position)); + flex: data.dec(_f$_flex), + position: data.dec(_f$_position)); } @override @@ -815,20 +815,20 @@ class _ImageOptionsCopyWithImpl<$R, $Out> $R call( {String? src, Object? fit = $none, - int? flex, - LayoutPosition? position}) => + Object? flex = $none, + Object? position = $none}) => $apply(FieldCopyWithData({ if (src != null) #src: src, if (fit != $none) #fit: fit, - if (flex != null) #flex: flex, - if (position != null) #position: position + if (flex != $none) #flex: flex, + if (position != $none) #position: position })); @override ImageOptions $make(CopyWithData data) => ImageOptions( src: data.get(#src, or: $value.src), fit: data.get(#fit, or: $value.fit), - flex: data.get(#flex, or: $value.flex), - position: data.get(#position, or: $value.position)); + flex: data.get(#flex, or: $value._flex), + position: data.get(#position, or: $value._position)); @override ImageOptionsCopyWith<$R2, ImageOptions, $Out2> $chain<$R2, $Out2>( @@ -851,91 +851,89 @@ class WidgetOptionsMapper extends ClassMapperBase { @override final String id = 'WidgetOptions'; - @override - Function get typeFactory => (f) => f>(); static String _$name(WidgetOptions v) => v.name; static const Field _f$name = Field('name', _$name); static Map _$args(WidgetOptions v) => v.args; static const Field> _f$args = Field('args', _$args, opt: true, def: const {}); - static int _$flex(WidgetOptions v) => v.flex; - static const Field _f$flex = - Field('flex', _$flex, opt: true, def: 1); - static LayoutPosition _$position(WidgetOptions v) => v.position; - static const Field _f$position = - Field('position', _$position, opt: true, def: LayoutPosition.right); + static int? _$_flex(WidgetOptions v) => v._flex; + static const Field _f$_flex = + Field('_flex', _$_flex, key: 'flex', opt: true); + static LayoutPosition? _$_position(WidgetOptions v) => v._position; + static const Field _f$_position = + Field('_position', _$_position, key: 'position', opt: true); @override final MappableFields fields = const { #name: _f$name, #args: _f$args, - #flex: _f$flex, - #position: _f$position, + #_flex: _f$_flex, + #_position: _f$_position, }; @override final bool ignoreNull = true; - static WidgetOptions _instantiate(DecodingData data) { + static WidgetOptions _instantiate(DecodingData data) { return WidgetOptions( name: data.dec(_f$name), args: data.dec(_f$args), - flex: data.dec(_f$flex), - position: data.dec(_f$position)); + flex: data.dec(_f$_flex), + position: data.dec(_f$_position)); } @override final Function instantiate = _instantiate; - static WidgetOptions fromMap(Map map) { - return ensureInitialized().decodeMap>(map); + static WidgetOptions fromMap(Map map) { + return ensureInitialized().decodeMap(map); } - static WidgetOptions fromJson(String json) { - return ensureInitialized().decodeJson>(json); + static WidgetOptions fromJson(String json) { + return ensureInitialized().decodeJson(json); } } -mixin WidgetOptionsMappable { +mixin WidgetOptionsMappable { String toJson() { return WidgetOptionsMapper.ensureInitialized() - .encodeJson>(this as WidgetOptions); + .encodeJson(this as WidgetOptions); } Map toMap() { return WidgetOptionsMapper.ensureInitialized() - .encodeMap>(this as WidgetOptions); + .encodeMap(this as WidgetOptions); } - WidgetOptionsCopyWith, WidgetOptions, WidgetOptions, T> + WidgetOptionsCopyWith get copyWith => _WidgetOptionsCopyWithImpl( - this as WidgetOptions, $identity, $identity); + this as WidgetOptions, $identity, $identity); @override String toString() { return WidgetOptionsMapper.ensureInitialized() - .stringifyValue(this as WidgetOptions); + .stringifyValue(this as WidgetOptions); } @override bool operator ==(Object other) { return WidgetOptionsMapper.ensureInitialized() - .equalsValue(this as WidgetOptions, other); + .equalsValue(this as WidgetOptions, other); } @override int get hashCode { return WidgetOptionsMapper.ensureInitialized() - .hashValue(this as WidgetOptions); + .hashValue(this as WidgetOptions); } } -extension WidgetOptionsValueCopy<$R, $Out, T> - on ObjectCopyWith<$R, WidgetOptions, $Out> { - WidgetOptionsCopyWith<$R, WidgetOptions, $Out, T> get $asWidgetOptions => +extension WidgetOptionsValueCopy<$R, $Out> + on ObjectCopyWith<$R, WidgetOptions, $Out> { + WidgetOptionsCopyWith<$R, WidgetOptions, $Out> get $asWidgetOptions => $base.as((v, t, t2) => _WidgetOptionsCopyWithImpl(v, t, t2)); } -abstract class WidgetOptionsCopyWith<$R, $In extends WidgetOptions, $Out, T> +abstract class WidgetOptionsCopyWith<$R, $In extends WidgetOptions, $Out> implements SplitOptionsCopyWith<$R, $In, $Out> { MapCopyWith<$R, String, dynamic, ObjectCopyWith<$R, dynamic, dynamic>> get args; @@ -945,13 +943,12 @@ abstract class WidgetOptionsCopyWith<$R, $In extends WidgetOptions, $Out, T> Map? args, int? flex, LayoutPosition? position}); - WidgetOptionsCopyWith<$R2, $In, $Out2, T> $chain<$R2, $Out2>( - Then<$Out2, $R2> t); + WidgetOptionsCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } -class _WidgetOptionsCopyWithImpl<$R, $Out, T> - extends ClassCopyWithBase<$R, WidgetOptions, $Out> - implements WidgetOptionsCopyWith<$R, WidgetOptions, $Out, T> { +class _WidgetOptionsCopyWithImpl<$R, $Out> + extends ClassCopyWithBase<$R, WidgetOptions, $Out> + implements WidgetOptionsCopyWith<$R, WidgetOptions, $Out> { _WidgetOptionsCopyWithImpl(super.value, super.then, super.then2); @override @@ -965,23 +962,23 @@ class _WidgetOptionsCopyWithImpl<$R, $Out, T> $R call( {String? name, Map? args, - int? flex, - LayoutPosition? position}) => + Object? flex = $none, + Object? position = $none}) => $apply(FieldCopyWithData({ if (name != null) #name: name, if (args != null) #args: args, - if (flex != null) #flex: flex, - if (position != null) #position: position + if (flex != $none) #flex: flex, + if (position != $none) #position: position })); @override - WidgetOptions $make(CopyWithData data) => WidgetOptions( + WidgetOptions $make(CopyWithData data) => WidgetOptions( name: data.get(#name, or: $value.name), args: data.get(#args, or: $value.args), - flex: data.get(#flex, or: $value.flex), - position: data.get(#position, or: $value.position)); + flex: data.get(#flex, or: $value._flex), + position: data.get(#position, or: $value._position)); @override - WidgetOptionsCopyWith<$R2, WidgetOptions, $Out2, T> $chain<$R2, $Out2>( + WidgetOptionsCopyWith<$R2, WidgetOptions, $Out2> $chain<$R2, $Out2>( Then<$Out2, $R2> t) => _WidgetOptionsCopyWithImpl($value, $cast, t); } @@ -1006,16 +1003,15 @@ class TransitionOptionsMapper extends ClassMapperBase { static TransitionType _$type(TransitionOptions v) => v.type; static const Field _f$type = Field('type', _$type); - static Duration _$duration(TransitionOptions v) => v.duration; - static const Field _f$duration = Field( - 'duration', _$duration, - opt: true, def: const Duration(milliseconds: 200)); - static Duration _$delay(TransitionOptions v) => v.delay; + static Duration? _$duration(TransitionOptions v) => v.duration; + static const Field _f$duration = + Field('duration', _$duration, opt: true); + static Duration? _$delay(TransitionOptions v) => v.delay; static const Field _f$delay = - Field('delay', _$delay, opt: true, def: const Duration(milliseconds: 0)); - static CurveType _$curve(TransitionOptions v) => v.curve; + Field('delay', _$delay, opt: true); + static CurveType? _$curve(TransitionOptions v) => v.curve; static const Field _f$curve = - Field('curve', _$curve, opt: true, def: CurveType.ease); + Field('curve', _$curve, opt: true); @override final MappableFields fields = const { @@ -1110,14 +1106,14 @@ class _TransitionOptionsCopyWithImpl<$R, $Out> @override $R call( {TransitionType? type, - Duration? duration, - Duration? delay, - CurveType? curve}) => + Object? duration = $none, + Object? delay = $none, + Object? curve = $none}) => $apply(FieldCopyWithData({ if (type != null) #type: type, - if (duration != null) #duration: duration, - if (delay != null) #delay: delay, - if (curve != null) #curve: curve + if (duration != $none) #duration: duration, + if (delay != $none) #delay: delay, + if (curve != $none) #curve: curve })); @override TransitionOptions $make(CopyWithData data) => TransitionOptions( diff --git a/packages/superdeck/lib/models/reference_model.dart b/packages/superdeck/lib/models/reference_model.dart new file mode 100644 index 00000000..47ae8be7 --- /dev/null +++ b/packages/superdeck/lib/models/reference_model.dart @@ -0,0 +1,28 @@ +import 'package:dart_mappable/dart_mappable.dart'; + +import 'asset_model.dart'; +import 'config_model.dart'; +import 'slide_model.dart'; + +part 'reference_model.mapper.dart'; + +@MappableClass() +class SuperDeckReference with SuperDeckReferenceMappable { + final Config config; + final List slides; + final List assets; + + SuperDeckReference({ + required this.config, + required this.slides, + required this.assets, + }); + + const SuperDeckReference.empty() + : config = const Config.empty(), + slides = const [], + assets = const []; + + static const fromMap = SuperDeckReferenceMapper.fromMap; + static const fromJson = SuperDeckReferenceMapper.fromJson; +} diff --git a/packages/superdeck/lib/models/reference_model.mapper.dart b/packages/superdeck/lib/models/reference_model.mapper.dart new file mode 100644 index 00000000..4983e382 --- /dev/null +++ b/packages/superdeck/lib/models/reference_model.mapper.dart @@ -0,0 +1,152 @@ +// coverage:ignore-file +// GENERATED CODE - DO NOT MODIFY BY HAND +// ignore_for_file: type=lint +// ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member +// ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter + +part of 'reference_model.dart'; + +class SuperDeckReferenceMapper extends ClassMapperBase { + SuperDeckReferenceMapper._(); + + static SuperDeckReferenceMapper? _instance; + static SuperDeckReferenceMapper ensureInitialized() { + if (_instance == null) { + MapperContainer.globals.use(_instance = SuperDeckReferenceMapper._()); + ConfigMapper.ensureInitialized(); + SlideMapper.ensureInitialized(); + SlideAssetMapper.ensureInitialized(); + } + return _instance!; + } + + @override + final String id = 'SuperDeckReference'; + + static Config _$config(SuperDeckReference v) => v.config; + static const Field _f$config = + Field('config', _$config); + static List _$slides(SuperDeckReference v) => v.slides; + static const Field> _f$slides = + Field('slides', _$slides); + static List _$assets(SuperDeckReference v) => v.assets; + static const Field> _f$assets = + Field('assets', _$assets); + + @override + final MappableFields fields = const { + #config: _f$config, + #slides: _f$slides, + #assets: _f$assets, + }; + @override + final bool ignoreNull = true; + + static SuperDeckReference _instantiate(DecodingData data) { + return SuperDeckReference( + config: data.dec(_f$config), + slides: data.dec(_f$slides), + assets: data.dec(_f$assets)); + } + + @override + final Function instantiate = _instantiate; + + static SuperDeckReference fromMap(Map map) { + return ensureInitialized().decodeMap(map); + } + + static SuperDeckReference fromJson(String json) { + return ensureInitialized().decodeJson(json); + } +} + +mixin SuperDeckReferenceMappable { + String toJson() { + return SuperDeckReferenceMapper.ensureInitialized() + .encodeJson(this as SuperDeckReference); + } + + Map toMap() { + return SuperDeckReferenceMapper.ensureInitialized() + .encodeMap(this as SuperDeckReference); + } + + SuperDeckReferenceCopyWith + get copyWith => _SuperDeckReferenceCopyWithImpl( + this as SuperDeckReference, $identity, $identity); + @override + String toString() { + return SuperDeckReferenceMapper.ensureInitialized() + .stringifyValue(this as SuperDeckReference); + } + + @override + bool operator ==(Object other) { + return SuperDeckReferenceMapper.ensureInitialized() + .equalsValue(this as SuperDeckReference, other); + } + + @override + int get hashCode { + return SuperDeckReferenceMapper.ensureInitialized() + .hashValue(this as SuperDeckReference); + } +} + +extension SuperDeckReferenceValueCopy<$R, $Out> + on ObjectCopyWith<$R, SuperDeckReference, $Out> { + SuperDeckReferenceCopyWith<$R, SuperDeckReference, $Out> + get $asSuperDeckReference => + $base.as((v, t, t2) => _SuperDeckReferenceCopyWithImpl(v, t, t2)); +} + +abstract class SuperDeckReferenceCopyWith<$R, $In extends SuperDeckReference, + $Out> implements ClassCopyWith<$R, $In, $Out> { + ConfigCopyWith<$R, Config, Config> get config; + ListCopyWith<$R, Slide, SlideCopyWith<$R, Slide, Slide>> get slides; + ListCopyWith<$R, SlideAsset, SlideAssetCopyWith<$R, SlideAsset, SlideAsset>> + get assets; + $R call({Config? config, List? slides, List? assets}); + SuperDeckReferenceCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>( + Then<$Out2, $R2> t); +} + +class _SuperDeckReferenceCopyWithImpl<$R, $Out> + extends ClassCopyWithBase<$R, SuperDeckReference, $Out> + implements SuperDeckReferenceCopyWith<$R, SuperDeckReference, $Out> { + _SuperDeckReferenceCopyWithImpl(super.value, super.then, super.then2); + + @override + late final ClassMapperBase $mapper = + SuperDeckReferenceMapper.ensureInitialized(); + @override + ConfigCopyWith<$R, Config, Config> get config => + $value.config.copyWith.$chain((v) => call(config: v)); + @override + ListCopyWith<$R, Slide, SlideCopyWith<$R, Slide, Slide>> get slides => + ListCopyWith($value.slides, (v, t) => v.copyWith.$chain(t), + (v) => call(slides: v)); + @override + ListCopyWith<$R, SlideAsset, SlideAssetCopyWith<$R, SlideAsset, SlideAsset>> + get assets => ListCopyWith($value.assets, (v, t) => v.copyWith.$chain(t), + (v) => call(assets: v)); + @override + $R call({Config? config, List? slides, List? assets}) => + $apply(FieldCopyWithData({ + if (config != null) #config: config, + if (slides != null) #slides: slides, + if (assets != null) #assets: assets + })); + @override + SuperDeckReference $make(CopyWithData data) => SuperDeckReference( + config: data.get(#config, or: $value.config), + slides: data.get(#slides, or: $value.slides), + assets: data.get(#assets, or: $value.assets)); + + @override + SuperDeckReferenceCopyWith<$R2, SuperDeckReference, $Out2> $chain<$R2, $Out2>( + Then<$Out2, $R2> t) => + _SuperDeckReferenceCopyWithImpl($value, $cast, t); +} diff --git a/lib/models/slide_model.dart b/packages/superdeck/lib/models/slide_model.dart similarity index 81% rename from lib/models/slide_model.dart rename to packages/superdeck/lib/models/slide_model.dart index d6c91549..178068a9 100644 --- a/lib/models/slide_model.dart +++ b/packages/superdeck/lib/models/slide_model.dart @@ -1,35 +1,33 @@ import 'package:dart_mappable/dart_mappable.dart'; -import '../helpers/config_model.dart'; import '../helpers/section_tag.dart'; -import '../helpers/utils.dart'; import '../schema/schema_model.dart'; -import '../styles/style_util.dart'; +import '../schema/schema_validation.dart'; import '../superdeck.dart'; +import 'config_model.dart'; part 'slide_model.mapper.dart'; @MappableClass(discriminatorKey: 'layout') -abstract class Slide extends Config with SlideMappable { +abstract class Slide extends BaseConfig with SlideMappable { final String? title; final String layout; - final String data; - final String raw; - final String hashKey; + final String content; + + final String key; - @MappableField(key: 'content') final ContentOptions? contentOptions; Slide({ required this.layout, - required this.data, - required this.raw, + required this.content, + required this.key, required this.title, required this.contentOptions, required super.background, required super.style, required super.transition, - }) : hashKey = shortHashId(raw); + }); static Slide parse(Map map) { final layout = map['layout'] ??= LayoutType.simple; @@ -64,21 +62,17 @@ abstract class Slide extends Config with SlideMappable { } } - SlideVariant? get styleVariant { - return style == null ? null : SlideVariant(style!); - } - static const fromMap = SlideMapper.fromMap; static const fromJson = SlideMapper.fromJson; - static final schema = Config.schema.merge( + static final schema = BaseConfig.schema.merge( { - "layout": Schema.string.isRequired(), - "data": Schema.string.isRequired(), - "content": ContentOptions.schema.isOptional(), - "title": Schema.string.isOptional(), - "raw": Schema.string.isOptional(), + "layout": Schema.string.required(), + "data": Schema.string.required(), + "content": ContentOptions.schema, + "title": Schema.string, + "raw": Schema.string, }, ); } @@ -88,11 +82,11 @@ class SimpleSlide extends Slide with SimpleSlideMappable { SimpleSlide({ super.title, super.background, - required super.contentOptions, + super.contentOptions, super.style, super.transition, - required super.raw, - required super.data, + required super.key, + required super.content, }) : super(layout: LayoutType.simple); static const fromMap = SimpleSlideMapper.fromMap; @@ -114,9 +108,9 @@ abstract class SplitSlide extends Slide required super.contentOptions, super.style, super.transition, - required super.data, + required super.content, required super.layout, - required super.raw, + required super.key, }); } @@ -128,9 +122,9 @@ class ImageSlide extends SplitSlide with ImageSlideMappable { super.background, required super.contentOptions, super.transition, - required super.data, + required super.content, required super.options, - required super.raw, + required super.key, }) : super(layout: LayoutType.image); static const fromMap = ImageSlideMapper.fromMap; @@ -139,7 +133,7 @@ class ImageSlide extends SplitSlide with ImageSlideMappable { static final schema = Slide.schema.merge( { - 'options': ImageOptions.schema.isRequired(), + 'options': ImageOptions.schema.required(), }, ); } @@ -153,8 +147,8 @@ class WidgetSlide extends SplitSlide with WidgetSlideMappable { super.background, required super.contentOptions, super.transition, - required super.data, - required super.raw, + required super.content, + required super.key, }) : super(layout: LayoutType.widget); static const fromMap = WidgetSlideMapper.fromMap; @@ -163,13 +157,13 @@ class WidgetSlide extends SplitSlide with WidgetSlideMappable { static final schema = Slide.schema.merge( { - 'options': WidgetOptions.schema.isRequired(), + 'options': WidgetOptions.schema.required(), }, ); } @MappableRecord() -typedef SectionData = ({String content, ContentOptions options}); +typedef SectionData = ({String content, ContentOptions? options}); @MappableClass() abstract class SectionsSlide extends Slide with SectionsSlideMappable { @@ -182,10 +176,10 @@ abstract class SectionsSlide extends Slide with SectionsSlideMappable { required super.contentOptions, super.style, super.transition, - required super.data, + required super.content, this.sections = const {}, required super.layout, - required super.raw, + required super.key, }); Map? _sectionCache; @@ -193,7 +187,7 @@ abstract class SectionsSlide extends Slide with SectionsSlideMappable { Map get _contentSections { if (_sectionCache != null) return _sectionCache!; - return _sectionCache = parseContentSections(data); + return _sectionCache = parseContentSections(content); } SectionData getSection(String section, [String? sectionFallback]) { @@ -218,9 +212,9 @@ class TwoColumnSlide extends SectionsSlide with TwoColumnSlideMappable { required super.contentOptions, super.style, super.transition, - required super.data, + required super.content, super.sections, - required super.raw, + required super.key, }) : super(layout: LayoutType.twoColumn); SectionData get left => getSection(SectionTag.left, SectionTag.first); @@ -232,9 +226,9 @@ class TwoColumnSlide extends SectionsSlide with TwoColumnSlideMappable { static const fromJson = TwoColumnSlideMapper.fromJson; static final schema = Slide.schema.merge({ - 'sections': SchemaMap.optional({ - 'left': ContentOptions.schema.isOptional(), - 'right': ContentOptions.schema.isOptional(), + 'sections': SchemaMap({ + 'left': ContentOptions.schema.optional(), + 'right': ContentOptions.schema.optional(), }), }); } @@ -248,9 +242,9 @@ class TwoColumnHeaderSlide extends SectionsSlide required super.contentOptions, super.style, super.transition, - required super.data, + required super.content, super.sections, - required super.raw, + required super.key, }) : super(layout: LayoutType.twoColumnHeader); SectionData get header => getSection(SectionTag.header, SectionTag.first); @@ -265,8 +259,8 @@ class TwoColumnHeaderSlide extends SectionsSlide static final schema = TwoColumnSlide.schema.merge( { - 'sections': SchemaMap.optional({ - 'header': ContentOptions.schema.isOptional(), + 'sections': SchemaMap({ + 'header': ContentOptions.schema.optional(), }), }, ); @@ -280,19 +274,19 @@ class InvalidSlide extends Slide with InvalidSlideMappable { super.background, super.style, super.transition, - required super.data, - required super.raw, + required super.content, + required super.key, }) : super(layout: LayoutType.invalid); InvalidSlide.message(String message) : super( layout: LayoutType.invalid, title: null, - data: message, + content: message, background: null, contentOptions: null, style: null, - raw: message, + key: 'invalid_key', transition: null, ); diff --git a/lib/models/slide_model.mapper.dart b/packages/superdeck/lib/models/slide_model.mapper.dart similarity index 86% rename from lib/models/slide_model.mapper.dart rename to packages/superdeck/lib/models/slide_model.mapper.dart index 1ab27097..ee3c1c6e 100644 --- a/lib/models/slide_model.mapper.dart +++ b/packages/superdeck/lib/models/slide_model.mapper.dart @@ -13,7 +13,7 @@ class SlideMapper extends SubClassMapperBase { static SlideMapper ensureInitialized() { if (_instance == null) { MapperContainer.globals.use(_instance = SlideMapper._()); - ConfigMapper.ensureInitialized().addSubMapper(_instance!); + BaseConfigMapper.ensureInitialized().addSubMapper(_instance!); SimpleSlideMapper.ensureInitialized(); SplitSlideMapper.ensureInitialized(); SectionsSlideMapper.ensureInitialized(); @@ -29,15 +29,15 @@ class SlideMapper extends SubClassMapperBase { static String _$layout(Slide v) => v.layout; static const Field _f$layout = Field('layout', _$layout); - static String _$data(Slide v) => v.data; - static const Field _f$data = Field('data', _$data); - static String _$raw(Slide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$content(Slide v) => v.content; + static const Field _f$content = Field('content', _$content); + static String _$key(Slide v) => v.key; + static const Field _f$key = Field('key', _$key); static String? _$title(Slide v) => v.title; static const Field _f$title = Field('title', _$title); static ContentOptions? _$contentOptions(Slide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$background(Slide v) => v.background; static const Field _f$background = Field('background', _$background); @@ -46,21 +46,17 @@ class SlideMapper extends SubClassMapperBase { static TransitionOptions? _$transition(Slide v) => v.transition; static const Field _f$transition = Field('transition', _$transition); - static String _$hashKey(Slide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { #layout: _f$layout, - #data: _f$data, - #raw: _f$raw, + #content: _f$content, + #key: _f$key, #title: _f$title, #contentOptions: _f$contentOptions, #background: _f$background, #style: _f$style, #transition: _f$transition, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -70,7 +66,7 @@ class SlideMapper extends SubClassMapperBase { @override final dynamic discriminatorValue = 'Slide'; @override - late final ClassMapperBase superMapper = ConfigMapper.ensureInitialized(); + late final ClassMapperBase superMapper = BaseConfigMapper.ensureInitialized(); static Slide _instantiate(DecodingData data) { throw MapperException.missingSubclass( @@ -96,7 +92,7 @@ mixin SlideMappable { } abstract class SlideCopyWith<$R, $In extends Slide, $Out> - implements ConfigCopyWith<$R, $In, $Out> { + implements BaseConfigCopyWith<$R, $In, $Out> { ContentOptionsCopyWith<$R, ContentOptions, ContentOptions>? get contentOptions; @override @@ -104,8 +100,8 @@ abstract class SlideCopyWith<$R, $In extends Slide, $Out> get transition; @override $R call( - {String? data, - String? raw, + {String? content, + String? key, String? title, ContentOptions? contentOptions, String? background, @@ -138,24 +134,23 @@ class SimpleSlideMapper extends SubClassMapperBase { static const Field _f$background = Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(SimpleSlide v) => v.contentOptions; - static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + static const Field _f$contentOptions = Field( + 'contentOptions', _$contentOptions, + key: 'content_options', opt: true); static String? _$style(SimpleSlide v) => v.style; static const Field _f$style = Field('style', _$style, opt: true); static TransitionOptions? _$transition(SimpleSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$raw(SimpleSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); - static String _$data(SimpleSlide v) => v.data; - static const Field _f$data = Field('data', _$data); + static String _$key(SimpleSlide v) => v.key; + static const Field _f$key = Field('key', _$key); + static String _$content(SimpleSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static String _$layout(SimpleSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(SimpleSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -164,10 +159,9 @@ class SimpleSlideMapper extends SubClassMapperBase { #contentOptions: _f$contentOptions, #style: _f$style, #transition: _f$transition, - #raw: _f$raw, - #data: _f$data, + #key: _f$key, + #content: _f$content, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -186,8 +180,8 @@ class SimpleSlideMapper extends SubClassMapperBase { contentOptions: data.dec(_f$contentOptions), style: data.dec(_f$style), transition: data.dec(_f$transition), - raw: data.dec(_f$raw), - data: data.dec(_f$data)); + key: data.dec(_f$key), + content: data.dec(_f$content)); } @override @@ -254,8 +248,8 @@ abstract class SimpleSlideCopyWith<$R, $In extends SimpleSlide, $Out> ContentOptions? contentOptions, String? style, TransitionOptions? transition, - String? raw, - String? data}); + String? key, + String? content}); SimpleSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -282,16 +276,16 @@ class _SimpleSlideCopyWithImpl<$R, $Out> Object? contentOptions = $none, Object? style = $none, Object? transition = $none, - String? raw, - String? data}) => + String? key, + String? content}) => $apply(FieldCopyWithData({ if (title != $none) #title: title, if (background != $none) #background: background, if (contentOptions != $none) #contentOptions: contentOptions, if (style != $none) #style: style, if (transition != $none) #transition: transition, - if (raw != null) #raw: raw, - if (data != null) #data: data + if (key != null) #key: key, + if (content != null) #content: content })); @override SimpleSlide $make(CopyWithData data) => SimpleSlide( @@ -300,8 +294,8 @@ class _SimpleSlideCopyWithImpl<$R, $Out> contentOptions: data.get(#contentOptions, or: $value.contentOptions), style: data.get(#style, or: $value.style), transition: data.get(#transition, or: $value.transition), - raw: data.get(#raw, or: $value.raw), - data: data.get(#data, or: $value.data)); + key: data.get(#key, or: $value.key), + content: data.get(#content, or: $value.content)); @override SimpleSlideCopyWith<$R2, SimpleSlide, $Out2> $chain<$R2, $Out2>( @@ -343,22 +337,20 @@ class SplitSlideMapper extends SubClassMapperBase { Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(SplitSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$style(SplitSlide v) => v.style; static const Field _f$style = Field('style', _$style, opt: true); static TransitionOptions? _$transition(SplitSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(SplitSlide v) => v.data; - static const Field _f$data = Field('data', _$data); + static String _$content(SplitSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static String _$layout(SplitSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout); - static String _$raw(SplitSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); - static String _$hashKey(SplitSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); + static String _$key(SplitSlide v) => v.key; + static const Field _f$key = Field('key', _$key); @override final MappableFields fields = const { @@ -368,10 +360,9 @@ class SplitSlideMapper extends SubClassMapperBase { #contentOptions: _f$contentOptions, #style: _f$style, #transition: _f$transition, - #data: _f$data, + #content: _f$content, #layout: _f$layout, - #raw: _f$raw, - #hashKey: _f$hashKey, + #key: _f$key, }; @override final bool ignoreNull = true; @@ -430,8 +421,8 @@ abstract class SplitSlideCopyWith<$R, $In extends SplitSlide, $Out, ContentOptions? contentOptions, String? style, TransitionOptions? transition, - String? data, - String? raw}); + String? content, + String? key}); SplitSlideCopyWith<$R2, $In, $Out2, T> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -464,23 +455,21 @@ class ImageSlideMapper extends SubClassMapperBase { Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(ImageSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static TransitionOptions? _$transition(ImageSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(ImageSlide v) => v.data; - static const Field _f$data = Field('data', _$data); + static String _$content(ImageSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static ImageOptions _$options(ImageSlide v) => v.options; static const Field _f$options = Field('options', _$options); - static String _$raw(ImageSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$key(ImageSlide v) => v.key; + static const Field _f$key = Field('key', _$key); static String _$layout(ImageSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(ImageSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -489,11 +478,10 @@ class ImageSlideMapper extends SubClassMapperBase { #background: _f$background, #contentOptions: _f$contentOptions, #transition: _f$transition, - #data: _f$data, + #content: _f$content, #options: _f$options, - #raw: _f$raw, + #key: _f$key, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -517,9 +505,9 @@ class ImageSlideMapper extends SubClassMapperBase { background: data.dec(_f$background), contentOptions: data.dec(_f$contentOptions), transition: data.dec(_f$transition), - data: data.dec(_f$data), + content: data.dec(_f$content), options: data.dec(_f$options), - raw: data.dec(_f$raw)); + key: data.dec(_f$key)); } @override @@ -588,9 +576,9 @@ abstract class ImageSlideCopyWith<$R, $In extends ImageSlide, $Out> String? background, ContentOptions? contentOptions, TransitionOptions? transition, - String? data, + String? content, ImageOptions? options, - String? raw}); + String? key}); ImageSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -620,18 +608,18 @@ class _ImageSlideCopyWithImpl<$R, $Out> Object? background = $none, Object? contentOptions = $none, Object? transition = $none, - String? data, + String? content, ImageOptions? options, - String? raw}) => + String? key}) => $apply(FieldCopyWithData({ if (title != $none) #title: title, if (style != $none) #style: style, if (background != $none) #background: background, if (contentOptions != $none) #contentOptions: contentOptions, if (transition != $none) #transition: transition, - if (data != null) #data: data, + if (content != null) #content: content, if (options != null) #options: options, - if (raw != null) #raw: raw + if (key != null) #key: key })); @override ImageSlide $make(CopyWithData data) => ImageSlide( @@ -640,9 +628,9 @@ class _ImageSlideCopyWithImpl<$R, $Out> background: data.get(#background, or: $value.background), contentOptions: data.get(#contentOptions, or: $value.contentOptions), transition: data.get(#transition, or: $value.transition), - data: data.get(#data, or: $value.data), + content: data.get(#content, or: $value.content), options: data.get(#options, or: $value.options), - raw: data.get(#raw, or: $value.raw)); + key: data.get(#key, or: $value.key)); @override ImageSlideCopyWith<$R2, ImageSlide, $Out2> $chain<$R2, $Out2>( @@ -671,8 +659,8 @@ class WidgetSlideMapper extends SubClassMapperBase { static String? _$title(WidgetSlide v) => v.title; static const Field _f$title = Field('title', _$title, opt: true); - static WidgetOptions _$options(WidgetSlide v) => v.options; - static const Field> _f$options = + static WidgetOptions _$options(WidgetSlide v) => v.options; + static const Field _f$options = Field('options', _$options); static String? _$style(WidgetSlide v) => v.style; static const Field _f$style = @@ -682,20 +670,18 @@ class WidgetSlideMapper extends SubClassMapperBase { Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(WidgetSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static TransitionOptions? _$transition(WidgetSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(WidgetSlide v) => v.data; - static const Field _f$data = Field('data', _$data); - static String _$raw(WidgetSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$content(WidgetSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); + static String _$key(WidgetSlide v) => v.key; + static const Field _f$key = Field('key', _$key); static String _$layout(WidgetSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(WidgetSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -705,10 +691,9 @@ class WidgetSlideMapper extends SubClassMapperBase { #background: _f$background, #contentOptions: _f$contentOptions, #transition: _f$transition, - #data: _f$data, - #raw: _f$raw, + #content: _f$content, + #key: _f$key, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -733,8 +718,8 @@ class WidgetSlideMapper extends SubClassMapperBase { background: data.dec(_f$background), contentOptions: data.dec(_f$contentOptions), transition: data.dec(_f$transition), - data: data.dec(_f$data), - raw: data.dec(_f$raw)); + content: data.dec(_f$content), + key: data.dec(_f$key)); } @override @@ -787,10 +772,9 @@ extension WidgetSlideValueCopy<$R, $Out> } abstract class WidgetSlideCopyWith<$R, $In extends WidgetSlide, $Out> - implements SplitSlideCopyWith<$R, $In, $Out, WidgetOptions> { + implements SplitSlideCopyWith<$R, $In, $Out, WidgetOptions> { @override - WidgetOptionsCopyWith<$R, WidgetOptions, WidgetOptions, - dynamic> get options; + WidgetOptionsCopyWith<$R, WidgetOptions, WidgetOptions> get options; @override ContentOptionsCopyWith<$R, ContentOptions, ContentOptions>? get contentOptions; @@ -800,13 +784,13 @@ abstract class WidgetSlideCopyWith<$R, $In extends WidgetSlide, $Out> @override $R call( {String? title, - WidgetOptions? options, + WidgetOptions? options, String? style, String? background, ContentOptions? contentOptions, TransitionOptions? transition, - String? data, - String? raw}); + String? content, + String? key}); WidgetSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -819,9 +803,8 @@ class _WidgetSlideCopyWithImpl<$R, $Out> late final ClassMapperBase $mapper = WidgetSlideMapper.ensureInitialized(); @override - WidgetOptionsCopyWith<$R, WidgetOptions, WidgetOptions, - dynamic> - get options => ($value.options as WidgetOptions) + WidgetOptionsCopyWith<$R, WidgetOptions, WidgetOptions> get options => + ($value.options as WidgetOptions) .copyWith .$chain((v) => call(options: v)); @override @@ -835,13 +818,13 @@ class _WidgetSlideCopyWithImpl<$R, $Out> @override $R call( {Object? title = $none, - WidgetOptions? options, + WidgetOptions? options, Object? style = $none, Object? background = $none, Object? contentOptions = $none, Object? transition = $none, - String? data, - String? raw}) => + String? content, + String? key}) => $apply(FieldCopyWithData({ if (title != $none) #title: title, if (options != null) #options: options, @@ -849,8 +832,8 @@ class _WidgetSlideCopyWithImpl<$R, $Out> if (background != $none) #background: background, if (contentOptions != $none) #contentOptions: contentOptions, if (transition != $none) #transition: transition, - if (data != null) #data: data, - if (raw != null) #raw: raw + if (content != null) #content: content, + if (key != null) #key: key })); @override WidgetSlide $make(CopyWithData data) => WidgetSlide( @@ -860,8 +843,8 @@ class _WidgetSlideCopyWithImpl<$R, $Out> background: data.get(#background, or: $value.background), contentOptions: data.get(#contentOptions, or: $value.contentOptions), transition: data.get(#transition, or: $value.transition), - data: data.get(#data, or: $value.data), - raw: data.get(#raw, or: $value.raw)); + content: data.get(#content, or: $value.content), + key: data.get(#key, or: $value.key)); @override WidgetSlideCopyWith<$R2, WidgetSlide, $Out2> $chain<$R2, $Out2>( @@ -896,26 +879,24 @@ class SectionsSlideMapper extends SubClassMapperBase { Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(SectionsSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$style(SectionsSlide v) => v.style; static const Field _f$style = Field('style', _$style, opt: true); static TransitionOptions? _$transition(SectionsSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(SectionsSlide v) => v.data; - static const Field _f$data = Field('data', _$data); + static String _$content(SectionsSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static Map _$sections(SectionsSlide v) => v.sections; static const Field> _f$sections = Field('sections', _$sections, opt: true, def: const {}); static String _$layout(SectionsSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout); - static String _$raw(SectionsSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); - static String _$hashKey(SectionsSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); + static String _$key(SectionsSlide v) => v.key; + static const Field _f$key = Field('key', _$key); @override final MappableFields fields = const { @@ -924,11 +905,10 @@ class SectionsSlideMapper extends SubClassMapperBase { #contentOptions: _f$contentOptions, #style: _f$style, #transition: _f$transition, - #data: _f$data, + #content: _f$content, #sections: _f$sections, #layout: _f$layout, - #raw: _f$raw, - #hashKey: _f$hashKey, + #key: _f$key, }; @override final bool ignoreNull = true; @@ -981,9 +961,9 @@ abstract class SectionsSlideCopyWith<$R, $In extends SectionsSlide, $Out> ContentOptions? contentOptions, String? style, TransitionOptions? transition, - String? data, + String? content, Map? sections, - String? raw}); + String? key}); SectionsSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -1012,27 +992,25 @@ class TwoColumnSlideMapper extends SubClassMapperBase { Field('background', _$background, opt: true); static ContentOptions? _$contentOptions(TwoColumnSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$style(TwoColumnSlide v) => v.style; static const Field _f$style = Field('style', _$style, opt: true); static TransitionOptions? _$transition(TwoColumnSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(TwoColumnSlide v) => v.data; - static const Field _f$data = Field('data', _$data); + static String _$content(TwoColumnSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static Map _$sections(TwoColumnSlide v) => v.sections; static const Field> _f$sections = Field('sections', _$sections, opt: true, def: const {}); - static String _$raw(TwoColumnSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$key(TwoColumnSlide v) => v.key; + static const Field _f$key = Field('key', _$key); static String _$layout(TwoColumnSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(TwoColumnSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -1041,11 +1019,10 @@ class TwoColumnSlideMapper extends SubClassMapperBase { #contentOptions: _f$contentOptions, #style: _f$style, #transition: _f$transition, - #data: _f$data, + #content: _f$content, #sections: _f$sections, - #raw: _f$raw, + #key: _f$key, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -1065,9 +1042,9 @@ class TwoColumnSlideMapper extends SubClassMapperBase { contentOptions: data.dec(_f$contentOptions), style: data.dec(_f$style), transition: data.dec(_f$transition), - data: data.dec(_f$data), + content: data.dec(_f$content), sections: data.dec(_f$sections), - raw: data.dec(_f$raw)); + key: data.dec(_f$key)); } @override @@ -1139,9 +1116,9 @@ abstract class TwoColumnSlideCopyWith<$R, $In extends TwoColumnSlide, $Out> ContentOptions? contentOptions, String? style, TransitionOptions? transition, - String? data, + String? content, Map? sections, - String? raw}); + String? key}); TwoColumnSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>( Then<$Out2, $R2> t); } @@ -1174,18 +1151,18 @@ class _TwoColumnSlideCopyWithImpl<$R, $Out> Object? contentOptions = $none, Object? style = $none, Object? transition = $none, - String? data, + String? content, Map? sections, - String? raw}) => + String? key}) => $apply(FieldCopyWithData({ if (title != $none) #title: title, if (background != $none) #background: background, if (contentOptions != $none) #contentOptions: contentOptions, if (style != $none) #style: style, if (transition != $none) #transition: transition, - if (data != null) #data: data, + if (content != null) #content: content, if (sections != null) #sections: sections, - if (raw != null) #raw: raw + if (key != null) #key: key })); @override TwoColumnSlide $make(CopyWithData data) => TwoColumnSlide( @@ -1194,9 +1171,9 @@ class _TwoColumnSlideCopyWithImpl<$R, $Out> contentOptions: data.get(#contentOptions, or: $value.contentOptions), style: data.get(#style, or: $value.style), transition: data.get(#transition, or: $value.transition), - data: data.get(#data, or: $value.data), + content: data.get(#content, or: $value.content), sections: data.get(#sections, or: $value.sections), - raw: data.get(#raw, or: $value.raw)); + key: data.get(#key, or: $value.key)); @override TwoColumnSlideCopyWith<$R2, TwoColumnSlide, $Out2> $chain<$R2, $Out2>( @@ -1231,7 +1208,7 @@ class TwoColumnHeaderSlideMapper static ContentOptions? _$contentOptions(TwoColumnHeaderSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$style(TwoColumnHeaderSlide v) => v.style; static const Field _f$style = Field('style', _$style, opt: true); @@ -1239,21 +1216,18 @@ class TwoColumnHeaderSlideMapper v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(TwoColumnHeaderSlide v) => v.data; - static const Field _f$data = - Field('data', _$data); + static String _$content(TwoColumnHeaderSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); static Map _$sections(TwoColumnHeaderSlide v) => v.sections; static const Field> _f$sections = Field('sections', _$sections, opt: true, def: const {}); - static String _$raw(TwoColumnHeaderSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$key(TwoColumnHeaderSlide v) => v.key; + static const Field _f$key = Field('key', _$key); static String _$layout(TwoColumnHeaderSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(TwoColumnHeaderSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -1262,11 +1236,10 @@ class TwoColumnHeaderSlideMapper #contentOptions: _f$contentOptions, #style: _f$style, #transition: _f$transition, - #data: _f$data, + #content: _f$content, #sections: _f$sections, - #raw: _f$raw, + #key: _f$key, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -1286,9 +1259,9 @@ class TwoColumnHeaderSlideMapper contentOptions: data.dec(_f$contentOptions), style: data.dec(_f$style), transition: data.dec(_f$transition), - data: data.dec(_f$data), + content: data.dec(_f$content), sections: data.dec(_f$sections), - raw: data.dec(_f$raw)); + key: data.dec(_f$key)); } @override @@ -1364,9 +1337,9 @@ abstract class TwoColumnHeaderSlideCopyWith< ContentOptions? contentOptions, String? style, TransitionOptions? transition, - String? data, + String? content, Map? sections, - String? raw}); + String? key}); TwoColumnHeaderSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>( Then<$Out2, $R2> t); } @@ -1399,18 +1372,18 @@ class _TwoColumnHeaderSlideCopyWithImpl<$R, $Out> Object? contentOptions = $none, Object? style = $none, Object? transition = $none, - String? data, + String? content, Map? sections, - String? raw}) => + String? key}) => $apply(FieldCopyWithData({ if (title != $none) #title: title, if (background != $none) #background: background, if (contentOptions != $none) #contentOptions: contentOptions, if (style != $none) #style: style, if (transition != $none) #transition: transition, - if (data != null) #data: data, + if (content != null) #content: content, if (sections != null) #sections: sections, - if (raw != null) #raw: raw + if (key != null) #key: key })); @override TwoColumnHeaderSlide $make(CopyWithData data) => TwoColumnHeaderSlide( @@ -1419,9 +1392,9 @@ class _TwoColumnHeaderSlideCopyWithImpl<$R, $Out> contentOptions: data.get(#contentOptions, or: $value.contentOptions), style: data.get(#style, or: $value.style), transition: data.get(#transition, or: $value.transition), - data: data.get(#data, or: $value.data), + content: data.get(#content, or: $value.content), sections: data.get(#sections, or: $value.sections), - raw: data.get(#raw, or: $value.raw)); + key: data.get(#key, or: $value.key)); @override TwoColumnHeaderSlideCopyWith<$R2, TwoColumnHeaderSlide, $Out2> @@ -1448,7 +1421,7 @@ class InvalidSlideMapper extends SubClassMapperBase { static ContentOptions? _$contentOptions(InvalidSlide v) => v.contentOptions; static const Field _f$contentOptions = - Field('contentOptions', _$contentOptions, key: 'content'); + Field('contentOptions', _$contentOptions, key: 'content_options'); static String? _$title(InvalidSlide v) => v.title; static const Field _f$title = Field('title', _$title, opt: true); @@ -1461,16 +1434,14 @@ class InvalidSlideMapper extends SubClassMapperBase { static TransitionOptions? _$transition(InvalidSlide v) => v.transition; static const Field _f$transition = Field('transition', _$transition, opt: true); - static String _$data(InvalidSlide v) => v.data; - static const Field _f$data = Field('data', _$data); - static String _$raw(InvalidSlide v) => v.raw; - static const Field _f$raw = Field('raw', _$raw); + static String _$content(InvalidSlide v) => v.content; + static const Field _f$content = + Field('content', _$content); + static String _$key(InvalidSlide v) => v.key; + static const Field _f$key = Field('key', _$key); static String _$layout(InvalidSlide v) => v.layout; static const Field _f$layout = Field('layout', _$layout, mode: FieldMode.member); - static String _$hashKey(InvalidSlide v) => v.hashKey; - static const Field _f$hashKey = - Field('hashKey', _$hashKey, key: 'hash_key', mode: FieldMode.member); @override final MappableFields fields = const { @@ -1479,10 +1450,9 @@ class InvalidSlideMapper extends SubClassMapperBase { #background: _f$background, #style: _f$style, #transition: _f$transition, - #data: _f$data, - #raw: _f$raw, + #content: _f$content, + #key: _f$key, #layout: _f$layout, - #hashKey: _f$hashKey, }; @override final bool ignoreNull = true; @@ -1501,8 +1471,8 @@ class InvalidSlideMapper extends SubClassMapperBase { background: data.dec(_f$background), style: data.dec(_f$style), transition: data.dec(_f$transition), - data: data.dec(_f$data), - raw: data.dec(_f$raw)); + content: data.dec(_f$content), + key: data.dec(_f$key)); } @override @@ -1570,8 +1540,8 @@ abstract class InvalidSlideCopyWith<$R, $In extends InvalidSlide, $Out> String? background, String? style, TransitionOptions? transition, - String? data, - String? raw}); + String? content, + String? key}); InvalidSlideCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } @@ -1598,16 +1568,16 @@ class _InvalidSlideCopyWithImpl<$R, $Out> Object? background = $none, Object? style = $none, Object? transition = $none, - String? data, - String? raw}) => + String? content, + String? key}) => $apply(FieldCopyWithData({ if (contentOptions != $none) #contentOptions: contentOptions, if (title != $none) #title: title, if (background != $none) #background: background, if (style != $none) #style: style, if (transition != $none) #transition: transition, - if (data != null) #data: data, - if (raw != null) #raw: raw + if (content != null) #content: content, + if (key != null) #key: key })); @override InvalidSlide $make(CopyWithData data) => InvalidSlide( @@ -1616,8 +1586,8 @@ class _InvalidSlideCopyWithImpl<$R, $Out> background: data.get(#background, or: $value.background), style: data.get(#style, or: $value.style), transition: data.get(#transition, or: $value.transition), - data: data.get(#data, or: $value.data), - raw: data.get(#raw, or: $value.raw)); + content: data.get(#content, or: $value.content), + key: data.get(#key, or: $value.key)); @override InvalidSlideCopyWith<$R2, InvalidSlide, $Out2> $chain<$R2, $Out2>( @@ -1640,7 +1610,7 @@ class SectionDataMapper extends RecordMapperBase { static String _$content(SectionData v) => v.content; static const Field _f$content = Field('content', _$content); - static ContentOptions _$options(SectionData v) => v.options; + static ContentOptions? _$options(SectionData v) => v.options; static const Field _f$options = Field('options', _$options); @@ -1707,10 +1677,10 @@ class _SectionDataCopyWithImpl<$R> extends RecordCopyWithBase<$R, SectionData> late final RecordMapperBase $mapper = SectionDataMapper.ensureInitialized(); @override - $R call({String? content, ContentOptions? options}) => + $R call({String? content, Object? options = $none}) => $apply(FieldCopyWithData({ if (content != null) #content: content, - if (options != null) #options: options + if (options != $none) #options: options })); @override SectionData $make(CopyWithData data) => ( diff --git a/packages/superdeck/lib/providers/assets_provider.dart b/packages/superdeck/lib/providers/assets_provider.dart new file mode 100644 index 00000000..e2d0f31c --- /dev/null +++ b/packages/superdeck/lib/providers/assets_provider.dart @@ -0,0 +1,32 @@ +import 'package:flutter/widgets.dart'; + +import '../superdeck.dart'; + +class AssetsProvider extends InheritedWidget { + const AssetsProvider({ + super.key, + required this.assets, + required super.child, + }); + + final List assets; + + static List of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType() + ?.assets ?? + []; + } + + static AssetsProvider inherit({ + required BuildContext context, + required Widget child, + }) { + return AssetsProvider(assets: of(context), child: child); + } + + @override + bool updateShouldNotify(covariant AssetsProvider oldWidget) { + return assets != oldWidget.assets; + } +} diff --git a/packages/superdeck/lib/providers/controller.dart b/packages/superdeck/lib/providers/controller.dart new file mode 100644 index 00000000..57ccadc2 --- /dev/null +++ b/packages/superdeck/lib/providers/controller.dart @@ -0,0 +1,77 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import '../models/asset_model.dart'; +import '../models/slide_model.dart'; +import '../services/reference_service.dart'; + +final $superdeck = SuperDeckController.instance; + +class SuperDeckController extends ChangeNotifier { + SuperDeckController._(); + + static final instance = SuperDeckController._(); + + bool _initialized = false; + + static Future initialize() async { + if (instance._initialized) return; + instance._initialized = true; + await instance._loadData(); + ReferenceService.instance.listen(instance._loadData); + } + + bool _loading = false; + Object? _error; + List _slides = []; + List _assets = []; + bool _completed = false; + bool _isRefreshing = false; + + bool get loading => _loading; + Object? get error => _error; + List get slides => _slides; + List get assets => _assets; + bool get completed => _completed; + bool get isRefreshing => _isRefreshing; + + Future _loadData() async { + _loading = true; + _error = null; + _completed = false; + notifyListeners(); + + try { + final data = await ReferenceService.instance.loadReference(); + + _slides = data.slides; + _assets = data.assets; + } catch (e) { + _error = e; + } finally { + _completed = true; + _loading = false; + _isRefreshing = false; + notifyListeners(); + } + } + + Future refresh() async { + _isRefreshing = true; + await _loadData(); + } +} + +T useSuperdeckSelector(T Function(SuperDeckController) selector) { + return useListenableSelector( + $superdeck, + () => selector($superdeck), + ); +} + +List useSlides() { + return useListenableSelector( + $superdeck, + () => $superdeck.slides, + ); +} diff --git a/packages/superdeck/lib/providers/examples_provider.dart b/packages/superdeck/lib/providers/examples_provider.dart new file mode 100644 index 00000000..a067cbea --- /dev/null +++ b/packages/superdeck/lib/providers/examples_provider.dart @@ -0,0 +1,31 @@ +import 'package:flutter/material.dart'; + +import '../superdeck.dart'; + +class ExamplesProvider extends InheritedWidget { + const ExamplesProvider({ + super.key, + required this.examples, + required super.child, + }); + + final Map examples; + + static Map of(BuildContext context) { + return context + .dependOnInheritedWidgetOfExactType()! + .examples; + } + + static ExamplesProvider inherit({ + required BuildContext context, + required Widget child, + }) { + return ExamplesProvider(examples: of(context), child: child); + } + + @override + bool updateShouldNotify(covariant ExamplesProvider oldWidget) { + return examples != oldWidget.examples; + } +} diff --git a/packages/superdeck/lib/providers/slide_provider.dart b/packages/superdeck/lib/providers/slide_provider.dart new file mode 100644 index 00000000..97fd8965 --- /dev/null +++ b/packages/superdeck/lib/providers/slide_provider.dart @@ -0,0 +1,51 @@ +// Create a SlideProvider that extends an Inherited widget +import 'package:flutter/material.dart'; + +import '../models/slide_model.dart'; +import '../templates/templates.dart'; + +enum SlideProviderAspect { + slide, + constraints, +} + +class SlideProvider extends InheritedWidget { + const SlideProvider({ + super.key, + required this.slide, + required super.child, + }); + + final T slide; + + static T of(BuildContext context) { + final slideProvider = + context.dependOnInheritedWidgetOfExactType>(); + if (slideProvider == null) { + throw Exception('SlideProvider not found in context'); + } + return slideProvider.slide; + } + + @override + bool updateShouldNotify(covariant SlideProvider oldWidget) { + return oldWidget.slide != slide; + } +} + +class SlideBuilder extends StatelessWidget { + const SlideBuilder( + this.config, { + super.key, + }); + + final Slide config; + + @override + Widget build(BuildContext context) { + return SlideProvider( + slide: config, + child: TemplateBuilder.buildTemplate(config), + ); + } +} diff --git a/packages/superdeck/lib/providers/snapshot_provider.dart b/packages/superdeck/lib/providers/snapshot_provider.dart new file mode 100644 index 00000000..bd7d66d3 --- /dev/null +++ b/packages/superdeck/lib/providers/snapshot_provider.dart @@ -0,0 +1,42 @@ +import 'package:flutter/widgets.dart'; + +class SnapshotRef { + BuildContext? _context; + + SnapshotRef._(); + + static final SnapshotRef instance = SnapshotRef._(); + + void setContext(BuildContext context) { + Scrollable.ensureVisible(context); + _context = context; + } + + BuildContext get context { + assert(_context != null, '$runtimeType has not updated its context'); + return _context!; + } +} + +class SnapshotProvider extends InheritedWidget { + final bool isCapturing; + + const SnapshotProvider({ + required this.isCapturing, + required super.child, + super.key, + }); + + static SnapshotProvider? of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType(); + } + + @override + bool updateShouldNotify(covariant SnapshotProvider oldWidget) { + return oldWidget.isCapturing != isCapturing; + } + + static bool isCapturingOf(BuildContext context) { + return of(context)?.isCapturing ?? false; + } +} diff --git a/packages/superdeck/lib/providers/style_provider.dart b/packages/superdeck/lib/providers/style_provider.dart new file mode 100644 index 00000000..34fcceb5 --- /dev/null +++ b/packages/superdeck/lib/providers/style_provider.dart @@ -0,0 +1,46 @@ +import 'package:flutter/widgets.dart'; + +import '../styles/style_util.dart'; +import '../superdeck.dart'; + +class StyleProvider extends InheritedWidget { + StyleProvider({ + super.key, + Style? baseStyle, + this.styles = const {}, + required super.child, + }) : style = defaultStyle.merge(baseStyle); + + final Style style; + final Map styles; + + static StyleProvider _of(BuildContext context) { + return context.dependOnInheritedWidgetOfExactType()!; + } + + static Style of(BuildContext context, String? styleName) { + final provider = _of(context); + if (styleName != null) { + final style = provider.styles[styleName]; + assert(style != null, 'No Style for $styleName found.'); + return provider.style.merge(style); + } + return _of(context).style; + } + + static StyleProvider inherit({ + required BuildContext context, + required Widget child, + }) { + return StyleProvider( + baseStyle: _of(context).style, + styles: _of(context).styles, + child: child, + ); + } + + @override + bool updateShouldNotify(covariant StyleProvider oldWidget) { + return style != oldWidget.style; + } +} diff --git a/packages/superdeck/lib/schema/schema_model.dart b/packages/superdeck/lib/schema/schema_model.dart new file mode 100644 index 00000000..ff327a5c --- /dev/null +++ b/packages/superdeck/lib/schema/schema_model.dart @@ -0,0 +1,162 @@ +import 'schema_validation.dart'; +import 'schema_values.dart'; +import 'validators.dart'; + +typedef JSON = Map; + +class SchemaMap extends SchemaValue> { + final Map properties; + final bool additionalProperties; + const SchemaMap( + this.properties, { + super.optional = true, + this.additionalProperties = false, + super.validators = const [], + }); + + @override + SchemaMap copyWith({ + bool? optional, + bool? additionalProperties, + Map? properties, + List>>? validators, + }) { + return SchemaMap( + properties ?? this.properties, + additionalProperties: additionalProperties ?? this.additionalProperties, + optional: optional ?? optionalValue, + validators: validators ?? this.validators, + ); + } + + @override + Map? tryParse(Object? value) { + return value is Map ? value : null; + } + + SchemaMap mergeSchema(SchemaMap schema) { + return merge( + schema.properties, + additionalProperties: schema.additionalProperties, + ); + } + + T? getSchemaValue(String key) { + return properties[key] as T?; + } + + SchemaMap merge( + Map properties, { + bool? additionalProperties, + bool? optional, + }) { + // if property SchemaValue is of SchemaMap, we need to merge them + final mergedProperties = {...this.properties}; + + for (final entry in properties.entries) { + final key = entry.key; + final prop = entry.value; + + final existingProp = mergedProperties[key]; + + if (existingProp is SchemaMap && prop is SchemaMap) { + mergedProperties[key] = existingProp.merge(prop.properties); + } else { + mergedProperties[key] = prop; + } + } + + return copyWith( + properties: mergedProperties, + optional: optional, + additionalProperties: additionalProperties, + ); + } + + @override + SchemaValidationResult validate(List path, Object? value) { + if (value == null) { + return optionalValue + ? SchemaValidationResult.valid(path) + : SchemaValidationResult.requiredPropMissing(path); + } + + final parsedValue = tryParse(value); + + if (parsedValue == null) { + return SchemaValidationResult.invalidType( + path, + value, + {}.toString(), + ); + } + + final keys = parsedValue.keys.toSet(); + + final required = properties.entries + .where((entry) => !entry.value.optionalValue) + .map((entry) => entry.key); + + final requiredKeys = required.toSet(); + + if (!keys.containsAll(requiredKeys)) { + return SchemaValidationResult( + key: path, + errors: requiredKeys + .difference(keys) + .map(SchemaError.requiredPropMissing) + .toList()); + } + + if (additionalProperties == false) { + final extraKeys = keys.difference(properties.keys.toSet()); + if (extraKeys.isNotEmpty) { + return SchemaValidationResult( + key: path, + errors: + extraKeys.map(SchemaError.unallowedAdditionalProperty).toList(), + ); + } + } + + for (final entry in parsedValue.entries) { + final key = entry.key; + final prop = properties[key]; + + if (prop == null) { + return additionalProperties == false + ? SchemaValidationResult( + key: path, + errors: [SchemaError.unallowedAdditionalProperty(key)]) + : SchemaValidationResult.valid(path); + } + + final result = prop.validate([...path, key], entry.value); + if (!result.isValid) { + return result; + } + } + + return SchemaValidationResult.valid(path); + } +} + +class SchemaShape extends SchemaMap { + const SchemaShape( + super.properties, { + super.additionalProperties = false, + }); +} + +typedef _DoubleType = double; + +class Schema { + const Schema._(); + + static const string = SchemaValue(); + static const map = SchemaShape.new; + static const double = SchemaValue<_DoubleType>(); + static const integer = SchemaValue(); + static const boolean = SchemaValue(); + static const any = SchemaShape({}, additionalProperties: true); +} diff --git a/packages/superdeck/lib/schema/schema_validation.dart b/packages/superdeck/lib/schema/schema_validation.dart new file mode 100644 index 00000000..b37f1428 --- /dev/null +++ b/packages/superdeck/lib/schema/schema_validation.dart @@ -0,0 +1,124 @@ +import 'package:dart_mappable/dart_mappable.dart'; + +part 'schema_validation.mapper.dart'; + +typedef JSON = Map; + +class SchemaValidationException implements Exception { + final SchemaValidationResult result; + + const SchemaValidationException(this.result); +} + +enum SchemaErrorType { + unallowedAdditionalProperty, + enumViolated, + requiredPropMissing, + invalidType, + constraints, + unknown; +} + +@MappableClass() +class SchemaError with SchemaErrorMappable { + final SchemaErrorType type; + final String message; + + const SchemaError.unknown() + : type = SchemaErrorType.unknown, + message = 'Unknown error'; + + const SchemaError.constraints(this.message) + : type = SchemaErrorType.constraints; + + const SchemaError.unallowedAdditionalProperty(String property) + : type = SchemaErrorType.unallowedAdditionalProperty, + message = 'Unallowed property: [$property]'; + + const SchemaError.enumViolated(String value, List possibleValues) + : type = SchemaErrorType.enumViolated, + message = 'Wrong value: [$value] \n\n Possible values: $possibleValues'; + + const SchemaError.requiredPropMissing(String property) + : type = SchemaErrorType.requiredPropMissing, + message = 'Missing prop: [$property]'; + + const SchemaError.invalidType(Type value, String expectedType) + : type = SchemaErrorType.invalidType, + message = 'Invalid type: [$expectedType] got [$value]'; + + @override + String toString() { + return 'SchemaValidationError{type: $type, message: $message}'; + } +} + +@MappableClass() +class SchemaValidationResult with SchemaValidationResultMappable { + final List key; + final List errors; + + const SchemaValidationResult({ + required this.key, + required this.errors, + }); + + const SchemaValidationResult.valid(this.key) : errors = const []; + + factory SchemaValidationResult.invalidType( + List path, + Object value, + String expectedType, + ) { + return SchemaValidationResult( + key: path, + errors: [ + SchemaError.invalidType( + value.runtimeType, + expectedType, + ) + ], + ); + } + + factory SchemaValidationResult.unallowedAdditionalProperty( + List path, String property) { + return SchemaValidationResult( + key: path, + errors: [SchemaError.unallowedAdditionalProperty(property)], + ); + } + + factory SchemaValidationResult.enumViolated( + List path, String value, List possibleValues) { + return SchemaValidationResult( + key: path, + errors: [SchemaError.enumViolated(value, possibleValues)], + ); + } + + factory SchemaValidationResult.requiredPropMissing(List path) { + return SchemaValidationResult( + key: path, + errors: [SchemaError.requiredPropMissing(path.last)], + ); + } + + factory SchemaValidationResult.constraints( + List path, String message) { + return SchemaValidationResult( + key: path, + errors: [SchemaError.constraints(message)], + ); + } + + @override + String toString() { + return '${errors.isEmpty ? 'VALID' : 'INVALID'}${errors.isEmpty ? ', Errors: $errors' : ''}'; + } + + bool get isValid => errors.isEmpty; + + static const fromMap = SchemaValidationResultMapper.fromMap; + static const fromJson = SchemaValidationResultMapper.fromJson; +} diff --git a/lib/schema/schema_model.mapper.dart b/packages/superdeck/lib/schema/schema_validation.mapper.dart similarity index 52% rename from lib/schema/schema_model.mapper.dart rename to packages/superdeck/lib/schema/schema_validation.mapper.dart index 545c22cf..1ea4d17a 100644 --- a/lib/schema/schema_model.mapper.dart +++ b/packages/superdeck/lib/schema/schema_validation.mapper.dart @@ -4,125 +4,113 @@ // ignore_for_file: unused_element, unnecessary_cast, override_on_non_overriding_member // ignore_for_file: strict_raw_type, inference_failure_on_untyped_parameter -part of 'schema_model.dart'; +part of 'schema_validation.dart'; -class SchemaValidationErrorMapper - extends ClassMapperBase { - SchemaValidationErrorMapper._(); +class SchemaErrorMapper extends ClassMapperBase { + SchemaErrorMapper._(); - static SchemaValidationErrorMapper? _instance; - static SchemaValidationErrorMapper ensureInitialized() { + static SchemaErrorMapper? _instance; + static SchemaErrorMapper ensureInitialized() { if (_instance == null) { - MapperContainer.globals.use(_instance = SchemaValidationErrorMapper._()); + MapperContainer.globals.use(_instance = SchemaErrorMapper._()); } return _instance!; } @override - final String id = 'SchemaValidationError'; + final String id = 'SchemaError'; - static String _$type(SchemaValidationError v) => v.type; - static const Field _f$type = - Field('type', _$type); - static String _$message(SchemaValidationError v) => v.message; - static const Field _f$message = - Field('message', _$message); + static SchemaErrorType _$type(SchemaError v) => v.type; + static const Field _f$type = + Field('type', _$type, mode: FieldMode.member); + static String _$message(SchemaError v) => v.message; + static const Field _f$message = + Field('message', _$message, mode: FieldMode.member); @override - final MappableFields fields = const { + final MappableFields fields = const { #type: _f$type, #message: _f$message, }; @override final bool ignoreNull = true; - static SchemaValidationError _instantiate(DecodingData data) { - return SchemaValidationError( - type: data.dec(_f$type), message: data.dec(_f$message)); + static SchemaError _instantiate(DecodingData data) { + return SchemaError.unknown(); } @override final Function instantiate = _instantiate; - static SchemaValidationError fromMap(Map map) { - return ensureInitialized().decodeMap(map); + static SchemaError fromMap(Map map) { + return ensureInitialized().decodeMap(map); } - static SchemaValidationError fromJson(String json) { - return ensureInitialized().decodeJson(json); + static SchemaError fromJson(String json) { + return ensureInitialized().decodeJson(json); } } -mixin SchemaValidationErrorMappable { +mixin SchemaErrorMappable { String toJson() { - return SchemaValidationErrorMapper.ensureInitialized() - .encodeJson(this as SchemaValidationError); + return SchemaErrorMapper.ensureInitialized() + .encodeJson(this as SchemaError); } Map toMap() { - return SchemaValidationErrorMapper.ensureInitialized() - .encodeMap(this as SchemaValidationError); + return SchemaErrorMapper.ensureInitialized() + .encodeMap(this as SchemaError); } - SchemaValidationErrorCopyWith - get copyWith => _SchemaValidationErrorCopyWithImpl( - this as SchemaValidationError, $identity, $identity); + SchemaErrorCopyWith get copyWith => + _SchemaErrorCopyWithImpl(this as SchemaError, $identity, $identity); @override String toString() { - return SchemaValidationErrorMapper.ensureInitialized() - .stringifyValue(this as SchemaValidationError); + return SchemaErrorMapper.ensureInitialized() + .stringifyValue(this as SchemaError); } @override bool operator ==(Object other) { - return SchemaValidationErrorMapper.ensureInitialized() - .equalsValue(this as SchemaValidationError, other); + return SchemaErrorMapper.ensureInitialized() + .equalsValue(this as SchemaError, other); } @override int get hashCode { - return SchemaValidationErrorMapper.ensureInitialized() - .hashValue(this as SchemaValidationError); + return SchemaErrorMapper.ensureInitialized().hashValue(this as SchemaError); } } -extension SchemaValidationErrorValueCopy<$R, $Out> - on ObjectCopyWith<$R, SchemaValidationError, $Out> { - SchemaValidationErrorCopyWith<$R, SchemaValidationError, $Out> - get $asSchemaValidationError => - $base.as((v, t, t2) => _SchemaValidationErrorCopyWithImpl(v, t, t2)); +extension SchemaErrorValueCopy<$R, $Out> + on ObjectCopyWith<$R, SchemaError, $Out> { + SchemaErrorCopyWith<$R, SchemaError, $Out> get $asSchemaError => + $base.as((v, t, t2) => _SchemaErrorCopyWithImpl(v, t, t2)); } -abstract class SchemaValidationErrorCopyWith< - $R, - $In extends SchemaValidationError, - $Out> implements ClassCopyWith<$R, $In, $Out> { - $R call({String? type, String? message}); - SchemaValidationErrorCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>( - Then<$Out2, $R2> t); +abstract class SchemaErrorCopyWith<$R, $In extends SchemaError, $Out> + implements ClassCopyWith<$R, $In, $Out> { + $R call(); + SchemaErrorCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>(Then<$Out2, $R2> t); } -class _SchemaValidationErrorCopyWithImpl<$R, $Out> - extends ClassCopyWithBase<$R, SchemaValidationError, $Out> - implements SchemaValidationErrorCopyWith<$R, SchemaValidationError, $Out> { - _SchemaValidationErrorCopyWithImpl(super.value, super.then, super.then2); +class _SchemaErrorCopyWithImpl<$R, $Out> + extends ClassCopyWithBase<$R, SchemaError, $Out> + implements SchemaErrorCopyWith<$R, SchemaError, $Out> { + _SchemaErrorCopyWithImpl(super.value, super.then, super.then2); @override - late final ClassMapperBase $mapper = - SchemaValidationErrorMapper.ensureInitialized(); + late final ClassMapperBase $mapper = + SchemaErrorMapper.ensureInitialized(); @override - $R call({String? type, String? message}) => $apply(FieldCopyWithData( - {if (type != null) #type: type, if (message != null) #message: message})); + $R call() => $apply(FieldCopyWithData({})); @override - SchemaValidationError $make(CopyWithData data) => SchemaValidationError( - type: data.get(#type, or: $value.type), - message: data.get(#message, or: $value.message)); + SchemaError $make(CopyWithData data) => SchemaError.unknown(); @override - SchemaValidationErrorCopyWith<$R2, SchemaValidationError, $Out2> - $chain<$R2, $Out2>(Then<$Out2, $R2> t) => - _SchemaValidationErrorCopyWithImpl($value, $cast, t); + SchemaErrorCopyWith<$R2, SchemaError, $Out2> $chain<$R2, $Out2>( + Then<$Out2, $R2> t) => + _SchemaErrorCopyWithImpl($value, $cast, t); } class SchemaValidationResultMapper @@ -133,7 +121,7 @@ class SchemaValidationResultMapper static SchemaValidationResultMapper ensureInitialized() { if (_instance == null) { MapperContainer.globals.use(_instance = SchemaValidationResultMapper._()); - SchemaValidationErrorMapper.ensureInitialized(); + SchemaErrorMapper.ensureInitialized(); } return _instance!; } @@ -144,10 +132,9 @@ class SchemaValidationResultMapper static List _$key(SchemaValidationResult v) => v.key; static const Field> _f$key = Field('key', _$key); - static List _$errors(SchemaValidationResult v) => - v.errors; - static const Field> - _f$errors = Field('errors', _$errors); + static List _$errors(SchemaValidationResult v) => v.errors; + static const Field> _f$errors = + Field('errors', _$errors); @override final MappableFields fields = const { @@ -220,12 +207,9 @@ abstract class SchemaValidationResultCopyWith< $In extends SchemaValidationResult, $Out> implements ClassCopyWith<$R, $In, $Out> { ListCopyWith<$R, String, ObjectCopyWith<$R, String, String>> get key; - ListCopyWith< - $R, - SchemaValidationError, - SchemaValidationErrorCopyWith<$R, SchemaValidationError, - SchemaValidationError>> get errors; - $R call({List? key, List? errors}); + ListCopyWith<$R, SchemaError, + SchemaErrorCopyWith<$R, SchemaError, SchemaError>> get errors; + $R call({List? key, List? errors}); SchemaValidationResultCopyWith<$R2, $In, $Out2> $chain<$R2, $Out2>( Then<$Out2, $R2> t); } @@ -244,14 +228,12 @@ class _SchemaValidationResultCopyWithImpl<$R, $Out> ListCopyWith($value.key, (v, t) => ObjectCopyWith(v, $identity, t), (v) => call(key: v)); @override - ListCopyWith< - $R, - SchemaValidationError, - SchemaValidationErrorCopyWith<$R, SchemaValidationError, - SchemaValidationError>> get errors => ListCopyWith( - $value.errors, (v, t) => v.copyWith.$chain(t), (v) => call(errors: v)); + ListCopyWith<$R, SchemaError, + SchemaErrorCopyWith<$R, SchemaError, SchemaError>> + get errors => ListCopyWith($value.errors, (v, t) => v.copyWith.$chain(t), + (v) => call(errors: v)); @override - $R call({List? key, List? errors}) => + $R call({List? key, List? errors}) => $apply(FieldCopyWithData( {if (key != null) #key: key, if (errors != null) #errors: errors})); @override diff --git a/packages/superdeck/lib/schema/schema_values.dart b/packages/superdeck/lib/schema/schema_values.dart new file mode 100644 index 00000000..bf05c956 --- /dev/null +++ b/packages/superdeck/lib/schema/schema_values.dart @@ -0,0 +1,214 @@ +import 'package:flutter/material.dart'; + +import 'schema_validation.dart'; +import 'validators.dart'; + +class SchemaValue { + const SchemaValue({ + bool optional = true, + this.validators = const [], + }) : optionalValue = optional; + + SchemaValue copyWith({ + bool? optional, + List>? validators, + }) { + return SchemaValue( + optional: optional ?? this.optionalValue, + validators: validators ?? this.validators, + ); + } + + SchemaValue required() { + return copyWith(optional: false); + } + + SchemaValue optional() { + return copyWith(optional: true); + } + + @protected + final bool optionalValue; + + final List> validators; + + bool get requiredValue => !optionalValue; + + V? tryParse(Object? value) { + if (value is V) { + return value; + } + if (V is int) { + return _tryParseInt(value) as V?; + } + + if (V is double) { + return _tryParseDouble(value) as V?; + } + + if (V is bool) { + return _tryParseBool(value) as V?; + } + + return null; + } + + void validateOrThrow(Object value) { + final result = validate([], value); + if (!result.isValid) { + throw SchemaValidationException(result); + } + } + + SchemaValidationResult validate(List path, Object? value) { + if (value == null) { + return optionalValue + ? SchemaValidationResult.valid(path) + : SchemaValidationResult.requiredPropMissing(path); + } + + final valueType = tryParse(value); + + if (valueType == null) { + return SchemaValidationResult.invalidType(path, value, V.toString()); + } + + for (final validator in validators) { + final error = validator.validate(valueType); + if (error != null) { + return SchemaValidationResult.constraints(path, error.message); + } + } + + return SchemaValidationResult.valid(path); + } +} + +/// Used to remove value from SchemaMap + +double? _tryParseDouble(Object? value) { + if (value is double) { + return value; + } + + if (value is int) { + return value.toDouble(); + } + + if (value is String) { + return double.tryParse(value); + } + + return null; +} + +bool? _tryParseBool(Object? value) { + if (value is bool) { + return value; + } + + if (value is String) { + if (value.toLowerCase() == 'true') { + return true; + } else if (value.toLowerCase() == 'false') { + return false; + } + } + + return null; +} + +int? _tryParseInt(Object? value) { + if (value is int) { + return value; + } + + if (value is String) { + return int.tryParse(value); + } + + return null; +} + +class BooleanSchema extends SchemaValue { + const BooleanSchema({super.optional = false, super.validators = const []}); + + @override + BooleanSchema copyWith({ + bool? optional, + List>? validators, + }) { + return BooleanSchema( + optional: optional ?? optionalValue, + validators: validators ?? this.validators, + ); + } + + @override + bool? tryParse(Object? value) { + if (value is bool) { + return value; + } + + if (value is String) { + if (value.toLowerCase() == 'true') { + return true; + } else if (value.toLowerCase() == 'false') { + return false; + } + } + + return null; + } +} + +extension StringSchemaExt on SchemaValue { + SchemaValue isPosixPath() { + return copyWith(validators: [ + ...validators, + const PosixPathValidator(), + ]); + } + + SchemaValue isEmail() { + return copyWith(validators: [ + ...validators, + const EmailValidator(), + ]); + } + + SchemaValue isHexColor() { + return copyWith(validators: [ + ...validators, + const HexColorValidator(), + ]); + } + + SchemaValue isArray(List values) { + return copyWith(validators: [ + ...validators, + ArrayValidator(values), + ]); + } + + SchemaValue isEmpty() { + return copyWith(validators: [ + ...validators, + const IsEmptyValidator(), + ]); + } + + SchemaValue minLength(int min) { + return copyWith(validators: [ + ...validators, + MinLengthValidator(min), + ]); + } + + SchemaValue maxLength(int max) { + return copyWith(validators: [ + ...validators, + MaxLengthValidator(max), + ]); + } +} diff --git a/lib/schema/validators.dart b/packages/superdeck/lib/schema/validators.dart similarity index 73% rename from lib/schema/validators.dart rename to packages/superdeck/lib/schema/validators.dart index f63c48f1..230e60c6 100644 --- a/lib/schema/validators.dart +++ b/packages/superdeck/lib/schema/validators.dart @@ -1,9 +1,27 @@ -import 'schema_model.dart'; +import 'schema_validation.dart'; abstract class Validator { const Validator(); - SchemaValidationError? validate(T value); + SchemaError? validate(T value); +} + +class ArrayValidator extends Validator { + final List values; + const ArrayValidator(this.values); + + @override + SchemaError? validate(Object? value) { + if (value is String) { + if (values.contains(value)) { + return null; + } + } + return SchemaError.enumViolated( + '$value', + values, + ); + } } class EmailValidator extends RegexValidator { @@ -54,9 +72,9 @@ class RegexValidator extends Validator { }); @override - SchemaValidationError? validate(String value) { + SchemaError? validate(String value) { if (!RegExp(pattern).hasMatch(value)) { - return SchemaValidationError.constraints( + return SchemaError.constraints( 'String does is not $name. Example: $example', ); } @@ -69,10 +87,10 @@ class IsEmptyValidator extends Validator { const IsEmptyValidator(); @override - SchemaValidationError? validate(String value) { + SchemaError? validate(String value) { return value.isEmpty ? null - : const SchemaValidationError.constraints('String is not empty'); + : const SchemaError.constraints('String is not empty'); } } @@ -81,10 +99,10 @@ class MinLengthValidator extends Validator { const MinLengthValidator(this.min); @override - SchemaValidationError? validate(String value) { + SchemaError? validate(String value) { return value.length >= min ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'String length is less than the minimum required length: $min', ); } @@ -95,10 +113,10 @@ class MaxLengthValidator extends Validator { const MaxLengthValidator(this.max); @override - SchemaValidationError? validate(String value) { + SchemaError? validate(String value) { return value.length <= max ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'String length is greater than the maximum required length: $max', ); } @@ -109,10 +127,10 @@ class MinValueValidator extends Validator { const MinValueValidator(this.min); @override - SchemaValidationError? validate(num value) { + SchemaError? validate(num value) { return value >= min ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'Value is less than the minimum required value: $min', ); } @@ -123,10 +141,10 @@ class MaxValueValidator extends Validator { const MaxValueValidator(this.max); @override - SchemaValidationError? validate(num value) { + SchemaError? validate(num value) { return value <= max ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'Value is greater than the maximum required value: $max', ); } @@ -138,10 +156,10 @@ class RangeValidator extends Validator { const RangeValidator(this.min, this.max); @override - SchemaValidationError? validate(num value) { + SchemaError? validate(num value) { return value >= min && value <= max ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'Value is not within the required range: $min - $max', ); } @@ -151,10 +169,8 @@ class RequiredValidator extends Validator { const RequiredValidator(); @override - SchemaValidationError? validate(value) { - return value != null - ? null - : const SchemaValidationError.constraints('is required'); + SchemaError? validate(value) { + return value != null ? null : const SchemaError.constraints('is required'); } } @@ -163,11 +179,11 @@ class UniqueItemsValidator extends Validator> { const UniqueItemsValidator(); @override - SchemaValidationError? validate(List value) { + SchemaError? validate(List value) { final unique = value.toSet().toList(); return unique.length == value.length ? null - : const SchemaValidationError.constraints('List items are not unique'); + : const SchemaError.constraints('List items are not unique'); } } @@ -177,10 +193,10 @@ class MinItemsValidator extends Validator> { const MinItemsValidator(this.min); @override - SchemaValidationError? validate(List value) { + SchemaError? validate(List value) { return value.length >= min ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'List length is less than the minimum required length: $min', ); } @@ -192,10 +208,10 @@ class MaxItemsValidator extends Validator> { const MaxItemsValidator(this.max); @override - SchemaValidationError? validate(List value) { + SchemaError? validate(List value) { return value.length <= max ? null - : SchemaValidationError.constraints( + : SchemaError.constraints( 'List length is greater than the maximum required length: $max', ); } diff --git a/packages/superdeck/lib/screens/export_screen.dart b/packages/superdeck/lib/screens/export_screen.dart new file mode 100644 index 00000000..21089c68 --- /dev/null +++ b/packages/superdeck/lib/screens/export_screen.dart @@ -0,0 +1,134 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import '../components/remix/button.dart'; +import '../helpers/extensions.dart'; +import '../helpers/routes.dart'; +import '../services/export_service.dart'; +import '../services/snapshot_service.dart'; +import '../superdeck.dart'; + +class ExportScreen extends HookWidget { + const ExportScreen({super.key}); + + @override + Widget build(BuildContext context) { + final slides = useSlides(); + final currentSlideIndex = useValueNotifier( + context.currentSlideIndex, [context.currentSlideIndex]); + + final export = usePdfExportController( + slides: slides, + slideIndex: currentSlideIndex.value, + ); + + final inProgress = export.inProgress; + + return Stack( + children: [ + Center( + child: export.render(), + ), + Positioned.fill( + child: Container( + color: const Color.fromARGB(255, 14, 14, 14).withOpacity(0.9), + )), + inProgress ? _ProgressDialog(export) : ExportDialog(export), + ], + ); + } +} + +class _ProgressDialog extends StatelessWidget { + const _ProgressDialog(this.controller); + + final PdfExportController controller; + + @override + Widget build(BuildContext context) { + return Dialog( + child: Padding( + padding: const EdgeInsets.symmetric( + horizontal: 16.0, + vertical: 32.0, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + controller.isComplete + ? Icon( + Icons.check_circle, + color: context.colorScheme.primary, + size: 42, + ) + : SizedBox( + height: 42, + width: 42, + child: CircularProgressIndicator( + color: context.colorScheme.primary, + value: controller.isBuilding ? null : controller.progress, + ), + ), + const SizedBox(height: 16.0), + Text(controller.progressText), + ], + ), + ), + ); + } +} + +class ExportDialog extends HookWidget { + const ExportDialog( + this.controller, { + super.key, + }); + final PdfExportController controller; + @override + Widget build(BuildContext context) { + final dropdownItems = SnapshotQuality.values.map( + (quality) => DropdownMenuItem( + value: quality, + child: Text( + quality.name.capitalize(), + ), + ), + ); + + return Dialog( + backgroundColor: Colors.black, + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + const Text('Select export quality:'), + const SizedBox(height: 16.0), + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(8.0), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + DropdownButton( + isDense: true, + value: controller.quality, + focusColor: Colors.transparent, + underline: const SizedBox.shrink(), + onChanged: (value) => controller.quality = value!, + items: dropdownItems.toList(), + ), + SizedBox(width: 10), + SDButtonSolid( + label: 'Export', + onPressed: () => controller.start(), + ), + ], + )), + ], + ), + ), + ); + } +} diff --git a/packages/superdeck/lib/screens/presentation_screen.dart b/packages/superdeck/lib/screens/presentation_screen.dart new file mode 100644 index 00000000..66b462ec --- /dev/null +++ b/packages/superdeck/lib/screens/presentation_screen.dart @@ -0,0 +1,47 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; + +import '../components/molecules/slide_preview.dart'; +import '../helpers/hooks.dart'; +import '../helpers/routes.dart'; +import '../superdeck.dart'; + +class PresentationScreen extends HookWidget { + const PresentationScreen({super.key}); + + final _duration = const Duration(milliseconds: 300); + final _curve = Curves.easeInOutCubic; + + @override + Widget build(BuildContext context) { + final slideIndex = context.currentSlideIndex; + final pageController = usePageController(initialPage: slideIndex); + + final slides = useSlides(); + + usePostFrameEffect(() { + if (slideIndex >= slides.length) { + return; + } + + if (slideIndex < 0) { + return; + } + pageController.animateToPage( + slideIndex, + duration: _duration, + curve: _curve, + ); + }, [slideIndex, slides.length]); + + return Center( + child: PageView.builder( + controller: pageController, + itemCount: slides.length, + itemBuilder: (_, index) { + return SlidePreview(slides[index]); + }, + ), + ); + } +} diff --git a/packages/superdeck/lib/services/export_service.dart b/packages/superdeck/lib/services/export_service.dart new file mode 100644 index 00000000..30e0b03e --- /dev/null +++ b/packages/superdeck/lib/services/export_service.dart @@ -0,0 +1,256 @@ +import 'dart:async'; + +import 'package:file_saver/file_saver.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_hooks/flutter_hooks.dart'; +import 'package:pdf/pdf.dart'; +import 'package:pdf/widgets.dart' as pw; + +import '../components/molecules/slide_preview.dart'; +import '../helpers/constants.dart'; +import '../superdeck.dart'; +import 'snapshot_service.dart'; + +enum PdfExportStatus { + idle, + capturing, + building, + complete, +} + +class PdfExportController extends ChangeNotifier { + SnapshotQuality _quality = SnapshotQuality.good; + PdfExportStatus _status = PdfExportStatus.idle; + List _images = []; + late final PageController _pageController; + + /// Create a map for keys and GlobalKey for each slide + Map _slideKeys = {}; + + late final List _slides; + + bool get isComplete => _status == PdfExportStatus.complete; + + PdfExportController({ + required List slides, + required int initialIndex, + }) : _slides = slides { + _slideKeys = {for (var slide in _slides) slide.key: GlobalKey()}; + _pageController = PageController(initialPage: initialIndex); + } + + bool get inProgress => _status != PdfExportStatus.idle; + + bool get isBuilding => _status == PdfExportStatus.building; + + double get progress => _images.length / _slides.length; + + String get progressText { + if (isBuilding) { + return 'Building PDF...'; + } + return isComplete + ? 'Done' + : 'Exporting ${_images.length} / ${_slides.length}'; + } + + SnapshotQuality get quality => _quality; + + set quality(SnapshotQuality quality) { + if (_quality == quality) return; + _quality = quality; + notifyListeners(); + } + + PdfExportStatus get status => _status; + + Future _wait() async { + await Future.delayed(Durations.short1); + } + + Future start() async { + final currentPage = _pageController.page?.toInt() ?? 0; + _status = PdfExportStatus.capturing; + _pageController.jumpToPage(0); + notifyListeners(); + + for (var i = 0; i < _slides.length; i++) { + await _convertSlide(i); + } + await _wait(); + _status = PdfExportStatus.building; + notifyListeners(); + await _wait(); + final pdf = await _buildPdf(_images); + await _wait(); + _pageController.jumpToPage(currentPage); + + _status = PdfExportStatus.complete; + _images = []; + notifyListeners(); + await _savePdf(pdf); + await _wait(); + _status = PdfExportStatus.idle; + notifyListeners(); + } + + Future _convertSlide(int index) async { + final slide = _slides[index]; + final key = _slideKeys[slide.key]!; + + _pageController.jumpToPage(index); + + // Keep checking until key is attached to the widget + while (!(key.currentContext?.findRenderObject()?.attached ?? false)) { + await _wait(); + } + + final image = await SnapshotService.instance.generateWithKey( + quality: _quality, + key: _slideKeys[slide.key]!, + ); + _images = [..._images, image]; + notifyListeners(); + } + + Future _savePdf(Uint8List pdf) async { + try { + final result = await FileSaver.instance.saveFile( + name: 'superdeck', + bytes: pdf, + ext: 'pdf', + mimeType: MimeType.pdf, + ); + print('Save as result: $result'); + } catch (e) { + print('Error saving pdf: $e'); + } + } + + Future _buildPdf(List images) async { + final pdf = pw.Document(); + + for (final imageData in images) { + // see how logn this takes per page + final image = pw.MemoryImage(imageData); + + final pdfImage = pw.Image( + image, + width: kResolution.width, + height: kResolution.height, + ); + + pdf.addPage( + pw.Page( + pageFormat: PdfPageFormat( + kResolution.width, + kResolution.height, + ), + build: (pw.Context context) { + return pw.Center( + child: pdfImage, + ); + }, + ), + ); + } + + return await pdf.save(); + } + + Widget render() { + return PageView.builder( + controller: _pageController, + itemCount: _slides.length, + itemBuilder: (_, index) { + final slide = _slides[index]; + + return RepaintBoundary( + key: _slideKeys[slide.key], + child: SlidePreview(slide), + ); + }, + ); + } + + @override + void dispose() { + super.dispose(); + _pageController.dispose(); + } +} + +PdfExportController usePdfExportController({ + required List slides, + required int slideIndex, +}) { + return use(_PdfExportControllerHook( + slides: slides, + initialSlideIndex: slideIndex, + )); +} + +class _PdfExportControllerHook extends Hook { + const _PdfExportControllerHook({ + required this.slides, + required this.initialSlideIndex, + }); + + final List slides; + final int initialSlideIndex; + + @override + _PdfExportControllerHookState createState() => + _PdfExportControllerHookState(); +} + +class _PdfExportControllerHookState + extends HookState { + late PdfExportController controller; + + void _attachController({bool update = false}) { + if (update) { + controller.dispose(); + } + controller = PdfExportController( + slides: hook.slides, + initialIndex: hook.initialSlideIndex, + ); + + controller.addListener(() { + setState(() {}); + }); + + if (update) { + setState(() {}); + } + } + + @override + void initHook() { + super.initHook(); + + _attachController(); + } + + @override + void didUpdateHook(_PdfExportControllerHook oldHook) { + super.didUpdateHook(oldHook); + if (oldHook.slides != hook.slides || + oldHook.initialSlideIndex != hook.initialSlideIndex) { + print('update'); + _attachController(update: true); + } + } + + @override + PdfExportController build(BuildContext context) { + return controller; + } + + @override + void dispose() { + controller.dispose(); + } +} diff --git a/packages/superdeck/lib/services/reference_service.dart b/packages/superdeck/lib/services/reference_service.dart new file mode 100644 index 00000000..80d9e1c5 --- /dev/null +++ b/packages/superdeck/lib/services/reference_service.dart @@ -0,0 +1,74 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/services.dart'; +import 'package:path/path.dart' as p; + +import '../helpers/constants.dart'; +import '../helpers/watcher.dart'; +import '../models/reference_model.dart'; + +final _assetDir = Directory(p.join('.superdeck')); +final _slideRef = File(p.join(_assetDir.path, 'slides.json')); +final _generatedDir = Directory(p.join(_assetDir.path, 'generated')); +final _markdown = File('slides.md'); + +class ReferenceService { + final _watcher = FileWatcher(_slideRef); + ReferenceService._(); + + static final instance = ReferenceService._(); + + Future loadString(String path) async { + if (kCanRunProcess) { + return File(path).readAsString(); + } else { + return rootBundle.loadString(path); + } + } + + Future loadMarkdown() async { + return _markdown.readAsString(); + } + + Future loadBytes(String path) async { + if (kCanRunProcess) { + final bytes = await File(path).readAsBytes(); + return ByteData.view(Uint8List.fromList(bytes).buffer); + } else { + return await rootBundle.load(path); + } + } + + Future saveMarkdown(String data) async { + await _markdown.writeAsString(data); + } + + File getAssetFile(String fileName) { + return File(p.join(_generatedDir.path, fileName)); + } + + void listen(VoidCallback callback) { + if (kCanRunProcess) { + if (!_watcher.isWatching) { + _watcher.start(callback); + } + } + } + + Future loadReference() async { + final slidesJson = await loadString(_slideRef.path); + try { + if (kCanRunProcess) { + return compute(SuperDeckReference.fromJson, slidesJson); + } else { + return SuperDeckReference.fromJson(slidesJson); + } + } catch (e) { + log('Error loading deck: $e'); + return const SuperDeckReference.empty(); + } + } +} diff --git a/lib/services/snapshot_service.dart b/packages/superdeck/lib/services/snapshot_service.dart similarity index 71% rename from lib/services/snapshot_service.dart rename to packages/superdeck/lib/services/snapshot_service.dart index ea0bd508..6e8570c8 100644 --- a/lib/services/snapshot_service.dart +++ b/packages/superdeck/lib/services/snapshot_service.dart @@ -2,13 +2,17 @@ import 'dart:async'; import 'dart:developer'; import 'dart:ui' as ui; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import '../components/atoms/slide_view.dart'; import '../helpers/constants.dart'; -import '../helpers/utils.dart'; +import '../providers/assets_provider.dart'; +import '../providers/examples_provider.dart'; +import '../providers/snapshot_provider.dart'; +import '../providers/style_provider.dart'; import '../superdeck.dart'; enum SnapshotQuality { @@ -36,7 +40,7 @@ class SnapshotService { required SnapshotQuality quality, required Slide slide, }) async { - final queueKey = shortHashId(slide.raw + quality.name); + final queueKey = shortHash(slide.key + quality.name); try { while (_generationQueue.length > _maxConcurrentGenerations) { await Future.delayed(const Duration(milliseconds: 100)); @@ -45,8 +49,8 @@ class SnapshotService { _generationQueue.add(queueKey); final image = await _fromWidgetToImage( - SlideView.snapshot(slide), - context: kAppKey.currentContext!, + SnapshotProvider(isCapturing: true, child: SlideView(slide)), + context: kScaffoldKey.currentContext!, pixelRatio: quality.pixelRatio, targetSize: kResolution, ); @@ -66,7 +70,15 @@ class SnapshotService { }) async { final boundary = key.currentContext!.findRenderObject() as RenderRepaintBoundary; - final image = await boundary.toImage(pixelRatio: quality.pixelRatio); + + // Get the size of the boundary + final boundarySize = boundary.size; + // adjust the pixel ratio based on the ideal size which is kResolution + final pixelRatio = kResolution.width / boundarySize.width; + + final image = await boundary.toImage( + pixelRatio: quality.pixelRatio * pixelRatio, + ); return _imageToUint8List(image); } @@ -83,17 +95,23 @@ class SnapshotService { Size? targetSize, }) async { try { - Widget child = widget; - - child = InheritedTheme.captureAll( + final child = InheritedTheme.captureAll( context, - MediaQuery( - data: MediaQuery.of(context), - child: MaterialApp( - debugShowCheckedModeBanner: false, - theme: Theme.of(context), - color: Colors.transparent, - home: Scaffold(body: child), + StyleProvider.inherit( + context: context, + child: AssetsProvider.inherit( + context: context, + child: ExamplesProvider.inherit( + context: context, + child: MediaQuery( + data: MediaQuery.of(context), + child: MaterialApp( + theme: Theme.of(context), + debugShowCheckedModeBanner: false, + home: Scaffold(body: widget), + ), + ), + ), ), ), ); @@ -119,6 +137,10 @@ class SnapshotService { maxWidth: logicalSize.width, maxHeight: logicalSize.height, ), + physicalConstraints: BoxConstraints( + maxWidth: logicalSize.width * pixelRatio, + maxHeight: logicalSize.height * pixelRatio, + ), devicePixelRatio: pixelRatio, ), ); @@ -149,11 +171,14 @@ class SnapshotService { while (retryCount > 0) { isDirty = false; - buildOwner.buildScope(rootElement); - buildOwner.finalizeTree(); - pipelineOwner.flushLayout(); - pipelineOwner.flushCompositingBits(); - pipelineOwner.flushPaint(); + buildOwner + ..buildScope(rootElement) + ..finalizeTree(); + + pipelineOwner + ..flushLayout() + ..flushCompositingBits() + ..flushPaint(); await Future.delayed(const Duration(milliseconds: 250)); diff --git a/packages/superdeck/lib/styles/style_spec.dart b/packages/superdeck/lib/styles/style_spec.dart new file mode 100644 index 00000000..8f2ac914 --- /dev/null +++ b/packages/superdeck/lib/styles/style_spec.dart @@ -0,0 +1,305 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_markdown/flutter_markdown.dart'; +import 'package:mix/mix.dart'; +import 'package:mix_annotations/mix_annotations.dart'; + +part 'style_spec.g.dart'; + +const _mdHeading = MixableFieldDto(type: 'MdHeadingSpecAttribute'); +const _mdParagraph = MixableFieldDto(type: 'MdParagraphSpecAttribute'); +const _mdCode = MixableFieldDto(type: 'MdCodeblockSpecAttribute'); +const _mdList = MixableFieldDto(type: 'MdListSpecAttribute'); +const _mdTable = MixableFieldDto(type: 'MdTableSpecAttribute'); + +const _mdBlockQuote = MixableFieldDto(type: 'MdBlockquoteSpecAttribute'); + +@MixableSpec() +final class MdTextSpec extends Spec with _$MdTextSpec { + final TextStyle? textStyle; + final EdgeInsets? padding; + final WrapAlignment? alignment; + + const MdTextSpec({ + this.textStyle, + this.padding, + this.alignment, + }); +} + +@MixableSpec() +final class MdHeadingSpec extends Spec with _$MdHeadingSpec { + final TextStyle? textStyle; + final EdgeInsets? padding; + final WrapAlignment? align; + + const MdHeadingSpec({ + this.textStyle, + this.padding, + this.align, + }); +} + +@MixableSpec() +final class MdParagraphSpec extends Spec + with _$MdParagraphSpec { + final TextStyle? textStyle; + final EdgeInsets? padding; + + const MdParagraphSpec({ + this.textStyle, + this.padding, + }); +} + +@MixableSpec() +final class MdListSpec extends Spec with _$MdListSpec { + final double? indent; + final TextStyle? bulletStyle; + final EdgeInsets? bulletPadding; + final WrapAlignment? orderedAlignment; + final WrapAlignment? unorderedAlignment; + + const MdListSpec({ + this.indent, + this.bulletStyle, + this.bulletPadding, + this.orderedAlignment, + this.unorderedAlignment, + }); +} + +@MixableSpec() +final class MdTableSpec extends Spec with _$MdTableSpec { + final TextStyle? headStyle; + final TextStyle? bodyStyle; + final TextAlign? headAlignment; + final EdgeInsets? padding; + final TableBorder? border; + final TableColumnWidth? columnWidth; + final EdgeInsets? cellPadding; + + final BoxDecoration? cellDecoration; + final TableCellVerticalAlignment? verticalAlignment; + + const MdTableSpec({ + this.headStyle, + this.bodyStyle, + this.headAlignment, + this.padding, + this.border, + this.columnWidth, + this.cellPadding, + this.cellDecoration, + this.verticalAlignment, + }); +} + +@MixableSpec() +final class MdBlockquoteSpec extends Spec + with _$MdBlockquoteSpec { + final TextStyle? textStyle; + final EdgeInsets? padding; + final BoxDecoration? decoration; + final WrapAlignment? alignment; + + const MdBlockquoteSpec({ + this.textStyle, + this.padding, + this.decoration, + this.alignment, + }); +} + +@MixableSpec() +final class MdCodeblockSpec extends Spec + with _$MdCodeblockSpec { + final TextStyle? textStyle; + final EdgeInsets? padding; + final BoxDecoration? decoration; + final WrapAlignment? alignment; + + const MdCodeblockSpec({ + this.textStyle, + this.padding, + this.decoration, + this.alignment, + }); +} + +@MixableSpec() +final class SlideSpec extends Spec with _$SlideSpec { + final WrapAlignment? textAlign; + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h1; + + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h2; + + final TextStyle? a; + final TextStyle? em; + final TextStyle? strong; + final TextStyle? del; + final TextStyle? img; + @MixableProperty(utilities: [MixableUtility(alias: 'divider')]) + final BoxDecoration? horizontalRuleDecoration; + final TextScaler? textScaleFactor; + + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h3; + + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h4; + + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h5; + + @MixableProperty(dto: _mdHeading) + final MdHeadingSpec? h6; + + @MixableProperty(dto: _mdParagraph) + final MdParagraphSpec? paragraph; + + final TextStyle? link; + final double? blockSpacing; + + @MixableProperty(dto: _mdBlockQuote) + final MdBlockquoteSpec? blockquote; + @MixableProperty(dto: _mdList) + final MdListSpec? list; + @MixableProperty(dto: _mdTable) + final MdTableSpec? table; + @MixableProperty(dto: _mdCode) + final MdCodeblockSpec? code; + final BoxSpec innerContainer; + final BoxSpec outerContainer; + final BoxSpec contentContainer; + final ImageSpec image; + final TextStyle? checkbox; + + static const of = _$SlideSpec.of; + static const from = _$SlideSpec.from; + + const SlideSpec({ + this.h1, + this.h2, + this.h3, + this.h4, + this.h5, + this.h6, + this.paragraph, + this.link, + this.blockSpacing, + this.blockquote, + this.list, + this.table, + this.checkbox, + this.code, + this.a, + this.em, + this.strong, + this.del, + this.textAlign, + this.img, + this.horizontalRuleDecoration, + this.textScaleFactor, + BoxSpec? innerContainer, + BoxSpec? outerContainer, + BoxSpec? contentContainer, + ImageSpec? image, + super.animated, + }) : outerContainer = outerContainer ?? const BoxSpec(), + innerContainer = innerContainer ?? const BoxSpec(), + contentContainer = contentContainer ?? const BoxSpec(), + image = image ?? const ImageSpec(); + + MarkdownStyleSheet toStyle() { + return MarkdownStyleSheet( + a: link, + h1: h1?.textStyle, + h1Padding: h1?.padding, + h1Align: h1?.align ?? WrapAlignment.start, + h2: h2?.textStyle, + h2Padding: h2?.padding, + h2Align: h2?.align ?? WrapAlignment.start, + h3: h3?.textStyle, + h3Padding: h3?.padding, + h3Align: h3?.align ?? WrapAlignment.start, + h4: h4?.textStyle, + h4Padding: h4?.padding, + h4Align: h4?.align ?? WrapAlignment.start, + h5: h5?.textStyle, + h5Padding: h5?.padding, + h5Align: h5?.align ?? WrapAlignment.start, + h6: h6?.textStyle, + h6Padding: h6?.padding, + h6Align: h6?.align ?? WrapAlignment.start, + p: paragraph?.textStyle, + pPadding: paragraph?.padding, + textAlign: textAlign ?? WrapAlignment.start, + blockSpacing: blockSpacing, + blockquote: blockquote?.textStyle, + blockquotePadding: blockquote?.padding, + blockquoteDecoration: blockquote?.decoration, + blockquoteAlign: blockquote?.alignment ?? WrapAlignment.start, + listBullet: list?.bulletStyle, + listBulletPadding: list?.bulletPadding, + listIndent: list?.indent, + orderedListAlign: list?.orderedAlignment ?? WrapAlignment.start, + unorderedListAlign: list?.unorderedAlignment ?? WrapAlignment.start, + tableHead: table?.headStyle, + tableBody: table?.bodyStyle, + tableHeadAlign: table?.headAlignment, + tablePadding: table?.padding, + tableBorder: table?.border, + tableColumnWidth: table?.columnWidth, + tableCellsPadding: table?.cellPadding, + tableCellsDecoration: table?.cellDecoration, + tableVerticalAlignment: + table?.verticalAlignment ?? TableCellVerticalAlignment.middle, + code: code?.textStyle, + codeblockAlign: code?.alignment ?? WrapAlignment.start, + codeblockPadding: code?.padding, + codeblockDecoration: code?.decoration, + checkbox: checkbox, + ); + } +} + +extension on MixData { + Spec specOf>(Spec fallback) { + return resolvableOf() ?? fallback; + } +} + +extension SlideSpecUtilityX on SlideSpecUtility { + TextStyleUtility get textStyle { + return TextStyleUtility( + (value) => only( + paragraph: MdParagraphSpecAttribute(textStyle: value), + h1: MdHeadingSpecAttribute(textStyle: value), + h2: MdHeadingSpecAttribute(textStyle: value), + h3: MdHeadingSpecAttribute(textStyle: value), + h4: MdHeadingSpecAttribute(textStyle: value), + h5: MdHeadingSpecAttribute(textStyle: value), + h6: MdHeadingSpecAttribute(textStyle: value), + list: MdListSpecAttribute(bulletStyle: value), + table: MdTableSpecAttribute(bodyStyle: value, headStyle: value), + code: MdCodeblockSpecAttribute(textStyle: value), + blockquote: MdBlockquoteSpecAttribute(textStyle: value), + ), + ); + } + + MdHeadingSpecUtility get headings { + return MdHeadingSpecUtility( + (value) => only( + h1: value, + h2: value, + h3: value, + h4: value, + h5: value, + h6: value, + ), + ); + } +} diff --git a/packages/superdeck/lib/styles/style_spec.g.dart b/packages/superdeck/lib/styles/style_spec.g.dart new file mode 100644 index 00000000..a340aeb5 --- /dev/null +++ b/packages/superdeck/lib/styles/style_spec.g.dart @@ -0,0 +1,2156 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'style_spec.dart'; + +// ************************************************************************** +// MixableSpecGenerator +// ************************************************************************** + +mixin _$MdTextSpec on Spec { + static MdTextSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdTextSpec(); + } + + /// {@template md_text_spec_of} + /// Retrieves the [MdTextSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdTextSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdTextSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdTextSpec = MdTextSpec.of(context); + /// ``` + /// {@endtemplate} + static MdTextSpec of(BuildContext context) { + return _$MdTextSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdTextSpec] but with the given fields + /// replaced with the new values. + @override + MdTextSpec copyWith({ + TextStyle? textStyle, + EdgeInsets? padding, + WrapAlignment? alignment, + }) { + return MdTextSpec( + textStyle: textStyle ?? _$this.textStyle, + padding: padding ?? _$this.padding, + alignment: alignment ?? _$this.alignment, + ); + } + + /// Linearly interpolates between this [MdTextSpec] and another [MdTextSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdTextSpec] is returned. When [t] is 1.0, the [other] [MdTextSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdTextSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdTextSpec] instance. + /// + /// The interpolation is performed on each property of the [MdTextSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [textStyle]. + /// - [EdgeInsets.lerp] for [padding]. + + /// For [alignment], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdTextSpec] is used. Otherwise, the value + /// from the [other] [MdTextSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdTextSpec] configurations. + @override + MdTextSpec lerp(MdTextSpec? other, double t) { + if (other == null) return _$this; + + return MdTextSpec( + textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + alignment: t < 0.5 ? _$this.alignment : other.alignment, + ); + } + + /// The list of properties that constitute the state of this [MdTextSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdTextSpec] instances for equality. + @override + List get props => [ + _$this.textStyle, + _$this.padding, + _$this.alignment, + ]; + + MdTextSpec get _$this => this as MdTextSpec; +} + +/// Represents the attributes of a [MdTextSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdTextSpec]. +/// +/// Use this class to configure the attributes of a [MdTextSpec] and pass it to +/// the [MdTextSpec] constructor. +final class MdTextSpecAttribute extends SpecAttribute { + final TextStyleDto? textStyle; + final EdgeInsetsDto? padding; + final WrapAlignment? alignment; + + const MdTextSpecAttribute({ + this.textStyle, + this.padding, + this.alignment, + }); + + /// Resolves to [MdTextSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdTextSpec = MdTextSpecAttribute(...).resolve(mix); + /// ``` + @override + MdTextSpec resolve(MixData mix) { + return MdTextSpec( + textStyle: textStyle?.resolve(mix), + padding: padding?.resolve(mix), + alignment: alignment, + ); + } + + /// Merges the properties of this [MdTextSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdTextSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdTextSpecAttribute merge(MdTextSpecAttribute? other) { + if (other == null) return this; + + return MdTextSpecAttribute( + textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, + padding: padding?.merge(other.padding) ?? other.padding, + alignment: other.alignment ?? alignment, + ); + } + + /// The list of properties that constitute the state of this [MdTextSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdTextSpecAttribute] instances for equality. + @override + List get props => [ + textStyle, + padding, + alignment, + ]; +} + +/// Utility class for configuring [MdTextSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdTextSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdTextSpecAttribute]. +class MdTextSpecUtility + extends SpecUtility { + /// Utility for defining [MdTextSpecAttribute.textStyle] + late final textStyle = TextStyleUtility((v) => only(textStyle: v)); + + /// Utility for defining [MdTextSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + /// Utility for defining [MdTextSpecAttribute.alignment] + late final alignment = WrapAlignmentUtility((v) => only(alignment: v)); + + MdTextSpecUtility(super.builder); + + static final self = MdTextSpecUtility((v) => v); + + /// Returns a new [MdTextSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? textStyle, + EdgeInsetsDto? padding, + WrapAlignment? alignment, + }) { + return builder(MdTextSpecAttribute( + textStyle: textStyle, + padding: padding, + alignment: alignment, + )); + } +} + +/// A tween that interpolates between two [MdTextSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdTextSpec] specifications. +class MdTextSpecTween extends Tween { + MdTextSpecTween({ + super.begin, + super.end, + }); + + @override + MdTextSpec lerp(double t) { + if (begin == null && end == null) { + return const MdTextSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdHeadingSpec on Spec { + static MdHeadingSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdHeadingSpec(); + } + + /// {@template md_heading_spec_of} + /// Retrieves the [MdHeadingSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdHeadingSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdHeadingSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdHeadingSpec = MdHeadingSpec.of(context); + /// ``` + /// {@endtemplate} + static MdHeadingSpec of(BuildContext context) { + return _$MdHeadingSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdHeadingSpec] but with the given fields + /// replaced with the new values. + @override + MdHeadingSpec copyWith({ + TextStyle? textStyle, + EdgeInsets? padding, + WrapAlignment? align, + }) { + return MdHeadingSpec( + textStyle: textStyle ?? _$this.textStyle, + padding: padding ?? _$this.padding, + align: align ?? _$this.align, + ); + } + + /// Linearly interpolates between this [MdHeadingSpec] and another [MdHeadingSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdHeadingSpec] is returned. When [t] is 1.0, the [other] [MdHeadingSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdHeadingSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdHeadingSpec] instance. + /// + /// The interpolation is performed on each property of the [MdHeadingSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [textStyle]. + /// - [EdgeInsets.lerp] for [padding]. + + /// For [align], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdHeadingSpec] is used. Otherwise, the value + /// from the [other] [MdHeadingSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdHeadingSpec] configurations. + @override + MdHeadingSpec lerp(MdHeadingSpec? other, double t) { + if (other == null) return _$this; + + return MdHeadingSpec( + textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + align: t < 0.5 ? _$this.align : other.align, + ); + } + + /// The list of properties that constitute the state of this [MdHeadingSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdHeadingSpec] instances for equality. + @override + List get props => [ + _$this.textStyle, + _$this.padding, + _$this.align, + ]; + + MdHeadingSpec get _$this => this as MdHeadingSpec; +} + +/// Represents the attributes of a [MdHeadingSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdHeadingSpec]. +/// +/// Use this class to configure the attributes of a [MdHeadingSpec] and pass it to +/// the [MdHeadingSpec] constructor. +final class MdHeadingSpecAttribute extends SpecAttribute { + final TextStyleDto? textStyle; + final EdgeInsetsDto? padding; + final WrapAlignment? align; + + const MdHeadingSpecAttribute({ + this.textStyle, + this.padding, + this.align, + }); + + /// Resolves to [MdHeadingSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdHeadingSpec = MdHeadingSpecAttribute(...).resolve(mix); + /// ``` + @override + MdHeadingSpec resolve(MixData mix) { + return MdHeadingSpec( + textStyle: textStyle?.resolve(mix), + padding: padding?.resolve(mix), + align: align, + ); + } + + /// Merges the properties of this [MdHeadingSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdHeadingSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdHeadingSpecAttribute merge(MdHeadingSpecAttribute? other) { + if (other == null) return this; + + return MdHeadingSpecAttribute( + textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, + padding: padding?.merge(other.padding) ?? other.padding, + align: other.align ?? align, + ); + } + + /// The list of properties that constitute the state of this [MdHeadingSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdHeadingSpecAttribute] instances for equality. + @override + List get props => [ + textStyle, + padding, + align, + ]; +} + +/// Utility class for configuring [MdHeadingSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdHeadingSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdHeadingSpecAttribute]. +class MdHeadingSpecUtility + extends SpecUtility { + /// Utility for defining [MdHeadingSpecAttribute.textStyle] + late final textStyle = TextStyleUtility((v) => only(textStyle: v)); + + /// Utility for defining [MdHeadingSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + /// Utility for defining [MdHeadingSpecAttribute.align] + late final align = WrapAlignmentUtility((v) => only(align: v)); + + MdHeadingSpecUtility(super.builder); + + static final self = MdHeadingSpecUtility((v) => v); + + /// Returns a new [MdHeadingSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? textStyle, + EdgeInsetsDto? padding, + WrapAlignment? align, + }) { + return builder(MdHeadingSpecAttribute( + textStyle: textStyle, + padding: padding, + align: align, + )); + } +} + +/// A tween that interpolates between two [MdHeadingSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdHeadingSpec] specifications. +class MdHeadingSpecTween extends Tween { + MdHeadingSpecTween({ + super.begin, + super.end, + }); + + @override + MdHeadingSpec lerp(double t) { + if (begin == null && end == null) { + return const MdHeadingSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdParagraphSpec on Spec { + static MdParagraphSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdParagraphSpec(); + } + + /// {@template md_paragraph_spec_of} + /// Retrieves the [MdParagraphSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdParagraphSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdParagraphSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdParagraphSpec = MdParagraphSpec.of(context); + /// ``` + /// {@endtemplate} + static MdParagraphSpec of(BuildContext context) { + return _$MdParagraphSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdParagraphSpec] but with the given fields + /// replaced with the new values. + @override + MdParagraphSpec copyWith({ + TextStyle? textStyle, + EdgeInsets? padding, + }) { + return MdParagraphSpec( + textStyle: textStyle ?? _$this.textStyle, + padding: padding ?? _$this.padding, + ); + } + + /// Linearly interpolates between this [MdParagraphSpec] and another [MdParagraphSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdParagraphSpec] is returned. When [t] is 1.0, the [other] [MdParagraphSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdParagraphSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdParagraphSpec] instance. + /// + /// The interpolation is performed on each property of the [MdParagraphSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [textStyle]. + /// - [EdgeInsets.lerp] for [padding]. + + /// For , the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdParagraphSpec] is used. Otherwise, the value + /// from the [other] [MdParagraphSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdParagraphSpec] configurations. + @override + MdParagraphSpec lerp(MdParagraphSpec? other, double t) { + if (other == null) return _$this; + + return MdParagraphSpec( + textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + ); + } + + /// The list of properties that constitute the state of this [MdParagraphSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdParagraphSpec] instances for equality. + @override + List get props => [ + _$this.textStyle, + _$this.padding, + ]; + + MdParagraphSpec get _$this => this as MdParagraphSpec; +} + +/// Represents the attributes of a [MdParagraphSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdParagraphSpec]. +/// +/// Use this class to configure the attributes of a [MdParagraphSpec] and pass it to +/// the [MdParagraphSpec] constructor. +final class MdParagraphSpecAttribute extends SpecAttribute { + final TextStyleDto? textStyle; + final EdgeInsetsDto? padding; + + const MdParagraphSpecAttribute({ + this.textStyle, + this.padding, + }); + + /// Resolves to [MdParagraphSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdParagraphSpec = MdParagraphSpecAttribute(...).resolve(mix); + /// ``` + @override + MdParagraphSpec resolve(MixData mix) { + return MdParagraphSpec( + textStyle: textStyle?.resolve(mix), + padding: padding?.resolve(mix), + ); + } + + /// Merges the properties of this [MdParagraphSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdParagraphSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdParagraphSpecAttribute merge(MdParagraphSpecAttribute? other) { + if (other == null) return this; + + return MdParagraphSpecAttribute( + textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, + padding: padding?.merge(other.padding) ?? other.padding, + ); + } + + /// The list of properties that constitute the state of this [MdParagraphSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdParagraphSpecAttribute] instances for equality. + @override + List get props => [ + textStyle, + padding, + ]; +} + +/// Utility class for configuring [MdParagraphSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdParagraphSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdParagraphSpecAttribute]. +class MdParagraphSpecUtility + extends SpecUtility { + /// Utility for defining [MdParagraphSpecAttribute.textStyle] + late final textStyle = TextStyleUtility((v) => only(textStyle: v)); + + /// Utility for defining [MdParagraphSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + MdParagraphSpecUtility(super.builder); + + static final self = MdParagraphSpecUtility((v) => v); + + /// Returns a new [MdParagraphSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? textStyle, + EdgeInsetsDto? padding, + }) { + return builder(MdParagraphSpecAttribute( + textStyle: textStyle, + padding: padding, + )); + } +} + +/// A tween that interpolates between two [MdParagraphSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdParagraphSpec] specifications. +class MdParagraphSpecTween extends Tween { + MdParagraphSpecTween({ + super.begin, + super.end, + }); + + @override + MdParagraphSpec lerp(double t) { + if (begin == null && end == null) { + return const MdParagraphSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdListSpec on Spec { + static MdListSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdListSpec(); + } + + /// {@template md_list_spec_of} + /// Retrieves the [MdListSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdListSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdListSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdListSpec = MdListSpec.of(context); + /// ``` + /// {@endtemplate} + static MdListSpec of(BuildContext context) { + return _$MdListSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdListSpec] but with the given fields + /// replaced with the new values. + @override + MdListSpec copyWith({ + double? indent, + TextStyle? bulletStyle, + EdgeInsets? bulletPadding, + WrapAlignment? orderedAlignment, + WrapAlignment? unorderedAlignment, + }) { + return MdListSpec( + indent: indent ?? _$this.indent, + bulletStyle: bulletStyle ?? _$this.bulletStyle, + bulletPadding: bulletPadding ?? _$this.bulletPadding, + orderedAlignment: orderedAlignment ?? _$this.orderedAlignment, + unorderedAlignment: unorderedAlignment ?? _$this.unorderedAlignment, + ); + } + + /// Linearly interpolates between this [MdListSpec] and another [MdListSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdListSpec] is returned. When [t] is 1.0, the [other] [MdListSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdListSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdListSpec] instance. + /// + /// The interpolation is performed on each property of the [MdListSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpDouble] for [indent]. + /// - [MixHelpers.lerpTextStyle] for [bulletStyle]. + /// - [EdgeInsets.lerp] for [bulletPadding]. + + /// For [orderedAlignment] and [unorderedAlignment], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdListSpec] is used. Otherwise, the value + /// from the [other] [MdListSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdListSpec] configurations. + @override + MdListSpec lerp(MdListSpec? other, double t) { + if (other == null) return _$this; + + return MdListSpec( + indent: MixHelpers.lerpDouble(_$this.indent, other.indent, t), + bulletStyle: + MixHelpers.lerpTextStyle(_$this.bulletStyle, other.bulletStyle, t), + bulletPadding: + EdgeInsets.lerp(_$this.bulletPadding, other.bulletPadding, t), + orderedAlignment: + t < 0.5 ? _$this.orderedAlignment : other.orderedAlignment, + unorderedAlignment: + t < 0.5 ? _$this.unorderedAlignment : other.unorderedAlignment, + ); + } + + /// The list of properties that constitute the state of this [MdListSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdListSpec] instances for equality. + @override + List get props => [ + _$this.indent, + _$this.bulletStyle, + _$this.bulletPadding, + _$this.orderedAlignment, + _$this.unorderedAlignment, + ]; + + MdListSpec get _$this => this as MdListSpec; +} + +/// Represents the attributes of a [MdListSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdListSpec]. +/// +/// Use this class to configure the attributes of a [MdListSpec] and pass it to +/// the [MdListSpec] constructor. +final class MdListSpecAttribute extends SpecAttribute { + final double? indent; + final TextStyleDto? bulletStyle; + final EdgeInsetsDto? bulletPadding; + final WrapAlignment? orderedAlignment; + final WrapAlignment? unorderedAlignment; + + const MdListSpecAttribute({ + this.indent, + this.bulletStyle, + this.bulletPadding, + this.orderedAlignment, + this.unorderedAlignment, + }); + + /// Resolves to [MdListSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdListSpec = MdListSpecAttribute(...).resolve(mix); + /// ``` + @override + MdListSpec resolve(MixData mix) { + return MdListSpec( + indent: indent, + bulletStyle: bulletStyle?.resolve(mix), + bulletPadding: bulletPadding?.resolve(mix), + orderedAlignment: orderedAlignment, + unorderedAlignment: unorderedAlignment, + ); + } + + /// Merges the properties of this [MdListSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdListSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdListSpecAttribute merge(MdListSpecAttribute? other) { + if (other == null) return this; + + return MdListSpecAttribute( + indent: other.indent ?? indent, + bulletStyle: bulletStyle?.merge(other.bulletStyle) ?? other.bulletStyle, + bulletPadding: + bulletPadding?.merge(other.bulletPadding) ?? other.bulletPadding, + orderedAlignment: other.orderedAlignment ?? orderedAlignment, + unorderedAlignment: other.unorderedAlignment ?? unorderedAlignment, + ); + } + + /// The list of properties that constitute the state of this [MdListSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdListSpecAttribute] instances for equality. + @override + List get props => [ + indent, + bulletStyle, + bulletPadding, + orderedAlignment, + unorderedAlignment, + ]; +} + +/// Utility class for configuring [MdListSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdListSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdListSpecAttribute]. +class MdListSpecUtility + extends SpecUtility { + /// Utility for defining [MdListSpecAttribute.indent] + late final indent = DoubleUtility((v) => only(indent: v)); + + /// Utility for defining [MdListSpecAttribute.bulletStyle] + late final bulletStyle = TextStyleUtility((v) => only(bulletStyle: v)); + + /// Utility for defining [MdListSpecAttribute.bulletPadding] + late final bulletPadding = EdgeInsetsUtility((v) => only(bulletPadding: v)); + + /// Utility for defining [MdListSpecAttribute.orderedAlignment] + late final orderedAlignment = + WrapAlignmentUtility((v) => only(orderedAlignment: v)); + + /// Utility for defining [MdListSpecAttribute.unorderedAlignment] + late final unorderedAlignment = + WrapAlignmentUtility((v) => only(unorderedAlignment: v)); + + MdListSpecUtility(super.builder); + + static final self = MdListSpecUtility((v) => v); + + /// Returns a new [MdListSpecAttribute] with the specified properties. + @override + T only({ + double? indent, + TextStyleDto? bulletStyle, + EdgeInsetsDto? bulletPadding, + WrapAlignment? orderedAlignment, + WrapAlignment? unorderedAlignment, + }) { + return builder(MdListSpecAttribute( + indent: indent, + bulletStyle: bulletStyle, + bulletPadding: bulletPadding, + orderedAlignment: orderedAlignment, + unorderedAlignment: unorderedAlignment, + )); + } +} + +/// A tween that interpolates between two [MdListSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdListSpec] specifications. +class MdListSpecTween extends Tween { + MdListSpecTween({ + super.begin, + super.end, + }); + + @override + MdListSpec lerp(double t) { + if (begin == null && end == null) { + return const MdListSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdTableSpec on Spec { + static MdTableSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdTableSpec(); + } + + /// {@template md_table_spec_of} + /// Retrieves the [MdTableSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdTableSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdTableSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdTableSpec = MdTableSpec.of(context); + /// ``` + /// {@endtemplate} + static MdTableSpec of(BuildContext context) { + return _$MdTableSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdTableSpec] but with the given fields + /// replaced with the new values. + @override + MdTableSpec copyWith({ + TextStyle? headStyle, + TextStyle? bodyStyle, + TextAlign? headAlignment, + EdgeInsets? padding, + TableBorder? border, + TableColumnWidth? columnWidth, + EdgeInsets? cellPadding, + BoxDecoration? cellDecoration, + TableCellVerticalAlignment? verticalAlignment, + }) { + return MdTableSpec( + headStyle: headStyle ?? _$this.headStyle, + bodyStyle: bodyStyle ?? _$this.bodyStyle, + headAlignment: headAlignment ?? _$this.headAlignment, + padding: padding ?? _$this.padding, + border: border ?? _$this.border, + columnWidth: columnWidth ?? _$this.columnWidth, + cellPadding: cellPadding ?? _$this.cellPadding, + cellDecoration: cellDecoration ?? _$this.cellDecoration, + verticalAlignment: verticalAlignment ?? _$this.verticalAlignment, + ); + } + + /// Linearly interpolates between this [MdTableSpec] and another [MdTableSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdTableSpec] is returned. When [t] is 1.0, the [other] [MdTableSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdTableSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdTableSpec] instance. + /// + /// The interpolation is performed on each property of the [MdTableSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [headStyle] and [bodyStyle]. + /// - [EdgeInsets.lerp] for [padding] and [cellPadding]. + /// - [TableBorder.lerp] for [border]. + /// - [BoxDecoration.lerp] for [cellDecoration]. + + /// For [headAlignment] and [columnWidth] and [verticalAlignment], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdTableSpec] is used. Otherwise, the value + /// from the [other] [MdTableSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdTableSpec] configurations. + @override + MdTableSpec lerp(MdTableSpec? other, double t) { + if (other == null) return _$this; + + return MdTableSpec( + headStyle: MixHelpers.lerpTextStyle(_$this.headStyle, other.headStyle, t), + bodyStyle: MixHelpers.lerpTextStyle(_$this.bodyStyle, other.bodyStyle, t), + headAlignment: t < 0.5 ? _$this.headAlignment : other.headAlignment, + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + border: TableBorder.lerp(_$this.border, other.border, t), + columnWidth: t < 0.5 ? _$this.columnWidth : other.columnWidth, + cellPadding: EdgeInsets.lerp(_$this.cellPadding, other.cellPadding, t), + cellDecoration: + BoxDecoration.lerp(_$this.cellDecoration, other.cellDecoration, t), + verticalAlignment: + t < 0.5 ? _$this.verticalAlignment : other.verticalAlignment, + ); + } + + /// The list of properties that constitute the state of this [MdTableSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdTableSpec] instances for equality. + @override + List get props => [ + _$this.headStyle, + _$this.bodyStyle, + _$this.headAlignment, + _$this.padding, + _$this.border, + _$this.columnWidth, + _$this.cellPadding, + _$this.cellDecoration, + _$this.verticalAlignment, + ]; + + MdTableSpec get _$this => this as MdTableSpec; +} + +/// Represents the attributes of a [MdTableSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdTableSpec]. +/// +/// Use this class to configure the attributes of a [MdTableSpec] and pass it to +/// the [MdTableSpec] constructor. +final class MdTableSpecAttribute extends SpecAttribute { + final TextStyleDto? headStyle; + final TextStyleDto? bodyStyle; + final TextAlign? headAlignment; + final EdgeInsetsDto? padding; + final TableBorder? border; + final TableColumnWidth? columnWidth; + final EdgeInsetsDto? cellPadding; + final BoxDecorationDto? cellDecoration; + final TableCellVerticalAlignment? verticalAlignment; + + const MdTableSpecAttribute({ + this.headStyle, + this.bodyStyle, + this.headAlignment, + this.padding, + this.border, + this.columnWidth, + this.cellPadding, + this.cellDecoration, + this.verticalAlignment, + }); + + /// Resolves to [MdTableSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdTableSpec = MdTableSpecAttribute(...).resolve(mix); + /// ``` + @override + MdTableSpec resolve(MixData mix) { + return MdTableSpec( + headStyle: headStyle?.resolve(mix), + bodyStyle: bodyStyle?.resolve(mix), + headAlignment: headAlignment, + padding: padding?.resolve(mix), + border: border, + columnWidth: columnWidth, + cellPadding: cellPadding?.resolve(mix), + cellDecoration: cellDecoration?.resolve(mix), + verticalAlignment: verticalAlignment, + ); + } + + /// Merges the properties of this [MdTableSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdTableSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdTableSpecAttribute merge(MdTableSpecAttribute? other) { + if (other == null) return this; + + return MdTableSpecAttribute( + headStyle: headStyle?.merge(other.headStyle) ?? other.headStyle, + bodyStyle: bodyStyle?.merge(other.bodyStyle) ?? other.bodyStyle, + headAlignment: other.headAlignment ?? headAlignment, + padding: padding?.merge(other.padding) ?? other.padding, + border: other.border ?? border, + columnWidth: other.columnWidth ?? columnWidth, + cellPadding: cellPadding?.merge(other.cellPadding) ?? other.cellPadding, + cellDecoration: + cellDecoration?.merge(other.cellDecoration) ?? other.cellDecoration, + verticalAlignment: other.verticalAlignment ?? verticalAlignment, + ); + } + + /// The list of properties that constitute the state of this [MdTableSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdTableSpecAttribute] instances for equality. + @override + List get props => [ + headStyle, + bodyStyle, + headAlignment, + padding, + border, + columnWidth, + cellPadding, + cellDecoration, + verticalAlignment, + ]; +} + +/// Utility class for configuring [MdTableSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdTableSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdTableSpecAttribute]. +class MdTableSpecUtility + extends SpecUtility { + /// Utility for defining [MdTableSpecAttribute.headStyle] + late final headStyle = TextStyleUtility((v) => only(headStyle: v)); + + /// Utility for defining [MdTableSpecAttribute.bodyStyle] + late final bodyStyle = TextStyleUtility((v) => only(bodyStyle: v)); + + /// Utility for defining [MdTableSpecAttribute.headAlignment] + late final headAlignment = TextAlignUtility((v) => only(headAlignment: v)); + + /// Utility for defining [MdTableSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + /// Utility for defining [MdTableSpecAttribute.border] + late final border = TableBorderUtility((v) => only(border: v)); + + /// Utility for defining [MdTableSpecAttribute.columnWidth] + late final columnWidth = TableColumnWidthUtility((v) => only(columnWidth: v)); + + /// Utility for defining [MdTableSpecAttribute.cellPadding] + late final cellPadding = EdgeInsetsUtility((v) => only(cellPadding: v)); + + /// Utility for defining [MdTableSpecAttribute.cellDecoration] + late final cellDecoration = + BoxDecorationUtility((v) => only(cellDecoration: v)); + + /// Utility for defining [MdTableSpecAttribute.verticalAlignment] + late final verticalAlignment = + TableCellVerticalAlignmentUtility((v) => only(verticalAlignment: v)); + + MdTableSpecUtility(super.builder); + + static final self = MdTableSpecUtility((v) => v); + + /// Returns a new [MdTableSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? headStyle, + TextStyleDto? bodyStyle, + TextAlign? headAlignment, + EdgeInsetsDto? padding, + TableBorder? border, + TableColumnWidth? columnWidth, + EdgeInsetsDto? cellPadding, + BoxDecorationDto? cellDecoration, + TableCellVerticalAlignment? verticalAlignment, + }) { + return builder(MdTableSpecAttribute( + headStyle: headStyle, + bodyStyle: bodyStyle, + headAlignment: headAlignment, + padding: padding, + border: border, + columnWidth: columnWidth, + cellPadding: cellPadding, + cellDecoration: cellDecoration, + verticalAlignment: verticalAlignment, + )); + } +} + +/// A tween that interpolates between two [MdTableSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdTableSpec] specifications. +class MdTableSpecTween extends Tween { + MdTableSpecTween({ + super.begin, + super.end, + }); + + @override + MdTableSpec lerp(double t) { + if (begin == null && end == null) { + return const MdTableSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdBlockquoteSpec on Spec { + static MdBlockquoteSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdBlockquoteSpec(); + } + + /// {@template md_blockquote_spec_of} + /// Retrieves the [MdBlockquoteSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdBlockquoteSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdBlockquoteSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdBlockquoteSpec = MdBlockquoteSpec.of(context); + /// ``` + /// {@endtemplate} + static MdBlockquoteSpec of(BuildContext context) { + return _$MdBlockquoteSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdBlockquoteSpec] but with the given fields + /// replaced with the new values. + @override + MdBlockquoteSpec copyWith({ + TextStyle? textStyle, + EdgeInsets? padding, + BoxDecoration? decoration, + WrapAlignment? alignment, + }) { + return MdBlockquoteSpec( + textStyle: textStyle ?? _$this.textStyle, + padding: padding ?? _$this.padding, + decoration: decoration ?? _$this.decoration, + alignment: alignment ?? _$this.alignment, + ); + } + + /// Linearly interpolates between this [MdBlockquoteSpec] and another [MdBlockquoteSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdBlockquoteSpec] is returned. When [t] is 1.0, the [other] [MdBlockquoteSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdBlockquoteSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdBlockquoteSpec] instance. + /// + /// The interpolation is performed on each property of the [MdBlockquoteSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [textStyle]. + /// - [EdgeInsets.lerp] for [padding]. + /// - [BoxDecoration.lerp] for [decoration]. + + /// For [alignment], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdBlockquoteSpec] is used. Otherwise, the value + /// from the [other] [MdBlockquoteSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdBlockquoteSpec] configurations. + @override + MdBlockquoteSpec lerp(MdBlockquoteSpec? other, double t) { + if (other == null) return _$this; + + return MdBlockquoteSpec( + textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + decoration: BoxDecoration.lerp(_$this.decoration, other.decoration, t), + alignment: t < 0.5 ? _$this.alignment : other.alignment, + ); + } + + /// The list of properties that constitute the state of this [MdBlockquoteSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdBlockquoteSpec] instances for equality. + @override + List get props => [ + _$this.textStyle, + _$this.padding, + _$this.decoration, + _$this.alignment, + ]; + + MdBlockquoteSpec get _$this => this as MdBlockquoteSpec; +} + +/// Represents the attributes of a [MdBlockquoteSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdBlockquoteSpec]. +/// +/// Use this class to configure the attributes of a [MdBlockquoteSpec] and pass it to +/// the [MdBlockquoteSpec] constructor. +final class MdBlockquoteSpecAttribute extends SpecAttribute { + final TextStyleDto? textStyle; + final EdgeInsetsDto? padding; + final BoxDecorationDto? decoration; + final WrapAlignment? alignment; + + const MdBlockquoteSpecAttribute({ + this.textStyle, + this.padding, + this.decoration, + this.alignment, + }); + + /// Resolves to [MdBlockquoteSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdBlockquoteSpec = MdBlockquoteSpecAttribute(...).resolve(mix); + /// ``` + @override + MdBlockquoteSpec resolve(MixData mix) { + return MdBlockquoteSpec( + textStyle: textStyle?.resolve(mix), + padding: padding?.resolve(mix), + decoration: decoration?.resolve(mix), + alignment: alignment, + ); + } + + /// Merges the properties of this [MdBlockquoteSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdBlockquoteSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdBlockquoteSpecAttribute merge(MdBlockquoteSpecAttribute? other) { + if (other == null) return this; + + return MdBlockquoteSpecAttribute( + textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, + padding: padding?.merge(other.padding) ?? other.padding, + decoration: decoration?.merge(other.decoration) ?? other.decoration, + alignment: other.alignment ?? alignment, + ); + } + + /// The list of properties that constitute the state of this [MdBlockquoteSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdBlockquoteSpecAttribute] instances for equality. + @override + List get props => [ + textStyle, + padding, + decoration, + alignment, + ]; +} + +/// Utility class for configuring [MdBlockquoteSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdBlockquoteSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdBlockquoteSpecAttribute]. +class MdBlockquoteSpecUtility + extends SpecUtility { + /// Utility for defining [MdBlockquoteSpecAttribute.textStyle] + late final textStyle = TextStyleUtility((v) => only(textStyle: v)); + + /// Utility for defining [MdBlockquoteSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + /// Utility for defining [MdBlockquoteSpecAttribute.decoration] + late final decoration = BoxDecorationUtility((v) => only(decoration: v)); + + /// Utility for defining [MdBlockquoteSpecAttribute.alignment] + late final alignment = WrapAlignmentUtility((v) => only(alignment: v)); + + MdBlockquoteSpecUtility(super.builder); + + static final self = MdBlockquoteSpecUtility((v) => v); + + /// Returns a new [MdBlockquoteSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? textStyle, + EdgeInsetsDto? padding, + BoxDecorationDto? decoration, + WrapAlignment? alignment, + }) { + return builder(MdBlockquoteSpecAttribute( + textStyle: textStyle, + padding: padding, + decoration: decoration, + alignment: alignment, + )); + } +} + +/// A tween that interpolates between two [MdBlockquoteSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdBlockquoteSpec] specifications. +class MdBlockquoteSpecTween extends Tween { + MdBlockquoteSpecTween({ + super.begin, + super.end, + }); + + @override + MdBlockquoteSpec lerp(double t) { + if (begin == null && end == null) { + return const MdBlockquoteSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$MdCodeblockSpec on Spec { + static MdCodeblockSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const MdCodeblockSpec(); + } + + /// {@template md_codeblock_spec_of} + /// Retrieves the [MdCodeblockSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [MdCodeblockSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [MdCodeblockSpec]. + /// + /// Example: + /// + /// ```dart + /// final mdCodeblockSpec = MdCodeblockSpec.of(context); + /// ``` + /// {@endtemplate} + static MdCodeblockSpec of(BuildContext context) { + return _$MdCodeblockSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [MdCodeblockSpec] but with the given fields + /// replaced with the new values. + @override + MdCodeblockSpec copyWith({ + TextStyle? textStyle, + EdgeInsets? padding, + BoxDecoration? decoration, + WrapAlignment? alignment, + }) { + return MdCodeblockSpec( + textStyle: textStyle ?? _$this.textStyle, + padding: padding ?? _$this.padding, + decoration: decoration ?? _$this.decoration, + alignment: alignment ?? _$this.alignment, + ); + } + + /// Linearly interpolates between this [MdCodeblockSpec] and another [MdCodeblockSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [MdCodeblockSpec] is returned. When [t] is 1.0, the [other] [MdCodeblockSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [MdCodeblockSpec] is returned. + /// + /// If [other] is null, this method returns the current [MdCodeblockSpec] instance. + /// + /// The interpolation is performed on each property of the [MdCodeblockSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [textStyle]. + /// - [EdgeInsets.lerp] for [padding]. + /// - [BoxDecoration.lerp] for [decoration]. + + /// For [alignment], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [MdCodeblockSpec] is used. Otherwise, the value + /// from the [other] [MdCodeblockSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [MdCodeblockSpec] configurations. + @override + MdCodeblockSpec lerp(MdCodeblockSpec? other, double t) { + if (other == null) return _$this; + + return MdCodeblockSpec( + textStyle: MixHelpers.lerpTextStyle(_$this.textStyle, other.textStyle, t), + padding: EdgeInsets.lerp(_$this.padding, other.padding, t), + decoration: BoxDecoration.lerp(_$this.decoration, other.decoration, t), + alignment: t < 0.5 ? _$this.alignment : other.alignment, + ); + } + + /// The list of properties that constitute the state of this [MdCodeblockSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdCodeblockSpec] instances for equality. + @override + List get props => [ + _$this.textStyle, + _$this.padding, + _$this.decoration, + _$this.alignment, + ]; + + MdCodeblockSpec get _$this => this as MdCodeblockSpec; +} + +/// Represents the attributes of a [MdCodeblockSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [MdCodeblockSpec]. +/// +/// Use this class to configure the attributes of a [MdCodeblockSpec] and pass it to +/// the [MdCodeblockSpec] constructor. +final class MdCodeblockSpecAttribute extends SpecAttribute { + final TextStyleDto? textStyle; + final EdgeInsetsDto? padding; + final BoxDecorationDto? decoration; + final WrapAlignment? alignment; + + const MdCodeblockSpecAttribute({ + this.textStyle, + this.padding, + this.decoration, + this.alignment, + }); + + /// Resolves to [MdCodeblockSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final mdCodeblockSpec = MdCodeblockSpecAttribute(...).resolve(mix); + /// ``` + @override + MdCodeblockSpec resolve(MixData mix) { + return MdCodeblockSpec( + textStyle: textStyle?.resolve(mix), + padding: padding?.resolve(mix), + decoration: decoration?.resolve(mix), + alignment: alignment, + ); + } + + /// Merges the properties of this [MdCodeblockSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [MdCodeblockSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + MdCodeblockSpecAttribute merge(MdCodeblockSpecAttribute? other) { + if (other == null) return this; + + return MdCodeblockSpecAttribute( + textStyle: textStyle?.merge(other.textStyle) ?? other.textStyle, + padding: padding?.merge(other.padding) ?? other.padding, + decoration: decoration?.merge(other.decoration) ?? other.decoration, + alignment: other.alignment ?? alignment, + ); + } + + /// The list of properties that constitute the state of this [MdCodeblockSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [MdCodeblockSpecAttribute] instances for equality. + @override + List get props => [ + textStyle, + padding, + decoration, + alignment, + ]; +} + +/// Utility class for configuring [MdCodeblockSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [MdCodeblockSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [MdCodeblockSpecAttribute]. +class MdCodeblockSpecUtility + extends SpecUtility { + /// Utility for defining [MdCodeblockSpecAttribute.textStyle] + late final textStyle = TextStyleUtility((v) => only(textStyle: v)); + + /// Utility for defining [MdCodeblockSpecAttribute.padding] + late final padding = EdgeInsetsUtility((v) => only(padding: v)); + + /// Utility for defining [MdCodeblockSpecAttribute.decoration] + late final decoration = BoxDecorationUtility((v) => only(decoration: v)); + + /// Utility for defining [MdCodeblockSpecAttribute.alignment] + late final alignment = WrapAlignmentUtility((v) => only(alignment: v)); + + MdCodeblockSpecUtility(super.builder); + + static final self = MdCodeblockSpecUtility((v) => v); + + /// Returns a new [MdCodeblockSpecAttribute] with the specified properties. + @override + T only({ + TextStyleDto? textStyle, + EdgeInsetsDto? padding, + BoxDecorationDto? decoration, + WrapAlignment? alignment, + }) { + return builder(MdCodeblockSpecAttribute( + textStyle: textStyle, + padding: padding, + decoration: decoration, + alignment: alignment, + )); + } +} + +/// A tween that interpolates between two [MdCodeblockSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [MdCodeblockSpec] specifications. +class MdCodeblockSpecTween extends Tween { + MdCodeblockSpecTween({ + super.begin, + super.end, + }); + + @override + MdCodeblockSpec lerp(double t) { + if (begin == null && end == null) { + return const MdCodeblockSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} + +mixin _$SlideSpec on Spec { + static SlideSpec from(MixData mix) { + return mix.attributeOf()?.resolve(mix) ?? + const SlideSpec(); + } + + /// {@template slide_spec_of} + /// Retrieves the [SlideSpec] from the nearest [Mix] ancestor in the widget tree. + /// + /// This method uses [Mix.of] to obtain the [Mix] instance associated with the + /// given [BuildContext], and then retrieves the [SlideSpec] from that [Mix]. + /// If no ancestor [Mix] is found, this method returns an empty [SlideSpec]. + /// + /// Example: + /// + /// ```dart + /// final slideSpec = SlideSpec.of(context); + /// ``` + /// {@endtemplate} + static SlideSpec of(BuildContext context) { + return _$SlideSpec.from(Mix.of(context)); + } + + /// Creates a copy of this [SlideSpec] but with the given fields + /// replaced with the new values. + @override + SlideSpec copyWith({ + MdHeadingSpec? h1, + MdHeadingSpec? h2, + MdHeadingSpec? h3, + MdHeadingSpec? h4, + MdHeadingSpec? h5, + MdHeadingSpec? h6, + MdParagraphSpec? paragraph, + TextStyle? link, + double? blockSpacing, + MdBlockquoteSpec? blockquote, + MdListSpec? list, + MdTableSpec? table, + TextStyle? checkbox, + MdCodeblockSpec? code, + TextStyle? a, + TextStyle? em, + TextStyle? strong, + TextStyle? del, + WrapAlignment? textAlign, + TextStyle? img, + BoxDecoration? horizontalRuleDecoration, + TextScaler? textScaleFactor, + BoxSpec? innerContainer, + BoxSpec? outerContainer, + BoxSpec? contentContainer, + ImageSpec? image, + AnimatedData? animated, + }) { + return SlideSpec( + h1: h1 ?? _$this.h1, + h2: h2 ?? _$this.h2, + h3: h3 ?? _$this.h3, + h4: h4 ?? _$this.h4, + h5: h5 ?? _$this.h5, + h6: h6 ?? _$this.h6, + paragraph: paragraph ?? _$this.paragraph, + link: link ?? _$this.link, + blockSpacing: blockSpacing ?? _$this.blockSpacing, + blockquote: blockquote ?? _$this.blockquote, + list: list ?? _$this.list, + table: table ?? _$this.table, + checkbox: checkbox ?? _$this.checkbox, + code: code ?? _$this.code, + a: a ?? _$this.a, + em: em ?? _$this.em, + strong: strong ?? _$this.strong, + del: del ?? _$this.del, + textAlign: textAlign ?? _$this.textAlign, + img: img ?? _$this.img, + horizontalRuleDecoration: + horizontalRuleDecoration ?? _$this.horizontalRuleDecoration, + textScaleFactor: textScaleFactor ?? _$this.textScaleFactor, + innerContainer: innerContainer ?? _$this.innerContainer, + outerContainer: outerContainer ?? _$this.outerContainer, + contentContainer: contentContainer ?? _$this.contentContainer, + image: image ?? _$this.image, + animated: animated ?? _$this.animated, + ); + } + + /// Linearly interpolates between this [SlideSpec] and another [SlideSpec] based on the given parameter [t]. + /// + /// The parameter [t] represents the interpolation factor, typically ranging from 0.0 to 1.0. + /// When [t] is 0.0, the current [SlideSpec] is returned. When [t] is 1.0, the [other] [SlideSpec] is returned. + /// For values of [t] between 0.0 and 1.0, an interpolated [SlideSpec] is returned. + /// + /// If [other] is null, this method returns the current [SlideSpec] instance. + /// + /// The interpolation is performed on each property of the [SlideSpec] using the appropriate + /// interpolation method: + /// + /// - [MixHelpers.lerpTextStyle] for [link] and [checkbox] and [a] and [em] and [strong] and [del] and [img]. + /// - [MixHelpers.lerpDouble] for [blockSpacing]. + /// - [BoxDecoration.lerp] for [horizontalRuleDecoration]. + /// - [BoxSpec.lerp] for [innerContainer] and [outerContainer] and [contentContainer]. + /// - [ImageSpec.lerp] for [image]. + + /// For [h1] and [h2] and [h3] and [h4] and [h5] and [h6] and [paragraph] and [blockquote] and [list] and [table] and [code] and [textAlign] and [textScaleFactor] and [animated], the interpolation is performed using a step function. + /// If [t] is less than 0.5, the value from the current [SlideSpec] is used. Otherwise, the value + /// from the [other] [SlideSpec] is used. + /// + /// This method is typically used in animations to smoothly transition between + /// different [SlideSpec] configurations. + @override + SlideSpec lerp(SlideSpec? other, double t) { + if (other == null) return _$this; + + return SlideSpec( + h1: _$this.h1?.lerp(other.h1, t) ?? other.h1, + h2: _$this.h2?.lerp(other.h2, t) ?? other.h2, + h3: _$this.h3?.lerp(other.h3, t) ?? other.h3, + h4: _$this.h4?.lerp(other.h4, t) ?? other.h4, + h5: _$this.h5?.lerp(other.h5, t) ?? other.h5, + h6: _$this.h6?.lerp(other.h6, t) ?? other.h6, + paragraph: _$this.paragraph?.lerp(other.paragraph, t) ?? other.paragraph, + link: MixHelpers.lerpTextStyle(_$this.link, other.link, t), + blockSpacing: + MixHelpers.lerpDouble(_$this.blockSpacing, other.blockSpacing, t), + blockquote: + _$this.blockquote?.lerp(other.blockquote, t) ?? other.blockquote, + list: _$this.list?.lerp(other.list, t) ?? other.list, + table: _$this.table?.lerp(other.table, t) ?? other.table, + checkbox: MixHelpers.lerpTextStyle(_$this.checkbox, other.checkbox, t), + code: _$this.code?.lerp(other.code, t) ?? other.code, + a: MixHelpers.lerpTextStyle(_$this.a, other.a, t), + em: MixHelpers.lerpTextStyle(_$this.em, other.em, t), + strong: MixHelpers.lerpTextStyle(_$this.strong, other.strong, t), + del: MixHelpers.lerpTextStyle(_$this.del, other.del, t), + textAlign: t < 0.5 ? _$this.textAlign : other.textAlign, + img: MixHelpers.lerpTextStyle(_$this.img, other.img, t), + horizontalRuleDecoration: BoxDecoration.lerp( + _$this.horizontalRuleDecoration, other.horizontalRuleDecoration, t), + textScaleFactor: t < 0.5 ? _$this.textScaleFactor : other.textScaleFactor, + innerContainer: _$this.innerContainer.lerp(other.innerContainer, t), + outerContainer: _$this.outerContainer.lerp(other.outerContainer, t), + contentContainer: _$this.contentContainer.lerp(other.contentContainer, t), + image: _$this.image.lerp(other.image, t), + animated: t < 0.5 ? _$this.animated : other.animated, + ); + } + + /// The list of properties that constitute the state of this [SlideSpec]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [SlideSpec] instances for equality. + @override + List get props => [ + _$this.h1, + _$this.h2, + _$this.h3, + _$this.h4, + _$this.h5, + _$this.h6, + _$this.paragraph, + _$this.link, + _$this.blockSpacing, + _$this.blockquote, + _$this.list, + _$this.table, + _$this.checkbox, + _$this.code, + _$this.a, + _$this.em, + _$this.strong, + _$this.del, + _$this.textAlign, + _$this.img, + _$this.horizontalRuleDecoration, + _$this.textScaleFactor, + _$this.innerContainer, + _$this.outerContainer, + _$this.contentContainer, + _$this.image, + _$this.animated, + ]; + + SlideSpec get _$this => this as SlideSpec; +} + +/// Represents the attributes of a [SlideSpec]. +/// +/// This class encapsulates properties defining the layout and +/// appearance of a [SlideSpec]. +/// +/// Use this class to configure the attributes of a [SlideSpec] and pass it to +/// the [SlideSpec] constructor. +final class SlideSpecAttribute extends SpecAttribute { + final MdHeadingSpecAttribute? h1; + final MdHeadingSpecAttribute? h2; + final MdHeadingSpecAttribute? h3; + final MdHeadingSpecAttribute? h4; + final MdHeadingSpecAttribute? h5; + final MdHeadingSpecAttribute? h6; + final MdParagraphSpecAttribute? paragraph; + final TextStyleDto? link; + final double? blockSpacing; + final MdBlockquoteSpecAttribute? blockquote; + final MdListSpecAttribute? list; + final MdTableSpecAttribute? table; + final TextStyleDto? checkbox; + final MdCodeblockSpecAttribute? code; + final TextStyleDto? a; + final TextStyleDto? em; + final TextStyleDto? strong; + final TextStyleDto? del; + final WrapAlignment? textAlign; + final TextStyleDto? img; + final BoxDecorationDto? horizontalRuleDecoration; + final TextScaler? textScaleFactor; + final BoxSpecAttribute? innerContainer; + final BoxSpecAttribute? outerContainer; + final BoxSpecAttribute? contentContainer; + final ImageSpecAttribute? image; + + const SlideSpecAttribute({ + this.h1, + this.h2, + this.h3, + this.h4, + this.h5, + this.h6, + this.paragraph, + this.link, + this.blockSpacing, + this.blockquote, + this.list, + this.table, + this.checkbox, + this.code, + this.a, + this.em, + this.strong, + this.del, + this.textAlign, + this.img, + this.horizontalRuleDecoration, + this.textScaleFactor, + this.innerContainer, + this.outerContainer, + this.contentContainer, + this.image, + super.animated, + }); + + /// Resolves to [SlideSpec] using the provided [MixData]. + /// + /// If a property is null in the [MixData], it falls back to the + /// default value defined in the `defaultValue` for that property. + /// + /// ```dart + /// final slideSpec = SlideSpecAttribute(...).resolve(mix); + /// ``` + @override + SlideSpec resolve(MixData mix) { + return SlideSpec( + h1: h1?.resolve(mix), + h2: h2?.resolve(mix), + h3: h3?.resolve(mix), + h4: h4?.resolve(mix), + h5: h5?.resolve(mix), + h6: h6?.resolve(mix), + paragraph: paragraph?.resolve(mix), + link: link?.resolve(mix), + blockSpacing: blockSpacing, + blockquote: blockquote?.resolve(mix), + list: list?.resolve(mix), + table: table?.resolve(mix), + checkbox: checkbox?.resolve(mix), + code: code?.resolve(mix), + a: a?.resolve(mix), + em: em?.resolve(mix), + strong: strong?.resolve(mix), + del: del?.resolve(mix), + textAlign: textAlign, + img: img?.resolve(mix), + horizontalRuleDecoration: horizontalRuleDecoration?.resolve(mix), + textScaleFactor: textScaleFactor, + innerContainer: innerContainer?.resolve(mix), + outerContainer: outerContainer?.resolve(mix), + contentContainer: contentContainer?.resolve(mix), + image: image?.resolve(mix), + animated: animated?.resolve(mix) ?? mix.animation, + ); + } + + /// Merges the properties of this [SlideSpecAttribute] with the properties of [other]. + /// + /// If [other] is null, returns this instance unchanged. Otherwise, returns a new + /// [SlideSpecAttribute] with the properties of [other] taking precedence over + /// the corresponding properties of this instance. + /// + /// Properties from [other] that are null will fall back + /// to the values from this instance. + @override + SlideSpecAttribute merge(SlideSpecAttribute? other) { + if (other == null) return this; + + return SlideSpecAttribute( + h1: h1?.merge(other.h1) ?? other.h1, + h2: h2?.merge(other.h2) ?? other.h2, + h3: h3?.merge(other.h3) ?? other.h3, + h4: h4?.merge(other.h4) ?? other.h4, + h5: h5?.merge(other.h5) ?? other.h5, + h6: h6?.merge(other.h6) ?? other.h6, + paragraph: paragraph?.merge(other.paragraph) ?? other.paragraph, + link: link?.merge(other.link) ?? other.link, + blockSpacing: other.blockSpacing ?? blockSpacing, + blockquote: blockquote?.merge(other.blockquote) ?? other.blockquote, + list: list?.merge(other.list) ?? other.list, + table: table?.merge(other.table) ?? other.table, + checkbox: checkbox?.merge(other.checkbox) ?? other.checkbox, + code: code?.merge(other.code) ?? other.code, + a: a?.merge(other.a) ?? other.a, + em: em?.merge(other.em) ?? other.em, + strong: strong?.merge(other.strong) ?? other.strong, + del: del?.merge(other.del) ?? other.del, + textAlign: other.textAlign ?? textAlign, + img: img?.merge(other.img) ?? other.img, + horizontalRuleDecoration: + horizontalRuleDecoration?.merge(other.horizontalRuleDecoration) ?? + other.horizontalRuleDecoration, + textScaleFactor: other.textScaleFactor ?? textScaleFactor, + innerContainer: + innerContainer?.merge(other.innerContainer) ?? other.innerContainer, + outerContainer: + outerContainer?.merge(other.outerContainer) ?? other.outerContainer, + contentContainer: contentContainer?.merge(other.contentContainer) ?? + other.contentContainer, + image: image?.merge(other.image) ?? other.image, + animated: animated?.merge(other.animated) ?? other.animated, + ); + } + + /// The list of properties that constitute the state of this [SlideSpecAttribute]. + /// + /// This property is used by the [==] operator and the [hashCode] getter to + /// compare two [SlideSpecAttribute] instances for equality. + @override + List get props => [ + h1, + h2, + h3, + h4, + h5, + h6, + paragraph, + link, + blockSpacing, + blockquote, + list, + table, + checkbox, + code, + a, + em, + strong, + del, + textAlign, + img, + horizontalRuleDecoration, + textScaleFactor, + innerContainer, + outerContainer, + contentContainer, + image, + animated, + ]; +} + +/// Utility class for configuring [SlideSpecAttribute] properties. +/// +/// This class provides methods to set individual properties of a [SlideSpecAttribute]. +/// Use the methods of this class to configure specific properties of a [SlideSpecAttribute]. +class SlideSpecUtility + extends SpecUtility { + /// Utility for defining [SlideSpecAttribute.h1] + late final h1 = MdHeadingSpecUtility((v) => only(h1: v)); + + /// Utility for defining [SlideSpecAttribute.h2] + late final h2 = MdHeadingSpecUtility((v) => only(h2: v)); + + /// Utility for defining [SlideSpecAttribute.h3] + late final h3 = MdHeadingSpecUtility((v) => only(h3: v)); + + /// Utility for defining [SlideSpecAttribute.h4] + late final h4 = MdHeadingSpecUtility((v) => only(h4: v)); + + /// Utility for defining [SlideSpecAttribute.h5] + late final h5 = MdHeadingSpecUtility((v) => only(h5: v)); + + /// Utility for defining [SlideSpecAttribute.h6] + late final h6 = MdHeadingSpecUtility((v) => only(h6: v)); + + /// Utility for defining [SlideSpecAttribute.paragraph] + late final paragraph = MdParagraphSpecUtility((v) => only(paragraph: v)); + + /// Utility for defining [SlideSpecAttribute.link] + late final link = TextStyleUtility((v) => only(link: v)); + + /// Utility for defining [SlideSpecAttribute.blockSpacing] + late final blockSpacing = DoubleUtility((v) => only(blockSpacing: v)); + + /// Utility for defining [SlideSpecAttribute.blockquote] + late final blockquote = MdBlockquoteSpecUtility((v) => only(blockquote: v)); + + /// Utility for defining [SlideSpecAttribute.list] + late final list = MdListSpecUtility((v) => only(list: v)); + + /// Utility for defining [SlideSpecAttribute.table] + late final table = MdTableSpecUtility((v) => only(table: v)); + + /// Utility for defining [SlideSpecAttribute.checkbox] + late final checkbox = TextStyleUtility((v) => only(checkbox: v)); + + /// Utility for defining [SlideSpecAttribute.code] + late final code = MdCodeblockSpecUtility((v) => only(code: v)); + + /// Utility for defining [SlideSpecAttribute.a] + late final a = TextStyleUtility((v) => only(a: v)); + + /// Utility for defining [SlideSpecAttribute.em] + late final em = TextStyleUtility((v) => only(em: v)); + + /// Utility for defining [SlideSpecAttribute.strong] + late final strong = TextStyleUtility((v) => only(strong: v)); + + /// Utility for defining [SlideSpecAttribute.del] + late final del = TextStyleUtility((v) => only(del: v)); + + /// Utility for defining [SlideSpecAttribute.textAlign] + late final textAlign = WrapAlignmentUtility((v) => only(textAlign: v)); + + /// Utility for defining [SlideSpecAttribute.img] + late final img = TextStyleUtility((v) => only(img: v)); + + /// Utility for defining [SlideSpecAttribute.horizontalRuleDecoration] + late final divider = + BoxDecorationUtility((v) => only(horizontalRuleDecoration: v)); + + /// Utility for defining [SlideSpecAttribute.textScaleFactor] + late final textScaleFactor = + TextScalerUtility((v) => only(textScaleFactor: v)); + + /// Utility for defining [SlideSpecAttribute.innerContainer] + late final innerContainer = BoxSpecUtility((v) => only(innerContainer: v)); + + /// Utility for defining [SlideSpecAttribute.outerContainer] + late final outerContainer = BoxSpecUtility((v) => only(outerContainer: v)); + + /// Utility for defining [SlideSpecAttribute.contentContainer] + late final contentContainer = + BoxSpecUtility((v) => only(contentContainer: v)); + + /// Utility for defining [SlideSpecAttribute.image] + late final image = ImageSpecUtility((v) => only(image: v)); + + /// Utility for defining [SlideSpecAttribute.animated] + late final animated = AnimatedUtility((v) => only(animated: v)); + + SlideSpecUtility(super.builder); + + static final self = SlideSpecUtility((v) => v); + + /// Returns a new [SlideSpecAttribute] with the specified properties. + @override + T only({ + MdHeadingSpecAttribute? h1, + MdHeadingSpecAttribute? h2, + MdHeadingSpecAttribute? h3, + MdHeadingSpecAttribute? h4, + MdHeadingSpecAttribute? h5, + MdHeadingSpecAttribute? h6, + MdParagraphSpecAttribute? paragraph, + TextStyleDto? link, + double? blockSpacing, + MdBlockquoteSpecAttribute? blockquote, + MdListSpecAttribute? list, + MdTableSpecAttribute? table, + TextStyleDto? checkbox, + MdCodeblockSpecAttribute? code, + TextStyleDto? a, + TextStyleDto? em, + TextStyleDto? strong, + TextStyleDto? del, + WrapAlignment? textAlign, + TextStyleDto? img, + BoxDecorationDto? horizontalRuleDecoration, + TextScaler? textScaleFactor, + BoxSpecAttribute? innerContainer, + BoxSpecAttribute? outerContainer, + BoxSpecAttribute? contentContainer, + ImageSpecAttribute? image, + AnimatedDataDto? animated, + }) { + return builder(SlideSpecAttribute( + h1: h1, + h2: h2, + h3: h3, + h4: h4, + h5: h5, + h6: h6, + paragraph: paragraph, + link: link, + blockSpacing: blockSpacing, + blockquote: blockquote, + list: list, + table: table, + checkbox: checkbox, + code: code, + a: a, + em: em, + strong: strong, + del: del, + textAlign: textAlign, + img: img, + horizontalRuleDecoration: horizontalRuleDecoration, + textScaleFactor: textScaleFactor, + innerContainer: innerContainer, + outerContainer: outerContainer, + contentContainer: contentContainer, + image: image, + animated: animated, + )); + } +} + +/// A tween that interpolates between two [SlideSpec] instances. +/// +/// This class can be used in animations to smoothly transition between +/// different [SlideSpec] specifications. +class SlideSpecTween extends Tween { + SlideSpecTween({ + super.begin, + super.end, + }); + + @override + SlideSpec lerp(double t) { + if (begin == null && end == null) { + return const SlideSpec(); + } + + if (begin == null) { + return end!; + } + + return begin!.lerp(end!, t); + } +} diff --git a/lib/styles/style_util.dart b/packages/superdeck/lib/styles/style_util.dart similarity index 80% rename from lib/styles/style_util.dart rename to packages/superdeck/lib/styles/style_util.dart index 1510eefe..6a258b67 100644 --- a/lib/styles/style_util.dart +++ b/packages/superdeck/lib/styles/style_util.dart @@ -33,6 +33,7 @@ final _outerContainer = _util.outerContainer; final _contentContainer = _util.contentContainer; final _blockSpacing = _util.blockSpacing; final _textStyle = _util.textStyle; +final _checkbox = _util.checkbox; Style get defaultStyle => Style.create([ _outerContainer.color.black(), @@ -56,31 +57,27 @@ Style get defaultStyle => Style.create([ _paragraph.textStyle.as(baseTextStyle), _paragraph.padding.bottom(12), _link.color(const Color.fromARGB(255, 66, 82, 96)), - _list.textStyle.as(baseTextStyle), - _list.itemTextStyle.as(baseTextStyle), - _list.itemMarkerTextStyle.as(baseTextStyle), - _list.itemMarkerTrailingSpace(12), - _list.itemMinIndent(12), - _table.textStyle.as(baseTextStyle), - _table.headTextStyle - .as(baseTextStyle.copyWith(fontWeight: FontWeight.bold)), - _table.bodyTextStyle.as(baseTextStyle), + _list.bulletStyle.as(baseTextStyle), + _checkbox.as(baseTextStyle), + + _table.headStyle.as(baseTextStyle.copyWith(fontWeight: FontWeight.bold)), + _table.bodyStyle.as(baseTextStyle), _blockSpacing(20), _table.cellPadding.all(12), _table.border.all(color: Colors.grey, width: 2), - _table.rowDecoration.color(Colors.grey.withOpacity(0.1)), - _code.span.as(monoTextStyle), + _table.cellDecoration.color(Colors.grey.withOpacity(0.1)), + _code.textStyle.as(monoTextStyle), _code.padding.all(24), _code.decoration.color(const Color.fromARGB(255, 23, 23, 23)), _code.decoration.borderRadius.circular(10), _blockquote.textStyle.as(serifTextStyle), _blockquote.textStyle.fontSize(32), _blockquote.padding.bottom(12), - _blockquote.contentPadding.left(30), + _blockquote.padding.left(30), _blockquote.decoration.border.left.color(Colors.grey), _blockquote.decoration.border.left.width(4), - _divider.height(1), + _divider.border.width(2), _divider.color(Colors.grey), - _divider.thickness(2), + _image.fit.cover(), ]); diff --git a/lib/superdeck.dart b/packages/superdeck/lib/superdeck.dart similarity index 100% rename from lib/superdeck.dart rename to packages/superdeck/lib/superdeck.dart diff --git a/packages/superdeck/lib/templates/image_template.dart b/packages/superdeck/lib/templates/image_template.dart new file mode 100644 index 00000000..39441020 --- /dev/null +++ b/packages/superdeck/lib/templates/image_template.dart @@ -0,0 +1,52 @@ +part of 'templates.dart'; + +class ImageTemplate extends SplitTemplateBuilder { + const ImageTemplate( + super.model, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final spec = SlideSpec.of(context); + final src = config.options.src; + final boxFit = config.options.fit?.toBoxFit() ?? spec.image.fit; + + // THis slide breaks in half and I want to calculate the size based on if its in top or bottom + // or left or right. Also there is a property called flex which is how much of the slide it takes + // so I can use that to calculate the size of the canvas + final firstHalf = config.contentOptions?.flex ?? defaultFlex; + final imageHalf = config.options.flex; + +// available size const width = 1280.0; +//const height = 720.0; + + double width; + double height; + const availableSize = kResolution; + if (config.options.position.isHorizontal()) { + width = availableSize.width * firstHalf / (firstHalf + imageHalf); + height = availableSize.height; + } else { + width = availableSize.width; + height = availableSize.height * firstHalf / (firstHalf + imageHalf); + } + + final side = Container( + height: spec.image.height ?? height, + width: spec.image.width ?? width, + alignment: spec.image.alignment, + decoration: BoxDecoration( + image: DecorationImage( + image: getImageProvider(src, targetSize: Size(width, height)), + centerSlice: spec.image.centerSlice, + repeat: spec.image.repeat ?? ImageRepeat.noRepeat, + filterQuality: spec.image.filterQuality ?? FilterQuality.low, + fit: boxFit, + ), + ), + ); + + return buildSplitSlide(side); + } +} diff --git a/packages/superdeck/lib/templates/invalid_template.dart b/packages/superdeck/lib/templates/invalid_template.dart new file mode 100644 index 00000000..4b5b1267 --- /dev/null +++ b/packages/superdeck/lib/templates/invalid_template.dart @@ -0,0 +1,40 @@ +part of 'templates.dart'; + +class InvalidTemplate extends TemplateBuilder { + const InvalidTemplate( + super.config, { + super.key, + }); + + @override + Widget build(BuildContext context) { + const red = Color.fromARGB(255, 166, 6, 6); + + return SpecBuilder( + style: _invalidStyle, + builder: (context) { + // Maybe there are no validation errors just return the content + return Container( + decoration: BoxDecoration( + color: red, + border: Border.all(color: red, width: 20), + ), + child: buildContent(), + ); + }); + } +} + +final _ = SlideSpecUtility.self; + +final _invalidStyle = Style( + _.paragraph.textStyle.color(Colors.white), + _.h1.textStyle.color(const Color.fromARGB(255, 71, 1, 1)), + _.h1.textStyle.fontSize(36.0), + _.h1.textStyle.bold(), + _.h2.padding.top(0), + _.h2.textStyle.bold(), + _.h2.textStyle.color.yellow(), + _.code.textStyle.color.yellow(), + _.code.textStyle.backgroundColor(const Color.fromARGB(255, 84, 6, 6)), +); diff --git a/packages/superdeck/lib/templates/simple_template.dart b/packages/superdeck/lib/templates/simple_template.dart new file mode 100644 index 00000000..1c144da8 --- /dev/null +++ b/packages/superdeck/lib/templates/simple_template.dart @@ -0,0 +1,11 @@ +part of 'templates.dart'; + +class SimpleTemplate extends TemplateBuilder { + const SimpleTemplate( + super.config, { + super.key, + }); + + @override + Widget build(BuildContext context) => buildContent(); +} diff --git a/packages/superdeck/lib/templates/template_builder.dart b/packages/superdeck/lib/templates/template_builder.dart new file mode 100644 index 00000000..e4f60148 --- /dev/null +++ b/packages/superdeck/lib/templates/template_builder.dart @@ -0,0 +1,79 @@ +part of 'templates.dart'; + +sealed class TemplateBuilder extends StatelessWidget { + @visibleForTesting + final T config; + + int get defaultFlex => 1; + + const TemplateBuilder(this.config, {super.key}); + + static TemplateBuilder buildTemplate(T config) { + return switch (config) { + (SimpleSlide c) => SimpleTemplate(c), + (WidgetSlide c) => WidgetTemplate(c), + (ImageSlide c) => ImageTemplate(c), + (TwoColumnSlide c) => TwoColumnTemplate(c), + (TwoColumnHeaderSlide c) => TwoColumnHeaderTemplate(c), + (InvalidSlide c) => InvalidTemplate(c), + _ => throw UnimplementedError( + 'Slide config not implemented ${config.runtimeType}'), + } as TemplateBuilder; + } + + Widget buildContent() { + return _buildContent( + config.content, + config.contentOptions, + ); + } + + @protected + Widget _buildContent(String content, ContentOptions? options) { + return SlideContent( + content: content, + options: options, + ); + } + + Widget buildContentSection(SectionData section) { + return Expanded( + flex: section.options?.flex ?? defaultFlex, + child: _buildContent(section.content, section.options), + ); + } +} + +abstract class SplitTemplateBuilder + extends TemplateBuilder { + const SplitTemplateBuilder( + super.config, { + super.key, + }); + + Widget buildSplitSlide(Widget side) { + final position = config.options.position; + final flex = config.options.flex; + + List children = [ + buildContentSection(( + content: config.content, + options: config.contentOptions ?? const ContentOptions(), + )), + Expanded(flex: flex, child: side), + ]; + + if (position == LayoutPosition.left || position == LayoutPosition.top) { + children = children.reversed.toList(); + } + + final isVertical = + position == LayoutPosition.top || position == LayoutPosition.bottom; + + if (isVertical) { + return Column(children: children); + } else { + return Row(children: children); + } + } +} diff --git a/packages/superdeck/lib/templates/templates.dart b/packages/superdeck/lib/templates/templates.dart new file mode 100644 index 00000000..12be6bf8 --- /dev/null +++ b/packages/superdeck/lib/templates/templates.dart @@ -0,0 +1,15 @@ +import 'package:flutter/material.dart'; + +import '../components/atoms/cache_image_widget.dart'; +import '../components/molecules/code_preview.dart'; +import '../components/molecules/slide_content.dart'; +import '../helpers/constants.dart'; +import '../providers/examples_provider.dart'; +import '../superdeck.dart'; + +part 'image_template.dart'; +part 'invalid_template.dart'; +part 'simple_template.dart'; +part 'template_builder.dart'; +part 'two_column_template.dart'; +part 'widget_example_template.dart'; diff --git a/packages/superdeck/lib/templates/two_column_template.dart b/packages/superdeck/lib/templates/two_column_template.dart new file mode 100644 index 00000000..7759c207 --- /dev/null +++ b/packages/superdeck/lib/templates/two_column_template.dart @@ -0,0 +1,76 @@ +part of 'templates.dart'; + +class TwoColumnTemplate extends TemplateBuilder { + const TwoColumnTemplate( + super.config, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final options = config.contentOptions ?? const ContentOptions(); + final alignment = options.alignment; + + return Container( + alignment: alignment.toAlignment(), + child: Row( + children: [ + buildContentSection(config.left), + buildContentSection(config.right), + ], + ), + ); + } +} + +class TwoColumnHeaderTemplate extends TemplateBuilder { + const TwoColumnHeaderTemplate( + super.config, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final options = config.contentOptions ?? const ContentOptions(); + final alignment = options.alignment; + final flex = options.flex ?? defaultFlex; + + final header = config.header; + + final left = config.left; + final right = config.right; + return Column( + children: [ + Expanded( + flex: header.options?.flex ?? defaultFlex, + child: Row( + children: [ + buildContentSection(( + content: header.content, + options: options.merge(header.options), + )), + ], + ), + ), + Expanded( + flex: flex, + child: Container( + alignment: alignment.toAlignment(), + child: Row( + children: [ + buildContentSection(( + content: left.content, + options: options.merge(left.options), + )), + buildContentSection(( + content: right.content, + options: options.merge(right.options), + )), + ], + ), + ), + ), + ], + ); + } +} diff --git a/packages/superdeck/lib/templates/widget_example_template.dart b/packages/superdeck/lib/templates/widget_example_template.dart new file mode 100644 index 00000000..7f2e3169 --- /dev/null +++ b/packages/superdeck/lib/templates/widget_example_template.dart @@ -0,0 +1,26 @@ +part of 'templates.dart'; + +class WidgetTemplate extends SplitTemplateBuilder { + const WidgetTemplate( + super.model, { + super.key, + }); + + @override + Widget build(BuildContext context) { + final options = config.options; + + final exampleBuilder = ExamplesProvider.of(context)[options.name]; + + return buildSplitSlide( + Builder( + builder: (context) { + return ExamplePreview( + args: options.args, + builder: exampleBuilder!, + ); + }, + ), + ); + } +} diff --git a/packages/superdeck/pubspec.yaml b/packages/superdeck/pubspec.yaml new file mode 100644 index 00000000..9c2ee43d --- /dev/null +++ b/packages/superdeck/pubspec.yaml @@ -0,0 +1,61 @@ +name: superdeck +description: Presentation slides for Flutter with Flutter +version: 0.0.4 +homepage: https://github.com/leoafarias/superdeck + +publish_to: none + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + flutter: + sdk: flutter + cached_network_image: ^3.3.1 + window_manager: ^0.3.9 + collection: ^1.18.0 + path: ^1.9.0 + dart_mappable: ^4.2.2 + yaml: ^3.1.2 + animate_do: ^3.3.4 + syntax_highlight: ^0.4.0 + scrollable_positioned_list: ^0.3.8 + pdf: ^3.11.1 + localstorage: ^5.0.0 + go_router: ^14.1.4 + file_picker: ^8.0.7 + path_provider: ^2.1.4 + url_launcher: ^6.2.6 + mix: ^1.4.4 + mix_annotations: ^0.2.1 + flutter_markdown: ^0.7.3 + markdown: ^7.2.2 + flutter_hooks: ^0.20.5 + web: ^0.5.1 + file_saver: ^0.2.13 + render: ^0.0.1 + remix: + path: ../../../mix/packages/remix + go_router_paths: ^0.2.2 + visibility_detector: ^0.4.0+2 + flutter_chat_ui: ^1.6.15 + image_picker: ^1.1.2 + open_filex: ^4.5.0 + google_generative_ai: ^0.4.4 + +dev_dependencies: + flutter_test: + sdk: flutter + flutter_lints: ^4.0.0 + build_runner: ^2.4.10 + dart_mappable_builder: ^4.2.3 + + build_verify: ^3.1.0 + mix_generator: ^0.2.2 + custom_lint: ^0.6.4 + mix_lint: ^0.1.1 + + +flutter: + assets: + - grammars/ \ No newline at end of file diff --git a/packages/superdeck/pubspec_overrides.yaml b/packages/superdeck/pubspec_overrides.yaml new file mode 100644 index 00000000..c11e4d1a --- /dev/null +++ b/packages/superdeck/pubspec_overrides.yaml @@ -0,0 +1,11 @@ +dependency_overrides: + mix: + path: ../../../mix/packages/mix + mix_generator: + path: ../../../mix/packages/mix_generator + mix_lint: + path: ../../../mix/packages/mix_lint + mix_annotations: + path: ../../../mix/packages/mix_annotations + remix: + path: ../../../mix/packages/remix diff --git a/test/ensure_build_test.dart b/packages/superdeck/test/ensure_build_test.dart similarity index 100% rename from test/ensure_build_test.dart rename to packages/superdeck/test/ensure_build_test.dart diff --git a/packages/superdeck/test/fixtures/complete_config.yml b/packages/superdeck/test/fixtures/complete_config.yml new file mode 100644 index 00000000..b4a63a19 --- /dev/null +++ b/packages/superdeck/test/fixtures/complete_config.yml @@ -0,0 +1,8 @@ +transition: + type: fade_in + duration: 500 + curve: ease_in_out + delay: 300 +background: background.jpg +style: custom + \ No newline at end of file diff --git a/packages/superdeck/test/fixtures/deck_reference.json b/packages/superdeck/test/fixtures/deck_reference.json new file mode 100644 index 00000000..78c0f41d --- /dev/null +++ b/packages/superdeck/test/fixtures/deck_reference.json @@ -0,0 +1,115 @@ +{ + "config": { "transition": { "type": "fade_in", "duration": 0 } }, + "slides": [ + { + "style": "quote", + "layout": "image", + "options": { + "src": "https://picsum.photos/600/600.webp", + "fit": "cover" + }, + "content": "> Create your Flutter presentations faster and easier than ever.\n> You can quote me on that\n> ### Leo Farias", + "key": "DMiweBMq", + "content_options": { "alignment": "bottom_right" } + }, + { + "background": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif", + "style": "cover", + "key": "bfnlriXq", + "content_options": null, + "content": "# Complex layout" + }, + { + "layout": "image", + "content": "## Image Layout\n\nCreate beautiful slides with images that fit your content.\n\n##### Options\n```yaml\ncontent:\noptions:\n src: https//www.url.com/image.jpg\n fit: cover\n position: left\n flex: 1\n```\n\n> Define position fit and flex options for the image.", + "style": "show_sections", + "options": { + "src": "https://picsum.photos/900/700?waves", + "fit": "cover", + "position": "left", + "flex": 1 + }, + "key": "9wPFDzIT", + "content_options": { "alignment": "bottom_right", "flex": 1 } + }, + { + "layout": "two_column", + "style": "show_sections", + "sections": { + "left": { "flex": 2 }, + "right": { "alignment": "bottom_left" } + }, + "key": "zZ3F13ZY", + "content_options": null, + "content": "::left::\n\n# Two Column\n\nThis is a two-column layout. You can use it to compare two different concepts or ideas.\n\n::right::\n\n### Section Options\n\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n\n\n```yaml\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n```" + }, + { + "layout": "two_column_header", + "content": "# Two Column + Header\n\n\n::left::\n\n### Left Section\nEasily customize the content of each section to suit your needs.\n\nUse front matter to define the layout of each section\n::right::\n\n#### Section Options\n\n```yaml\nsections:\n left:\n alignment: bottom_right\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\n```", + "sections": { + "left": { "flex": 2 }, + "right": { "alignment": "bottom_left" }, + "header": { "alignment": "bottom_left" } + }, + "style": "show_sections", + "key": "SHmGSyUB", + "content_options": { "alignment": "center", "flex": 2 } + }, + { + "style": "rad", + "layout": "two_column", + "content": "# Mix\n\nIntegration with Mix gives you complete control over all styling elements in your slides with a simple and intuitive API.\n\n::right::\n\n```dart\nVariantAttribute get radStyle {\n return const SlideVariant('rad')(\n $.h1.textStyle.as(GoogleFonts.poppins()),\n $.h1.textStyle.fontSize(140),\n $.code.decoration.border.all(\n color: Colors.red,\n width: 3,\n ),\n $.code.decoration(\n color: Colors.black54,\n ),\n $.code.padding.all(40),\n\n $.outerContainer.margin.all(60),\n\n $.innerContainer.borderRadius(25),\n $.innerContainer.shadow(\n blurRadius: 0,\n spreadRadius: 10,\n color: Colors.red.withOpacity(1),\n ),\n $.innerContainer.gradient.radial(\n stops: [0.0, 1.0],\n radius: 0.7,\n colors: [Colors.purple, Colors.deepPurple],\n ),\n\n // Events\n onMouseHover((event) {\n final position = event.position;\n final dx = position.x * 10;\n final dy = position.y * 10;\n\n return Style(\n $.innerContainer.transform(_transformMatrix(position)),\n $.innerContainer.shadow.offset(dx, dy),\n $.innerContainer.gradient.radial(\n center: position,\n ),\n );\n }),\n\n (onPressed | onLongPressed)(\n $.innerContainer.shadow(\n blurRadius: 5,\n spreadRadius: 1,\n offset: Offset.zero,\n color: Colors.purpleAccent,\n ),\n $.innerContainer.border.all(color: Colors.white, width: 1),\n $.innerContainer.gradient.radial\n .colors([Colors.purpleAccent, Colors.purpleAccent]),\n ),\n );\n}\n```", + "sections": { + "left": null, + "right": { "alignment": "bottom_left", "flex": 2 } + }, + "key": "Ad4sj9H3", + "content_options": { "alignment": "center" } + }, + { + "style": "cover", + "background": "https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif", + "key": "J7Nbh0zk", + "content_options": null, + "content": "# Markdown support" + }, + { + "style": "show_sections", + "layout": "two_column", + "sections": null, + "content": "::left::\n\n\n**Bold Text**\n\n*Italic Text*\n\n~~Strikethrough~~\n\n`Inline Code`\n\n[Link here](https://github.com/leoafarias/superdeck)\n\n::right::\n\nLists\n\n1. Ordered list item 1\n2. Ordered list item 2\n\n- Unordered list item 1\n- Unordered list item 2\n\nQuotes\n\n> If you want to go fast, go alone. \n> If you want to go far, go together.\n> ### African Proverb", + "key": "ZiBdgDuC", + "content_options": { "flex": 4 } + }, + { + "layout": "two_column", + "key": "segi4wcj", + "content_options": null, + "content": "::left::\n\n\nCode\n```dart\nint factorial(int n) {\n return n == 0 ? 1 : n * factorial(n - 1);\n}\n```\n\nTasks\n- [ ] Item 1\n- [x] Item 2\n\nSubtasks\n\n- [x] Item 1\n - [ ] Subitem 1\n\n::right::\n\nImages\n![Unsplash Image](https://picsum.photos/300/200?landscape)\n\n\nTable\n\n| Header 1 | Header 2 |\n|----------|----------|\n| Cell 1A | Cell 1B |\n| Cell 2A | Cell 2B |\n\nDivider\n\n___" + }, + { + "title": "Mermaid example", + "layout": "two_column", + "key": "ElKv6elN", + "content_options": null, + "content": "::left::\n\n![Mermaid Diagram](superdeck/generated/sd_mermaid_s3Iic43G.png)\n \n\n::right::\n\n## Mermaid Support\n\nSuperdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation." + }, + { + "layout": "widget", + "options": { + "name": "demo", + "args": { "text": "Hello, Superdeck!", "height": 200.0, "width": 300.0 } + }, + "key": "7r83tgXu", + "content_options": null, + "content": "## Showcase your widgets" + } + ], + "assets": [ + { + "path": "superdeck/generated/sd_mermaid_s3Iic43G.png", + "width": 600, + "height": 866 + } + ] +} diff --git a/packages/superdeck/test/fixtures/empty_config.yml b/packages/superdeck/test/fixtures/empty_config.yml new file mode 100644 index 00000000..e69de29b diff --git a/packages/superdeck/test/helpers/deep_merge_test.dart b/packages/superdeck/test/helpers/deep_merge_test.dart new file mode 100644 index 00000000..f1bf88ad --- /dev/null +++ b/packages/superdeck/test/helpers/deep_merge_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/helpers/deep_merge.dart'; + +void main() { + group('deepMerge', () { + test('merges two maps with no overlapping keys', () { + final map1 = {'a': 1, 'b': 2}; + final map2 = {'c': 3, 'd': 4}; + final expected = {'a': 1, 'b': 2, 'c': 3, 'd': 4}; + expect(deepMerge(map1, map2), expected); + }); + + test('merges two maps with overlapping keys', () { + final map1 = {'a': 1, 'b': 2}; + final map2 = {'b': 3, 'c': 4}; + final expected = {'a': 1, 'b': 3, 'c': 4}; + expect(deepMerge(map1, map2), expected); + }); + + test('merges nested maps', () { + final map1 = { + 'a': 1, + 'b': {'c': 2, 'd': 3} + }; + final map2 = { + 'b': {'d': 4, 'e': 5}, + 'f': 6 + }; + final expected = { + 'a': 1, + 'b': {'c': 2, 'd': 4, 'e': 5}, + 'f': 6 + }; + expect(deepMerge(map1, map2), expected); + }); + + test('merges maps with null values', () { + final map1 = {'a': 1, 'b': null}; + final map2 = {'b': 2, 'c': null}; + final expected = {'a': 1, 'b': 2, 'c': null}; + expect(deepMerge(map1, map2), expected); + }); + + test('returns an empty map when merging two empty maps', () { + expect(deepMerge({}, {}), {}); + }); + }); +} diff --git a/packages/superdeck/test/helpers/measure_size_test.dart b/packages/superdeck/test/helpers/measure_size_test.dart new file mode 100644 index 00000000..649bd1b3 --- /dev/null +++ b/packages/superdeck/test/helpers/measure_size_test.dart @@ -0,0 +1,66 @@ +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/helpers/measure_size.dart'; + +import '../test_helpers.dart'; + +void main() { + group('MeasureSize', () { + testWidgets('calls onChange with correct size when child size changes', + (WidgetTester tester) async { + Size? size; + await tester.pumpWithScaffold( + MeasureSize( + onChange: (newSize) => size = newSize, + child: const SizedBox(width: 100, height: 100), + ), + ); + + expect(size, equals(const Size(100, 100))); + + await tester.pumpWithScaffold( + MeasureSize( + onChange: (newSize) => size = newSize, + child: const SizedBox(width: 200, height: 200), + ), + ); + + expect(size, equals(const Size(200, 200))); + }); + + testWidgets('does not call onChange when child size remains the same', + (WidgetTester tester) async { + int onChangeCalls = 0; + await tester.pumpWithScaffold( + MeasureSize( + onChange: (newSize) => onChangeCalls++, + child: const SizedBox(width: 100, height: 100), + ), + ); + + expect(onChangeCalls, equals(1)); + + await tester.pumpWithScaffold( + MeasureSize( + onChange: (newSize) => onChangeCalls++, + child: const SizedBox(width: 100, height: 100), + ), + ); + + expect(onChangeCalls, equals(1)); + }); + + testWidgets('calls onChange with Size.zero when child is null', + (WidgetTester tester) async { + Size? size; + await tester.pumpWithScaffold( + MeasureSize( + onChange: (newSize) => size = newSize, + child: const SizedBox.shrink(), + ), + ); + + expect(size, equals(Size.zero)); + }); + }); +} diff --git a/packages/superdeck/test/helpers/memory_image_provider_test.dart b/packages/superdeck/test/helpers/memory_image_provider_test.dart new file mode 100644 index 00000000..6ef97407 --- /dev/null +++ b/packages/superdeck/test/helpers/memory_image_provider_test.dart @@ -0,0 +1,49 @@ +import 'dart:convert'; + +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/helpers/cache_memory_image.dart'; + +void main() { + group('CachedMemoryImage', () { + const base64TestImage = + 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAACklEQVR4nGMAAQAABQABDQottAAAAABJRU5ErkJggg=='; + + testWidgets('builds Image widget with correct properties', + (WidgetTester tester) async { + await tester.pumpWidget( + const CachedMemoryImage( + base64Image: base64TestImage, + width: 100, + height: 100, + fit: BoxFit.fill, + ), + ); + + final Image image = tester.widget(find.byType(Image)); + expect(image.width, equals(100.0)); + expect(image.height, equals(100.0)); + expect(image.fit, equals(BoxFit.fill)); + expect(image.gaplessPlayback, isTrue); + }); + + testWidgets('decodes base64 string to bytes', (WidgetTester tester) async { + await tester.pumpWidget( + const CachedMemoryImage(base64Image: base64TestImage), + ); + + final Image image = tester.widget(find.byType(Image)); + final MemoryImage memoryImage = image.image as MemoryImage; + expect(memoryImage.bytes, equals(base64Decode(base64TestImage))); + }); + + testWidgets('uses base64 string as ValueKey', (WidgetTester tester) async { + await tester.pumpWidget( + const CachedMemoryImage(base64Image: base64TestImage), + ); + + final Image image = tester.widget(find.byType(Image)); + expect(image.key, equals(const ValueKey(base64TestImage))); + }); + }); +} diff --git a/test/helpers/syntax_highlighter_test.dart b/packages/superdeck/test/helpers/syntax_highlighter_test.dart similarity index 100% rename from test/helpers/syntax_highlighter_test.dart rename to packages/superdeck/test/helpers/syntax_highlighter_test.dart diff --git a/packages/superdeck/test/models/asset_model_test.dart b/packages/superdeck/test/models/asset_model_test.dart new file mode 100644 index 00000000..ff0315bb --- /dev/null +++ b/packages/superdeck/test/models/asset_model_test.dart @@ -0,0 +1,72 @@ +import 'dart:io'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/superdeck.dart'; + +void main() { + group('AssetFileType', () { + test('parse should return correct enum value', () { + expect(AssetFileType.parse('png'), AssetFileType.png); + expect(AssetFileType.parse('jpg'), AssetFileType.jpg); + expect(AssetFileType.parse('jpeg'), AssetFileType.jpeg); + expect(AssetFileType.parse('gif'), AssetFileType.gif); + expect(AssetFileType.parse('webp'), AssetFileType.webp); + }); + + test('parse should throw exception for invalid value', () { + expect(() => AssetFileType.parse('invalid'), throwsException); + }); + + test('tryParse should return correct enum value', () { + expect(AssetFileType.tryParse('png'), AssetFileType.png); + expect(AssetFileType.tryParse('jpg'), AssetFileType.jpg); + expect(AssetFileType.tryParse('jpeg'), AssetFileType.jpeg); + expect(AssetFileType.tryParse('gif'), AssetFileType.gif); + expect(AssetFileType.tryParse('webp'), AssetFileType.webp); + }); + + test('tryParse should return null for invalid value', () { + expect(AssetFileType.tryParse('invalid'), isNull); + }); + + test('isPng should return true for png', () { + expect(AssetFileType.png.isPng(), isTrue); + }); + + test('isJpg should return true for jpg and jpeg', () { + expect(AssetFileType.jpg.isJpg(), isTrue); + expect(AssetFileType.jpeg.isJpg(), isTrue); + }); + + test('isGif should return true for gif', () { + expect(AssetFileType.gif.isGif(), isTrue); + }); + }); + + group('SlideAsset', () { + late SlideAsset asset; + late File file; + + setUp(() { + file = File('test.png'); + + asset = SlideAsset( + path: file.path, width: 800, height: 600, reference: 'test'); + }); + + test('extension should return correct file extension', () { + expect(asset.extension, '.png'); + }); + + test('isPortrait should return true when height is greater than width', () { + expect(asset.isPortrait, isFalse); + }); + + test('isLandscape should return true when width is greater than height', + () { + asset = SlideAsset( + path: file.path, width: 1200, height: 800, reference: 'test'); + expect(asset.isLandscape, isTrue); + }); + }); +} diff --git a/packages/superdeck/test/models/config_model_test.dart b/packages/superdeck/test/models/config_model_test.dart new file mode 100644 index 00000000..2531b0ce --- /dev/null +++ b/packages/superdeck/test/models/config_model_test.dart @@ -0,0 +1,37 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/models/config_model.dart'; +import 'package:superdeck/superdeck.dart'; + +void main() { + group('Config', () { + test('fromMap should create Config instance from map', () { + final map = { + 'background': 'bg.jpg', + 'style': 'dark', + 'transition': {'type': 'fade_in_down_big', 'duration': 500}, + 'cache_remote_assets': true, + }; + + final config = Config.fromMap(map); + + expect(config.background, 'bg.jpg'); + expect(config.style, 'dark'); + expect(config.transition?.type, TransitionType.fadeInDownBig); + expect(config.transition?.duration, const Duration(milliseconds: 500)); + expect(config.cacheRemoteAssets, true); + }); + + test('fromJson should create Config instance from JSON string', () { + const json = + '{"background":"bg.jpg","style":"light","transition":{"type":"slide_in_right","duration":1000},"cache_remote_assets":false}'; + + final config = Config.fromJson(json); + + expect(config.background, 'bg.jpg'); + expect(config.style, 'light'); + expect(config.transition?.type, TransitionType.slideInRight); + expect(config.transition?.duration, const Duration(seconds: 1)); + expect(config.cacheRemoteAssets, false); + }); + }); +} diff --git a/packages/superdeck/test/models/deck_reference_model_test.dart b/packages/superdeck/test/models/deck_reference_model_test.dart new file mode 100644 index 00000000..af8d8b9b --- /dev/null +++ b/packages/superdeck/test/models/deck_reference_model_test.dart @@ -0,0 +1,43 @@ +import 'dart:io'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/models/reference_model.dart'; + +void main() { + group('DeckReference', () { + /// Create a test for performance parsing of fromJson + test('parses a json deck reference in less than 100ms', () async { + final fixture = File('test/fixtures/deck_reference.json'); + final contents = await fixture.readAsString(); + + final stopwatch = Stopwatch()..start(); + SuperDeckReference.fromJson(contents); + stopwatch.stop(); + + // Use Isolate.compute to run the parsing in a separate isolate + // and measure the time it takes to parse the json + + final stopwatch2 = Stopwatch()..start(); + + await compute(SuperDeckReference.fromJson, contents); + + stopwatch2.stop(); + + // stopwatch 2 should be faster than stopwatch + expect(stopwatch2.elapsedMilliseconds, + lessThan(stopwatch.elapsedMilliseconds)); + }); + + test('parses a json deck reference', () async { + final fixture = File('test/fixtures/deck_reference.json'); + final contents = await fixture.readAsString(); + final deckReference = SuperDeckReference.fromJson(contents); + + expect(deckReference, isNotNull); + expect(deckReference.config, isNotNull); + expect(deckReference.slides, isNotEmpty); + expect(deckReference.assets, isNotEmpty); + }); + }); +} diff --git a/test/models/syntax_tag_test.dart b/packages/superdeck/test/models/syntax_tag_test.dart similarity index 100% rename from test/models/syntax_tag_test.dart rename to packages/superdeck/test/models/syntax_tag_test.dart diff --git a/packages/superdeck/test/options_model_test.dart b/packages/superdeck/test/options_model_test.dart new file mode 100644 index 00000000..003b0579 --- /dev/null +++ b/packages/superdeck/test/options_model_test.dart @@ -0,0 +1,85 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/superdeck.dart'; + +void main() { + group('ContentOptions', () { + test('merge returns original instance when other is null', () { + const options = ContentOptions(); + expect(options.merge(null), same(options)); + }); + + test('merge returns new instance with merged values', () { + const options1 = + ContentOptions(flex: 2, alignment: ContentAlignment.topLeft); + const options2 = ContentOptions(alignment: ContentAlignment.bottomRight); + final merged = options1.merge(options2); + expect(merged.flex, options1.flex); + expect(merged.alignment, options2.alignment); + }); + }); + + group('ImageOptions', () { + test('constructor sets properties correctly', () { + const options = ImageOptions( + src: 'image.png', + fit: ImageFit.cover, + flex: 3, + position: LayoutPosition.left, + ); + expect(options.src, 'image.png'); + expect(options.fit, ImageFit.cover); + expect(options.flex, 3); + expect(options.position, LayoutPosition.left); + }); + }); + + group('WidgetOptions', () { + test('constructor sets properties correctly', () { + const options = WidgetOptions( + name: 'MyWidget', + args: {'key': 'value'}, + flex: 2, + position: LayoutPosition.top, + ); + expect(options.name, 'MyWidget'); + expect(options.args, {'key': 'value'}); + expect(options.flex, 2); + expect(options.position, LayoutPosition.top); + }); + }); + + group('TransitionOptions', () { + test('merge returns original instance when other is null', () { + const options = TransitionOptions(type: TransitionType.fadeIn); + expect(options.merge(null), same(options)); + }); + + test('merge returns new instance with merged values', () { + const options1 = TransitionOptions( + type: TransitionType.fadeIn, + duration: Duration(seconds: 1), + delay: Duration(milliseconds: 500), + curve: CurveType.easeIn, + ); + const options2 = TransitionOptions( + type: TransitionType.fadeOut, + duration: Duration(seconds: 2), + ); + final merged = options1.merge(options2); + expect(merged.type, options2.type); + expect(merged.duration, options2.duration); + expect(merged.delay, options1.delay); + expect(merged.curve, options1.curve); + }); + + test('totalAnimationDuration returns sum of duration and delay', () { + const options = TransitionOptions( + type: TransitionType.fadeIn, + duration: Duration(seconds: 1), + delay: Duration(milliseconds: 500), + ); + expect( + options.totalAnimationDuration, const Duration(milliseconds: 1500)); + }); + }); +} diff --git a/test/superdeck_test.dart b/packages/superdeck/test/superdeck_test.dart similarity index 100% rename from test/superdeck_test.dart rename to packages/superdeck/test/superdeck_test.dart diff --git a/packages/superdeck/test/templates/image_template_test.dart b/packages/superdeck/test/templates/image_template_test.dart new file mode 100644 index 00000000..b95220a5 --- /dev/null +++ b/packages/superdeck/test/templates/image_template_test.dart @@ -0,0 +1,32 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/components/molecules/slide_content.dart'; +import 'package:superdeck/superdeck.dart'; +import 'package:superdeck/templates/templates.dart'; + +import '../test_helpers.dart'; + +void main() { + group('ImageTemplate', () { + final slideConfig = ImageSlide( + key: 'image-slide', + content: '', + contentOptions: const ContentOptions(), + options: const ImageOptions( + src: 'https://example.com/image.jpg', + ), + ); + + testWidgets('builds image template with correct layout', + (WidgetTester tester) async { + await tester.pumpSlide(slideConfig); + + expect(find.byType(SlideContent), findsOneWidget); + expect(find.byType(ImageTemplate), findsOneWidget); + + final finder = find.byType(ImageTemplate); + final template = tester.widget(finder); + + expect(template.config, slideConfig); + }); + }); +} diff --git a/packages/superdeck/test/templates/simple_template_test.dart b/packages/superdeck/test/templates/simple_template_test.dart new file mode 100644 index 00000000..feede34b --- /dev/null +++ b/packages/superdeck/test/templates/simple_template_test.dart @@ -0,0 +1,22 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/models/slide_model.dart'; +import 'package:superdeck/templates/templates.dart'; + +import '../test_helpers.dart'; + +void main() { + group('SimpleTemplate', () { + const rawMarkdown = ''' +# Hello +'''; + final slideConfig = SimpleSlide(content: rawMarkdown, key: 'simple-slide'); + testWidgets('builds content', (WidgetTester tester) async { + await tester.pumpSlide(slideConfig); + final finder = find.byType(SimpleTemplate); + expect(finder, findsOneWidget); + // Check if template model equals to slide model + final template = tester.widget(finder); + expect(template.config, slideConfig); + }); + }); +} diff --git a/packages/superdeck/test/test_helpers.dart b/packages/superdeck/test/test_helpers.dart new file mode 100644 index 00000000..16a51574 --- /dev/null +++ b/packages/superdeck/test/test_helpers.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:superdeck/components/atoms/slide_view.dart'; +import 'package:superdeck/providers/assets_provider.dart'; +import 'package:superdeck/providers/examples_provider.dart'; +import 'package:superdeck/providers/snapshot_provider.dart'; +import 'package:superdeck/providers/style_provider.dart'; +import 'package:superdeck/superdeck.dart'; + +extension WidgetTesterX on WidgetTester { + Future pumpWithScaffold(Widget widget) async { + await pumpWidget(MaterialApp(home: Scaffold(body: widget))); + } + + Future pumpSlide( + T slide, { + bool isSnapshot = false, + Style style = const Style.empty(), + Map examples = const {}, + List assets = const [], + }) async { + return pumpWithScaffold( + SnapshotProvider( + isCapturing: isSnapshot, + child: StyleProvider( + baseStyle: style, + child: AssetsProvider( + assets: assets, + child: ExamplesProvider( + examples: examples, + child: SlideView(slide), + ), + ), + ), + ), + ); + } +} diff --git a/packages/superdeck_cli/.gitignore b/packages/superdeck_cli/.gitignore new file mode 100644 index 00000000..3cceda55 --- /dev/null +++ b/packages/superdeck_cli/.gitignore @@ -0,0 +1,7 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ + +# Avoid committing pubspec.lock for library packages; see +# https://dart.dev/guides/libraries/private-files#pubspeclock. +pubspec.lock diff --git a/packages/superdeck_cli/CHANGELOG.md b/packages/superdeck_cli/CHANGELOG.md new file mode 100644 index 00000000..effe43c8 --- /dev/null +++ b/packages/superdeck_cli/CHANGELOG.md @@ -0,0 +1,3 @@ +## 1.0.0 + +- Initial version. diff --git a/packages/superdeck_cli/README.md b/packages/superdeck_cli/README.md new file mode 100644 index 00000000..8b55e735 --- /dev/null +++ b/packages/superdeck_cli/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/packages/superdeck_cli/analysis_options.yaml b/packages/superdeck_cli/analysis_options.yaml new file mode 100644 index 00000000..dee8927a --- /dev/null +++ b/packages/superdeck_cli/analysis_options.yaml @@ -0,0 +1,30 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +# Uncomment the following section to specify additional rules. + +# linter: +# rules: +# - camel_case_types + +# analyzer: +# exclude: +# - path/to/excluded/files/** + +# For more information about the core and recommended set of lints, see +# https://dart.dev/go/core-lints + +# For additional information about configuring this file, see +# https://dart.dev/guides/language/analysis-options diff --git a/packages/superdeck_cli/bin/build.dart b/packages/superdeck_cli/bin/build.dart new file mode 100644 index 00000000..93e4be04 --- /dev/null +++ b/packages/superdeck_cli/bin/build.dart @@ -0,0 +1,13 @@ +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:superdeck_cli/superdeck_cli.dart'; + +void main(List arguments) async { + try { + await SlidesLoader().generate(); + } on UsageException catch (e) { + print(e); + exit(64); + } +} diff --git a/packages/superdeck_cli/bin/watch.dart b/packages/superdeck_cli/bin/watch.dart new file mode 100644 index 00000000..c8181632 --- /dev/null +++ b/packages/superdeck_cli/bin/watch.dart @@ -0,0 +1,15 @@ +import 'dart:io'; + +import 'package:args/command_runner.dart'; +import 'package:superdeck_cli/src/slides_loader.dart'; + +void main(List arguments) async { + try { + final loader = SlidesLoader(); + await loader.generate(); + await loader.watch(); + } on UsageException catch (e) { + print(e); + exit(64); + } +} diff --git a/packages/superdeck_cli/example/superdeck_cli_example.dart b/packages/superdeck_cli/example/superdeck_cli_example.dart new file mode 100644 index 00000000..ab73b3a2 --- /dev/null +++ b/packages/superdeck_cli/example/superdeck_cli_example.dart @@ -0,0 +1 @@ +void main() {} diff --git a/packages/superdeck_cli/lib/src/constants.dart b/packages/superdeck_cli/lib/src/constants.dart new file mode 100644 index 00000000..b6b72157 --- /dev/null +++ b/packages/superdeck_cli/lib/src/constants.dart @@ -0,0 +1,13 @@ +import 'dart:io'; + +import 'package:path/path.dart' as p; + +final File kMarkdownFile = File('slides.md'); + +final Directory kAssetsDir = Directory(p.join('.superdeck')); + +final Directory kGeneratedAssetsDir = + Directory(p.join(kAssetsDir.path, 'generated')); +final File kProjectConfigFile = File('superdeck.yaml'); + +final File kReferenceFile = File(p.join(kAssetsDir.path, 'slides.json')); diff --git a/lib/helpers/extensions.dart b/packages/superdeck_cli/lib/src/helpers/extensions.dart similarity index 100% rename from lib/helpers/extensions.dart rename to packages/superdeck_cli/lib/src/helpers/extensions.dart diff --git a/packages/superdeck_cli/lib/src/helpers/pretty_json.dart b/packages/superdeck_cli/lib/src/helpers/pretty_json.dart new file mode 100644 index 00000000..a21e6138 --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/pretty_json.dart @@ -0,0 +1,8 @@ +import 'dart:convert'; + +/// Formats [json] +String prettyJson(dynamic json) { + var spaces = ' ' * 2; + var encoder = JsonEncoder.withIndent(spaces); + return encoder.convert(json); +} diff --git a/packages/superdeck_cli/lib/src/helpers/raw_models.dart b/packages/superdeck_cli/lib/src/helpers/raw_models.dart new file mode 100644 index 00000000..47bf53ed --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/raw_models.dart @@ -0,0 +1,134 @@ +import 'dart:convert'; +import 'dart:io'; + +import 'package:superdeck_cli/src/helpers/short_hash_id.dart'; +import 'package:superdeck_cli/src/helpers/yaml_to_map.dart'; + +typedef Json = Map; + +class RawReference { + final List assets; + final List slides; + final Json config; + + const RawReference({ + required this.assets, + required this.slides, + required this.config, + }); + + static RawReference loadFile(File file) { + try { + return fromJson(file.readAsStringSync()); + } catch (e) { + return const RawReference(assets: [], slides: [], config: {}); + } + } + + static RawReference fromMap(Json map) { + return RawReference( + assets: (map['assets'] as List) + .map((e) => RawAsset.fromMap(e)) + .toList(), + slides: (map['slides'] as List) + .map((e) => RawSlide(e as Json)) + .toList(), + config: map['config'] as Json, + ); + } + + static RawReference fromJson(String json) { + return fromMap(jsonDecode(json) as Json); + } +} + +class RawConfig { + final Json _map; + + const RawConfig(this._map); + + static RawConfig loadFile(File file) { + try { + final yamlString = file.readAsStringSync(); + return RawConfig(converYamlToMap(yamlString)); + } catch (e) { + return const RawConfig({}); + } + } + + Json toMap() => _map; +} + +class RawSlide { + final String key; + final Json _map; + + RawSlide(this._map) : key = shortHashId(_map.toString()); + + String get content => (_map['content'] ?? '') as String; + + static RawSlide fromYaml(String yamlString) { + final (:content, :options) = parseSlideMarkdown(yamlString); + return RawSlide({ + ...options, + + // Change from content on the frontMatter to content options in on the map + if (options['content'] != null) 'content_options': options['content'], + 'content': content, + }); + } + + RawSlide copyWith({ + String? content, + }) { + return RawSlide( + { + ..._map, + 'content': content ?? _map['content'], + }, + ); + } + + Json toMap() => { + ..._map, + 'key': key, + }; + + static RawSlide fromJson(String json) { + return RawSlide(jsonDecode(json)); + } +} + +class RawAsset { + final String path; + final String? reference; + final int width; + final int height; + + const RawAsset({ + required this.path, + required this.width, + required this.height, + required this.reference, + }); + + static RawAsset fromMap(Json json) { + return RawAsset( + path: json['path'] as String, + reference: json['reference'] as String?, + width: json['width'] as int, + height: json['height'] as int, + ); + } + + static RawAsset fromJson(String json) { + return fromMap(jsonDecode(json) as Json); + } + + Json toMap() => { + 'path': path, + 'width': width, + 'height': height, + if (reference != null) 'reference': reference, + }; +} diff --git a/packages/superdeck_cli/lib/src/helpers/short_hash_id.dart b/packages/superdeck_cli/lib/src/helpers/short_hash_id.dart new file mode 100644 index 00000000..989b0cb8 --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/short_hash_id.dart @@ -0,0 +1,32 @@ +/// Generates a short, unique identifier from a given string. +/// This is needed as hashCode for strings is not guaranteed to be unique across different platforms +/// +/// This function uses a hashing mechanism to transform the input string into +/// a unique, 8-character identifier. It is useful for creating compact and +/// unique keys for database entries, URLs, etc. +/// +/// [valueToHash] is the string input that you want to convert into a hash ID. +/// +/// Returns an 8-character string that represents the hashed ID. +String shortHashId(String valueToHash) { + const characters = + 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'; + + int hash = 0; + + for (int i = 0; i < valueToHash.length; i++) { + int charCode = valueToHash.codeUnitAt(i); + hash = (hash * 31 + charCode) % 2147483647; + } + + String shortId = ''; + int base = characters.length; + int remainingHash = hash; + + for (int i = 0; i < 8; i++) { + shortId += characters[remainingHash % base]; + remainingHash = (remainingHash * 31 + hash + i) % 2147483647; + } + + return shortId; +} diff --git a/packages/superdeck_cli/lib/src/helpers/slide_parser.dart b/packages/superdeck_cli/lib/src/helpers/slide_parser.dart new file mode 100644 index 00000000..359129a4 --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/slide_parser.dart @@ -0,0 +1,63 @@ +// lib/slide_parser.dart + +import 'package:superdeck_cli/src/helpers/raw_models.dart'; + +class SlideParser { + final String contents; + SlideParser(this.contents); + + List _splitSlides(String content) { + final lines = content.split('\n'); + final slides = []; + final buffer = StringBuffer(); + bool inSlide = false; + + var isCodeBlock = false; + + for (var line in lines) { + if (line.trim().startsWith('```')) { + isCodeBlock = !isCodeBlock; + } + if (line.trim() == '---' && !isCodeBlock) { + if (buffer.isNotEmpty) { + if (inSlide) { + slides.add(buffer.toString().trim()); + inSlide = false; + buffer.clear(); + } else { + inSlide = true; + } + } + buffer.writeln(line); + } else { + buffer.writeln(line); + } + } + + if (buffer.isNotEmpty) { + slides.add(buffer.toString()); + } + + return slides; + } + + List run() { + final markdownContents = _splitSlides(contents.trim()); + return markdownContents.map(RawSlide.fromYaml).toList(); + } +} + +Map deepMerge( + Map source, Map updates) { + final result = Map.from(source); + + updates.forEach((key, value) { + if (value is Map && result[key] is Map) { + result[key] = deepMerge(result[key] as Map, value); + } else { + result[key] = value; + } + }); + + return result; +} diff --git a/packages/superdeck_cli/lib/src/helpers/update_pubspec.dart b/packages/superdeck_cli/lib/src/helpers/update_pubspec.dart new file mode 100644 index 00000000..f0da6411 --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/update_pubspec.dart @@ -0,0 +1,52 @@ +import 'dart:io'; + +import 'package:yaml/yaml.dart'; +import 'package:yaml_writer/yaml_writer.dart'; + +/// Path/filename comment +const String pubspecPath = 'pubspec.yaml'; + +void main() { + updatePubspecAssets(); +} + +/// Updates the pubspec.yaml to include .superdeck/ and .superdeck/generated/ assets. +void updatePubspecAssets() { + // Read the pubspec.yaml file + final File file = File(pubspecPath); + if (!file.existsSync()) { + print('pubspec.yaml not found.'); + return; + } + + final String content = file.readAsStringSync(); + final YamlMap yamlContent = loadYaml(content); + + // Ensure the flutter: key exists + Map flutterSection = yamlContent['flutter'] ?? {}; + + // Get the existing assets or create a new list if it doesn't exist + List assets = flutterSection['assets']?.toList() ?? []; + + // Add the new asset paths if they don't exist + if (!assets.contains('.superdeck/')) { + assets.add('.superdeck/'); + } + if (!assets.contains('.superdeck/generated/')) { + assets.add('.superdeck/generated/'); + } + + // Update the flutter section with the new assets list + flutterSection['assets'] = assets; + + // Convert the updated map back to YAML + final updatedYaml = Map.from(yamlContent) + ..['flutter'] = flutterSection; + + final String updatedContent = YamlWriter().write(updatedYaml); + + // Write the updated content back to the pubspec.yaml file + file.writeAsStringSync(updatedContent); + + print('pubspec.yaml updated successfully.'); +} diff --git a/packages/superdeck_cli/lib/src/helpers/yaml_to_map.dart b/packages/superdeck_cli/lib/src/helpers/yaml_to_map.dart new file mode 100644 index 00000000..b9f76d89 --- /dev/null +++ b/packages/superdeck_cli/lib/src/helpers/yaml_to_map.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +import 'package:superdeck_cli/src/helpers/raw_models.dart'; +import 'package:yaml/yaml.dart'; + +final _frontMatterRegex = RegExp(r'---([\s\S]*?)---'); + +Json converYamlToMap(String yamlString) { + final yamlMap = loadYaml(yamlString) as YamlMap? ?? YamlMap(); + + final yaml = jsonEncode(yamlMap); + + return jsonDecode(yaml); +} + +({String content, Json options}) parseSlideMarkdown(String slideContents) { + final frontMatter = + _frontMatterRegex.firstMatch(slideContents)?.group(1) ?? ''; + final options = converYamlToMap(frontMatter); + + final content = slideContents + .substring(_frontMatterRegex.matchAsPrefix(slideContents)?.end ?? 0) + .trim(); + + return ( + content: content, + options: options, + ); +} diff --git a/packages/superdeck_cli/lib/src/slides_loader.dart b/packages/superdeck_cli/lib/src/slides_loader.dart new file mode 100644 index 00000000..1ca794e5 --- /dev/null +++ b/packages/superdeck_cli/lib/src/slides_loader.dart @@ -0,0 +1,43 @@ +import 'dart:async'; + +import 'package:superdeck_cli/src/constants.dart'; +import 'package:superdeck_cli/src/tasks/dart_formatter_task.dart'; +import 'package:superdeck_cli/src/tasks/image_cache_task.dart'; +import 'package:superdeck_cli/src/tasks/mermaid_task.dart'; +import 'package:superdeck_cli/src/tasks/slide_thumbnail_task.dart'; +import 'package:watcher/watcher.dart'; + +import 'slides_pipeline.dart'; + +String _markdownContents = ''; + +class SlidesLoader { + SlidesLoader(); + + Future watch() async { + print('Watching for changes...'); + final watcher = FileWatcher(kMarkdownFile.path); + await for (final event in watcher.events) { + if (event.type == ChangeType.MODIFY) { + final newContents = await kMarkdownFile.readAsString(); + if (newContents != _markdownContents) { + _markdownContents = newContents; + await generate(); + } + } + } + } + + Future generate() async { + print('Generating slides...'); + + final pipeline = TaskPipeline([ + const MermaidConverterTask(), + const DartFormatterTask(), + const SlideThumbnailTask(), + const ImageCachingTask(), + ]); + + await pipeline.run(); + } +} diff --git a/packages/superdeck_cli/lib/src/slides_pipeline.dart b/packages/superdeck_cli/lib/src/slides_pipeline.dart new file mode 100644 index 00000000..f6e6f3f2 --- /dev/null +++ b/packages/superdeck_cli/lib/src/slides_pipeline.dart @@ -0,0 +1,216 @@ +import 'dart:async'; +import 'dart:io'; + +import 'package:collection/collection.dart'; +import 'package:image/image.dart' as img; +import 'package:path/path.dart' as p; +import 'package:superdeck_cli/src/constants.dart'; +import 'package:superdeck_cli/src/helpers/extensions.dart'; +import 'package:superdeck_cli/src/helpers/pretty_json.dart'; +import 'package:superdeck_cli/src/helpers/raw_models.dart'; +import 'package:superdeck_cli/src/helpers/short_hash_id.dart'; +import 'package:superdeck_cli/src/helpers/slide_parser.dart'; + +typedef MarkdownReplacement = ({ + Pattern pattern, + String replacement, +}); + +typedef PipelineResult = ({ + List slides, + List neededAssets, + List markdownReplacements, +}); + +class TaskController { + final RawSlide slide; + final List _assets; + final List markdownReplacements; + + TaskController._({ + required this.slide, + required List assets, + required this.markdownReplacements, + }) : _assets = assets; + + TaskController({ + required this.slide, + required List assets, + }) : _assets = assets, + markdownReplacements = []; + + List neededAssets = []; + + RawAsset? checkAssetExists(String assetName) { + return _assets + .firstWhereOrNull((element) => element.path.contains(assetName)); + } + + TaskController copyWith({ + RawSlide? slide, + List? assets, + List? markdownReplacements, + }) { + return TaskController._( + slide: slide ?? this.slide, + markdownReplacements: markdownReplacements ?? this.markdownReplacements, + assets: assets ?? _assets, + )..neededAssets = neededAssets; + } + + Future markFileAsNeeded(File file, [String? reference]) async { + var asset = _assets.firstWhereOrNull((f) => f.path == file.path); + + if (asset == null) { + print(file.path); + final image = await img.decodeImageFile(file.path); + if (image == null) { + throw Exception('Could not decode image'); + } + + asset = RawAsset( + path: file.path, + width: image.width, + height: image.height, + reference: reference, + ); + } else { + if (!await file.exists()) { + throw Exception('File does not exist'); + } + } + markNeeded(asset); + } + + void markNeeded(RawAsset asset) { + neededAssets.add(asset); + } +} + +class TaskPipeline { + final List builders; + + TaskPipeline( + this.builders, + ); + + Future _runEachSlide( + RawSlide slide, + List assets, + ) async { + var controller = TaskController(slide: slide, assets: assets); + for (var task in builders) { + controller = await task.run(controller); + } + return controller; + } + + Future run() async { + await kMarkdownFile.ensureExists(); + await kGeneratedAssetsDir.ensureExists(); + await kReferenceFile.ensureExists(); + final markdownRaw = kMarkdownFile.readAsStringSync(); + + final reference = RawReference.loadFile(kReferenceFile); + final rawAssets = reference.assets; + + final parser = SlideParser(markdownRaw); + final slides = parser.run(); + + final futures = >[]; + + for (var slide in slides) { + futures.add(_runEachSlide(slide, rawAssets)); + } + + final controllers = await Future.wait(futures); + + final result = ( + slides: controllers.map((e) => e.slide).toList(), + neededAssets: controllers.expand((e) => e.neededAssets).toList(), + markdownReplacements: + controllers.expand((e) => e.markdownReplacements).toList(), + ); + + await _applyResults(result); + + return result; + } +} + +Future _applyResults(PipelineResult result) async { + final config = RawConfig.loadFile(kProjectConfigFile); + await _applyMarkdownReplacements(result.markdownReplacements); + await _cleanupGeneratedFiles(result.neededAssets); + await kReferenceFile.writeAsString( + prettyJson( + { + 'config': config.toMap(), + 'slides': result.slides.map((e) => e.toMap()).toList(), + 'assets': result.neededAssets.map((e) => e.toMap()).toList(), + }, + ), + ); +} + +Future _cleanupGeneratedFiles(List assets) async { + final files = await _loadGeneratedFiles(); + + for (var file in files) { + if (!assets.any((element) => element.path == file.path)) { + if (await file.exists()) { + await file.delete(); + } + } + } +} + +Future _applyMarkdownReplacements( + List replacements, +) async { + var markdownRaw = await kMarkdownFile.readAsString(); + + for (final entry in replacements) { + markdownRaw = markdownRaw.replaceAll(entry.pattern, entry.replacement); + } + + await kMarkdownFile.writeAsString(markdownRaw); +} + +abstract class Task { + final String taskName; + const Task(this.taskName); + + FutureOr run( + TaskController controller, + ); + + String buildReferenceName(String content) { + return shortHashId(content); + } + + File buildAssetFile(String assetName) { + if (p.extension(assetName).isEmpty) { + assetName = '$assetName.png'; + } + final updatedFileName = ('${taskName}_$assetName'); + return File(p.join(kGeneratedAssetsDir.path, updatedFileName)); + } + + bool isAssetFile(File file) { + // check if file name starts with sd_ + return file.path.contains(kGeneratedAssetsDir.path); + } +} + +Future> _loadGeneratedFiles() async { + final files = []; + + await for (var entity in kGeneratedAssetsDir.list()) { + if (entity is File) { + files.add(entity); + } + } + + return files; +} diff --git a/packages/superdeck_cli/lib/src/tasks/dart_formatter_task.dart b/packages/superdeck_cli/lib/src/tasks/dart_formatter_task.dart new file mode 100644 index 00000000..b852e7f8 --- /dev/null +++ b/packages/superdeck_cli/lib/src/tasks/dart_formatter_task.dart @@ -0,0 +1,40 @@ +import 'dart:async'; + +import 'package:dart_style/dart_style.dart'; +import 'package:superdeck_cli/src/slides_pipeline.dart'; + +class DartFormatterTask extends Task { + const DartFormatterTask() : super('dart_formatter'); + + @override + FutureOr run(controller) async { + final formattedMarkdown = _formatDartCodeBlocks(controller); + + return controller.copyWith( + slide: controller.slide.copyWith(content: formattedMarkdown), + ); + } + + String _formatDartCodeBlocks( + TaskController controller, + ) { + final codeBlockRegex = RegExp(r'```dart\n([\s\S]*?)\n```'); + final markdown = controller.slide.content; + return markdown.replaceAllMapped(codeBlockRegex, (match) { + final code = match.group(1)!; + final formatter = DartFormatter(); + final formattedCode = formatter.format(code); + + final replacement = '```dart\n$formattedCode\n```'; + + controller.markdownReplacements.add( + ( + pattern: match.group(0)!, + replacement: replacement, + ), + ); + + return replacement; + }); + } +} diff --git a/packages/superdeck_cli/lib/src/tasks/image_cache_task.dart b/packages/superdeck_cli/lib/src/tasks/image_cache_task.dart new file mode 100644 index 00000000..4f218c0b --- /dev/null +++ b/packages/superdeck_cli/lib/src/tasks/image_cache_task.dart @@ -0,0 +1,111 @@ +import 'dart:io'; + +import 'package:http/http.dart' as http; +import 'package:superdeck_cli/src/slides_pipeline.dart'; + +class ImageCachingTask extends Task { + const ImageCachingTask() : super('image_caching'); + + @override + Future run(controller) async { + final slide = controller.slide; + + var content = slide.content; + final slideData = slide.toMap(); + // Do not cache remot edata if cacheRemoteAssets is false + + // Get any url of images that are in the markdown + // Save it the local path on the device + // and replace the url with the local path + final imageRegex = RegExp(r'!\[.*?\]\((.*?)\)'); + final urlRegex = RegExp( + r'((http|https|ftp):\/\/)?(www\.)?([a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+)(\/[^\s]*)?', + caseSensitive: false, + ); + + final matches = imageRegex.allMatches(content); + + Future saveAsset(String url) async { + if (isAssetFile(File(url))) return; + // Look by hashcode to see if the asset is already cached + final refName = buildReferenceName(url); + + final asset = controller.checkAssetExists(refName); + + // if (asset != null && await File(asset.path).exists()) { + // final file = File(asset.path); + // try { + // print('File already exists: ${file.path}'); + // await controller.markFileAsNeeded(file, url); + // return; + // } catch (e) { + // log('Error saving asset: ${file.path} $e'); + // await file.delete(); + // saveAsset(url); + // } + // } + + try { + final checkFileType = await http.head(Uri.parse(url)); + final contentType = checkFileType.headers['content-type']; + if (contentType == null) { + return; + } + + if (!contentType.startsWith('image/')) { + return; + } + + final commonRenderableFormats = ['jpeg', 'png', 'gif', 'webp']; + final extension = contentType.split('/').last; + if (!commonRenderableFormats.contains(extension)) { + return; + } + final response = await http.get(Uri.parse(url)); + + if (response.statusCode == 200) { + // Get the extension from the Content-Type header + final contentType = response.headers['content-type']; + + final extension = contentType?.split('/').last; + + // Create a file with the appropriate extension + final file = buildAssetFile(buildReferenceName(url) + '.$extension'); + + // Write the image data to the file + await file.writeAsBytes(response.bodyBytes); + + print('Image saved to: ${url}'); + + await controller.markFileAsNeeded(file, url); + } else {} + } catch (e) { + print('Error: $e'); + } + } + + for (final Match match in matches) { + final assetUri = match.group(1); + if (assetUri == null) continue; + + await saveAsset(assetUri); + } + + if (slideData['background'] != null) { + // Check if it matches + if (urlRegex.hasMatch(slideData['background'])) { + await saveAsset(slideData['background']); + } + } + + if (slideData['options'] != null) { + if (slideData['options']['background'] != null) { + if (urlRegex.hasMatch(slideData['options']['background'])) { + await saveAsset(slideData['options']['background']); + } + } + } + + return controller; + } +} diff --git a/packages/superdeck_cli/lib/src/tasks/mermaid_task.dart b/packages/superdeck_cli/lib/src/tasks/mermaid_task.dart new file mode 100644 index 00000000..823329ce --- /dev/null +++ b/packages/superdeck_cli/lib/src/tasks/mermaid_task.dart @@ -0,0 +1,130 @@ +import 'dart:async'; +import 'dart:developer'; +import 'dart:io'; +import 'dart:typed_data'; + +import 'package:path/path.dart' as p; +import 'package:superdeck_cli/src/slides_pipeline.dart'; + +class MermaidConverterTask extends Task { + final _mermaidService = const MermaidService(); + const MermaidConverterTask() : super('mermaid'); + + @override + FutureOr run(controller) async { + final mermaidBlockRegex = RegExp(r'```mermaid([\s\S]*?)```'); + final slide = controller.slide; + + final matches = mermaidBlockRegex.allMatches(slide.content); + + if (matches.isEmpty) return controller; + final replacements = <({int start, int end, String markdown})>[]; + + for (final Match match in matches) { + final mermaidSyntax = match.group(1); + + if (mermaidSyntax == null) continue; + + final mermaidFile = buildAssetFile(buildReferenceName(mermaidSyntax)); + + if (!await mermaidFile.exists()) { + // Process the mermaid syntax to generate an image file + final imageData = await _mermaidService.generateImage(mermaidSyntax); + + if (imageData != null) { + await mermaidFile.writeAsBytes(imageData); + } + } + + // If file existeed or was create it then replace it + if (await mermaidFile.exists()) { + await controller.markFileAsNeeded(mermaidFile); + + replacements.add(( + start: match.start, + end: match.end, + markdown: '![Mermaid Diagram](${mermaidFile.path})', + )); + } + } + + var replacedData = slide.content; + + // Apply replacements in reverse order + for (var replacement in replacements.reversed) { + final ( + :start, + :end, + :markdown, + ) = replacement; + + replacedData = replacedData.replaceRange(start, end, markdown); + } + + return controller.copyWith( + slide: slide.copyWith(content: replacedData), + ); + } +} + +class MermaidService { + const MermaidService(); + + Future generateImage(String mermaidSyntax) async { + final fileName = mermaidSyntax.hashCode; + + final tempDir = Directory('.tmp_superdeck'); + + final tempFile = File(p.join(tempDir.path, '$fileName.mmd')); + final outputFile = File(p.join(tempDir.path, '$fileName.png')); + + if (!await tempDir.exists()) { + await tempDir.create(recursive: true); + } + + try { + mermaidSyntax = mermaidSyntax.trim().replaceAll(r'\n', '\n'); + + await tempFile.writeAsString(mermaidSyntax); + + // Check if can execute mmdc before executing command + final mmdcResult = await Process.run('mmdc', ['--version']); + + if (mmdcResult.exitCode != 0) { + log( + '"mmdc" not found. You need mermaid cli installed to process mermaid syntax', + ); + + return null; + } + + final params = [ + '-t dark', + '-b transparent', + '-i ${tempFile.path}', + '-o ${outputFile.path}', + '--scale 2' + ]; + + final result = await Process.run( + 'mmdc', + params.expand((e) => e.split(' ')).toList(), + ); + + if (result.exitCode != 0) { + log('Error while processing mermaid syntax'); + log(result.stderr); + return null; + } + + return outputFile.readAsBytes(); + } catch (e) { + log('Error while processing mermaid syntax: $e'); + return null; + } finally { + if (await tempDir.exists()) { + await tempDir.delete(recursive: true); + } + } + } +} diff --git a/packages/superdeck_cli/lib/src/tasks/slide_thumbnail_task.dart b/packages/superdeck_cli/lib/src/tasks/slide_thumbnail_task.dart new file mode 100644 index 00000000..55892839 --- /dev/null +++ b/packages/superdeck_cli/lib/src/tasks/slide_thumbnail_task.dart @@ -0,0 +1,20 @@ +import 'dart:async'; + +import 'package:superdeck_cli/src/slides_pipeline.dart'; + +/// This task marks the thumbnail file as needed if it exists. +/// The goal is to ensure that any generated thumbnails are kept. +class SlideThumbnailTask extends Task { + const SlideThumbnailTask() : super('thumbnail'); + + @override + FutureOr run(controller) async { + final file = buildAssetFile(controller.slide.key + '.png'); + + if (await file.exists()) { + await controller.markFileAsNeeded(file); + } + + return controller; + } +} diff --git a/packages/superdeck_cli/lib/superdeck_cli.dart b/packages/superdeck_cli/lib/superdeck_cli.dart new file mode 100644 index 00000000..fb22aa63 --- /dev/null +++ b/packages/superdeck_cli/lib/superdeck_cli.dart @@ -0,0 +1 @@ +export 'package:superdeck_cli/src/slides_loader.dart' show SlidesLoader; diff --git a/packages/superdeck_cli/pubspec.yaml b/packages/superdeck_cli/pubspec.yaml new file mode 100644 index 00000000..cb2b800e --- /dev/null +++ b/packages/superdeck_cli/pubspec.yaml @@ -0,0 +1,23 @@ +name: superdeck_cli +description: A starting point for Dart libraries or applications. +version: 0.0.1 +# repository: https://github.com/my_org/my_repo + +environment: + sdk: ">=3.3.0 <4.0.0" + +dependencies: + yaml: ^3.1.2 + args: ^2.5.0 + path: ^1.9.0 + collection: ^1.18.0 + image: ^4.2.0 + watcher: ^1.1.0 + dart_style: ^2.3.6 + yaml_writer: ^2.0.0 + http: ^1.2.2 + + +dev_dependencies: + lints: ^4.0.0 + test: ^1.25.8 diff --git a/packages/superdeck_cli/slides.md b/packages/superdeck_cli/slides.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/superdeck_cli/test/src/helpers/extensions_test.dart b/packages/superdeck_cli/test/src/helpers/extensions_test.dart new file mode 100644 index 00000000..49cd8a5f --- /dev/null +++ b/packages/superdeck_cli/test/src/helpers/extensions_test.dart @@ -0,0 +1,68 @@ +import 'dart:io'; + +import 'package:superdeck_cli/src/helpers/extensions.dart'; +import 'package:test/test.dart'; + +void main() { + group('FileExt', () { + late File file; + late Directory tempDir; + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp(); + file = File('${tempDir.path}/test.txt'); + }); + + tearDown(() async { + await tempDir.delete(recursive: true); + }); + + test('ensureWrite creates file if it does not exist', () async { + await file.ensureWrite('test content'); + expect(await file.exists(), isTrue); + expect(await file.readAsString(), 'test content'); + }); + + test('ensureWrite overwrites existing file', () async { + await file.writeAsString('old content'); + await file.ensureWrite('new content'); + expect(await file.readAsString(), 'new content'); + }); + + test('ensureExists creates file if it does not exist', () async { + await file.ensureExists(); + expect(await file.exists(), isTrue); + }); + + test('ensureExists does not modify existing file', () async { + await file.writeAsString('test content'); + await file.ensureExists(); + expect(await file.readAsString(), 'test content'); + }); + }); + + group('DirectoryExt', () { + late Directory dir; + late Directory tempDir; + + setUp(() async { + tempDir = await Directory.systemTemp.createTemp(); + dir = Directory('${tempDir.path}/test_dir'); + }); + + tearDown(() async { + await tempDir.delete(recursive: true); + }); + + test('ensureExists creates directory if it does not exist', () async { + await dir.ensureExists(); + expect(await dir.exists(), isTrue); + }); + + test('ensureExists does not modify existing directory', () async { + await dir.create(); + await dir.ensureExists(); + expect(await dir.exists(), isTrue); + }); + }); +} diff --git a/packages/superdeck_cli/test/src/helpers/short_hash_id_test.dart b/packages/superdeck_cli/test/src/helpers/short_hash_id_test.dart new file mode 100644 index 00000000..cecd8561 --- /dev/null +++ b/packages/superdeck_cli/test/src/helpers/short_hash_id_test.dart @@ -0,0 +1,58 @@ +import 'package:superdeck_cli/src/helpers/short_hash_id.dart'; +import 'package:test/test.dart'; + +void main() { + group('shortHashId', () { + test('generates unique hash for different input strings', () { + String input1 = 'hello world'; + String input2 = 'hello world!'; + + String hash1 = shortHashId(input1); + String hash2 = shortHashId(input2); + + expect(hash1, isNot(equals(hash2))); + }); + + test('generates same hash for same input string', () { + String input = 'test input'; + + String hash1 = shortHashId(input); + String hash2 = shortHashId(input); + + expect(hash1, equals(hash2)); + }); + + test('generates 8-character hash', () { + String input = 'some long input string'; + + String hash = shortHashId(input); + + expect(hash.length, equals(8)); + }); + + test('generates hash with valid characters', () { + String input = 'another input'; + + String hash = shortHashId(input); + + expect(hash, matches(RegExp(r'^[a-zA-Z0-9]{8}$'))); + }); + + test('handles empty input string', () { + String input = ''; + + String hash = shortHashId(input); + + expect(hash.length, equals(8)); + }); + + test('handles input string with unsupported characters', () { + String input = 'input with spaces and !@#\$%^&*()'; + + String hash = shortHashId(input); + + expect(hash.length, equals(8)); + expect(hash, matches(RegExp(r'^[a-zA-Z0-9]{8}$'))); + }); + }); +} diff --git a/pubspec.lock b/pubspec.lock index 951acc18..ba8915f4 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,54 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: - _fe_analyzer_shared: + ansi_styles: dependency: transitive description: - name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + name: ansi_styles + sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a" url: "https://pub.dev" source: hosted - version: "67.0.0" - analyzer: - dependency: transitive - description: - name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" - url: "https://pub.dev" - source: hosted - version: "6.4.1" - analyzer_plugin: - dependency: transitive - description: - name: analyzer_plugin - sha256: "9661b30b13a685efaee9f02e5d01ed9f2b423bd889d28a304d02d704aee69161" - url: "https://pub.dev" - source: hosted - version: "0.11.3" - animate_do: - dependency: "direct main" - description: - name: animate_do - sha256: "7a3162729f0ea042f9dd84da217c5bde5472ad9cef644079929d4304a5dc4ca0" - url: "https://pub.dev" - source: hosted - version: "3.3.4" - ansicolor: - dependency: transitive - description: - name: ansicolor - sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - archive: - dependency: transitive - description: - name: archive - sha256: ecf4273855368121b1caed0d10d4513c7241dfc813f7d3c8933b36622ae9b265 - url: "https://pub.dev" - source: hosted - version: "3.5.1" + version: "0.3.2+1" args: dependency: transitive description: @@ -65,22 +25,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.11.0" - barcode: - dependency: transitive - description: - name: barcode - sha256: ab180ce22c6555d77d45f0178a523669db67f95856e3378259ef2ffeb43e6003 - url: "https://pub.dev" - source: hosted - version: "2.2.8" - bidi: - dependency: transitive - description: - name: bidi - sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63" - url: "https://pub.dev" - source: hosted - version: "2.0.10" boolean_selector: dependency: transitive description: @@ -89,110 +33,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.1" - build: - dependency: transitive - description: - name: build - sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0" - url: "https://pub.dev" - source: hosted - version: "2.4.1" - build_config: - dependency: transitive - description: - name: build_config - sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1 - url: "https://pub.dev" - source: hosted - version: "1.1.1" - build_daemon: - dependency: transitive - description: - name: build_daemon - sha256: "0343061a33da9c5810b2d6cee51945127d8f4c060b7fbdd9d54917f0a3feaaa1" - url: "https://pub.dev" - source: hosted - version: "4.0.1" - build_resolvers: - dependency: transitive - description: - name: build_resolvers - sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a" - url: "https://pub.dev" - source: hosted - version: "2.4.2" - build_runner: - dependency: "direct dev" - description: - name: build_runner - sha256: "1414d6d733a85d8ad2f1dfcb3ea7945759e35a123cb99ccfac75d0758f75edfa" - url: "https://pub.dev" - source: hosted - version: "2.4.10" - build_runner_core: - dependency: transitive - description: - name: build_runner_core - sha256: "4ae8ffe5ac758da294ecf1802f2aff01558d8b1b00616aa7538ea9a8a5d50799" - url: "https://pub.dev" - source: hosted - version: "7.3.0" - build_verify: - dependency: "direct dev" - description: - name: build_verify - sha256: abbb9b9eda076854ac1678d284c053a5ec608e64da741d0801f56d4bbea27e23 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - built_collection: - dependency: transitive - description: - name: built_collection - sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" - source: hosted - version: "5.1.1" - built_value: - dependency: transitive - description: - name: built_value - sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb - url: "https://pub.dev" - source: hosted - version: "8.9.2" - cached_network_image: - dependency: "direct main" - description: - name: cached_network_image - sha256: "28ea9690a8207179c319965c13cd8df184d5ee721ae2ce60f398ced1219cea1f" - url: "https://pub.dev" - source: hosted - version: "3.3.1" - cached_network_image_platform_interface: - dependency: transitive - description: - name: cached_network_image_platform_interface - sha256: "9e90e78ae72caa874a323d78fa6301b3fb8fa7ea76a8f96dc5b5bf79f283bf2f" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - cached_network_image_web: - dependency: transitive - description: - name: cached_network_image_web - sha256: "205d6a9f1862de34b93184f22b9d2d94586b2f05c581d546695e3d8f6a805cd7" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - characters: - dependency: transitive - description: - name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" - url: "https://pub.dev" - source: hosted - version: "1.3.0" charcode: dependency: transitive description: @@ -201,22 +41,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.1" - checked_yaml: - dependency: transitive - description: - name: checked_yaml - sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff - url: "https://pub.dev" - source: hosted - version: "2.0.3" - ci: + cli_launcher: dependency: transitive description: - name: ci - sha256: "145d095ce05cddac4d797a158bc4cf3b6016d1fe63d8c3d2fbd7212590adca13" + name: cli_launcher + sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005" url: "https://pub.dev" source: hosted - version: "0.1.0" + version: "0.3.1" cli_util: dependency: transitive description: @@ -233,134 +65,22 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.1" - code_builder: - dependency: transitive - description: - name: code_builder - sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37 - url: "https://pub.dev" - source: hosted - version: "4.10.0" collection: - dependency: "direct main" - description: - name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a - url: "https://pub.dev" - source: hosted - version: "1.18.0" - convert: - dependency: transitive - description: - name: convert - sha256: "0f08b14755d163f6e2134cb58222dd25ea2a2ee8a195e53983d57c075324d592" - url: "https://pub.dev" - source: hosted - version: "3.1.1" - coverage: - dependency: transitive - description: - name: coverage - sha256: "3945034e86ea203af7a056d98e98e42a5518fff200d6e8e6647e1886b07e936e" - url: "https://pub.dev" - source: hosted - version: "1.8.0" - cross_file: - dependency: transitive - description: - name: cross_file - sha256: "55d7b444feb71301ef6b8838dbc1ae02e63dd48c8773f3810ff53bb1e2945b32" - url: "https://pub.dev" - source: hosted - version: "0.3.4+1" - crypto: - dependency: transitive - description: - name: crypto - sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab - url: "https://pub.dev" - source: hosted - version: "3.0.3" - csslib: - dependency: transitive - description: - name: csslib - sha256: "706b5707578e0c1b4b7550f64078f0a0f19dec3f50a178ffae7006b0a9ca58fb" - url: "https://pub.dev" - source: hosted - version: "1.0.0" - custom_lint: - dependency: "direct dev" - description: - name: custom_lint - sha256: "7c0aec12df22f9082146c354692056677f1e70bc43471644d1fdb36c6fdda799" - url: "https://pub.dev" - source: hosted - version: "0.6.4" - custom_lint_builder: dependency: transitive description: - name: custom_lint_builder - sha256: d7dc41e709dde223806660268678be7993559e523eb3164e2a1425fd6f7615a9 - url: "https://pub.dev" - source: hosted - version: "0.6.4" - custom_lint_core: - dependency: transitive - description: - name: custom_lint_core - sha256: a85e8f78f4c52f6c63cdaf8c872eb573db0231dcdf3c3a5906d493c1f8bc20e6 - url: "https://pub.dev" - source: hosted - version: "0.6.3" - dart_mappable: - dependency: "direct main" - description: - name: dart_mappable - sha256: "47269caf2060533c29b823ff7fa9706502355ffcb61e7f2a374e3a0fb2f2c3f0" - url: "https://pub.dev" - source: hosted - version: "4.2.2" - dart_mappable_builder: - dependency: "direct dev" - description: - name: dart_mappable_builder - sha256: ab5cf9086862d3fceb9773e945b5f95cc5471a28c782a4fc451bd400a4e0c64e - url: "https://pub.dev" - source: hosted - version: "4.2.3" - dart_markdown: - dependency: "direct main" - description: - name: dart_markdown - sha256: b9b7e13733dd418bc83e6f4e68d79caebab6cd3a8b301e91d1bfd6ef5ef8e993 - url: "https://pub.dev" - source: hosted - version: "3.1.7" - dart_style: - dependency: transitive - description: - name: dart_style - sha256: "99e066ce75c89d6b29903d788a7bb9369cf754f7b24bf70bf4b6d6d6b26853b9" + name: collection + sha256: a1ace0a119f20aabc852d165077c036cd864315bd99b7eaa10a60100341941bf url: "https://pub.dev" source: hosted - version: "2.3.6" - fake_async: + version: "1.19.0" + conventional_commit: dependency: transitive description: - name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + name: conventional_commit + sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2 url: "https://pub.dev" source: hosted - version: "1.3.1" - ffi: - dependency: transitive - description: - name: ffi - sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21" - url: "https://pub.dev" - source: hosted - version: "2.1.2" + version: "0.6.0+1" file: dependency: transitive description: @@ -369,77 +89,6 @@ packages: url: "https://pub.dev" source: hosted version: "7.0.0" - file_picker: - dependency: "direct main" - description: - name: file_picker - sha256: "29c90806ac5f5fb896547720b73b17ee9aed9bba540dc5d91fe29f8c5745b10a" - url: "https://pub.dev" - source: hosted - version: "8.0.3" - fixnum: - dependency: transitive - description: - name: fixnum - sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1" - url: "https://pub.dev" - source: hosted - version: "1.1.0" - flutter: - dependency: "direct main" - description: flutter - source: sdk - version: "0.0.0" - flutter_cache_manager: - dependency: transitive - description: - name: flutter_cache_manager - sha256: "395d6b7831f21f3b989ebedbb785545932adb9afe2622c1ffacf7f4b53a7e544" - url: "https://pub.dev" - source: hosted - version: "3.3.2" - flutter_lints: - dependency: "direct dev" - description: - name: flutter_lints - sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - flutter_plugin_android_lifecycle: - dependency: transitive - description: - name: flutter_plugin_android_lifecycle - sha256: "8cf40eebf5dec866a6d1956ad7b4f7016e6c0cc69847ab946833b7d43743809f" - url: "https://pub.dev" - source: hosted - version: "2.0.19" - flutter_test: - dependency: "direct dev" - description: flutter - source: sdk - version: "0.0.0" - flutter_web_plugins: - dependency: transitive - description: flutter - source: sdk - version: "0.0.0" - freezed_annotation: - dependency: transitive - description: - name: freezed_annotation - sha256: f9f6597ac43cc262fa7d7f2e65259a6060c23a560525d1f2631be374540f2a9b - url: "https://pub.dev" - source: hosted - version: "2.4.3" - frontend_server_client: - dependency: transitive - description: - name: frontend_server_client - sha256: "408e3ca148b31c20282ad6f37ebfa6f4bdc8fede5b74bc2f08d9d92b55db3612" - url: "https://pub.dev" - source: hosted - version: "3.2.0" glob: dependency: transitive description: @@ -448,70 +97,38 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - go_router: - dependency: "direct main" - description: - name: go_router - sha256: abec47eb8c8c36ebf41d0a4c64dbbe7f956e39a012b3aafc530e951bdc12fe3f - url: "https://pub.dev" - source: hosted - version: "14.1.4" graphs: dependency: transitive description: name: graphs - sha256: aedc5a15e78fc65a6e23bcd927f24c64dd995062bcd1ca6eda65a3cff92a4d19 + sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" url: "https://pub.dev" source: hosted - version: "2.3.1" - hotreloader: - dependency: transitive - description: - name: hotreloader - sha256: ed56fdc1f3a8ac924e717257621d09e9ec20e308ab6352a73a50a1d7a4d9158e - url: "https://pub.dev" - source: hosted - version: "4.2.0" - html: - dependency: transitive - description: - name: html - sha256: "3a7812d5bcd2894edf53dfaf8cd640876cf6cef50a8f238745c8b8120ea74d3a" - url: "https://pub.dev" - source: hosted - version: "0.15.4" + version: "2.3.2" http: dependency: transitive description: name: http - sha256: "761a297c042deedc1ffbb156d6e2af13886bb305c2a343a4d972504cd67dd938" + sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted - version: "1.2.1" - http_multi_server: - dependency: transitive - description: - name: http_multi_server - sha256: "97486f20f9c2f7be8f514851703d0119c3596d14ea63227af6f7a481ef2b2f8b" - url: "https://pub.dev" - source: hosted - version: "3.2.1" + version: "1.2.2" http_parser: dependency: transitive description: name: http_parser - sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" + sha256: "40f592dd352890c3b60fec1b68e786cefb9603e05ff303dbc4dda49b304ecdf4" url: "https://pub.dev" source: hosted - version: "4.0.2" - image: + version: "4.1.0" + intl: dependency: transitive description: - name: image - sha256: "4c68bfd5ae83e700b5204c1e74451e7bf3cf750e6843c6e158289cf56bda018e" + name: intl + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "4.1.7" + version: "0.19.0" io: dependency: transitive description: @@ -520,14 +137,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.0.4" - js: - dependency: transitive - description: - name: js - sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3 - url: "https://pub.dev" - source: hosted - version: "0.6.7" json_annotation: dependency: transitive description: @@ -536,62 +145,6 @@ packages: url: "https://pub.dev" source: hosted version: "4.9.0" - leak_tracker: - dependency: transitive - description: - name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" - url: "https://pub.dev" - source: hosted - version: "10.0.4" - leak_tracker_flutter_testing: - dependency: transitive - description: - name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" - url: "https://pub.dev" - source: hosted - version: "3.0.3" - leak_tracker_testing: - dependency: transitive - description: - name: leak_tracker_testing - sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - lints: - dependency: transitive - description: - name: lints - sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" - url: "https://pub.dev" - source: hosted - version: "4.0.0" - localstorage: - dependency: "direct main" - description: - name: localstorage - sha256: "6340acefdd3a969cceb044a69cde2dc5877c5b861b2e02d0803930ed483dbe91" - url: "https://pub.dev" - source: hosted - version: "5.0.0" - logging: - dependency: transitive - description: - name: logging - sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - markdown_viewer: - dependency: "direct main" - description: - name: markdown_viewer - sha256: e819ea779eaeca933bb38228e02888ad72409aba922efb2e65abb2f8b046dc8e - url: "https://pub.dev" - source: hosted - version: "0.6.2" matcher: dependency: transitive description: @@ -600,179 +153,46 @@ packages: url: "https://pub.dev" source: hosted version: "0.12.16+1" - material_color_utilities: - dependency: transitive + melos: + dependency: "direct dev" description: - name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + name: melos + sha256: a3f06ed871e0348cb99909ad5ddf5f8b53cc61d894c302b5417d2db1ee7ec381 url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "6.1.0" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted - version: "1.12.0" - mime: + version: "1.15.0" + mustache_template: dependency: transitive description: - name: mime - sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2" - url: "https://pub.dev" - source: hosted - version: "1.0.5" - mix: - dependency: "direct main" - description: - path: "../mix/packages/mix" - relative: true - source: path - version: "1.3.0" - mix_annotations: - dependency: "direct main" - description: - path: "../mix/packages/mix_annotations" - relative: true - source: path - version: "0.2.0+1" - mix_generator: - dependency: "direct dev" - description: - path: "../mix/packages/mix_generator" - relative: true - source: path - version: "0.2.1" - mix_lint: - dependency: "direct dev" - description: - name: mix_lint - sha256: "2f684dc18e46341a25f3e2dff46355d11746866bf70bdc99a0424809a3345883" - url: "https://pub.dev" - source: hosted - version: "0.1.0" - node_preamble: - dependency: transitive - description: - name: node_preamble - sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db" - url: "https://pub.dev" - source: hosted - version: "2.0.2" - octo_image: - dependency: transitive - description: - name: octo_image - sha256: "45b40f99622f11901238e18d48f5f12ea36426d8eced9f4cbf58479c7aa2430d" + name: mustache_template + sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c url: "https://pub.dev" source: hosted version: "2.0.0" - package_config: - dependency: transitive - description: - name: package_config - sha256: "1c5b77ccc91e4823a5af61ee74e6b972db1ef98c2ff5a18d3161c982a55448bd" - url: "https://pub.dev" - source: hosted - version: "2.1.0" path: - dependency: "direct main" + dependency: transitive description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted version: "1.9.0" - path_parsing: - dependency: transitive - description: - name: path_parsing - sha256: e3e67b1629e6f7e8100b367d3db6ba6af4b1f0bb80f64db18ef1fbabd2fa9ccf - url: "https://pub.dev" - source: hosted - version: "1.0.1" - path_provider: - dependency: "direct main" - description: - name: path_provider - sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 - url: "https://pub.dev" - source: hosted - version: "2.1.3" - path_provider_android: - dependency: transitive - description: - name: path_provider_android - sha256: a248d8146ee5983446bf03ed5ea8f6533129a12b11f12057ad1b4a67a2b3b41d - url: "https://pub.dev" - source: hosted - version: "2.2.4" - path_provider_foundation: - dependency: transitive - description: - name: path_provider_foundation - sha256: "5a7999be66e000916500be4f15a3633ebceb8302719b47b9cc49ce924125350f" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - path_provider_linux: - dependency: transitive - description: - name: path_provider_linux - sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" - source: hosted - version: "2.2.1" - path_provider_platform_interface: - dependency: transitive - description: - name: path_provider_platform_interface - sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" - source: hosted - version: "2.1.2" - path_provider_windows: - dependency: transitive - description: - name: path_provider_windows - sha256: "8bc9f22eee8690981c22aa7fc602f5c85b497a6fb2ceb35ee5a5e5ed85ad8170" - url: "https://pub.dev" - source: hosted - version: "2.2.1" - pdf: - dependency: "direct main" - description: - name: pdf - sha256: "243f05342fc0bdf140eba5b069398985cdbdd3dbb1d776cf43d5ea29cc570ba6" - url: "https://pub.dev" - source: hosted - version: "3.10.8" - petitparser: - dependency: transitive - description: - name: petitparser - sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27 - url: "https://pub.dev" - source: hosted - version: "6.0.2" platform: dependency: transitive description: name: platform - sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec" - url: "https://pub.dev" - source: hosted - version: "3.1.4" - plugin_platform_interface: - dependency: transitive - description: - name: plugin_platform_interface - sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" + sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted - version: "2.1.8" + version: "3.1.5" pool: dependency: transitive description: @@ -781,147 +201,54 @@ packages: url: "https://pub.dev" source: hosted version: "1.5.1" - pub_semver: + process: dependency: transitive description: - name: pub_semver - sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" + name: process + sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32" url: "https://pub.dev" source: hosted - version: "2.1.4" - pubspec_parse: + version: "5.0.2" + prompts: dependency: transitive description: - name: pubspec_parse - sha256: c63b2876e58e194e4b0828fcb080ad0e06d051cb607a6be51a9e084f47cb9367 + name: prompts + sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1" url: "https://pub.dev" source: hosted - version: "1.2.3" - qr: - dependency: transitive - description: - name: qr - sha256: "64957a3930367bf97cc211a5af99551d630f2f4625e38af10edd6b19131b64b3" - url: "https://pub.dev" - source: hosted - version: "3.0.1" - recase: - dependency: "direct main" - description: - name: recase - sha256: e4eb4ec2dcdee52dcf99cb4ceabaffc631d7424ee55e56f280bc039737f89213 - url: "https://pub.dev" - source: hosted - version: "4.1.0" - rxdart: - dependency: transitive - description: - name: rxdart - sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb" - url: "https://pub.dev" - source: hosted - version: "0.27.7" - screen_retriever: - dependency: transitive - description: - name: screen_retriever - sha256: "6ee02c8a1158e6dae7ca430da79436e3b1c9563c8cf02f524af997c201ac2b90" - url: "https://pub.dev" - source: hosted - version: "0.1.9" - scrollable_positioned_list: - dependency: "direct main" - description: - name: scrollable_positioned_list - sha256: "1b54d5f1329a1e263269abc9e2543d90806131aa14fe7c6062a8054d57249287" - url: "https://pub.dev" - source: hosted - version: "0.3.8" - shelf: - dependency: transitive - description: - name: shelf - sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4 - url: "https://pub.dev" - source: hosted - version: "1.4.1" - shelf_packages_handler: - dependency: transitive - description: - name: shelf_packages_handler - sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e" - url: "https://pub.dev" - source: hosted - version: "3.0.2" - shelf_static: - dependency: transitive - description: - name: shelf_static - sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e - url: "https://pub.dev" - source: hosted - version: "1.1.2" - shelf_web_socket: - dependency: transitive - description: - name: shelf_web_socket - sha256: "9ca081be41c60190ebcb4766b2486a7d50261db7bd0f5d9615f2d653637a84c1" - url: "https://pub.dev" - source: hosted - version: "1.0.4" - signals: - dependency: "direct main" - description: - name: signals - sha256: "38052d9107b4ae54b6b47a0e4625f31e6b4486decc8d92f113b3b557de6d4767" - url: "https://pub.dev" - source: hosted - version: "5.2.1" - signals_core: - dependency: transitive - description: - name: signals_core - sha256: e34d9e3a5125f2063a6a3817729961cf948ca8cfdd66b6036c1c8c48735d2dda - url: "https://pub.dev" - source: hosted - version: "5.2.0" - signals_flutter: + version: "2.0.0" + pub_semver: dependency: transitive description: - name: signals_flutter - sha256: d37006c8dc92e86aa80900d3d1eeb1ba62ae7bd67378d8cbea187c2730ad090d + name: pub_semver + sha256: "40d3ab1bbd474c4c2328c91e3a7df8c6dd629b79ece4c4bd04bee496a224fb0c" url: "https://pub.dev" source: hosted - version: "5.2.1" - sky_engine: - dependency: transitive - description: flutter - source: sdk - version: "0.0.99" - source_gen: + version: "2.1.4" + pub_updater: dependency: transitive description: - name: source_gen - sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832" + name: pub_updater + sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60" url: "https://pub.dev" source: hosted - version: "1.5.0" - source_map_stack_trace: + version: "0.4.0" + pubspec: dependency: transitive description: - name: source_map_stack_trace - sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae" + name: pubspec + sha256: f534a50a2b4d48dc3bc0ec147c8bd7c304280fff23b153f3f11803c4d49d927e url: "https://pub.dev" source: hosted - version: "2.1.1" - source_maps: + version: "2.3.0" + quiver: dependency: transitive description: - name: source_maps - sha256: "708b3f6b97248e5781f493b765c3337db11c5d2c81c3094f10904bfa8004c703" + name: quiver + sha256: b1c1ac5ce6688d77f65f3375a9abb9319b3cb32486bdc7a1e0fdf004d7ba4e47 url: "https://pub.dev" source: hosted - version: "0.10.12" + version: "3.2.1" source_span: dependency: transitive description: @@ -930,30 +257,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.10.0" - sprintf: - dependency: transitive - description: - name: sprintf - sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23" - url: "https://pub.dev" - source: hosted - version: "7.0.0" - sqflite: - dependency: transitive - description: - name: sqflite - sha256: a43e5a27235518c03ca238e7b4732cf35eabe863a369ceba6cbefa537a66f16d - url: "https://pub.dev" - source: hosted - version: "2.3.3+1" - sqflite_common: - dependency: transitive - description: - name: sqflite_common - sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4" - url: "https://pub.dev" - source: hosted - version: "2.5.4" stack_trace: dependency: transitive description: @@ -970,38 +273,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" - stream_transform: - dependency: transitive - description: - name: stream_transform - sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f" - url: "https://pub.dev" - source: hosted - version: "2.1.0" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" - url: "https://pub.dev" - source: hosted - version: "1.2.0" - synchronized: - dependency: transitive - description: - name: synchronized - sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558" - url: "https://pub.dev" - source: hosted - version: "3.1.0+1" - syntax_highlight: - dependency: "direct main" - description: - name: syntax_highlight - sha256: ee33b6aa82cc722bb9b40152a792181dee222353b486c0255fde666a3e3a4997 + sha256: "688af5ed3402a4bde5b3a6c15fd768dbf2621a614950b17f04626c431ab3c4c3" url: "https://pub.dev" source: hosted - version: "0.4.0" + version: "1.3.0" term_glyph: dependency: transitive description: @@ -1010,46 +289,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.1" - test: - dependency: transitive - description: - name: test - sha256: "7ee446762c2c50b3bd4ea96fe13ffac69919352bd3b4b17bac3f3465edc58073" - url: "https://pub.dev" - source: hosted - version: "1.25.2" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" - url: "https://pub.dev" - source: hosted - version: "0.7.0" - test_core: - dependency: transitive - description: - name: test_core - sha256: "2bc4b4ecddd75309300d8096f781c0e3280ca1ef85beda558d33fcbedc2eead4" - url: "https://pub.dev" - source: hosted - version: "0.6.0" - timing: - dependency: transitive - description: - name: timing - sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32" + sha256: "664d3a9a64782fcdeb83ce9c6b39e78fd2971d4e37827b9b06c3aa1edc5e760c" url: "https://pub.dev" source: hosted - version: "1.0.1" - type_plus: - dependency: transitive - description: - name: type_plus - sha256: d5d1019471f0d38b91603adb9b5fd4ce7ab903c879d2fbf1a3f80a630a03fcc9 - url: "https://pub.dev" - source: hosted - version: "2.1.1" + version: "0.7.3" typed_data: dependency: transitive description: @@ -1058,182 +305,37 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.2" - universal_html: - dependency: "direct main" - description: - name: universal_html - sha256: "56536254004e24d9d8cfdb7dbbf09b74cf8df96729f38a2f5c238163e3d58971" - url: "https://pub.dev" - source: hosted - version: "2.2.4" - universal_io: - dependency: transitive - description: - name: universal_io - sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" - source: hosted - version: "2.2.2" - url_launcher: - dependency: "direct main" - description: - name: url_launcher - sha256: "6ce1e04375be4eed30548f10a315826fd933c1e493206eab82eed01f438c8d2e" - url: "https://pub.dev" - source: hosted - version: "6.2.6" - url_launcher_android: - dependency: transitive - description: - name: url_launcher_android - sha256: "360a6ed2027f18b73c8d98e159dda67a61b7f2e0f6ec26e86c3ada33b0621775" - url: "https://pub.dev" - source: hosted - version: "6.3.1" - url_launcher_ios: - dependency: transitive - description: - name: url_launcher_ios - sha256: "9149d493b075ed740901f3ee844a38a00b33116c7c5c10d7fb27df8987fb51d5" - url: "https://pub.dev" - source: hosted - version: "6.2.5" - url_launcher_linux: - dependency: transitive - description: - name: url_launcher_linux - sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - url_launcher_macos: - dependency: transitive - description: - name: url_launcher_macos - sha256: b7244901ea3cf489c5335bdacda07264a6e960b1c1b1a9f91e4bc371d9e68234 - url: "https://pub.dev" - source: hosted - version: "3.1.0" - url_launcher_platform_interface: - dependency: transitive - description: - name: url_launcher_platform_interface - sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029" - url: "https://pub.dev" - source: hosted - version: "2.3.2" - url_launcher_web: - dependency: transitive - description: - name: url_launcher_web - sha256: "8d9e750d8c9338601e709cd0885f95825086bd8b642547f26bda435aade95d8a" - url: "https://pub.dev" - source: hosted - version: "2.3.1" - url_launcher_windows: - dependency: transitive - description: - name: url_launcher_windows - sha256: ecf9725510600aa2bb6d7ddabe16357691b6d2805f66216a97d1b881e21beff7 - url: "https://pub.dev" - source: hosted - version: "3.1.1" - uuid: + uri: dependency: transitive description: - name: uuid - sha256: "814e9e88f21a176ae1359149021870e87f7cddaf633ab678a5d2b0bff7fd1ba8" + name: uri + sha256: "889eea21e953187c6099802b7b4cf5219ba8f3518f604a1033064d45b1b8268a" url: "https://pub.dev" source: hosted - version: "4.4.0" - vector_math: - dependency: transitive - description: - name: vector_math - sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" - url: "https://pub.dev" - source: hosted - version: "2.1.4" - vm_service: - dependency: transitive - description: - name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" - url: "https://pub.dev" - source: hosted - version: "14.2.1" - watcher: - dependency: "direct main" - description: - name: watcher - sha256: "3d2ad6751b3c16cf07c7fca317a1413b3f26530319181b37e3b9039b84fc01d8" - url: "https://pub.dev" - source: hosted - version: "1.1.0" + version: "1.0.0" web: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" - url: "https://pub.dev" - source: hosted - version: "0.5.1" - web_socket_channel: - dependency: transitive - description: - name: web_socket_channel - sha256: "58c6666b342a38816b2e7e50ed0f1e261959630becd4c879c4f26bfa14aa5a42" - url: "https://pub.dev" - source: hosted - version: "2.4.5" - webkit_inspection_protocol: - dependency: transitive - description: - name: webkit_inspection_protocol - sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572" - url: "https://pub.dev" - source: hosted - version: "1.2.1" - win32: - dependency: transitive - description: - name: win32 - sha256: "0eaf06e3446824099858367950a813472af675116bf63f008a4c2a75ae13e9cb" - url: "https://pub.dev" - source: hosted - version: "5.5.0" - window_manager: - dependency: "direct main" - description: - name: window_manager - sha256: "8699323b30da4cdbe2aa2e7c9de567a6abd8a97d9a5c850a3c86dcd0b34bbfbf" + sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062 url: "https://pub.dev" source: hosted - version: "0.3.9" - xdg_directories: + version: "1.0.0" + yaml: dependency: transitive description: - name: xdg_directories - sha256: faea9dee56b520b55a566385b84f2e8de55e7496104adada9962e0bd11bcff1d + name: yaml + sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" url: "https://pub.dev" source: hosted - version: "1.0.4" - xml: + version: "3.1.2" + yaml_edit: dependency: transitive description: - name: xml - sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 - url: "https://pub.dev" - source: hosted - version: "6.5.0" - yaml: - dependency: "direct main" - description: - name: yaml - sha256: "75769501ea3489fca56601ff33454fe45507ea3bfb014161abc3b43ae25989d5" + name: yaml_edit + sha256: e9c1a3543d2da0db3e90270dbb1e4eebc985ee5e3ffe468d83224472b2194a5f url: "https://pub.dev" source: hosted - version: "3.1.2" + version: "2.2.1" sdks: dart: ">=3.4.0 <4.0.0" - flutter: ">=3.19.0" diff --git a/pubspec.yaml b/pubspec.yaml index 8c56cc63..262e0518 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,51 +1,11 @@ name: superdeck -description: Presentation slides for Flutter with Flutter -version: 0.0.4 -homepage: https://github.com/leoafarias/superdeck environment: - sdk: ">=3.0.0 <4.0.0" + sdk: ">=3.3.0 <4.0.0" -dependencies: - flutter: - sdk: flutter - cached_network_image: ^3.3.1 - window_manager: ^0.3.9 - watcher: ^1.1.0 - collection: ^1.18.0 - path: ^1.9.0 - dart_mappable: ^4.2.2 - dart_markdown: ^3.1.7 - markdown_viewer: ^0.6.2 - yaml: ^3.1.2 - animate_do: ^3.3.4 - recase: ^4.1.0 - syntax_highlight: ^0.4.0 - signals: ^5.2.1 - scrollable_positioned_list: ^0.3.8 - pdf: ^3.10.8 - localstorage: ^5.0.0 - go_router: ^14.1.4 - file_picker: ^8.0.3 - universal_html: ^2.2.4 - path_provider: ^2.1.3 - url_launcher: ^6.2.6 - mix: ^1.2.0 - mix_annotations: ^0.2.0 - - dev_dependencies: - flutter_test: - sdk: flutter - flutter_lints: ^4.0.0 - build_runner: ^2.4.10 - dart_mappable_builder: ^4.2.3 - build_verify: ^3.1.0 - mix_generator: ^0.2.0 - custom_lint: ^0.6.4 - mix_lint: ^0.1.0 - - -flutter: - assets: - - grammars/ \ No newline at end of file + melos: ^6.1.0 + + +lint_staged: + '**.dart': dart fix --apply && dcm fix diff --git a/pubspec_overrides.yaml b/pubspec_overrides.yaml deleted file mode 100644 index 8fe29f21..00000000 --- a/pubspec_overrides.yaml +++ /dev/null @@ -1,7 +0,0 @@ -dependency_overrides: - mix: - path: ../mix/packages/mix - mix_generator: - path: ../mix/packages/mix_generator - mix_annotations: - path: ../mix/packages/mix_annotations diff --git a/superdeck.code-workspace b/superdeck.code-workspace new file mode 100644 index 00000000..cdf01b27 --- /dev/null +++ b/superdeck.code-workspace @@ -0,0 +1,38 @@ +{ + "folders": [ + { + "name": "root", + "path": "." + }, + { + "name": "superdeck", + "path": "packages/superdeck" + }, + { + "name": "superdeck_cli", + "path": "packages/superdeck_cli" + }, + ], + "settings": { + "workbench.colorCustomizations": { + "activityBar.activeBackground": "#3c0b66", + "activityBar.background": "#3c0b66", + "activityBar.foreground": "#e7e7e7", + "activityBar.inactiveForeground": "#e7e7e799", + "activityBarBadge.background": "#905510", + "activityBarBadge.foreground": "#e7e7e7", + "commandCenter.border": "#e7e7e799", + "sash.hoverBorder": "#3c0b66", + "statusBar.background": "#210638", + "statusBar.foreground": "#e7e7e7", + "statusBarItem.hoverBackground": "#3c0b66", + "statusBarItem.remoteBackground": "#210638", + "statusBarItem.remoteForeground": "#e7e7e7", + "titleBar.activeBackground": "#210638", + "titleBar.activeForeground": "#e7e7e7", + "titleBar.inactiveBackground": "#21063899", + "titleBar.inactiveForeground": "#e7e7e799" + }, + "peacock.color": "#210638" + } +} \ No newline at end of file diff --git a/test/builder/slides_loader_test.dart b/test/builder/slides_loader_test.dart deleted file mode 100644 index b46850b3..00000000 --- a/test/builder/slides_loader_test.dart +++ /dev/null @@ -1,14 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:superdeck/builder/slides_loader.dart'; - -void main() { - group( - SlidesLoader, - () { - test( - '', - () async {}, - ); - }, - ); -} diff --git a/test/helpers/utils_test.dart b/test/helpers/utils_test.dart deleted file mode 100644 index 3306d922..00000000 --- a/test/helpers/utils_test.dart +++ /dev/null @@ -1,151 +0,0 @@ -import 'package:flutter/foundation.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:superdeck/helpers/utils.dart'; - -void main() { - group( - 'Util tests', - () { - test('shortHashId generates consistent short IDs', () { - const input1 = 'Hello, World!'; - const expectedOutput1 = 'zUZHawvl'; - expect(shortHashId(input1), equals(expectedOutput1)); - - const input2 = 'Flutter is awesome'; - const expectedOutput2 = 'NQZU1Szg'; - expect(shortHashId(input2), equals(expectedOutput2)); - - const input3 = 'Dart is fun'; - const expectedOutput3 = 'YDepxypq'; - expect(shortHashId(input3), equals(expectedOutput3)); - - const input4 = 'SuperDecks'; - const expectedOutput4 = '84Zl5kOM'; - expect(shortHashId(input4), equals(expectedOutput4)); - - const input5 = 'Short ID Generator'; - const expectedOutput5 = 'ymnjxlmN'; - expect(shortHashId(input5), equals(expectedOutput5)); - - const input6 = 'https://www.google.com'; - const expectedOutput6 = 'LxeeFu6H'; - expect(shortHashId(input6), equals(expectedOutput6)); - - const input7 = 'https://some.domain.com/image.jpg'; - const expectedOutput7 = 'M1wJvIQn'; - expect(shortHashId(input7), equals(expectedOutput7)); - - const input8 = 'https://www.asdf.com/asdf?v=1234567890'; - const expectedOutput8 = 'WhQYB0uJ'; - expect(shortHashId(input8), equals(expectedOutput8)); - - const input9 = 'asdfasdfasdfasdfasdfasdf'; - const expectedOutput9 = '0K3ABgP0'; - expect(shortHashId(input9), equals(expectedOutput9)); - - const input10 = '&%*43;,.asdfasdf.INFINITY>;;`122190-+*/=-+'; - const expectedOutput10 = 'jzYRk5Oj'; - expect(shortHashId(input10), equals(expectedOutput10)); - }); - }, - ); - - group('converYamlToMap', () { - test('converts YAML string to Map correctly', () { - const yamlString = ''' -name: John Doe -age: 30 -city: New York -'''; - final expectedMap = { - 'name': 'John Doe', - 'age': 30, - 'city': 'New York', - }; - expect(converYamlToMap(yamlString), equals(expectedMap)); - }); - - test('returns empty Map for empty YAML string', () { - const yamlString = ''; - expect(converYamlToMap(yamlString), equals({})); - }); - - test('returns empty Map for null YAML string', () { - const yamlString = ''; - expect(converYamlToMap(yamlString), equals({})); - }); - }); - - group('prettyJson', () { - test('formats JSON string with indentation', () { - final json = { - 'name': 'John Doe', - 'age': 30, - 'city': 'New York', - 'hobbies': ['reading', 'traveling'], - }; - const expectedOutput = ''' -{ - "name": "John Doe", - "age": 30, - "city": "New York", - "hobbies": [ - "reading", - "traveling" - ] -}'''; - expect(prettyJson(json), equals(expectedOutput)); - }); - - test('returns empty string for empty JSON', () { - final json = {}; - expect(prettyJson(json), equals('{}')); - }); - - test('returns "null" string for null JSON', () { - const json = null; - expect(prettyJson(json), equals('null')); - }); - }); - - group('compareListChanges', () { - test('identifies added and removed items correctly', () { - final oldList = [1, 2, 3, 4]; - final newList = [2, 3, 4, 5]; - final expectedResult = ( - added: [5], - removed: [1], - ); - final actualResults = compareListChanges(oldList, newList); - expect(listEquals(actualResults.added, expectedResult.added), true); - expect(listEquals(actualResults.removed, expectedResult.removed), true); - }); - - test('returns empty lists when no changes', () { - final oldList = [1, 2, 3]; - final newList = [1, 2, 3]; - final expectedResult = ( - added: [], - removed: [], - ); - final actualResults = compareListChanges(oldList, newList); - - expect(listEquals(actualResults.added, expectedResult.added), true); - expect(listEquals(actualResults.removed, expectedResult.removed), true); - }); - - test('handles empty lists correctly', () { - final oldList = []; - final newList = [1, 2, 3]; - final expectedResult = ( - added: [1, 2, 3], - removed: [], - ); - - final actualResults = compareListChanges(oldList, newList); - - expect(listEquals(actualResults.added, expectedResult.added), true); - expect(listEquals(actualResults.removed, expectedResult.removed), true); - }); - }); -}