diff --git a/.flutter-plugins b/.flutter-plugins index 20500c8..57664ce 100644 --- a/.flutter-plugins +++ b/.flutter-plugins @@ -1,7 +1,7 @@ # This is a generated file; do not edit or check into version control. file_picker=/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/ flutter_plugin_android_lifecycle=/Users/leofarias/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.19/ -path_provider=/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider-2.1.2/ +path_provider=/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider-2.1.3/ path_provider_android=/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/ path_provider_foundation=/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/ path_provider_linux=/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/ diff --git a/.flutter-plugins-dependencies b/.flutter-plugins-dependencies index fd58e60..d5e656d 100644 --- a/.flutter-plugins-dependencies +++ b/.flutter-plugins-dependencies @@ -1 +1 @@ -{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.3.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.19/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.1/","native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"path_provider_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.3.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"linux":[{"name":"path_provider_linux","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"windows":[{"name":"path_provider_windows","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"web":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/","dependencies":[]}]},"dependencyGraph":[{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"screen_retriever","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":[]},{"name":"window_manager","dependencies":["screen_retriever"]}],"date_created":"2024-04-19 15:56:07.666631","version":"3.19.5"} \ No newline at end of file +{"info":"This is a generated file; do not edit or check into version control.","plugins":{"ios":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","native_build":true,"dependencies":[]},{"name":"path_provider_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.3.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]}],"android":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","native_build":true,"dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/flutter_plugin_android_lifecycle-2.0.19/","native_build":true,"dependencies":[]},{"name":"path_provider_android","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_android-2.2.2/","native_build":true,"dependencies":[]},{"name":"shared_preferences_android","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_android-2.2.1/","native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","native_build":true,"dependencies":[]}],"macos":[{"name":"path_provider_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_foundation-2.3.2/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_foundation","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_foundation-2.3.5/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"sqflite","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/sqflite-2.3.3/","shared_darwin_source":true,"native_build":true,"dependencies":[]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"linux":[{"name":"path_provider_linux","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_linux-2.2.1/","native_build":false,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_linux","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_linux-2.3.2/","native_build":false,"dependencies":["path_provider_linux"]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"windows":[{"name":"path_provider_windows","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/path_provider_windows-2.2.1/","native_build":false,"dependencies":[]},{"name":"screen_retriever","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/screen_retriever-0.1.9/","native_build":true,"dependencies":[]},{"name":"shared_preferences_windows","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_windows-2.3.2/","native_build":false,"dependencies":["path_provider_windows"]},{"name":"window_manager","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/window_manager-0.3.8/","native_build":true,"dependencies":["screen_retriever"]}],"web":[{"name":"file_picker","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/file_picker-8.0.1/","dependencies":[]},{"name":"shared_preferences_web","path":"/Users/leofarias/.pub-cache/hosted/pub.dev/shared_preferences_web-2.3.0/","dependencies":[]}]},"dependencyGraph":[{"name":"file_picker","dependencies":["flutter_plugin_android_lifecycle"]},{"name":"flutter_plugin_android_lifecycle","dependencies":[]},{"name":"path_provider","dependencies":["path_provider_android","path_provider_foundation","path_provider_linux","path_provider_windows"]},{"name":"path_provider_android","dependencies":[]},{"name":"path_provider_foundation","dependencies":[]},{"name":"path_provider_linux","dependencies":[]},{"name":"path_provider_windows","dependencies":[]},{"name":"screen_retriever","dependencies":[]},{"name":"shared_preferences","dependencies":["shared_preferences_android","shared_preferences_foundation","shared_preferences_linux","shared_preferences_web","shared_preferences_windows"]},{"name":"shared_preferences_android","dependencies":[]},{"name":"shared_preferences_foundation","dependencies":[]},{"name":"shared_preferences_linux","dependencies":["path_provider_linux"]},{"name":"shared_preferences_web","dependencies":[]},{"name":"shared_preferences_windows","dependencies":["path_provider_windows"]},{"name":"sqflite","dependencies":[]},{"name":"window_manager","dependencies":["screen_retriever"]}],"date_created":"2024-04-20 19:07:49.577497","version":"3.19.5"} \ No newline at end of file diff --git a/.github/workflows/firebase-hosting-merge.yml b/.github/workflows/firebase-hosting-merge.yml index 07d21d0..498516c 100644 --- a/.github/workflows/firebase-hosting-merge.yml +++ b/.github/workflows/firebase-hosting-merge.yml @@ -11,9 +11,11 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: npm ci && npm run build - uses: FirebaseExtended/action-hosting-deploy@v0 + env: + FIREBASE_CLI_EXPERIMENTS: webframeworks with: + entryPoint: ./example repoToken: ${{ secrets.GITHUB_TOKEN }} firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SUPERDECK_DEV }} channelId: live diff --git a/.github/workflows/firebase-hosting-pull-request.yml b/.github/workflows/firebase-hosting-pull-request.yml index a8adc18..5a6bc56 100644 --- a/.github/workflows/firebase-hosting-pull-request.yml +++ b/.github/workflows/firebase-hosting-pull-request.yml @@ -13,9 +13,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - run: npm ci && npm run build + - uses: FirebaseExtended/action-hosting-deploy@v0 + env: + FIREBASE_CLI_EXPERIMENTS: webframeworks with: + entryPoint: ./example repoToken: ${{ secrets.GITHUB_TOKEN }} firebaseServiceAccount: ${{ secrets.FIREBASE_SERVICE_ACCOUNT_SUPERDECK_DEV }} projectId: superdeck-dev diff --git a/example/assets/assets.json b/example/assets/assets.json index 29fb9ef..0637a08 100644 --- a/example/assets/assets.json +++ b/example/assets/assets.json @@ -1,8 +1 @@ -[ - { - "bytes": "iVBORw0KGgoAAAANSUhEUgAAAlgAAAN+CAYAAADJ5HVDAAAAAXNSR0IArs4c6QAAIABJREFUeJzs3Xl8FPXh//H3JJs7ISAkCEkIpwoqAlVEQRRUFA9QOUSrIopVwParrVq1tgTvalX81aOirReKUCmBinggRcQDpSCnQLjvKxAgB+TY+f2B2e7sbkKOSWY2eT0fj+/jsbPZzHwSC7y+n5n5jAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADYxXB6AIAbPTor50KvqQ5OjwNwNVOHs67p9KHTwwDcyOP0AAA38prmXYaMEU6PA3Az0zA3SiKwgBAILKASW1d8qyMHdjs9DMBVPFHROvX8K50eBuBqBBZQiTXfztb2VYucHgbgKrGJyQQWcAIRTg8AAACgoSGwAAAAbEZgAQAA2IzAAgAAsBmBBQAAYDMCCwAAwGYEFgAAgM0ILAAAAJsRWAAAADYjsAAAAGxGYAEAANiMwAIAALAZgQUAAGAzAgsAAMBmBBYAAIDNCCwAAACbEVgAAAA28zg9AACVS0xM1LXXXCNJMiVNnTpVJSUlzg4KAFApAguoA8/95S+Ki4ur9X7uufdepaam6pqfA0uSFi1apJycnBrtLzU1Vfffd59atGihBV99pTfffLPWY6yqHt27645f/UqGpPenTNGCBQvq7dgAUN8ILKAOtGnTRhERtT8DHxkZacNo/ue2UaPUvn17SdJVV16puXPnatu2bbYeoyLjxo1T06ZNJUlj7rqLwALQoHENFtCIJP8cOOVatGhRb8f2n9HzeDy2BCgAuBUzWEAdmDVrlm+2JlCfPn3k8Rz/o1dQUKAffvgh5OdKS0tVVFRk67gmT56sRx55RNFRUdqyZYuWLl1q6/4rkz1zpoYNHSrDMDRv3jx5vd56OzYA1DcCC6gD706eXOHXzj33XF9g7du3T3996aX6GpZWrVqlG264QU2bNlVeXl69HVeSpk2bpuzsbHk8HhUWFtbrsQGgvhFYQCNU33FVrri4WMXFxY4cGwDqE4EFhKH+/frp0gEDlJ6Wpvj4eB07dkz79u/X5MmTQ55yvOeee3RKp06W97bv2KEnn3zS8l50dLRuv+029ezZU/Hx8fJ4PCouKdGhvDz98MMPmpGdrQMHDlRrrM8/95xiY2Mt783/8ktNmzatzo8dqHfv3urfr59ap6WpadOmio6K0tGjR7Vnzx6tWrVKH0ydqoKCAt/nLxswQIMHD5YkHThwQI/88Y/q2bOnBl5+uTp27Kj4+HiVlJRo27Ztuv+BByRJhmFo8ODB6n3++UpNTVVCQoIMw1BJSYn27dunFStX6u9//7vKysosY7u4f38NGTKk2j/TjOxs3zIekvTCxImV3mXas2dP3TpypG/7d/fdZ/upaAAEFhB27r3nHrVs2dLyXkxMjNLT0vTg73+vuXPn6tW//c3y9TNOP13NmjWzvJeUlGTZTk1N1YsvvqjoqCjL+9FRUUpJSdEVV1yhK664Qr+86SYdPXq0yuPNzMwMeu/000+vl2OXS0hI0MMPPaTTTjst6GuxsbHKzMxUZmamBgwYoEmTJumLefOOj71tW9/v+qSTTlK/fv1097hxlu+PiorSrl27JElnnXWWHnzwwaCfo/xzrVu3VuvWrXVh377KmjDBEkKnnnZa0H/XqiguLlZKSorvpoEbRozQo489VuHnr73mGt9xiktKiCugjnAbDxBm/P8RNk0z6OuXXHKJzjzjjGrv909//GNQGATuPz8/v0aB4+SxU1NT9fc33ggZV4HH8Hg8KiktDbmfqKgojRs7NuTX3n7nHUlSYWFhyLgKFBsbq9//PONVG1u2bNGXX36pNWvW+N47/fTTK7xDMzIyUh07dvRtL/vxx1qPAUBozGABYWjvvn16/7339PU338jj8ejee+5Rz549fV8fO26cxowZ49v+0/jxiouL0y233KIzAmaPpOPx0KpVK992aWmp/vznP2vJ0qVKTEzU8GHDNHDgQC1btqzaY73/gQdkGIb++MgjQbNmdX1sSXrw979XlF/0FBQU6JVXXtGSpUtVXFys1q1b64orrtCll1yiVatWVbo+l2EYKiws1OOPP66169apT+/eap2WptzcXElSTk6OtmzZoszMTO3cuVMfffSR1qxdq0OHDunss8/WqFtv9Z0ubdasma666ip99NFHkqQ5c+Zoy5YtlR771pEjZRiGJKmkpETjs7IkSR9MnapHJ0yQdDwSBw4cqNmzZwft4+KLL7bE1z8//LBKv0MA1UdgAWFm7bp1+sMf/uCbfSkuLtafn3lGE194QRkZGZKklID1rXbu3Cnp+CxQKE2aNLFsr169Wkt+XsIhPz9f/3jzTf1rxowaLa2wceNGScfDqb6P3a9fP8spyt27d+ve3/7WcqH9zp079cYbb+jtt98Oui4qUHFJie686y7fXZALv/466DMvTJyoU089VXPnzrW8P3fuXK1Yvlwvv/yyL5LOPvtsX2Bt2rRJmzZtqvDY48aN832fJL3y6qs6cuSIpON3h+bn5ysxMVGSNPDyy0MG1oABA3yvjxw5og0bNlT68wKoOU4RAmHmzTffDHlqMHvmTN9rwzCqtYhobm6uZZ+dOnUKmm3Ky8vT4cOHazBi5449fPhwy/ZTTz9d4V2MJSUlJ4y4GTNmnHCJiW3btgXFVbk9e/dq69atvu3UlJRK91WuW7du6t+vn297xYoVQTNt87/80ve6VatWat68ueXr0dHRausXm998+22Vjg2gZggsoIFYv369ZbtNmzbV+n7/2ZO4uDhNmjRJAy+/3JaxOXVs/8VeN27cqO3bt9dqfx9//HGVPxsbG6uRI0fqsUcf1d9efVWT331Xk157TWlpab7PhDplGiguLk4P3H+/b/vo0aN68qmngj43bdo0S6hef/31lq8PuPRSywzYh5weBOoUgQU0EIGn/8oXM62qPz/zjI4dO+bbjo6K0ujRo/Xe5Mk1Wj7A6WNHR0dbLjjfXMn1TVVRWlpa4SlWfxEREbrjjjv09ltvadDVV6tLly5KSUlRXFycmjdvbvnvUpX/Rn/6058UExPj237m2WdDzsIVFBRYQvX8886zfP3SSy/1vd6zd2+tl7wAUDkCC4Akaf/+/br717/Wnj17LO/Hxsbqxhtu0Jv/+Idat24dNscOWverlrNXoU7LhvLsM8/o8ssus8ST1+tVfn5+tVewH3T11ZafY8GCBZVe7P/h9Om+13FxcerRvbuk479H/5mzLyo4hQnAPgQWAJ8DBw5o7Lhx+vMzz2jfvn2WrzVp0kTPP/dclU5rueHYRwJmm+L9HjZdV/7vN79R27ZtfduFhYV6+eWXNWz4cI289VbdfMstmvef/1RpX61bt9bNN9/s2z58+LD+31//Wun3LFq0yLKUxdBhwyRJV15xhe/0oGma+vfPF9YDqDsEFoAg33//ve4aM0Zvvvmm5e6/qKgoy/IPbj72li1bLLNO1b0mrboiIiJ0/vnn+7aPHj2qMWPHVjmo/BmGoUcnTPAtqWCaph597LEqzaJ9+913vtedOnZUdHS0+vldIL9p0yYeVwTUAwILQIU+mj1bv7vvPst7gafe3Hxs/1XKA1ePt1vnzp0tpwWnfPBBla7ZCuXXd99tWXl/5syZlS7h4G/KlCm+1xEREbphxAjLOmOzZs2q0ZgAVA+BBUDS8VW+o6Ojg97fvn275fl88fHxYXPsbdu2+V4nJCToxhtvrPkgTyAmYPwVLSuR4r80g99dfeV6dO+uCy+80Le9e/duvTt5cpXHkZub61v3TJIGDRrke11SUqKvFi6s8r4A1ByBBUCSdOvIkXr3nXc0cuRIy2rf6enpSkhI8G0X1sGz6+rq2M89/7xlbash112nsWPHWpYrkKSMjAy9+OKL+vsbb9TwJ5B+8ntcjSRdfdVVQZ/5wx/+YHmMUXRUlGWV+fj4eN3vtyRDaWmpb7X26phdwXISy1esqPa+ANQMK7kDkCRdcMEF8ng8GnT11bpi4EDt2bNH8fHxQQ+JXvLf/4bNsXNzc/Xvf/9bgwcP9r13cf/+6nvBBTpw8KAOHzqkli1bWlaTP++88/RtDRbhLCoq0sG8PDX7ee2t9u3b652331ZOTo6io6PVvn1732Ny/A0cONB32i5r/HjLTJ7H49HfXn210uNOnz5dUz74wPLeZ599plG33hq0DMR01r4C6g0zWADUMjXVcoeex+NRWlpaUOAcPnxYb/z972F17HfefVdffPGF5b2oqCi1TE1Vp06dgh7Vc9WVV1b7GOWefvppy4XoCQkJ6tatm7p06eKLq7379qmkpMT3mVtuvlnNmzfX4MGD1aFDh6B9GoZR6f8l/Px4HH9er1cbA67ZKigo0Np162r8swGoHgILcDn/BTjryv7cXE1+7z0dPHgw5Ne9Xq++/fZb3f3rX9t+B1p9HPuVV1/Vww8/HPRYHn8H8/L09jvv6JE//tH33tFqnpJcv369xmdlWa4bK1dcUqJPP/tMY8aM0eOPP+47dWkYhpo0aaJevXpV61gnHEtOjmX7O7+7CwHUveArLAEoa+a6KYaMEZ9NekTbVy1yejj1Ki4uTh3at1ebzEwdLSrSps2btWXLlho9bNmtx27Xtq1ap6UpMjJSW7Zs0bZt22w/Rnp6urp27ar9+/dr5cqVQYuMRkREqFu3bmqZmqr5X35puePRDv369dPd48b5tu+86y7t37/fln3HJibrxic+lClzY9bgU4Kn3QBwDRYAq6KiIq1ctUorV61qsMfetHmzNm3eXKfH2L59e6Wrx3u9Xi1ZsqTOjt+9Wzff63379tkWVwCqhsACgAYkOjpao0ePVu/evX3vfTFvnoMjAhonAgsAGoAhQ4bo0ksuUfPmzS1LXRQVFSk7O9vBkQGNE4EFAA1A165drYuYSiouLtb4rCzLXYsA6geBBQANTGlpqZYtX64XX3wx5B2NAOoegQUADcCUKVN0epcu2rJ1q5YuXaqysjKnhwQ0agQWADQAa9as0ZqAx/UAcA4LjQIAANiMwAIAALAZgQUAAGAzAgsAAMBmBBYAAIDNCCwAAACbEVgAAAA2I7AAAABsRmABAADYjMACAACwGYEFAABgMwILAADAZgQWAACAzTxODwBws5j4Jopv0tzpYQCuEp2Q5PQQANcjsIBKXHjTA04PAQAQhggsICRjp2lqndOjgBQVaSbFRJityrdLvMbBY2XGPifHBJ+tTg8AcCvD6QEAQGVWrFhxvaQPyrdN03yha9euv3VwSABwQlzkDgAAYDMCCwAAwGYEFgAAgM0ILAAAAJsRWAAAADYjsAAAAGxGYAEAANiMwAIAALAZgQUAAGAzAgsAAMBmBBYAAIDNCCwAAACbEVgAAAA2I7AAAABsRmABAADYjMACAACwGYEFAABgMwILAADAZgQWAACAzQgsAAAAmxFYAAAANiOwAAAAbEZgAQAA2IzAAgAAsBmBBQAAYDMCCwAAwGYEFgAAgM0ILAAAAJsRWAAAADYjsAAAAGxGYAEAANiMwAIAALAZgQUAAGAzAgsAAMBmBBYAAIDNCCwAAACbEVgAAAA2I7AAAABsRmABAADYjMACAACwGYEFAABgMwILAADAZgQWAACAzQgsAAAAmxFYAAAANiOwAAAAbOZxegBAYzVs2LC4iIiIzk6Pw+0WL17cLj093bd9+PDh1Ouvv76Hg0MKC4Zh7Pvggw+2OT0OoLEisACHREZGnibpv06Pw+0+/fTTwLd+aRjGL50YSzgxTfM1SXc5PQ6gseIUIQAAgM0ILAAAAJsRWAAAADYjsAAAAGxGYAFhIjY21ukhAACqiLsIARdKTk5Wr1691LZtW8XHxysyMlKSZJqmiouLlZ+fr++//16rVq1yeKQAgFAILMBFIiMjddVVV6ljx44yDCPo64ZhKCYmRjExMRo4cKB69eql7Oxs5ebm1vnYkpKSdOTIkTo/DgA0BAQW4BJxcXEaOXKkEhMTq/w9zZo1U0ZGRp0FVlxcnPr06aPTTjtN0dHReu655+rkOADQ0BBYgEuEiqvCwkLt3LlTW7du1e7du3XyySerffv2yszMlGEYKi4u1o8//lhnY+rRo4fOOussScdPTwIAqobAAlygV69eQXG1du1affTRR5aw2blzp5YsWaLk5GRdddVV+umnn+p7qACAKiCwAIdFR0fr/PPPt7z32Wefafny5RV+z6FDh/Tee+/V9dAAADVEYAEOu/rqqxUR8b8VU/bt21dpXFVXZGSkLr74YqWnp6tJkybyeDwqLS1VQUGB9u/fr7lz5wZdvJ6amqpBgwYpISHB955hGBo9erTlc999951WrlxZrfFcdtllysjI0JEjRzR16lQlJCToiiuuUGpqqmJjY1VWVqb8/Hzt3btXc+bMUUlJiWJjY9WvXz+lpaUpKSlJEREROnbsmPbu3atZs2bp6NGjlR6zS5cu6tq1q5o3b67Y2Fh5vV4VFRXp4MGDWrJkiXJyckJ+380336yYmBht27ZNn376qZo3b66LLrpIKSkpvt9NYWGhtm/frtmzZ8vr9VY6jnbt2uncc8/1jUOSioqKdOTIEa1evVr//a/10ZRXXXWVTj75ZEnH/3cxc+bMSvc/fPhwNWnSRJK0cePGM6dOnVrp5wHUHQILcFh6erpl++OPP7Zt323atNHgwYMVExNjed/j8Sg5OVnJyclq3769fvjhBy1YsMD39RYtWqhp06ZB+wt8r1WrVtUOrFatWqlp06Zq2rSp+vTpo7PPPlsez//+KvJ4PL6vt2nTRgsWLFD//v0tn5GOrwvWpk0bjRkzRlOnTtXOnTuDjhUREaFrr71W7dq1s7wfGRmpxMREJSYmKiMjQzk5OZo1a1bQdWYpKSmKiIhQcnKy1q1bp2uvvdYSw5KUkJCgU089VZmZmXrrrbeUn58fchxXX321OnXqFPS1+Ph4xcfHq2XLlurevbtmzJjhu2mhrKzM9ztv2rSpEhISVFBQEPL32qxZM7Vp08a3XVJSUhbygwDqBQuNAg6KiIhQVFSUb3v//v3at2+fLfvu2LGjhg8fHhRXgRFhGIZ69uyp/v37+9470UxMRfuqrl69egWFk7/Y2FgNGDDA8pnAY0ZGRmrQoEEhv3/06NFBcVVWVha0j06dOumWW26pcByGYWjIkCGWuArcR2xsrC677LKQ33/bbbeFjKtATZs21c0336zo6GhJ0sKFCy1fP++88yr83p49e1rG9sMPP6w74QEB1BlmsAAHlZ/+Kbd//35b9hsZGakrr7zSt22aplauXKkvv/xSR48eVXR0tHr16qVzzjnHt95W9+7dtWzZMuXm5mrNmjVas2aNevfu7ftH3TTNOlmmoaysTN99950WL16sJk2aqEePHuratWvQOmDr1q3TwoULdeDAAbVt21aDBw/2xWliYqI6dOigDRs2+D7fq1cv3+kySTp27Jj++c9/avfu3ZKkjIwMDRkyxBdvKSkpQfsIVFpaqq+++korVqxQcXGxTj/9dA0YMMC3EGzbtm0VGxtrOWV5/vnnW2b+CgoKNHv2bG3dulXS8f8NDBo0yDdWj8eja6+9VlOnTtWRI0d08OBBNWvWTJLUuXNnzZ07N+TYOnbs6Hudm5uroqKi0gp/EAB1jhkswEFpaWmWbbtmr66++mrLzNiMGTP06aef+v7hLy4u1oIFCzRt2jTfZwzD0MUXX2zL8avKNE1Nnz5d3377rUpKSpSbm6vPP/9cy5Yts3xu3bp1mjVrlg4cOCBJ2rx5sz766CPLZ/x/lx6PxzLbU1paqtdff90XV5K0bds2vf7665aZqAEDBlQ41qKiIr3xxhv673//q+LiYknSqlWrLNdNGYZhGUdSUpJlHAcPHtSrr77qiytJ2r17tyZNmmRZyywjI8N3jdbixYt978fExFhOA5ZLSUlRXFycb3vp0qUV/hwA6gczWICDWrRoYdk+UWDdd999Id/ftGmTpk+f7tvu0KGD7/XGjRu1cePGkN+3bds27dy5U61bt5YknXTSSVUat10OHjxoiY1ymzZtUrdu3Xzb/teHlduwYYPKysp8s0f+v8suXbr43pek5cuXh7wQvqCgQFu3blVmZqak49dTBc5Alfvpp59CXl/17bffWk7PNW/e3DcLdu655/pm4kzT1Pvvvx/0/eX+9a9/6Y477vBtt23bVmvWrNHy5ct18cUX+05PnnfeeUG/M//jl5WV2XqTBICaIbAABx07dsyyXX7tTW0kJydbTq95PB717t27ws/7P0Q6Pj6+1se3Q1lZ1a7PPnbsmG/M/jN2qampls+FCrRyy5Yt8wWWdPwi/E2bNlV5rCUlJTJN0/c7Lz+dFzgO0zTVo0ePKu+3devWWrNmjUzT1MaNG32nANPT0xUZGWn5HbVv3973eufOnSwKC7gAgQU4aM+ePZbtli1bas2aNRV+3v8f8opkZGRYttu0aRPytFIoJ9q321R0Mb7/bJbX61VpacWXIwU+Zig1NbVagSUdD8Lya7n8L4T3v/YqIiKi0ovUA/mH71dffeULLMMw9Itf/ELff/+9pOPXcPnfyLBo0aJqjR1A3SCwAAft2rXLsh14yjCQ/0Xmv/vd70IGUfnpvpqoLETCif9M3Ilmw/Ly8izbNTlNWtGMkf91UdVVfr2ZdDwC8/Pzfav9d+vWzRdY5557ru9zJSUl2rx5c42PCcA+BBbgoAMHDlhmpWoTR+UCTzt+9NFHQRFRkaKiolof3w1KSkqq/NmkpCTLduCiq7Xh9Xp914Ll5eUFXZhfmcDr8ZYuXaoLLrhAktSkSRMlJyfr0KFDatu2re8z69evt2HUAOxAYAEOO3bsmO90UExMjHr16qXvvvuuxvvbvn27zjnnHN+2x+Ox3D3XGBw8eFAtW7aUpErX2ZKO34HnL/C0bW0UFBT4ll+IiYmp1X+HxYsXq0+fPr4Y79Onj5YvX2659qw2/7sBYC+WaQAcVn6qp9yJFt88kW3btlm2O3fuXON9+TMMI2yu0fKf/TEMI+i6NH+nn366ZTvwtG1t+J/mi42NVXJyco33VVZWpu3bt/u2O3bsaAnpgoKCoOvJADiHwAIc9v3336uwsNC37fF4dOedd6p58+YVfk/g41r8FRcXW04TZmZmBj2Op6oClyuw4xRmfQg8Vda3b9+Qn/N4PJYlLUpLS0MuxVBT/tdDGYaha665plb7++abb3yvo6KiLHcPrl69ulb7BmAvAgtwgc8//9yyHRcXp1tvvVV9+/YNCq1TTjlFd999d6WzSZ988olle+jQoerSpUvQ5zIyMnTXXXfpuuuuC7mfHTt2WLYvueQSy3EjIyNdOauVm5trGXurVq104YUXWj4TGxur0aNHW8bvHzB2WLx4sQ4fPuzbTklJ0YgRIyx3CErHg/nyyy/XPffcU+ks17Zt2yp8sDV3DwLuwjVYgAvk5OTop59+spzOK39GYM+ePWWapkpLS+XxeKoUNDk5Odq6datveQaPx6MrrrhC/fv31+HDh2UYhpKSknz/0CcmJurkk08OukZoz549Kikp8V3nk5KSorvvvlsHDx5UcnKy4uLi9Mknn1T7gc/14eOPP7YE1DnnnKMzzjhD+/fvV3R0tFq0aGFZjLSwsDDodK0dPvzwQ40aNco3jvT0dI0bN05HjhxRQUGBEhISlJCQ4BvLoEGD9O6771a4v7Vr1+qss86yvHfgwIEKwwuAM5jBAlxi9uzZmj17dsi1nQzDUFRUVMi4Kioq0tdffx30/r/+9S/t3LnT8l5sbKxSU1OVkpISNIvif7qpnGmaQbNrMTExOvnkk31LEAQ+7sctDh06pFmzZlmWaYiLi1NGRoZatmxpiauCggJNmTKlTsZx4MABzZkzxzIOwzDUpEkTtWrVSk2aNLGM5UTLRIS6kP3HH3+0b8AAbMEMFuAiP/30k7Zv367BgwerefPmljvE/JWWlio3N1cLFy6scFHM0tJSvf/++zrjjDPUr18/y2KU5UpKSnTgwAHNnz8/6OL4cqtXr1ZERIQGDBgQ8tqv6q7+XtVV2u2Qk5OjSZMmaejQoTrppJMsISMd/x2tX79es2fPDrmWldfrrfR6t6pavXq1Nm7cqGuvvVatW7cOCmXTNFVYWKi1a9dWuuq8dHwZCf9HBJWVlfHsQcCF3HfxBNBIjBgxorukJZV9xuPxKDMzU2lpaSooKNCuXbu0e/fuClcwP9G+MjIyFBMToyNHjmjXrl3V2k/5g4yTkpJUWlqqffv26dChQ2H1WJbk5GSlpqYqMjJSmzdvduy0WtOmTZWSkqKoqCht3bq12hfWjxs3zjeDuH37dn3wwQdBnzFN87WpU6feZcuAAVQbM1iAi5WWlmrDhg2+hwfXdl/VfQSMP9M0LcsEhKNDhw7p0KFDTg9DeXl5VV78NVBsbKzl9G5dXDcGoPYILAAIExkZGRo8eLDvFGNJSYk2btzo8KgAhEJgAYCLeTwe3XTTTUpMTAy6MYGlGQD3IrAAwMU8Hk/Ih4CvX7+eR+MALkZgAUAYyc/P18KFC1259hiA/yGwAMDFjh49qq+++kpFRUXavn275fmGANyLwAIcUlZWtisyMvJPTo/D7dq2bXtmZmbmsPLtAwcOfLtixYo5To6pvtXkWivTNP9bB0MBUEWsgwXA1VasWHG9JN9CT6ZpvtC1a9ffOjgkADghHpUDAABgMwILAADAZgQWAACAzQgsAAAAmxFYAAAANiOwAAAAbEZgAQAA2IzAAgAAsBmBBQAAYDMCCwAAwGYEFgAAgM0ILAAAAJsRWAAAADYjsAAAAGxGYAEAANiMwAIAALAZgQUAAGAzAgsAAMBmBBYAAIDNCCwAAACbEVgAAAA2I7AAAABsRmABAADYzHB6AAAarxUrVph1te8zzzyTv98AOIYZLAAAAJsRWAAAADYjsAA4xjTNt+to11vqaL8AUCUEFgDHREVFZdVvsyYlAAAgAElEQVTFfk3TrJP9AkBVEVgAHNO5c+fNdTCLtaVr165v2bxPAKgWAguAo+yexWL2CoAbEFgAHGXzLBazVwBcgcAC4Di7ZrGYvQLgFgQWAMfZNIvF7BUA1yCwALhCbWexmL0C4CYEFgBXqOUsFrNXAFyFwALgGjWdxWL2CoDbEFgAXKOGs1jMXgFwHQILgKtUdxaL2SsAbkRgAXCVas5iMXsFwJUILACuU9VZLGavALgVgQXAdao4i8XsFQDXIrAAuNKJZrGYvQLgZgQWAFc6wSwWs1cAXI3AAuBaFc1iMXsFwO0ILACuVcEsFrNXAFyPwALgaoGzWMxeAQgHhtMDAMLV+Fkb+kSYZgevYaZFmIp2ejyAHUxTXsMwdinC2BgTq8UPXtrhkNNjAsIRgQVUQ9aMdd2MCD0uGVc6PRagPpjSd4ZpPDP+mo4znB4LEE4ILKCKJmSv+5sM405JKj521Ltv8+qIvZtWa9+WNSotPur08ABbRER61Dy9g1LbdlHL9md4YxOTIyTJNPVZdLRueviKTvucHiMQDggs4ASe/nxD8tGCsvmGYXSTpOVzp2rxv99welhAvTi191U699oxpicq2pC0x1TkgKzB7Zc7PS7A7QgsoBJZphlRNmPNF55Iz0UFefs0/52ntGfDCqeHBdSrpBat1H/Un9Q8vaPKykp3xidFd+HaLKBy3EUIVMKclXOfJ9JzUUnxUX38/35HXKFROrJ/lz6a+H86tG+HIiM9rY8VeF9zekyA2xFYQAWe+mhLM0PGeEn6fsbfdCR3l9NDAhxTVlKsBe88Ja/XKxm6PmtWTg+nxwS4GYEFVOBYacnthhS/K2eZ1n4z2+nhAI7bt3WtVnwx9fiGV79zdjSAuxFYQAUMw7xKklb8559ODwVwjdULZkqSDOlK0zS5jheoAIEFVMCUukvSod1bnR4K4BpFh3NVfKzIlKHkx7PXt3d6PIBbEVhACK8tNqMMqUlZaQnXXgEB8nZuMiSpzDBaOj0WwK0ILCCE3L3rm0pSQR5rKgKB8g/ukSQZhtHM4aEArkVgASGYJZH82QAqYPpeePlzAlSAPxwAAAA2I7AAAABsRmABAADYjMACAACwGYEFAABgMwILAADAZgQWAACAzQgsAAAAm3mcHgAAKSEhQQUFBU4Po9b69O6tdu3aSZLWrF2rH374weERAYAzCCygnqWmpmrokCHq1q2bkpOT5fEc/2NomqaKiop04MABzcjO1vz58x0eafUNHTpUGRkZkqTtO3YQWD/r0b277vjVr2RIen/KFC1YsMDpIQGoYwQWUE+ioqJ07733quc558gwjKCvG4ah+Ph4xcfH69d3362hQ4bo6T//Wdu3b3dgtLDTuHHj1LRpU0nSmLvuIrCARoDAAupBUlKSXnj+eTVrVvVn47Zq1UpnnHEGgdUAxMXF+V57PB5FRETI6/U6OCIAdY3AAupBqLg6fPiw1qxdq5UrV2r9+vXq2LGjenTvrrPOOkuGYaioqEiffPKJQyOGnbJnztSwoUNlGIbmzZtHXAGNAIEF1LEhQ4YExdU333yj5194QaZp+t5bu3atZs+erZapqbrn3nv11Vdf1fdQUUemTZum7OxseTweFRYWOj0cAPWAwALqUHx8vIYPG2Z572+vvabPP/+8wu/Zs3evHnrooboeGupZcXGxiouLHR1DVFSUnnziCb319ttatWqVo2MBGjoCC6hD9957r+8uQUnavHlzpXFVXR06dNCIESOUkZ6uZs2aKTIyUgUFBdqzZ48Wff+9pk+fXqv9t0xN1ZixY9W+XTvFxcXJMAwdO3ZM+/fv17z//Edz5sypNBoSExN1xx136JRTTlGL5s0lSYcOH9batWs1ceJElZSUVHr8vn37asCllyotLU1JSUkqKyvTkSNHtGPnTs35+GN9t2hRyO979plnlJCQIEmaMmWKWrVurf79+vl+R4WFhdq7b5+WLl2q9957L+Q+LrnkEl137bWSpLy8PE188UXde++9Sk9LU1xcnEpLS5WXl6edO3fq3cmTtWnTpqB9PP/cc4qNjbW8N//LLzVt2jTfdmpqqrLGj5ckTZ8+XV/Mm6fevXvrioEDlZGRofj4eN+xPp8794T/TW8YMUIXXHCBmp10kqKjolRaWqrCwkJ9/c03Ou2009SubVtljR+vp55+WkuWLKl0XwBqLvhWJgB6YubGlqUq2314/059+NjIGu/n/ffeU0xMjG/7t7/7nbZs2WLHEDV8+HANHzYs5B2J5Xbs2KE/jR+vvLy8au+/b9+++s2vf13p/g8fPqxRt93m2574wgu+ZRry8/MVFRVl+fn9FRQU6OE//CHkRfyRkZF66KGH1L1bt0rH+N2iRXruueeCrmma+sEHlrCtTG5urrImTNDOnTst748aNUpXXXmlb9vr9SoiIvTazKZpKnvmTE2ePNny/vQPPwz67MpVqzT+56CSpPbt2+vZZ56RJB3My1P2jBkaNWpUheNdt26dHnr44aD3U1NT9cTjj+ukk06q8Hv9fbdokZ599tkqfTbQhSMfVoce/SRTg8Zf0+nfNdoJ0MCxkjtQRyIjIy1xsW3bNtvi6p577tH1w4db4sc0TZWWllo+l5aWpldfeUXR0dHV2n9kZKTGjR0bFFf+14xJCjlrUy4xMdHy8wd+b0JCgh64//6Q3/vySy8FxVVpaWlQSPU691z9pRqREOri8ubNm+uF559XYmJipd9bHleBP4d0fImNa6+5Rr++++4qjyWUZk2bWuIq1LFOOeUUXXrppUHHf/KJJ4Li6tixYyH38elnn9U4rgBUDYEF1JGOHTpYtrdu3WrLftPT03VBnz6+ba/Xq1f/9jcNGz5c148YoV/deaf27Nnj+3p0dLTuuvPOah2jb9++lhmggwcP6re/+52GDhumcXffreXLl0s6/g91ZUzT1JxPPtGYsWM1dNgwPfb44zp27Jjv62lpaerRo4fle4Zcd51SUlJ824WFhXrg97/X9SNGaNjw4Ro/frzltGRmZqbOOeecSsfx9ddf6/bRozVs+HDdPnq0Jr/3niVGPR7PCeOooKBAzzz7rIYOG6Zhw4frwYce0saNGy2fufDCCy2Rc/8DD+iB3/9eR44cqXTfgfbu26fnX3hBw6+/XsOvv16ffPqp5esjrr/esn377bdbbqTYs3evbh89Wjf+8pe68Ze/DBpnq5NPrtZ4AFQfgQXUkVNPO82ybdfs1f333WfZzpowQXPnzvXNVOTm5mrc3Xfr4MGDvs/07dvXt9BlVQTOhHw4fbpv/Lt379aERx/V/91zj77//vsK91FaWqrHn3hCb7zxhvbu3StJ+vHHHzXh0Uctn+t9/vm+19HR0Rrmd1NAcXGxxowdqw0bNvjeW7lqlcaMHWuZjbqzkoCc+8UXev6FF3ynSfPy8jRjxgzdPnq0iv2uAfvFL36h1NTUkPvwer0addttWvTzNV9er1c5OTm6/4EH9PHHH/s+ZxiGfvOb3/i2N27cqA0bNgTNLFZm7bp1Gjt2rL7++mt5vV6VlZXp9ddf16bNm32fSU5OtnzPL37xC9/rkpIS/fa3v/X9vMXFxfr9gw+qqKjI95nTOneu8ngA1AyBBdSRtpmZlm3/fyBDmf7hhyH/75FHHvF9Jjo6Wunp6b7t7du3h7wbzDRNfeh3MbRhGOrTu3eVxx546u/iiy8OOl24ffv2kKefyu3es0c//vhj0Ptr1661XBPWOi3N97pv376KiorybX/++efKz88P2kdeXp5Wrlzp227WtKnvovZAxX4zZv7y8/M1Mzvbt20YhmVm0F956ITy5ltvWWblOnXsGPJzVfXmm2+G/L3OnTvXMlb/YD7Jb/bqp59+0tGjRy3f6/V6tXjxYt92dFRUhdeTAbAHf8KAOhL48Gb/1bxrqkPAacd/f/RRhZ8NXKS07c8PYa6KZcuWWU7DtW/XTq9PmqQzTj+9yvuozEG/wGrmFwrt27e3fG5yBXf4SdJnAXdjnnLKKdUex1S/u/kk+S7Qrw6v16sfly3zbVd0UX9t7dixw7Kd4RfakZGRvtehglQ6fkOCv/j4eBtHByAQgQXUkQ0B1710CIiHQJXNBpULjIjAa2sC+S+D0Lp16xPuv1xZWZleevlly5iaNWumCRMmaNJrr+nss8+u8r5C8Z9V8p9JaeMXOKWlpZUuAbFt2zbLdvtqBGQ50zQtv6OWLVtWex+SLHcgGoZR4anG2ti3b59l23+mz/9naNOmTcjvb+M3o+r1eisMMQD2ILCAOpKTk2PZzqjgH75yQ4cN05ChQzVk6NAKH6US+A/3iZ5T6H/qqjrPQZSOXxj++BNPBJ1uat68uR568EE98+c/W/6Rt0OTJk18r0903ZL/hfyS1KoaAenP/+er7u+oXPk1ZuUyA04P26Gyx+vs2rXL9zo9PV2nBVz/17RpU8vs46FDh2wfHwArAguoIzt27LDMAJ1ag1NYgQJPO57ownX/5Rnyq3knm3T8ovSbbr5Z70+ZEhRaHTp00AvPP1/tfVbGPwgrW39LOh56/g4cOFCjY/qf0gv8GauqecBNAbm5uTXaT01NCzjV+dijj+rGG2/Uqaeeqssvv1yvvPyy5ff53vvv1+v4gMaIwALqkP9z5xISEjRkyJBa7c//bjrp+DIHlfGfYfKf5agO0zQ1ffp03XzLLfpq4ULL11q1alXhheE14T/GE63dFXgTQeDvpqr8j7Nr9+4a7SPwv8PmE9zQYLfvFi3SN99849uOiIjQkOuu05NPPKE7Ro+2ROSaNWv0n//8p17HBzRGBBZQh/41Y4Zle+jQodVe9NNf4GnHytZ/6tu3r2XWorbLRHi9Xk2cOFEfTJ1qef/cc8+t1X79+Y/RMAydXslF9RdeeKFlO/B3UxWXDRhgPX4Nw+iMM87wvS4uLq70dF5dee755zUn4MYGf8UlJXrjjTf0B7+7UgHUHQILqEPZ2dmWu7eio6I06bXXLEstBPK/IyzQgQMHLBd+XxQQGf5uuOEGy/b3P/xQlSH7VHTX40cBdy42b9GiWvutTOAYb7rpppCfi46OtlxoX1xcXKNThIH7/9pvFqiqBl19tZKSknzbdq13Vl1jxozRwMsv922XP5NyzZo1mvXvf2vkyJGVBhgAexFYQB3722uvWbaTkpL0wvPP66abbgpaFuC8887T22+9VekaRdP++U/f65iYGD315JNBUfbwQw8p1W819J9++inorrsTefrpp/XO22+rb9++lvcHDxpk2fZf0LS2tm3bpjVr1vi2T+nUSSNvucXymYSEBL380kuW31Hgcgv+unfvHrRGVnp6ut54/XXLUgWV/Y48Ho/69+sX9P6oUaN0S8D4/vrSSxWOpa50795dl1x8saTj13/dcOONumXkSD351FOaM2eO8vPz1bt37xotQwGgZqr2NFQANbZo0SJ99dVXuuCCC3zvRURE6NprrtG111wjr9er4uJixcTEnPDCbkmaMWOGrhk82PfsvFNOOUXvvvOOtm/fruLiYmVmZlrCwTRNvTBxYrXGnJSUpPSfryv6v9/8Rnf+6lfas2ePWrRoERQrc+bMqda+T+TF//f/LBdlDxo0SBdddJG2btum+Ph4ZaSnW64tO3z4sLL9FgwN1KpVK7315pvau3evCouK1KJ5cyUlJQU9x3Hiiy9WOq5x48Zp5MiR2rt3rxISEtTspJMUHXAX5dIffwxar6o++K+J1bx5c703eXKFkZ6fn69Jr7+ur7/+ur6GBzRKzGAB9WDiiy9q4sSJIZceiIiIUGxsbMi4Onz4sD744IOg9x96+GHLYp0xMTHq0KGDOnfubImrkpISPfuXv1T7rraBAwdatmNjY5WZmRkUV8uXL9eKFSuqte8T2bt3r579y18sazs1adJEZ5x+utq3a2eJq4N5eVW6pigiIkInn3yy2rdrpyZNmlh+18UlJXriySe1f//+E+4nMTFR7du3V8uWLYPiatmyZXrqqaeq8iPa7ot58yzblc2AJiYm6t577tGpp55a18MCGjUCC6gnXy1cqLHjxmnDhg2VLgdQXFysjRs36vEnntCo227T+vXrgz6zc+dO3XHHHfr2228tz9MrV1paqk2bN+tXd97pe35edXzyySeaO3euZdmEwDF++OGHevSxxyzvl4QYS00sWrRId40Zoy1btoSM0uKSEn399de64447LIt8hnLs2LGQF52XlpZqw4YNuuOOO7R06dITjqmiU6EFBQWaNGmSHn3ssQofp3Oi8dVWQUGBnnjySR08eND3s5aUlCgvL08bNmzQjh07LL9HwzA0buzYWh8XQMVOfD4CaISemLmxZanKdh/ev1MfPjayTo4RHR2trl276rTTTlPewYPKycnR+g0bavSPdGJios466yyVlZZq46ZNQQtf1kbTpk3VsWNHtW7VSrkHDignJ8fW/VdFamqq2rdrJ4/Hox+XLTvhKuRTP/hAHs/xKyA+/vhj/f0f/1BaWpratGmjyIgIrVy1yvI8xFBGjRqlq668UtLxGLt+xAjFxsaqc+fOSoiP1/7cXK1bt86ROwZr6o3XX/ctplpcUhJ0I0RVXTjyYXXo0U8yNWj8NZ3+becYgYaCa7AAhxQXF2vx4sWWh/DWVH5+fp1dU5OXl2fLGGtj7969tY66HTt21Pr6qKNHj1ZptssJSUlJSkpKqnRGb8nSpbq4f39JkqeSu1UB1B6BBQANwOOPP670tDStXr1af33ppaAg7dmzpy70uyOUZxECdYvAAoAwN+S663x3fXbp0kWvvvKKCgsLfQ+ITklJsdz8IAWvZwbAXgQWAIS5liefHPRefHx8hQ+dXvT995r+r3/V9bCARo3AAoAw98orryg7O1t33nmnTjv1VN8F/oF27dqlyZMn67sa3FkKoHoILAANzrRp03yP8KlpTMyfP9+35lZBQYFtY6srO3fu1Pjx4yUdX2z0zDPPVPt27XTo0CFt2bpV69evP+GdkwDsQ2ABaHDsOP21adMm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6dIlm4ttnBE94gU8PyUVMJm2rX5l4JtuOzE55D8PF30HKJ468N8zcL+ic5a4CQ8Pxy9GjEBgYCAKr1zB5cuXmx30Aw1pPwnx8QgKDkZhYSHUVpL9VSoVhg4diurqaqubxDtr1O/mY8jo8QAwefWEuL1uO7ED2lUOliuqq6vxDxu5VU2uXbuGa5KI2d658pyo/9HEZDKhsLDQ4deVlJS4VF9HEARcLSqyu18bUUfgal9xB5PJ1OLPFCJPVl5ejhyzHUFaQqfT4V/NbLlTWVnptqLA7U2bTxESERERdTQMsIiIiIjcjAEWERERkZsxwCIiIiJysw6T5O6srOxsPNFYpd2T9ugrLS3F18ePi3V3Kioq2rhFRERE1KTTB1j5+flO7Rje1gwGA7Zv397WzSAiIiIrOEVIRERE5GYMsIiIiIjcjAEWERERkZsxwCIiIiJyMwZYRERERG7GAIuIiIjIzRhgEREREbkZAywiIiIiN2OARURERORmDLCIiIiI3IwBFhEREZGbMcAiIiIicjMGWERERERu5uPqCXr3H4qRr85xR1vIA3j7dGnrJlCj3rFPsu91YD1VkW3dBJKIHzsJ+hptWzeDWqh3/6Ft3QTXA6yQvtEI6RvtjrYQkQNC+8YgtG9MWzeDqFOIferFtm4CeRjnAyxv0xnUY7ob20JELeDt5XVaMBrZ9zoJhbfiSlu3oTMzKbBWIZiUbd0Oco7JB2fbug1ERERERERERERERERERE9zuIIAAACqSURBVERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERNSe/R/W27uMjhrTlQAAAABJRU5ErkJggg==", - "width": 600.0, - "height": 894.0, - "path": "assets/images/sd_568989600.png" - } -] \ No newline at end of file +[] \ No newline at end of file diff --git a/example/assets/images/sd_568989600.png b/example/assets/images/sd_568989600.png deleted file mode 100644 index 0d0b928..0000000 Binary files a/example/assets/images/sd_568989600.png and /dev/null differ diff --git a/example/assets/slides.json b/example/assets/slides.json index 5f0a865..18941b9 100644 --- a/example/assets/slides.json +++ b/example/assets/slides.json @@ -12,14 +12,14 @@ "sections": { "header": { "flex": 2, - "alignment": "bottom_center" + "alignment": "center" }, "left": { "flex": 1, "alignment": "center_left" } }, - "raw": "\nstyle: custom\nlayout: two_column_header\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc21yZzZhNzQ3bmt4dGk3amE5a2ozaHQxbTdpeGM4bHlmazdibmJjdSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/8L43c9x5Lvl2o/giphy.gif\nsections:\n header:\n alignment: bottom_center\n flex: 2\n left:\n flex: 1\n", + "raw": "\nstyle: custom\nlayout: two_column_header\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc21yZzZhNzQ3bmt4dGk3amE5a2ozaHQxbTdpeGM4bHlmazdibmJjdSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/8L43c9x5Lvl2o/giphy.gif\nsections:\n header:\n alignment: center\n flex: 2\n left:\n flex: 1\n", "type": "Slide", "layout": "two_column_header" }, @@ -198,72 +198,5 @@ "raw": "\nlayout: two_column\n", "type": "Slide", "layout": "two_column" - }, - { - "title": "Mermaid example", - "transition": { - "type": "fade_in", - "duration": 0, - "delay": 0, - "curve": "ease" - }, - "data": "::left::\n\n![Mermaid Diagram](assets/images/sd_568989600.png)\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", - "type": "Slide", - "layout": "two_column" - }, - { - "options": { - "name": "demo", - "args": { - "text": "Custom", - "height": 200.0, - "width": 200.0 - }, - "preview": false, - "flex": 1, - "position": "right" - }, - "transition": { - "type": "fade_in", - "duration": 0, - "delay": 0, - "curve": "ease" - }, - "data": "## Interactive Examples\n\nShowcase your custom widgets with ease.", - "raw": "\nlayout: widget\noptions:\n name: demo\n position: right\n args:\n text: Custom\n height: 200.0\n width: 200.0\n", - "type": "Slide", - "layout": "widget" - }, - { - "content": { - "flex": 2, - "alignment": "center_left" - }, - "transition": { - "type": "fade_in", - "duration": 0, - "delay": 0, - "curve": "ease" - }, - "data": "::header::\n# Pretty cool right?\n\n::left::\n\nThis whole presentation was created using the following markdown. With some custom styling with Mix.\n\n## Scroll here 👉\n\n::right:: \n\n```markdown\n---\nstyle: custom\nlayout: two_column_header\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc21yZzZhNzQ3bmt4dGk3amE5a2ozaHQxbTdpeGM4bHlmazdibmJjdSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/8L43c9x5Lvl2o/giphy.gif\nsections:\n header:\n alignment: bottom_center\n flex: 2\n left:\n flex: 1\n---\n\n \n# Superdeck\n\n::left::\n## Beautiful Flutter presentations with Markdown\n\n---\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\n> Create your Flutter presentations faster and easier than ever.\n> You can quote me on that.\n> ### Leo Farias\n\n\n---\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif\nstyle: cover\n---\n\n# Complex layouts\n\n---\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\nyaml\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.\n\n\n\n---\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\nyaml\nsections:\n left:\n flex: 2\n right:\n alignment: bottom_left\n\n\n---\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\nyaml\nsections:\n left:\n alignment: bottom_right\n flex: 2\n right:\n alignment: bottom_left\n header:\n alignment: bottom_left\n\n \n\n---\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\ndart\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\n---\nbackground: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif\nstyle: cover\n---\n\n# Markdown support\n\n---\nlayout: two_column\nsections:\n\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\n\n\n---\nlayout: two_column\n---\n\n::left::\n\n\nCode\ndart\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___\n\n\n---\ntitle: \"Mermaid example\"\nlayout: two_column\n---\n\n::left::\n\nmermaid\nflowchart TD\n A[This is crazy] -->|Get money| B(Go shopping)\n B --> C{Let me think}\n C -->|One| D[Laptop]\n C -->|Two| E[iPhone]\n C -->|Three| F[fa:fa-car Car]\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.\n\n---\nlayout: widget\noptions:\n name: demo\n position: right\n preview: true\n args:\n text: Custom\n height: 200.0\n width: 200.0\n---\n\n## Interactive Examples\n\nShowcase your custom widgets with ease.\n\n---\nlayout: two_column\nsections:\n right: \n flex: 2\n---\n\n# Isn't this amazing?\n\n```", - "sections": { - "right": { - "flex": 2, - "alignment": "center_left" - }, - "header": { - "flex": 1, - "alignment": "bottom_left" - }, - "left": { - "flex": 1, - "alignment": "top_center" - } - }, - "raw": "\nlayout: two_column_header\nsections:\n right: \n flex: 2\n header: \n alignment: bottom_left\n left:\n alignment: top_center\ncontent:\n flex: 2\n", - "type": "Slide", - "layout": "two_column_header" } ] \ No newline at end of file diff --git a/example/pubspec.lock b/example/pubspec.lock index dbf83d0..5f7a419 100644 --- a/example/pubspec.lock +++ b/example/pubspec.lock @@ -465,10 +465,10 @@ packages: dependency: transitive description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: diff --git a/example/slides.md b/example/slides.md index 45c3ed6..79a6e56 100644 --- a/example/slides.md +++ b/example/slides.md @@ -4,7 +4,7 @@ layout: two_column_header background: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc21yZzZhNzQ3bmt4dGk3amE5a2ozaHQxbTdpeGM4bHlmazdibmJjdSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/8L43c9x5Lvl2o/giphy.gif sections: header: - alignment: bottom_center + alignment: center flex: 2 left: flex: 1 @@ -299,417 +299,3 @@ Table Divider ___ - - ---- -title: "Mermaid example" -layout: two_column ---- - -::left:: - -```mermaid -flowchart TD - A[This is crazy] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car] - -``` -::right:: - -## Mermaid Support - -Superdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation. - ---- -layout: widget -options: - name: demo - position: right - args: - text: Custom - height: 200.0 - width: 200.0 ---- - -## Interactive Examples - -Showcase your custom widgets with ease. - ---- -layout: two_column_header -sections: - right: - flex: 2 - header: - alignment: bottom_left - left: - alignment: top_center -content: - flex: 2 ---- -::header:: -# Pretty cool right? - -::left:: - -This whole presentation was created using the following markdown. With some custom styling with Mix. - -## Scroll here 👉 - -::right:: - -```markdown ---- -style: custom -layout: two_column_header -background: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExc21yZzZhNzQ3bmt4dGk3amE5a2ozaHQxbTdpeGM4bHlmazdibmJjdSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/8L43c9x5Lvl2o/giphy.gif -sections: - header: - alignment: bottom_center - flex: 2 - left: - flex: 1 ---- - - -# Superdeck - -::left:: -## Beautiful Flutter presentations with Markdown - ---- -style: quote -layout: image -options: - src: https://source.unsplash.com/people-watching-concert-during-night-time-blgOFmPIlr0 - fit: cover -content: - alignment: bottom_right ---- - - -> Create your Flutter presentations faster and easier than ever. -> You can quote me on that. -> ### Leo Farias - - ---- -background: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExZGt1MnQ5N2k3cXVma24wb3V5cThlZ3ExY2NvY3czcmozang0bGQ1ZSZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/XzWd8acQ37byKR4tmd/giphy.gif -style: cover ---- - -# Complex layouts - ---- -layout: image -style: show_sections -options: - src: https://source.unsplash.com/random/900×700/?waves - fit: cover - position: left - flex: 1 ---- - -# Image Layout - -Create beautiful slides with images that fit your content. - -##### Options -yaml -options: - src: https//www.url.com/image.jpg - fit: cover - position: left - flex: 1 - - -> Define position fit and flex options for the image. - - - ---- -layout: two_column -style: show_sections -sections: - left: - flex: 2 - right: - alignment: bottom_left ---- - -::left:: - -# Two Column - -This is a two-column layout. You can use it to compare two different concepts or ideas. - -::right:: - -### Section Options - -Easily customize the content of each section to suit your needs. - -Use front matter to define the layout of each section - - -yaml -sections: - left: - flex: 2 - right: - alignment: bottom_left - - ---- -layout: two_column_header -content: - alignment: center - flex: 2 -sections: - left: - flex: 2 - right: - alignment: bottom_left - header: - alignment: bottom_left -style: show_sections ---- - -# Two Column + Header - - -::left:: - -### Left Section -Easily customize the content of each section to suit your needs. - -Use front matter to define the layout of each section -::right:: - -#### Section Options - -yaml -sections: - left: - alignment: bottom_right - flex: 2 - right: - alignment: bottom_left - header: - alignment: bottom_left - - - ---- -style: rad -layout: two_column -content: - alignment: center -sections: - left: - right: - alignment: bottom_left - flex: 2 ---- - -# Mix - -Integration with Mix gives you complete control over all styling elements in your slides with a simple and intuitive API. - -::right:: - -dart -VariantAttribute get radStyle { - return const SlideVariant('rad')( - $.h1.textStyle.as(GoogleFonts.poppins()), - $.h1.textStyle.fontSize(140), - $.code.decoration.border.all( - color: Colors.red, - width: 3, - ), - $.code.decoration( - color: Colors.black54, - ), - $.code.padding.all(40), - - $.outerContainer.margin.all(60), - - $.innerContainer.borderRadius(25), - $.innerContainer.shadow( - blurRadius: 0, - spreadRadius: 10, - color: Colors.red.withOpacity(1), - ), - $.innerContainer.gradient.radial( - stops: [0.0, 1.0], - radius: 0.7, - colors: [Colors.purple, Colors.deepPurple], - ), - - // Events - onMouseHover((event) { - final position = event.position; - final dx = position.x * 10; - final dy = position.y * 10; - - return Style( - $.innerContainer.transform(_transformMatrix(position)), - $.innerContainer.shadow.offset(dx, dy), - $.innerContainer.gradient.radial( - center: position, - ), - ); - }), - - (onPressed | onLongPressed)( - $.innerContainer.shadow( - blurRadius: 5, - spreadRadius: 1, - offset: Offset.zero, - color: Colors.purpleAccent, - ), - $.innerContainer.border.all(color: Colors.white, width: 1), - $.innerContainer.gradient.radial - .colors([Colors.purpleAccent, Colors.purpleAccent]), - ), - ); -} - - ---- -background: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExeGswdWJvY2oxazJoY3g2Y2poNHBvZXlpYmd5YTg0Z2g0ODRrbng4MyZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/oB6KlAvOuaLtxYy8l4/giphy.gif -style: cover ---- - -# Markdown support - ---- -layout: two_column -sections: - -content: - flex: 4 ---- - -::left:: - - -**Bold Text** - -*Italic Text* - -~~Strikethrough~~ - -`Inline Code` - -[Link here](https://github.com/leoafarias/superdeck) - -::right:: - -Lists - -1. Ordered list item 1 -2. Ordered list item 2 - -- Unordered list item 1 -- Unordered list item 2 - -Quotes - -> If you want to go fast, go alone. -> If you want to go far, go together. -> ### African Proverb - - ---- -layout: two_column ---- - -::left:: - - -Code -dart -int factorial(int n) { - return n == 0 ? 1 : n * factorial(n - 1); -} - - -Tasks -- [ ] Item 1 -- [x] Item 2 - -Subtasks - -- [x] Item 1 - - [ ] Subitem 1 - -::right:: - -Images -![Unsplash Image](https://source.unsplash.com/random/300x200/?landscape) - - -Table - -| Header 1 | Header 2 | -|----------|----------| -| Cell 1A | Cell 1B | -| Cell 2A | Cell 2B | - -Divider - -___ - - ---- -title: "Mermaid example" -layout: two_column ---- - -::left:: - -mermaid -flowchart TD - A[This is crazy] -->|Get money| B(Go shopping) - B --> C{Let me think} - C -->|One| D[Laptop] - C -->|Two| E[iPhone] - C -->|Three| F[fa:fa-car Car] - - -::right:: - -## Mermaid Support - -Superdeck allows you to use Mermaid diagrams in your slides. It automatically converts the code into a visual representation. - ---- -layout: widget -options: - name: demo - position: right - preview: true - args: - text: Custom - height: 200.0 - width: 200.0 ---- - -## Interactive Examples - -Showcase your custom widgets with ease. - ---- -layout: two_column -sections: - right: - flex: 2 ---- - -# Isn't this amazing? - -``` diff --git a/lib/components/atoms/markdown_viewer.dart b/lib/components/atoms/markdown_viewer.dart index cb09441..67bc1c4 100644 --- a/lib/components/atoms/markdown_viewer.dart +++ b/lib/components/atoms/markdown_viewer.dart @@ -1,12 +1,12 @@ import 'package:flutter/material.dart'; import 'package:markdown_viewer/markdown_viewer.dart'; -import 'package:signals/signals_flutter.dart'; +import '../../helpers/measure_size.dart'; import '../../helpers/syntax_highlighter.dart'; import '../../helpers/utils.dart'; import '../../models/asset_model.dart'; +import '../../providers/slide_provider.dart'; import '../../superdeck.dart'; -import 'slide_view.dart'; class AnimatedMarkdownViewer extends ImplicitlyAnimatedWidget { final String content; @@ -97,8 +97,8 @@ Widget _imageBuilder( return Builder( builder: (context) { final size = SlideConstraints.of(context).biggest; - final assets = superdeck.assets.watch(context); - final spec = SlideSpec.of(context); + final assets = SlideProvider.assetsOf(context); + final spec = SlideProvider.specOf(context); final imageSpec = spec.image; final constraints = calculateConstraints(size, spec.contentContainer); return ConstrainedBox( diff --git a/lib/components/atoms/slide_thumbnail.dart b/lib/components/atoms/slide_thumbnail.dart index f0b055d..1632421 100644 --- a/lib/components/atoms/slide_thumbnail.dart +++ b/lib/components/atoms/slide_thumbnail.dart @@ -1,13 +1,16 @@ import 'package:flutter/material.dart'; +import 'package:signals/signals_flutter.dart'; +import '../../helpers/constants.dart'; +import '../../helpers/slide_to_image.dart'; import '../../models/slide_model.dart'; import '../../superdeck.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, @@ -21,7 +24,9 @@ final _previewStyle = AnimatedStyle( // ignore: non_constant_identifier_names final PreviewBox = _previewStyle.box; -class SlideThumbnail extends StatelessWidget { +bool _isGenerating = false; + +class SlideThumbnail extends StatefulWidget { final bool selected; final VoidCallback onTap; final Slide slide; @@ -33,19 +38,85 @@ class SlideThumbnail extends StatelessWidget { required this.slide, }); + @override + State createState() => _SlideThumbnailState(); +} + +class _SlideThumbnailState extends State { + late final thumbnailGen = SlideToImage.instance; + late final quality = ExportQuality.low; + + late final slideImage = futureSignal(() async { + const delay = Durations.short2; + await Future.delayed(delay); + while (_isGenerating) { + await Future.delayed(delay); + } + + _isGenerating = true; + + try { + final data = await thumbnailGen.generate( + // ignore: use_build_context_synchronously + context: context, + quality: quality, + slide: widget.slide, + ); + + return data; + } finally { + _isGenerating = false; + } + }); + + // if widget.slide changes, we need to refresh the widget + @override + void didUpdateWidget(covariant SlideThumbnail oldWidget) { + super.didUpdateWidget(oldWidget); + + if (oldWidget.slide != widget.slide) { + slideImage.refresh(); + } + } + @override Widget build(BuildContext context) { + final selectedColor = widget.selected ? Colors.blue : Colors.transparent; + + final result = slideImage.watch(context); + + Widget buildChild() { + final cacheData = thumbnailGen.getFromCache(widget.slide, quality); + if (cacheData != null) { + return Image.memory(cacheData); + } else { + return result.map( + data: (data) => Image.memory(data), + loading: () { + return const Center( + child: CircularProgressIndicator(), + ); + }, + error: (error, _) { + return const Center( + child: Text('Error loading image'), + ); + }, + ); + } + } + return GestureDetector( - onTap: onTap, + onTap: widget.onTap, child: PreviewBox( style: Style( - box.border.all( - color: selected ? Colors.blue : Colors.transparent, - width: selected ? 2 : 0, - ), + box.border.all.color(selectedColor), ), child: AbsorbPointer( - child: SlideView(slide), + child: AspectRatio( + aspectRatio: kAspectRatio, + child: buildChild(), + ), ), ), ); diff --git a/lib/components/atoms/slide_view.dart b/lib/components/atoms/slide_view.dart index 4756732..5fdc1a6 100644 --- a/lib/components/atoms/slide_view.dart +++ b/lib/components/atoms/slide_view.dart @@ -6,15 +6,23 @@ import '../../helpers/measure_size.dart'; import '../../helpers/utils.dart'; import '../../models/asset_model.dart'; import '../../models/slide_model.dart'; +import '../../providers/slide_provider.dart'; import '../../superdeck.dart'; import '../molecules/scaled_app.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; @@ -27,54 +35,63 @@ class SlideView extends StatelessWidget { final variantStyle = style.applyVariant(variant); - return ScaledWidget( - child: TransitionWidget( - key: ValueKey(slide.transition), - transition: slide.transition, - child: Pressable( - onPress: () {}, - child: MixBuilder( - key: ValueKey(variantStyle), - style: variantStyle.animate(), - builder: (mix) { - final spec = SlideSpec.fromMix(mix); - return Builder(builder: (context) { - return AnimatedMixedBox( - spec: spec.outerContainer, - duration: Durations.medium1, - child: AnimatedMixedBox( - spec: _buildInnerContainerSpec( - slide: slide, - spec: spec.innerContainer, - assets: assets, - context: context, - ), - duration: const Duration(milliseconds: 300), - child: SlideConstraintBuilder( - builder: (_, __) { - 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', - ); - } - }, + return Center( + child: ScaledWidget( + child: TransitionWidget( + key: ValueKey(slide.transition), + transition: slide.transition, + child: Pressable( + onPress: () {}, + child: MixBuilder( + key: ValueKey(variantStyle), + style: variantStyle.animate(), + builder: (mix) { + final spec = SlideSpec.fromMix(mix); + return Builder(builder: (context) { + return AnimatedMixedBox( + spec: spec.outerContainer, + duration: Durations.medium1, + child: AnimatedMixedBox( + spec: _buildInnerContainerSpec( + slide: slide, + spec: spec.innerContainer, + assets: assets, + context: context, + ), + duration: const Duration(milliseconds: 300), + child: SlideProvider( + slide: slide, + spec: spec, + assets: assets, + examples: superdeck.examples.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', + ); + } + }, + ), + ), ), - ), - ); - }); - }, + ); + }); + }, + ), ), ), ), @@ -82,8 +99,8 @@ class SlideView extends StatelessWidget { } } -class SlideConstraints extends InheritedWidget { - const SlideConstraints({ +class SlideConstraintsProvider extends InheritedWidget { + const SlideConstraintsProvider({ required this.constraints, required super.child, super.key, @@ -93,7 +110,7 @@ class SlideConstraints extends InheritedWidget { static BoxConstraints of(BuildContext context) { final slideConstraints = - context.dependOnInheritedWidgetOfExactType(); + context.dependOnInheritedWidgetOfExactType(); if (slideConstraints == null) { throw Exception('SlideConstraints not found in context'); } @@ -101,7 +118,7 @@ class SlideConstraints extends InheritedWidget { } @override - bool updateShouldNotify(SlideConstraints oldWidget) { + bool updateShouldNotify(SlideConstraintsProvider oldWidget) { return oldWidget.constraints != constraints; } } diff --git a/lib/components/molecules/slide_content.dart b/lib/components/molecules/slide_content.dart index 514f536..23d2e57 100644 --- a/lib/components/molecules/slide_content.dart +++ b/lib/components/molecules/slide_content.dart @@ -1,9 +1,8 @@ import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; -import 'package:signals/signals_flutter.dart'; import '../../models/options_model.dart'; -import '../../providers/superdeck_controller.dart'; +import '../../providers/slide_provider.dart'; import '../../styles/style_spec.dart'; import '../atoms/markdown_viewer.dart'; @@ -23,23 +22,30 @@ class SlideContent extends StatelessWidget { final alignment = options?.alignment ?? ContentAlignment.center; - final assets = superdeck.assets.watch(context); + final assets = SlideProvider.assetsOf(context); + final isExporting = SlideProvider.isSnapshotOf(context); + + Widget child = IntrinsicWidth( + child: AnimatedMarkdownViewer( + content: data, + spec: spec, + assets: assets, + duration: Durations.medium1, + ), + ); + + if (!isExporting) { + child = SingleChildScrollView( + child: child, + ); + } return AnimatedMixedBox( duration: const Duration(milliseconds: 300), spec: spec.contentContainer.copyWith( alignment: alignment.toAlignment(), ), - child: SingleChildScrollView( - child: IntrinsicWidth( - child: AnimatedMarkdownViewer( - content: data, - spec: spec, - assets: assets, - duration: const Duration(milliseconds: 300), - ), - ), - ), + child: child, ); } } diff --git a/lib/components/molecules/slide_preview.dart b/lib/components/molecules/slide_preview.dart index 34a27bb..1e08811 100644 --- a/lib/components/molecules/slide_preview.dart +++ b/lib/components/molecules/slide_preview.dart @@ -34,9 +34,9 @@ class SlidePreview extends StatelessWidget { ), ], ), - child: SlideConstraintBuilder(builder: (context, _) { - return SlideView(slide); - }), + child: SlideConstraints( + (_) => SlideView(slide), + ), ), ); } @@ -62,19 +62,17 @@ class SlideMarkdownPreview extends StatelessWidget { ), ), builder: (mix) { - return SlideConstraintBuilder( - builder: (context, size) { - return SingleChildScrollView( - padding: const EdgeInsets.all(40.0), - child: AnimatedMarkdownViewer( - content: "$options\n$data\n", - spec: SlideSpec.of(context), - assets: const [], - duration: Duration.zero, - ), - ); - }, - ); + return SlideConstraints((_) { + return SingleChildScrollView( + padding: const EdgeInsets.all(40.0), + child: AnimatedMarkdownViewer( + content: "$options\n$data\n", + spec: SlideSpec.of(context), + assets: const [], + duration: Duration.zero, + ), + ); + }); }, ); } diff --git a/lib/components/organisms/app_shell.dart b/lib/components/organisms/app_shell.dart index 1f980ac..dea7926 100644 --- a/lib/components/organisms/app_shell.dart +++ b/lib/components/organisms/app_shell.dart @@ -4,7 +4,7 @@ import 'package:go_router/go_router.dart'; import 'package:signals/signals_flutter.dart'; import '../../models/slide_model.dart'; -import '../../providers/superdeck_controller.dart'; +import '../../providers/sd_provider.dart'; import '../../superdeck.dart'; import '../molecules/split_view.dart'; diff --git a/lib/components/organisms/drawer.dart b/lib/components/organisms/drawer.dart index f6de44d..d024a70 100644 --- a/lib/components/organisms/drawer.dart +++ b/lib/components/organisms/drawer.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import '../../providers/superdeck_controller.dart'; +import '../../providers/sd_provider.dart'; enum SideMenu { preview(icon: Icons.play_arrow, label: 'Preview'), diff --git a/lib/components/superdeck_app.dart b/lib/components/superdeck_app.dart index 3077486..9b0e94b 100644 --- a/lib/components/superdeck_app.dart +++ b/lib/components/superdeck_app.dart @@ -62,18 +62,16 @@ class _SuperDeckAppState extends State { WidgetsFlutterBinding.ensureInitialized(); SignalsObserver.instance = null; - await initLocalStorage(); - await SyntaxHighlight.initialize(); + await Future.wait([ + initLocalStorage(), + SyntaxHighlight.initialize(), + _initializeWindowManager(), + ]); await superdeck.initialize( style: widget.style, examples: widget.examples, ); - - // Return if its web - if (kIsWeb) return; - // Must add this line. - await _initializeWindowManager(); } void onRetry() { @@ -115,6 +113,7 @@ class _SuperDeckAppState extends State { } Future _initializeWindowManager() async { + if (kIsWeb) return; // Must add this line. await windowManager.ensureInitialized(); diff --git a/lib/helpers/layout_builder.dart b/lib/helpers/layout_builder.dart index 49ddf81..1310f7f 100644 --- a/lib/helpers/layout_builder.dart +++ b/lib/helpers/layout_builder.dart @@ -1,14 +1,14 @@ -import 'package:cached_network_image/cached_network_image.dart'; import 'package:device_preview/device_preview.dart'; import 'package:flutter/material.dart'; -import 'package:signals/signals_flutter.dart'; import '../components/molecules/code_preview.dart'; import '../components/molecules/slide_content.dart'; import '../models/options_model.dart'; import '../models/slide_model.dart'; +import '../providers/slide_provider.dart'; import '../superdeck.dart'; import 'measure_size.dart'; +import 'utils.dart'; abstract class SlideBuilder extends StatelessWidget { final T config; @@ -113,29 +113,31 @@ class WidgetSlideBuilder extends SplitSlideBuilder { Widget build(BuildContext context) { final options = config.options; - final previewBuilders = superdeck.examples.watch(context); + final examples = SlideProvider.examplesOf(context); - final builder = previewBuilders[options.name]; + final builder = examples[options.name]; - final side = SlideConstraintBuilder(builder: (context, size) { - return MediaQuery( - data: MediaQueryData(size: size), - child: ConstrainedBox( - constraints: BoxConstraints( - maxWidth: size.width, - maxHeight: size.height, - ), - child: DevicePreview( - enabled: options.preview, - builder: (context) { - return CodePreview( - child: builder?.call(options.args), - ); - }, + final side = SlideConstraints( + (size) { + return MediaQuery( + data: MediaQueryData(size: size), + child: ConstrainedBox( + constraints: BoxConstraints( + maxWidth: size.width, + maxHeight: size.height, + ), + child: DevicePreview( + enabled: options.preview, + builder: (context) { + return CodePreview( + child: builder?.call(options.args), + ); + }, + ), ), - ), - ); - }); + ); + }, + ); return buildSplitSlide(side); } @@ -146,35 +148,20 @@ class ImageSlideBuilder extends SplitSlideBuilder { @override Widget build(BuildContext context) { - final spec = SlideSpec.of(context); - final assets = superdeck.assets.watch(context); + final spec = SlideProvider.specOf(context); + final assets = SlideProvider.assetsOf(context); final src = config.options.src; final boxFit = config.options.fit?.toBoxFit() ?? spec.image.fit; - ImageProvider provider; - - if (src.startsWith('http') || src.startsWith('https')) { - provider = CachedNetworkImageProvider(src); - } else { - final asset = assets.firstWhereOrNull( - (element) => element.path == src, - ); - - if (asset != null) { - provider = MemoryImage(asset.bytes); - } else { - provider = AssetImage(src); - } - } - + final uri = Uri.parse(src); final side = Container( height: spec.image.height, width: spec.image.width, alignment: spec.image.alignment, decoration: BoxDecoration( image: DecorationImage( - image: provider, + image: getImageProvider(uri, assets), centerSlice: spec.image.centerSlice, repeat: spec.image.repeat ?? ImageRepeat.noRepeat, filterQuality: spec.image.filterQuality ?? FilterQuality.low, diff --git a/lib/helpers/measure_size.dart b/lib/helpers/measure_size.dart index ca19eab..27318b0 100644 --- a/lib/helpers/measure_size.dart +++ b/lib/helpers/measure_size.dart @@ -46,20 +46,35 @@ class MeasureSize extends SingleChildRenderObjectWidget { } } -class SlideConstraintBuilder extends StatefulWidget { - final Widget Function(BuildContext context, Size size) builder; +class SlideConstraints extends StatefulWidget { + final Widget Function(Size) builder; - const SlideConstraintBuilder({ + const SlideConstraints( + this.builder, { super.key, - required this.builder, }); + 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 - _SlideConstraintBuilderState createState() => _SlideConstraintBuilderState(); + _SlideConstraintsState createState() => _SlideConstraintsState(); } -class _SlideConstraintBuilderState extends State { +class _SlideConstraintsState extends State { Size? _widgetSize; void _onWidgetSizeChange(Size size) { @@ -83,11 +98,11 @@ class _SlideConstraintBuilderState extends State { return MeasureSize( onChange: _onWidgetSizeChange, - child: SlideConstraints( + child: SlideConstraintsProvider( constraints: constraintSize, child: Builder( builder: (BuildContext context) { - return widget.builder(context, size); + return widget.builder(size); }, ), ), diff --git a/lib/helpers/slide_to_image.dart b/lib/helpers/slide_to_image.dart index ffa4316..13ad1a4 100644 --- a/lib/helpers/slide_to_image.dart +++ b/lib/helpers/slide_to_image.dart @@ -1,20 +1,79 @@ import 'dart:async'; -import 'dart:typed_data'; +import 'dart:io'; import 'dart:ui' as ui; +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:path_provider/path_provider.dart'; import '../components/atoms/slide_view.dart'; import '../helpers/constants.dart'; import '../models/slide_model.dart'; -Map _imageCache = {}; +enum ExportQuality { + low('Low', pixelRatio: 0.4), + good('Good', pixelRatio: 1), + better('Better', pixelRatio: 2), + best('Best', pixelRatio: 3); + + const ExportQuality(this.label, {required this.pixelRatio}); + + final String label; + final double pixelRatio; +} -String getCacheKey(Slide slide, ExportQuality quality) { +String _getCacheKey(Slide slide, ExportQuality quality) { return '${slide.hashCode}_${quality.label}'; } +final Map _imageCache = {}; +// Create a simple cache class that also stores the image in application folder +// to avoid generating the image again + +class ImageCache { + const ImageCache._(); + + static ImageCache get instance => _instance; + + static const _instance = ImageCache._(); + + Future set(String key, Uint8List image) async { + _imageCache[key] = image; + if (kIsWeb) return; + try { + final directory = await getApplicationSupportDirectory(); + await get(key); + final file = File('${directory.path}/$key.png'); + await file.writeAsBytes(image); + } catch (e) { + print(e); + } + } + + Future get(String key) async { + if (_imageCache.containsKey(key)) { + return _imageCache[key]; + } + if (kIsWeb) return null; + try { + final directory = await getApplicationSupportDirectory(); + final file = File('${directory.path}/$key.png'); + + if (await file.exists()) { + print('exists!'); + final data = await file.readAsBytes(); + _imageCache[key] = data; + return data; + } + return null; + } catch (e) { + print(e); + return null; + } + } +} + class SlideToImage { SlideToImage._(); @@ -22,55 +81,44 @@ class SlideToImage { static final _instance = SlideToImage._(); + final cache = ImageCache.instance; + + Uint8List? getFromCache(Slide slide, ExportQuality quality) { + final key = _getCacheKey(slide, quality); + return _imageCache[key]; + } + Future generate({ required BuildContext context, ExportQuality quality = ExportQuality.good, required Slide slide, }) async { - final key = getCacheKey(slide, quality); - if (_imageCache.containsKey(key)) { - return _imageCache[key]!; + final key = _getCacheKey(slide, quality); + final cacheData = await cache.get(key); + if (cacheData != null) { + print('exists'); + return cacheData; } - final image = await getImageFromWidget(context, quality, SlideView(slide)); + final image = await fromWidgetToImage( + SlideView.snapshot(slide), + context: context, + pixelRatio: quality.pixelRatio, + targetSize: kResolution, + ); final convertedImage = await getImageInBytes(image); image.dispose(); - _imageCache[key] = convertedImage; + await cache.set(key, convertedImage); return convertedImage; } - Future getImageFromWidget( - BuildContext context, - ExportQuality quality, - Widget child, - ) async { - return await fromWidgetToImage( - child, - pixelRatio: quality.pixelRatio, - context: context, - targetSize: kResolution, - ); - } - Future getImageInBytes(ui.Image image) async { final byteData = await image.toByteData(format: ui.ImageByteFormat.png); return byteData!.buffer.asUint8List(); } } -enum ExportQuality { - low('Low', pixelRatio: 0.5), - good('Good', pixelRatio: 1), - better('Better', pixelRatio: 2), - best('Best', pixelRatio: 3); - - const ExportQuality(this.label, {required this.pixelRatio}); - - final String label; - final double pixelRatio; -} - Future fromWidgetToImage( Widget widget, { required double pixelRatio, @@ -113,7 +161,11 @@ Future fromWidgetToImage( ), ); - final pipelineOwner = PipelineOwner(); + final pipelineOwner = PipelineOwner( + onNeedVisualUpdate: () { + isDirty = true; + }, + ); final buildOwner = BuildOwner( focusManager: FocusManager(), onBuildScheduled: () { @@ -133,8 +185,8 @@ Future fromWidgetToImage( ).attachToRenderTree(buildOwner); ui.Image? image; while (retryCount > 0) { - // await Future.delayed(const Duration(seconds: 1)); isDirty = false; + image = await _captureImage( buildOwner: buildOwner, rootElement: rootElement, @@ -148,7 +200,8 @@ Future fromWidgetToImage( break; } - await Future.delayed(Durations.medium1); + await _waitForImagesLoaded(rootElement); + // await Future.delayed(Durations.short2); retryCount--; } @@ -174,7 +227,7 @@ Future _captureImage({ pipelineOwner.flushLayout(); pipelineOwner.flushCompositingBits(); pipelineOwner.flushPaint(); - + await Future.delayed(Durations.short2); return repaintBoundary.toImage(pixelRatio: pixelRatio); } @@ -184,20 +237,18 @@ Future _waitForImagesLoaded(Element rootElement) async { void traverseElement(Element element) { if (element.widget is Image) { final imageProvider = (element.widget as Image).image; - print(element.widget); + final stream = imageProvider.resolve(ImageConfiguration.empty); - print('Resolved ImageStream: $stream'); + final completer = Completer(); late ImageStreamListener listener; listener = ImageStreamListener( (ImageInfo image, bool synchronousCall) { - print('Image loaded: $image'); completer.complete(); stream.removeListener(listener); }, onError: (dynamic exception, StackTrace? stackTrace) { - print('Image loading error: $exception'); completer.completeError(exception, stackTrace); stream.removeListener(listener); }, @@ -205,7 +256,6 @@ Future _waitForImagesLoaded(Element rootElement) async { stream.addListener(listener); futures.add(completer.future); - print('Number of futures: ${futures.length}'); } element.visitChildren(traverseElement); diff --git a/lib/helpers/syntax_highlighter.dart b/lib/helpers/syntax_highlighter.dart index dd4bffa..235a61a 100644 --- a/lib/helpers/syntax_highlighter.dart +++ b/lib/helpers/syntax_highlighter.dart @@ -18,7 +18,7 @@ class SyntaxHighlight { static final List _secondarySupportedLangs = []; - static initialize() async { + static Future initialize() async { await Highlighter.initialize(_mainSupportedLanguages); // Load the default light theme and create a highlightfer. diff --git a/lib/helpers/utils.dart b/lib/helpers/utils.dart index 2d2ec89..5bdecbd 100644 --- a/lib/helpers/utils.dart +++ b/lib/helpers/utils.dart @@ -1,7 +1,7 @@ import 'dart:convert'; import 'package:cached_network_image/cached_network_image.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:mix/mix.dart'; import 'package:yaml/yaml.dart'; diff --git a/lib/providers/superdeck_controller.dart b/lib/providers/sd_provider.dart similarity index 99% rename from lib/providers/superdeck_controller.dart rename to lib/providers/sd_provider.dart index f9cafaf..b584564 100644 --- a/lib/providers/superdeck_controller.dart +++ b/lib/providers/sd_provider.dart @@ -28,7 +28,6 @@ class SuperDeckProvider { final config = signal(const ProjectConfig.empty()); final loading = signal(true); - final list = listSignal([]); final slides = listSignal([]); diff --git a/lib/providers/slide_provider.dart b/lib/providers/slide_provider.dart new file mode 100644 index 0000000..c089e07 --- /dev/null +++ b/lib/providers/slide_provider.dart @@ -0,0 +1,93 @@ +// 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, + assets, + isExporting, + examples, +} + +class SlideProvider extends InheritedModel { + final Slide slide; + final SlideSpec spec; + final List assets; + // If slide is a snapshot for image generation + final bool isSnapshot; + final Map examples; + + const SlideProvider({ + super.key, + required this.slide, + required this.spec, + required this.assets, + required this.isSnapshot, + required this.examples, + 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.assets) && + assets != oldWidget.assets) { + return true; + } + if (dependencies.contains(SlideProviderAspect.isExporting) && + isSnapshot != oldWidget.isSnapshot) { + return true; + } + if (dependencies.contains(SlideProviderAspect.examples) && + examples != oldWidget.examples) { + return true; + } + return false; + } + + static Slide slideOf(BuildContext context) { + return SlideProvider.of(context).slide; + } + + static SlideSpec specOf(BuildContext context) { + return SlideProvider.of(context).spec; + } + +// Only update if the individual asset changes + static List assetsOf(BuildContext context) { + return SlideProvider.of(context).assets; + } + + static bool isSnapshotOf(BuildContext context) { + return SlideProvider.of(context).isSnapshot; + } + +// TODO: only get notified if the individual example changes + static Map examplesOf(BuildContext context) { + return SlideProvider.of(context).examples; + } +} diff --git a/lib/screens/export_screen.dart b/lib/screens/export_screen.dart index 4c823ac..812f95e 100644 --- a/lib/screens/export_screen.dart +++ b/lib/screens/export_screen.dart @@ -134,6 +134,7 @@ class _ExportingProcessScreenState extends State { final _pageController = PageController(); late List _slides; late final _images = listSignal([]); + late final superdeck = SuperDeckProvider.instance; @override void initState() { diff --git a/lib/superdeck.dart b/lib/superdeck.dart index b5d6720..747f787 100644 --- a/lib/superdeck.dart +++ b/lib/superdeck.dart @@ -2,7 +2,7 @@ library superdeck; export 'package:mix/mix.dart'; export 'package:superdeck/components/superdeck_app.dart'; -export 'package:superdeck/providers/superdeck_controller.dart'; +export 'package:superdeck/providers/sd_provider.dart'; export 'package:superdeck/styles/style_attribute.dart'; export 'package:superdeck/styles/style_dto.dart'; export 'package:superdeck/styles/style_spec.dart'; diff --git a/pubspec.lock b/pubspec.lock index d632042..276e593 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -694,13 +694,13 @@ packages: source: hosted version: "1.0.1" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider - sha256: b27217933eeeba8ff24845c34003b003b2b22151de3c908d0e679e8fe1aa078b + sha256: c9e7d3a4cd1410877472158bee69963a4579f78b68c65a2b7d40d1a7a88bb161 url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.3" path_provider_android: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index df0a7c9..717d2d6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -32,6 +32,7 @@ dependencies: go_router: ^13.2.4 file_picker: ^8.0.1 universal_html: ^2.2.4 + path_provider: ^2.1.3 dev_dependencies: