├── .github └── FUNDING.yml ├── android ├── settings_aar.gradle ├── gradle.properties ├── .gitignore ├── app │ ├── src │ │ ├── main │ │ │ ├── res │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ └── ic_launcher.png │ │ │ │ ├── drawable-hdpi │ │ │ │ │ ├── ic_shortcut_add.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-mdpi │ │ │ │ │ ├── ic_shortcut_add.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xhdpi │ │ │ │ │ ├── ic_shortcut_add.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xxhdpi │ │ │ │ │ ├── ic_shortcut_add.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── drawable-xxxhdpi │ │ │ │ │ ├── ic_shortcut_add.png │ │ │ │ │ └── ic_launcher_foreground.png │ │ │ │ ├── values │ │ │ │ │ ├── colors.xml │ │ │ │ │ └── styles.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ │ └── ic_launcher.xml │ │ │ │ ├── drawable │ │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable-v21 │ │ │ │ │ └── launch_background.xml │ │ │ │ └── values-night │ │ │ │ │ └── styles.xml │ │ │ ├── kotlin │ │ │ │ └── net │ │ │ │ │ └── redsolver │ │ │ │ │ └── noteless │ │ │ │ │ └── MainActivity.kt │ │ │ └── AndroidManifest.xml │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ └── profile │ │ │ └── AndroidManifest.xml │ └── build.gradle ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties ├── settings.gradle └── build.gradle ├── fastlane └── metadata │ └── android │ └── en-US │ ├── title.txt │ ├── short_description.txt │ ├── images │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 2.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ └── 9.png │ └── full_description.txt ├── assets ├── i18n │ └── en.json ├── icon │ ├── icon.png │ ├── icon_ios.png │ └── adaptive_icon.png ├── fonts │ ├── FiraMono-Regular.ttf │ └── LICENSE ├── tutorial │ ├── attachments │ │ ├── icon.png │ │ └── icon_small.png │ └── notes │ │ ├── 09 - Synchronization.md │ │ ├── 10 - Version Control.md │ │ ├── 04 - The Editor.md │ │ ├── 02 - The Sidebar.md │ │ ├── Wrapping up 🎉.md │ │ ├── Welcome to Noteless 🙋.md │ │ ├── 06 - Tags.md │ │ ├── 01 - The Data Directory.md │ │ ├── 03 - The Main Page.md │ │ ├── 07 - Multi-Note Editing.md │ │ ├── 08 - Linking Attachments, Notes, Tags and Searches.md │ │ └── 05 - Notes.md └── preview │ ├── fonts │ ├── KaTeX_AMS-Regular.ttf │ ├── KaTeX_Main-Bold.ttf │ ├── KaTeX_Main-Bold.woff │ ├── KaTeX_Main-Bold.woff2 │ ├── KaTeX_Main-Italic.ttf │ ├── KaTeX_Math-Italic.ttf │ ├── KaTeX_AMS-Regular.woff │ ├── KaTeX_AMS-Regular.woff2 │ ├── KaTeX_Fraktur-Bold.ttf │ ├── KaTeX_Fraktur-Bold.woff │ ├── KaTeX_Main-Italic.woff │ ├── KaTeX_Main-Italic.woff2 │ ├── KaTeX_Main-Regular.ttf │ ├── KaTeX_Main-Regular.woff │ ├── KaTeX_Math-Italic.woff │ ├── KaTeX_Math-Italic.woff2 │ ├── KaTeX_Size1-Regular.ttf │ ├── KaTeX_Size2-Regular.ttf │ ├── KaTeX_Size3-Regular.ttf │ ├── KaTeX_Size4-Regular.ttf │ ├── KaTeX_Caligraphic-Bold.ttf │ ├── KaTeX_Fraktur-Bold.woff2 │ ├── KaTeX_Fraktur-Regular.ttf │ ├── KaTeX_Fraktur-Regular.woff │ ├── KaTeX_Main-BoldItalic.ttf │ ├── KaTeX_Main-BoldItalic.woff │ ├── KaTeX_Main-Regular.woff2 │ ├── KaTeX_Math-BoldItalic.ttf │ ├── KaTeX_Math-BoldItalic.woff │ ├── KaTeX_SansSerif-Bold.ttf │ ├── KaTeX_SansSerif-Bold.woff │ ├── KaTeX_SansSerif-Bold.woff2 │ ├── KaTeX_SansSerif-Italic.ttf │ ├── KaTeX_Script-Regular.ttf │ ├── KaTeX_Script-Regular.woff │ ├── KaTeX_Script-Regular.woff2 │ ├── KaTeX_Size1-Regular.woff │ ├── KaTeX_Size1-Regular.woff2 │ ├── KaTeX_Size2-Regular.woff │ ├── KaTeX_Size2-Regular.woff2 │ ├── KaTeX_Size3-Regular.woff │ ├── KaTeX_Size3-Regular.woff2 │ ├── KaTeX_Size4-Regular.woff │ ├── KaTeX_Size4-Regular.woff2 │ ├── KaTeX_Caligraphic-Bold.woff │ ├── KaTeX_Caligraphic-Bold.woff2 │ ├── KaTeX_Fraktur-Regular.woff2 │ ├── KaTeX_Main-BoldItalic.woff2 │ ├── KaTeX_Math-BoldItalic.woff2 │ ├── KaTeX_SansSerif-Italic.woff │ ├── KaTeX_SansSerif-Italic.woff2 │ ├── KaTeX_SansSerif-Regular.ttf │ ├── KaTeX_SansSerif-Regular.woff │ ├── KaTeX_Typewriter-Regular.ttf │ ├── KaTeX_Caligraphic-Regular.ttf │ ├── KaTeX_Caligraphic-Regular.woff │ ├── KaTeX_Caligraphic-Regular.woff2 │ ├── KaTeX_SansSerif-Regular.woff2 │ ├── KaTeX_Typewriter-Regular.woff │ └── KaTeX_Typewriter-Regular.woff2 │ ├── prism.LICENSE │ ├── katex.LICENSE │ ├── mermaid.LICENSE │ ├── katex.auto-render.min.js │ └── prism.css ├── ios ├── Flutter │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── AppFrameworkInfo.plist ├── Runner │ ├── Runner-Bridging-Header.h │ ├── Assets.xcassets │ │ ├── LaunchImage.imageset │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ ├── README.md │ │ │ └── Contents.json │ │ └── AppIcon.appiconset │ │ │ ├── Icon-App-20x20@1x.png │ │ │ ├── Icon-App-20x20@2x.png │ │ │ ├── Icon-App-20x20@3x.png │ │ │ ├── Icon-App-29x29@1x.png │ │ │ ├── Icon-App-29x29@2x.png │ │ │ ├── Icon-App-29x29@3x.png │ │ │ ├── Icon-App-40x40@1x.png │ │ │ ├── Icon-App-40x40@2x.png │ │ │ ├── Icon-App-40x40@3x.png │ │ │ ├── Icon-App-50x50@1x.png │ │ │ ├── Icon-App-50x50@2x.png │ │ │ ├── Icon-App-57x57@1x.png │ │ │ ├── Icon-App-57x57@2x.png │ │ │ ├── Icon-App-60x60@2x.png │ │ │ ├── Icon-App-60x60@3x.png │ │ │ ├── Icon-App-72x72@1x.png │ │ │ ├── Icon-App-72x72@2x.png │ │ │ ├── Icon-App-76x76@1x.png │ │ │ ├── Icon-App-76x76@2x.png │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ └── Contents.json │ ├── AppDelegate.swift │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.storyboard │ └── Info.plist ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── WorkspaceSettings.xcsettings │ │ └── IDEWorkspaceChecks.plist ├── Runner.xcodeproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme └── .gitignore ├── screenshots ├── screen1.png ├── screen10.png ├── screen11.png ├── screen12.png ├── screen2.png ├── screen3.png ├── screen4.png ├── screen5.png ├── screen6.png ├── screen7.png ├── screen8.png └── screen9.png ├── .metadata ├── skydroid-dev.yaml ├── lib ├── data │ └── samples.dart ├── model │ └── note.dart ├── main.dart ├── page │ ├── about.dart │ ├── preview.dart │ └── settings.dart ├── provider │ └── theme.dart ├── editor │ ├── pairer.dart │ └── syntax_highlighter.dart ├── utils │ └── yaml.dart ├── sync │ └── webdav.dart └── store │ ├── persistent.dart │ └── notes.dart ├── test └── widget_test.dart ├── LICENSE ├── .gitignore ├── skydroid-app.yaml ├── pubspec.yaml ├── CHANGELOG.md ├── README.md └── pubspec.lock /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: redsolver -------------------------------------------------------------------------------- /android/settings_aar.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/title.txt: -------------------------------------------------------------------------------- 1 | Noteless -------------------------------------------------------------------------------- /assets/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "// TODO":"Add flutter_i18n" 3 | } -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" -------------------------------------------------------------------------------- /assets/icon/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/icon/icon.png -------------------------------------------------------------------------------- /assets/icon/icon_ios.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/icon/icon_ios.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/short_description.txt: -------------------------------------------------------------------------------- 1 | A markdown note-taking app for mobile devices. -------------------------------------------------------------------------------- /screenshots/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen1.png -------------------------------------------------------------------------------- /screenshots/screen10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen10.png -------------------------------------------------------------------------------- /screenshots/screen11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen11.png -------------------------------------------------------------------------------- /screenshots/screen12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen12.png -------------------------------------------------------------------------------- /screenshots/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen2.png -------------------------------------------------------------------------------- /screenshots/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen3.png -------------------------------------------------------------------------------- /screenshots/screen4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen4.png -------------------------------------------------------------------------------- /screenshots/screen5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen5.png -------------------------------------------------------------------------------- /screenshots/screen6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen6.png -------------------------------------------------------------------------------- /screenshots/screen7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen7.png -------------------------------------------------------------------------------- /screenshots/screen8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen8.png -------------------------------------------------------------------------------- /screenshots/screen9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/screenshots/screen9.png -------------------------------------------------------------------------------- /assets/icon/adaptive_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/icon/adaptive_icon.png -------------------------------------------------------------------------------- /assets/fonts/FiraMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/fonts/FiraMono-Regular.ttf -------------------------------------------------------------------------------- /assets/tutorial/attachments/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/tutorial/attachments/icon.png -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_AMS-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_AMS-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Bold.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Bold.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Bold.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Italic.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-Italic.ttf -------------------------------------------------------------------------------- /assets/tutorial/attachments/icon_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/tutorial/attachments/icon_small.png -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableR8=true 4 | android.enableJetifier=true -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_AMS-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_AMS-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_AMS-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_AMS-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Bold.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Bold.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Italic.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Italic.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-Italic.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-Italic.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size1-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size1-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size2-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size2-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size3-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size3-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size4-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size4-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Bold.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Bold.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-BoldItalic.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-BoldItalic.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-BoldItalic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-BoldItalic.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Bold.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Bold.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Bold.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Italic.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Script-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Script-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Script-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Script-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Script-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Script-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size1-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size1-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size1-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size1-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size2-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size2-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size2-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size2-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size3-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size3-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size3-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size3-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size4-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size4-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Size4-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Size4-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Bold.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Bold.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Fraktur-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Fraktur-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Main-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Main-BoldItalic.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Math-BoldItalic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Math-BoldItalic.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Italic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Italic.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Italic.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Italic.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Typewriter-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Typewriter-Regular.ttf -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Regular.ttf -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Caligraphic-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Caligraphic-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_SansSerif-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_SansSerif-Regular.woff2 -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Typewriter-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Typewriter-Regular.woff -------------------------------------------------------------------------------- /assets/preview/fonts/KaTeX_Typewriter-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/assets/preview/fonts/KaTeX_Typewriter-Regular.woff2 -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_shortcut_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-hdpi/ic_shortcut_add.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_shortcut_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-mdpi/ic_shortcut_add.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_shortcut_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xhdpi/ic_shortcut_add.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_shortcut_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_shortcut_add.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_shortcut_add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_shortcut_add.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #ffffff 4 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/10.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/11.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/12.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/7.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/8.png -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/images/phoneScreenshots/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/fastlane/metadata/android/en-US/images/phoneScreenshots/9.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/android/app/src/main/res/drawable-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redsolver/noteless/HEAD/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /android/app/src/main/kotlin/net/redsolver/noteless/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package net.redsolver.noteless 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | 7 | } -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip 7 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 20e59316b8b8474554b38493b8ca888794b0234a 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /skydroid-dev.yaml: -------------------------------------------------------------------------------- 1 | name: noteless.redsolver 2 | metadataFile: skydroid-app.yaml 3 | skynetPortal: https://skyportal.xyz 4 | 5 | checkVersion: 6 | file: pubspec.yaml 7 | versionCode: 'version:\s.+\+(\d+)' 8 | versionName: 'version:\s(.+)\+' 9 | 10 | uploadBuild: 11 | file: build/app/outputs/flutter-apk/app-release.apk 12 | 13 | updateMetadataFile: 14 | updateWhatsNew: false 15 | 16 | # uploadMetadata: 17 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /assets/tutorial/notes/09 - Synchronization.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Advanced, Notebooks/Tutorial] 3 | title: 09 - Synchronization 4 | created: '2018-12-16T23:15:11.439Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 09 - Synchronization 9 | 10 | To synchronize your notes, you can use an external data directory via `Settings -> Data Directory` and then use any other Sync-App to sync your notes. 11 | 12 | Personally I recommend [Syncthing](https://syncthing.net/). 13 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /fastlane/metadata/android/en-US/full_description.txt: -------------------------------------------------------------------------------- 1 | Features 2 | 3 | * Markdown-optimized editor with syntax highlighting 4 | * Supports Github Flavored Markdown, KaTeX and mermaidjs for diagrams 5 | * Tags for organizing your notes 6 | * Pin, Star and sort your notes by title or different dates 7 | * Very themable - dark/light mode and accent color 8 | * Full-text search 9 | * File Attachments that can be embedded into a note 10 | * Multi-Note Editing 11 | * Slide actions for easier editing 12 | * Tutorial notes which explain how to use the app -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /lib/data/samples.dart: -------------------------------------------------------------------------------- 1 | class Samples { 2 | static List tutorialNotes = [ 3 | 'Welcome to Noteless 🙋.md', 4 | '01 - The Data Directory.md', 5 | '02 - The Sidebar.md', 6 | '03 - The Main Page.md', 7 | '04 - The Editor.md', 8 | '05 - Notes.md', 9 | '06 - Tags.md', 10 | '07 - Multi-Note Editing.md', 11 | '08 - Linking Attachments, Notes, Tags and Searches.md', 12 | '09 - Synchronization.md', 13 | '10 - Version Control.md', 14 | 'Wrapping up 🎉.md', 15 | ]; 16 | static List tutorialAttachments = ['icon.png', 'icon_small.png']; 17 | } 18 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | //import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:app/main.dart'; 12 | 13 | void main() { 14 | // TODO Maybe add some tests 15 | } 16 | -------------------------------------------------------------------------------- /assets/tutorial/notes/10 - Version Control.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Advanced, Notebooks/Tutorial] 3 | title: 10 - Version Control 4 | created: '2019-03-12T15:20:40.396Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 10 - Version Control 9 | 10 | Noteless doesn't have version control built-in, but since the data directory is just a regular directory you could make it a git repository and every once in a while take a snapshot of your notes, or perhaps a small script could make commits automatically for you everytime something changes. 11 | 12 | You have to use an external data directory for this via `Settings -> Data Directory`. -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /assets/tutorial/notes/04 - The Editor.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 04 - The Editor 4 | created: '2019-03-12T15:20:40.393Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 04 - The Editor 9 | 10 | The editor is where you can edit and preview the currently active note. 11 | 12 | It can be accessed by tapping on a note. 13 | 14 | ## Toolbar 15 | 16 | The toolbar has a slider to toggle between edit and preview mode. 17 | 18 | The popup menu in the top right allows you to favorite/unfavorite, pin/unpin a note and add edit tags or attachments. 19 | 20 | ## Editor 21 | 22 | When editing you'll use a simple text field. It features copy/paste functionality and an undo button 23 | 24 | ## Saving 25 | 26 | When you're done editing a note, you can save it via the "Save" button in the toolbar. -------------------------------------------------------------------------------- /assets/tutorial/notes/02 - The Sidebar.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 02 - The Sidebar 4 | created: '2019-03-12T15:20:40.392Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 02 - The Sidebar 9 | 10 | The sidebar lists all your tags and you can access the Settings from there. 11 | 12 | It can be opened by clicking the hamburger icon in the top left corner or by swiping from the left edge. 13 | 14 | 15 | ## Categories 16 | 17 | - **All Notes**: This section contains all notes. 18 | - **Favorites**: This section contains all notes you've favorited. 19 | - **Untagged**: This section contains all notes that have no tags. 20 | - **Trash**: This section contains all notes that have been deleted. These notes won't be displayed in any other category. 21 | - **All your other tags** 22 | 23 | You can create sub-categories by using nested tags. (`tag/subtag`) 24 | -------------------------------------------------------------------------------- /assets/tutorial/notes/Wrapping up 🎉.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Notebooks/Tutorial] 3 | title: "Wrapping up \U0001F389" 4 | created: '2019-03-12T15:20:40.398Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # Wrapping up :tada: 9 | 10 | Awesome, you've reached the end of the tutorial! 11 | 12 | The next step is deleting all these tutorial notes, you can do this one-by-one, using multi-note editing, or you could just trash the whole `notes` sub-directory from your data directory. 13 | 14 | ## Feedback 15 | 16 | If you've reached this far chances are you're considering using Noteless as your main mobile note-taking app, that's great! 17 | 18 | Feel free to [contact us](https://github.com/redsolver/noteless/issues) about any issues you may encounter, any features suggestions and generally sharing your opinion about Noteless and how we can improve it. 19 | 20 | Have a wonderful day! :wave: 21 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /assets/tutorial/notes/Welcome to Noteless 🙋.md: -------------------------------------------------------------------------------- 1 | --- 2 | attachments: [icon.png] 3 | pinned: true 4 | tags: [Notebooks/Tutorial] 5 | title: "Welcome to Noteless \U0001F64B" 6 | created: '2019-05-16T14:56:08.914Z' 7 | modified: '2020-07-05T12:00:00.000Z' 8 | --- 9 | 10 | # Welcome to Noteless :raising_hand_woman: 11 | 12 |

13 | 14 |

15 | 16 | ## About 17 | 18 | Noteless is a markdown note-taking app which aims to be compatible with notes saved in [Notable](https://notable.app/). 19 | 20 | ## Tutorial 21 | 22 | Some tutorial notes have been added to your data directory. 23 | 24 | They will guide you towards the main features Noteless provides. 25 | 26 | You can read them in a nicer way by pressing the switch in the toolbar. 27 | 28 | Once you're done exploring feel free to permanently delete them, if at any point you'd like to read them again you can re-add them to your data directory via the `Settings -> More -> Recreate tutorial notes` option. 29 | 30 | ## Credits 31 | 32 | This tutorial notes are based on the ones in [Notable](https://notable.app/). -------------------------------------------------------------------------------- /lib/model/note.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | class Note { 4 | String title; 5 | DateTime created; 6 | DateTime modified; 7 | List tags = []; 8 | List attachments = []; 9 | bool pinned = false; 10 | bool favorited = false; 11 | bool deleted = false; 12 | File file; 13 | 14 | bool usesMillis = false; 15 | bool usesUpdatedInsteadOfModified = false; 16 | Map additionalFrontMatterKeys; 17 | 18 | bool hasTag(String cTag) { 19 | if (cTag != '') { 20 | if (cTag == 'Trash') { 21 | return deleted; 22 | } else if (cTag == 'Favorites') { 23 | return favorited; 24 | } else if (cTag == 'Untagged') { 25 | return tags.isEmpty; 26 | } else { 27 | bool hasTag = false; 28 | for (String tag in tags) { 29 | if (tag.startsWith(cTag)) { 30 | hasTag = true; 31 | break; 32 | } 33 | } 34 | if (!hasTag) return false; 35 | } 36 | } else {} 37 | if (deleted) return false; 38 | //if (note.deleted) return false; 39 | return true; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2019 redsolver 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a 6 | copy of this software and associated documentation files (the "Software"), 7 | to deal in the Software without restriction, including without limitation 8 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 9 | and/or sell copies of the Software, and to permit persons to whom the 10 | Software is furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 20 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 21 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /assets/preview/prism.LICENSE: -------------------------------------------------------------------------------- 1 | MIT LICENSE 2 | 3 | Copyright (c) 2012 Lea Verou 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /assets/preview/katex.LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2018 Khan Academy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /assets/preview/mermaid.LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 - 2018 Knut Sveidqvist 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /assets/tutorial/notes/06 - Tags.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 06 - Tags 4 | created: '2019-01-28T19:46:36.681Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 06 - Tags 9 | 10 | Notes can have multiple tags, which are useful for better categorization. 11 | 12 | ## Syntax 13 | 14 | - **Root**: Root tags don't contain any forward slash (`/`), they will be rendered in the sidebar. 15 | 16 | - **Nested**: Tags can also be nested, _indefinitely_, just write them like a path, separating the levels with a forward slash: `foo/bar/baz`. 17 | 18 | ## Collapse/Expand 19 | 20 | Tags with children can be collapsed/expanded, just click the arrow next to them. 21 | 22 | ## Editing 23 | 24 | There are multiple ways to add/remove tags: 25 | 26 | - **Single-note editing**: You can edit a note's tags via the popup menu in the toolbar. 27 | - **Multi-note editing**: Tags can be added/removed from multiple notes at once via the [multi-note editing](@note/07 - Multi-Note Editing.md) features provided. 28 | - **Advanced search & replace**: Alternatively you could just open your data directory with your editor and perform a search & replace there, this way you can also use advanced features like regexes. 29 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.4' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | 13 | /* subprojects { 14 | project.configurations.all { 15 | resolutionStrategy.eachDependency { details -> 16 | if (details.requested.group == 'androidx.lifecycle' && details.requested.name == 'lifecycle-runtime') { 17 | details.useVersion "2.1.0" 18 | }else 19 | if (details.requested.group == 'androidx.core' && details.requested.name == 'core') { 20 | details.useVersion "1.1.0" 21 | } 22 | } 23 | } 24 | } */ 25 | } 26 | 27 | allprojects { 28 | repositories { 29 | google() 30 | jcenter() 31 | } 32 | } 33 | 34 | rootProject.buildDir = '../build' 35 | subprojects { 36 | project.buildDir = "${rootProject.buildDir}/${project.name}" 37 | } 38 | subprojects { 39 | project.evaluationDependsOn(':app') 40 | } 41 | 42 | task clean(type: Delete) { 43 | delete rootProject.buildDir 44 | } 45 | -------------------------------------------------------------------------------- /assets/tutorial/notes/01 - The Data Directory.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 01 - The Data Directory 4 | created: '2018-12-16T21:43:52.886Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 01 - The Data Directory 9 | 10 | The data directory is where all your notes and attachments will be stored, it has the following structure: 11 | 12 | ``` 13 | /path/to/your/data_directory 14 | ├─┬ attachments 15 | │ ├── foo.ext 16 | │ ├── bar.ext 17 | │ └── … 18 | └─┬ notes 19 | ├── foo.md 20 | ├── bar.md 21 | └── … 22 | ``` 23 | 24 | ## Features 25 | 26 | - The data directory gives you freedom since your notes are never locked into some sort of proprietary database, all your files use sane formats and are easily accessible and portable. 27 | - You can also change the data directory at any time via `Settings -> Data Directory`, the current content won't be copied over to the new one. 28 | - You can edit your notes/attachments without even using Noteless, all changes you make to them will be reflected here (after a quick refresh). In fact you could also import a Markdown note simply by copying it into the `notes` directory. 29 | 30 | ## Advanced Features 31 | 32 | The data directory allows you to leverage third-party tools to have powerful features like synchronization, versioning and encryption, we'll talk about those in the [advanced](@tag/Advanced) sections. 33 | -------------------------------------------------------------------------------- /assets/tutorial/notes/03 - The Main Page.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 03 - The Main Page 4 | created: '2019-03-12T15:20:46.181Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 03 - The Main Page 9 | 10 | The main page shows you all notes contained in the currently active category, properly ordered and filtered by the search query. 11 | 12 | ## Search 13 | 14 | To search just click on the top right icon and type something in the search bar. 15 | 16 | The content of notes is searched in too, a full-match is always required. 17 | 18 | ## New Note Button 19 | 20 | On the bottom right there's a button for creating a new note. 21 | 22 | ## Sorting Order 23 | 24 | Right below the title bar you can change the order in which notes are being displayed. 25 | 26 | By default this is by `Title - Ascending`, so that the tutorial notes get displayed in order, but you might want to change this later to `Date Modified - Descending`, so that the most recently edited notes are at the top. 27 | 28 | ## Notes 29 | 30 | Lastly there's the notes list. 31 | 32 | Notes will have some badges if they are pinned, favorited or have attachments. 33 | 34 | Pinned notes are displayed before the others. 35 | 36 | If you long-press them you can access some commands, all of them are also available by sliding a note to the side, most of them are also available from the editor's toolbar. 37 | -------------------------------------------------------------------------------- /assets/tutorial/notes/07 - Multi-Note Editing.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Intermediate, Notebooks/Tutorial] 3 | title: 07 - Multi-Note Editing 4 | created: '2018-12-16T23:45:42.026Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 07 - Multi-Note Editing 9 | 10 | ## Built-in 11 | 12 | Some multi-note editing features are built-in. 13 | 14 | To select multiple notes, you need to **long-press** one and then simply **press** others to remove and add them. You can also use the `ALL` and `NONE` selectors which appear in the multi-note editing toolbar on the bottom. 15 | 16 | The selection is persisted if you change categories via the sidebar or search for notes, so you can perform advanced selections. 17 | 18 | These are the actions you can take on selected notes: 19 | 20 | - Favorite/unfavorite them. 21 | - Pin/unpin them. 22 | - Move to trash/restore/permanently delete them. 23 | - Add one or multiple tags to them. 24 | - Remove one or multiple tags from them. 25 | 26 | ## Advanced 27 | 28 | If you need more advanced multi-note editing, like global search & replace, remember that your notes are just plain Markdown files. 29 | 30 | You could open your data directory into your favorite editor of choice and perform the search & replace there, this way you can also use advanced features like regexes. 31 | 32 | All the edits performed with a third-party application will be reflected into Noteless after a quick refresh. 33 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | Noteless 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | // @dart=2.9 2 | 3 | import 'package:device_info/device_info.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:preferences/preference_service.dart'; 6 | 7 | import 'package:app/page/note_list.dart'; 8 | 9 | import 'package:intl/intl.dart'; 10 | import 'package:provider/provider.dart'; 11 | 12 | import 'provider/theme.dart'; 13 | 14 | main() async { 15 | WidgetsFlutterBinding.ensureInitialized(); 16 | 17 | await PrefService.init(prefix: 'pref_'); 18 | /* 19 | await initializeDateFormatting("en_US", null); */ 20 | Intl.defaultLocale = 'en_US'; 21 | 22 | final deviceInfo = DeviceInfoPlugin(); 23 | androidDeviceInfo = await deviceInfo.androidInfo; 24 | 25 | // Disable note preview/render feature on Android KitKat see #32 26 | if (androidDeviceInfo.version.sdkInt < 20) { 27 | previewFeatureEnabled = false; 28 | } 29 | 30 | runApp( 31 | ChangeNotifierProvider( 32 | create: (_) => ThemeNotifier(), 33 | child: App(), 34 | ), 35 | ); 36 | } 37 | 38 | AndroidDeviceInfo androidDeviceInfo; 39 | 40 | bool previewFeatureEnabled = true; 41 | 42 | class App extends StatelessWidget { 43 | @override 44 | Widget build(BuildContext context) { 45 | return MaterialApp( 46 | title: 'Noteless', 47 | theme: Provider.of(context).currentThemeData, 48 | home: NoteListPage( 49 | isFirstPage: true, 50 | ), 51 | /* localizationsDelegates: [ 52 | FlutterI18nDelegate(path: 'assets/i18n', fallbackFile: 'en'), 53 | GlobalMaterialLocalizations.delegate, 54 | GlobalWidgetsLocalizations.delegate 55 | ], */ 56 | /* debugShowCheckedModeBanner: false, */ 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /assets/tutorial/notes/08 - Linking Attachments, Notes, Tags and Searches.md: -------------------------------------------------------------------------------- 1 | --- 2 | attachments: [icon_small.png] 3 | tags: [Intermediate, Notebooks/Tutorial] 4 | title: '08 - Linking Attachments, Notes, Tags and Searches' 5 | created: '2018-12-27T18:53:01.510Z' 6 | modified: '2020-07-05T12:00:00.000Z' 7 | --- 8 | 9 | # 08 - Linking Attachments, Notes, Tags and Searches 10 | 11 | Sometimes, like when writing a tutorial for a note-taking app :wink:, you may need to link to other notes or embed a few attachments. Noteless makes this easy for you. 12 | 13 | ## Attachments 14 | 15 | Attachments can be rendered inline, linked to, and linked to via a button. The `@attachment` token is used for this. 16 | 17 | ##### Syntax 18 | 19 | ```markdown 20 | ![Icon](@attachment/icon_small.png) 21 | [Icon](@attachment/icon_small.png) 22 | ``` 23 | 24 | ##### Result 25 | 26 | ![Icon](@attachment/icon_small.png) 27 | 28 | [Icon](@attachment/icon_small.png) 29 | 30 | ## Notes 31 | 32 | Notes can be linked to, and linked to via a button. The `@note` token is used for this. Wiki-style links are supported too. 33 | 34 | ##### Syntax 35 | 36 | ```markdown 37 | [Tags](@note/06 - Tags.md) 38 | [[06 - Tags]] 39 | ``` 40 | 41 | ##### Result 42 | 43 | [Tags](@note/06 - Tags.md) 44 | 45 | [[06 - Tags]] 46 | 47 | ## Tags 48 | 49 | Tags can be linked to, and linked to via a button. The `@tag` token is used for this. 50 | 51 | ##### Syntax 52 | 53 | ```markdown 54 | [Basics](@tag/Basics) 55 | ``` 56 | 57 | ##### Result 58 | 59 | [Basics](@tag/Basics) 60 | 61 | ## Searches 62 | 63 | Searches can be linked to, and linked to via a button. The `@search` token is used for this. 64 | 65 | ##### Syntax 66 | 67 | ```markdown 68 | [linking](@search/linking) 69 | ``` 70 | 71 | ##### Result 72 | 73 | [linking](@search/linking) 74 | -------------------------------------------------------------------------------- /lib/page/about.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:app/store/notes.dart'; 3 | import 'package:url_launcher/url_launcher.dart'; 4 | import 'package:package_info/package_info.dart'; 5 | 6 | class AboutPage extends StatefulWidget { 7 | final NotesStore store; 8 | AboutPage(this.store); 9 | @override 10 | _AboutPageState createState() => _AboutPageState(); 11 | } 12 | 13 | class _AboutPageState extends State { 14 | @override 15 | Widget build(BuildContext context) { 16 | return Scaffold( 17 | appBar: AppBar( 18 | title: Text('About'), 19 | ), 20 | body: Center( 21 | child: Column( 22 | mainAxisSize: MainAxisSize.min, 23 | children: [ 24 | FutureBuilder( 25 | future: PackageInfo.fromPlatform(), 26 | builder: (context, snap) { 27 | if (!snap.hasData) return Container(); 28 | PackageInfo info = snap.data; 29 | return Column( 30 | children: [ 31 | Text('${info.appName}'), 32 | Text('${info.packageName}'), 33 | SizedBox( 34 | height: 8, 35 | ), 36 | Text('Version ${info.version}'), 37 | Text('Build ${info.buildNumber}'), 38 | ], 39 | ); 40 | }, 41 | ), 42 | SizedBox( 43 | height: 16, 44 | ), 45 | RaisedButton( 46 | child: Text('GitHub Repo'), 47 | onPressed: () { 48 | launch('https://github.com/redsolver/noteless'); 49 | }, 50 | ), 51 | ], 52 | ))); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .packages 28 | .pub-cache/ 29 | .pub/ 30 | /build/ 31 | .flutter-plugins-dependencies 32 | 33 | 34 | # Android related 35 | **/android/**/gradle-wrapper.jar 36 | **/android/.gradle 37 | **/android/captures/ 38 | **/android/gradlew 39 | **/android/gradlew.bat 40 | **/android/local.properties 41 | **/android/key.properties 42 | **/android/**/GeneratedPluginRegistrant.java 43 | **/android/key.properties 44 | 45 | # iOS/XCode related 46 | **/ios/**/*.mode1v3 47 | **/ios/**/*.mode2v3 48 | **/ios/**/*.moved-aside 49 | **/ios/**/*.pbxuser 50 | **/ios/**/*.perspectivev3 51 | **/ios/**/*sync/ 52 | **/ios/**/.sconsign.dblite 53 | **/ios/**/.tags* 54 | **/ios/**/.vagrant/ 55 | **/ios/**/DerivedData/ 56 | **/ios/**/Icon? 57 | **/ios/**/Pods/ 58 | **/ios/**/.symlinks/ 59 | **/ios/**/profile 60 | **/ios/**/xcuserdata 61 | **/ios/.generated/ 62 | **/ios/Flutter/App.framework 63 | **/ios/Flutter/Flutter.framework 64 | **/ios/Flutter/Generated.xcconfig 65 | **/ios/Flutter/app.flx 66 | **/ios/Flutter/app.zip 67 | **/ios/Flutter/flutter_assets/ 68 | **/ios/ServiceDefinitions.json 69 | **/ios/Runner/GeneratedPluginRegistrant.* 70 | **/ios/Flutter/flutter_export_environment.sh 71 | 72 | # Exceptions to above rules. 73 | !**/ios/**/default.mode1v3 74 | !**/ios/**/default.mode2v3 75 | !**/ios/**/default.pbxuser 76 | !**/ios/**/default.perspectivev3 77 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 78 | 79 | .env 80 | -------------------------------------------------------------------------------- /assets/tutorial/notes/05 - Notes.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: [Basics, Notebooks/Tutorial] 3 | title: 05 - Notes 4 | created: '2019-04-30T21:17:06.073Z' 5 | modified: '2020-07-05T12:00:00.000Z' 6 | --- 7 | 8 | # 05 - Notes 9 | 10 | ## Syntax 11 | 12 | Notes are written in [GitHub-flavored Markdown](https://guides.github.com/features/mastering-markdown), so you can write emojis (`:joy:` -> :joy:), ~~strikethrough~~ text etc. in a familiar fashion. 13 | 14 | This also means that your notes aren't locked into any proprietary format. 15 | 16 | Notes can have some metadata: if they are favorited or not, which tags they have, which attachments they have, etc. These metadata are written as Markdown front matter. This is taken care of for you. 17 | 18 | ## Syntax Plugins 19 | 20 | Some syntax plugins for providing you [KaTeX](https://katex.org) and [mermaid](https://github.com/knsv/mermaid) support are built-in, check out this note's source. 21 | 22 | #### KaTeX 23 | 24 | Wrap a formula in `$$` to display it as a block: 25 | 26 | $$f{x} = \int_{-\infty}^\infty \hat f\xi\,e^{2 \pi i \xi x} \,d\xi$$ 27 | 28 | Multi-line block formulas are supported too: 29 | 30 | $$ 31 | \begin{pmatrix} 32 | f(\alpha) & b & c \\ 33 | e & f(\beta) & g \\ 34 | i & j & f(\gamma) 35 | \end{pmatrix} 36 | $$ 37 | 38 | Wrap it in `$` to display it inline: $e^{iπ} + 1 = 0$. 39 | 40 | The [mhchem](https://mhchem.github.io/MathJax-mhchem) syntax for writing chemical expressions is supported too: 41 | 42 | $$\ce{ SO4^2- + Ba^2+ -> BaSO4 v }$$ 43 | 44 | #### AsciiMath 45 | 46 | Wrap a formula in `&&` to display it as a block: 47 | 48 | &&sum_(i=1)^n i^3=((n(n+1))/2)^2&& 49 | 50 | Wrap it in `&` to display it inline: &e = mc^2&. 51 | 52 | #### mermaid 53 | 54 | ```mermaid 55 | graph TD 56 | Install --> Tutorial[Read the tutorial] 57 | Tutorial --> Star 58 | Tutorial --> Share 59 | ``` 60 | 61 | ## Attachments 62 | 63 | Notes can have attachments, because sooner or later you'll want to save a file in a note, be it a boarding pass for your next trip or something else. 64 | 65 | Attachments can be added via the popup menu in the toolbar. Attachments are simply copied into your data directory, under the `attachments` sub-directory. 66 | 67 | You can open/remove them at any time. 68 | -------------------------------------------------------------------------------- /lib/provider/theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:preferences/preference_service.dart'; 3 | 4 | class ThemeNotifier with ChangeNotifier { 5 | ThemeNotifier() { 6 | _accentColor = Color(PrefService.getInt('theme_color') ?? 0xff21d885); 7 | updateTheme(PrefService.getString('theme') ?? 'light'); 8 | } 9 | 10 | ThemeType currentTheme = ThemeType.light; 11 | ThemeData _currentThemeData; 12 | 13 | /* void switchTheme() => currentTheme == ThemeType.light 14 | ? currentTheme = ThemeType.dark 15 | : currentTheme = ThemeType.light; */ 16 | 17 | updateTheme([String theme]) { 18 | switch (theme) { 19 | case 'light': 20 | currentTheme = ThemeType.light; 21 | break; 22 | case 'dark': 23 | currentTheme = ThemeType.dark; 24 | break; 25 | case 'black': 26 | currentTheme = ThemeType.dark; 27 | break; 28 | } 29 | _currentThemeData = ThemeData( 30 | brightness: 31 | currentTheme == ThemeType.light ? Brightness.light : Brightness.dark, 32 | scaffoldBackgroundColor: theme == 'black' ? Colors.black : null, 33 | backgroundColor: theme == 'black' ? Colors.black : null, 34 | dialogBackgroundColor: theme == 'black' ? Colors.black : null, 35 | canvasColor: theme == 'black' ? Colors.black : null, 36 | cardColor: theme == 'black' ? Colors.black : null, 37 | accentColor: _accentColor, 38 | primaryColor: _accentColor, 39 | toggleableActiveColor: _accentColor, 40 | highlightColor: _accentColor, 41 | buttonColor: _accentColor, 42 | floatingActionButtonTheme: FloatingActionButtonThemeData( 43 | backgroundColor: _accentColor, 44 | ), 45 | buttonTheme: ButtonThemeData( 46 | textTheme: ButtonTextTheme.primary, 47 | buttonColor: _accentColor, 48 | ), 49 | textTheme: TextTheme( 50 | button: TextStyle(color: _accentColor), 51 | ), 52 | ); 53 | notifyListeners(); 54 | } 55 | 56 | ThemeData get currentThemeData => _currentThemeData; 57 | 58 | Color _accentColor; 59 | 60 | get accentColor => _accentColor; 61 | set accentColor(Color color) { 62 | if (color != null) { 63 | _accentColor = color; 64 | 65 | updateTheme(); 66 | } 67 | } 68 | } 69 | 70 | enum ThemeType { light, dark } 71 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | 29 | def keystorePropertiesFile = rootProject.file("key.properties") 30 | def keystoreProperties = new Properties() 31 | if (keystorePropertiesFile.exists()) { 32 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 33 | } 34 | 35 | 36 | android { 37 | ndkVersion "22.0.7026061" 38 | compileSdkVersion 29 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | lintOptions { 45 | disable 'InvalidPackage' 46 | } 47 | 48 | defaultConfig { 49 | applicationId "net.redsolver.noteless" 50 | minSdkVersion 19 51 | targetSdkVersion 29 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 55 | } 56 | 57 | signingConfigs { 58 | release { 59 | keyAlias keystoreProperties['keyAlias'] 60 | keyPassword keystoreProperties['keyPassword'] 61 | storeFile file(keystoreProperties['storeFile']) 62 | storePassword keystoreProperties['storePassword'] 63 | } 64 | } 65 | 66 | buildTypes { 67 | release { 68 | signingConfig signingConfigs.release 69 | } 70 | } 71 | } 72 | 73 | flutter { 74 | source '../..' 75 | } 76 | 77 | dependencies { 78 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 79 | testImplementation 'junit:junit:4.12' 80 | /* androidTestImplementation 'androidx.test:runner:1.0.2' 81 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.0.2' */ 82 | } 83 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 18 | 22 | 29 | 33 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /lib/editor/pairer.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/services.dart'; 2 | 3 | class CharacterPair implements TextInputFormatter { 4 | 5 | /// All the characters that can be paired, and their pairing character. 6 | static const PAIRED_CHARACTERS = { 7 | '"': '"', 8 | "'": "'", 9 | '(': ')', 10 | '{': '}', 11 | '[': ']', 12 | '`': '`' 13 | }; 14 | 15 | final bool enablePairing; 16 | 17 | /// Creates a CharacterPair with the ability to toggle it on/off with 18 | /// [enablePairing] which should be derived from the settings. This is final 19 | /// as a new instance is created when the note is navigated to again. 20 | CharacterPair(this.enablePairing); 21 | 22 | @override 23 | TextEditingValue formatEditUpdate( 24 | TextEditingValue oldValue, TextEditingValue newValue) { 25 | if (enablePairing) { 26 | var keyPressed = KeyPressUtils.getPressedKey(oldValue, newValue).trim(); 27 | if (PAIRED_CHARACTERS.containsKey(keyPressed)) { 28 | return pairCharacter(keyPressed, oldValue, newValue); 29 | } 30 | } 31 | 32 | return newValue; 33 | } 34 | 35 | /// Pairs a character with the first character being [char] and the second 36 | /// corresponding to its value in [PAIRED_CHARACTERS]. Takes in the same 37 | /// [oldValue] and [newValue] as [formatEditUpdate] does. Does not do 38 | /// any checking if the pairing _should_ happen, that is left to its invoker. 39 | TextEditingValue pairCharacter( 40 | String char, TextEditingValue oldValue, TextEditingValue newValue) { 41 | var oldSelection = oldValue.selection; 42 | 43 | final before = oldValue.text.substring(0, oldSelection.start); 44 | final content = oldValue.text.substring(oldSelection.start, oldSelection.end); 45 | final after = oldValue.text.substring(oldSelection.end); 46 | 47 | var newSelection = TextSelection( 48 | baseOffset: oldSelection.baseOffset + 1, 49 | extentOffset: oldSelection.extentOffset + 1, 50 | affinity: oldSelection.affinity, 51 | isDirectional: oldSelection.isDirectional); 52 | 53 | return TextEditingValue( 54 | text: '$before$char$content${PAIRED_CHARACTERS[char]}$after', 55 | selection: newSelection, 56 | composing: oldValue.composing); 57 | } 58 | } 59 | 60 | class KeyPressUtils { 61 | static const BACKSPACE = '\u0008'; 62 | 63 | /// Gets the pressed key from the new and old values. 64 | /// Adapted from rich_editable_code's KeyboardUtilz.getPressedKey 65 | static String getPressedKey( 66 | TextEditingValue oldValue, TextEditingValue newValue) { 67 | if (newValue.text.length == 1 && newValue.text == '\n') { 68 | return '\n'; 69 | } 70 | 71 | var newSelection = newValue.selection; 72 | var currentSelection = oldValue.selection; 73 | 74 | if (currentSelection.baseOffset > newSelection.baseOffset) { 75 | //backspace was pressed 76 | return BACKSPACE; 77 | } 78 | 79 | return newValue.text 80 | .substring(currentSelection.baseOffset, newSelection.baseOffset); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/utils/yaml.dart: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Anders Holmgren. All rights reserved. Use of this source code 2 | // is governed by a BSD-style license that can be found in the LICENSE file. 3 | 4 | /// Serializes [node] into a String and returns it. 5 | String toYamlString(node) { 6 | var sb = new StringBuffer(); 7 | writeYamlString(node, sb); 8 | return sb.toString(); 9 | } 10 | 11 | /// Serializes [node] into a String and writes it to the [sink]. 12 | void writeYamlString(node, StringSink sink) { 13 | _writeYamlString(node, 0, sink, true); 14 | } 15 | 16 | void _writeYamlString(node, int indent, StringSink ss, bool isTopLevel) { 17 | if (node is Map) { 18 | _mapToYamlString(node, indent, ss, isTopLevel); 19 | } else if (node is Iterable) { 20 | _listToYamlString(node, indent, ss, isTopLevel); 21 | } else if (node is String) { 22 | ss..writeln('"${_escapeString(node)}"'); 23 | } else if (node is double) { 24 | ss.writeln("!!float $node"); 25 | } else { 26 | ss.writeln(node); 27 | } 28 | } 29 | 30 | String _escapeString(String s) => 31 | s.replaceAll('"', r'\"').replaceAll("\n", r"\n"); 32 | 33 | void _mapToYamlString(Map node, int indent, StringSink ss, bool isTopLevel) { 34 | if (!isTopLevel) { 35 | ss.writeln(); 36 | indent += 2; 37 | } 38 | 39 | final keys = _sortKeys(node); 40 | 41 | keys.forEach((k) { 42 | final v = node[k]; 43 | _writeIndent(indent, ss); 44 | ss..write(k)..write(': '); 45 | _writeYamlString(v, indent, ss, false); 46 | }); 47 | } 48 | 49 | Iterable _sortKeys(Map m) { 50 | final simple = []; 51 | final maps = []; 52 | final other = []; 53 | 54 | m.forEach((k, v) { 55 | if (v is String) { 56 | simple.add(k); 57 | } else if (v is Map) { 58 | maps.add(k); 59 | } else { 60 | other.add(k); 61 | } 62 | }); 63 | 64 | return concat([simple..sort(), maps..sort(), other..sort()]).cast(); 65 | } 66 | 67 | void _listToYamlString( 68 | Iterable node, int indent, StringSink ss, bool isTopLevel) { 69 | if (!isTopLevel) { 70 | ss.writeln(); 71 | indent += 2; 72 | } 73 | 74 | node.forEach((v) { 75 | _writeIndent(indent, ss); 76 | ss.write('- '); 77 | _writeYamlString(v, indent, ss, false); 78 | }); 79 | } 80 | 81 | void _writeIndent(int indent, StringSink ss) => ss.write(' ' * indent); 82 | 83 | // Copyright 2013 Google Inc. All Rights Reserved. 84 | // 85 | // Licensed under the Apache License, Version 2.0 (the "License"); 86 | // you may not use this file except in compliance with the License. 87 | // You may obtain a copy of the License at 88 | // 89 | // http://www.apache.org/licenses/LICENSE-2.0 90 | // 91 | // Unless required by applicable law or agreed to in writing, software 92 | // distributed under the License is distributed on an "AS IS" BASIS, 93 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 94 | // See the License for the specific language governing permissions and 95 | // limitations under the License. 96 | 97 | Iterable concat(Iterable> iterables) => 98 | iterables.expand((x) => x); 99 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /assets/preview/katex.auto-render.min.js: -------------------------------------------------------------------------------- 1 | !function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t(require("katex")):"function"==typeof define&&define.amd?define(["katex"],t):"object"==typeof exports?exports.renderMathInElement=t(require("katex")):e.renderMathInElement=t(e.katex)}("undefined"!=typeof self?self:this,function(e){return function(e){var t={};function r(n){if(t[n])return t[n].exports;var o=t[n]={i:n,l:!1,exports:{}};return e[n].call(o.exports,o,o.exports,r),o.l=!0,o.exports}return r.m=e,r.c=t,r.d=function(e,t,n){r.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:n})},r.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},r.t=function(e,t){if(1&t&&(e=r(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var n=Object.create(null);if(r.r(n),Object.defineProperty(n,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var o in e)r.d(n,o,function(t){return e[t]}.bind(null,o));return n},r.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return r.d(t,"a",t),t},r.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},r.p="",r(r.s=1)}([function(t,r){t.exports=e},function(e,t,r){"use strict";r.r(t);var n=r(0),o=r.n(n),a=function(e,t,r){for(var n=r,o=0,a=e.length;n 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: app 2 | description: A markdown note-taking app for mobile devices. 3 | publish_to: none 4 | # The following defines the version and build number for your application. 5 | # A version number is three numbers separated by dots, like 1.2.43 6 | # followed by an optional build number separated by a +. 7 | # Both the version and the builder number may be overridden in flutter 8 | # build by specifying --build-name and --build-number, respectively. 9 | # In Android, build-name is used as versionName while build-number used as versionCode. 10 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 11 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 12 | # Read more about iOS versioning at 13 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 14 | version: 1.4.6+146 15 | 16 | environment: 17 | sdk: ">=2.5.0 <3.0.0" 18 | 19 | dependencies: 20 | flutter: 21 | sdk: flutter 22 | flutter_markdown: ^0.4.2 23 | path_provider: ^2.0.0 24 | yaml: ^2.2.0 25 | material_design_icons_flutter: ^4.0.5045 26 | markd: 27 | git: https://github.com/redsolver/markd.git 28 | # flutter_widget_from_html: ^0.3.3 29 | bsdiff: ^0.1.0 30 | webview_flutter: ^1.0.1 31 | # flutter_i18n: ^0.7.0 32 | preferences: ^5.2.0 33 | html: ^0.14.0+2 34 | url_launcher: ^6.0.2 35 | package_info: ^0.4.0+8 36 | 37 | provider: ^4.0.5+1 38 | 39 | front_matter: ^1.1.0 40 | flutter_slidable: ^0.5.4 41 | #time_machine: ^0.9.9 42 | file_picker: ^3.0.0 43 | rich_code_editor: 44 | git: https://github.com/bats64mgutsi/rich_code_editor.git 45 | device_info: ^2.0.0 46 | quick_actions: ^0.4.0+8 47 | receive_sharing_intent: ^1.4.1 48 | quiver: ^3.0.0 49 | intl: ^0.17.0 50 | permission_handler: ^6.1.0 51 | uuid: ^3.0.3 52 | 53 | dependency_overrides: 54 | shared_preferences: ^2.0.4 55 | # dev_dependencies: 56 | # flutter_launcher_icons: ^0.8.1 57 | flutter_icons: 58 | android: true 59 | ios: true 60 | image_path_android: "assets/icon/icon.png" 61 | image_path_ios: "assets/icon/icon_ios.png" 62 | adaptive_icon_background: "#ffffff" 63 | adaptive_icon_foreground: "assets/icon/adaptive_icon.png" 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://dart.dev/tools/pub/pubspec 66 | # The following section is specific to Flutter. 67 | flutter: 68 | generate: true 69 | # The following line ensures that the Material Icons font is 70 | # included with your application, so that you can use the icons in 71 | # the material Icons class. 72 | uses-material-design: true 73 | 74 | # To add assets to your application, add an assets section, like this: 75 | assets: 76 | - assets/tutorial/notes/ 77 | - assets/tutorial/attachments/ 78 | # - assets/i18n/ 79 | - assets/preview/fonts/ 80 | - assets/preview/ 81 | 82 | # - images/a_dot_burr.jpeg 83 | # - images/a_dot_ham.jpeg 84 | # An image asset can refer to one or more resolution-specific "variants", see 85 | # https://flutter.dev/assets-and-images/#resolution-aware. 86 | # For details regarding adding assets from package dependencies, see 87 | # https://flutter.dev/assets-and-images/#from-packages 88 | # To add custom fonts to your application, add a fonts section here, 89 | # in this "flutter" section. Each entry in this list should have a 90 | # "family" key with the font family name, and a "fonts" key with a 91 | # list giving the asset and other descriptors for the font. For 92 | # example: 93 | fonts: 94 | - family: FiraMono 95 | fonts: 96 | - asset: assets/fonts/FiraMono-Regular.ttf 97 | # - asset: fonts/Schyler-Italic.ttf 98 | # style: italic 99 | # - family: Trajan Pro 100 | # fonts: 101 | # - asset: fonts/TrajanPro.ttf 102 | # - asset: fonts/TrajanPro_Bold.ttf 103 | # weight: 700 104 | # 105 | # For details regarding fonts from package dependencies, 106 | # see https://flutter.dev/custom-fonts/#from-packages 107 | -------------------------------------------------------------------------------- /assets/preview/prism.css: -------------------------------------------------------------------------------- 1 | /* PrismJS 1.17.1 2 | https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript+abap+abnf+actionscript+ada+apacheconf+apl+applescript+c+arff+asciidoc+asm6502+csharp+autohotkey+autoit+bash+basic+batch+bison+bnf+brainfuck+bro+cpp+aspnet+arduino+cil+coffeescript+cmake+clojure+ruby+csp+css-extras+d+dart+diff+markup-templating+dns-zone-file+docker+ebnf+eiffel+ejs+elixir+elm+erb+erlang+fsharp+flow+fortran+gcode+gedcom+gherkin+git+glsl+gml+go+graphql+groovy+less+handlebars+haskell+haxe+hcl+http+hpkp+hsts+ichigojam+icon+inform7+ini+io+j+java+scala+php+javastacktrace+jolie+jq+javadoclike+n4js+markdown+json+jsonp+json5+julia+keyman+kotlin+latex+crystal+scheme+liquid+lisp+livescript+lolcode+lua+makefile+js-templates+django+matlab+mel+mizar+monkey+n1ql+typescript+nand2tetris-hdl+nasm+nginx+nim+nix+nsis+objectivec+ocaml+opencl+oz+parigp+parser+pascal+pascaligo+pcaxis+perl+jsdoc+phpdoc+php-extras+sql+powershell+processing+prolog+properties+protobuf+scss+puppet+pure+python+q+qore+r+js-extras+jsx+renpy+reason+vala+rest+rip+roboconf+textile+rust+sas+sass+stylus+javadoc+lilypond+shell-session+smalltalk+smarty+soy+splunk-spl+plsql+twig+swift+yaml+tcl+haml+toml+tt2+pug+tsx+t4-templating+visual-basic+t4-cs+regex+vbnet+velocity+verilog+vhdl+vim+t4-vb+wasm+wiki+xeora+xojo+xquery+tap */ 3 | /** 4 | * prism.js default theme for JavaScript, CSS and HTML 5 | * Based on dabblet (http://dabblet.com) 6 | * @author Lea Verou 7 | */ 8 | 9 | code[class*="language-"], 10 | pre[class*="language-"] { 11 | color: black; 12 | background: none; 13 | text-shadow: 0 1px white; 14 | font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace; 15 | font-size: 1em; 16 | text-align: left; 17 | white-space: pre; 18 | word-spacing: normal; 19 | word-break: normal; 20 | word-wrap: normal; 21 | line-height: 1.5; 22 | 23 | -moz-tab-size: 4; 24 | -o-tab-size: 4; 25 | tab-size: 4; 26 | 27 | -webkit-hyphens: none; 28 | -moz-hyphens: none; 29 | -ms-hyphens: none; 30 | hyphens: none; 31 | } 32 | 33 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 34 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 35 | text-shadow: none; 36 | background: #b3d4fc; 37 | } 38 | 39 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 40 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 41 | text-shadow: none; 42 | background: #b3d4fc; 43 | } 44 | 45 | @media print { 46 | code[class*="language-"], 47 | pre[class*="language-"] { 48 | text-shadow: none; 49 | } 50 | } 51 | 52 | /* Code blocks */ 53 | pre[class*="language-"] { 54 | padding: 1em; 55 | margin: .5em 0; 56 | overflow: auto; 57 | } 58 | 59 | :not(pre) > code[class*="language-"], 60 | pre[class*="language-"] { 61 | background: #f5f2f0; 62 | } 63 | 64 | /* Inline code */ 65 | :not(pre) > code[class*="language-"] { 66 | padding: .1em; 67 | border-radius: .3em; 68 | white-space: normal; 69 | } 70 | 71 | .token.comment, 72 | .token.prolog, 73 | .token.doctype, 74 | .token.cdata { 75 | color: slategray; 76 | } 77 | 78 | .token.punctuation { 79 | color: #999; 80 | } 81 | 82 | .namespace { 83 | opacity: .7; 84 | } 85 | 86 | .token.property, 87 | .token.tag, 88 | .token.boolean, 89 | .token.number, 90 | .token.constant, 91 | .token.symbol, 92 | .token.deleted { 93 | color: #905; 94 | } 95 | 96 | .token.selector, 97 | .token.attr-name, 98 | .token.string, 99 | .token.char, 100 | .token.builtin, 101 | .token.inserted { 102 | color: #690; 103 | } 104 | 105 | .token.operator, 106 | .token.entity, 107 | .token.url, 108 | .language-css .token.string, 109 | .style .token.string { 110 | color: #9a6e3a; 111 | background: hsla(0, 0%, 100%, .5); 112 | } 113 | 114 | .token.atrule, 115 | .token.attr-value, 116 | .token.keyword { 117 | color: #07a; 118 | } 119 | 120 | .token.function, 121 | .token.class-name { 122 | color: #DD4A68; 123 | } 124 | 125 | .token.regex, 126 | .token.important, 127 | .token.variable { 128 | color: #e90; 129 | } 130 | 131 | .token.important, 132 | .token.bold { 133 | font-weight: bold; 134 | } 135 | .token.italic { 136 | font-style: italic; 137 | } 138 | 139 | .token.entity { 140 | cursor: help; 141 | } 142 | 143 | -------------------------------------------------------------------------------- /assets/fonts/LICENSE: -------------------------------------------------------------------------------- 1 | Digitized data copyright (c) 2012-2015, The Mozilla Foundation and Telefonica S.A. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | This license is copied below, and is also available with a FAQ at: 5 | http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## 1.4.6 4 | 5 | - Added support for the Android 11 permission system 6 | - Added option to sort tags alphabetically in the sidebar 7 | - Improved compatibility with other Markdown-based note-taking tools (especially Dendron) 8 | - Some small bug and theme fixes 9 | 10 | ## 1.3.1 11 | 12 | - Added optional auto save option 13 | - Added optional auto pairing of brackets/quotes (thanks to @RubbaBoy) 14 | - Editor: Added option to move and restore note to/from trash 15 | - Improved table styling (thanks to @davidebersani) 16 | - Fixed some bugs 17 | 18 | ## 1.3.0 19 | 20 | - Added feature to create a new note by sharing text with Noteless (#42) 21 | - Adding an attachment to a note now automatically embeds it 22 | - Improved blockquote styling (#44) 23 | - Fixed some bugs 24 | 25 | ## 1.2.1 26 | 27 | - Added Android App Shortcut for creating a new note from the homescreen 28 | - Added proper table borders 29 | - Fixed some bugs 30 | 31 | ## 1.2.0 32 | 33 | - Added AsciiMath support 34 | - Added Black/AMOLED theme 35 | - Added experimental option: Automatic bullet points 36 | - Added optional single line break syntax 37 | - Increased height of editor toolbar 38 | - Changed behaviour of list-button in editor toolbar 39 | - Fixed image sizing in preview 40 | - Fixed text field submitting when adding a tag 41 | 42 | ## 1.1.1 43 | 44 | - Added support for spaces in links (#36) 45 | 46 | ## 1.1.0 47 | 48 | - Made checkboxes in preview mode toggleable (#37) 49 | - Added support for Wiki-style links like `[[My Note]]` (#38) 50 | 51 | ## 1.0.0 52 | 53 | - First stable release 54 | - Submitted the app to Google Play Store and F-Droid 55 | - Updated to Flutter 1.20 (Better Performance and some bug fixes in the editor) 56 | 57 | ## 0.3.2 58 | 59 | - Added F-Droid metadata 60 | 61 | ## 0.3.1 62 | 63 | - Fixed editor content not loading without front matter data 64 | 65 | ## 0.3.0 66 | 67 | - Fully reworked editor with syntax highlighting and a new keyboard toolbar to help with common Markdown operations 68 | - Added fallback to file metadata if front matter data is missing 69 | 70 | ## 0.2.1 71 | 72 | - Disabled the preview feature on Android 4.4 KitKat devices. 73 | - Removed WebDav sync 74 | 75 | ## Important Changes in Version 0.2.0 76 | 77 | The app has been renamed from `Notable Mobile` to `Noteless` on 02.07.2020. 78 | 79 | If you used an earlier Alpha Version, you need to uninstall the old one and install one of the new APKs (Don't forget to backup your notes!) 80 | 81 | This is because the app also has a new package name: `net.redsolver.noteless`. 82 | 83 | Also I decided to drop support for syncing notes directly via the app because there are alternative options which work a lot better. 84 | 85 | I recommend using an external data directory and a third-party sync app for Android like [Syncthing](https://syncthing.net/), Nextcloud Sync or FolderSync for other cloud services. 86 | 87 | ## 0.2.0 88 | 89 | - Renamed the app to "Noteless" 90 | - New app icon 91 | - Reworked tutorial notes 92 | - The Editor/Preview Mode Switcher is now the default option 93 | - New error handling: When an exception occurs while reading a note, the note is skipped and the errors are shown as "virtual notes". 94 | - Show loading dialog when changing external data directory 95 | - Fixed issue with using an external data directory on Android Q (10) 96 | - QOL Improvements (Autofocus, Small design improvements) 97 | 98 | ## 0.1.8 99 | 100 | - Added support for subdirectories 101 | - Added options to restore notes from trash 102 | - With the swipe actions of a note 103 | - With the "Restore from trash" button in the multi select options 104 | - Added option to create a logfile for sync 105 | - Added experimental option to enable virtual folder tags 106 | - Minor theme fixes 107 | 108 | ## 0.1.7 109 | 110 | - Fixed white flash when loading note preview 111 | 112 | ## 0.1.6 113 | 114 | - Added option to use a mode switcher for editor and preview 115 | 116 | ## 0.1.5 117 | 118 | - Added feature to add and remove attachments 119 | - Searching in content of notes 120 | - Enabled by default 121 | - Can be disabled in settings 122 | - Can get slow with more than 2000 notes 123 | 124 | ## 0.1.4 125 | 126 | - Added KaTeX and mhchem support 127 | - Added option to change accent color 128 | - Added note swipe actions (trash, delete, pin and favorite) 129 | 130 | ## 0.1.3 131 | 132 | - Fixed webdav sync 133 | 134 | ## 0.1.2 135 | 136 | - Fixed sync when using different data directory 137 | 138 | ## 0.1.1 139 | 140 | - Added option to select data directory on device 141 | - Moved multi select options to bottom app bar 142 | - Pressing back while being in select mode cancels it 143 | - Added option to recreate all tutorial notes and attachments in settings 144 | - Updated info page 145 | - Dark Theme now supports markdown preview 146 | 147 | ## 0.1.0 148 | 149 | - Select multiple notes by long pressing 150 | - After entering select mode, add notes to the selection by tapping them 151 | - Select or unselect all notes in the select menu 152 | - Favorite/Unfavorite multiple notes at once 153 | - Pin/Unpin multiple notes at once 154 | - Add and remove tags to multiple notes at once 155 | - Move to Trash and delete multiple notes at once 156 | 157 | ## 0.0.9 158 | 159 | - Dark Theme 160 | - Confirmation Dialogs 161 | -------------------------------------------------------------------------------- /lib/sync/webdav.dart: -------------------------------------------------------------------------------- 1 | /* import 'dart:core' hide writeDebugLine; 2 | import 'dart:convert'; 3 | import 'dart:io'; 4 | 5 | import 'package:flutter/services.dart'; 6 | import 'package:app/model/note.dart'; 7 | import 'package:app/store/notes.dart'; 8 | import 'package:app/store/persistent.dart'; 9 | import 'package:preferences/preferences.dart'; 10 | import 'package:webdav/webdav.dart'; 11 | import 'package:webdav/src/client.dart'; 12 | import 'package:intl/intl.dart'; 13 | 14 | class WebdavSync { 15 | Future syncFiles(NotesStore store) async { 16 | debugOutput = ''; 17 | var hostUri = 18 | Uri.parse('https://' + PrefService.getString('sync_webdav_host') ?? ''); 19 | var hostPath = hostUri.path; 20 | if (hostPath.startsWith('/')) { 21 | hostPath = hostPath.substring(1); 22 | } 23 | if (hostPath.endsWith('/')) { 24 | hostPath = hostPath.substring(0, hostPath.length - 1); 25 | } 26 | Client client = Client( 27 | hostUri.host, 28 | PrefService.getString('sync_webdav_username') ?? '', 29 | PrefService.getString('sync_webdav_password') ?? '', 30 | hostPath, 31 | port: 443, 32 | protocol: 'https'); 33 | 34 | try { 35 | var path = PrefService.getString('sync_webdav_path'); 36 | client.mkdirs(path); 37 | await syncDirectory(client, '$path/notes/', 'notes'); 38 | await syncDirectory(client, '$path/attachments/', 'attachments'); 39 | } catch (e, st) { 40 | writeDebugLine(e); 41 | writeDebugLine(st); 42 | if (e is WebDavException) { 43 | return e.cause; 44 | } else { 45 | return e.toString(); 46 | } 47 | } 48 | 49 | if (PrefService.getBool('debug_logs_sync') ?? false) { 50 | Note logNote = Note(); 51 | logNote.title = '[DEBUG] Sync'; 52 | 53 | logNote.created = DateTime.now(); 54 | logNote.modified = logNote.created; 55 | 56 | logNote.tags.add('debug'); 57 | 58 | logNote.file = File('${store.notesDir.path}/${logNote.title}.md'); 59 | 60 | await PersistentStore.saveNote( 61 | logNote, 62 | '# ${logNote.title}\n\n${DateTime.now().toIso8601String()}\n\n' + 63 | debugOutput); 64 | } 65 | return null; 66 | } 67 | 68 | String debugOutput; 69 | 70 | writeDebugLine(var o) { 71 | if (PrefService.getBool('debug_logs_sync') ?? false) { 72 | debugOutput += o.toString() + '\n'; 73 | } 74 | } 75 | 76 | Future syncDirectory(Client client, String path, String dir) async { 77 | await client.mkdir('$path'); 78 | 79 | List noteFiles = await client.ls('$path'); 80 | 81 | writeDebugLine('SYNC $path'); 82 | writeDebugLine('${noteFiles.length} files'); 83 | 84 | final directory = Directory(PrefService.getString('notable_directory')); 85 | 86 | final fileDir = Directory('${directory.path}/$dir'); 87 | 88 | final timestampFile = File('${directory.path}/.$dir.sync'); 89 | if (!timestampFile.existsSync()) { 90 | timestampFile.createSync(); 91 | timestampFile.writeAsStringSync('{}'); 92 | } 93 | 94 | Map localSyncTimestamps = json.decode(timestampFile.readAsStringSync()); 95 | writeDebugLine('localSyncTimestamps: $localSyncTimestamps'); 96 | 97 | List syncedNotes = []; 98 | 99 | for (FileInfo info in noteFiles) { 100 | if (info.isDict) { 101 | continue; 102 | } 103 | 104 | writeDebugLine('----'); 105 | 106 | writeDebugLine(info.name); 107 | writeDebugLine(info.contentType); 108 | writeDebugLine(info.ctime); 109 | writeDebugLine(info.mtime); 110 | 111 | String name = Uri.decodeFull(info.name).split('/').last; 112 | writeDebugLine(name); 113 | if (name.trim().isEmpty) continue; 114 | 115 | syncedNotes.add(name); 116 | 117 | DateFormat format = new DateFormat("EEE, dd MMM yyyy hh:mm:ss zzz"); 118 | 119 | String localFilePath = '${fileDir.path}/${name}'; 120 | File localFile = File(localFilePath); 121 | DateTime lastModifiedServer = format.parse(info.mtime, true); 122 | 123 | writeDebugLine('LMS $lastModifiedServer'); 124 | 125 | if (!localFile.existsSync()) { 126 | writeDebugLine('File does not exist locally -> DOWNLOAD'); 127 | // DOWNLOAD 128 | client.download('$path${name}', localFilePath); 129 | localSyncTimestamps[name] = lastModifiedServer.toIso8601String(); 130 | } else { 131 | DateTime lastModifiedClientSync = 132 | DateTime.tryParse(localSyncTimestamps[name] ?? ''); 133 | 134 | if (lastModifiedClientSync == null) 135 | lastModifiedClientSync = DateTime.fromMillisecondsSinceEpoch(0); 136 | DateTime lastModifiedClientFile = localFile.lastModifiedSync().toUtc(); 137 | 138 | DateTime lastModifiedClient; 139 | 140 | if (lastModifiedClientSync.isAfter(lastModifiedClientFile)) { 141 | lastModifiedClient = lastModifiedClientSync; 142 | } else { 143 | lastModifiedClient = lastModifiedClientFile; 144 | } 145 | 146 | writeDebugLine('lastModifiedServer'); 147 | writeDebugLine(lastModifiedServer); 148 | writeDebugLine('lastModifiedClient'); 149 | writeDebugLine(lastModifiedClient); 150 | 151 | writeDebugLine(lastModifiedClient.difference(lastModifiedServer)); 152 | if (lastModifiedServer.difference(lastModifiedClient).abs().inSeconds < 153 | 3) { 154 | writeDebugLine('FILE MATCH!'); 155 | } else if (lastModifiedServer.isBefore(lastModifiedClient)) { 156 | // UPLOAD 157 | writeDebugLine('UPLOAD'); 158 | client.uploadFile(localFilePath, '$path${name}'); 159 | 160 | localSyncTimestamps[name] = DateTime.now().toIso8601String(); 161 | } else if (lastModifiedServer.isAfter(lastModifiedClient)) { 162 | // DOWNLOAD 163 | writeDebugLine('DOWNLOAD'); 164 | client.download('$path${name}', localFilePath); 165 | localSyncTimestamps[name] = lastModifiedServer.toIso8601String(); 166 | } 167 | 168 | // _upload(); 169 | } 170 | } 171 | writeDebugLine(syncedNotes); 172 | 173 | for (var entity in fileDir.listSync()) { 174 | if (entity is! File) continue; 175 | String name = entity.uri.pathSegments.last; 176 | writeDebugLine(entity); 177 | if (!syncedNotes.contains(name)) { 178 | writeDebugLine('DOESNT EXIST ON SERVER $entity -> UPLOAD'); 179 | client.uploadFile(entity.path, '$path${name}'); 180 | localSyncTimestamps[name] = DateTime.now().toIso8601String(); 181 | } 182 | } 183 | 184 | timestampFile.writeAsStringSync(json.encode(localSyncTimestamps)); 185 | } 186 | } 187 | */ -------------------------------------------------------------------------------- /lib/store/persistent.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:app/utils/yaml.dart'; 4 | import 'package:preferences/preference_service.dart'; 5 | import 'package:front_matter/front_matter.dart' as fm; 6 | 7 | import 'package:app/model/note.dart'; 8 | 9 | const supportedFrontMatterKeys = [ 10 | 'title', 11 | 'modified', 12 | 'created', 13 | 'tags', 14 | 'attachments', 15 | 'pinned', 16 | 'favorited', 17 | 'deleted', 18 | 'updated', 19 | ]; 20 | 21 | class PersistentStore { 22 | static bool get isDendronModeEnabled => 23 | (PrefService.getBool('dendron_mode') ?? false); 24 | 25 | static Future readContent( 26 | Note note, 27 | ) async { 28 | if (!note.file.existsSync()) return null; 29 | 30 | String fileContent = await note.file.readAsString(); 31 | 32 | var content; 33 | 34 | if (fileContent.trimLeft().startsWith('---')) { 35 | var doc = fm.parse(fileContent); 36 | if (doc.content != null) { 37 | content = doc.content.trimLeft(); 38 | } else { 39 | content = fileContent.trimLeft(); 40 | } 41 | } else { 42 | content = fileContent.trimLeft(); 43 | } 44 | 45 | if (isDendronModeEnabled) { 46 | if (!content.startsWith('# ')) { 47 | content = '# ${note.title}\n\n' + content; 48 | } 49 | } 50 | 51 | return content; 52 | } 53 | 54 | static Future saveNote(Note note, [String content]) async { 55 | // print('PersistentStore.saveNote'); 56 | 57 | if (content == null) { 58 | content = await readContent(note); 59 | } 60 | if (isDendronModeEnabled) { 61 | final index = content.indexOf('\n'); 62 | if (index != -1) content = content.substring(index).trimLeft(); 63 | } 64 | 65 | String header = '---\n'; 66 | Map data = {}; 67 | 68 | data['title'] = note.title; 69 | 70 | if (PrefService.getBool('notes_list_virtual_tags') ?? false) { 71 | note.tags.removeWhere((s) => s.startsWith('#/')); 72 | } 73 | 74 | if (!isDendronModeEnabled) { 75 | if (note.tags.isNotEmpty) data['tags'] = note.tags; 76 | } 77 | 78 | if (note.attachments.isNotEmpty) data['attachments'] = note.attachments; 79 | 80 | if (note.usesMillis ?? false) { 81 | data['created'] = note.created.millisecondsSinceEpoch; 82 | data[note.usesUpdatedInsteadOfModified ? 'updated' : 'modified'] = 83 | note.modified.millisecondsSinceEpoch; 84 | } else { 85 | data['created'] = note.created.toIso8601String(); 86 | data[note.usesUpdatedInsteadOfModified ? 'updated' : 'modified'] = 87 | note.modified.toIso8601String(); 88 | } 89 | 90 | if (note.pinned) data['pinned'] = true; 91 | if (note.favorited) data['favorited'] = true; 92 | if (note.deleted) data['deleted'] = true; 93 | 94 | if (note.additionalFrontMatterKeys != null) { 95 | data.addAll(note.additionalFrontMatterKeys.cast()); 96 | } 97 | 98 | header += toYamlString(data); 99 | 100 | if (!header.endsWith('\n')) header += '\n'; 101 | 102 | header += '---\n\n'; 103 | 104 | // print(header); 105 | 106 | note.file.writeAsStringSync(header + content); 107 | /* print(header + content); */ 108 | } 109 | 110 | static Future readNote(File file) async { 111 | // print('PersistentStore.readNote'); 112 | 113 | if (!file.existsSync()) return null; 114 | 115 | String fileContent = file.readAsStringSync(); 116 | 117 | Map header; 118 | 119 | if (fileContent.trimLeft().startsWith('---')) { 120 | var doc = fm.parse(fileContent); 121 | /* 122 | String headerString = fileContent.split('---')[1]; */ 123 | 124 | header = doc.data ?? {}; 125 | } else { 126 | header = {}; 127 | } 128 | 129 | /* for (String line in headerString.split('\n')) { 130 | if (line.trim().length == 0) continue; 131 | print(line); 132 | String key=line.split(':').first; 133 | header[key] = line.sub; 134 | } */ 135 | //print(header); 136 | Note note = Note(); 137 | 138 | note.file = file; 139 | 140 | note.title = header['title'].toString(); 141 | 142 | if (note.title == null) { 143 | var title = file.path.split('/').last; 144 | if (title.endsWith('.md')) { 145 | title = title.substring(0, title.length - 3); 146 | } 147 | note.title = title; 148 | } 149 | 150 | if (header['modified'] != null && !isDendronModeEnabled) { 151 | if (header['modified'] is int) { 152 | note.usesMillis = true; 153 | note.modified = DateTime.fromMillisecondsSinceEpoch(header['modified']); 154 | } else { 155 | note.modified = DateTime.tryParse(header['modified']); 156 | } 157 | } else if (header['updated'] != null) { 158 | note.usesUpdatedInsteadOfModified = true; 159 | if (header['updated'] is int) { 160 | note.usesMillis = true; 161 | note.modified = DateTime.fromMillisecondsSinceEpoch(header['updated']); 162 | } else { 163 | note.modified = DateTime.tryParse(header['updated']); 164 | } 165 | } else { 166 | note.modified = file.lastModifiedSync(); 167 | } 168 | 169 | if (header['created'] != null) { 170 | if (header['created'] is int) { 171 | note.usesMillis = true; 172 | note.created = DateTime.fromMillisecondsSinceEpoch(header['created']); 173 | } else { 174 | note.created = DateTime.tryParse(header['created']); 175 | } 176 | } 177 | if (note.created == null) { 178 | note.created = note.modified; 179 | } 180 | 181 | /* 182 | note.tags = 183 | (header['tags'] as YamlList).map((s) => s.toString()).toList(); */ 184 | note.tags = List.from((header['tags'] ?? []).cast()); 185 | note.attachments = List.from((header['attachments'] ?? []).cast()); 186 | 187 | note.pinned = header['pinned'] ?? false; 188 | note.favorited = header['favorited'] ?? false; 189 | note.deleted = header['deleted'] ?? false; 190 | 191 | if (header.isNotEmpty) { 192 | note.additionalFrontMatterKeys = Map.from(header); 193 | 194 | note.additionalFrontMatterKeys 195 | .removeWhere((key, value) => supportedFrontMatterKeys.contains(key)); 196 | } 197 | 198 | return note; 199 | } 200 | 201 | /* static Future readNoteMetadata(File file) async { 202 | // print('PersistentStore.readNote'); 203 | 204 | if (!file.existsSync()) return null; 205 | 206 | var raf = await file.open(); 207 | 208 | List bytes = []; 209 | 210 | bool equalBytes(List l1, List l2) { 211 | int i = -1; 212 | return l1.every((val) { 213 | i++; 214 | return l2[i] == val; 215 | }); 216 | } 217 | 218 | while (true) { 219 | var byte = raf.readByteSync(); 220 | if (byte == -1) break; 221 | 222 | bytes.add(byte); 223 | 224 | int length = bytes.length; 225 | 226 | if (length > 6 && 227 | equalBytes(bytes.sublist(bytes.length - 4, bytes.length), 228 | [10, 45, 45, 45] /* == "\n---" */)) break; 229 | } 230 | 231 | String fileContent = utf8.decode(bytes); 232 | 233 | var doc = fm.parse(fileContent); 234 | /* 235 | String headerString = fileContent.split('---')[1]; */ 236 | 237 | var header = doc.data /* loadYaml(headerString) */; 238 | 239 | if (header == null) return null; 240 | 241 | /* for (String line in headerString.split('\n')) { 242 | if (line.trim().length == 0) continue; 243 | print(line); 244 | String key=line.split(':').first; 245 | header[key] = line.sub; 246 | } */ 247 | //print(header); 248 | Note note = Note(); 249 | 250 | note.file = file; 251 | 252 | note.title = header['title']; 253 | note.created = DateTime.parse(header['created']); 254 | note.modified = DateTime.parse(header['modified']); 255 | /* 256 | note.tags = 257 | (header['tags'] as YamlList).map((s) => s.toString()).toList(); */ 258 | note.tags = List.from((header['tags'] ?? []).cast()); 259 | note.attachments = List.from((header['attachments'] ?? []).cast()); 260 | 261 | note.pinned = header['pinned'] ?? false; 262 | note.favorited = header['favorited'] ?? false; 263 | note.deleted = header['deleted'] ?? false; 264 | 265 | return note; 266 | } */ 267 | 268 | static Future deleteNote(Note note) async { 269 | await note.file.delete(); 270 | } 271 | } 272 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | App Icon 2 | 3 | # Noteless 4 | 5 | A markdown-based note-taking app for Android 6 | 7 | [![Google Play](https://img.shields.io/endpoint?style=for-the-badge&color=orange&logo=google-play&logoColor=orange&url=https%3A%2F%2Fplayshields.herokuapp.com%2Fplay%3Fi%3Dnet.redsolver.noteless%26l%3DGoogle%2520Play%26m%3D%24version)](https://play.google.com/store/apps/details?id=net.redsolver.noteless) 8 | [![GitHub All Releases](https://img.shields.io/github/downloads/redsolver/noteless/total?style=for-the-badge)](https://github.com/redsolver/noteless/releases) 9 | [![SkyDroid Download](https://img.shields.io/badge/SkyDroid-Download-blueviolet?style=for-the-badge&logo=android)](https://to.skydroid.app/noteless.redsolver) 10 | [![Chat on Matrix](https://img.shields.io/matrix/noteless:matrix.org?style=for-the-badge)](https://matrix.to/#/#noteless:matrix.org) 11 | ![MIT License](https://img.shields.io/github/license/redsolver/noteless?style=for-the-badge) 12 | 13 | Compatible with notes saved in [Notable](https://notable.app/) 14 | 15 | ## Features 16 | 17 | - Markdown-optimized editor with syntax highlighting 18 | - Supports Github Flavored Markdown, AsciiMath, KaTeX and mermaidjs for diagrams 19 | - Tags for organizing your notes 20 | - Pin, Star and sort your notes by title or different dates 21 | - Very themable - dark/light mode and accent color 22 | - Full-text search 23 | - File Attachments that can be embedded into a note 24 | - Multi-Note Editing 25 | - Slide actions for easier editing 26 | - Tutorial notes which explain how to use the app 27 | 28 | ## Screenshots 29 | 30 |

31 | 32 | 33 | 34 |

35 | 36 |

37 | 38 | 39 | 40 |

41 | 42 |

43 | 44 | 45 | 46 |

47 | 48 |

49 | 50 | 51 | 52 |

53 | 54 | ## Download 55 | 56 | [https://github.com/redsolver/noteless/releases](https://github.com/redsolver/noteless/releases) 57 | 58 | ## Changelog 59 | 60 | ### 1.4.6 61 | 62 | - Added support for the Android 11 permission system 63 | - Added option to sort tags alphabetically in the sidebar 64 | - Improved compatibility with other Markdown-based note-taking tools (especially Dendron) 65 | - Some small bug and theme fixes 66 | 67 | ### 1.3.1 68 | 69 | - Added optional auto save option 70 | - Added optional auto pairing of brackets/quotes (thanks to @RubbaBoy) 71 | - Editor: Added option to move and restore note to/from trash 72 | - Improved table styling (thanks to @davidebersani) 73 | - Fixed some bugs 74 | 75 | ### 1.3.0 76 | 77 | - Added feature to create a new note by sharing text with Noteless (#42) 78 | - Adding an attachment to a note now automatically embeds it 79 | - Improved blockquote styling (#44) 80 | - Fixed some bugs 81 | 82 | ### 1.2.1 83 | 84 | - Added Android App Shortcut for creating a new note from the homescreen 85 | - Added proper table borders 86 | - Fixed some bugs 87 | 88 | ### 1.2.0 89 | 90 | - Added AsciiMath support 91 | - Added Black/AMOLED theme 92 | - Added experimental option: Automatic bullet points 93 | - Added optional single line break syntax 94 | - Increased height of editor toolbar 95 | - Changed behaviour of list-button in editor toolbar 96 | - Fixed image sizing in preview 97 | - Fixed text field submitting when adding a tag 98 | 99 | ### 1.1.1 100 | 101 | - Added support for spaces in links (#36) 102 | 103 | ### 1.1.0 104 | 105 | - Made checkboxes in preview mode toggleable (#37) 106 | - Added support for Wiki-style links like `[[My Note]]` (#38) 107 | 108 | ### 1.0.0 109 | 110 | - First stable release 111 | - Submitted the app to Google Play Store and F-Droid 112 | - Updated to Flutter 1.20 (Better Performance and some bug fixes in the editor) 113 | 114 | ### 0.3.2 115 | 116 | - Added F-Droid metadata 117 | 118 | ### 0.3.1 119 | 120 | - Fixed editor content not loading without front matter data 121 | 122 | ### 0.3.0 123 | 124 | - Fully reworked editor with syntax highlighting and a new keyboard toolbar to help with common Markdown operations 125 | - Added fallback to file metadata if front matter data is missing 126 | 127 | ### 0.2.1 128 | 129 | - Disabled the preview feature on Android 4.4 KitKat devices. 130 | - Removed WebDav sync 131 | 132 | ### Important Changes in Version 0.2.0 133 | 134 | The app has been renamed from `Notable Mobile` to `Noteless` on 02.07.2020. 135 | 136 | If you used an earlier Alpha Version, you need to uninstall the old one and install one of the new APKs (Don't forget to backup your notes!) 137 | 138 | This is because the app also has a new package name: `net.redsolver.noteless`. 139 | 140 | Also I decided to drop support for syncing notes directly via the app because there are alternative options which work a lot better. 141 | 142 | I recommend using an external data directory and a third-party sync app for Android like [Syncthing](https://syncthing.net/), Nextcloud Sync or FolderSync for other cloud services. 143 | 144 | ### 0.2.0 145 | 146 | - Renamed the app to "Noteless" 147 | - New app icon 148 | - Reworked tutorial notes 149 | - The Editor/Preview Mode Switcher is now the default option 150 | - New error handling: When an exception occurs while reading a note, the note is skipped and the errors are shown as "virtual notes". 151 | - Show loading dialog when changing external data directory 152 | - Fixed issue with using an external data directory on Android Q (10) 153 | - QOL Improvements (Autofocus, Small design improvements) 154 | 155 | ### 0.1.8 156 | 157 | - Added support for subdirectories 158 | - Added options to restore notes from trash 159 | - With the swipe actions of a note 160 | - With the "Restore from trash" button in the multi select options 161 | - Added option to create a logfile for sync 162 | - Added experimental option to enable virtual folder tags 163 | - Minor theme fixes 164 | 165 | ### 0.1.7 166 | 167 | - Fixed white flash when loading note preview 168 | 169 | ### 0.1.6 170 | 171 | - Added option to use a mode switcher for editor and preview 172 | 173 | ### 0.1.5 174 | 175 | - Added feature to add and remove attachments 176 | - Searching in content of notes 177 | - Enabled by default 178 | - Can be disabled in settings 179 | - Can get slow with more than 2000 notes 180 | 181 | ### 0.1.4 182 | 183 | - Added KaTeX and mhchem support 184 | - Added option to change accent color 185 | - Added note swipe actions (trash, delete, pin and favorite) 186 | 187 | ### 0.1.3 188 | 189 | - Fixed webdav sync 190 | 191 | ### 0.1.2 192 | 193 | - Fixed sync when using different data directory 194 | 195 | ### 0.1.1 196 | 197 | - Added option to select data directory on device 198 | - Moved multi select options to bottom app bar 199 | - Pressing back while being in select mode cancels it 200 | - Added option to recreate all tutorial notes and attachments in settings 201 | - Updated info page 202 | - Dark Theme now supports markdown preview 203 | 204 | ### 0.1.0 205 | 206 | - Select multiple notes by long pressing 207 | - After entering select mode, add notes to the selection by tapping them 208 | - Select or unselect all notes in the select menu 209 | - Favorite/Unfavorite multiple notes at once 210 | - Pin/Unpin multiple notes at once 211 | - Add and remove tags to multiple notes at once 212 | - Move to Trash and delete multiple notes at once 213 | 214 | ### 0.0.9 215 | 216 | - Dark Theme 217 | - Confirmation Dialogs 218 | 219 | ## License 220 | 221 | The app is MIT licensed. 222 | -------------------------------------------------------------------------------- /lib/store/notes.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:app/model/note.dart'; 4 | import 'package:app/store/persistent.dart'; 5 | import 'package:app/sync/webdav.dart'; 6 | import 'package:path_provider/path_provider.dart'; 7 | import 'package:preferences/preference_service.dart'; 8 | import 'package:app/data/samples.dart'; 9 | import 'package:flutter/services.dart' show rootBundle; 10 | 11 | class NotesStore { 12 | /* String subDirectoryNotes = ; 13 | String subDirectoryAttachments = ; */ 14 | 15 | String get subDirectoryNotes => isDendronModeEnabled ? '' : '/notes'; 16 | String get subDirectoryAttachments => 17 | isDendronModeEnabled ? '/assets' : '/attachments'; 18 | 19 | bool get isDendronModeEnabled => PrefService.getBool('dendron_mode') ?? false; 20 | 21 | Directory notesDir, attachmentsDir; 22 | 23 | String searchText; 24 | 25 | String syncMethod; 26 | 27 | get syncMethodName { 28 | switch (syncMethod) { 29 | case 'webdav': 30 | return 'WebDav'; 31 | default: 32 | return 'No'; 33 | } 34 | } 35 | 36 | void init() { 37 | syncMethod = PrefService.getString('sync') ?? ''; 38 | } 39 | 40 | Future createTutorialNotes() async { 41 | for (String fileName in Samples.tutorialNotes) { 42 | File('${notesDir.path}/$fileName').writeAsStringSync( 43 | await rootBundle.loadString('assets/tutorial/notes/$fileName')); 44 | } 45 | } 46 | 47 | Future createTutorialAttachments() async { 48 | for (String fileName in Samples.tutorialAttachments) { 49 | File('${attachmentsDir.path}/$fileName').writeAsBytesSync( 50 | (await rootBundle.load('assets/tutorial/attachments/$fileName')) 51 | .buffer 52 | .asUint8List()); 53 | } 54 | } 55 | 56 | Directory applicationDocumentsDirectory; 57 | 58 | Future listNotes() async { 59 | applicationDocumentsDirectory = await getApplicationDocumentsDirectory(); 60 | Directory directory; 61 | 62 | if (PrefService.getBool('notable_external_directory_enabled') ?? false) { 63 | directory = 64 | Directory(PrefService.getString('notable_external_directory')); 65 | } else { 66 | directory = applicationDocumentsDirectory; 67 | } 68 | PrefService.setString('notable_directory', directory.path); 69 | 70 | // print(isDendronModeEnabled); 71 | 72 | notesDir = Directory('${directory.path}$subDirectoryNotes'); 73 | 74 | PrefService.setString('notable_notes_directory', notesDir.path); 75 | 76 | if (!notesDir.existsSync()) { 77 | notesDir.createSync(); 78 | if (!isDendronModeEnabled) await createTutorialNotes(); 79 | } 80 | 81 | attachmentsDir = Directory('${directory.path}$subDirectoryAttachments'); 82 | PrefService.setString('notable_attachments_directory', attachmentsDir.path); 83 | 84 | if (!attachmentsDir.existsSync()) { 85 | attachmentsDir.createSync(); 86 | if (!isDendronModeEnabled) await createTutorialAttachments(); 87 | } 88 | 89 | /* for (String fileName in Samples.tutorialNotes) { 90 | File('${notesDir.path}/$fileName').writeAsStringSync( 91 | await rootBundle.loadString('assets/tutorial/notes/$fileName')); 92 | } */ 93 | 94 | allNotes = []; 95 | 96 | DateTime start = DateTime.now(); 97 | 98 | await _listNotesInFolder(''); 99 | 100 | print(DateTime.now().difference(start)); 101 | 102 | // _updateTagList(); 103 | } 104 | 105 | Future _listNotesInFolder(String dir, {bool isSubDirectory = false}) async { 106 | await for (var entity in Directory('${notesDir.path}$dir').list()) { 107 | if (entity is File) { 108 | try { 109 | if (isDendronModeEnabled) { 110 | if (!entity.path.endsWith('.md')) continue; 111 | } 112 | Note note = await PersistentStore.readNote(entity); 113 | 114 | if (note != null) { 115 | if (PrefService.getBool('notes_list_virtual_tags') ?? false) { 116 | if (isSubDirectory) note.tags.add('#$dir'); 117 | } 118 | if (isDendronModeEnabled) { 119 | var path = entity.path 120 | .substring(notesDir.path.length, entity.path.length - 3); 121 | while (path.startsWith('/')) { 122 | path = path.substring(1); 123 | } 124 | note.tags.add('${path.replaceAll('.', '/')}'); 125 | } 126 | 127 | allNotes.add(note); 128 | } 129 | } catch (e, st) { 130 | final note = Note(); 131 | note.title = 132 | 'ERROR - "$e" on parsing "${entity.path.split('/notes/').last}"'; 133 | 134 | note.created = DateTime.now(); 135 | note.modified = note.created; 136 | note.tags = ['ERROR']; 137 | note.attachments = []; 138 | 139 | note.pinned = true; 140 | note.favorited = true; 141 | note.deleted = false; 142 | 143 | allNotes.add(note); 144 | } 145 | } else if (entity is Directory) { 146 | final dirName = entity.path.split('/').last; 147 | 148 | if (dirName.startsWith('.')) continue; 149 | 150 | if (entity.path.startsWith(attachmentsDir.path)) continue; 151 | 152 | await _listNotesInFolder( 153 | dir + '/' + dirName, 154 | isSubDirectory: true, 155 | ); 156 | } 157 | } 158 | } 159 | 160 | updateTagList() { 161 | for (Note note in allNotes) { 162 | for (String tag in note.tags) { 163 | allTags.add(tag); 164 | } 165 | } 166 | 167 | final tmpRootTags = {}; 168 | 169 | for (String tag in allTags) { 170 | tmpRootTags.add(tag.split('/').first); 171 | } 172 | 173 | rootTags = tmpRootTags.toList(); 174 | rootTags.sort(); 175 | } 176 | 177 | List getSubTags(String forTag) { 178 | Set subTags = 179 | allTags.where((tag) => tag.startsWith(forTag) && tag != forTag).toSet(); 180 | 181 | subTags = subTags.map((String t) => t.replaceFirst('$forTag/', '')).toSet(); 182 | 183 | subTags = subTags.map((String t) => t.split('/').first).toSet(); 184 | 185 | final subTagsList = subTags.toList(); 186 | 187 | if (PrefService.getBool('sort_tags_in_sidebar') ?? true) { 188 | subTagsList.sort(); 189 | } 190 | 191 | return subTagsList; 192 | } 193 | 194 | filterAndSortNotes() { 195 | //shownNotes = List.from(allNotes); 196 | 197 | shownNotes = _filterByTag(allNotes, currentTag); 198 | 199 | if (searchText != null) { 200 | List keywords = 201 | searchText.split(' ').map((s) => s.toLowerCase()).toList(); 202 | 203 | if (PrefService.getBool('search_content') ?? true) { 204 | List toRemove = []; 205 | 206 | for (Note note in shownNotes) { 207 | String content = note.file.readAsStringSync().toLowerCase(); 208 | 209 | bool _contains = true; 210 | for (String keyword in keywords) { 211 | if (!content.contains(keyword)) { 212 | _contains = false; 213 | break; 214 | } 215 | } 216 | if (!_contains) { 217 | toRemove.add(note.title); 218 | } 219 | } 220 | shownNotes.removeWhere((n) => toRemove.contains(n.title)); 221 | } else { 222 | shownNotes.retainWhere((note) { 223 | String noteTitle = note.title.toLowerCase(); 224 | for (String keyword in keywords) { 225 | if (!noteTitle.contains(keyword)) return false; 226 | } 227 | return true; 228 | }); 229 | } 230 | } 231 | 232 | shownNotes.sort((a, b) { 233 | if (a.pinned ^ b.pinned) { 234 | return a.pinned ? -1 : 1; 235 | } else { 236 | int value = 0; 237 | 238 | switch (PrefService.getString('sort_key') ?? 'title') { 239 | case 'title': 240 | value = a.title.compareTo(b.title); 241 | break; 242 | case 'date_created': 243 | value = a.created.compareTo(b.created); 244 | break; 245 | case 'date_modified': 246 | value = a.modified.compareTo(b.modified); 247 | break; 248 | } 249 | if (!(PrefService.getBool('sort_direction_asc') ?? true)) value *= -1; 250 | 251 | return value; 252 | } 253 | }); 254 | } 255 | 256 | List _filterByTag(List notes, String cTag) { 257 | return notes.where((note) => note.hasTag(cTag)).toList(); 258 | } 259 | 260 | int countNotesWithTag(List notes, String tag) { 261 | int count = 0; 262 | notes.forEach((note) { 263 | if (note.hasTag(tag)) count++; 264 | }); 265 | return count; 266 | } 267 | 268 | List allNotes; 269 | 270 | List shownNotes; 271 | 272 | String currTag = ''; 273 | 274 | set currentTag(String newTag) { 275 | currTag = newTag; 276 | PrefService.setString('current_tag', newTag); 277 | } 278 | 279 | String get currentTag => currTag; 280 | 281 | Set allTags = {}; 282 | 283 | List rootTags = []; 284 | 285 | Future syncNow() async { 286 | /* switch (syncMethod) { 287 | case 'webdav': 288 | return await WebdavSync().syncFiles(this); 289 | } */ 290 | return null; 291 | } 292 | 293 | Note getNote(String title) { 294 | return allNotes.firstWhere((n) => n.title == title); 295 | } 296 | } 297 | -------------------------------------------------------------------------------- /lib/page/preview.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:app/model/note.dart'; 5 | import 'package:app/page/edit.dart'; 6 | import 'package:app/page/note_list.dart'; 7 | import 'package:app/provider/theme.dart'; 8 | import 'package:app/store/notes.dart'; 9 | import 'package:app/store/persistent.dart'; 10 | import 'package:markd/markdown.dart'; 11 | import 'package:preferences/preference_service.dart'; 12 | 13 | import 'package:provider/provider.dart'; 14 | import 'package:rich_code_editor/rich_code_controller.dart'; 15 | import 'package:webview_flutter/webview_flutter.dart'; 16 | import 'package:url_launcher/url_launcher.dart'; 17 | import 'package:path_provider/path_provider.dart'; 18 | import 'package:markd/markdown.dart' as markd; 19 | 20 | import 'package:markd/src/ast.dart' as markd_ast; 21 | 22 | class PreviewPage extends StatefulWidget { 23 | final NotesStore store; 24 | final String textContent; 25 | final RichCodeEditingController richCtrl; 26 | 27 | final ThemeData theme; 28 | 29 | PreviewPage(this.store, this.textContent, this.richCtrl, this.theme); 30 | 31 | @override 32 | _PreviewPageState createState() => _PreviewPageState(); 33 | } 34 | 35 | class _PreviewPageState extends State { 36 | // BuildContext context; 37 | 38 | String currentTextContent; 39 | 40 | @override 41 | void initState() { 42 | _processContent(); 43 | super.initState(); 44 | } 45 | 46 | List checkboxPositions = []; 47 | 48 | File previewFile; 49 | 50 | _processContent() async { 51 | //this.context = context; 52 | 53 | final directory = widget.store.applicationDocumentsDirectory; 54 | 55 | final previewDir = Directory('${directory.path}/preview'); 56 | 57 | const staticPreviewDir = 58 | 'file:///android_asset/flutter_assets/assets/preview'; 59 | 60 | /* final previewAssetsDir = 61 | Directory('${directory.path}/preview/assets'); */ 62 | 63 | previewFile = File('${previewDir.path}/index.html'); 64 | previewFile.createSync(recursive: true); 65 | 66 | // TODO iOS Preview 67 | 68 | currentTextContent = widget.textContent; 69 | 70 | for (final match in RegExp(r'\[(x| )\]').allMatches(currentTextContent)) { 71 | // print(match); 72 | checkboxPositions.add(match.start + 1); 73 | } 74 | 75 | String content = widget.textContent; 76 | 77 | // Wiki-Style note links like [[Note]] 78 | 79 | content = content.replaceAllMapped(RegExp(r'\[\[[^\]]+\]\]'), (match) { 80 | var str = match.input.substring(match.start, match.end); 81 | 82 | String title = str.substring(2).split(']').first; 83 | 84 | return '[$title](@note/$title' + 85 | (title.endsWith('.md') ? '' : '.md') + 86 | ')'; 87 | }); 88 | 89 | content = 90 | content.replaceAllMapped(RegExp(r'(?<=\]\(@note\/).*(?=\))'), (match) { 91 | return content.substring(match.start, match.end).replaceAll(' ', '%20'); 92 | }); 93 | 94 | content = content.replaceAll(RegExp(r'\\\\'), '\\\\\\\\'); 95 | 96 | ThemeData theme = widget.theme; 97 | 98 | String backgroundColor = theme.scaffoldBackgroundColor.value 99 | .toRadixString(16) 100 | .padLeft(8, '0') 101 | .substring(2); 102 | 103 | String textColor = theme.textTheme.body1.color.value 104 | .toRadixString(16) 105 | .padLeft(8, '0') 106 | .substring(2); 107 | 108 | String accentColor = 109 | theme.accentColor.value.toRadixString(16).padLeft(8, '0').substring(2); 110 | 111 | String generatedPreview = ''' 112 | 113 | 114 | 115 | ''' + 116 | (Provider.of(context, listen: false).currentTheme == 117 | ThemeType.light 118 | ? '' 119 | : ''' 120 | 132 | ''') + 133 | ''' 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 170 | 171 | 199 | 200 | 201 | ''' + 202 | markd.markdownToHtml( 203 | content, 204 | extensionSet: markd.ExtensionSet.gitHubWeb, 205 | inlineSyntaxes: [ 206 | if (PrefService.getBool('single_line_break_syntax') ?? false) 207 | SingleLineBreakSyntax(), 208 | ], 209 | /* blockSyntaxes: [FencedCodeBlockSyntax()], */ 210 | ) + 211 | ''' 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 225 | 226 | 231 | 232 | 233 | '''; 234 | 235 | /* generatedPreview = generatedPreview.replaceAll('\\ ', ' '); */ 236 | generatedPreview = generatedPreview 237 | .replaceAll( 238 | 'src="@attachment/', 239 | 'src="' + 240 | 'file://' + 241 | PrefService.getString('notable_attachments_directory') + 242 | '/') 243 | .replaceAll( 244 | 'src="/', 245 | 'src="' + 246 | 'file://' + 247 | PrefService.getString('notable_notes_directory') + 248 | '/'); 249 | 250 | generatedPreview = 251 | generatedPreview.replaceAll('')[1]); 254 | 255 | int checkboxIndex = -1; 256 | 257 | generatedPreview = generatedPreview.replaceAllMapped( 258 | 'disabled="disabled" class="todo" type="checkbox"', (match) { 259 | checkboxIndex++; 260 | 261 | return 'class="todo" type="checkbox" onclick="notelesscheckbox.postMessage( this.checked + \'-$checkboxIndex\');"'; 262 | }); 263 | 264 | await previewFile.writeAsString(generatedPreview); 265 | 266 | setState(() { 267 | _processingDone = true; 268 | }); 269 | } 270 | 271 | bool _processingDone = false; 272 | 273 | bool _pageLoaded = false; 274 | 275 | @override 276 | Widget build(BuildContext context) { 277 | // print('BUILD'); 278 | 279 | return StatefulBuilder( 280 | builder: (context, setState) { 281 | return !_processingDone 282 | ? Center( 283 | child: CircularProgressIndicator(), 284 | ) 285 | : Stack( 286 | children: [ 287 | WebView( 288 | initialUrl: 'file://' + previewFile.path, 289 | javascriptMode: JavascriptMode.unrestricted, 290 | onWebViewCreated: (ctrl) {}, 291 | javascriptChannels: { 292 | JavascriptChannel( 293 | name: 'flutternotable', 294 | onMessageReceived: (_) async { 295 | setState(() { 296 | _pageLoaded = true; 297 | }); 298 | }), 299 | JavascriptChannel( 300 | name: 'notelesscheckbox', 301 | onMessageReceived: (msg) async { 302 | final parts = msg.message.split('-'); 303 | 304 | final bool checked = parts[0] == 'true'; 305 | 306 | final int id = int.parse(parts[1]); 307 | 308 | final index = checkboxPositions[id]; 309 | 310 | currentTextContent = 311 | currentTextContent.substring(0, index) + 312 | (checked ? 'x' : ' ') + 313 | currentTextContent.substring(index + 1); 314 | 315 | widget.richCtrl.text = currentTextContent; 316 | 317 | // textContent 318 | }), 319 | }, 320 | navigationDelegate: (request) { 321 | print(request.url); 322 | 323 | if (request.url.startsWith('file://')) { 324 | String link = Uri.decodeFull( 325 | RegExp(r'@.*').stringMatch(request.url)); 326 | print(link); 327 | 328 | String type = 329 | RegExp(r'(?<=@).*(?=/)').stringMatch(link); 330 | 331 | String data = RegExp(r'(?<=/).*').stringMatch(link); 332 | print(type); 333 | print(data); 334 | print(Theme.of(context).brightness); 335 | switch (type) { 336 | case 'note': 337 | _navigateToNote(data); 338 | 339 | break; 340 | case 'tag': 341 | _navigateToTag(data); 342 | break; 343 | case 'search': 344 | _navigateToSearch(data); 345 | break; 346 | case 'attachment': 347 | break; 348 | } 349 | } else { 350 | launch( 351 | request.url, 352 | ); 353 | } 354 | return NavigationDecision.prevent; 355 | }, 356 | ), 357 | if (!_pageLoaded) 358 | Container( 359 | color: Theme.of(context).scaffoldBackgroundColor, 360 | alignment: Alignment.center, 361 | child: CircularProgressIndicator(), 362 | ), 363 | ], 364 | ); 365 | }, 366 | ); 367 | } 368 | 369 | void _navigateToNote(String title) async { 370 | if (!title.endsWith('.md')) title += '.md'; 371 | Note newNote = await PersistentStore.readNote( 372 | File('${PrefService.getString('notable_notes_directory')}/${title}')); 373 | if (newNote == null) { 374 | // TODO Show Error 375 | } else { 376 | Navigator.of(context).push(MaterialPageRoute( 377 | builder: (context) => EditPage(newNote, widget.store))); 378 | } 379 | } 380 | 381 | void _navigateToTag(String tag) async { 382 | Navigator.of(context).push(MaterialPageRoute( 383 | builder: (context) => NoteListPage( 384 | filterTag: tag, 385 | isFirstPage: false, 386 | ))); 387 | } 388 | 389 | void _navigateToSearch(String search) async { 390 | Navigator.of(context).push(MaterialPageRoute( 391 | builder: (context) => NoteListPage( 392 | searchText: search, 393 | isFirstPage: false, 394 | ))); 395 | } 396 | } 397 | 398 | /// Represents a hard line break. 399 | class SingleLineBreakSyntax extends InlineSyntax { 400 | SingleLineBreakSyntax() : super(r'\n'); 401 | 402 | /// Create a void
element. 403 | @override 404 | bool onMatch(InlineParser parser, Match match) { 405 | parser.addNode(markd_ast.Element.empty('br')); 406 | return true; 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /lib/editor/syntax_highlighter.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:rich_code_editor/exports.dart'; 5 | 6 | class NotelessSyntaxHighlighter implements SyntaxHighlighterBase { 7 | Color accentColor; 8 | 9 | Map styles; 10 | 11 | init(Color accentColor) { 12 | this.accentColor = accentColor; 13 | styles = { 14 | '1': TextStyle( 15 | fontStyle: FontStyle.italic, 16 | ), 17 | '2': TextStyle(fontWeight: FontWeight.bold), 18 | '3': TextStyle( 19 | fontWeight: FontWeight.bold, 20 | fontStyle: FontStyle.italic, 21 | ), 22 | '4': TextStyle( 23 | color: Colors.blue, 24 | ), 25 | '5': TextStyle( 26 | color: Colors.purple, 27 | ), 28 | '6': TextStyle( 29 | decoration: TextDecoration.lineThrough, 30 | ), 31 | '7': TextStyle( 32 | color: accentColor, 33 | fontWeight: FontWeight.bold, 34 | ), 35 | }; 36 | } 37 | 38 | NotelessSyntaxHighlighter({this.accentColor}); 39 | 40 | @override 41 | TextEditingValue addTextRemotely(TextEditingValue oldValue, String newText) { 42 | return null; 43 | } 44 | 45 | @override 46 | TextEditingValue onBackSpacePress( 47 | TextEditingValue oldValue, TextSpan currentSpan) { 48 | return null; 49 | } 50 | 51 | @override 52 | TextEditingValue onEnterPress(TextEditingValue oldValue) { 53 | int oldStart = oldValue.selection.start; 54 | 55 | final bef = oldValue.text.substring(0, oldStart - 1); 56 | 57 | String befLine = bef.split('\n').last; 58 | 59 | int trimSpace = befLine.length; 60 | 61 | befLine = befLine.trimLeft(); 62 | 63 | trimSpace = trimSpace - befLine.length; 64 | 65 | if (befLine.startsWith('- ') || befLine.startsWith('* ')) { 66 | if (befLine.length <= 2) { 67 | if (trimSpace == 0) { 68 | var newValue = oldValue.copyWith( 69 | text: bef.substring(0, oldStart - 3) + 70 | '\n\n' + 71 | oldValue.text.substring(oldStart + 1), 72 | composing: TextRange(start: -1, end: -1), 73 | selection: TextSelection.fromPosition( 74 | TextPosition( 75 | affinity: TextAffinity.upstream, offset: bef.length - 1), 76 | ), 77 | ); 78 | 79 | return newValue; 80 | } else { 81 | var newValue = oldValue.copyWith( 82 | text: oldValue.text.substring(0, oldStart - 1 - 4) + 83 | oldValue.text.substring(oldStart - 3, oldStart) + 84 | oldValue.text.substring(oldStart + 1), 85 | composing: TextRange(start: -1, end: -1), 86 | selection: TextSelection.fromPosition( 87 | TextPosition( 88 | affinity: TextAffinity.upstream, offset: bef.length - 2), 89 | ), 90 | ); 91 | 92 | return newValue; 93 | } 94 | } 95 | 96 | String sym = befLine.startsWith('* ') ? '*' : '-'; 97 | 98 | for (int i = 0; i < trimSpace; i++) { 99 | sym = ' ' + sym; 100 | } 101 | 102 | var newValue = oldValue.copyWith( 103 | text: bef + '\n$sym \n' + oldValue.text.substring(oldStart + 1), 104 | composing: TextRange(start: -1, end: -1), 105 | selection: TextSelection.fromPosition( 106 | TextPosition( 107 | affinity: TextAffinity.upstream, 108 | offset: bef.length + 3 + trimSpace), 109 | ), 110 | ); 111 | 112 | return newValue; 113 | } 114 | 115 | return null; 116 | 117 | int start = oldStart; 118 | 119 | int breakCount = 0; 120 | 121 | while (start > 0) { 122 | start--; 123 | if (oldValue.text[start] == '\n') { 124 | if (breakCount >= 1) break; 125 | breakCount++; 126 | } 127 | } 128 | if (start != 0) start++; 129 | 130 | String startOfLine = oldValue.text.substring( 131 | start, 132 | ); 133 | final before = oldValue.text.substring(0, oldStart); 134 | 135 | print(startOfLine.substring(0, 10)); 136 | 137 | if (startOfLine.startsWith('- ')) { 138 | int length = 1; 139 | 140 | if (startOfLine.startsWith('- ')) length++; 141 | /* _rec.text = before + startOfLine.substring(1).trimLeft(); 142 | _rec.selection = TextSelection( 143 | baseOffset: oldStart - length, extentOffset: oldStart - length); */ 144 | var newValue = oldValue.copyWith( 145 | text: before + '- \n' + oldValue.text, 146 | composing: TextRange(start: -1, end: -1), 147 | selection: TextSelection.fromPosition( 148 | TextPosition( 149 | affinity: TextAffinity.upstream, offset: before.length + 2), 150 | ), 151 | ); 152 | 153 | return newValue; 154 | } else {} 155 | return oldValue; 156 | } 157 | 158 | @override 159 | List parseText(TextEditingValue tev) { 160 | var texts = tev.text.split('\n'); 161 | 162 | var lsSpans = List(); 163 | 164 | bool inCodeBlock = false; 165 | 166 | int i = 0; 167 | texts.forEach((text) { 168 | i++; 169 | // print('"$text"'); 170 | 171 | if (text.startsWith('```')) { 172 | inCodeBlock = !inCodeBlock; 173 | lsSpans.add(TextSpan(text: text, style: styles['4'])); 174 | /* if (text.endsWith(' ')) { 175 | lsSpans.add(TextSpan(text: ' ')); 176 | } */ 177 | lsSpans.add(TextSpan(text: '\n')); 178 | return; 179 | } 180 | 181 | if (inCodeBlock) { 182 | lsSpans.add(TextSpan(text: text)); 183 | /* if (text.endsWith(' ')) { 184 | lsSpans.add(TextSpan(text: ' ')); 185 | } */ 186 | lsSpans.add(TextSpan(text: '\n')); 187 | 188 | return; 189 | } 190 | 191 | int lengthDiff = text.length; 192 | 193 | text = text.trimLeft(); 194 | 195 | lengthDiff = lengthDiff - text.length; 196 | 197 | String lineStart = ''; 198 | 199 | for (int i = 0; i < lengthDiff; i++) { 200 | lineStart += ' '; 201 | } 202 | 203 | if (lineStart != null) 204 | lsSpans.add( 205 | TextSpan( 206 | text: lineStart, 207 | ), 208 | ); 209 | 210 | addPrefix(String prefix) { 211 | lsSpans.add( 212 | TextSpan( 213 | text: prefix, 214 | style: TextStyle(color: accentColor, fontWeight: FontWeight.bold), 215 | ), 216 | ); 217 | } 218 | 219 | if (text.startsWith('# ')) { 220 | addPrefix('# '); 221 | text = text.substring(2); 222 | } else if (text.startsWith('## ')) { 223 | addPrefix('## '); 224 | text = text.substring(3); 225 | } else if (text.startsWith('### ')) { 226 | addPrefix('### '); 227 | text = text.substring(4); 228 | } else if (text.startsWith('#### ')) { 229 | addPrefix('#### '); 230 | text = text.substring(5); 231 | } else if (text.startsWith('##### ')) { 232 | addPrefix('##### '); 233 | text = text.substring(6); 234 | } else if (text.startsWith('###### ')) { 235 | addPrefix('###### '); 236 | text = text.substring(7); 237 | } else if (text.startsWith('- ')) { 238 | addPrefix('- '); 239 | text = text.substring(2); 240 | } else if (text.startsWith('> ')) { 241 | while (text.startsWith('> ')) { 242 | addPrefix('> '); 243 | text = text.substring(2); 244 | } 245 | } else if (text.startsWith('* ')) { 246 | addPrefix('* '); 247 | text = text.substring(2); 248 | } else {} 249 | 250 | /* String str = ''; */ 251 | 252 | // Star 253 | 254 | String s = text.replaceAllMapped( 255 | RegExp(r'(? 257 | '1' + 258 | match.input.substring(match.start, match.end) + 259 | '0'); 260 | 261 | s = s.replaceAllMapped( 262 | RegExp(r'(? 264 | '2' + 265 | match.input.substring(match.start, match.end) + 266 | '0'); 267 | 268 | s = s.replaceAllMapped( 269 | RegExp(r'(? 271 | '3' + 272 | match.input.substring(match.start, match.end) + 273 | '0'); 274 | 275 | // Underscore 276 | 277 | s = s.replaceAllMapped( 278 | RegExp(r'(? 280 | '1' + 281 | match.input.substring(match.start, match.end) + 282 | '0'); 283 | 284 | s = s.replaceAllMapped( 285 | RegExp(r'(? 287 | '2' + 288 | match.input.substring(match.start, match.end) + 289 | '0'); 290 | 291 | s = s.replaceAllMapped( 292 | RegExp(r'(? 294 | '3' + 295 | match.input.substring(match.start, match.end) + 296 | '0'); 297 | 298 | // Strikethrough 299 | 300 | s = s.replaceAllMapped( 301 | RegExp(r'~~[^~]+~~'), 302 | (match) => 303 | '6' + 304 | match.input.substring(match.start, match.end) + 305 | '0'); 306 | 307 | // Inline Code 308 | 309 | s = s.replaceAllMapped( 310 | RegExp(r'\`[^\`]+\`'), 311 | (match) => 312 | '4' + 313 | match.input.substring(match.start, match.end) + 314 | '0'); 315 | 316 | // Divider --- 317 | 318 | s = s.replaceAllMapped( 319 | RegExp(r'^---$'), 320 | (match) => 321 | '7' + 322 | match.input.substring(match.start, match.end) + 323 | '0'); 324 | 325 | s = s.replaceAllMapped( 326 | RegExp(r'^\*\*\*$'), 327 | (match) => 328 | '7' + 329 | match.input.substring(match.start, match.end) + 330 | '0'); 331 | 332 | // KaTeX 333 | 334 | s = s.replaceAllMapped( 335 | RegExp(r'(? 337 | '4' + 338 | match.input.substring(match.start, match.end) + 339 | '0'); 340 | 341 | s = s.replaceAllMapped( 342 | RegExp(r'\$\$[^\$]+\$\$'), 343 | (match) => 344 | '4' + 345 | match.input.substring(match.start, match.end) + 346 | '0'); 347 | 348 | // AsciiMath 349 | 350 | s = s.replaceAllMapped( 351 | RegExp(r'(? 353 | '4' + 354 | match.input.substring(match.start, match.end) + 355 | '0'); 356 | 357 | s = s.replaceAllMapped( 358 | RegExp(r'&&[^&]+&&'), 359 | (match) => 360 | '4' + 361 | match.input.substring(match.start, match.end) + 362 | '0'); 363 | 364 | // Wiki-Style note links like [[Note]] 365 | 366 | s = s.replaceAllMapped(RegExp(r'\[\[[^\]]+\]\]'), (match) { 367 | var str = match.input.substring(match.start, match.end); 368 | 369 | String title = str.substring(2).split(']').first; 370 | 371 | return '7[[0$title7]]0'; 372 | }); 373 | 374 | // Emojis 375 | 376 | s = s.replaceAllMapped( 377 | RegExp(r'(? 379 | '4' + 380 | match.input.substring(match.start, match.end) + 381 | '0'); 382 | 383 | // Links 384 | 385 | s = s.replaceAllMapped(RegExp(r'(!)?\[[^\]]*\]\([^\)]+\)'), (match) { 386 | var str = match.input.substring(match.start, match.end); 387 | String out = ''; 388 | if (str.startsWith('!')) { 389 | str = str.substring(1); 390 | out += '4!'; 391 | } 392 | String title = str.substring(1).split(']').first; 393 | 394 | out += 395 | '7[0$title7]'; 396 | 397 | str = str.substring(title.length + 2); 398 | 399 | out += '4' + str + '0'; 400 | 401 | return out; 402 | }); 403 | 404 | s = '0$s'; 405 | 406 | for (var part in s.split('')) { 407 | TextStyle style = styles[part[0]]; 408 | 409 | lsSpans.add(TextSpan( 410 | text: part.substring(1), 411 | style: style, 412 | )); 413 | /* if (part == '*') { 414 | lsSpans.add(TextSpan( 415 | text: str, style: TextStyle(fontWeight: FontWeight.bold))); 416 | str = ''; 417 | } */ 418 | } 419 | 420 | 421 | if (i < texts.length) { 422 | lsSpans.add(TextSpan(text: '\n')); 423 | } 424 | }); 425 | return lsSpans; 426 | } 427 | } 428 | -------------------------------------------------------------------------------- /lib/page/settings.dart: -------------------------------------------------------------------------------- 1 | import 'dart:io'; 2 | import 'dart:math'; 3 | 4 | import 'package:file_picker/file_picker.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:app/provider/theme.dart'; 7 | import 'package:app/store/notes.dart'; 8 | import 'package:permission_handler/permission_handler.dart'; 9 | import 'package:path_provider/path_provider.dart'; 10 | import 'package:preferences/preferences.dart'; 11 | import 'package:preferences/radio_preference.dart'; 12 | import 'package:provider/provider.dart'; 13 | 14 | class SettingsPage extends StatefulWidget { 15 | final NotesStore store; 16 | SettingsPage(this.store); 17 | @override 18 | _SettingsPageState createState() => _SettingsPageState(); 19 | } 20 | 21 | class _SettingsPageState extends State { 22 | NotesStore get store => widget.store; 23 | @override 24 | void initState() { 25 | PrefService.setDefaultValues({ 26 | 'sync': '', 27 | 'sync_webdav_host': '', 28 | 'sync_webdav_path': '', 29 | 'sync_webdav_username': '', 30 | 'sync_webdav_password': '', 31 | 'theme': 'light', 32 | 'search_content': true, 33 | 'editor_mode_switcher': true, 34 | 'editor_pair_brackets': false, 35 | 'notes_list_virtual_tags': false, 36 | 'debug_logs_sync': false, 37 | 'editor_auto_save': false, 38 | 'dendron_mode': false, 39 | 'sort_tags_in_sidebar': true, 40 | }); 41 | super.initState(); 42 | } 43 | 44 | @override 45 | Widget build(BuildContext context) { 46 | return Scaffold( 47 | appBar: AppBar( 48 | title: Text('Settings'), 49 | ), 50 | body: ListView(children: [ 51 | PreferenceTitle('Theme'), 52 | RadioPreference( 53 | 'Light', 54 | 'light', 55 | 'theme', 56 | isDefault: true, 57 | onSelect: () { 58 | Provider.of(context, listen: false) 59 | .updateTheme('light'); 60 | }, 61 | ), 62 | RadioPreference( 63 | 'Dark', 64 | 'dark', 65 | 'theme', 66 | onSelect: () { 67 | Provider.of(context, listen: false) 68 | .updateTheme('dark'); 69 | }, 70 | ), 71 | RadioPreference( 72 | 'Black / AMOLED', 73 | 'black', 74 | 'theme', 75 | onSelect: () { 76 | Provider.of(context, listen: false) 77 | .updateTheme('black'); 78 | }, 79 | ), 80 | ListTile( 81 | title: Text('Accent Color'), 82 | trailing: Padding( 83 | padding: const EdgeInsets.only(right: 9, left: 9), 84 | child: Container( 85 | decoration: BoxDecoration( 86 | border: Border.all(), 87 | color: Color(PrefService.getInt('theme_color') ?? 0xff21d885), 88 | ), 89 | child: SizedBox( 90 | width: 28, 91 | height: 28, 92 | ), 93 | ), 94 | ), 95 | onTap: () async { 96 | Color color = await showDialog( 97 | context: context, 98 | builder: (context) => AlertDialog( 99 | title: Text('Select accent color'), 100 | content: Container( 101 | child: GridView.count( 102 | crossAxisCount: 5, 103 | children: [ 104 | for (Color color in [ 105 | Color(0xff21d885), 106 | ...Colors.primaries, 107 | ...Colors.accents, 108 | ]) 109 | InkWell( 110 | child: Container( 111 | margin: const EdgeInsets.all(5), 112 | color: color, 113 | ), 114 | onTap: () { 115 | Navigator.of(context).pop(color); 116 | }, 117 | ) 118 | ], 119 | ), 120 | width: MediaQuery.of(context).size.width * .7, 121 | ), 122 | actions: [ 123 | FlatButton( 124 | child: Text('Cancel'), 125 | onPressed: () { 126 | Navigator.of(context).pop(); 127 | }, 128 | ), 129 | ], 130 | )); 131 | if (color != null) { 132 | PrefService.setInt('theme_color', color.value); 133 | 134 | Provider.of(context, listen: false).accentColor = 135 | color; 136 | } 137 | }, 138 | ), 139 | if (Platform.isAndroid) ...[ 140 | PreferenceTitle('Data Directory'), 141 | SwitchPreference( 142 | 'Use external storage', 143 | 'notable_external_directory_enabled', 144 | onChange: () async { 145 | if (PrefService.getString('notable_external_directory') == null) { 146 | PrefService.setString('notable_external_directory', 147 | (await getExternalStorageDirectory()).path); 148 | } 149 | 150 | await store.listNotes(); 151 | await store.filterAndSortNotes(); 152 | await store.updateTagList(); 153 | 154 | if (mounted) setState(() {}); 155 | }, 156 | ), 157 | PreferenceHider([ 158 | ListTile( 159 | title: Text('Location'), 160 | subtitle: Text( 161 | PrefService.getString('notable_external_directory') ?? '', 162 | ), 163 | onTap: () async { 164 | Directory dir; 165 | 166 | final dirStr = await _pickExternalDir(); 167 | 168 | if (dirStr == null) { 169 | return; 170 | } 171 | 172 | dir = Directory(dirStr); 173 | 174 | if (dir != null) { 175 | showDialog( 176 | context: context, 177 | builder: (context) => AlertDialog( 178 | title: ListTile( 179 | leading: CircularProgressIndicator(), 180 | title: Text('Processing files...'), 181 | ), 182 | ), 183 | barrierDismissible: false, 184 | ); 185 | PrefService.setString('notable_external_directory', dir.path); 186 | 187 | await store.listNotes(); 188 | await store.filterAndSortNotes(); 189 | await store.updateTagList(); 190 | setState(() {}); 191 | Navigator.of(context).pop(); 192 | } 193 | }, 194 | ), 195 | ], '!notable_external_directory_enabled'), 196 | ], 197 | PreferenceTitle('Editor'), 198 | SwitchPreference( 199 | 'Auto Save', 200 | 'editor_auto_save', 201 | ), 202 | SwitchPreference( 203 | 'Use Mode Switcher', 204 | 'editor_mode_switcher', 205 | ), 206 | SwitchPreference( 207 | 'Pair Brackets/Quotes', 208 | 'editor_pair_brackets', 209 | ), 210 | PreferenceTitle('Search'), 211 | SwitchPreference( 212 | 'Search content of notes', 213 | 'search_content', 214 | ), 215 | PreferenceTitle('Tags'), 216 | SwitchPreference( 217 | 'Sort tags alphabetically in the sidebar', 218 | 'sort_tags_in_sidebar', 219 | ), 220 | PreferenceTitle('Preview'), 221 | SwitchPreference( 222 | 'Enable single line break syntax', 223 | 'single_line_break_syntax', 224 | desc: 225 | 'When enabled, single line breaks are rendered as real line breaks', 226 | ), 227 | /* PreferenceTitle('Sync'), 228 | RadioPreference( 229 | 'No Sync', 230 | '', 231 | 'sync', 232 | isDefault: true, 233 | onSelect: () { 234 | setState(() { 235 | store.syncMethod = ''; 236 | }); 237 | }, 238 | ), 239 | RadioPreference( 240 | 'WebDav Sync', 241 | 'webdav', 242 | 'sync', 243 | onSelect: () { 244 | setState(() { 245 | store.syncMethod = 'webdav'; 246 | }); 247 | }, 248 | ), 249 | if (store.syncMethod == 'webdav') 250 | Column( 251 | children: [ 252 | Padding( 253 | padding: const EdgeInsets.symmetric(horizontal: 16), 254 | child: Text( 255 | 'WARNING: WebDav Sync is not supported! Please use another app to sync if possible (Syncthing is recommended) and do NOT use it for important data or accounts! ', 256 | style: TextStyle(color: Colors.red), 257 | ), 258 | ), 259 | TextFieldPreference( 260 | 'Host', 261 | 'sync_webdav_host', 262 | hintText: 'mynextcloud.tld/remote.php/webdav/', 263 | ), 264 | TextFieldPreference( 265 | 'Path', 266 | 'sync_webdav_path', 267 | hintText: 'notable', 268 | ), 269 | TextFieldPreference('Username', 'sync_webdav_username'), 270 | TextFieldPreference( 271 | 'Password', 272 | 'sync_webdav_password', 273 | obscureText: true, 274 | ), 275 | ], 276 | ), 277 | */ 278 | PreferenceTitle('More'), 279 | ListTile( 280 | title: Text('Recreate tutorial notes'), 281 | onTap: () async { 282 | if (await showDialog( 283 | context: context, 284 | builder: (context) => AlertDialog( 285 | title: Text( 286 | 'Do you want to recreate the tutorial notes and attachments?'), 287 | actions: [ 288 | FlatButton( 289 | child: Text('Cancel'), 290 | onPressed: () { 291 | Navigator.of(context).pop(false); 292 | }, 293 | ), 294 | FlatButton( 295 | child: Text('Recreate'), 296 | onPressed: () { 297 | Navigator.of(context).pop(true); 298 | }, 299 | ) 300 | ], 301 | )) ?? 302 | false) { 303 | await store.createTutorialNotes(); 304 | await store.createTutorialAttachments(); 305 | await store.listNotes(); 306 | await store.filterAndSortNotes(); 307 | await store.updateTagList(); 308 | } 309 | }, 310 | ), 311 | /* PreferenceTitle('Debug'), 312 | SwitchPreference( 313 | 'Create sync logfile ', 314 | 'debug_logs_sync', 315 | ), */ 316 | PreferenceTitle('Experimental'), 317 | SwitchPreference( 318 | 'Enable Dendron support', 319 | 'dendron_mode', 320 | desc: 'Dendron is a VSCode-based note-taking tool', 321 | onChange: () async { 322 | await store.listNotes(); 323 | await store.filterAndSortNotes(); 324 | await store.updateTagList(); 325 | 326 | if (mounted) setState(() {}); 327 | }, 328 | ), 329 | SwitchPreference( 330 | 'Automatic bullet points', 331 | 'auto_bullet_points', 332 | desc: 333 | 'Adds a bullet point to a new line if the line before it had one', 334 | ), 335 | SwitchPreference( 336 | 'Show virtual tags', 337 | 'notes_list_virtual_tags', 338 | desc: 339 | 'Adds a virtual tag (#/path) to notes which are in a subdirectory', 340 | ), 341 | ]), 342 | ); 343 | } 344 | 345 | Future _pickExternalDir() async { 346 | if (!await Permission.storage.request().isGranted) { 347 | return null; 348 | } 349 | 350 | var dir = await FilePicker.platform.getDirectoryPath(); 351 | if ((dir ?? '').isNotEmpty) { 352 | if (await _checkIfDirectoryIsWritable(dir)) { 353 | return dir; 354 | } 355 | } 356 | 357 | if ((await Permission.storage.request()).isDenied) { 358 | return null; 359 | } 360 | 361 | var externalDir = await getExternalStorageDirectory(); 362 | if (await _checkIfDirectoryIsWritable(externalDir.path)) { 363 | return externalDir.path; 364 | } 365 | return null; 366 | } 367 | 368 | Future _checkIfDirectoryIsWritable(String path) async { 369 | final testFile = File('$path/${Random().nextInt(1000000)}'); 370 | 371 | try { 372 | await testFile.create(recursive: true); 373 | await testFile.writeAsString("This is only a test file, please ignore."); 374 | await testFile.delete(); 375 | } catch (e) { 376 | return false; 377 | } 378 | return true; 379 | } 380 | } 381 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | args: 5 | dependency: transitive 6 | description: 7 | name: args 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "1.6.0" 11 | bsdiff: 12 | dependency: "direct main" 13 | description: 14 | name: bsdiff 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.1.3" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | crypto: 47 | dependency: transitive 48 | description: 49 | name: crypto 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "3.0.0" 53 | csslib: 54 | dependency: transitive 55 | description: 56 | name: csslib 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "0.16.2" 60 | device_info: 61 | dependency: "direct main" 62 | description: 63 | name: device_info 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "2.0.0" 67 | device_info_platform_interface: 68 | dependency: transitive 69 | description: 70 | name: device_info_platform_interface 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "2.0.1" 74 | ffi: 75 | dependency: transitive 76 | description: 77 | name: ffi 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.0.0" 81 | file: 82 | dependency: transitive 83 | description: 84 | name: file 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "6.1.0" 88 | file_picker: 89 | dependency: "direct main" 90 | description: 91 | name: file_picker 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "3.0.0" 95 | flutter: 96 | dependency: "direct main" 97 | description: flutter 98 | source: sdk 99 | version: "0.0.0" 100 | flutter_markdown: 101 | dependency: "direct main" 102 | description: 103 | name: flutter_markdown 104 | url: "https://pub.dartlang.org" 105 | source: hosted 106 | version: "0.4.4" 107 | flutter_plugin_android_lifecycle: 108 | dependency: transitive 109 | description: 110 | name: flutter_plugin_android_lifecycle 111 | url: "https://pub.dartlang.org" 112 | source: hosted 113 | version: "2.0.0" 114 | flutter_slidable: 115 | dependency: "direct main" 116 | description: 117 | name: flutter_slidable 118 | url: "https://pub.dartlang.org" 119 | source: hosted 120 | version: "0.5.7" 121 | flutter_web_plugins: 122 | dependency: transitive 123 | description: flutter 124 | source: sdk 125 | version: "0.0.0" 126 | front_matter: 127 | dependency: "direct main" 128 | description: 129 | name: front_matter 130 | url: "https://pub.dartlang.org" 131 | source: hosted 132 | version: "1.1.0" 133 | html: 134 | dependency: "direct main" 135 | description: 136 | name: html 137 | url: "https://pub.dartlang.org" 138 | source: hosted 139 | version: "0.14.0+4" 140 | intl: 141 | dependency: "direct main" 142 | description: 143 | name: intl 144 | url: "https://pub.dartlang.org" 145 | source: hosted 146 | version: "0.17.0" 147 | js: 148 | dependency: transitive 149 | description: 150 | name: js 151 | url: "https://pub.dartlang.org" 152 | source: hosted 153 | version: "0.6.3" 154 | markd: 155 | dependency: "direct main" 156 | description: 157 | path: "." 158 | ref: HEAD 159 | resolved-ref: af66d9941323d68764c940ba29031f9eac32fda4 160 | url: "https://github.com/redsolver/markd.git" 161 | source: git 162 | version: "2.1.3+7" 163 | markdown: 164 | dependency: transitive 165 | description: 166 | name: markdown 167 | url: "https://pub.dartlang.org" 168 | source: hosted 169 | version: "2.1.8" 170 | matcher: 171 | dependency: transitive 172 | description: 173 | name: matcher 174 | url: "https://pub.dartlang.org" 175 | source: hosted 176 | version: "0.12.10" 177 | material_design_icons_flutter: 178 | dependency: "direct main" 179 | description: 180 | name: material_design_icons_flutter 181 | url: "https://pub.dartlang.org" 182 | source: hosted 183 | version: "4.0.5655" 184 | meta: 185 | dependency: transitive 186 | description: 187 | name: meta 188 | url: "https://pub.dartlang.org" 189 | source: hosted 190 | version: "1.3.0" 191 | nested: 192 | dependency: transitive 193 | description: 194 | name: nested 195 | url: "https://pub.dartlang.org" 196 | source: hosted 197 | version: "0.0.4" 198 | package_info: 199 | dependency: "direct main" 200 | description: 201 | name: package_info 202 | url: "https://pub.dartlang.org" 203 | source: hosted 204 | version: "0.4.3" 205 | path: 206 | dependency: transitive 207 | description: 208 | name: path 209 | url: "https://pub.dartlang.org" 210 | source: hosted 211 | version: "1.8.0" 212 | path_provider: 213 | dependency: "direct main" 214 | description: 215 | name: path_provider 216 | url: "https://pub.dartlang.org" 217 | source: hosted 218 | version: "2.0.1" 219 | path_provider_linux: 220 | dependency: transitive 221 | description: 222 | name: path_provider_linux 223 | url: "https://pub.dartlang.org" 224 | source: hosted 225 | version: "2.0.0" 226 | path_provider_macos: 227 | dependency: transitive 228 | description: 229 | name: path_provider_macos 230 | url: "https://pub.dartlang.org" 231 | source: hosted 232 | version: "2.0.0" 233 | path_provider_platform_interface: 234 | dependency: transitive 235 | description: 236 | name: path_provider_platform_interface 237 | url: "https://pub.dartlang.org" 238 | source: hosted 239 | version: "2.0.1" 240 | path_provider_windows: 241 | dependency: transitive 242 | description: 243 | name: path_provider_windows 244 | url: "https://pub.dartlang.org" 245 | source: hosted 246 | version: "2.0.0" 247 | permission_handler: 248 | dependency: "direct main" 249 | description: 250 | name: permission_handler 251 | url: "https://pub.dartlang.org" 252 | source: hosted 253 | version: "6.1.0" 254 | permission_handler_platform_interface: 255 | dependency: transitive 256 | description: 257 | name: permission_handler_platform_interface 258 | url: "https://pub.dartlang.org" 259 | source: hosted 260 | version: "3.1.0" 261 | platform: 262 | dependency: transitive 263 | description: 264 | name: platform 265 | url: "https://pub.dartlang.org" 266 | source: hosted 267 | version: "3.0.0" 268 | plugin_platform_interface: 269 | dependency: transitive 270 | description: 271 | name: plugin_platform_interface 272 | url: "https://pub.dartlang.org" 273 | source: hosted 274 | version: "2.0.0" 275 | preferences: 276 | dependency: "direct main" 277 | description: 278 | name: preferences 279 | url: "https://pub.dartlang.org" 280 | source: hosted 281 | version: "5.2.1" 282 | process: 283 | dependency: transitive 284 | description: 285 | name: process 286 | url: "https://pub.dartlang.org" 287 | source: hosted 288 | version: "4.1.0" 289 | provider: 290 | dependency: "direct main" 291 | description: 292 | name: provider 293 | url: "https://pub.dartlang.org" 294 | source: hosted 295 | version: "4.3.2+2" 296 | quick_actions: 297 | dependency: "direct main" 298 | description: 299 | name: quick_actions 300 | url: "https://pub.dartlang.org" 301 | source: hosted 302 | version: "0.4.0+9" 303 | quiver: 304 | dependency: "direct main" 305 | description: 306 | name: quiver 307 | url: "https://pub.dartlang.org" 308 | source: hosted 309 | version: "3.0.0" 310 | receive_sharing_intent: 311 | dependency: "direct main" 312 | description: 313 | name: receive_sharing_intent 314 | url: "https://pub.dartlang.org" 315 | source: hosted 316 | version: "1.4.1" 317 | rich_code_editor: 318 | dependency: "direct main" 319 | description: 320 | path: "." 321 | ref: HEAD 322 | resolved-ref: dd57cc6f808f2bb125c458eba016edb85f1ebaaa 323 | url: "https://github.com/bats64mgutsi/rich_code_editor.git" 324 | source: git 325 | version: "1.0.5" 326 | shared_preferences: 327 | dependency: "direct overridden" 328 | description: 329 | name: shared_preferences 330 | url: "https://pub.dartlang.org" 331 | source: hosted 332 | version: "2.0.4" 333 | shared_preferences_linux: 334 | dependency: transitive 335 | description: 336 | name: shared_preferences_linux 337 | url: "https://pub.dartlang.org" 338 | source: hosted 339 | version: "2.0.0" 340 | shared_preferences_macos: 341 | dependency: transitive 342 | description: 343 | name: shared_preferences_macos 344 | url: "https://pub.dartlang.org" 345 | source: hosted 346 | version: "2.0.0" 347 | shared_preferences_platform_interface: 348 | dependency: transitive 349 | description: 350 | name: shared_preferences_platform_interface 351 | url: "https://pub.dartlang.org" 352 | source: hosted 353 | version: "2.0.0" 354 | shared_preferences_web: 355 | dependency: transitive 356 | description: 357 | name: shared_preferences_web 358 | url: "https://pub.dartlang.org" 359 | source: hosted 360 | version: "2.0.0" 361 | shared_preferences_windows: 362 | dependency: transitive 363 | description: 364 | name: shared_preferences_windows 365 | url: "https://pub.dartlang.org" 366 | source: hosted 367 | version: "2.0.0" 368 | sky_engine: 369 | dependency: transitive 370 | description: flutter 371 | source: sdk 372 | version: "0.0.99" 373 | source_span: 374 | dependency: transitive 375 | description: 376 | name: source_span 377 | url: "https://pub.dartlang.org" 378 | source: hosted 379 | version: "1.7.0" 380 | stack_trace: 381 | dependency: transitive 382 | description: 383 | name: stack_trace 384 | url: "https://pub.dartlang.org" 385 | source: hosted 386 | version: "1.10.0" 387 | string_scanner: 388 | dependency: transitive 389 | description: 390 | name: string_scanner 391 | url: "https://pub.dartlang.org" 392 | source: hosted 393 | version: "1.0.5" 394 | term_glyph: 395 | dependency: transitive 396 | description: 397 | name: term_glyph 398 | url: "https://pub.dartlang.org" 399 | source: hosted 400 | version: "1.1.0" 401 | typed_data: 402 | dependency: transitive 403 | description: 404 | name: typed_data 405 | url: "https://pub.dartlang.org" 406 | source: hosted 407 | version: "1.3.0" 408 | url_launcher: 409 | dependency: "direct main" 410 | description: 411 | name: url_launcher 412 | url: "https://pub.dartlang.org" 413 | source: hosted 414 | version: "6.0.2" 415 | url_launcher_linux: 416 | dependency: transitive 417 | description: 418 | name: url_launcher_linux 419 | url: "https://pub.dartlang.org" 420 | source: hosted 421 | version: "2.0.0" 422 | url_launcher_macos: 423 | dependency: transitive 424 | description: 425 | name: url_launcher_macos 426 | url: "https://pub.dartlang.org" 427 | source: hosted 428 | version: "2.0.0" 429 | url_launcher_platform_interface: 430 | dependency: transitive 431 | description: 432 | name: url_launcher_platform_interface 433 | url: "https://pub.dartlang.org" 434 | source: hosted 435 | version: "2.0.2" 436 | url_launcher_web: 437 | dependency: transitive 438 | description: 439 | name: url_launcher_web 440 | url: "https://pub.dartlang.org" 441 | source: hosted 442 | version: "2.0.0" 443 | url_launcher_windows: 444 | dependency: transitive 445 | description: 446 | name: url_launcher_windows 447 | url: "https://pub.dartlang.org" 448 | source: hosted 449 | version: "2.0.0" 450 | uuid: 451 | dependency: "direct main" 452 | description: 453 | name: uuid 454 | url: "https://pub.dartlang.org" 455 | source: hosted 456 | version: "3.0.3" 457 | vector_math: 458 | dependency: transitive 459 | description: 460 | name: vector_math 461 | url: "https://pub.dartlang.org" 462 | source: hosted 463 | version: "2.1.0" 464 | webview_flutter: 465 | dependency: "direct main" 466 | description: 467 | name: webview_flutter 468 | url: "https://pub.dartlang.org" 469 | source: hosted 470 | version: "1.0.1" 471 | win32: 472 | dependency: transitive 473 | description: 474 | name: win32 475 | url: "https://pub.dartlang.org" 476 | source: hosted 477 | version: "2.0.4" 478 | xdg_directories: 479 | dependency: transitive 480 | description: 481 | name: xdg_directories 482 | url: "https://pub.dartlang.org" 483 | source: hosted 484 | version: "0.2.0" 485 | yaml: 486 | dependency: "direct main" 487 | description: 488 | name: yaml 489 | url: "https://pub.dartlang.org" 490 | source: hosted 491 | version: "2.2.1" 492 | sdks: 493 | dart: ">=2.12.0 <3.0.0" 494 | flutter: ">=1.22.0" 495 | --------------------------------------------------------------------------------