From 436c537514bd7a9810b2042eb87acd1061ddbc76 Mon Sep 17 00:00:00 2001 From: springeye Date: Thu, 18 Jan 2024 14:11:09 +0800 Subject: [PATCH] init commit --- .github/workflows/release.yml | 61 ++ .github/workflows/test.yml | 48 + .gitignore | 20 + LICENSE | 21 + README.md | 14 + build.gradle.kts | 8 + composeApp/build.gradle.kts | 227 ++++ composeApp/rules.pro | 13 + .../src/androidMain/AndroidManifest.xml | 35 + .../springeye/memosc/ContextProvider.kt | 18 + .../memosc/DataStorePreferences.android.kt | 21 + .../github/springeye/memosc/MainActivity.kt | 85 ++ .../springeye/memosc/MemoscApplication.kt | 17 + .../springeye/memosc/Platform.android.kt | 10 + .../memosc/PopupNotification.android.kt | 11 + .../springeye/memosc/components/Card.kt | 57 + .../memosc/components/Heatmap.android.kt | 33 + .../memosc/components/HeatmapStat.android.kt | 8 + .../springeye/memosc/core/IFile.android.kt | 8 + .../springeye/memosc/core/Instant.android.kt | 22 + .../springeye/memosc/core/Instant1.android.kt | 14 + .../memosc/db/AppDatabase.android.kt | 7 + .../springeye/memosc/db/MemoDao.android.kt | 53 + .../memosc/db/RemoteKeyDao.android.kt | 5 + .../memosc/di/CacheCookiesStorage.android.kt | 60 ++ .../github/springeye/memosc/logger.android.kt | 8 + .../memosc/model/DailyUsageStat.android.kt | 27 + .../springeye/memosc/tab/ArchivedTab.kt | 33 + .../springeye/memosc/tab/DaysReviewTab.kt | 35 + .../github/springeye/memosc/tab/ExploreTab.kt | 63 ++ .../github/springeye/memosc/tab/HomeTab.kt | 646 ++++++++++++ .../github/springeye/memosc/tab/InboxTab.kt | 33 + .../springeye/memosc/tab/ResourcesTab.kt | 87 ++ .../springeye/memosc/tab/SettingsTab.kt | 33 + .../drawable-v24/ic_launcher_foreground.xml | 30 + .../res/drawable/ic_launcher_background.xml | 170 +++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + .../res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3593 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5339 bytes .../res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2636 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4926 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7472 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7909 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11873 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10652 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16570 bytes .../src/androidMain/res/values/strings.xml | 3 + .../github/springeye/memosc/AppPreferences.kt | 75 ++ .../github/springeye/memosc/AppSettings.kt | 6 + .../github/springeye/memosc/CoreComponent.kt | 22 + .../springeye/memosc/CoroutinesComponent.kt | 23 + .../springeye/memosc/DataStorePreferences.kt | 34 + .../com/github/springeye/memosc/Greeting.kt | 8 + .../springeye/memosc/LoadingAnimation.kt | 74 ++ .../com/github/springeye/memosc/MemosApi.kt | 136 +++ .../com/github/springeye/memosc/Platform.kt | 6 + .../springeye/memosc/PopupNotification.kt | 9 + .../com/github/springeye/memosc/Toast.kt | 83 ++ .../springeye/memosc/components/Heatmap.kt | 39 + .../memosc/components/HeatmapStat.kt | 21 + .../springeye/memosc/components/TextField.kt | 102 ++ .../memosc/core/AspectRatioReference.kt | 15 + .../core/AspectRatioReferenceModifier.kt | 130 +++ .../memosc/core/Base64ImageFetcher.kt | 43 + .../com/github/springeye/memosc/core/IFile.kt | 7 + .../github/springeye/memosc/core/Instant.kt | 7 + .../github/springeye/memosc/db/AppDatabase.kt | 5 + .../springeye/memosc/db/MemoQueryWhere.kt | 17 + .../springeye/memosc/db/MemoRemoteMediator.kt | 61 ++ .../memosc/di/PersistentCookiesStorage.kt | 88 ++ .../com/github/springeye/memosc/di/module.kt | 135 +++ .../com/github/springeye/memosc/logger.kt | 21 + .../springeye/memosc/model/DailyUsageStat.kt | 20 + .../springeye/memosc/model/ErrorMessage.kt | 8 + .../com/github/springeye/memosc/model/Memo.kt | 78 ++ .../github/springeye/memosc/model/Resource.kt | 72 ++ .../springeye/memosc/model/ShareContent.kt | 51 + .../github/springeye/memosc/model/Status.kt | 14 + .../com/github/springeye/memosc/model/User.kt | 54 + .../memosc/repository/MemoPagingSource.kt | 31 + .../memosc/repository/MemoRepository.kt | 15 + .../github/springeye/memosc/ui/AppModel.kt | 9 + .../springeye/memosc/ui/app/AppScreenModel.kt | 41 + .../springeye/memosc/ui/home/ArchivedModel.kt | 85 ++ .../springeye/memosc/ui/home/MemoModel.kt | 224 ++++ .../springeye/memosc/ui/home/NotifiModel.kt | 27 + .../springeye/memosc/ui/home/ProfileModel.kt | 86 ++ .../memosc/ui/login/LoginScreenModel.kt | 50 + .../wakaztahir/codeeditor/model/CodeLang.kt | 61 ++ .../codeeditor/parser/ParseResult.kt | 143 +++ .../wakaztahir/codeeditor/parser/Parser.kt | 42 + .../codeeditor/prettify/PrettifyParser.kt | 46 + .../codeeditor/prettify/lang/Lang.kt | 36 + .../codeeditor/prettify/lang/LangAppollo.kt | 95 ++ .../codeeditor/prettify/lang/LangBasic.kt | 64 ++ .../codeeditor/prettify/lang/LangClj.kt | 100 ++ .../codeeditor/prettify/lang/LangCss.kt | 171 +++ .../codeeditor/prettify/lang/LangDart.kt | 164 +++ .../codeeditor/prettify/lang/LangErlang.kt | 137 +++ .../codeeditor/prettify/lang/LangEx.kt | 122 +++ .../codeeditor/prettify/lang/LangGo.kt | 89 ++ .../codeeditor/prettify/lang/LangHs.kt | 146 +++ .../codeeditor/prettify/lang/LangKotlin.kt | 72 ++ .../codeeditor/prettify/lang/LangLasso.kt | 107 ++ .../codeeditor/prettify/lang/LangLisp.kt | 143 +++ .../codeeditor/prettify/lang/LangLlvm.kt | 100 ++ .../codeeditor/prettify/lang/LangLogtalk.kt | 69 ++ .../codeeditor/prettify/lang/LangLua.kt | 103 ++ .../codeeditor/prettify/lang/LangMatlab.kt | 283 +++++ .../codeeditor/prettify/lang/LangMd.kt | 37 + .../codeeditor/prettify/lang/LangMl.kt | 102 ++ .../codeeditor/prettify/lang/LangMumps.kt | 180 ++++ .../codeeditor/prettify/lang/LangN.kt | 132 +++ .../codeeditor/prettify/lang/LangPascal.kt | 105 ++ .../codeeditor/prettify/lang/LangProto.kt | 21 + .../codeeditor/prettify/lang/LangR.kt | 96 ++ .../codeeditor/prettify/lang/LangRd.kt | 87 ++ .../codeeditor/prettify/lang/LangScala.kt | 105 ++ .../codeeditor/prettify/lang/LangSql.kt | 100 ++ .../codeeditor/prettify/lang/LangSwift.kt | 75 ++ .../codeeditor/prettify/lang/LangTcl.kt | 95 ++ .../codeeditor/prettify/lang/LangTex.kt | 86 ++ .../codeeditor/prettify/lang/LangVb.kt | 122 +++ .../codeeditor/prettify/lang/LangVhdl.kt | 101 ++ .../codeeditor/prettify/lang/LangWiki.kt | 112 ++ .../codeeditor/prettify/lang/LangXq.kt | 113 ++ .../codeeditor/prettify/lang/LangYaml.kt | 98 ++ .../prettify/parser/CombinePrefixPattern.kt | 365 +++++++ .../codeeditor/prettify/parser/Job.kt | 108 ++ .../codeeditor/prettify/parser/Prettify.kt | 978 ++++++++++++++++++ .../codeeditor/prettify/parser/Util.kt | 244 +++++ .../wakaztahir/codeeditor/theme/CodeTheme.kt | 48 + .../codeeditor/theme/EditorThemes.kt | 39 + .../codeeditor/theme/SyntaxColors.kt | 19 + .../wakaztahir/codeeditor/utils/Annotation.kt | 35 + .../com/wakaztahir/codeeditor/utils/utils.kt | 15 + .../resources/compose-multiplatform.xml | 36 + .../resources/images/ic_chrome_close.png | Bin 0 -> 298 bytes .../resources/images/ic_chrome_maximize.png | Bin 0 -> 271 bytes .../resources/images/ic_chrome_minimize.png | Bin 0 -> 166 bytes .../resources/images/ic_chrome_unmaximize.png | Bin 0 -> 366 bytes composeApp/src/commonMain/resources/logo.png | Bin 0 -> 57311 bytes .../springeye/memosc/db/model/create_table.sq | 34 + .../github/springeye/memosc/db/model/query.sq | 41 + .../com/github/springeye/memosc/Bootstrap.kt | 46 + .../memosc/DataStorePreferences.desktop.kt | 24 + .../com/github/springeye/memosc/DesktopApp.kt | 57 + .../github/springeye/memosc/Platform.jvm.kt | 7 + .../memosc/PopupNotification.desktop.kt | 20 + .../springeye/memosc/components/Card.kt | 71 ++ .../springeye/memosc/components/EditMemo.kt | 355 +++++++ .../memosc/components/Heatmap.desktop.kt | 31 + .../memosc/components/HeatmapStat.desktop.kt | 52 + .../springeye/memosc/components/Markdown.kt | 121 +++ .../springeye/memosc/components/MemoList.kt | 263 +++++ .../springeye/memosc/components/Resources.kt | 98 ++ .../springeye/memosc/core/IFile.desktop.kt | 19 + .../springeye/memosc/core/Instant.desktop.kt | 23 + .../springeye/memosc/core/LinkBuilder.kt | 142 +++ .../memosc/db/AppDatabase.desktop.kt | 24 + .../memosc/di/CacheCookiesStorage.desktop.kt | 57 + .../memosc/ext/AnnotatedStringExt.kt | 273 +++++ .../github/springeye/memosc/logger.desktop.kt | 8 + .../com/github/springeye/memosc/main.kt | 156 +++ .../memosc/model/DailyUsageStat.desktop.kt | 43 + .../springeye/memosc/ui/app/AppScreen.kt | 69 ++ .../springeye/memosc/ui/home/HomeScreen.kt | 168 +++ .../memosc/ui/home/tab/ArchivedTab.kt | 77 ++ .../memosc/ui/home/tab/DaysReviewTab.kt | 40 + .../memosc/ui/home/tab/ExploreTab.kt | 63 ++ .../springeye/memosc/ui/home/tab/HomeTab.kt | 215 ++++ .../springeye/memosc/ui/home/tab/InboxTab.kt | 49 + .../memosc/ui/home/tab/ProfileTab.kt | 121 +++ .../memosc/ui/home/tab/ResourcesTab.kt | 90 ++ .../memosc/ui/home/tab/SettingsTab.kt | 36 + .../springeye/memosc/ui/login/LoginScreen.kt | 131 +++ .../kotlin/DataStorePreferences.ios.kt | 31 + composeApp/src/iosMain/kotlin/Database.ios.kt | 5 + .../src/iosMain/kotlin/MainViewController.kt | 3 + composeApp/src/iosMain/kotlin/Platform.ios.kt | 7 + .../iosMain/kotlin/PopupNotification.ios.kt | 15 + .../iosMain/kotlin/components/Heatmap.ios.kt | 7 + .../src/iosMain/kotlin/core/IFile.ios.kt | 6 + .../src/iosMain/kotlin/core/Instant.ios.kt | 32 + .../iosMain/kotlin/core/loader/MemoDao.ios.kt | 5 + .../kotlin/di/CacheCookiesStorage.ios.kt | 64 ++ composeApp/src/iosMain/kotlin/logger.ios.kt | 6 + .../kotlin/model/DailyUsageStat.ios.kt | 5 + gradle.properties | 19 + gradle/libs.versions.toml | 135 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63375 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 248 +++++ gradlew.bat | 92 ++ iosApp/Configuration/Config.xcconfig | 3 + iosApp/iosApp.xcodeproj/project.pbxproj | 383 +++++++ .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/app-icon-1024.png | Bin 0 -> 67285 bytes iosApp/iosApp/Assets.xcassets/Contents.json | 6 + iosApp/iosApp/ContentView.swift | 21 + iosApp/iosApp/Info.plist | 50 + .../Preview Assets.xcassets/Contents.json | 6 + iosApp/iosApp/iOSApp.swift | 10 + launcher/logo.ico | Bin 0 -> 4286 bytes launcher/logo.png | Bin 0 -> 57311 bytes settings.gradle.kts | 25 + 210 files changed, 14281 insertions(+) create mode 100644 .github/workflows/release.yml create mode 100644 .github/workflows/test.yml create mode 100755 .gitignore create mode 100644 LICENSE create mode 100755 README.md create mode 100755 build.gradle.kts create mode 100755 composeApp/build.gradle.kts create mode 100644 composeApp/rules.pro create mode 100755 composeApp/src/androidMain/AndroidManifest.xml create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/ContextProvider.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/DataStorePreferences.android.kt create mode 100755 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MainActivity.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MemoscApplication.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/Platform.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/PopupNotification.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Card.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Heatmap.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/IFile.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant1.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/AppDatabase.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/MemoDao.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/RemoteKeyDao.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/logger.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.android.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ArchivedTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/DaysReviewTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ExploreTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/HomeTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/InboxTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ResourcesTab.kt create mode 100644 composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/SettingsTab.kt create mode 100755 composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml create mode 100755 composeApp/src/androidMain/res/drawable/ic_launcher_background.xml create mode 100755 composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100755 composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100755 composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png create mode 100755 composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png create mode 100755 composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher.png create mode 100755 composeApp/src/androidMain/res/mipmap-mdpi/ic_launcher_round.png create mode 100755 composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher.png create mode 100755 composeApp/src/androidMain/res/mipmap-xhdpi/ic_launcher_round.png create mode 100755 composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher.png create mode 100755 composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100755 composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher.png create mode 100755 composeApp/src/androidMain/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100755 composeApp/src/androidMain/res/values/strings.xml create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppPreferences.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppSettings.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoreComponent.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoroutinesComponent.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/DataStorePreferences.kt create mode 100755 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Greeting.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/LoadingAnimation.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/MemosApi.kt create mode 100755 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Platform.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/PopupNotification.kt create mode 100755 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Toast.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/Heatmap.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/TextField.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReference.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReferenceModifier.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Base64ImageFetcher.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/IFile.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Instant.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/AppDatabase.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoQueryWhere.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoRemoteMediator.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/PersistentCookiesStorage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/module.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/logger.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ErrorMessage.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Memo.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Resource.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ShareContent.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Status.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/User.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoPagingSource.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoRepository.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/AppModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/app/AppScreenModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ArchivedModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/MemoModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/NotifiModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ProfileModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreenModel.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/model/CodeLang.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/ParseResult.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/Parser.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/PrettifyParser.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/Lang.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangAppollo.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangBasic.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangClj.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangCss.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangDart.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangErlang.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangEx.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangGo.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangHs.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangKotlin.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLasso.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLisp.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLlvm.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLogtalk.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLua.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMatlab.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMd.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMumps.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangN.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangPascal.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangProto.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangR.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangRd.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangScala.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSql.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSwift.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTcl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTex.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVb.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVhdl.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangWiki.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangXq.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangYaml.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/CombinePrefixPattern.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Job.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Prettify.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Util.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/CodeTheme.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/EditorThemes.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/SyntaxColors.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/Annotation.kt create mode 100644 composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/utils.kt create mode 100755 composeApp/src/commonMain/resources/compose-multiplatform.xml create mode 100644 composeApp/src/commonMain/resources/images/ic_chrome_close.png create mode 100644 composeApp/src/commonMain/resources/images/ic_chrome_maximize.png create mode 100644 composeApp/src/commonMain/resources/images/ic_chrome_minimize.png create mode 100644 composeApp/src/commonMain/resources/images/ic_chrome_unmaximize.png create mode 100644 composeApp/src/commonMain/resources/logo.png create mode 100644 composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/create_table.sq create mode 100644 composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/query.sq create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Bootstrap.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DataStorePreferences.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DesktopApp.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Platform.jvm.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/PopupNotification.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Card.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/EditMemo.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Heatmap.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Markdown.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/MemoList.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Resources.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/IFile.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/Instant.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/LinkBuilder.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/db/AppDatabase.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ext/AnnotatedStringExt.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/logger.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/main.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.desktop.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/app/AppScreen.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/HomeScreen.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ArchivedTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/DaysReviewTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ExploreTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/HomeTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/InboxTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ProfileTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ResourcesTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/SettingsTab.kt create mode 100644 composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreen.kt create mode 100644 composeApp/src/iosMain/kotlin/DataStorePreferences.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/Database.ios.kt create mode 100755 composeApp/src/iosMain/kotlin/MainViewController.kt create mode 100755 composeApp/src/iosMain/kotlin/Platform.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/PopupNotification.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/components/Heatmap.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/core/IFile.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/core/Instant.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/core/loader/MemoDao.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/di/CacheCookiesStorage.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/logger.ios.kt create mode 100644 composeApp/src/iosMain/kotlin/model/DailyUsageStat.ios.kt create mode 100755 gradle.properties create mode 100755 gradle/libs.versions.toml create mode 100755 gradle/wrapper/gradle-wrapper.jar create mode 100755 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100755 gradlew.bat create mode 100755 iosApp/Configuration/Config.xcconfig create mode 100755 iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100755 iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100755 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100755 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png create mode 100755 iosApp/iosApp/Assets.xcassets/Contents.json create mode 100755 iosApp/iosApp/ContentView.swift create mode 100755 iosApp/iosApp/Info.plist create mode 100755 iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100755 iosApp/iosApp/iOSApp.swift create mode 100644 launcher/logo.ico create mode 100644 launcher/logo.png create mode 100755 settings.gradle.kts diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..c8936f6 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,61 @@ +name: Release +on: + push: + tags: + - '*' + +jobs: + gradle: + + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + artifact_name: memosc_${{ github.ref_name }}_amd64.deb + asset_name: memosc_${{ github.ref_name }}_amd64.deb + type: Deb + dir: deb + - os: windows-latest + artifact_name: memosc-${{ github.ref_name }}.exe + asset_name: memosc-${{ github.ref_name }}.exe + type: Exe + dir: exe + - os: macos-latest + artifact_name: memosc-${{ github.ref_name }}.dmg + asset_name: memosc-${{ github.ref_name }}.dmg + type: Dmg + dir: dmg + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: oracle + java-version: 19 + cache: 'gradle' + - uses: ConorMacBride/install-package@v1 + with: + apt: fakeroot + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - uses: SebRollen/toml-action@v1.0.2 + id: read_toml + with: + file: 'gradle/libs.versions.toml' + field: 'versions.packageVersion' + + - name: Execute Gradle build + + run: ./gradlew packageRelease${{ matrix.type }} --no-daemon + - name: Upload binaries to release + uses: svenstaro/upload-release-action@v2 + env: + FILE_NAME: ${{steps.read_toml.outputs.value}} + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: composeApp/build/compose/binaries/main-release/${{ matrix.dir }}/${{ matrix.artifact_name }} + asset_name: ${{ matrix.asset_name }} +# tag: ${{ github.ref }} + overwrite: true +#composeApp/build/compose/binaries/main/dmg/memosc-1.0.0.dmg diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..a17bbf2 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,48 @@ +name: test +on: + push: + branches: + - '*' + +jobs: + gradle: + + strategy: + fail-fast: false + matrix: + include: + - os: ubuntu-latest + artifact_name: memosc_${{ github.ref_name }}_amd64.deb + asset_name: memosc_${{ github.ref_name }}_amd64.deb + type: Deb + dir: deb + - os: windows-latest + artifact_name: memosc-${{ github.ref_name }}.exe + asset_name: memosc-${{ github.ref_name }}.exe + type: Exe + dir: exe + - os: macos-latest + artifact_name: memosc-${{ github.ref_name }}.dmg + asset_name: memosc-${{ github.ref_name }}.dmg + type: Dmg + dir: dmg + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-java@v4 + with: + distribution: oracle + java-version: 19 + cache: 'gradle' + - uses: ConorMacBride/install-package@v1 + with: + apt: fakeroot + - name: Setup Gradle + uses: gradle/gradle-build-action@v2 + - uses: SebRollen/toml-action@v1.0.2 + id: read_toml + with: + file: 'gradle/libs.versions.toml' + field: 'versions.packageVersion' + - name: Execute Gradle build + run: ./gradlew packageRelease${{ matrix.type }} --no-daemon \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100755 index 0000000..769535c --- /dev/null +++ b/.gitignore @@ -0,0 +1,20 @@ +*.iml +.gradle +**/build/ +xcuserdata +!src/**/build/ +local.properties +.idea +.DS_Store +captures +.externalNativeBuild +.cxx +*.xcodeproj/* +!*.xcodeproj/project.pbxproj +!*.xcodeproj/xcshareddata/ +!*.xcodeproj/project.xcworkspace/ +!*.xcworkspace/contents.xcworkspacedata +**/xcshareddata/WorkspaceSettings.xcsettings +/composeApp/settings_preferences.preferences_pb +/.env +*.db \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b821351 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 springeye + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/README.md b/README.md new file mode 100755 index 0000000..ef5ec7b --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +This is a Kotlin Multiplatform project targeting Android, iOS, Desktop. + +* `/composeApp` is for code that will be shared across your Compose Multiplatform applications. + It contains several subfolders: + - `commonMain` is for code that’s common for all targets. + - Other folders are for Kotlin code that will be compiled for only the platform indicated in the folder name. + For example, if you want to use Apple’s CoreCrypto for the iOS part of your Kotlin app, + `iosMain` would be the right folder for such calls. + +* `/iosApp` contains iOS applications. Even if you’re sharing your UI with Compose Multiplatform, + you need this entry point for your iOS app. This is also where you should add SwiftUI code for your project. + + +Learn more about [Kotlin Multiplatform](https://www.jetbrains.com/help/kotlin-multiplatform-dev/get-started.html)… \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100755 index 0000000..52cf9fa --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,8 @@ +plugins { + // this is necessary to avoid the plugins to be loaded multiple times + // in each subproject's classloader + alias(libs.plugins.androidApplication) apply false + alias(libs.plugins.androidLibrary) apply false + alias(libs.plugins.jetbrainsCompose) apply false + alias(libs.plugins.kotlinMultiplatform) apply false +} \ No newline at end of file diff --git a/composeApp/build.gradle.kts b/composeApp/build.gradle.kts new file mode 100755 index 0000000..f0714e2 --- /dev/null +++ b/composeApp/build.gradle.kts @@ -0,0 +1,227 @@ +import org.jetbrains.compose.ExperimentalComposeLibrary +import org.jetbrains.compose.desktop.application.dsl.TargetFormat + +plugins { + alias(libs.plugins.kotlinMultiplatform) + alias(libs.plugins.androidApplication) + alias(libs.plugins.jetbrainsCompose) + kotlin("plugin.serialization") version "1.9.21" + id("com.google.devtools.ksp") version "1.9.21-1.0.16" + id("de.jensklingenberg.ktorfit") version "1.11.0" + id("app.cash.sqldelight") version "2.0.1" + +} +sqldelight { + databases { + create("AppDatabase") { + packageName.set("com.github.springeye.memosc.db.model") + } + } +} +val ktorfitVersion = "1.11.0" +val sqllinVersion = "1.2.3" +kotlin { + + androidTarget { + compilations.all { + kotlinOptions { + jvmTarget = "1.8" + } + } + } + + jvm("desktop") +// +// listOf( +// iosX64(), +// iosArm64(), +// iosSimulatorArm64() +// ).forEach { iosTarget -> +// iosTarget.binaries.framework { +// baseName = "ComposeApp" +// isStatic = true +// } +// } + + sourceSets { + val desktopMain by getting +// iosMain.dependencies { +//// implementation(libs.ktor.client.darwin) +// implementation("app.cash.paging:paging-runtime-uikit:3.3.0-alpha02-0.4.0") +// } + androidMain.dependencies { + implementation(libs.compose.ui.tooling.preview.android) + implementation(libs.androidx.activity.compose) + implementation(libs.ktor.client.okhttp) + implementation(libs.kotlinx.coroutines.android) + api(libs.androidx.startup) + implementation(libs.android.driver) + implementation(libs.koin.android) + // Jetpack WorkManager + implementation(libs.koin.androidx.workmanager) + // Navigation Graph + implementation(libs.koin.androidx.navigation) + implementation(libs.koin.androidx.compose) +// implementation(libs.voyager.bottomSheetNavigator) +// implementation(libs.voyager.tabNavigator) + } + desktopMain.dependencies { + implementation(compose.desktop.currentOs) + implementation(libs.kotlinx.coroutines.swing) + implementation(libs.ktor.client.cio) + implementation(libs.jetbrains.ui.tooling.preview.desktop) + implementation(libs.androidx.ui.graphics.desktop) + implementation(libs.sqlite.driver) + + } + + commonMain.dependencies { + implementation(compose.runtime) + implementation(compose.foundation) +// implementation(compose.material) + implementation(compose.material3) + implementation(compose.materialIconsExtended) + implementation(compose.ui) + @OptIn(ExperimentalComposeLibrary::class) + implementation(compose.components.resources) + implementation(libs.ktor.client.core) + implementation(libs.kotlinx.coroutines.core) + implementation(libs.kotlinx.serialization.json) + implementation(libs.okio) + implementation(libs.stately.common) + + // Multiplatform + + // Navigator + implementation(libs.voyager.navigator) + + // Screen Model + implementation(libs.voyager.screenModel) + + // BottomSheetNavigator + implementation(libs.voyager.bottomSheetNavigator) + + // TabNavigator + implementation(libs.voyager.tabNavigator) + + // Transitions + implementation(libs.voyager.transitions) + + implementation(libs.voyager.koin) + implementation(libs.koin.core) + implementation(libs.koin.compose) + + + implementation(libs.kotlin.stdlib) + implementation(libs.kotlinx.datetime) + implementation(libs.kotlinx.datetime) + + + implementation(libs.ktor.client.core) + implementation(libs.ktor.client.content.negotiation) + implementation(libs.ktor.serialization.kotlinx.json) + implementation(libs.ktor.client.logging) + implementation(libs.napier) + + + implementation(libs.napier) + + implementation(libs.androidx.datastore.preferences.core) +// api(libs.retrofit) +// api(libs.retrofit.converter.gson) +// implementation(libs.okhttp) +// api(libs.okhttp.interceptor.logging) + + implementation(libs.ktorfit.lib) + + implementation(libs.multiplatform.markdown.renderer) + + implementation(libs.kamel.image) + implementation(libs.mpfilepicker) + implementation(libs.highlights) + implementation(libs.napier.v271) + implementation(libs.paging.compose.common) + implementation(libs.androidx.paging3.extensions) + implementation("app.cash.sqldelight:coroutines-extensions:2.0.1") + } + + } +} + +android { + namespace = "com.github.springeye.memosc" + compileSdk = libs.versions.android.compileSdk.get().toInt() + + sourceSets["main"].manifest.srcFile("src/androidMain/AndroidManifest.xml") + sourceSets["main"].res.srcDirs("src/androidMain/res") + sourceSets["main"].resources.srcDirs("src/commonMain/resources") + + defaultConfig { + applicationId = "com.github.springeye.memosc" + minSdk = libs.versions.android.minSdk.get().toInt() + targetSdk = libs.versions.android.targetSdk.get().toInt() + versionCode = 1 + versionName = "1.0" + } + packaging { + resources { + excludes += "/META-INF/{AL2.0,LGPL2.1}" + } + } + buildTypes { + getByName("release") { + isMinifyEnabled = false + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + dependencies { + debugImplementation(libs.compose.ui.tooling) + + } +} + +compose.desktop { + application { + mainClass = "com.github.springeye.memosc.MainKt" + buildTypes.release { + proguard { + isEnabled=false + configurationFiles.from("rules.pro") + } + } + nativeDistributions { + targetFormats(TargetFormat.Dmg, TargetFormat.Msi,TargetFormat.Exe, TargetFormat.Deb) + packageName = "memosc" + packageVersion = libs.versions.packageVersion.get() + + linux{ + iconFile=rootProject.file("launcher/logo.png") + } + macOS{ + iconFile=rootProject.file("launcher/logo.png") + } + windows{ + shortcut=true + menuGroup=packageName + iconFile=rootProject.file("launcher/logo.png") + } + } + } +} +ksp{ +} +dependencies { + + add("kspCommonMainMetadata", "de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") +// add("kspIosX64","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") +// add("kspIosArm64","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") +// add("kspIosSimulatorArm64","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") + add("kspDesktop","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") + add("kspAndroid","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") +// add("kspLinuxArm64","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") +// add("kspLinuxX64","de.jensklingenberg.ktorfit:ktorfit-ksp:$ktorfitVersion") + +} \ No newline at end of file diff --git a/composeApp/rules.pro b/composeApp/rules.pro new file mode 100644 index 0000000..6151ca9 --- /dev/null +++ b/composeApp/rules.pro @@ -0,0 +1,13 @@ +-dontwarn +-keep class kotlinx.coroutines.** {*;} +-keep class org.jetbrains.skia.** {*;} +-keep class org.jetbrains.skiko.** {*;} +-keepclassmembernames class kotlinx.** { + volatile ; +} +-keep public class MainKt { + public void main(); +} + +-keep class api.** {*;} +-keep class model.** {*;} \ No newline at end of file diff --git a/composeApp/src/androidMain/AndroidManifest.xml b/composeApp/src/androidMain/AndroidManifest.xml new file mode 100755 index 0000000..7eb43d8 --- /dev/null +++ b/composeApp/src/androidMain/AndroidManifest.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/ContextProvider.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/ContextProvider.kt new file mode 100644 index 0000000..78fbdf8 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/ContextProvider.kt @@ -0,0 +1,18 @@ +package com.github.springeye.memosc + +import android.content.Context +import androidx.startup.Initializer + +internal lateinit var applicationContext: Context + private set + +data object ContextProviderInitializer + +class ContextProvider: Initializer { + override fun create(context: Context): ContextProviderInitializer { + applicationContext = context.applicationContext + return ContextProviderInitializer + } + + override fun dependencies(): List>> = emptyList() +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/DataStorePreferences.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/DataStorePreferences.android.kt new file mode 100644 index 0000000..32df58d --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/DataStorePreferences.android.kt @@ -0,0 +1,21 @@ +package com.github.springeye.memosc + +import androidx.datastore.core.DataMigration +import androidx.datastore.core.DataStore +import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import java.io.File + +actual fun dataStorePreferences( + corruptionHandler: ReplaceFileCorruptionHandler?, + coroutineScope: CoroutineScope, + migrations: List> +): DataStore = createDataStoreWithDefaults( + corruptionHandler = corruptionHandler, + migrations = migrations, + coroutineScope = coroutineScope, + path = { + File(applicationContext.filesDir, "datastore/$SETTINGS_PREFERENCES.preferences_pb").path + } +) \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MainActivity.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MainActivity.kt new file mode 100755 index 0000000..e146b40 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MainActivity.kt @@ -0,0 +1,85 @@ +package com.github.springeye.memosc + + +import android.os.Bundle +import androidx.activity.ComponentActivity +import androidx.activity.compose.setContent +import androidx.compose.foundation.layout.RowScope +import androidx.compose.material.BottomNavigation +import androidx.compose.material.BottomNavigationItem +import androidx.compose.material.Icon +import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.tooling.preview.Preview +import cafe.adriel.voyager.navigator.tab.CurrentTab +import cafe.adriel.voyager.navigator.tab.LocalTabNavigator +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabNavigator +import com.github.springeye.memosc.core.Base64ImageFetcher +import com.github.springeye.memosc.di.appModule +import com.github.springeye.memosc.di.homeModule +import io.kamel.core.config.KamelConfig +import io.kamel.core.config.httpFetcher +import io.kamel.core.config.takeFrom +import io.kamel.image.config.Default +import io.kamel.image.config.LocalKamelConfig +import io.ktor.client.plugins.cookies.HttpCookies +import org.koin.compose.KoinApplication +import org.koin.compose.getKoin +import com.github.springeye.memosc.tab.DaysReviewTab +import com.github.springeye.memosc.tab.ExploreTab +import com.github.springeye.memosc.tab.HomeTab +class MainActivity : ComponentActivity() { + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + setContent { + KoinApplication(application = { + modules(appModule, homeModule) + }) { + val koin= getKoin() + val kamelConfig= KamelConfig { + takeFrom(KamelConfig.Default) + fetcher(Base64ImageFetcher()) + httpFetcher { + install(HttpCookies){ + this.storage=koin.get() + } + } + } + CompositionLocalProvider(LocalKamelConfig provides kamelConfig) { + TabNavigator(HomeTab) { + Scaffold( + content = {padding-> + println(padding) + CurrentTab() + }, + bottomBar = { + BottomNavigation { + TabNavigationItem(HomeTab) + TabNavigationItem(ExploreTab) + TabNavigationItem(DaysReviewTab) + } + } + ) + } + } + } + } + } +} +@Composable +private fun RowScope.TabNavigationItem(tab: Tab) { + val tabNavigator = LocalTabNavigator.current + + BottomNavigationItem( + selected = tabNavigator.current == tab, + onClick = { tabNavigator.current = tab }, + icon = { Icon(painter = tab.options.icon!!, contentDescription = tab.options.title) } + ) +} +@Preview +@Composable +fun AppAndroidPreview() { +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MemoscApplication.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MemoscApplication.kt new file mode 100644 index 0000000..e20cef5 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/MemoscApplication.kt @@ -0,0 +1,17 @@ +package com.github.springeye.memosc + +import android.app.Application + +class MemoscApplication: Application() { + override fun onCreate() { + super.onCreate() +// startKoin { +// // Log Koin into Android logger +// androidLogger() +// // Reference Android context +// androidContext(this@MemoscApplication) +// // Load modules +// modules(appModule, homeModule) +// } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/Platform.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/Platform.android.kt new file mode 100644 index 0000000..14d62e6 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/Platform.android.kt @@ -0,0 +1,10 @@ +package com.github.springeye.memosc + +import Platform +import android.os.Build + +class AndroidPlatform : Platform { + override val name: String = "Android ${Build.VERSION.SDK_INT}" +} + +actual fun getPlatform(): Platform = AndroidPlatform() \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/PopupNotification.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/PopupNotification.android.kt new file mode 100644 index 0000000..729aecc --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/PopupNotification.android.kt @@ -0,0 +1,11 @@ +package com.github.springeye.memosc + +import PopupNotification +import ToastState +import androidx.compose.runtime.MutableState + + + +actual fun createPopupNotification(state: MutableState): PopupNotification { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Card.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Card.kt new file mode 100644 index 0000000..cc7a6ba --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Card.kt @@ -0,0 +1,57 @@ +package com.github.springeye.memosc.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.ExperimentalComposeUiApi +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp + +@Preview +@Composable +fun PreviewCard() { + Scaffold { + Column(modifier = Modifier.padding(it).fillMaxSize().background(Color.Gray)) { + CardItem(radius = 10.dp, + hoverColor = Color.Blue, + + ) { + Text("asdfasfas") + } + } + } +} +@OptIn(ExperimentalComposeUiApi::class) +@Composable +fun CardItem( + radius: Dp =15.dp, + color: Color=Color.White, + hoverColor: Color=color, + borderColor: Color=color, + hoverBorderColor: Color=color, + borderWidth:Dp=0.dp, + paddingValues: PaddingValues= PaddingValues(10.dp), + modifier:Modifier = Modifier, content: @Composable() (BoxScope.() -> Unit)) { + + var background = Modifier.clip(RoundedCornerShape(radius)) + .background(color) + + Box(modifier = background + .then(modifier) + .border(borderWidth,borderColor,shape = RoundedCornerShape(radius)) + .padding(paddingValues), + content = content) +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Heatmap.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Heatmap.android.kt new file mode 100644 index 0000000..6c4fa17 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/Heatmap.android.kt @@ -0,0 +1,33 @@ +package com.github.springeye.memosc.components + +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.ui.unit.Constraints +import java.time.LocalDate +import java.time.temporal.ChronoUnit +import java.time.temporal.TemporalAdjusters +import java.time.temporal.WeekFields +import java.util.Locale +import kotlin.math.ceil + +actual fun countHeatmap(constraints: Constraints): Int { + val cellSize = ceil(constraints.maxHeight.toDouble() / 7).toInt() + if (cellSize <= 0) { + return 0 + } + val columns = constraints.maxWidth / cellSize + val fullCells = columns * 7 + + val firstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek + val firstDayOfThisWeek = LocalDate.now().with(TemporalAdjusters.previousOrSame(firstDayOfWeek)) + val lastColumn = ChronoUnit.DAYS.between(firstDayOfThisWeek, LocalDate.now()).toInt() + 1 + if (lastColumn % 7 == 0) { + return fullCells + } + return fullCells - 7 + lastColumn +} + +@Composable +actual fun aa() { + Text("我是andriod") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.android.kt new file mode 100644 index 0000000..53ab670 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.android.kt @@ -0,0 +1,8 @@ +package com.github.springeye.memosc.components + +import androidx.compose.runtime.Composable +import com.github.springeye.memosc.model.DailyUsageStat + +@Composable +actual fun HeatmapStat2(day: DailyUsageStat) { +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/IFile.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/IFile.android.kt new file mode 100644 index 0000000..ad0a55e --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/IFile.android.kt @@ -0,0 +1,8 @@ +package com.github.springeye.memosc.core + +import core.IFile + + +actual fun createIFile(path: String): IFile { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant.android.kt new file mode 100644 index 0000000..eeda1fe --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant.android.kt @@ -0,0 +1,22 @@ +import kotlinx.datetime.Instant +import java.text.SimpleDateFormat +import java.util.Date + +actual fun Instant.formatDate( + pattern: String, + defValue: String +): String { + return try { + SimpleDateFormat(pattern).format(Date(this.toEpochMilliseconds())) + } catch (e: Exception) { + defValue + } +} + +actual fun String.parseDate(pattern: String, defValue: Long): Long { + return try { + SimpleDateFormat(pattern).parse(this).time + } catch (e: Exception) { + defValue + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant1.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant1.android.kt new file mode 100644 index 0000000..7432096 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/core/Instant1.android.kt @@ -0,0 +1,14 @@ +package com.github.springeye.memosc.core + +import kotlinx.datetime.Instant + +actual fun Instant.formatDate( + pattern: String, + defValue: String +): String { + TODO("Not yet implemented") +} + +actual fun String.parseDate(pattern: String, defValue: Long): Long { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/AppDatabase.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/AppDatabase.android.kt new file mode 100644 index 0000000..c799c5f --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/AppDatabase.android.kt @@ -0,0 +1,7 @@ +package com.github.springeye.memosc.db + +import com.github.springeye.memosc.db.model.AppDatabase + +actual fun createAppDatabase(): AppDatabase { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/MemoDao.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/MemoDao.android.kt new file mode 100644 index 0000000..6ccc150 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/MemoDao.android.kt @@ -0,0 +1,53 @@ +package com.github.springeye.memosc.db + +import com.github.springeye.memosc.db.model.AppDatabase +import com.github.springeye.memosc.db.model.MemoQueries +import com.github.springeye.memosc.model.Memo + +actual fun createMemoDao(database: AppDatabase): MemoDao { + return AndroidMemoDao(database) +} +internal class AndroidMemoDao(private val database: AppDatabase): MemoDao { + + override suspend fun findAll(): List { + return database.memoQueries.selectAll().executeAsList().map { + Memo.fromDbMemo(it) + } + } + + override suspend fun clear() { + TODO("Not yet implemented") + } + + override suspend fun findById(memoId: Long): Memo? { + TODO("Not yet implemented") + } + + override suspend fun insertAll(vararg memos: Memo, clear: Boolean) { + database.memoQueries.transaction { + if(clear){ + database.memoQueries.deleteAll() + } + for (memo in memos) { + val item= with(memo) { + com.github.springeye.memosc.db.model.Memo( + id, + createdTs, + creatorId, + creatorName, + content, + pinned, + rowStatus, + visibility, + updatedTs + ) + } + database.memoQueries.insertMemo(item) + } + } + } + + override fun memoQueries(): MemoQueries { + return database.memoQueries + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/RemoteKeyDao.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/RemoteKeyDao.android.kt new file mode 100644 index 0000000..1c9cabc --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/db/RemoteKeyDao.android.kt @@ -0,0 +1,5 @@ +package com.github.springeye.memosc.db + +actual fun createRemoteKeyDao(): RemoteKeyDao { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.android.kt new file mode 100644 index 0000000..5bebc21 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.android.kt @@ -0,0 +1,60 @@ +package com.github.springeye.memosc.di + +import AppPreferences +import di.fillDefaults +import di.fromString +import di.matches +import di.toSaveString +import io.ktor.client.plugins.cookies.CookiesStorage +import io.ktor.http.Cookie +import io.ktor.http.Url +import io.ktor.util.date.getTimeMillis +import kotlinx.serialization.encodeToString +import kotlinx.serialization.json.Json +import java.util.concurrent.atomic.AtomicLong +import kotlin.math.min + + +actual class PersistentCookiesStorage actual constructor(val store: AppPreferences) : + CookiesStorage { + override suspend fun addCookie(requestUrl: Url, cookie: Cookie) { + val items = getCookiesFromStorage() + items.removeAll { it.name == cookie.name && it.matches(requestUrl) } + items.add(cookie.fillDefaults(requestUrl)) + store.setString("cookies", Json.encodeToString(items.map { it.toSaveString() }.toList())); + } + + private suspend fun getCookiesFromStorage(): MutableList { + val old = store.getString("cookies") ?: "[]" + val items = Json.decodeFromString>(old) + val cookies = mutableListOf() + return cookies.apply { + addAll(items.map { fromString(it) }) + }; + } + private val oldestCookie: AtomicLong = AtomicLong(0L) + override suspend fun get(requestUrl: Url): List { + val now = getTimeMillis() + if (now >= oldestCookie.get()) cleanup(now) + return getCookiesFromStorage().filter { + it.matches(requestUrl) + } + } + private suspend fun cleanup(timestamp: Long) { + val cookies = getCookiesFromStorage() + cookies.removeAll { cookie -> + val expires = cookie.expires?.timestamp ?: return@removeAll false + expires < timestamp + } + + val newOldest = cookies.fold(Long.MAX_VALUE) { acc, cookie -> + cookie.expires?.timestamp?.let { min(acc, it) } ?: acc + } + + oldestCookie.set(newOldest) + store.setString("cookies", Json.encodeToString(cookies.map { it.toSaveString() }.toList())); + } + override fun close() { + } +} + diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/logger.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/logger.android.kt new file mode 100644 index 0000000..aa9db7d --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/logger.android.kt @@ -0,0 +1,8 @@ +package com.github.springeye.memosc + +import io.github.aakira.napier.DebugAntilog +import io.github.aakira.napier.Napier + +actual fun initLogger() { + Napier.base(DebugAntilog()) +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.android.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.android.kt new file mode 100644 index 0000000..bee1286 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.android.kt @@ -0,0 +1,27 @@ +package com.github.springeye.memosc.model + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.toKotlinLocalDate +import java.time.DayOfWeek +import java.time.format.TextStyle +import java.time.temporal.WeekFields +import java.util.Locale + +actual fun initialMatrix(): List { + val now = java.time.LocalDate.now() + return (1..now.lengthOfYear()).map { day -> + DailyUsageStat(date = now.minusDays(day - 1L).toKotlinLocalDate()) + }.reversed() +} + +actual val weekDays: List + get(){ + val day = WeekFields.of(Locale.getDefault()).firstDayOfWeek + return DayOfWeek.values().mapIndexed { index, _ -> + day.plus(index.toLong()).getDisplayName(TextStyle.SHORT, Locale.getDefault()) + } + } + +actual fun calculateMatrix(memos: Map>): List { + TODO("Not yet implemented") +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ArchivedTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ArchivedTab.kt new file mode 100644 index 0000000..780222d --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ArchivedTab.kt @@ -0,0 +1,33 @@ +package com.github.springeye.memosc.tab + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Archive +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions + +object ArchivedTab: Tab { + @Composable + override fun Content() { + Text("home2") + } + + override val options: TabOptions + @Composable + get() { + val title = "已归档" + val icon = rememberVectorPainter(Icons.Outlined.Archive) + + return remember { + TabOptions( + index = 5u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/DaysReviewTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/DaysReviewTab.kt new file mode 100644 index 0000000..d2a50eb --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/DaysReviewTab.kt @@ -0,0 +1,35 @@ +package com.github.springeye.memosc.tab + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.CalendarToday +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions +import org.jetbrains.compose.resources.ExperimentalResourceApi + +@OptIn(ExperimentalResourceApi::class) +object DaysReviewTab: Tab { + @Composable + override fun Content() { + Text("home2") + } + + override val options: TabOptions + @Composable + get() { + val title = "每次回顾" + val icon = rememberVectorPainter(Icons.Outlined.CalendarToday) + + return remember { + TabOptions( + index = 1u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ExploreTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ExploreTab.kt new file mode 100644 index 0000000..637a8bf --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ExploreTab.kt @@ -0,0 +1,63 @@ +package com.github.springeye.memosc.tab + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Add +import androidx.compose.material.icons.filled.Home +import androidx.compose.material.icons.outlined.Explore +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import app.cash.paging.LoadStateLoading +import app.cash.paging.compose.collectAsLazyPagingItems +import cafe.adriel.voyager.koin.getScreenModel +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions +import ui.home.MemoModel + +object ExploreTab:Tab{ + @OptIn(ExperimentalMaterialApi::class) + @Composable + override fun Content() { + val model = getScreenModel() + val lazyPagingItems = model.pager.flow.collectAsLazyPagingItems() + val refreshing = lazyPagingItems.loadState.refresh == LoadStateLoading + val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = { + lazyPagingItems.refresh() + }) + Box(modifier = Modifier.fillMaxWidth().fillMaxHeight() + .background(Color.Red) + .pullRefresh(pullRefreshState)) { + Text("探索") + PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter)) + } + + } + + override val options: TabOptions + @Composable + get() { + val title = "探索" + val icon = rememberVectorPainter(Icons.Outlined.Explore) + + return remember { + TabOptions( + index = 3u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/HomeTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/HomeTab.kt new file mode 100644 index 0000000..84b8ecf --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/HomeTab.kt @@ -0,0 +1,646 @@ +package com.github.springeye.memosc.tab + +import AppModel +import androidx.compose.animation.AnimatedContent +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.horizontalScroll +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.wrapContentHeight +import androidx.compose.foundation.layout.wrapContentWidth +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Divider +import androidx.compose.material.DropdownMenu +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.Icon +import androidx.compose.material.Surface +import androidx.compose.material.Text +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Attachment +import androidx.compose.material.icons.filled.Close +import androidx.compose.material.icons.filled.ExpandLess +import androidx.compose.material.icons.filled.ExpandMore +import androidx.compose.material.icons.filled.Lock +import androidx.compose.material.icons.filled.MoreVert +import androidx.compose.material.icons.filled.Photo +import androidx.compose.material.icons.filled.Send +import androidx.compose.material.icons.filled.Tag +import androidx.compose.material.icons.outlined.Attachment +import androidx.compose.material.icons.outlined.Home +import androidx.compose.material.pullrefresh.PullRefreshIndicator +import androidx.compose.material.pullrefresh.pullRefresh +import androidx.compose.material.pullrefresh.rememberPullRefreshState +import androidx.compose.material3.AssistChip +import androidx.compose.material3.AssistChipDefaults +import androidx.compose.material3.Button +import androidx.compose.material3.DropdownMenuItem +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.input.pointer.PointerIcon +import androidx.compose.ui.input.pointer.pointerHoverIcon +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalUriHandler +import androidx.compose.ui.text.AnnotatedString +import androidx.compose.ui.text.SpanStyle +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.buildAnnotatedString +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import app.cash.paging.LoadStateLoading +import app.cash.paging.compose.collectAsLazyPagingItems +import cafe.adriel.voyager.koin.getScreenModel +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions +import com.darkrockstudios.libraries.mpfilepicker.FilePicker +import com.mikepenz.markdown.compose.LocalMarkdownColors +import com.mikepenz.markdown.compose.LocalMarkdownTypography +import com.mikepenz.markdown.compose.Markdown +import com.mikepenz.markdown.compose.components.MarkdownComponent +import com.mikepenz.markdown.compose.components.markdownComponents +import com.mikepenz.markdown.model.markdownColor +import com.wakaztahir.codeeditor.prettify.PrettifyParser +import com.wakaztahir.codeeditor.theme.CodeThemeType +import com.wakaztahir.codeeditor.utils.parseCodeAsAnnotatedString +import com.github.springeye.memosc.components.CardItem +import components.ITextField +import dev.snipme.highlights.Highlights +import dev.snipme.highlights.model.BoldHighlight +import dev.snipme.highlights.model.ColorHighlight +import dev.snipme.highlights.model.SyntaxLanguage +import dev.snipme.highlights.model.SyntaxThemes +import io.kamel.image.KamelImage +import io.kamel.image.asyncPainterResource +import kotlinx.coroutines.launch +import kotlinx.datetime.Instant +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toJavaLocalDateTime +import kotlinx.datetime.toLocalDateTime +import model.Memo +import model.Resource +import org.intellij.markdown.ast.ASTNode +import ui.home.MemoModel +import java.time.format.DateTimeFormatter +import kotlin.math.ceil +import kotlin.math.min + +object HomeTab : Tab { + @OptIn(ExperimentalMaterialApi::class) + @Composable + override fun Content() { + + val model = getScreenModel() + val lazyPagingItems = model.pager.flow.collectAsLazyPagingItems() + val memos = model.memos + val refreshing = lazyPagingItems.loadState.refresh == LoadStateLoading + val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = { + lazyPagingItems.refresh() + }) + Column(modifier = Modifier.fillMaxWidth().pullRefresh(pullRefreshState)) { + PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.CenterHorizontally)) + Box(Modifier.padding(top = 20.dp)) { + EditMemo() + } + LazyColumn(modifier = Modifier.fillMaxWidth()) { + items(lazyPagingItems.itemCount) { index -> + val it = lazyPagingItems[index]!! + Box(Modifier.padding(top = 10.dp)) { + CardItem( + radius = 10.dp, + modifier = Modifier.fillMaxWidth(), + borderColor = Color.White, + borderWidth = 1.dp, + hoverBorderColor = Color(0xccCCCCCC), + ) { + Column { + val dateTime = + Instant.fromEpochSeconds(it.createdTs) + .toLocalDateTime( + TimeZone.currentSystemDefault() + ).toJavaLocalDateTime() + .format(DateTimeFormatter.ofPattern("YYYY-MM-DD HH:mm:ss")) + Row(modifier = Modifier) { + Text( + dateTime, + style = TextStyle.Default.copy( + color = Color( + 156, + 163, + 175 + ) + ) + ) + Spacer(Modifier.weight(1F)) + ItemEditMenu(it) + } + + val components = markdownComponents( + codeBlock = codeBlockComponent, + codeFence = codeFenceBlockComponent + ) + + Markdown( + it.content, modifier = Modifier.fillMaxWidth(), + components = components, + colors = markdownColor(codeText = Color.Black), + // typography = markdownTypography(code = MaterialTheme.typography.body2.copy(fontFamily = FontFamily.Monospace, color = Color.Black)) + ) + MemoResourceContent(memo = it); + } + } + } + } + } + + } + + + } + + private val codeBlockComponent: MarkdownComponent = { + MarkdownCodeBlock(it.content, it.node) + } + private val codeFenceBlockComponent: MarkdownComponent = { + MarkdownCodeFenceBlock(it.content, it.node) + } + + @Composable + internal fun MarkdownCodeFenceBlock( + content: String, + node: ASTNode + ) { + // CODE_FENCE_START, FENCE_LANG, {content}, CODE_FENCE_END + if (node.children.size >= 3) { + + val start = node.children[2].startOffset + val end = node.children[node.children.size - 2].endOffset + val langStart = node.children[1].startOffset + val langEnd = node.children[1].endOffset + val lang = content.substring(langStart, langEnd) + MarkdownCode(content.subSequence(start, end).toString().replaceIndent(), lang) + } else { + // invalid code block, skipping + } + } + + @Composable + internal fun MarkdownCodeBlock( + content: String, + node: ASTNode + ) { + val start = node.children[0].startOffset + val end = node.children[node.children.size - 1].endOffset + MarkdownCode(content.subSequence(start, end).toString().replaceIndent(), "txt") + } + + @Composable + private fun MarkdownCode( + code: String, + lang: String, + style: TextStyle = LocalMarkdownTypography.current.code + ) { + val color = LocalMarkdownColors.current.codeText + var language = lang + language = language.replace("^golang$".toRegex(), "go") +// language=language.replace("^java$".toRegex(),"kotlin") + val backgroundCodeColor = LocalMarkdownColors.current.codeBackground + Surface( + color = backgroundCodeColor, + shape = RoundedCornerShape(8.dp), + modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp) + ) { + + val syn = SyntaxLanguage.getByName(language) + if (syn != null) { + val highlights = Highlights.Builder() + .code(code) + .theme(SyntaxThemes.darcula()) + .language(syn) + .build() + .getHighlights() + Text( + text = buildAnnotatedString { + append(code) + highlights + .filterIsInstance() + .forEach { + addStyle( + SpanStyle(color = Color(it.rgb).copy(alpha = 1f)), + start = it.location.start, + end = it.location.end, + ) + } + + highlights + .filterIsInstance() + .forEach { + addStyle( + SpanStyle(fontWeight = FontWeight.Bold), + start = it.location.start, + end = it.location.end, + ) + } + }, + color = color, + modifier = Modifier.horizontalScroll(rememberScrollState()).padding(8.dp), + style = style + ) + } else { + + val parser = remember { PrettifyParser() } + var themeState by remember { mutableStateOf(CodeThemeType.Monokai) } + val theme = remember(themeState) { themeState.theme } + val parsedCode: AnnotatedString = remember { + if (!parser.isSupport(language)) { + AnnotatedString(code) + } else { + parseCodeAsAnnotatedString( + parser = parser, + theme = theme, + lang = language, + code = code + ) + } + } + + Text( + parsedCode, + color = color, + modifier = Modifier.horizontalScroll(rememberScrollState()).padding(8.dp), + style = style + ) + } + + } + } + + override val options: TabOptions + @Composable + get() { + val title = "主页" + val icon = rememberVectorPainter(Icons.Outlined.Home) + + return remember { + TabOptions( + index = 0u, + title = title, + icon = icon + ) + } + } + + @OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class) + @Preview + @Composable + fun EditMemo() { + val model = getScreenModel() + val settings by getScreenModel().state.collectAsState() + CardItem( + radius = 10.dp, + borderColor = Color(0xccCCCCCC), + borderWidth = 1.dp, + paddingValues = PaddingValues(start = 10.dp, end = 10.dp, bottom = 10.dp), + hoverBorderColor = Color(0xccCCCCCC), + ) { + Column(Modifier.fillMaxWidth().wrapContentHeight()) { + ITextField( + model.content, + onValueChange = { + model.content = it + }, + minLines = 2, + maxLines = 10, + placeholder = { + Text( + "任何想法", + style = TextStyle.Default.copy(color = Color(156, 163, 175)) + ) + }, + modifier = Modifier + .fillMaxWidth(), // Here I have decreased the height + shape = RoundedCornerShape(0.dp), + colors = TextFieldDefaults.colors( + focusedContainerColor = Color.White, + unfocusedContainerColor = Color.White, + disabledContainerColor = Color.White, + disabledIndicatorColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + + ), + + ) + var showFilePicker by remember { mutableStateOf(false) } + Row { + Icon(Icons.Default.Tag, "") + Icon(Icons.Default.Photo, "", Modifier.padding(start = 10.dp).clickable { + showFilePicker = true + }) + Icon(Icons.Default.Attachment, "", Modifier.padding(start = 10.dp)) + } + Row { + + + val fileType = listOf("jpg", "png") + FilePicker(show = showFilePicker, fileExtensions = fileType) { file -> + showFilePicker = false +// println(file?.path) + file?.path?.let { model.upload(it) } + // do something with the file + } + } + Divider(Modifier.padding(top = 10.dp, bottom = 10.dp)) + + FlowRow() { + model.resources.forEach { item -> + AnimatedContent(targetState = item) {s-> + ResourceItem(s, settings.host) + } + } + } + Row(Modifier.padding(vertical = 10.dp)) { + VisibilityButton() + Spacer(Modifier.weight(1f)) + SubmitButton(model.content.isNotEmpty()) + } + + } + } + } + + @Composable + fun ResourceItem(item: Resource, host: String) { + val model = getScreenModel() + + val scope = rememberCoroutineScope() + Row( + modifier = Modifier + .wrapContentWidth() + .padding(end = 5.dp) + .background(MaterialTheme.colorScheme.background), + verticalAlignment = Alignment.CenterVertically + ) { + KamelImage( + asyncPainterResource(item.uri(host)), + "", + Modifier.size(20.dp) + + ) + Text( + item.filename, + style = MaterialTheme.typography.bodySmall, + modifier = Modifier.padding(start = 5.dp) + ) + Icon(Icons.Default.Close, "", modifier = Modifier.clickable { + scope.launch { + model.removeResource(item) + + } + + }) + } + } + + @Composable + fun SubmitButton(enable: Boolean) { + val model = getScreenModel() + Button( + enabled = enable, + onClick = { + model.submit() + }, + contentPadding = PaddingValues(horizontal = 10.dp), + shape = RoundedCornerShape(5.dp), + modifier = Modifier + .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp) + .height(30.dp) + .pointerHoverIcon(PointerIcon.Hand) + ) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text("保存") + Icon( + Icons.Filled.Send, "", + tint = Color.White, + modifier = Modifier + .size(20.dp) + .padding(start = 5.dp) + ) + } + } + } + + @Composable + fun MemoResourceIcons(memo: Memo) { + val settings by getScreenModel().state.collectAsState() + memo.resourceList?.let { resourceList -> + val cols = min(3, resourceList.size) + val imageList = resourceList.filter { it.type.startsWith("image/") } + if (imageList.isNotEmpty()) { + val rows = ceil(imageList.size.toFloat() / cols).toInt() + for (rowIndex in 0 until rows) { + Row { + for (colIndex in 0 until cols) { + val index = rowIndex * cols + colIndex + if (index < imageList.size) { + Box(modifier = Modifier.fillMaxWidth(1f / (cols - colIndex))) { + val uri = imageList[index].uri(settings.host) + KamelImage( + asyncPainterResource(uri), + onLoading = { + Text("加载中") + }, + onFailure = { + it.printStackTrace() + Text("加载失败") + }, + contentDescription = null, + contentScale = ContentScale.FillWidth, + modifier = Modifier + .padding(2.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + ) + } + } else { + Spacer(modifier = Modifier.fillMaxWidth(1f / cols)) + } + } + } + } + } + resourceList.filterNot { it.type.startsWith("image/") }.forEach { resource -> + Attachment(resource) + } + } + } + + @Composable + fun MemoResourceContent(memo: Memo) { + val settings by getScreenModel().state.collectAsState() + memo.resourceList?.let { resourceList -> + val cols = min(3, resourceList.size) + val imageList = resourceList.filter { it.type.startsWith("image/") } + if (imageList.isNotEmpty()) { + val rows = ceil(imageList.size.toFloat() / cols).toInt() + for (rowIndex in 0 until rows) { + Row { + for (colIndex in 0 until cols) { + val index = rowIndex * cols + colIndex + if (index < imageList.size) { + Box(modifier = Modifier.fillMaxWidth(1f / (cols - colIndex))) { + val uri = imageList[index].uri(settings.host) + KamelImage( + asyncPainterResource(uri), + onLoading = { + Text("加载中") + }, + onFailure = { + it.printStackTrace() + Text("加载失败") + }, + contentDescription = null, + contentScale = ContentScale.FillWidth, + modifier = Modifier + .padding(2.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)) + ) + } + } else { + Spacer(modifier = Modifier.fillMaxWidth(1f / cols)) + } + } + } + } + } + resourceList.filterNot { it.type.startsWith("image/") }.forEach { resource -> + Attachment(resource) + } + } + } + + @Composable + fun Attachment( + resource: Resource + ) { + val uriHandler = LocalUriHandler.current + val settings by getScreenModel().state.collectAsState() + + AssistChip( + modifier = Modifier.padding(bottom = 10.dp), + onClick = { + uriHandler.openUri(resource.uri(settings.host)) + }, + label = { Text(resource.filename) }, + leadingIcon = { + androidx.compose.material3.Icon( + Icons.Outlined.Attachment, + contentDescription = "附件", + Modifier.size(AssistChipDefaults.IconSize) + ) + } + ) + } + + @Composable + fun VisibilityButton() { + var expanded by remember { mutableStateOf(false) } + Box(Modifier.clip(RoundedCornerShape(5.dp)) + .background(Color(0x99eeeeee)) + .clickable { + expanded = !expanded + }.padding(vertical = 5.dp, horizontal = 10.dp) + ) { + Row( + modifier = Modifier.wrapContentWidth() + ) { + val tint = Color(156, 163, 175) + Icon( + imageVector = Icons.Default.Lock, + contentDescription = "More", + modifier = Modifier.size(20.dp), + tint = tint + ) + Text( + "私有", + style = TextStyle.Default.copy(color = tint), + modifier = Modifier.padding(horizontal = 5.dp) + ) + Icon( + imageVector = if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore, + contentDescription = "More", + modifier = Modifier.size(20.dp), + tint = tint + + + ) + } + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem( + text = { Text("Load") }, + onClick = { } + ) + DropdownMenuItem( + text = { Text("Save") }, + onClick = { } + ) + } + } + } + + @Composable + fun ItemEditMenu(memo: Memo) { + var expanded by remember { mutableStateOf(false) } + val model = getScreenModel() + Box() { + Icon(Icons.Default.MoreVert, "More", Modifier.clickable { + expanded = !expanded + }) + DropdownMenu( + expanded = expanded, + onDismissRequest = { expanded = false } + ) { + DropdownMenuItem( + text = { Text("Edit") }, + onClick = { + model.setEdit(memo) + expanded = false + } + ) + DropdownMenuItem( + text = { Text("Save") }, + onClick = { } + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/InboxTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/InboxTab.kt new file mode 100644 index 0000000..a532b4a --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/InboxTab.kt @@ -0,0 +1,33 @@ +package com.github.springeye.memosc.tab +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Archive +import androidx.compose.material.icons.outlined.Inbox +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions + +object InboxTab:Tab{ + @Composable + override fun Content() { + Text("home2") + } + + override val options: TabOptions + @Composable + get() { + val title = "通知" + val icon = rememberVectorPainter(Icons.Outlined.Inbox) + + return remember { + TabOptions( + index = 4u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ResourcesTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ResourcesTab.kt new file mode 100644 index 0000000..95adcfa --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/ResourcesTab.kt @@ -0,0 +1,87 @@ +package com.github.springeye.memosc.tab +import AppModel +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.ExperimentalLayoutApi +import androidx.compose.foundation.layout.FlowRow +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.PhotoLibrary +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.unit.dp +import cafe.adriel.voyager.koin.getScreenModel +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions +import com.github.springeye.memosc.components.CardItem +import formatDate +import io.kamel.image.KamelImage +import io.kamel.image.asyncPainterResource +import org.jetbrains.compose.resources.ExperimentalResourceApi +import ui.home.MemoModel + +object ResourcesTab : Tab { + @OptIn(ExperimentalLayoutApi::class) + @Composable + override fun Content() { + val model = getScreenModel() + val settings = getScreenModel().state.value + val group by model.resourcesGroup.collectAsState(mapOf()) + CardItem(modifier = Modifier.fillMaxSize()) { + Column { + for (entry in group) { + Row { + Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(horizontal = 15.dp).padding(top = 10.dp)) { + Text("${entry.key.formatDate("yyyy")}", style = MaterialTheme.typography.bodySmall) + Text("${entry.key.formatDate("MM")}", style = MaterialTheme.typography.titleMedium) + } + + FlowRow { + for (resource in entry.value) { + KamelImage( + asyncPainterResource(resource.uri(settings.host)), + "", + modifier = Modifier.size(100.dp) + .padding(5.dp) + .fillMaxWidth() + .clip(RoundedCornerShape(4.dp)), + contentScale = ContentScale.FillWidth, + + ) + } + } + } + } + } + } + } + + @OptIn(ExperimentalResourceApi::class) + override val options: TabOptions + @Composable + get() { + val title = "资源库" + val icon = rememberVectorPainter(Icons.Outlined.PhotoLibrary); + return remember { + TabOptions( + index = 2u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/SettingsTab.kt b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/SettingsTab.kt new file mode 100644 index 0000000..7d26009 --- /dev/null +++ b/composeApp/src/androidMain/kotlin/com/github/springeye/memosc/tab/SettingsTab.kt @@ -0,0 +1,33 @@ +package com.github.springeye.memosc.tab +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Archive +import androidx.compose.material.icons.outlined.Settings +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.graphics.vector.rememberVectorPainter +import cafe.adriel.voyager.navigator.tab.Tab +import cafe.adriel.voyager.navigator.tab.TabOptions + +object SettingsTab:Tab{ + @Composable + override fun Content() { + Text("home2") + } + + override val options: TabOptions + @Composable + get() { + val title = "设置" + val icon = rememberVectorPainter(Icons.Outlined.Settings) + + return remember { + TabOptions( + index = 6u, + title = title, + icon = icon + ) + } + } + +} \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml b/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml new file mode 100755 index 0000000..2b068d1 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml b/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml new file mode 100755 index 0000000..07d5da9 --- /dev/null +++ b/composeApp/src/androidMain/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100755 index 0000000..eca70cf --- /dev/null +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100755 index 0000000..eca70cf --- /dev/null +++ b/composeApp/src/androidMain/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher.png new file mode 100755 index 0000000000000000000000000000000000000000..a571e60098c92c2baca8a5df62f2929cbff01b52 GIT binary patch literal 3593 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 P00000NkvXXu0mjfP=x?Y literal 0 HcmV?d00001 diff --git a/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-hdpi/ic_launcher_round.png new file mode 100755 index 0000000000000000000000000000000000000000..61da551c5594a1f9d26193983d2cd69189014603 GIT binary patch literal 5339 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png b/composeApp/src/androidMain/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100755 index 0000000000000000000000000000000000000000..b216f2d313cc673d8b8c4da591c174ebed52795c GIT binary patch literal 11873 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 literal 0 HcmV?d00001 diff --git a/composeApp/src/androidMain/res/values/strings.xml b/composeApp/src/androidMain/res/values/strings.xml new file mode 100755 index 0000000..1dd08c3 --- /dev/null +++ b/composeApp/src/androidMain/res/values/strings.xml @@ -0,0 +1,3 @@ + + memosc + \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppPreferences.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppPreferences.kt new file mode 100644 index 0000000..8e93e1a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppPreferences.kt @@ -0,0 +1,75 @@ +package com.github.springeye.memosc +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import androidx.datastore.preferences.core.edit +import androidx.datastore.preferences.core.stringPreferencesKey +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.firstOrNull +import kotlinx.coroutines.flow.map + +interface AppPreferences { + suspend fun host(): String? + suspend fun username(): String? + suspend fun password(): String? + suspend fun host(host:String) + suspend fun username(username:String) + suspend fun password(password:String) + suspend fun setString(key:String,value:String) + suspend fun getString(key:String):String? +} + +internal class AppPreferencesImpl( + private val dataStore: DataStore +) : AppPreferences { + + private companion object { + private const val PREFS_TAG_KEY = "AppPreferences" + private const val HOST = "host" + private const val USERNAME = "username" + private const val PASSWORD = "password" + } + + private val hostKey = stringPreferencesKey("$PREFS_TAG_KEY$HOST") + private val usernameKey = stringPreferencesKey("$PREFS_TAG_KEY$USERNAME") + private val passwordKey = stringPreferencesKey("$PREFS_TAG_KEY$PASSWORD") + + + override suspend fun host(): String? = dataStore.data.map { preferences -> + preferences[hostKey] + }.first() + override suspend fun host(host: String) { + dataStore.edit { preferences -> + preferences[hostKey] = host + } + } + + override suspend fun username(): String? =dataStore.data.map { preferences -> + preferences[usernameKey] + }.first() + + override suspend fun username(username: String){ + dataStore.edit { preferences -> + preferences[usernameKey] = username + } + } + + override suspend fun password(): String? =dataStore.data.map { preferences -> + preferences[passwordKey] + }.first() + + override suspend fun password(password: String) { + dataStore.edit { preferences -> + preferences[passwordKey] = password + } + } + + override suspend fun setString(key: String, value: String) { + dataStore.edit { preferences -> + preferences[stringPreferencesKey("$PREFS_TAG_KEY$key")]=value + } + } + + override suspend fun getString(key: String): String? = dataStore.data.map {preferences -> + preferences[stringPreferencesKey("$PREFS_TAG_KEY$key")] + }.firstOrNull() +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppSettings.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppSettings.kt new file mode 100644 index 0000000..cbc6062 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/AppSettings.kt @@ -0,0 +1,6 @@ +package com.github.springeye.memosc +import androidx.compose.runtime.Stable +import androidx.compose.runtime.compositionLocalOf + +@Stable +data class AppSettings(val host:String) diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoreComponent.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoreComponent.kt new file mode 100644 index 0000000..1752d25 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoreComponent.kt @@ -0,0 +1,22 @@ +package com.github.springeye.memosc +import androidx.datastore.core.DataStore +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.plus + +interface CoreComponent : CoroutinesComponent { + val appPreferences: AppPreferences +} + +internal class CoreComponentImpl internal constructor() : CoreComponent, + CoroutinesComponent by CoroutinesComponentImpl.create() { + + private val dataStore: DataStore = dataStorePreferences( + corruptionHandler = null, + coroutineScope = applicationScope + Dispatchers.IO, + migrations = emptyList() + ) + + override val appPreferences : AppPreferences = AppPreferencesImpl(dataStore) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoroutinesComponent.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoroutinesComponent.kt new file mode 100644 index 0000000..113214a --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/CoroutinesComponent.kt @@ -0,0 +1,23 @@ +package com.github.springeye.memosc +import kotlinx.coroutines.CoroutineDispatcher +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob + +interface CoroutinesComponent { + val mainImmediateDispatcher: CoroutineDispatcher + val applicationScope: CoroutineScope +} + +internal class CoroutinesComponentImpl private constructor() : CoroutinesComponent { + + companion object { + fun create(): CoroutinesComponent = CoroutinesComponentImpl() + } + + override val mainImmediateDispatcher: CoroutineDispatcher = Dispatchers.Main.immediate + override val applicationScope: CoroutineScope + get() = CoroutineScope( + SupervisorJob() + mainImmediateDispatcher, + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/DataStorePreferences.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/DataStorePreferences.kt new file mode 100644 index 0000000..fe8c4c3 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/DataStorePreferences.kt @@ -0,0 +1,34 @@ +package com.github.springeye.memosc +import androidx.datastore.core.DataMigration +import androidx.datastore.core.DataStore +import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler +import androidx.datastore.preferences.core.PreferenceDataStoreFactory +import androidx.datastore.preferences.core.Preferences +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.IO +import kotlinx.coroutines.SupervisorJob +import okio.Path.Companion.toPath + +expect fun dataStorePreferences( + corruptionHandler: ReplaceFileCorruptionHandler?, + coroutineScope: CoroutineScope, + migrations: List>, +): DataStore + +internal const val SETTINGS_PREFERENCES = "settings_preferences" + +internal fun createDataStoreWithDefaults( + corruptionHandler: ReplaceFileCorruptionHandler? = null, + coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.IO + SupervisorJob()), + migrations: List> = emptyList(), + path: () -> String, +) = PreferenceDataStoreFactory + .createWithPath( + corruptionHandler = corruptionHandler, + scope = coroutineScope, + migrations = migrations, + produceFile = { + path().toPath() + } + ) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Greeting.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Greeting.kt new file mode 100755 index 0000000..b357277 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Greeting.kt @@ -0,0 +1,8 @@ +package com.github.springeye.memosc +class Greeting { + private val platform = getPlatform() + + fun greet(): String { + return "Hello, ${platform.name}!" + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/LoadingAnimation.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/LoadingAnimation.kt new file mode 100644 index 0000000..16b851b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/LoadingAnimation.kt @@ -0,0 +1,74 @@ +package com.github.springeye.memosc + +import androidx.compose.animation.core.Animatable +import androidx.compose.animation.core.LinearOutSlowInEasing +import androidx.compose.animation.core.RepeatMode +import androidx.compose.animation.core.infiniteRepeatable +import androidx.compose.animation.core.keyframes +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.delay + +@Composable +fun LoadingAnimation( + modifier: Modifier = Modifier, + circleSize: Dp = 25.dp, + circleColor: Color = MaterialTheme.colorScheme.primary, + spaceBetween: Dp = 10.dp, + travelDistance: Dp = 20.dp +) { + val circles = listOf( + remember { Animatable(initialValue = 0f) }, + remember { Animatable(initialValue = 0f) }, + remember { Animatable(initialValue = 0f) } + ) + + circles.forEachIndexed { index, animatable -> + LaunchedEffect(key1 = animatable) { + delay(index * 100L) + animatable.animateTo( + targetValue = 1f, + animationSpec = infiniteRepeatable( + animation = keyframes { + durationMillis = 1200 + 0.0f at 0 with LinearOutSlowInEasing + 1.0f at 300 with LinearOutSlowInEasing + 0.0f at 600 with LinearOutSlowInEasing + 0.0f at 1200 with LinearOutSlowInEasing + }, + repeatMode = RepeatMode.Restart + ) + ) + } + } + + val circleValues = circles.map { it.value } + val distance = with(LocalDensity.current) { travelDistance.toPx() } + val lastCircle = circleValues.size - 1 + + Row(modifier = modifier) { + circleValues.forEachIndexed { index, value -> + Box(modifier = Modifier + .size(circleSize) + .graphicsLayer { translationY = -value * distance } + .background(color = circleColor, shape = CircleShape) + ) + if (index != lastCircle) Spacer(modifier = Modifier.width(spaceBetween)) + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/MemosApi.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/MemosApi.kt new file mode 100644 index 0000000..e6686b8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/MemosApi.kt @@ -0,0 +1,136 @@ +package com.github.springeye.memosc +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility +import com.github.springeye.memosc.model.Resource +import com.github.springeye.memosc.model.Status +import com.github.springeye.memosc.model.User +import de.jensklingenberg.ktorfit.Response +import de.jensklingenberg.ktorfit.http.* +import io.ktor.client.request.forms.MultiPartFormDataContent +import kotlinx.serialization.Serializable + +@Serializable +data class SignInInput( + val email: String, + var username: String, + val password: String, + val remember:Boolean=true +) +@Serializable +data class CreateMemoInput( + val content: String, + val visibility: MemosVisibility? = null, + val resourceIdList: List? = null +) +@Serializable +data class UpdateMemoOrganizerInput( + val pinned: Boolean +) +@Serializable +data class UpdateTagInput( + val name: String +) +@Serializable +data class DeleteTagInput( + val name: String +) +@Serializable +data class PatchMemoInput( + val id: Long, + val createdTs: Long? = null, + val rowStatus: MemosRowStatus? = null, + val content: String? = null, + val visibility: MemosVisibility? = null, + val resourceIdList: List? = null, +) + +interface MemosApi { + @POST("api/v1/auth/signin") + suspend fun signIn(@Body body: SignInInput):Response + + @POST("api/v1/auth/signout") + suspend fun logout() + + @GET("api/v1/user/me") + suspend fun me(): User + + + /** + * /api/v1/memo + * GET + * Summary + * Get a list of memos matching optional filters + * + * Parameters + * Name Located in Description Required Schema + * creatorId query Creator ID No integer + * creatorUsername query Creator username No string + * rowStatus query Row status No string + * pinned query Pinned No boolean + * tag query Search for tag. Do not append # No string + * content query Search for content No string + * limit query Limit No integer + * offset query Offset No integer + */ + @GET("api/v1/memo") + suspend fun listMemo( + @Query("creatorId") creatorId: Long? = null, + @Query("creatorUsername") creatorUsername: String? = null, + @Query("pinned") pinned: Boolean? = null, + @Query("tag") tag: String? = null, + @Query("limit") limit: Int? = null, + @Query("offset") offset: Int? = null, + @Query("rowStatus") rowStatus: MemosRowStatus? = null, + @Query("visibility") visibility: MemosVisibility? = null, + @Query("content") content: String? = null + ): List + + @POST("api/v1/memo") + suspend fun createMemo(@Body body: CreateMemoInput): Memo + + @GET("api/v1/tag") + suspend fun getTags(@Query("creatorId") creatorId: Long? = null): List + + @POST("api/v1/tag") + suspend fun updateTag(@Body body: UpdateTagInput): String + + @POST("api/v1/tag/delete") + suspend fun deleteTag(@Body body: DeleteTagInput): Unit + + @POST("api/v1/memo/{id}/organizer") + suspend fun updateMemoOrganizer(@Path("id") memoId: Long, @Body body: UpdateMemoOrganizerInput): Memo + + @PATCH("api/v1/memo/{id}") + suspend fun patchMemo(@Path("id") memoId: Long, @Body body: PatchMemoInput): Memo + + @DELETE("api/v1/memo/{id}") + suspend fun deleteMemo(@Path("id") memoId: Long): Unit + + @GET("api/v1/resource") + suspend fun getResources(): List + + @POST("api/v1/resource/blob") + suspend fun uploadResource(@Body map: MultiPartFormDataContent): Resource + + @DELETE("api/v1/resource/{id}") + suspend fun deleteResource(@Path("id") resourceId: Long): Unit + + @GET("api/v1/status") + suspend fun status(): Status + + /** + * Used to generate the heatmap + * @return Memo createdTs list + */ + @GET("api/v1/memo/stats") + suspend fun stats(@Query("creatorId") creatorId: Long?=null, @Query("creatorUsername") creatorUsername: String? = null): List + @GET("api/v1/memo/all") + suspend fun listAllMemo( + @Query("pinned") pinned: Boolean? = null, + @Query("tag") tag: String? = null, + @Query("visibility") visibility: MemosVisibility? = null, + @Query("limit") limit: Int? = null, + @Query("offset") offset: Int? = null + ): List +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Platform.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Platform.kt new file mode 100755 index 0000000..7fe02be --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Platform.kt @@ -0,0 +1,6 @@ +package com.github.springeye.memosc +interface Platform { + val name: String +} + +expect fun getPlatform(): Platform \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/PopupNotification.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/PopupNotification.kt new file mode 100644 index 0000000..a6ce788 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/PopupNotification.kt @@ -0,0 +1,9 @@ +package com.github.springeye.memosc +import androidx.compose.runtime.MutableState + +interface PopupNotification { + fun showPopUpMessage(text: String) + fun showLoading(text: String="loading") + fun hideLoading() +} +expect fun createPopupNotification(state:MutableState): PopupNotification \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Toast.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Toast.kt new file mode 100755 index 0000000..20b3cde --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/Toast.kt @@ -0,0 +1,83 @@ +package com.github.springeye.memosc +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.CircularProgressIndicator +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.MutableState +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import androidx.compose.ui.window.Dialog +import kotlinx.coroutines.delay + +sealed interface ToastState { + object Hidden : ToastState + class Shown(val message: String) : ToastState + class Loading(val message: String) : ToastState + +} + +@Composable +fun Toast( + state: MutableState +) { + val value = state.value + if (value is ToastState.Shown) { + Box( + modifier = Modifier.fillMaxSize().padding(bottom = 20.dp), + contentAlignment = Alignment.BottomCenter + ) { + Surface( + modifier = Modifier.size(300.dp, 70.dp), + color = Color(23, 23, 23), + shape = RoundedCornerShape(4.dp) + ) { + Box(contentAlignment = Alignment.Center) { + Text(value.message, color = Color.White) + } + + LaunchedEffect(value.message) { + delay(3000) + state.value = ToastState.Hidden + } + } + } + } else if (value is ToastState.Loading) { + Dialog(onDismissRequest = { + state.value = ToastState.Hidden + }) { +// CircularProgressIndicator( +// modifier = Modifier.width(64.dp).wrapContentSize(Alignment.Center), +// color = MaterialTheme.colorScheme.secondary, +// ) + LoadingAnimation() + } +// Box( +// modifier = Modifier.fillMaxSize().background(Color.Gray), +// contentAlignment = Alignment.Center +// ) { +// Surface( +// modifier = Modifier.size(70.dp, 70.dp), +// color = Color(23, 23, 23), +// shape = RoundedCornerShape(4.dp) +// ) { +// Box(contentAlignment = Alignment.Center,modifier = Modifier.fillMaxSize() ) { +// CircularProgressIndicator( +// modifier = Modifier.width(64.dp).wrapContentSize(Alignment.Center), +// color = MaterialTheme.colors.secondary, +// ) +// } +// } +// } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/Heatmap.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/Heatmap.kt new file mode 100644 index 0000000..bed20fe --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/Heatmap.kt @@ -0,0 +1,39 @@ +package com.github.springeye.memosc.components + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.lazy.grid.GridCells +import androidx.compose.foundation.lazy.grid.LazyHorizontalGrid +import androidx.compose.foundation.lazy.grid.LazyVerticalGrid +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.dp +import com.github.springeye.memosc.model.DailyUsageStat +import kotlinx.datetime.* + +@Composable +expect fun aa() + +@Composable +fun Heatmap(matrix:List) { + + BoxWithConstraints(modifier = Modifier.fillMaxSize()) { + LazyVerticalGrid( + columns = GridCells.Fixed(7), + horizontalArrangement = Arrangement.spacedBy(2.dp, alignment = Alignment.End), + verticalArrangement = Arrangement.spacedBy(2.dp), + userScrollEnabled = false, + ) { + val countHeatmap = countHeatmap(constraints) + matrix.takeLast(31).forEach { + item(key = it.date) { + HeatmapStat2(day = it) + } + } + } + } +} +expect fun countHeatmap(constraints: Constraints): Int \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.kt new file mode 100644 index 0000000..5508bd4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.kt @@ -0,0 +1,21 @@ +package com.github.springeye.memosc.components + +import androidx.compose.foundation.background +import androidx.compose.foundation.border +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp +import com.github.springeye.memosc.model.DailyUsageStat +import kotlinx.datetime.Clock +import kotlinx.datetime.LocalDate +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toLocalDateTime +@Composable +expect fun HeatmapStat2(day: DailyUsageStat) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/TextField.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/TextField.kt new file mode 100644 index 0000000..662a987 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/components/TextField.kt @@ -0,0 +1,102 @@ +package com.github.springeye.memosc.components + +import androidx.compose.foundation.interaction.MutableInteractionSource +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.text.BasicTextField +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.LocalTextStyle +import androidx.compose.material3.TextFieldColors +import androidx.compose.material3.TextFieldDefaults +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Size +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.Shape +import androidx.compose.ui.graphics.takeOrElse +import androidx.compose.ui.text.TextStyle +import androidx.compose.ui.text.input.VisualTransformation +import androidx.compose.ui.unit.dp + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ITextField( + value: String, + onValueChange: (String) -> Unit, + modifier: Modifier, + enabled: Boolean = true, + readOnly: Boolean = false, + textStyle: TextStyle = LocalTextStyle.current, + label: @Composable (() -> Unit)? = null, + visualTransformation: VisualTransformation = VisualTransformation.None, + placeholder: @Composable (() -> Unit)? = null, + leadingIcon: @Composable (() -> Unit)? = null, + trailingIcon: @Composable (() -> Unit)? = null, + prefix: @Composable (() -> Unit)? = null, + suffix: @Composable (() -> Unit)? = null, + supportingText: @Composable (() -> Unit)? = null, + shape: Shape = TextFieldDefaults.shape, + colors: TextFieldColors = TextFieldDefaults.colors( + disabledIndicatorColor = Color.Transparent, + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + ), + singleLine: Boolean = false, + maxLines: Int = if (singleLine) 1 else Int.MAX_VALUE, + minLines: Int = 1, + isError: Boolean = false, + interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, +) { + + BasicTextField(value, + onValueChange = onValueChange, + modifier = modifier + .defaultMinSize( + minWidth = TextFieldDefaults.MinWidth / 2, + minHeight = TextFieldDefaults.MinHeight / 2 + ), + singleLine = singleLine, + maxLines = maxLines, + minLines = minLines, + enabled = enabled, + readOnly = readOnly, + textStyle = textStyle, + decorationBox = @Composable { innerTextField -> + TextFieldDefaults.DecorationBox( + value = value, + visualTransformation = visualTransformation, + innerTextField = innerTextField, + placeholder = placeholder, + label = label, + leadingIcon = leadingIcon, + trailingIcon = trailingIcon, + prefix = prefix, + suffix = suffix, + supportingText = supportingText, + shape = shape, + singleLine = singleLine, + enabled = enabled, + isError = isError, + interactionSource = interactionSource, + colors = colors, + contentPadding = + if (label == null) { + TextFieldDefaults.contentPaddingWithoutLabel( + start = 8.dp, + end = 8.dp, + bottom = 8.dp, + top = 8.dp + ) + } else { + TextFieldDefaults.contentPaddingWithLabel( + start = 8.dp, + end = 8.dp, + bottom = 4.dp, + top = 4.dp + ) + }, + ) + } + ) +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReference.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReference.kt new file mode 100644 index 0000000..95000e2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReference.kt @@ -0,0 +1,15 @@ +package com.github.springeye.memosc.core +enum class AspectRatioReference { + + /** Child width matches parent width. Child height adjusted to keep the ratio */ + PARENT_WIDTH, + + /** Child height matches parent height. Child height adjusted to keep the ratio */ + PARENT_HEIGHT, + + /** Child fits parent smallest width or height */ + MIN_PARENT_WIDTH_PARENT_HEIGHT, + + /** Child fits parent biggest width or height */ + MAX_PARENT_WIDTH_PARENT_HEIGHT +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReferenceModifier.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReferenceModifier.kt new file mode 100644 index 0000000..7d29624 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/AspectRatioReferenceModifier.kt @@ -0,0 +1,130 @@ +package com.github.springeye.memosc.core + +import androidx.compose.runtime.Stable +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.IntrinsicMeasurable +import androidx.compose.ui.layout.IntrinsicMeasureScope +import androidx.compose.ui.layout.LayoutModifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.InspectorValueInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.IntSize +import kotlin.math.roundToInt + +@Stable +fun Modifier.aspectRatioReference( + ratioWidth: Float, + ratioHeight: Float, + reference: AspectRatioReference = AspectRatioReference.MIN_PARENT_WIDTH_PARENT_HEIGHT +) = then( + AspectRatioReferenceModifier( + ratioWidth = ratioWidth, + ratioHeight = ratioHeight, + reference = reference, + debugInspectorInfo { + name = "aspectRatioReference" + properties["ratioWidth"] = ratioWidth + properties["ratioHeight"] = ratioHeight + properties["reference"] = reference + } + ) +) + +private class AspectRatioReferenceModifier( + private val ratioWidth: Float, + private val ratioHeight: Float, + private val reference: AspectRatioReference, + inspectorInfo: InspectorInfo.() -> Unit +) : LayoutModifier, InspectorValueInfo(inspectorInfo) { + + init { + require(ratioWidth > 0) { "ratioWidth $ratioWidth must be > 0" } + require(ratioHeight > 0) { "ratioHeight $ratioHeight must be > 0" } + } + + private val ratio = ratioWidth / ratioHeight + + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val size = constraints.findSize() + val wrappedConstraints = if (size != IntSize.Zero) { + Constraints.fixed(size.width, size.height) + } else { + constraints + } + val placeable = measurable.measure(wrappedConstraints) + return layout(placeable.width, placeable.height) { + placeable.placeRelative(0, 0) + } + } + + override fun IntrinsicMeasureScope.minIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ) = if (height != Constraints.Infinity) { + (height * ratio).roundToInt() + } else { + measurable.minIntrinsicWidth(height) + } + + override fun IntrinsicMeasureScope.maxIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ) = if (height != Constraints.Infinity) { + (height * ratio).roundToInt() + } else { + measurable.maxIntrinsicWidth(height) + } + + override fun IntrinsicMeasureScope.minIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ) = if (width != Constraints.Infinity) { + (width / ratio).roundToInt() + } else { + measurable.minIntrinsicHeight(width) + } + + override fun IntrinsicMeasureScope.maxIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ) = if (width != Constraints.Infinity) { + (width / ratio).roundToInt() + } else { + measurable.maxIntrinsicHeight(width) + } + + private fun Constraints.findSize(): IntSize { + val matchPARENTWidth = when (reference) { + AspectRatioReference.PARENT_WIDTH -> true + AspectRatioReference.PARENT_HEIGHT -> false + AspectRatioReference.MIN_PARENT_WIDTH_PARENT_HEIGHT -> maxWidth < maxHeight + AspectRatioReference.MAX_PARENT_WIDTH_PARENT_HEIGHT -> maxWidth > maxHeight + } + return if (matchPARENTWidth) { + IntSize(maxWidth, (maxWidth * ratioHeight / ratioWidth).roundToInt()) + } else { + IntSize((maxHeight * ratioWidth / ratioHeight).roundToInt(), maxHeight) + } + } + + override fun equals(other: Any?): Boolean { + if (this === other) return true + val otherModifier = other as? AspectRatioReferenceModifier ?: return false + return ratio == otherModifier.ratio && reference == other.reference + } + + override fun hashCode(): Int = ratio.hashCode() * 31 + reference.hashCode() + + override fun toString(): String = "AspectRatioReferenceModifier(" + + "ratioWidth=$ratioWidth, " + + "ratioHeight=$ratioHeight, " + + "reference=$reference" + + ")" +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Base64ImageFetcher.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Base64ImageFetcher.kt new file mode 100644 index 0000000..5c37eaa --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Base64ImageFetcher.kt @@ -0,0 +1,43 @@ +package com.github.springeye.memosc.core + +import io.kamel.core.DataSource +import io.kamel.core.Resource +import io.kamel.core.config.ResourceConfig +import io.kamel.core.fetcher.Fetcher +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.flow +import kotlin.io.encoding.Base64 +import kotlin.io.encoding.ExperimentalEncodingApi +import kotlin.reflect.KClass + +data class Base64Image(val content:String?=null) +class Base64ImageFetcher : Fetcher { + + override val inputDataKClass: KClass + get() = Base64Image::class + override val source: DataSource + get() = DataSource.None + + @OptIn(ExperimentalEncodingApi::class) + override fun fetch( + data: Base64Image, + resourceConfig: ResourceConfig + ): Flow> { + return flow { + try { + val reg = "^data:image/([a-zA-Z]*);base64,([^\"]*)".toRegex(RegexOption.IGNORE_CASE) + val matchResult = + reg.matchEntire(data.content ?: "") ?: throw Exception("not match") + val (type, base64) = matchResult.destructured + val bytes = Base64.decode(base64) + emit(Resource.Success(ByteReadChannel(bytes))) + } catch (e: Exception) { + emit(Resource.Failure(e)) + } + } + } + + override val Base64Image.isSupported: Boolean + get() = "^data:image/([a-zA-Z]*);base64,([^\"]*)".toRegex(RegexOption.IGNORE_CASE).matches(content?:"") +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/IFile.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/IFile.kt new file mode 100644 index 0000000..1cba784 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/IFile.kt @@ -0,0 +1,7 @@ +package com.github.springeye.memosc.core +interface IFile{ + fun readBytes():ByteArray + val mimeType:String + val filename:String +} +expect fun createIFile(path:String):IFile \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Instant.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Instant.kt new file mode 100644 index 0000000..244241e --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/core/Instant.kt @@ -0,0 +1,7 @@ +package com.github.springeye.memosc.core +import kotlinx.datetime.Instant +import kotlinx.datetime.LocalDate + +expect fun Instant.formatDate(pattern: String, defValue: String = ""): String + +expect fun String.parseDate(pattern: String, defValue: Long = 0L): Long \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/AppDatabase.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/AppDatabase.kt new file mode 100644 index 0000000..d06e82c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/AppDatabase.kt @@ -0,0 +1,5 @@ +package com.github.springeye.memosc.db + +import com.github.springeye.memosc.db.model.AppDatabase + +expect fun createAppDatabase():AppDatabase \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoQueryWhere.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoQueryWhere.kt new file mode 100644 index 0000000..9ad1147 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoQueryWhere.kt @@ -0,0 +1,17 @@ +package com.github.springeye.memosc.db + +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility + + +data class MemoQueryWhere( + val creatorId: Long?=null, + val creatorUsername: String?=null, + val pinned: Boolean?=null, + val tag: String?=null, + val limit: Int?=null, + val offset: Int?=null, + val rowStatus: MemosRowStatus?=null, + val visibility: MemosVisibility?=null, + val content: String?=null +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoRemoteMediator.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoRemoteMediator.kt new file mode 100644 index 0000000..86c4754 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/db/MemoRemoteMediator.kt @@ -0,0 +1,61 @@ +package com.github.springeye.memosc.db + +import androidx.paging.ExperimentalPagingApi +import androidx.paging.LoadType +import androidx.paging.PagingState +import app.cash.paging.RemoteMediator +import com.github.springeye.memosc.db.model.QueryQueries + +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.repository.MemoRepository + + +@OptIn(ExperimentalPagingApi::class) +class MemoRemoteMediator(private val api: MemoRepository, + private val memoQueries: QueryQueries, + private val query: MemoQueryWhere = MemoQueryWhere(),): RemoteMediator() { + override suspend fun initialize(): InitializeAction { + return InitializeAction.SKIP_INITIAL_REFRESH + } + override suspend fun load(loadType: LoadType, state: PagingState): MediatorResult { + return try { +// val loadKey = when (loadType) { +// LoadType.REFRESH -> null +// +// LoadType.PREPEND -> return MediatorResult.Success( +// endOfPaginationReached = true +// ) +// LoadType.APPEND -> { +// val remoteKey = +// remoteKeyDao.remoteKeyByQuery(query?.creatorId,query?.rowStatus,query?.visibility) +// +// return MediatorResult.Success( +// endOfPaginationReached = true +// ) +// +// remoteKey +// } +// } + + + // Suspending network load via Retrofit. This doesn't need to be wrapped in a + // withContext(Dispatcher.IO) { ... } block since Retrofit's Coroutine CallAdapter + // dispatches on a worker thread. + val response = api.listMemo(filter = query) + memoQueries.transaction { + memoQueries.deleteAllMemo() + memoQueries.deleteAllResource() + for (memo in response) { + memoQueries.insertMemo(memo.toDbMemo()) + for (resource in memo.resourceList) { + memoQueries.insertResource(resource.toDBResource(memo.id)) + } + } + } + MediatorResult.Success(endOfPaginationReached = true) + } catch (e: Exception) { + MediatorResult.Error(e) + } + + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/PersistentCookiesStorage.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/PersistentCookiesStorage.kt new file mode 100644 index 0000000..e72174b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/PersistentCookiesStorage.kt @@ -0,0 +1,88 @@ +package com.github.springeye.memosc.di + +import com.github.springeye.memosc.AppPreferences +import io.ktor.client.plugins.cookies.CookiesStorage +import io.ktor.http.Cookie +import io.ktor.http.CookieEncoding +import io.ktor.http.Url +import io.ktor.http.hostIsIp +import io.ktor.http.isSecure +import io.ktor.util.date.GMTDate +import io.ktor.util.toLowerCasePreservingASCIIRules + +expect class PersistentCookiesStorage(store: AppPreferences):CookiesStorage +internal fun Cookie.matches(requestUrl: Url): Boolean { + val domain = domain?.toLowerCasePreservingASCIIRules()?.trimStart('.') + ?: error("Domain field should have the default value") + + val path = with(path) { + val current = path ?: error("Path field should have the default value") + if (current.endsWith('/')) current else "$path/" + } + + val host = requestUrl.host.toLowerCasePreservingASCIIRules() + val requestPath = let { + val pathInRequest = requestUrl.encodedPath + if (pathInRequest.endsWith('/')) pathInRequest else "$pathInRequest/" + } + + if (host != domain && (hostIsIp(host) || !host.endsWith(".$domain"))) { + return false + } + + if (path != "/" && + requestPath != path && + !requestPath.startsWith(path) + ) { + return false + } + + return !(secure && !requestUrl.protocol.isSecure()) +} + +internal fun Cookie.fillDefaults(requestUrl: Url): Cookie { + var result = this + + if (result.path?.startsWith("/") != true) { + result = result.copy(path = requestUrl.encodedPath) + } + + if (result.domain.isNullOrBlank()) { + result = result.copy(domain = requestUrl.host) + } + + return result +} + +fun fromString(string: String): Cookie { + val strings = string.split(":") + val name = strings[0]; + val value = strings[1]; + val encoding = strings[2]; + val maxAge = strings[3].toInt(); + val expires = strings[4].toLong(); + val domain = strings[5]; + val path = strings[6]; + val secure = strings[7].toBoolean(); + val httpOnly = strings[8].toBoolean(); + val extensions = strings[9].split(",").map { + val keyValue = it.split("#") + keyValue[0] to keyValue[1] + }.toMap() + return Cookie( + name, value, + CookieEncoding.valueOf(encoding), maxAge, + GMTDate(expires), domain, path, secure, httpOnly, extensions, + ) +} + +internal fun Cookie.toSaveString(): String { + val cookie = this + val extensions = extensions.map { + "${it.key}#${it.value}" + }.joinToString(",") + val c = + "${cookie.name}:${cookie.value}:${cookie.encoding}:${cookie.maxAge}:${cookie.expires?.timestamp}:${cookie.domain}:${cookie.path}:${cookie.secure}:${cookie.httpOnly}:$extensions" + + return c +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/module.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/module.kt new file mode 100644 index 0000000..b01baa2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/di/module.kt @@ -0,0 +1,135 @@ +package com.github.springeye.memosc.di + +import com.github.springeye.memosc.AppPreferences +import com.github.springeye.memosc.CoreComponent +import com.github.springeye.memosc.CoreComponentImpl +import com.github.springeye.memosc.MemosApi + +import com.github.springeye.memosc.db.createAppDatabase +import com.github.springeye.memosc.db.model.AppDatabase +import com.github.springeye.memosc.repository.MemoRepository +import com.github.springeye.memosc.ui.AppModel +import com.github.springeye.memosc.ui.app.AppScreenModel +import com.github.springeye.memosc.ui.home.ArchivedModel +import com.github.springeye.memosc.ui.home.MemoModel +import com.github.springeye.memosc.ui.home.NotifiModel +import com.github.springeye.memosc.ui.home.ProfileModel +import com.github.springeye.memosc.ui.login.LoginScreenModel +import de.jensklingenberg.ktorfit.Ktorfit +import de.jensklingenberg.ktorfit.converter.builtin.CallConverterFactory +import de.jensklingenberg.ktorfit.ktorfit +import io.github.aakira.napier.Napier +import io.ktor.client.HttpClient +import io.ktor.client.plugins.contentnegotiation.ContentNegotiation +import io.ktor.client.plugins.cookies.CookiesStorage +import io.ktor.client.plugins.cookies.HttpCookies +import io.ktor.client.plugins.defaultRequest +import io.ktor.client.plugins.logging.LogLevel +import io.ktor.client.plugins.logging.Logger +import io.ktor.client.plugins.logging.Logging +import io.ktor.client.request.header +import io.ktor.serialization.kotlinx.json.json +import kotlinx.coroutines.runBlocking +import kotlinx.serialization.ExperimentalSerializationApi +import kotlinx.serialization.json.Json +import org.koin.dsl.module + + +@OptIn(ExperimentalSerializationApi::class) +val appModule = module { + + single { + CoreComponentImpl() + } + single { + get().appPreferences + } + + single { + Json { + encodeDefaults = true + isLenient = true + ignoreUnknownKeys=true + explicitNulls=false + allowSpecialFloatingPointValues = true +// namingStrategy= JsonNamingStrategy.SnakeCase + allowStructuredMapKeys = true + prettyPrint = false + useArrayPolymorphism = false + } + } + single { + PersistentCookiesStorage(get()) + } + single { + HttpClient { + defaultRequest { + + if(headers["Content-Type"].isNullOrEmpty()){ + header("Content-Type","application/json") + } + url{ + val prefer: AppPreferences =get() + val url=runBlocking { prefer.host() } + //this url should https://api.github.com + url + } + } + install(Logging) { + logger = object: Logger { + override fun log(message: String) { + Napier.v("HTTP Client", null, message) + } + } + level = LogLevel.ALL + } + install(HttpCookies){ + this.storage=get() + } + install(ContentNegotiation) { + json(json= get()) + } + } + } + single { + + ktorfit { + val sp=get() + val host = runBlocking { sp.host() ?: "" } + baseUrl(host,false) + httpClient(get()) + converterFactories( + CallConverterFactory() + ) + } + } + single { + val ktorfit=get() + ktorfit.create() + } + single{ + MemoRepository(get()) + } + single { + createAppDatabase() + } + single { + get().queryQueries + } +} +val homeModule = module { + + single { + val sp=get() + val host = runBlocking { sp.host() ?: "" } + AppModel(host) + } + single { AppScreenModel(get(), get(),get()) } + single { LoginScreenModel(get(),get(),get()) } + single { MemoModel(get(),get(),get()) } + single { NotifiModel() } + single { ProfileModel(get(),get(),get()) } + single { ArchivedModel(get(),get(),get()) } + +} + diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/logger.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/logger.kt new file mode 100644 index 0000000..73c5e04 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/logger.kt @@ -0,0 +1,21 @@ +package com.github.springeye.memosc +import io.github.aakira.napier.Napier + +expect fun initLogger() +object Logger{ + fun d(message: String, throwable: Throwable? = null, tag: String? = null){ + Napier.d(message,throwable,tag) + } + fun i(message: String, throwable: Throwable? = null, tag: String? = null){ + Napier.i(message,throwable,tag) + } + fun e(message: String, throwable: Throwable? = null, tag: String? = null){ + Napier.e(message,throwable,tag) + } + fun w(message: String, throwable: Throwable? = null, tag: String? = null){ + Napier.w(message,throwable,tag) + } + fun v(message: String, throwable: Throwable? = null, tag: String? = null){ + Napier.v(message,throwable,tag) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.kt new file mode 100644 index 0000000..69dd6d2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.kt @@ -0,0 +1,20 @@ +package com.github.springeye.memosc.model + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.toJavaLocalDate +import java.time.format.DateTimeFormatter + +data class DailyUsageStat( + //milliseconds + val date: LocalDate, + val count: Int = 0 +) { + override fun toString(): String { + return date.toJavaLocalDate().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")) + } +} +expect fun initialMatrix(): List + + +expect val weekDays:List +expect fun calculateMatrix(memos: Map>): List \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ErrorMessage.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ErrorMessage.kt new file mode 100644 index 0000000..5f622d2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ErrorMessage.kt @@ -0,0 +1,8 @@ +package com.github.springeye.memosc.model +import kotlinx.serialization.Serializable + +@Serializable +data class ErrorMessage( + val error: String? = null, + val message: String +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Memo.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Memo.kt new file mode 100644 index 0000000..0945ce9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Memo.kt @@ -0,0 +1,78 @@ +package com.github.springeye.memosc.model + + +import com.github.springeye.memosc.db.model.QueryQueries +import kotlinx.serialization.Serializable + +@Serializable +enum class MemosVisibility { + + PUBLIC, + + PROTECTED, + + PRIVATE +} +@Serializable +enum class MemosRowStatus { + + NORMAL, + + ARCHIVED +} +@Serializable +data class Memo( + val id: Long, + val createdTs: Long, + val creatorId: Long, + val creatorName: String? = null, + var content: String, + var pinned: Boolean, + val rowStatus: MemosRowStatus = MemosRowStatus.NORMAL, + val updatedTs: Long, + val visibility: MemosVisibility = MemosVisibility.PRIVATE, + val resourceList: MutableList = mutableListOf() +){ + fun loadResource(memoQueries: QueryQueries) { + resourceList.clear() + resourceList.addAll(memoQueries.selectResourceByMemoId(this.id).executeAsList().map { Resource.fromDBResource(it) }) + + } + fun toDbMemo(): com.github.springeye.memosc.db.model.Memo { + return com.github.springeye.memosc.db.model.Memo( + id, + createdTs, + creatorId, + creatorName, + content, + pinned, + rowStatus, + visibility, + updatedTs + ) + } + companion object{ + fun empty(): Memo { + return Memo( + System.currentTimeMillis(), System.currentTimeMillis(), 1, "henjue", + "aaa", + false, MemosRowStatus.NORMAL,System.currentTimeMillis(), + ) + } + fun fromDbMemo(memo:com.github.springeye.memosc.db.model.Memo):Memo{ + return with(memo) { + Memo( + id, + createdTs, + creatorId, + creatorName, + content, + pinned, + rowStatus, + updatedTs, + visibility + ) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Resource.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Resource.kt new file mode 100644 index 0000000..2174af4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Resource.kt @@ -0,0 +1,72 @@ +package com.github.springeye.memosc.model + +import io.ktor.http.URLBuilder +import io.ktor.http.path +import kotlinx.serialization.Serializable +import kotlinx.serialization.Transient + +@Serializable +data class Resource( + val id: Long, + val createdTs: Long, + val creatorId: Long, + val filename: String, + val size: Long, + val type: String, + val updatedTs: Long, + val externalLink: String?, + val publicId: String?, + @Transient + val memoId: Long=0 +) { + fun toDBResource( memoId: Long): com.github.springeye.memosc.db.model.Resource { + return com.github.springeye.memosc.db.model.Resource( + id, + createdTs, + creatorId, + filename, + size, + type, + updatedTs, + externalLink, + publicId, + memoId + ) + } + fun uri(host: String): String { + if (!externalLink.isNullOrEmpty()) { + return URLBuilder(externalLink).buildString() + } + if (!publicId.isNullOrEmpty()) { + return URLBuilder(host) + .apply { + path("o","r",id.toString(),publicId) + } + .buildString() + } + return return URLBuilder(host) + .apply { + path("o","r",id.toString(),filename) + } + .buildString() + } + companion object{ + fun fromDBResource(resource:com.github.springeye.memosc.db.model.Resource):Resource{ + return with(resource) { + Resource( + id, + createdTs, + creatorId, + filename, + size, + type, + updatedTs, + externalLink, + publicId, + memoId?:0 + ) + } + + } + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ShareContent.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ShareContent.kt new file mode 100644 index 0000000..aecb002 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/ShareContent.kt @@ -0,0 +1,51 @@ +package com.github.springeye.memosc.model + +import kotlinx.serialization.Serializable + + +@Serializable +data class ShareContent( + val text: String = "", + val images: List = ArrayList() +) { +// companion object { +// fun parseIntent(intent: Intent): ShareContent { +// val text = intent.getStringExtra(Intent.EXTRA_TEXT) +// val images = ArrayList() +// +// when (intent.action) { +// Intent.ACTION_SEND -> { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// intent.getParcelableExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let { +// images.add(it) +// } +// } else { +// @Suppress("DEPRECATION") +// (intent.getParcelableExtra(Intent.EXTRA_STREAM) as? Uri)?.let { +// images.add(it) +// } +// } +// } +// +// Intent.ACTION_SEND_MULTIPLE -> { +// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { +// intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM, Uri::class.java)?.let { +// images.addAll(it) +// } +// } else { +// @Suppress("DEPRECATION") +// intent.getParcelableArrayListExtra(Intent.EXTRA_STREAM)?.let { +// for (item in it) { +// if (item is Uri) { +// images.add(item) +// } +// } +// } +// } +// } +// } +// +// return ShareContent(text ?: "", images) +// } +// } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Status.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Status.kt new file mode 100644 index 0000000..608e7a2 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/Status.kt @@ -0,0 +1,14 @@ +package com.github.springeye.memosc.model +import kotlinx.serialization.Serializable + + +@Serializable +data class Profile( + val mode: String, + val version: String +) + +@Serializable +data class Status( + val profile: Profile +) \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/User.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/User.kt new file mode 100644 index 0000000..8d83e59 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/model/User.kt @@ -0,0 +1,54 @@ +package com.github.springeye.memosc.model + +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable + +@Serializable +enum class MemosRole { + HOST, + USER +} + +@Serializable +enum class MemosUserSettingKey { + @SerialName("locale") + LOCALE, + @SerialName("memo-visibility") + MEMO_VISIBILITY, + @SerialName("editorFontStyle") + EDITOR_FONT_STYLE, + UNKNOWN +} + +@Serializable +data class MemosUserSetting( + val key: MemosUserSettingKey = MemosUserSettingKey.UNKNOWN, + val value: String +) + +@Serializable +data class User( + val createdTs: Long, + val email: String?, + val id: Long, + val name: String?, + val role: MemosRole = MemosRole.USER, + val rowStatus: MemosRowStatus = MemosRowStatus.NORMAL, + val updatedTs: Long?, + val userSettingList: List? = null, + val nickname: String?, + val username: String?, + val avatarUrl: String?, +) { + val displayEmail get() = email ?: username ?: "" + val displayName get() = nickname ?: name ?: "" + + val memoVisibility: MemosVisibility + get() = userSettingList?.find { it.key == MemosUserSettingKey.MEMO_VISIBILITY }?.let { + try { + MemosVisibility.valueOf(it.value.removePrefix("\"").removeSuffix("\"")) + } catch (_: IllegalArgumentException) { + MemosVisibility.PRIVATE + } + } ?: MemosVisibility.PRIVATE +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoPagingSource.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoPagingSource.kt new file mode 100644 index 0000000..ab57366 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoPagingSource.kt @@ -0,0 +1,31 @@ +package com.github.springeye.memosc.repository + +import androidx.paging.PagingState +import app.cash.paging.PagingSource +import com.github.springeye.memosc.db.MemoQueryWhere +import com.github.springeye.memosc.model.Memo +import io.github.aakira.napier.Napier + +class MemoPagingSource( + val repository: MemoRepository, + val filter: MemoQueryWhere, + ): PagingSource() { + override fun getRefreshKey(state: PagingState): Int? { + return state.anchorPosition?.let { anchorPosition -> + state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) + ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) + } + } + + override suspend fun load(params: LoadParams): LoadResult { + val nextPage:Int = (params.key ?: 0) + val size = params.loadSize + Napier.d("page:$nextPage size:$size") + val newFilter=filter.copy(offset = nextPage * params.loadSize, limit = size) + val list = repository.listMemo(filter = newFilter) + return LoadResult.Page(list, + prevKey = if(nextPage>0) nextPage-1 else null, + nextKey = if(list.size>=size) nextPage+1 else null) + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoRepository.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoRepository.kt new file mode 100644 index 0000000..cfab311 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/repository/MemoRepository.kt @@ -0,0 +1,15 @@ +package com.github.springeye.memosc.repository + +import com.github.springeye.memosc.MemosApi +import com.github.springeye.memosc.db.MemoQueryWhere +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility +import kotlinx.coroutines.delay + + +class MemoRepository(private val api: MemosApi,) { + suspend fun listMemo(filter: MemoQueryWhere=MemoQueryWhere()): List { + return api.listMemo(filter.creatorId, filter.creatorUsername, filter.pinned, filter.tag, filter.limit, filter.offset, filter.rowStatus, filter.visibility, filter.content) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/AppModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/AppModel.kt new file mode 100644 index 0000000..95806d0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/AppModel.kt @@ -0,0 +1,9 @@ +package com.github.springeye.memosc.ui +import cafe.adriel.voyager.core.model.StateScreenModel +import com.github.springeye.memosc.AppSettings + +class AppModel(private val host:String): StateScreenModel(AppSettings(host)) { + fun setHost(host: String){ + mutableState.value=mutableState.value.copy(host=host) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/app/AppScreenModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/app/AppScreenModel.kt new file mode 100644 index 0000000..ae8f42b --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/app/AppScreenModel.kt @@ -0,0 +1,41 @@ +package com.github.springeye.memosc.ui.app +import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import com.github.springeye.memosc.AppPreferences +import com.github.springeye.memosc.MemosApi +import io.ktor.client.HttpClient +import io.ktor.client.plugins.cookies.cookies +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +class AppScreenModel(private val api: MemosApi, + private val httpClient: HttpClient, + private val prefers: AppPreferences + +) : StateScreenModel(State.Loading) { + sealed class State { + + object Loading : State() + data class Result(val isLogin:Boolean) : State() + } + + fun check(){ + + screenModelScope.launch { + val host=prefers.host() + val username=prefers.username() + val password=prefers.password() + mutableState.value=State.Loading + delay(1000) + if(host!=null){ + val cookies=httpClient.cookies(host) + if(cookies.isNotEmpty()){ + mutableState.value=State.Result(true) + return@launch + } + } + mutableState.value=State.Result(false) +// println("cookies===>${cookies}") + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ArchivedModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ArchivedModel.kt new file mode 100644 index 0000000..ffa9126 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ArchivedModel.kt @@ -0,0 +1,85 @@ +package com.github.springeye.memosc.ui.home + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.paging.ExperimentalPagingApi +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.filter +import app.cash.paging.Pager +import app.cash.paging.map +import app.cash.sqldelight.Query +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import cafe.adriel.voyager.core.model.ScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import com.github.springeye.memosc.CreateMemoInput +import com.github.springeye.memosc.MemosApi +import com.github.springeye.memosc.PatchMemoInput +import com.github.springeye.memosc.UpdateMemoOrganizerInput +import com.github.springeye.memosc.core.createIFile +import com.github.springeye.memosc.core.formatDate +import com.github.springeye.memosc.core.parseDate +import com.github.springeye.memosc.db.MemoQueryWhere +import com.github.springeye.memosc.db.model.QueryQueries +import com.github.springeye.memosc.model.DailyUsageStat +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility +import com.github.springeye.memosc.model.Resource +import com.github.springeye.memosc.model.calculateMatrix +import com.github.springeye.memosc.model.initialMatrix +import com.github.springeye.memosc.repository.MemoPagingSource +import com.github.springeye.memosc.repository.MemoRepository +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.forms.formData +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.datetime.Instant +import org.jetbrains.compose.resources.LoadState + +@OptIn(ExperimentalPagingApi::class) +class ArchivedModel( + private val api: MemosApi, + private val repository: MemoRepository, + private val memoQueries: QueryQueries, + + + ) : ScreenModel { + + + + + fun getPagerByUserId(uid:Long): Flow> { + return Pager( + config = PagingConfig(pageSize = 10), +// remoteMediator = MemoRemoteMediator(repository, memoQueries,query) + ) { + MemoPagingSource(repository,MemoQueryWhere(creatorId = uid, rowStatus = MemosRowStatus.ARCHIVED)) + + }.flow.cachedIn(screenModelScope) + } + + + suspend fun setPininned(it: Memo) { + api.updateMemoOrganizer(it.id, UpdateMemoOrganizerInput(!it.pinned)) + } + + suspend fun remove(it: Memo) { + api.deleteMemo(it.id) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/MemoModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/MemoModel.kt new file mode 100644 index 0000000..cf0f306 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/MemoModel.kt @@ -0,0 +1,224 @@ +package com.github.springeye.memosc.ui.home + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.paging.ExperimentalPagingApi +import androidx.paging.PagingConfig +import androidx.paging.cachedIn +import app.cash.paging.Pager +import app.cash.sqldelight.Query +import cafe.adriel.voyager.core.model.ScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import com.github.springeye.memosc.CreateMemoInput +import com.github.springeye.memosc.MemosApi +import com.github.springeye.memosc.PatchMemoInput +import com.github.springeye.memosc.UpdateMemoOrganizerInput +import com.github.springeye.memosc.core.createIFile +import com.github.springeye.memosc.core.formatDate +import com.github.springeye.memosc.core.parseDate +import com.github.springeye.memosc.db.MemoQueryWhere +import com.github.springeye.memosc.db.model.QueryQueries +import com.github.springeye.memosc.model.DailyUsageStat +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility +import com.github.springeye.memosc.model.Resource +import com.github.springeye.memosc.model.User +import com.github.springeye.memosc.model.calculateMatrix +import com.github.springeye.memosc.model.initialMatrix +import com.github.springeye.memosc.repository.MemoPagingSource +import com.github.springeye.memosc.repository.MemoRepository +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.forms.formData +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.datetime.Instant +import kotlinx.datetime.TimeZone +import kotlinx.datetime.toKotlinInstant +import kotlinx.datetime.toLocalDateTime +import java.util.Date + +@OptIn(ExperimentalPagingApi::class) +class MemoModel( + private val api: MemosApi, + private val repository: MemoRepository, + private val memoQueries: QueryQueries, + + + ) : ScreenModel { + + + private val resources = mutableStateListOf() + var resourcesGroup: Flow>> = snapshotFlow { resources.toList() } + .map { + return@map it.sortedByDescending { it.createdTs }.groupBy { + val old = Instant.fromEpochSeconds(it.createdTs).formatDate("yyyy-MM") + val time = old.parseDate("yyyy-MM") + val date = Instant.fromEpochMilliseconds(time) + date + } + } + + private val _query = MutableStateFlow(MemoQueryWhere()) + + @OptIn(ExperimentalCoroutinesApi::class) + val pager = _query.flatMapLatest { query -> + Pager( + config = PagingConfig(pageSize = 10), +// remoteMediator = MemoRemoteMediator(repository, memoQueries,query) + ) { + MemoPagingSource(repository, query) +// QueryPagingSource( +// countQuery =memoQueries.countMemos(), +// transacter =memoQueries, +// context = Dispatchers.IO, +// queryProvider = createQuery(memoQueries,query) +// ) + }.flow.cachedIn(screenModelScope) + } + var user by mutableStateOf(null) + private set + + fun fetchExtraInfo() { + screenModelScope.launch { + user = api.me() + if (user != null) { + resources.clear() + resources.addAll(api.getResources()) + + + val stats = api.stats(user!!.id).map { + Date(it * 1000).toInstant().toKotlinInstant().toLocalDateTime( + TimeZone.currentSystemDefault() + ).date + }.groupBy { it } + matrix=calculateMatrix(stats) + + } + } + + } + + var matrix by mutableStateOf>(initialMatrix()) + private set + + + var query by mutableStateOf(MemoQueryWhere()) + + + + suspend fun upload(path: String): Resource { + val multipart = formData { + val iFile = createIFile(path) + val bytes = iFile.readBytes() + val mimeType = iFile.mimeType + val filename = iFile.filename + append("file", bytes, Headers.build { + append(HttpHeaders.ContentType, mimeType) + append(HttpHeaders.ContentDisposition, "filename=${filename}") + }) + } + return api.uploadResource(MultiPartFormDataContent(multipart)) + + } + + + suspend fun submit( + editId: Long, + content: String, + resources: List, + visibility: MemosVisibility + ) { + + if (editId > 0) { + val memo = api.patchMemo( + editId, + PatchMemoInput( + editId, + content = content, + visibility = visibility, + resourceIdList = resources.map { it.id }) + ); +// memos.indexOfFirst { memo.id == it.id }.takeIf { it >= 0 }?.let { +// memos[it] = memo +// } + } else { + val memo = api.createMemo( + CreateMemoInput( + content, + visibility = visibility, + resourceIdList = resources.map { it.id }) + ); +// memos.add(0, memo) + } + + } + + + suspend fun filterCreatorId(creatorId: Long? = null) { + query = query.copy(creatorId = creatorId) + _query.emit(_query.value.copy(creatorId = creatorId)) + } + + suspend fun filterRowStatus(rowStatus: MemosRowStatus? = null) { + query = query.copy(rowStatus = rowStatus) + _query.emit(_query.value.copy(rowStatus = rowStatus)) + } + + suspend fun filterVisibility(visibility: MemosVisibility? = null) { + query = query.copy(visibility = visibility) + _query.emit(_query.value.copy(visibility = visibility)) + } + + fun filterContent(content: String? = null) { + query = query.copy(content = content) + screenModelScope.launch { + _query.emit(_query.value.copy(content = content)) + } + } + + + private fun createQuery( + memoQueries: QueryQueries, + query: MemoQueryWhere + ): (limit: Long, offset: Long) -> Query { + return { limit: Long, offset: Long -> + memoQueries.memos( + limit, + offset + ) { id: Long, createdTs: Long, creatorId: Long, creatorName: String?, content: String, pinned: Boolean, rowStatus: MemosRowStatus, visibility: MemosVisibility, updatedTs: Long -> + return@memos com.github.springeye.memosc.model.Memo( + id, + createdTs, + creatorId, + creatorName, + content, + pinned, + rowStatus, + updatedTs, + visibility + ).apply { + loadResource(memoQueries) + } + } + } + } + + suspend fun setPininned(it: Memo) { + api.updateMemoOrganizer(it.id, UpdateMemoOrganizerInput(!it.pinned)) + } + + suspend fun remove(it: Memo) { + api.deleteMemo(it.id) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/NotifiModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/NotifiModel.kt new file mode 100644 index 0000000..572a2b7 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/NotifiModel.kt @@ -0,0 +1,27 @@ +package com.github.springeye.memosc.ui.home + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import cafe.adriel.voyager.core.model.ScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch + +class NotifiModel:ScreenModel { + val items= mutableStateListOf("") + var counter by mutableStateOf( 0) + init { + + snapshotFlow { items.toList() }.onEach { + println(it) + counter+=it.sumOf { it.length }; + }.launchIn(screenModelScope) + } + fun addItem(value:String){ + items.add(value) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ProfileModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ProfileModel.kt new file mode 100644 index 0000000..4aca8f7 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/home/ProfileModel.kt @@ -0,0 +1,86 @@ +package com.github.springeye.memosc.ui.home + +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue +import androidx.compose.runtime.snapshotFlow +import androidx.paging.ExperimentalPagingApi +import androidx.paging.PagingConfig +import androidx.paging.PagingData +import androidx.paging.cachedIn +import androidx.paging.filter +import app.cash.paging.Pager +import app.cash.paging.map +import app.cash.sqldelight.Query +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import cafe.adriel.voyager.core.model.ScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import com.github.springeye.memosc.CreateMemoInput +import com.github.springeye.memosc.MemosApi +import com.github.springeye.memosc.PatchMemoInput +import com.github.springeye.memosc.UpdateMemoOrganizerInput +import com.github.springeye.memosc.core.createIFile +import com.github.springeye.memosc.core.formatDate +import com.github.springeye.memosc.core.parseDate +import com.github.springeye.memosc.db.MemoQueryWhere +import com.github.springeye.memosc.db.model.QueryQueries +import com.github.springeye.memosc.model.DailyUsageStat +import com.github.springeye.memosc.model.Memo +import com.github.springeye.memosc.model.MemosRowStatus +import com.github.springeye.memosc.model.MemosVisibility +import com.github.springeye.memosc.model.Resource +import com.github.springeye.memosc.model.calculateMatrix +import com.github.springeye.memosc.model.initialMatrix +import com.github.springeye.memosc.repository.MemoPagingSource +import com.github.springeye.memosc.repository.MemoRepository +import io.ktor.client.request.forms.MultiPartFormDataContent +import io.ktor.client.request.forms.formData +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.launch +import kotlinx.datetime.Instant +import org.jetbrains.compose.resources.LoadState + +@OptIn(ExperimentalPagingApi::class) +class ProfileModel( + private val api: MemosApi, + private val repository: MemoRepository, + private val memoQueries: QueryQueries, + + + ) : ScreenModel { + + + + + fun getPagerByUserId(uid:Long): Flow> { + return Pager( + config = PagingConfig(pageSize = 10), +// remoteMediator = MemoRemoteMediator(repository, memoQueries,query) + ) { + MemoPagingSource(repository,MemoQueryWhere(creatorId = uid)) + + }.flow.cachedIn(screenModelScope) + } + + + + suspend fun setPininned(it: Memo) { + api.updateMemoOrganizer(it.id, UpdateMemoOrganizerInput(!it.pinned)) + } + + suspend fun remove(it: Memo) { + api.deleteMemo(it.id) + } +} diff --git a/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreenModel.kt b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreenModel.kt new file mode 100644 index 0000000..1b9bc14 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreenModel.kt @@ -0,0 +1,50 @@ +package com.github.springeye.memosc.ui.login + + +import cafe.adriel.voyager.core.model.StateScreenModel +import cafe.adriel.voyager.core.model.screenModelScope +import com.github.springeye.memosc.AppPreferences +import com.github.springeye.memosc.MemosApi +import com.github.springeye.memosc.SignInInput +import io.ktor.client.HttpClient +import io.ktor.http.isSuccess +import kotlinx.coroutines.launch + +class LoginScreenModel(private val api: MemosApi, + private val client: HttpClient, + private val prefers: AppPreferences +): StateScreenModel( + State.Init +) { + sealed class State { + object Init : State() + object Loading : State() + data class Result(val isSuccess:Boolean) : State() + } + fun login(host:String,username:String,password:String){ + screenModelScope.launch { + mutableState.value = State.Loading + prefers.host(host) + val input = SignInInput(email = "", username = username, password = password) + + + + +// client.post("api/v1/auth/signin"){ +// contentType(ContentType.Application.Json) +// setBody(input) +// } + val res=api.signIn(input) + //todo handle login success + if(res.status.isSuccess()) { + prefers.host(host) + prefers.username(username) + prefers.password(password) + mutableState.value = State.Result(true) + }else{ + println(res.toString()) + mutableState.value = State.Result(false) + } + } + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/model/CodeLang.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/model/CodeLang.kt new file mode 100644 index 0000000..5db27e0 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/model/CodeLang.kt @@ -0,0 +1,61 @@ +package com.wakaztahir.codeeditor.model + +import com.wakaztahir.codeeditor.prettify.lang.* +import com.wakaztahir.codeeditor.prettify.parser.Prettify + +enum class CodeLang(val langProvider: Prettify.LangProvider?, val value: List) { + Default(null, listOf("default-code")), + HTML(null, listOf("default-markup")), + C(null, listOf("c")), + CPP(null, listOf("cpp")), + ObjectiveC(null, listOf("cxx")), + CSharp(null, listOf("cs")), + Java(null, listOf("java")), + Bash(null, listOf("bash")), + Python(null, listOf("python")), + Perl(null, listOf("perl")), + Ruby(null, listOf("ruby")), + JavaScript(null, listOf("javascript")), + CoffeeScript(null, listOf("coffee")), + Rust(null, listOf("rust")), + OCAML(null, listOf("ml")), + SML(null, listOf("ml")), + FSharp(null, listOf("fs")), + JSON(null,listOf("json")), + XML(null,listOf("xml")), + Proto(null,listOf("proto")), + RegEx(null,listOf("regex")), + Appollo({ LangAppollo() },LangAppollo.fileExtensions), + Basic({ LangBasic() },LangBasic.fileExtensions), + Clojure({ LangClj() },LangClj.fileExtensions), + CSS({ LangCss() },LangCss.fileExtensions), + Dart({ LangDart() },LangDart.fileExtensions), + Erlang({ LangErlang() },LangErlang.fileExtensions), + Go({ LangGo() },LangGo.fileExtensions), + Haskell({ LangHs() },LangHs.fileExtensions), + Lisp({ LangLisp() },LangLisp.fileExtensions), + Llvm({ LangLlvm() },LangLlvm.fileExtensions), + Lua({ LangLua() },LangLua.fileExtensions), + Matlab({ LangMatlab() },LangMatlab.fileExtensions), + ML({ LangMl() },LangMl.fileExtensions), + Mumps({ LangMumps() },LangMumps.fileExtensions), + N({ LangN() },LangN.fileExtensions), + Pascal({ LangPascal() },LangPascal.fileExtensions), + R({ LangR() },LangR.fileExtensions), + Rd({ LangRd() },LangRd.fileExtensions), + Scala({ LangScala() },LangScala.fileExtensions), + SQL({ LangSql() },LangSql.fileExtensions), + Tex({ LangTex() },LangTex.fileExtensions), + VB({ LangVb() },LangVb.fileExtensions), + VHDL({ LangVhdl() },LangVhdl.fileExtensions), + Tcl({ LangTcl() },LangTcl.fileExtensions), + Wiki({ LangWiki() },LangWiki.fileExtensions), + XQuery({ LangXq() },LangXq.fileExtensions), + YAML({ LangYaml() },LangYaml.fileExtensions), + Markdown({ LangMd() },LangMd.fileExtensions), + Ex({ LangEx() },LangEx.fileExtensions), + Kotlin({ LangKotlin() },LangKotlin.fileExtensions), + Lasso({ LangLasso() },LangLasso.fileExtensions), + Logtalk({ LangLogtalk() },LangLogtalk.fileExtensions), + Swift({ LangSwift() },LangSwift.fileExtensions), +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/ParseResult.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/ParseResult.kt new file mode 100644 index 0000000..6b1d959 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/ParseResult.kt @@ -0,0 +1,143 @@ +// Copyright (c) 2012 Chan Wai Shing +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +package com.wakaztahir.codeeditor.parser + +/** + * The parser parsed result. + * + * This class include the information needed to highlight the syntax. + * Information includes where the content located in the document (offset and + * length) and what style(s) should be applied on that segment of content. + * + * @author Chan Wai Shing @gmail.com> + */ +class ParseResult( + /** + * The start position of the content. + */ + var offset: Int, + /** + * The length of the content. + */ + var length: Int, styleKeys: List? +) { + /** + * The start position of the content. + * @return the start position of the content + */ + /** + * The start position of the content. + * @param offset the start position of the content + */ + /** + * The length of the content. + * @return the length of the content + */ + /** + * The length of the content. + * @param length the length of the content + */ + + /** + * The style keys of the content. The style at higher index of the list will + * override the style of the lower index. + */ + private var styleKeys: MutableList + + /** + * Get the style keys represented by one string key, see + * [Theme.getStylesAttributeSet]. + * @return the style keys of the content + */ + val styleKeysString: String + get() { + val sb = StringBuilder(10) + var i = 0 + val iEnd = styleKeys.size + while (i < iEnd) { + if (i != 0) { + sb.append(" ") + } + sb.append(styleKeys[i]) + i++ + } + return sb.toString() + } + + /** + * The style keys of the content. + * @param styleKey the style key + * @return see the return value of [List.add] + */ + fun addStyleKey(styleKey: String): Boolean { + return styleKeys.add(styleKey) + } + + /** + * The style keys of the content. + * @param styleKey the style key + * @return see the return value of [List.remove] + */ + fun removeStyleKey(styleKey: String): Boolean { + return styleKeys.remove(styleKey) + } + + /** + * The style keys of the content. + */ + fun clearStyleKeys() { + styleKeys.clear() + } + + /** + * {@inheritDoc} + */ + override fun toString(): String { + val sb = StringBuilder() + sb.append("[") + sb.append(offset) + sb.append("; ") + sb.append(length) + sb.append("; ") + var i = 0 + val iEnd = styleKeys.size + while (i < iEnd) { + if (i != 0) { + sb.append(", ") + } + sb.append(styleKeys[i]) + i++ + } + sb.append("]") + return sb.toString() + } + + /** + * Constructor. + * + * @param offset the start position of the content + * @param length the length of the content + * @param styleKeys the style keys of the content + */ + init { + this.styleKeys = styleKeys?.toMutableList() ?: mutableListOf() + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/Parser.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/Parser.kt new file mode 100644 index 0000000..0c9bf9f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/parser/Parser.kt @@ -0,0 +1,42 @@ +// Copyright (c) 2012 Chan Wai Shing +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +package com.wakaztahir.codeeditor.parser + +import com.wakaztahir.codeeditor.prettify.parser.Prettify + +/** + * The parser for syntax highlight. + * + * @author Chan Wai Shing @gmail.com> + */ +interface Parser { + /** + * Parse the `content` and return the parsed result. + * + * @param fileExtension the file extension of the content, null means not + * provided + * @param content the content + * @return the parsed result + */ + fun parse(fileExtension: String, content: String): List + fun isSupport(extension:String): Boolean + fun parse(provider: Prettify.LangProvider, content: String): List +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/PrettifyParser.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/PrettifyParser.kt new file mode 100644 index 0000000..38c3d0f --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/PrettifyParser.kt @@ -0,0 +1,46 @@ +package com.wakaztahir.codeeditor.prettify + +import com.wakaztahir.codeeditor.parser.ParseResult +import com.wakaztahir.codeeditor.parser.Parser +import com.wakaztahir.codeeditor.prettify.parser.Job +import com.wakaztahir.codeeditor.prettify.parser.Prettify + + +/** + * The prettify parser for syntax highlight. + * @author Chan Wai Shing @gmail.com> + */ +class PrettifyParser : Parser { + + private val prettify: Prettify = Prettify() + + override fun parse(fileExtension: String, content: String): List = + parse(prettify.getLexerForExtension(fileExtension), content) + + override fun parse(provider: Prettify.LangProvider, content: String): List = + parse(prettify.getLexerForLanguageProvider(provider), content) + + override fun isSupport(extension: String): Boolean { + return prettify.isSupport(extension) + } + + private fun parse(lexer: Prettify.CreateSimpleLexer, content: String): List { + val job = Job(0, content) + lexer.decorate(job) + val decorations = job.decorations + val returnList = ArrayList() + + // apply style according to the style list + var i = 0 + val iEnd = decorations.size + while (i < iEnd) { + val endPos = if (i + 2 < iEnd) decorations[i + 2] as Int else content.length + val startPos = decorations[i] as Int + returnList.add( + ParseResult(startPos, endPos - startPos, listOf((decorations[i + 1] as String))) + ) + i += 2 + } + return returnList + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/Lang.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/Lang.kt new file mode 100644 index 0000000..b428892 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/Lang.kt @@ -0,0 +1,36 @@ +// Copyright (C) 2011 Chan Wai Shing +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.StylePattern + +/** + * Lang class for Java Prettify. + * Note that the method [.getFileExtensions] should be overridden. + * + * @author Chan Wai Shing @gmail.com> + */ +abstract class Lang { + /** + * Similar to those in JavaScript prettify.js. + */ + abstract val shortcutStylePatterns: List + + /** + * Similar to those in JavaScript prettify.js. + */ + abstract val fallthroughStylePatterns: List + + abstract fun getFileExtensions(): List +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangAppollo.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangAppollo.kt new file mode 100644 index 0000000..cf1cdea --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangAppollo.kt @@ -0,0 +1,95 @@ +// Copyright (C) 2009 Onno Hommes. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-appollo.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for the AGC/AEA Assembly Language as described + * at http://virtualagc.googlecode.com + * + * + * This file could be used by goodle code to allow syntax highlight for + * Virtual AGC SVN repository or if you don't want to commonize + * the header for the agc/aea html assembly listing. + * + * @author ohommes@alumni.cmu.edu + */ +class LangAppollo : Lang() { + + companion object { + val fileExtensions = listOf("apollo", "agc", "aea") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // A line comment that starts with ; + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^#[^\r\n]*"), + null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:ADS|AD|AUG|BZF|BZMF|CAE|CAF|CA|CCS|COM|CS|DAS|DCA|DCOM|DCS|DDOUBL|DIM|DOUBLE|DTCB|DTCF|DV|DXCH|EDRUPT|EXTEND|INCR|INDEX|NDX|INHINT|LXCH|MASK|MSK|MP|MSU|NOOP|OVSK|QXCH|RAND|READ|RELINT|RESUME|RETURN|ROR|RXOR|SQUARE|SU|TCR|TCAA|OVSK|TCF|TC|TS|WAND|WOR|WRITE|XCH|XLQ|XXALQ|ZL|ZQ|ADD|ADZ|SUB|SUZ|MPY|MPR|MPZ|DVP|COM|ABS|CLA|CLZ|LDQ|STO|STQ|ALS|LLS|LRS|TRA|TSQ|TMI|TOV|AXT|TIX|DLY|INP|OUT)\\s"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^(?:-?GENADR|=MINUS|2BCADR|VN|BOF|MM|-?2CADR|-?[1-6]DNADR|ADRES|BBCON|[SE]?BANK\\=?|BLOCK|BNKSUM|E?CADR|COUNT\\*?|2?DEC\\*?|-?DNCHAN|-?DNPTR|EQUALS|ERASE|MEMORY|2?OCT|REMADR|SETLOC|SUBRO|ORG|BSS|BES|SYN|EQU|DEFINE|END)\\s"), + null + ) + // A single quote possibly followed by a word that optionally ends with + // = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?") + ) + // Any word including labels that optionally ends with = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex( + "^-*(?:[!-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", + RegexOption.IGNORE_CASE + ) + ) + // A printable non-space non-special character + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+") + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangBasic.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangBasic.kt new file mode 100644 index 0000000..1e210ec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangBasic.kt @@ -0,0 +1,64 @@ +// Contributed by peter dot kofler at code minus cop dot org +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-basic.js in JavaScript Prettify. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my BASIC code)
+ * + * @author peter dot kofler at code minus cop dot org + */ +class LangBasic : Lang() { + + companion object { + val fileExtensions = listOf("basic", "cbm") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // "single-line-string" + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\"(?:[^\\\\\"\\r\\n]|\\\\.)*(?:\"|$))"), + null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^\\s+"), null + ) + + // A line comment that starts with REM + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^REM[^\\r\\n]*"), null + ) + + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(?:AND|CLOSE|CLR|CMD|CONT|DATA|DEF ?FN|DIM|END|FOR|GET|GOSUB|GOTO|IF|INPUT|LET|LIST|LOAD|NEW|NEXT|NOT|ON|OPEN|OR|POKE|PRINT|READ|RESTORE|RETURN|RUN|SAVE|STEP|STOP|SYS|THEN|TO|VERIFY|WAIT)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[A-Z][A-Z0-9]?(?:\\$|%)?", RegexOption.IGNORE_CASE), null + ) + // Literals .0, 0, 0.0 0E13 + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+\\-]?\\d+)?", RegexOption.IGNORE_CASE), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^.[^\\s\\w\\.$%\"]*"), null + ) + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangClj.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangClj.kt new file mode 100644 index 0000000..3bc3671 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangClj.kt @@ -0,0 +1,100 @@ +/** + * @license Copyright (C) 2011 Google Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-clj.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for Clojure. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my lisp code)
+ * The lang-cl class identifies the language as common lisp. + * This file supports the following language extensions: + * lang-clj - Clojure + * + * + * I used lang-lisp.js as the basis for this adding the clojure specific + * keywords and syntax. + * + * "Name" = 'Clojure' + * "Author" = 'Rich Hickey' + * "Version" = '1.2' + * "About" = 'Clojure is a lisp for the jvm with concurrency primitives and a richer set of types.' + * + * + * I used [Clojure.org Reference](http://clojure.org/Reference) as + * the basis for the reserved word list. + * + * + * @author jwall@google.com + */ +class LangClj : Lang() { + companion object { + val fileExtensions: List + get() = listOf("clj") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // clojure has more paren types than minimal lisp. + shortcutStylePatterns.new("opn", Regex("^[\\(\\{\\[]+"), null) + shortcutStylePatterns.new("clo", Regex("^[\\)\\}\\]]+"), null) + // A line comment that starts with ; + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^;[^\r\n]*"), + null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + // clojure has a much larger set of keywords + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:def|if|do|let|quote|var|fn|loop|recur|throw|try|monitor-enter|monitor-exit|defmacro|defn|defn-|macroexpand|macroexpand-1|for|doseq|dosync|dotimes|and|or|when|not|assert|doto|proxy|defstruct|first|rest|cons|defprotocol|deftype|defrecord|reify|defmulti|defmethod|meta|with-meta|ns|in-ns|create-ns|import|intern|refer|alias|namespace|resolve|ref|deref|refset|new|set!|memfn|to-array|into-array|aset|gen-class|reduce|map|filter|find|nil?|empty?|hash-map|hash-set|vec|vector|seq|flatten|reverse|assoc|dissoc|list|list?|disj|get|union|difference|intersection|extend|extend-type|extend-protocol|prn)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^:[0-9a-zA-Z\\-]+") + ) + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangCss.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangCss.kt new file mode 100644 index 0000000..2ed5770 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangCss.kt @@ -0,0 +1,171 @@ +// Copyright (C) 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-css.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for CSS. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *

+ *
+ *
+ * http://www.w3.org/TR/CSS21/grammar.html Section G2 defines the lexical
+ * grammar.  This scheme does not recognize keywords containing escapes.
+ *
+ * @author mikesamuel@gmail.com
+ */
+class LangCss : Lang() {
+    companion object {
+        val fileExtensions: List
+            get() = listOf("css")
+    }
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+    init {
+
+        // The space production 
+        shortcutStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex("^[ \t\r\n\\f]+"),
+            null
+        )
+        // Quoted strings.   and 
+        fallthroughStylePatterns.new(
+            Prettify.PR_STRING,
+            Regex("^\\\"(?:[^\n\r\\f\\\\\\\"]|\\\\(?:\r\n?|\n|\\f)|\\\\[\\s\\S])*\\\""),
+            null
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_STRING,
+            Regex("^\\'(?:[^\n\r\\f\\\\\\']|\\\\(?:\r\n?|\n|\\f)|\\\\[\\s\\S])*\\'"),
+            null
+        )
+        fallthroughStylePatterns.new(
+            "lang-css-str",
+            Regex("^url\\(([^\\)\\\"\\']+)\\)", RegexOption.IGNORE_CASE)
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_KEYWORD,
+            Regex(
+                "^(?:url|rgb|\\!important|@import|@page|@media|@charset|inherit)(?=[^\\-\\w]|$)",
+                RegexOption.IGNORE_CASE
+            ),
+            null
+        )
+        // A property name -- an identifier followed by a colon.
+        fallthroughStylePatterns.new(
+            "lang-css-kw",
+            Regex(
+                "^(-?(?:[_a-z]|(?:\\\\[0-9a-f]+ ?))(?:[_a-z0-9\\-]|\\\\(?:\\\\[0-9a-f]+ ?))*)\\s*:",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        // A C style block comment.  The  production.
+        fallthroughStylePatterns.new(
+            Prettify.PR_COMMENT,
+            Regex("^\\/\\*[^*]*\\*+(?:[^\\/*][^*]*\\*+)*\\/")
+        )
+        // Escaping text spans
+        fallthroughStylePatterns.new(
+            Prettify.PR_COMMENT,
+            Regex("^(?:)")
+        )
+        // A number possibly containing a suffix.
+        fallthroughStylePatterns.new(
+            Prettify.PR_LITERAL,
+            Regex("^(?:\\d+|\\d*\\.\\d+)(?:%|[a-z]+)?", RegexOption.IGNORE_CASE)
+        )
+        // A hex color
+        fallthroughStylePatterns.new(
+            Prettify.PR_LITERAL,
+            Regex("^#(?:[0-9a-f]{3}){1,2}\\b", RegexOption.IGNORE_CASE)
+        )
+        // An identifier
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex(
+                "^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        // A run of punctuation
+        fallthroughStylePatterns.new(
+            Prettify.PR_PUNCTUATION,
+            Regex("^[^\\s\\w\\'\\\"]+", RegexOption.IGNORE_CASE)
+        )
+    }
+
+    override fun getFileExtensions(): List {
+        return fileExtensions
+    }
+
+    class LangCssKeyword : Lang() {
+        companion object {
+            val fileExtensions: List
+                get() = listOf("css-kw")
+        }
+
+        override val fallthroughStylePatterns = ArrayList()
+        override val shortcutStylePatterns = ArrayList()
+
+        init {
+            fallthroughStylePatterns.new(
+                Prettify.PR_KEYWORD,
+                Regex(
+                    "^-?(?:[_a-z]|(?:\\\\[\\da-f]+ ?))(?:[_a-z\\d\\-]|\\\\(?:\\\\[\\da-f]+ ?))*",
+                    RegexOption.IGNORE_CASE
+                )
+            )
+        }
+
+        override fun getFileExtensions(): List {
+            return fileExtensions
+        }
+    }
+
+    class LangCssString : Lang() {
+        companion object {
+            val fileExtensions: List = listOf("css-str")
+        }
+
+        override val fallthroughStylePatterns = ArrayList()
+        override val shortcutStylePatterns = ArrayList()
+
+        init {
+            fallthroughStylePatterns.new(
+                Prettify.PR_STRING,
+                Regex("^[^\\)\\\"\\']+")
+            )
+        }
+
+        override fun getFileExtensions(): List {
+            return fileExtensions
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangDart.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangDart.kt
new file mode 100644
index 0000000..2560a65
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangDart.kt
@@ -0,0 +1,164 @@
+/**
+ * @license Copyright (C) 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.wakaztahir.codeeditor.prettify.lang
+
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+import com.wakaztahir.codeeditor.utils.new
+
+/**
+ * This is similar to the lang-dart.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Registers a language handler for Dart.
+ *
+ *
+ * Loosely structured based on the DartLexer in Pygments: http://pygments.org/.
+ *
+ * To use, include prettify.js and this file in your HTML page.
+ * Then put your code in an HTML tag like
+ * 
(Dart code)
+ * + * @author armstrong.timothy@gmail.com + */ +class LangDart : Lang() { + companion object { + val fileExtensions: List + get() = listOf(("dart")) + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // Whitespace. + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + + // Script tag. + fallthroughStylePatterns.new(Prettify.PR_COMMENT, Regex("^#!(?:.*)")) + // `import`, `library`, `part of`, `part`, `as`, `show`, and `hide` + // keywords. + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(?:import|library|part of|part|as|show|hide)\\b", RegexOption.IGNORE_CASE) + ) + // Single-line comments. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\/(?:.*)") + ) + // Multiline comments. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\*[^*]*\\*+(?:[^\\/*][^*]*\\*+)*\\/") + ) + // `class` and `interface` keywords. + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(?:class|interface)\\b", RegexOption.IGNORE_CASE) + ) + // General keywords. + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex( + "^\\b(?:assert|break|case|catch|continue|default|do|else|finally|for|if|in|is|new|return|super|switch|this|throw|try|while)\\b", + RegexOption.IGNORE_CASE + ) + ) + // Declaration keywords. + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex( + "^\\b(?:abstract|const|extends|factory|final|get|implements|native|operator|set|static|typedef|var)\\b", + RegexOption.IGNORE_CASE + ) + ) + // Keywords for types. + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex( + "^\\b(?:bool|double|Dynamic|int|num|Object|String|void)\\b", + RegexOption.IGNORE_CASE + ) + ) + // Keywords for constants. + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("\\b(?:false|null|true)\\b", RegexOption.IGNORE_CASE) + ) + // Multiline strings, single- and double-quoted. + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^r?[\\']{3}[\\s|\\S]*?[^\\\\][\\']{3}") + ) + + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^r?[\\\"]{3}[\\s|\\S]*?[^\\\\][\\\"]{3}") + ) + + // Normal and raw strings, single- and double-quoted. + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^r?\\'(\\'|(?:[^\\n\\r\\f])*?[^\\\\]\\')") + ) + + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^r?\\\"(\\\"|(?:[^\\n\\r\\f])*?[^\\\\]\\\")") + ) + // Identifiers. + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z_$][a-z0-9_]*", RegexOption.IGNORE_CASE) + ) + // Operators. + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[~!%^&*+=|?:<>/-]") + ) + // Hex numbers. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\b0x[0-9a-f]+", RegexOption.IGNORE_CASE) + ) + // Decimal numbers. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\b\\d+(?:\\.\\d*)?(?:e[+-]?\\d+)?", RegexOption.IGNORE_CASE) + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\b\\.\\d+(?:e[+-]?\\d+)?", RegexOption.IGNORE_CASE) + ) + // Punctuation. + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[(){}\\[\\],.;]") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangErlang.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangErlang.kt new file mode 100644 index 0000000..b4804a9 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangErlang.kt @@ -0,0 +1,137 @@ +// Copyright (C) 2013 Andrew Allen +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-erlang.js in JavaScript Prettify. + * + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * + * + * + * Derived from https://raw.github.com/erlang/otp/dev/lib/compiler/src/core_parse.yrl + * Modified from Mike Samuel's Haskell plugin for google-code-prettify + * + * @author achew22@gmail.com + */ +class LangErlang : Lang() { + companion object { + val fileExtensions: List + get() = listOf("erlang", "erl") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + // Whitespace + // whitechar -> newline | vertab | space | tab | uniWhite + // newline -> return linefeed | return | linefeed | formfeed + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("\\t\\n\\x0B\\x0C\\r ]+"), null + ) + // Single line double-quoted strings. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\\\n\\x0C\\r]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + + // Handle atoms + shortcutStylePatterns.new( + Prettify.PR_LITERAL, Regex("^[a-z][a-zA-Z0-9_]*") + ) + // Handle single quoted atoms + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\'(?:[^\\'\\\\\\n\\x0C\\r]|\\\\[^&])+\\'?"), + null + ) + + // Handle macros. Just to be extra clear on this one, it detects the ? + // then uses the regexp to end it so be very careful about matching + // all the terminal elements + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\?[^ \\t\\n({]+"), + null + ) + + // decimal -> digit{digit} + // octal -> octit{octit} + // hexadecimal -> hexit{hexit} + // integer -> decimal + // | 0o octal | 0O octal + // | 0x hexadecimal | 0X hexadecimal + // float -> decimal . decimal [exponent] + // | decimal exponent + // exponent -> (e | E) [+ | -] decimal + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^(?:0o[0-7]+|0x[\\da-f]+|\\d+(?:\\.\\d+)?(?:e[+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ), + null + ) + + + // TODO: catch @declarations inside comments + + // Comments in erlang are started with % and go till a newline + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^%[^\\n\\r]*") + ) + + // Catch macros + //[PR['PR_TAG'], /?[^( \n)]+/], + /** + * %% Keywords (atoms are assumed to always be single-quoted). + * 'module' 'attributes' 'do' 'let' 'in' 'letrec' + * 'apply' 'call' 'primop' + * 'case' 'of' 'end' 'when' 'fun' 'try' 'catch' 'receive' 'after' + */ + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:module|attributes|do|let|in|letrec|apply|call|primop|case|of|end|when|fun|try|catch|receive|after|char|integer|float,atom,string,var)\\b") + ) + /** + * Catch definitions (usually defined at the top of the file) + * Anything that starts -something + */ + fallthroughStylePatterns.new(Prettify.PR_KEYWORD, Regex("^-[a-z_]+")) + + // Catch variables + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^[A-Z_][a-zA-Z0-9_]*") + ) + + // matches the symbol production + fallthroughStylePatterns.new(Prettify.PR_PUNCTUATION, Regex("^[.,;]")) + } + + override fun getFileExtensions(): List = fileExtensions + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangEx.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangEx.kt new file mode 100644 index 0000000..a7413be --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangEx.kt @@ -0,0 +1,122 @@ +package com.wakaztahir.codeeditor.prettify.lang + + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + + +class LangEx : Lang() { + + companion object { + val fileExtensions = listOf("ex", "exs") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[\\t\\n\\r \\xA0]+"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^#.*"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^'(?:[^'\\\\]|\\\\(?:.|\\n|\\r))*'?"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_ATTRIB_NAME, + Regex("^@\\w+"), + null + ) + shortcutStylePatterns.new( + + Prettify.PR_PUNCTUATION, + Regex("^[!%&()*+,\\-;<=>?\\[\\\\\\]^{|}]+"), + null + + ) + shortcutStylePatterns.new( + + Prettify.PR_LITERAL, + Regex("^(?:0o[0-7](?:[0-7]|_[0-7])*|0x[\\da-fA-F](?:[\\da-fA-F]|_[\\da-fA-F])*|\\d(?:\\d|_\\d)*(?:\\.\\d(?:\\d|_\\d)*)?(?:[eE][+\\-]?\\d(?:\\d|_\\d)*)?)"), + null + + ) + fallthroughStylePatterns.new( + Prettify.PR_ATTRIB_NAME, + Regex("^iex(?:\\(\\d+\\))?> ") + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^::"), + null + + ) + fallthroughStylePatterns.new( + + Prettify.PR_LITERAL, + Regex("^:(?:\\w+[\\!\\?\\@]?|\"(?:[^\"\\\\]|\\\\.)*\"?)") + ) + fallthroughStylePatterns.new( + + Prettify.PR_ATTRIB_NAME, + Regex("^(?:__(?:CALLER|ENV|MODULE|DIR)__)") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:alias|case|catch|def(?:delegate|exception|impl|macrop?|module|overridable|p?|protocol|struct)|do|else|end|fn|for|if|in|import|quote|raise|require|rescue|super|throw|try|unless|unquote(?:_splicing)?|use|when|with|yield)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:true|false|nil)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:\\w+[\\!\\?\\@]?|\"(?:[^\"\\\\]|\\\\.)*\"):(?!:)") + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^\"\"\"\\s*(\\r|\\n)+(?:\"\"?(?!\")|[^\\\\\"]|\\\\(?:.|\\n|\\r))*\"{0,3}") + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^\"(?:[^\"\\\\]|\\\\(?:.|\\n|\\r))*\"?(?!\")") + ) + fallthroughStylePatterns.new(Prettify.PR_TYPE, Regex("^[A-Z]\\w*")) + fallthroughStylePatterns.new(Prettify.PR_COMMENT, Regex("^_\\w*")) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[\$a-z]\\w*[\\!\\?]?") + ) + fallthroughStylePatterns.new( + Prettify.PR_ATTRIB_VALUE, + Regex( + "^~[A-Z](?:\\/(?:[^\\/\\r\\n\\\\]|\\\\.)+\\/|\\|(?:[^\\|\\r\\n\\\\]|\\\\.)+\\||\"(?:[^\"\\r\\n\\\\]|\\\\.)+\"|'(?:[^'\\r\\n\\\\]|\\\\.)+')[A-Z]*", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_ATTRIB_VALUE, + Regex( + "^~[A-Z](?:\\((?:[^\\)\\r\\n\\\\]|\\\\.)+\\)|\\[(?:[^\\]\\r\\n\\\\]|\\\\.)+\\]|\\{(?:[^\\}\\r\\n\\\\]|\\\\.)+\\}|\\<(?:[^\\>\\r\\n\\\\]|\\\\.)+\\>)[A-Z]*", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^(?:\\.+|\\/|[:~])") + ) + + + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangGo.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangGo.kt new file mode 100644 index 0000000..ae7dcbf --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangGo.kt @@ -0,0 +1,89 @@ +// Copyright (C) 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-go.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for the Go language.. + * + * + * Based on the lexical grammar at + * http://golang.org/doc/go_spec.html#Lexical_elements + * + * + * Go uses a minimal style for highlighting so the below does not distinguish + * strings, keywords, literals, etc. by design. + * From a discussion with the Go designers: + *
+ * On Thursday, July 22, 2010, Mike Samuel <...> wrote:
+ * > On Thu, Jul 22, 2010, Rob 'Commander' Pike <...> wrote:
+ * >> Personally, I would vote for the subdued style godoc presents at http://golang.org
+ * >>
+ * >> Not as fancy as some like, but a case can be made it's the official style.
+ * >> If people want more colors, I wouldn't fight too hard, in the interest of
+ * >> encouragement through familiarity, but even then I would ask to shy away
+ * >> from technicolor starbursts.
+ * >
+ * > Like http://golang.org/pkg/go/scanner/ where comments are blue and all
+ * > other content is black?  I can do that.
+
* + * + * @author mikesamuel@gmail.com + */ +class LangGo : Lang() { + companion object { + val fileExtensions: List + get() = listOf("go") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + + // Whitespace is made up of spaces, tabs and newline characters. + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // Not escaped as a string. See note on minimalism above. + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])+(?:\\'|$)|`[^`]*(?:`|$))"), + null + ) + // Block comments are delimited by /* and */. + // Single-line comments begin with // and extend to the end of a line. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^(?:\\/\\/[^\\r\\n]*|\\/\\*[\\s\\S]*?\\*\\/)") + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^(?:[^\\/\\\"\\'`]|\\/(?![\\/\\*]))+", RegexOption.IGNORE_CASE) + ) + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangHs.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangHs.kt new file mode 100644 index 0000000..f317d81 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangHs.kt @@ -0,0 +1,146 @@ +// Copyright (C) 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-hs.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for Haskell. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my lisp code)
+ * The lang-cl class identifies the language as common lisp. + * This file supports the following language extensions: + * lang-cl - Common Lisp + * lang-el - Emacs Lisp + * lang-lisp - Lisp + * lang-scm - Scheme + * + * + * I used http://www.informatik.uni-freiburg.de/~thiemann/haskell/haskell98-report-html/syntax-iso.html + * as the basis, but ignore the way the ncomment production nests since this + * makes the lexical grammar irregular. It might be possible to support + * ncomments using the lookbehind filter. + * + * + * @author mikesamuel@gmail.com + */ +class LangHs : Lang() { + companion object { + val fileExtensions: List + get() = listOf(("hs")) + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // Whitespace + // whitechar -> newline | vertab | space | tab | uniWhite + // newline -> return linefeed | return | linefeed | formfeed + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\x0B\\x0C\\r ]+"), null + ) + // Single line double and single-quoted strings. + // char -> ' (graphic<' | \> | space | escape<\&>) ' + // string -> " {graphic<" | \> | space | escape | gap}" + // escape -> \ ( charesc | ascii | decimal | o octal + // | x hexadecimal ) + // charesc -> a | b | f | n | r | t | v | \ | " | ' | & + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\\\n\\x0C\\r]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\'(?:[^\\'\\\\\\n\\x0C\\r]|\\\\[^&])\\'?"), + null + ) + // decimal -> digit{digit} + // octal -> octit{octit} + // hexadecimal -> hexit{hexit} + // integer -> decimal + // | 0o octal | 0O octal + // | 0x hexadecimal | 0X hexadecimal + // float -> decimal . decimal [exponent] + // | decimal exponent + // exponent -> (e | E) [+ | -] decimal + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^(?:0o[0-7]+|0x[\\da-f]+|\\d+(?:\\.\\d+)?(?:e[+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ), + null + ) + // Haskell does not have a regular lexical grammar due to the nested + // ncomment. + // comment -> dashes [ any {any}] newline + // ncomment -> opencom ANYseq {ncomment ANYseq}closecom + // dashes -> '--' {'-'} + // opencom -> '{-' + // closecom -> '-}' + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^(?:(?:--+(?:[^\\r\\n\\x0C]*)?)|(?:\\{-(?:[^-]|-+[^-\\}])*-\\}))") + ) + // reservedid -> case | class | data | default | deriving | do + // | else | if | import | in | infix | infixl | infixr + // | instance | let | module | newtype | of | then + // | type | where | _ + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:case|class|data|default|deriving|do|else|if|import|in|infix|infixl|infixr|instance|let|module|newtype|of|then|type|where|_)(?=[^a-zA-Z0-9\\']|$)"), + null + ) + // qvarid -> [ modid . ] varid + // qconid -> [ modid . ] conid + // varid -> (small {small | large | digit | ' }) + // conid -> large {small | large | digit | ' } + // modid -> conid + // small -> ascSmall | uniSmall | _ + // ascSmall -> a | b | ... | z + // uniSmall -> any Unicode lowercase letter + // large -> ascLarge | uniLarge + // ascLarge -> A | B | ... | Z + // uniLarge -> any uppercase or titlecase Unicode letter + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^(?:[A-Z][\\w\\']*\\.)*[a-zA-Z][\\w\\']*") + ) + // matches the symbol production + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[^\\t\\n\\x0B\\x0C\\r a-zA-Z0-9\\'\\\"]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangKotlin.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangKotlin.kt new file mode 100644 index 0000000..3dc1554 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangKotlin.kt @@ -0,0 +1,72 @@ +package com.wakaztahir.codeeditor.prettify.lang + + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +class LangKotlin : Lang() { + + companion object { + val fileExtensions = listOf("kt", "kotlin") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[\\t\\n\\r \\xA0]+"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[.!%&()*+,\\-;<=>?\\[\\\\\\]^{|}:]+"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(package|public|protected|external|internal|private|open|abstract|constructor|final|override|import|for|while|as|typealias|get|set|((data|enum|annotation|sealed) )?class|this|super|val|var|fun|is|in|throw|return|break|continue|(companion )?object|if|try|else|do|when|init|interface|typeof)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:true|false|null)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(0[xX][0-9a-fA-F_]+L?|0[bB][0-1]+L?|[0-9_.]+([eE]-?[0-9]+)?[fFL]?)") + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^(\\b[A-Z]+[a-z][a-zA-Z0-9_$@]*|`.*`)"), + null + ) + fallthroughStylePatterns.new(Prettify.PR_COMMENT, Regex("^\\/\\/.*")) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)") + ) + fallthroughStylePatterns.new(Prettify.PR_STRING, Regex("'.'")) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^\"([^\"\\\\]|\\\\[\\s\\S])*\"") + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^\"{3}[\\s\\S]*?[^\\\\]\"{3}") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^@([a-zA-Z0-9_$@]*|`.*`)") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^[a-zA-Z0-9_]+@") + ) + + + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLasso.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLasso.kt new file mode 100644 index 0000000..5c46d85 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLasso.kt @@ -0,0 +1,107 @@ +package com.wakaztahir.codeeditor.prettify.lang + + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +class LangLasso : Lang() { + + companion object { + val fileExtensions = listOf("lasso", "ls", "lassoscript") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[\\t\\n\\r \\xA0]+"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\'[^\\'\\\\]*(?:\\\\[\\s\\S][^\\'\\\\]*)*(?:\\'|$)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"[^\\\"\\\\]*(?:\\\\[\\s\\S][^\\\"\\\\]*)*(?:\\\"|$)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\`[^\\`]*(?:\\`|$)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^0x[\\da-f]+|\\d+", RegexOption.IGNORE_CASE), + null + ) + shortcutStylePatterns.new( + Prettify.PR_ATTRIB_NAME, + Regex("^[#$][a-z_][\\w.]*|#\\d+\\b|#![ \\S]+lasso9\\b", RegexOption.IGNORE_CASE), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TAG, + Regex( + "^[\\[\\]]|<\\?(?:lasso(?:script)?|=)|\\?>|(no_square_brackets|noprocess)\\b", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\/[^\\r\\n]*|\\/\\*[\\s\\S]*?\\*\\/") + ) + fallthroughStylePatterns.new( + Prettify.PR_ATTRIB_NAME, + Regex( + "^-(?!infinity)[a-z_][\\w.]*|\\.\\s*'[a-z_][\\w.]*'|\\.{3}", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\d*\\.\\d+(?:e[-+]?\\d+)?|(infinity|NaN)\\b", RegexOption.IGNORE_CASE) + ) + fallthroughStylePatterns.new( + Prettify.PR_ATTRIB_VALUE, + Regex("^::\\s*[a-z_][\\w.]*", RegexOption.IGNORE_CASE) + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^(?:true|false|none|minimal|full|all|void|and|or|not|bw|nbw|ew|new|cn|ncn|lt|lte|gt|gte|eq|neq|rx|nrx|ft)\\b", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex( + "^(?:array|date|decimal|duration|integer|map|pair|string|tag|xml|null|boolean|bytes|keyword|list|locale|queue|set|stack|staticarray|local|var|variable|global|data|self|inherited|currentcapture|givenblock)\\b|^\\.\\.?", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, Regex( + "^(?:cache|database_names|database_schemanames|database_tablenames|define_tag|define_type|email_batch|encode_set|html_comment|handle|handle_error|header|if|inline|iterate|ljax_target|link|link_currentaction|link_currentgroup|link_currentrecord|link_detail|link_firstgroup|link_firstrecord|link_lastgroup|link_lastrecord|link_nextgroup|link_nextrecord|link_prevgroup|link_prevrecord|log|loop|namespace_using|output_none|portal|private|protect|records|referer|referrer|repeating|resultset|rows|search_args|search_arguments|select|sort_args|sort_arguments|thread_atomic|value_list|while|abort|case|else|fail_if|fail_ifnot|fail|if_empty|if_false|if_null|if_true|loop_abort|loop_continue|loop_count|params|params_up|return|return_value|run_children|soap_definetag|soap_lastrequest|soap_lastresponse|tag_name|ascending|average|by|define|descending|do|equals|frozen|group|handle_failure|import|in|into|join|let|match|max|min|on|order|parent|protected|provide|public|require|returnhome|skip|split_thread|sum|take|thread|to|trait|type|where|with|yield|yieldhome)\\b", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z_][\\w.]*(?:=\\s*(?=\\())?", RegexOption.IGNORE_CASE) + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^:=|[-+*\\/%=<>&|!?\\\\]+") + ) + + + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLisp.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLisp.kt new file mode 100644 index 0000000..edc3868 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLisp.kt @@ -0,0 +1,143 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-lisp.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for Common Lisp and related languages. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my lisp code)
+ * The lang-cl class identifies the language as common lisp. + * This file supports the following language extensions: + * lang-cl - Common Lisp + * lang-el - Emacs Lisp + * lang-lisp - Lisp + * lang-scm - Scheme + * lang-lsp - FAT 8.3 filename version of lang-lisp. + * + * + * I used http://www.devincook.com/goldparser/doc/meta-language/grammar-LISP.htm + * as the basis, but added line comments that start with ; and changed the atom + * production to disallow unquoted semicolons. + * + * "Name" = 'LISP' + * "Author" = 'John McCarthy' + * "Version" = 'Minimal' + * "About" = 'LISP is an abstract language that organizes ALL' + * | 'data around "lists".' + * + * "Start Symbol" = [s-Expression] + * + * {Atom Char} = {Printable} - {Whitespace} - [()"\''] + * + * Atom = ( {Atom Char} | '\'{Printable} )+ + * + * [s-Expression] ::= [Quote] Atom + * | [Quote] '(' [Series] ')' + * | [Quote] '(' [s-Expression] '.' [s-Expression] ')' + * + * [Series] ::= [s-Expression] [Series] + * | + * + * [Quote] ::= '' !Quote = do not evaluate + * | + * + * + * I used [Practical Common Lisp](http://gigamonkeys.com/book/) as + * the basis for the reserved word list. + * + * + * @author mikesamuel@gmail.com + */ +class LangLisp : Lang() { + companion object { + val fileExtensions: List + get() = listOf("cl", "el", "lisp", "lsp", "scm", "ss", "rkt") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + shortcutStylePatterns.new("opn", Regex("^\\(+"), null) + shortcutStylePatterns.new("clo", Regex("^\\)+"), null) + // A line comment that starts with ; + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^;[^\r\n]*"), + null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex( + "^(?:block|c[ad]+r|catch|con[ds]|def(?:ine|un)|do|eq|eql|equal|equalp|eval-when|flet|format|go|if|labels|lambda|let|load-time-value|locally|macrolet|multiple-value-call|nil|progn|progv|quote|require|return-from|setq|symbol-macrolet|t|tagbody|the|throw|unwind)\\b", + RegexOption.IGNORE_CASE + ), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex( + "^[+\\-]?(?:[0#]x[0-9a-f]+|\\d+\\/\\d+|(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:[ed][+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ) + ) + // A single quote possibly followed by a word that optionally ends with + // = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?") + ) + // A word that optionally ends with = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex( + "^-*(?:[a-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?", + RegexOption.IGNORE_CASE + ) + ) + // A printable non-space non-special character + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLlvm.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLlvm.kt new file mode 100644 index 0000000..8f09f26 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLlvm.kt @@ -0,0 +1,100 @@ +// Copyright (C) 2013 Nikhil Dabas +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-ml.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * Registers a language handler for LLVM. From + * https://gist.github.com/ndabas/2850418 + * + * + * To use, include prettify.js and this file in your HTML page. Then put your + * code in an HTML tag like
(my LLVM code)
+ * + * + * The regular expressions were adapted from: + * https://github.com/hansstimer/llvm.tmbundle/blob/76fedd8f50fd6108b1780c51d79fbe3223de5f34/Syntaxes/LLVM.tmLanguage + * + * http://llvm.org/docs/LangRef.html#constants describes the language grammar. + * + * @author Nikhil Dabas + */ +class LangLlvm : Lang() { + companion object { + val fileExtensions: List + get() = listOf("llvm", "ll") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^!?\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + // comment.llvm + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^;[^\r\n]*"), + null + ) + // variable.llvm + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[%@!](?:[-a-zA-Z$._][-a-zA-Z$._0-9]*|\\d+)") + ) + // According to http://llvm.org/docs/LangRef.html#well-formedness + // These reserved words cannot conflict with variable names, because none of them start with a prefix character ('%' or '@'). + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^[A-Za-z_][0-9A-Za-z_]*"), + null + ) + // constant.numeric.float.llvm + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^\\d+\\.\\d+") + ) + // constant.numeric.integer.llvm + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^(?:\\d+|0[xX][a-fA-F0-9]+)") + ) + // punctuation + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^[()\\[\\]{},=*<>:]|\\.\\.\\.$") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLogtalk.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLogtalk.kt new file mode 100644 index 0000000..037acf8 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLogtalk.kt @@ -0,0 +1,69 @@ +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +class LangLogtalk : Lang() { + + companion object { + val fileExtensions = listOf("logtalk", "lgt") + } + + override fun getFileExtensions(): List = fileExtensions + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\\"(?:[^\\\"\\\\\\n\\x0C\\r]|\\\\[\\s\\S])*(?:\\\"|$)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^[a-z][a-zA-Z0-9_]*") + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\'(?:[^\\'\\\\\\n\\x0C\\r]|\\\\[^&])+\\'?"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^(?:0'.|0b[0-1]+|0o[0-7]+|0x[\\da-f]+|\\d+(?:\\.\\d+)?(?:e[+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^%[^\\r\\n]*"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\*[\\s\\S]*?\\*\\/") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\s*:-\\s(c(a(lls|tegory)|oinductive)|p(ublic|r(ot(ocol|ected)|ivate))|e(l(if|se)|n(coding|sure_loaded)|xport)|i(f|n(clude|itialization|fo))|alias|d(ynamic|iscontiguous)|m(eta_(non_terminal|predicate)|od(e|ule)|ultifile)|reexport|s(et_(logtalk|prolog)_flag|ynchronized)|o(bject|p)|use(s|_module))") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\s*:-\\s(e(lse|nd(if|_(category|object|protocol)))|built_in|dynamic|synchronized|threaded)") + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^[A-Z_][a-zA-Z0-9_]*") + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[.,;{}:^<>=\\\\/+*?#!-]") + ) + + + } +} diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLua.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLua.kt new file mode 100644 index 0000000..38aeaec --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangLua.kt @@ -0,0 +1,103 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-lua.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for Lua. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my Lua code)
+ * + * + * I used http://www.lua.org/manual/5.1/manual.html#2.1 + * Because of the long-bracket concept used in strings and comments, Lua does + * not have a regular lexical grammar, but luckily it fits within the space + * of irregular grammars supported by javascript regular expressions. + * + * @author mikesamuel@gmail.com + */ +class LangLua : Lang() { + companion object { + val fileExtensions: List + get() = listOf("lua") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double or single quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])*(?:\\'|$))"), + null + ) + // A comment is either a line comment that starts with two dashes, or + // two dashes preceding a long bracketed block. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^--(?:\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)|[^\\r\\n]*)") + ) + // A long bracketed block not preceded by -- is a string. + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^\\[(=*)\\[[\\s\\S]*?(?:\\]\\1\\]|$)") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:and|break|do|else|elseif|end|false|for|function|if|in|local|nil|not|or|repeat|return|then|true|until|while)\\b"), + null + ) + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", + RegexOption.IGNORE_CASE + ) + ) + // An identifier + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z_]\\w*", RegexOption.IGNORE_CASE) + ) + // A run of punctuation + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[^\\w\\t\\n\\r \\xA0][^\\w\\n\\r \\xA0\\\"\\'\\-\\+=]*") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMatlab.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMatlab.kt new file mode 100644 index 0000000..4242c77 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMatlab.kt @@ -0,0 +1,283 @@ +// Copyright (c) 2013 by Amro +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-ml.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * 0 Registers a language handler for MATLAB. + * + * To use, include prettify.js and this file in your HTML page. Then put your + * code inside an HTML tag like
+
* + * + * @see https://github.com/amroamroamro/prettify-matlab + */ +class LangMatlab : Lang() { + + companion object { + const val PR_IDENTIFIER = "ident" + const val PR_CONSTANT = "const" + const val PR_FUNCTION = "fun" + const val PR_FUNCTION_TOOLBOX = "fun_tbx" + const val PR_SYSCMD = "syscmd" + const val PR_CODE_OUTPUT = "codeoutput" + const val PR_ERROR = "err" + const val PR_WARNING = "wrn" + const val PR_TRANSPOSE = "transpose" + const val PR_LINE_CONTINUATION = "linecont" + val fileExtensions: List + get() = listOf("matlab") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // identifiers: variable/function name, or a chain of variable names joined by dots (obj.method, struct.field1.field2, etc..) + // valid variable names (start with letter, and contains letters, digits, and underscores). + // we match "xx.yy" as a whole so that if "xx" is plain and "yy" is not, we dont get a false positive for "yy" + //var reIdent = '(?:[a-zA-Z][a-zA-Z0-9_]*)'; + //var reIdentChain = '(?:' + reIdent + '(?:\.' + reIdent + ')*' + ')'; + + // patterns that always start with a known character. Must have a shortcut string. + // whitespaces: space, tab, carriage return, line feed, line tab, form-feed, non-break space + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[ \\t\\r\\n\\v\\f\\xA0]+"), + null + ) + // block comments + //TODO: chokes on nested block comments + //TODO: false positives when the lines with %{ and %} contain non-spaces + //[PR.PR_COMMENT, /^%(?:[^\{].*|\{(?:%|%*[^\}%])*(?:\}+%?)?)/, null], + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^%\\{[^%]*%+(?:[^\\}%][^%]*%+)*\\}"), + null + ) + + // single-line comments + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^%[^\\r\\n]*"), + null + ) + // system commands + shortcutStylePatterns.new(PR_SYSCMD, Regex("^![^\\r\\n]*"), null) + + // patterns that will be tried in order if the shortcut ones fail. May have shortcuts. + // line continuation + fallthroughStylePatterns.new( + PR_LINE_CONTINUATION, + Regex("^\\.\\.\\.\\s*[\\r\\n]"), + null + ) + // error message + fallthroughStylePatterns.new( + PR_ERROR, + Regex("^\\?\\?\\? [^\\r\\n]*"), + null + ) + // warning message + fallthroughStylePatterns.new( + PR_WARNING, + Regex("^Warning: [^\\r\\n]*"), + null + ) + // command prompt/output + //[PR_CODE_OUTPUT, /^>>\s+[^\r\n]*[\r\n]{1,2}[^=]*=[^\r\n]*[\r\n]{1,2}[^\r\n]*/, null], // full command output (both loose/compact format): `>> EXP\nVAR =\n VAL` + fallthroughStylePatterns.new( + PR_CODE_OUTPUT, + Regex("^>>\\s+"), + null + ) // only the command prompt `>> ` + fallthroughStylePatterns.new( + PR_CODE_OUTPUT, + Regex("^octave:\\d+>\\s+"), + null + ) // Octave command prompt `octave:1> ` + // identifier (chain) or closing-parenthesis/brace/bracket, and IS followed by transpose operator + // this way we dont misdetect the transpose operator ' as the start of a string + fallthroughStylePatterns.new( + "lang-matlab-operators", + Regex("^((?:[a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)*|\\)|\\]|\\}|\\.)')"), + null + ) + // identifier (chain), and NOT followed by transpose operator + // this must come AFTER the "is followed by transpose" step (otherwise it chops the last char of identifier) + fallthroughStylePatterns.new( + "lang-matlab-identifiers", + Regex("^([a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)*)(?!')"), + null + ) + // single-quoted strings: allow for escaping with '', no multilines + //[PR.PR_STRING, /(?:(?<=(?:\(|\[|\{|\s|=|;|,|:))|^)'(?:[^']|'')*'(?=(?:\)|\]|\}|\s|=|;|,|:|~|<|>|&|-|\+|\*|\.|\^|\|))/, null], // string vs. transpose (check before/after context using negative/positive lookbehind/lookahead) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^'(?:[^']|'')*'"), + null + ) // "'" + // floating point numbers: 1, 1.0, 1i, -1.1E-1 + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^[+\\-]?\\.?\\d+(?:\\.\\d*)?(?:[Ee][+\\-]?\\d+)?[ij]?"), + null + ) + // parentheses, braces, brackets + fallthroughStylePatterns.new( + Prettify.PR_TAG, + Regex("^(?:\\{|\\}|\\(|\\)|\\[|\\])"), + null + ) // "{}()[]" + // other operators + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^(?:<|>|=|~|@|&|;|,|:|!|\\-|\\+|\\*|\\^|\\.|\\||\\\\|\\/)"), + null + ) + } + + override fun getFileExtensions(): List = fileExtensions + + class LangMatlabIdentifier : Lang() { + companion object { + val fileExtensions: List + get() = listOf(("matlab-identifiers")) + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // Refer to: http://www.mathworks.com/help/matlab/functionlist-alpha.html + val coreFunctions = + "abs|accumarray|acos(?:d|h)?|acot(?:d|h)?|acsc(?:d|h)?|actxcontrol(?:list|select)?|actxGetRunningServer|actxserver|addlistener|addpath|addpref|addtodate|airy|align|alim|all|allchild|alpha|alphamap|amd|ancestor|and|angle|annotation|any|area|arrayfun|asec(?:d|h)?|asin(?:d|h)?|assert|assignin|atan(?:2|d|h)?|audiodevinfo|audioplayer|audiorecorder|aufinfo|auread|autumn|auwrite|avifile|aviinfo|aviread|axes|axis|balance|bar(?:3|3h|h)?|base2dec|beep|BeginInvoke|bench|bessel(?:h|i|j|k|y)|beta|betainc|betaincinv|betaln|bicg|bicgstab|bicgstabl|bin2dec|bitand|bitcmp|bitget|bitmax|bitnot|bitor|bitset|bitshift|bitxor|blanks|blkdiag|bone|box|brighten|brush|bsxfun|builddocsearchdb|builtin|bvp4c|bvp5c|bvpget|bvpinit|bvpset|bvpxtend|calendar|calllib|callSoapService|camdolly|cameratoolbar|camlight|camlookat|camorbit|campan|campos|camproj|camroll|camtarget|camup|camva|camzoom|cart2pol|cart2sph|cast|cat|caxis|cd|cdf2rdf|cdfepoch|cdfinfo|cdflib(?:\\.(?:close|closeVar|computeEpoch|computeEpoch16|create|createAttr|createVar|delete|deleteAttr|deleteAttrEntry|deleteAttrgEntry|deleteVar|deleteVarRecords|epoch16Breakdown|epochBreakdown|getAttrEntry|getAttrgEntry|getAttrMaxEntry|getAttrMaxgEntry|getAttrName|getAttrNum|getAttrScope|getCacheSize|getChecksum|getCompression|getCompressionCacheSize|getConstantNames|getConstantValue|getCopyright|getFileBackward|getFormat|getLibraryCopyright|getLibraryVersion|getMajority|getName|getNumAttrEntries|getNumAttrgEntries|getNumAttributes|getNumgAttributes|getReadOnlyMode|getStageCacheSize|getValidate|getVarAllocRecords|getVarBlockingFactor|getVarCacheSize|getVarCompression|getVarData|getVarMaxAllocRecNum|getVarMaxWrittenRecNum|getVarName|getVarNum|getVarNumRecsWritten|getVarPadValue|getVarRecordData|getVarReservePercent|getVarsMaxWrittenRecNum|getVarSparseRecords|getVersion|hyperGetVarData|hyperPutVarData|inquire|inquireAttr|inquireAttrEntry|inquireAttrgEntry|inquireVar|open|putAttrEntry|putAttrgEntry|putVarData|putVarRecordData|renameAttr|renameVar|setCacheSize|setChecksum|setCompression|setCompressionCacheSize|setFileBackward|setFormat|setMajority|setReadOnlyMode|setStageCacheSize|setValidate|setVarAllocBlockRecords|setVarBlockingFactor|setVarCacheSize|setVarCompression|setVarInitialRecs|setVarPadValue|SetVarReservePercent|setVarsCacheSize|setVarSparseRecords))?|cdfread|cdfwrite|ceil|cell2mat|cell2struct|celldisp|cellfun|cellplot|cellstr|cgs|checkcode|checkin|checkout|chol|cholinc|cholupdate|circshift|cla|clabel|class|clc|clear|clearvars|clf|clipboard|clock|close|closereq|cmopts|cmpermute|cmunique|colamd|colon|colorbar|colordef|colormap|colormapeditor|colperm|Combine|comet|comet3|commandhistory|commandwindow|compan|compass|complex|computer|cond|condeig|condest|coneplot|conj|containers\\.Map|contour(?:3|c|f|slice)?|contrast|conv|conv2|convhull|convhulln|convn|cool|copper|copyfile|copyobj|corrcoef|cos(?:d|h)?|cot(?:d|h)?|cov|cplxpair|cputime|createClassFromWsdl|createSoapMessage|cross|csc(?:d|h)?|csvread|csvwrite|ctranspose|cumprod|cumsum|cumtrapz|curl|customverctrl|cylinder|daqread|daspect|datacursormode|datatipinfo|date|datenum|datestr|datetick|datevec|dbclear|dbcont|dbdown|dblquad|dbmex|dbquit|dbstack|dbstatus|dbstep|dbstop|dbtype|dbup|dde23|ddeget|ddesd|ddeset|deal|deblank|dec2base|dec2bin|dec2hex|decic|deconv|del2|delaunay|delaunay3|delaunayn|DelaunayTri|delete|demo|depdir|depfun|det|detrend|deval|diag|dialog|diary|diff|diffuse|dir|disp|display|dither|divergence|dlmread|dlmwrite|dmperm|doc|docsearch|dos|dot|dragrect|drawnow|dsearch|dsearchn|dynamicprops|echo|echodemo|edit|eig|eigs|ellipj|ellipke|ellipsoid|empty|enableNETfromNetworkDrive|enableservice|EndInvoke|enumeration|eomday|eq|erf|erfc|erfcinv|erfcx|erfinv|error|errorbar|errordlg|etime|etree|etreeplot|eval|evalc|evalin|event\\.(?:EventData|listener|PropertyEvent|proplistener)|exifread|exist|exit|exp|expint|expm|expm1|export2wsdlg|eye|ezcontour|ezcontourf|ezmesh|ezmeshc|ezplot|ezplot3|ezpolar|ezsurf|ezsurfc|factor|factorial|fclose|feather|feature|feof|ferror|feval|fft|fft2|fftn|fftshift|fftw|fgetl|fgets|fieldnames|figure|figurepalette|fileattrib|filebrowser|filemarker|fileparts|fileread|filesep|fill|fill3|filter|filter2|find|findall|findfigs|findobj|findstr|finish|fitsdisp|fitsinfo|fitsread|fitswrite|fix|flag|flipdim|fliplr|flipud|floor|flow|fminbnd|fminsearch|fopen|format|fplot|fprintf|frame2im|fread|freqspace|frewind|fscanf|fseek|ftell|FTP|full|fullfile|func2str|functions|funm|fwrite|fzero|gallery|gamma|gammainc|gammaincinv|gammaln|gca|gcbf|gcbo|gcd|gcf|gco|ge|genpath|genvarname|get|getappdata|getenv|getfield|getframe|getpixelposition|getpref|ginput|gmres|gplot|grabcode|gradient|gray|graymon|grid|griddata(?:3|n)?|griddedInterpolant|gsvd|gt|gtext|guidata|guide|guihandles|gunzip|gzip|h5create|h5disp|h5info|h5read|h5readatt|h5write|h5writeatt|hadamard|handle|hankel|hdf|hdf5|hdf5info|hdf5read|hdf5write|hdfinfo|hdfread|hdftool|help|helpbrowser|helpdesk|helpdlg|helpwin|hess|hex2dec|hex2num|hgexport|hggroup|hgload|hgsave|hgsetget|hgtransform|hidden|hilb|hist|histc|hold|home|horzcat|hostid|hot|hsv|hsv2rgb|hypot|ichol|idivide|ifft|ifft2|ifftn|ifftshift|ilu|im2frame|im2java|imag|image|imagesc|imapprox|imfinfo|imformats|import|importdata|imread|imwrite|ind2rgb|ind2sub|inferiorto|info|inline|inmem|inpolygon|input|inputdlg|inputname|inputParser|inspect|instrcallback|instrfind|instrfindall|int2str|integral(?:2|3)?|interp(?:1|1q|2|3|ft|n)|interpstreamspeed|intersect|intmax|intmin|inv|invhilb|ipermute|isa|isappdata|iscell|iscellstr|ischar|iscolumn|isdir|isempty|isequal|isequaln|isequalwithequalnans|isfield|isfinite|isfloat|isglobal|ishandle|ishghandle|ishold|isinf|isinteger|isjava|iskeyword|isletter|islogical|ismac|ismatrix|ismember|ismethod|isnan|isnumeric|isobject|isocaps|isocolors|isonormals|isosurface|ispc|ispref|isprime|isprop|isreal|isrow|isscalar|issorted|isspace|issparse|isstr|isstrprop|isstruct|isstudent|isunix|isvarname|isvector|javaaddpath|javaArray|javachk|javaclasspath|javacomponent|javaMethod|javaMethodEDT|javaObject|javaObjectEDT|javarmpath|jet|keyboard|kron|lasterr|lasterror|lastwarn|lcm|ldivide|ldl|le|legend|legendre|length|libfunctions|libfunctionsview|libisloaded|libpointer|libstruct|license|light|lightangle|lighting|lin2mu|line|lines|linkaxes|linkdata|linkprop|linsolve|linspace|listdlg|listfonts|load|loadlibrary|loadobj|log|log10|log1p|log2|loglog|logm|logspace|lookfor|lower|ls|lscov|lsqnonneg|lsqr|lt|lu|luinc|magic|makehgtform|mat2cell|mat2str|material|matfile|matlab\\.io\\.MatFile|matlab\\.mixin\\.(?:Copyable|Heterogeneous(?:\\.getDefaultScalarElement)?)|matlabrc|matlabroot|max|maxNumCompThreads|mean|median|membrane|memmapfile|memory|menu|mesh|meshc|meshgrid|meshz|meta\\.(?:class(?:\\.fromName)?|DynamicProperty|EnumeratedValue|event|MetaData|method|package(?:\\.(?:fromName|getAllPackages))?|property)|metaclass|methods|methodsview|mex(?:\\.getCompilerConfigurations)?|MException|mexext|mfilename|min|minres|minus|mislocked|mkdir|mkpp|mldivide|mlint|mlintrpt|mlock|mmfileinfo|mmreader|mod|mode|more|move|movefile|movegui|movie|movie2avi|mpower|mrdivide|msgbox|mtimes|mu2lin|multibandread|multibandwrite|munlock|namelengthmax|nargchk|narginchk|nargoutchk|native2unicode|nccreate|ncdisp|nchoosek|ncinfo|ncread|ncreadatt|ncwrite|ncwriteatt|ncwriteschema|ndgrid|ndims|ne|NET(?:\\.(?:addAssembly|Assembly|convertArray|createArray|createGeneric|disableAutoRelease|enableAutoRelease|GenericClass|invokeGenericMethod|NetException|setStaticProperty))?|netcdf\\.(?:abort|close|copyAtt|create|defDim|defGrp|defVar|defVarChunking|defVarDeflate|defVarFill|defVarFletcher32|delAtt|endDef|getAtt|getChunkCache|getConstant|getConstantNames|getVar|inq|inqAtt|inqAttID|inqAttName|inqDim|inqDimID|inqDimIDs|inqFormat|inqGrpName|inqGrpNameFull|inqGrpParent|inqGrps|inqLibVers|inqNcid|inqUnlimDims|inqVar|inqVarChunking|inqVarDeflate|inqVarFill|inqVarFletcher32|inqVarID|inqVarIDs|open|putAtt|putVar|reDef|renameAtt|renameDim|renameVar|setChunkCache|setDefaultFormat|setFill|sync)|newplot|nextpow2|nnz|noanimate|nonzeros|norm|normest|not|notebook|now|nthroot|null|num2cell|num2hex|num2str|numel|nzmax|ode(?:113|15i|15s|23|23s|23t|23tb|45)|odeget|odeset|odextend|onCleanup|ones|open|openfig|opengl|openvar|optimget|optimset|or|ordeig|orderfields|ordqz|ordschur|orient|orth|pack|padecoef|pagesetupdlg|pan|pareto|parseSoapResponse|pascal|patch|path|path2rc|pathsep|pathtool|pause|pbaspect|pcg|pchip|pcode|pcolor|pdepe|pdeval|peaks|perl|perms|permute|pie|pink|pinv|planerot|playshow|plot|plot3|plotbrowser|plotedit|plotmatrix|plottools|plotyy|plus|pol2cart|polar|poly|polyarea|polyder|polyeig|polyfit|polyint|polyval|polyvalm|pow2|power|ppval|prefdir|preferences|primes|print|printdlg|printopt|printpreview|prod|profile|profsave|propedit|propertyeditor|psi|publish|PutCharArray|PutFullMatrix|PutWorkspaceData|pwd|qhull|qmr|qr|qrdelete|qrinsert|qrupdate|quad|quad2d|quadgk|quadl|quadv|questdlg|quit|quiver|quiver3|qz|rand|randi|randn|randperm|RandStream(?:\\.(?:create|getDefaultStream|getGlobalStream|list|setDefaultStream|setGlobalStream))?|rank|rat|rats|rbbox|rcond|rdivide|readasync|real|reallog|realmax|realmin|realpow|realsqrt|record|rectangle|rectint|recycle|reducepatch|reducevolume|refresh|refreshdata|regexp|regexpi|regexprep|regexptranslate|rehash|rem|Remove|RemoveAll|repmat|reset|reshape|residue|restoredefaultpath|rethrow|rgb2hsv|rgb2ind|rgbplot|ribbon|rmappdata|rmdir|rmfield|rmpath|rmpref|rng|roots|rose|rosser|rot90|rotate|rotate3d|round|rref|rsf2csf|run|save|saveas|saveobj|savepath|scatter|scatter3|schur|sec|secd|sech|selectmoveresize|semilogx|semilogy|sendmail|serial|set|setappdata|setdiff|setenv|setfield|setpixelposition|setpref|setstr|setxor|shading|shg|shiftdim|showplottool|shrinkfaces|sign|sin(?:d|h)?|size|slice|smooth3|snapnow|sort|sortrows|sound|soundsc|spalloc|spaugment|spconvert|spdiags|specular|speye|spfun|sph2cart|sphere|spinmap|spline|spones|spparms|sprand|sprandn|sprandsym|sprank|spring|sprintf|spy|sqrt|sqrtm|squeeze|ss2tf|sscanf|stairs|startup|std|stem|stem3|stopasync|str2double|str2func|str2mat|str2num|strcat|strcmp|strcmpi|stream2|stream3|streamline|streamparticles|streamribbon|streamslice|streamtube|strfind|strjust|strmatch|strncmp|strncmpi|strread|strrep|strtok|strtrim|struct2cell|structfun|strvcat|sub2ind|subplot|subsasgn|subsindex|subspace|subsref|substruct|subvolume|sum|summer|superclasses|superiorto|support|surf|surf2patch|surface|surfc|surfl|surfnorm|svd|svds|swapbytes|symamd|symbfact|symmlq|symrcm|symvar|system|tan(?:d|h)?|tar|tempdir|tempname|tetramesh|texlabel|text|textread|textscan|textwrap|tfqmr|throw|tic|Tiff(?:\\.(?:getTagNames|getVersion))?|timer|timerfind|timerfindall|times|timeseries|title|toc|todatenum|toeplitz|toolboxdir|trace|transpose|trapz|treelayout|treeplot|tril|trimesh|triplequad|triplot|TriRep|TriScatteredInterp|trisurf|triu|tscollection|tsearch|tsearchn|tstool|type|typecast|uibuttongroup|uicontextmenu|uicontrol|uigetdir|uigetfile|uigetpref|uiimport|uimenu|uiopen|uipanel|uipushtool|uiputfile|uiresume|uisave|uisetcolor|uisetfont|uisetpref|uistack|uitable|uitoggletool|uitoolbar|uiwait|uminus|undocheckout|unicode2native|union|unique|unix|unloadlibrary|unmesh|unmkpp|untar|unwrap|unzip|uplus|upper|urlread|urlwrite|usejava|userpath|validateattributes|validatestring|vander|var|vectorize|ver|verctrl|verLessThan|version|vertcat|VideoReader(?:\\.isPlatformSupported)?|VideoWriter(?:\\.getProfiles)?|view|viewmtx|visdiff|volumebounds|voronoi|voronoin|wait|waitbar|waitfor|waitforbuttonpress|warndlg|warning|waterfall|wavfinfo|wavplay|wavread|wavrecord|wavwrite|web|weekday|what|whatsnew|which|whitebg|who|whos|wilkinson|winopen|winqueryreg|winter|wk1finfo|wk1read|wk1write|workspace|xlabel|xlim|xlsfinfo|xlsread|xlswrite|xmlread|xmlwrite|xor|xslt|ylabel|ylim|zeros|zip|zlabel|zlim|zoom" + val statsFunctions = + "addedvarplot|andrewsplot|anova(?:1|2|n)|ansaribradley|aoctool|barttest|bbdesign|beta(?:cdf|fit|inv|like|pdf|rnd|stat)|bino(?:cdf|fit|inv|pdf|rnd|stat)|biplot|bootci|bootstrp|boxplot|candexch|candgen|canoncorr|capability|capaplot|caseread|casewrite|categorical|ccdesign|cdfplot|chi2(?:cdf|gof|inv|pdf|rnd|stat)|cholcov|Classification(?:BaggedEnsemble|Discriminant(?:\\.(?:fit|make|template))?|Ensemble|KNN(?:\\.(?:fit|template))?|PartitionedEnsemble|PartitionedModel|Tree(?:\\.(?:fit|template))?)|classify|classregtree|cluster|clusterdata|cmdscale|combnk|Compact(?:Classification(?:Discriminant|Ensemble|Tree)|Regression(?:Ensemble|Tree)|TreeBagger)|confusionmat|controlchart|controlrules|cophenet|copula(?:cdf|fit|param|pdf|rnd|stat)|cordexch|corr|corrcov|coxphfit|createns|crosstab|crossval|cvpartition|datasample|dataset|daugment|dcovary|dendrogram|dfittool|disttool|dummyvar|dwtest|ecdf|ecdfhist|ev(?:cdf|fit|inv|like|pdf|rnd|stat)|ExhaustiveSearcher|exp(?:cdf|fit|inv|like|pdf|rnd|stat)|factoran|fcdf|ff2n|finv|fitdist|fitensemble|fpdf|fracfact|fracfactgen|friedman|frnd|fstat|fsurfht|fullfact|gagerr|gam(?:cdf|fit|inv|like|pdf|rnd|stat)|GeneralizedLinearModel(?:\\.fit)?|geo(?:cdf|inv|mean|pdf|rnd|stat)|gev(?:cdf|fit|inv|like|pdf|rnd|stat)|gline|glmfit|glmval|glyphplot|gmdistribution(?:\\.fit)?|gname|gp(?:cdf|fit|inv|like|pdf|rnd|stat)|gplotmatrix|grp2idx|grpstats|gscatter|haltonset|harmmean|hist3|histfit|hmm(?:decode|estimate|generate|train|viterbi)|hougen|hyge(?:cdf|inv|pdf|rnd|stat)|icdf|inconsistent|interactionplot|invpred|iqr|iwishrnd|jackknife|jbtest|johnsrnd|KDTreeSearcher|kmeans|knnsearch|kruskalwallis|ksdensity|kstest|kstest2|kurtosis|lasso|lassoglm|lassoPlot|leverage|lhsdesign|lhsnorm|lillietest|LinearModel(?:\\.fit)?|linhyptest|linkage|logn(?:cdf|fit|inv|like|pdf|rnd|stat)|lsline|mad|mahal|maineffectsplot|manova1|manovacluster|mdscale|mhsample|mle|mlecov|mnpdf|mnrfit|mnrnd|mnrval|moment|multcompare|multivarichart|mvn(?:cdf|pdf|rnd)|mvregress|mvregresslike|mvt(?:cdf|pdf|rnd)|NaiveBayes(?:\\.fit)?|nan(?:cov|max|mean|median|min|std|sum|var)|nbin(?:cdf|fit|inv|pdf|rnd|stat)|ncf(?:cdf|inv|pdf|rnd|stat)|nct(?:cdf|inv|pdf|rnd|stat)|ncx2(?:cdf|inv|pdf|rnd|stat)|NeighborSearcher|nlinfit|nlintool|nlmefit|nlmefitsa|nlparci|nlpredci|nnmf|nominal|NonLinearModel(?:\\.fit)?|norm(?:cdf|fit|inv|like|pdf|rnd|stat)|normplot|normspec|ordinal|outlierMeasure|parallelcoords|paretotails|partialcorr|pcacov|pcares|pdf|pdist|pdist2|pearsrnd|perfcurve|perms|piecewisedistribution|plsregress|poiss(?:cdf|fit|inv|pdf|rnd|tat)|polyconf|polytool|prctile|princomp|ProbDist(?:Kernel|Parametric|UnivKernel|UnivParam)?|probplot|procrustes|qqplot|qrandset|qrandstream|quantile|randg|random|randsample|randtool|range|rangesearch|ranksum|rayl(?:cdf|fit|inv|pdf|rnd|stat)|rcoplot|refcurve|refline|regress|Regression(?:BaggedEnsemble|Ensemble|PartitionedEnsemble|PartitionedModel|Tree(?:\\.(?:fit|template))?)|regstats|relieff|ridge|robustdemo|robustfit|rotatefactors|rowexch|rsmdemo|rstool|runstest|sampsizepwr|scatterhist|sequentialfs|signrank|signtest|silhouette|skewness|slicesample|sobolset|squareform|statget|statset|stepwise|stepwisefit|surfht|tabulate|tblread|tblwrite|tcdf|tdfread|tiedrank|tinv|tpdf|TreeBagger|treedisp|treefit|treeprune|treetest|treeval|trimmean|trnd|tstat|ttest|ttest2|unid(?:cdf|inv|pdf|rnd|stat)|unif(?:cdf|inv|it|pdf|rnd|stat)|vartest(?:2|n)?|wbl(?:cdf|fit|inv|like|pdf|rnd|stat)|wblplot|wishrnd|x2fx|xptread|zscore|ztest" + val imageFunctions = + "adapthisteq|analyze75info|analyze75read|applycform|applylut|axes2pix|bestblk|blockproc|bwarea|bwareaopen|bwboundaries|bwconncomp|bwconvhull|bwdist|bwdistgeodesic|bweuler|bwhitmiss|bwlabel|bwlabeln|bwmorph|bwpack|bwperim|bwselect|bwtraceboundary|bwulterode|bwunpack|checkerboard|col2im|colfilt|conndef|convmtx2|corner|cornermetric|corr2|cp2tform|cpcorr|cpselect|cpstruct2pairs|dct2|dctmtx|deconvblind|deconvlucy|deconvreg|deconvwnr|decorrstretch|demosaic|dicom(?:anon|dict|info|lookup|read|uid|write)|edge|edgetaper|entropy|entropyfilt|fan2para|fanbeam|findbounds|fliptform|freqz2|fsamp2|fspecial|ftrans2|fwind1|fwind2|getheight|getimage|getimagemodel|getline|getneighbors|getnhood|getpts|getrangefromclass|getrect|getsequence|gray2ind|graycomatrix|graycoprops|graydist|grayslice|graythresh|hdrread|hdrwrite|histeq|hough|houghlines|houghpeaks|iccfind|iccread|iccroot|iccwrite|idct2|ifanbeam|im2bw|im2col|im2double|im2int16|im2java2d|im2single|im2uint16|im2uint8|imabsdiff|imadd|imadjust|ImageAdapter|imageinfo|imagemodel|imapplymatrix|imattributes|imbothat|imclearborder|imclose|imcolormaptool|imcomplement|imcontour|imcontrast|imcrop|imdilate|imdisplayrange|imdistline|imdivide|imellipse|imerode|imextendedmax|imextendedmin|imfill|imfilter|imfindcircles|imfreehand|imfuse|imgca|imgcf|imgetfile|imhandles|imhist|imhmax|imhmin|imimposemin|imlincomb|imline|immagbox|immovie|immultiply|imnoise|imopen|imoverview|imoverviewpanel|impixel|impixelinfo|impixelinfoval|impixelregion|impixelregionpanel|implay|impoint|impoly|impositionrect|improfile|imputfile|impyramid|imreconstruct|imrect|imregconfig|imregionalmax|imregionalmin|imregister|imresize|imroi|imrotate|imsave|imscrollpanel|imshow|imshowpair|imsubtract|imtool|imtophat|imtransform|imview|ind2gray|ind2rgb|interfileinfo|interfileread|intlut|ippl|iptaddcallback|iptcheckconn|iptcheckhandle|iptcheckinput|iptcheckmap|iptchecknargin|iptcheckstrs|iptdemos|iptgetapi|iptGetPointerBehavior|iptgetpref|ipticondir|iptnum2ordinal|iptPointerManager|iptprefs|iptremovecallback|iptSetPointerBehavior|iptsetpref|iptwindowalign|iradon|isbw|isflat|isgray|isicc|isind|isnitf|isrgb|isrset|lab2double|lab2uint16|lab2uint8|label2rgb|labelmatrix|makecform|makeConstrainToRectFcn|makehdr|makelut|makeresampler|maketform|mat2gray|mean2|medfilt2|montage|nitfinfo|nitfread|nlfilter|normxcorr2|ntsc2rgb|openrset|ordfilt2|otf2psf|padarray|para2fan|phantom|poly2mask|psf2otf|qtdecomp|qtgetblk|qtsetblk|radon|rangefilt|reflect|regionprops|registration\\.metric\\.(?:MattesMutualInformation|MeanSquares)|registration\\.optimizer\\.(?:OnePlusOneEvolutionary|RegularStepGradientDescent)|rgb2gray|rgb2ntsc|rgb2ycbcr|roicolor|roifill|roifilt2|roipoly|rsetwrite|std2|stdfilt|strel|stretchlim|subimage|tformarray|tformfwd|tforminv|tonemap|translate|truesize|uintlut|viscircles|warp|watershed|whitepoint|wiener2|xyz2double|xyz2uint16|ycbcr2rgb" + val optimFunctions = + "bintprog|color|fgoalattain|fminbnd|fmincon|fminimax|fminsearch|fminunc|fseminf|fsolve|fzero|fzmult|gangstr|ktrlink|linprog|lsqcurvefit|lsqlin|lsqnonlin|lsqnonneg|optimget|optimset|optimtool|quadprog" + + // list of keywords (`iskeyword`) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(?:break|case|catch|classdef|continue|else|elseif|end|for|function|global|if|otherwise|parfor|persistent|return|spmd|switch|try|while)\\b"), + null + ) + // some specials variables/constants + fallthroughStylePatterns.new( + PR_CONSTANT, + Regex("^\\b(?:true|false|inf|Inf|nan|NaN|eps|pi|ans|nargin|nargout|varargin|varargout)\\b"), + null + ) + // some data types + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^\\b(?:cell|struct|char|double|single|logical|u?int(?:8|16|32|64)|sparse)\\b"), + null + ) + // commonly used builtin functions from core MATLAB and a few popular toolboxes + fallthroughStylePatterns.new( + PR_FUNCTION, Regex( + "^\\b(?:$coreFunctions)\\b" + ), null + ) + fallthroughStylePatterns.new( + PR_FUNCTION_TOOLBOX, Regex( + "^\\b(?:$statsFunctions)\\b" + ), null + ) + fallthroughStylePatterns.new( + PR_FUNCTION_TOOLBOX, Regex( + "^\\b(?:$imageFunctions)\\b" + ), null + ) + fallthroughStylePatterns.new( + PR_FUNCTION_TOOLBOX, Regex( + "^\\b(?:$optimFunctions)\\b" + ), null + ) + // plain identifier (user-defined variable/function name) + fallthroughStylePatterns.new( + PR_IDENTIFIER, + Regex("^[a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)*"), + null + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } + } + + class LangMatlabOperator : Lang() { + companion object { + val fileExtensions: List + get() = listOf("matlab-operators") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // forward to identifiers to match + fallthroughStylePatterns.new( + "lang-matlab-identifiers", + Regex("^([a-zA-Z][a-zA-Z0-9_]*(?:\\.[a-zA-Z][a-zA-Z0-9_]*)*)"), + null + ) + // parentheses, braces, brackets + fallthroughStylePatterns.new( + Prettify.PR_TAG, + Regex("^(?:\\{|\\}|\\(|\\)|\\[|\\])"), + null + ) // "{}()[]" + // other operators + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^(?:<|>|=|~|@|&|;|,|:|!|\\-|\\+|\\*|\\^|\\.|\\||\\\\|\\/)"), + null + ) + // transpose operators + fallthroughStylePatterns.new(PR_TRANSPOSE, Regex("^'"), null) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } + } + +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMd.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMd.kt new file mode 100644 index 0000000..6dc77f4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMd.kt @@ -0,0 +1,37 @@ +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * Registers a language handler for markdown. + * + * @author Kirill Biakov (kbiakov@gmail.com) + */ +class LangMd : Lang() { + companion object { + val fileExtensions: List + get() = listOf("md", "markdown") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + fallthroughStylePatterns.new( + Prettify.PR_DECLARATION, + Regex("^#.*?[\\n\\r]") + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^```[\\s\\S]*?(?:```|$)") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMl.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMl.kt new file mode 100644 index 0000000..8daf520 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMl.kt @@ -0,0 +1,102 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-ml.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for OCaml, SML, F# and similar languages. + * + * Based on the lexical grammar at + * http://research.microsoft.com/en-us/um/cambridge/projects/fsharp/manual/spec.html#_Toc270597388 + * + * @author mikesamuel@gmail.com + */ +class LangMl : Lang() { + companion object { + val fileExtensions: List + get() = listOf("fs", "ml") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + + // Whitespace is made up of spaces, tabs and newline characters. + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // #if ident/#else/#endif directives delimit conditional compilation + // sections + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex( + "^#(?:if[\\t\\n\\r \\xA0]+(?:[a-z_$][\\w\\']*|``[^\\r\\n\\t`]*(?:``|$))|else|endif|light)", + RegexOption.IGNORE_CASE + ), + null + ) + // A double or single quoted, possibly multi-line, string. + // F# allows escaped newlines in strings. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\]|\\\\[\\s\\S])(?:\\'|$))"), + null + ) + // Block comments are delimited by (* and *) and may be + // nested. Single-line comments begin with // and extend to + // the end of a line. + // TODO: (*...*) comments can be nested. This does not handle that. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^(?:\\/\\/[^\\r\\n]*|\\(\\*[\\s\\S]*?\\*\\))") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:abstract|and|as|assert|begin|class|default|delegate|do|done|downcast|downto|elif|else|end|exception|extern|false|finally|for|fun|function|if|in|inherit|inline|interface|internal|lazy|let|match|member|module|mutable|namespace|new|null|of|open|or|override|private|public|rec|return|static|struct|then|to|true|try|type|upcast|use|val|void|when|while|with|yield|asr|land|lor|lsl|lsr|lxor|mod|sig|atomic|break|checked|component|const|constraint|constructor|continue|eager|event|external|fixed|functor|global|include|method|mixin|object|parallel|process|protected|pure|sealed|trait|virtual|volatile)\\b") + ) + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex( + "^[+\\-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", + RegexOption.IGNORE_CASE + ) + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^(?:[a-z_][\\w']*[!?#]?|``[^\\r\\n\\t`]*(?:``|$))", RegexOption.IGNORE_CASE) + ) + // A printable non-space non-special character + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^[^\\t\\n\\r \\xA0\\\"\\'\\w]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMumps.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMumps.kt new file mode 100644 index 0000000..7359f7c --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangMumps.kt @@ -0,0 +1,180 @@ +// Copyright (C) 2011 Kitware Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-mumps.js in JavaScript Prettify. + * + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my SQL code)
+ * + * + * Commands, intrinsic functions and variables taken from ISO/IEC 11756:1999(E) + * + * @author chris.harris@kitware.com + * + * + * Known issues: + * + * + * - Currently can't distinguish between keywords and local or global variables having the same name + * for exampe SET IF="IF?" + * - m file are already used for MatLab hence using mumps. + */ +class LangMumps : Lang() { + companion object { + val fileExtensions: List + get() = listOf("mumps") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + val commands = "B|BREAK|" + + "C|CLOSE|" + + "D|DO|" + + "E|ELSE|" + + "F|FOR|" + + "G|GOTO|" + + "H|HALT|" + + "H|HANG|" + + "I|IF|" + + "J|JOB|" + + "K|KILL|" + + "L|LOCK|" + + "M|MERGE|" + + "N|NEW|" + + "O|OPEN|" + + "Q|QUIT|" + + "R|READ|" + + "S|SET|" + + "TC|TCOMMIT|" + + "TRE|TRESTART|" + + "TRO|TROLLBACK|" + + "TS|TSTART|" + + "U|USE|" + + "V|VIEW|" + + "W|WRITE|" + + "X|XECUTE" + val intrinsicVariables = "D|DEVICE|" + + "EC|ECODE|" + + "ES|ESTACK|" + + "ET|ETRAP|" + + "H|HOROLOG|" + + "I|IO|" + + "J|JOB|" + + "K|KEY|" + + "P|PRINCIPAL|" + + "Q|QUIT|" + + "ST|STACK|" + + "S|STORAGE|" + + "SY|SYSTEM|" + + "T|TEST|" + + "TL|TLEVEL|" + + "TR|TRESTART|" + + "X|" + + "Y|" + + "Z[A-Z]*|" + val intrinsicFunctions = "A|ASCII|" + + "C|CHAR|" + + "D|DATA|" + + "E|EXTRACT|" + + "F|FIND|" + + "FN|FNUMBER|" + + "G|GET|" + + "J|JUSTIFY|" + + "L|LENGTH|" + + "NA|NAME|" + + "O|ORDER|" + + "P|PIECE|" + + "QL|QLENGTH|" + + "QS|QSUBSCRIPT|" + + "Q|QUERY|" + + "R|RANDOM|" + + "RE|REVERSE|" + + "S|SELECT|" + + "ST|STACK|" + + "T|TEXT|" + + "TR|TRANSLATE|" + + "V|VIEW|" + + "Z[A-Z]*|" + val intrinsic = intrinsicVariables + intrinsicFunctions + + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\t\n\r \\xA0]+"), null + ) + // A double or single quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\"(?:[^\"]|\\\\.)*\")"), + null + ) + + // A line comment that starts with ; + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^;[^\\r\\n]*"), + null + ) + // Add intrinsic variables and functions as declarations, there not really but it mean + // they will hilighted differently from commands. + fallthroughStylePatterns.new( + Prettify.PR_DECLARATION, Regex( + "^(?:\\$(?:$intrinsic))\\b", RegexOption.IGNORE_CASE + ), null + ) + // Add commands as keywords + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, Regex( + "^(?:[^\\$]$commands)\\b", RegexOption.IGNORE_CASE + ), null + ) + // A number is a decimal real literal or in scientific notation. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^[+-]?(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:E[+\\-]?\\d+)?)", RegexOption.IGNORE_CASE) + ) + // An identifier + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z][a-zA-Z0-9]*", RegexOption.IGNORE_CASE) + ) + // Exclude $ % and ^ + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[^\\w\\t\\n\\r\\xA0\\\"\\$;%\\^]|_") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangN.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangN.kt new file mode 100644 index 0000000..4917c13 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangN.kt @@ -0,0 +1,132 @@ +// Copyright (C) 2011 Zimin A.V. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-n.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for the Nemerle language. + * http://nemerle.org + * @author Zimin A.V. + */ +class LangN : Lang() { + companion object { + private var keywords = ("abstract|and|as|base|catch|class|def|delegate|enum|event|extern|false|finally|" + + "fun|implements|interface|internal|is|macro|match|matches|module|mutable|namespace|new|" + + "null|out|override|params|partial|private|protected|public|ref|sealed|static|struct|" + + "syntax|this|throw|true|try|type|typeof|using|variant|virtual|volatile|when|where|with|" + + "assert|assert2|async|break|checked|continue|do|else|ensures|for|foreach|if|late|lock|new|nolate|" + + "otherwise|regexp|repeat|requires|return|surroundwith|unchecked|unless|using|while|yield") + val fileExtensions: List + get() = listOf("n", "nemerle") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\\'(?:[^\\\\\\'\\r\\n]|\\\\.)*\\'|\\\"(?:[^\\\\\\\"\\r\\n]|\\\\.)*(?:\\\"|$))"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\\b|[^\\r\\n]*)"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^\\s+"), null + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^@\\\"(?:[^\\\"]|\\\"\\\")*(?:\\\"|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^<#(?:[^#>])*(?:#>|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_STRING, + Regex("^<(?:(?:(?:\\.\\.\\/)*|\\/?)(?:[\\w-]+(?:\\/[\\w-]+)+)?[\\w-]+\\.h|[a-z]\\w*)>"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\/[^\\r\\n]*"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:$keywords)\\\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^(?:array|bool|byte|char|decimal|double|float|int|list|long|object|sbyte|short|string|ulong|uint|ufloat|ulong|ushort|void)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^@[a-z_$][a-z_$@0-9]*", RegexOption.IGNORE_CASE), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^@[A-Z]+[a-z][A-Za-z_$@0-9]*"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^'?[A-Za-z_$][a-z_$@0-9]*", RegexOption.IGNORE_CASE), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex( + "^(?:" // A hex number + + "0x[a-f0-9]+" // or an octal or decimal number, + + "|(?:\\\\d(?:_\\\\d+)*\\\\d*(?:\\\\.\\\\d*)?|\\\\.\\\\d\\\\+)" // possibly in scientific notation + + "(?:e[+\\\\-]?\\\\d+)?" + + ")" // with an optional modifier like UL for unsigned long + + "[a-z]*", RegexOption.IGNORE_CASE + ), null + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^.[^\\s\\w\\.$@\\'\\\"\\`\\/\\#]*"), + null + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangPascal.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangPascal.kt new file mode 100644 index 0000000..d360893 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangPascal.kt @@ -0,0 +1,105 @@ +// Copyright (C) 2009 Onno Hommes. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-appollo.js in JavaScript Prettify. + * + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * + * Registers a language handler for the AGC/AEA Assembly Language as described + * at http://virtualagc.googlecode.com + * + * + * This file could be used by goodle code to allow syntax highlight for + * Virtual AGC SVN repository or if you don't want to commonize + * the header for the agc/aea html assembly listing. + * + * @author ohommes@alumni.cmu.edu + */ +class LangPascal : Lang() { + companion object { + val fileExtensions: List + get() = listOf(("pascal")) + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // 'single-line-string' + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\\'(?:[^\\\\\\'\\r\\n]|\\\\.)*(?:\\'|$))"), + null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^\\s+"), null + ) + + // A cStyleComments comment (* *) or {} + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\(\\*[\\s\\S]*?(?:\\*\\)|$)|^\\{[\\s\\S]*?(?:\\}|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex( + "^(?:ABSOLUTE|AND|ARRAY|ASM|ASSEMBLER|BEGIN|CASE|CONST|CONSTRUCTOR|DESTRUCTOR|DIV|DO|DOWNTO|ELSE|END|EXTERNAL|FOR|FORWARD|FUNCTION|GOTO|IF|IMPLEMENTATION|IN|INLINE|INTERFACE|INTERRUPT|LABEL|MOD|NOT|OBJECT|OF|OR|PACKED|PROCEDURE|PROGRAM|RECORD|REPEAT|SET|SHL|SHR|THEN|TO|TYPE|UNIT|UNTIL|USES|VAR|VIRTUAL|WHILE|WITH|XOR)\\b", + RegexOption.IGNORE_CASE + ), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:true|false|self|nil)", RegexOption.IGNORE_CASE), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z][a-z0-9]*", RegexOption.IGNORE_CASE), + null + ) + // Literals .0, 0, 0.0 0E13 + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^(?:\\$[a-f0-9]+|(?:\\d+(?:\\.\\d*)?|\\.\\d+)(?:e[+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^.[^\\s\\w\\.$@\\'\\/]*"), + null + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangProto.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangProto.kt new file mode 100644 index 0000000..3ad3086 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangProto.kt @@ -0,0 +1,21 @@ +// Copyright (C) 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify + +/** + * It is included directly in the [Prettify]. + */ +class LangProto \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangR.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangR.kt new file mode 100644 index 0000000..864a1b4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangR.kt @@ -0,0 +1,96 @@ +// Copyright (C) 2012 Jeffrey B. Arnold +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-r.js in JavaScript Prettify. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
 code 
+ * + * + * Language definition from + * http://cran.r-project.org/doc/manuals/R-lang.html. + * Many of the regexes are shared with the pygments SLexer, + * http://pygments.org/. + * + * + * Original: https://raw.github.com/jrnold/prettify-lang-r-bugs/master/lang-r.js + * + * @author jeffrey.arnold@gmail.com + */ +class LangR : Lang() { + companion object { + val fileExtensions: List + get() = listOf("r", "s", "R", "S", "Splus") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, Regex("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, Regex("^\\'(?:[^\\'\\\\]|\\\\[\\s\\S])*(?:\\'|$)"), null + ) + fallthroughStylePatterns.new(Prettify.PR_COMMENT, Regex("^#.*")) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:if|else|for|while|repeat|in|next|break|return|switch|function)(?![A-Za-z0-9_.])") + ) + // hex numbes + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^0[xX][a-fA-F0-9]+([pP][0-9]+)?[Li]?") + ) + // Decimal numbers + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^[+-]?([0-9]+(\\.[0-9]+)?|\\.[0-9]+)([eE][+-]?[0-9]+)?[Li]?") + ) + // builtin symbols + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:NULL|NA(?:_(?:integer|real|complex|character)_)?|Inf|TRUE|FALSE|NaN|\\.\\.(?:\\.|[0-9]+))(?![A-Za-z0-9_.])") + ) + // assignment, operators, and parens, etc. + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^(?:<>?|-|==|<=|>=|<|>|&&?|!=|\\|\\|?|\\*|\\+|\\^|\\/|!|%.*?%|=|~|\\$|@|:{1,3}|[\\[\\](){};,?])") + ) + // valid variable names + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^(?:[A-Za-z]+[A-Za-z0-9_.]*|\\.[a-zA-Z_][0-9a-zA-Z\\._]*)(?![A-Za-z0-9_.])") + ) + // string backtick + fallthroughStylePatterns.new(Prettify.PR_STRING, Regex("^`.+`")) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangRd.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangRd.kt new file mode 100644 index 0000000..d0d04cc --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangRd.kt @@ -0,0 +1,87 @@ +// Copyright (C) 2012 Jeffrey Arnold +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-rd.js in JavaScript Prettify. + * + * + * Support for R documentation (Rd) files + * + * + * Minimal highlighting or Rd files, basically just highlighting + * macros. It does not try to identify verbatim or R-like regions of + * macros as that is too complicated for a lexer. Descriptions of the + * Rd format can be found + * http://cran.r-project.org/doc/manuals/R-exts.html and + * http://developer.r-project.org/parseRd.pdf. + * + * @author Jeffrey Arnold + */ +class LangRd : Lang() { + companion object { + val fileExtensions: List + get() = listOf("Rd", "rd") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // all comments begin with '%' + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^%[^\\r\\n]*"), + null + ) + + // special macros with no args + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^\\\\(?:cr|l?dots|R|tab)\\b") + ) + // macros + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\\\[a-zA-Z@]+") + ) + // highlighted as macros, since technically they are + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^#(?:ifn?def|endif)") + ) + // catch escaped brackets + fallthroughStylePatterns.new(Prettify.PR_PLAIN, Regex("^\\\\[{}]")) + // punctuation + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[{}()\\[\\]]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangScala.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangScala.kt new file mode 100644 index 0000000..2551694 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangScala.kt @@ -0,0 +1,105 @@ +// Copyright (C) 2010 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-scala.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for Scala. + * + * Derived from http://lampsvn.epfl.ch/svn-repos/scala/scala-documentation/trunk/src/reference/SyntaxSummary.tex + * + * @author mikesamuel@gmail.com + */ +class LangScala : Lang() { + companion object { + val fileExtensions: List + get() = listOf("scala") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // A double or single quoted string + // or a triple double-quoted multi-line string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\"(?:(?:\"\"(?:\"\"?(?!\")|[^\\\\\"]|\\\\.)*\"{0,3})|(?:[^\"\\r\\n\\\\]|\\\\.)*\"?))"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_LITERAL, Regex("^`(?:[^\\r\\n\\\\`]|\\\\.)*`?"), null + ) + shortcutStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[!#%&()*+,\\-:;<=>?@\\[\\\\\\]^{|}~]+"), + null + ) + // A symbol literal is a single quote followed by an identifier with no + // single quote following + // A character literal has single quotes on either side + fallthroughStylePatterns.new( + Prettify.PR_STRING, Regex("^'(?:[^\\r\\n\\\\']|\\\\(?:'|[^\\r\\n']+))'") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^'[a-zA-Z_$][\\w$]*(?!['$\\w])") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:abstract|case|catch|class|def|do|else|extends|final|finally|for|forSome|if|implicit|import|lazy|match|new|object|override|package|private|protected|requires|return|sealed|super|throw|trait|try|type|val|var|while|with|yield)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^(?:true|false|null|this)\\b") + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex( + "^(?:(?:0(?:[0-7]+|X[0-9A-F]+))L?|(?:(?:0|[1-9][0-9]*)(?:(?:\\.[0-9]+)?(?:E[+\\-]?[0-9]+)?F?|L?))|\\\\.[0-9]+(?:E[+\\-]?[0-9]+)?F?)", + RegexOption.IGNORE_CASE + ) + ) + // Treat upper camel case identifiers as types. + fallthroughStylePatterns.new( + Prettify.PR_TYPE, Regex("^[\$_]*[A-Z][_\$A-Z0-9]*[a-z][\\w$]*") + ) + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\$a-zA-Z_][\\w$]*") + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, Regex("^\\/(?:\\/.*|\\*(?:\\/|\\**[^*/])*(?:\\*+\\/?)?)") + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^(?:\\.+|\\/)") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSql.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSql.kt new file mode 100644 index 0000000..57c8308 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSql.kt @@ -0,0 +1,100 @@ +// Copyright (C) 2008 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-sql.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for SQL. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
(my SQL code)
+ * + * + * http://savage.net.au/SQL/sql-99.bnf.html is the basis for the grammar, and + * http://msdn.microsoft.com/en-us/library/aa238507(SQL.80).aspx and + * http://meta.stackoverflow.com/q/92352/137403 as the bases for the keyword + * list. + * + * @author mikesamuel@gmail.com + */ +class LangSql : Lang() { + companion object { + val fileExtensions: List + get() = listOf(("sql")) + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // A double or single quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^(?:\"(?:[^\\\"\\\\]|\\\\.)*\"|'(?:[^\\'\\\\]|\\\\.)*')"), + null + ) + // A comment is either a line comment that starts with two dashes, or + // two dashes preceding a long bracketed block. + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^(?:--[^\\r\\n]*|\\/\\*[\\s\\S]*?(?:\\*\\/|$))") + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, Regex( + "^(?:ADD|ALL|ALTER|AND|ANY|APPLY|AS|ASC|AUTHORIZATION|BACKUP|BEGIN|BETWEEN|BREAK|BROWSE|BULK|BY|CASCADE|CASE|CHECK|CHECKPOINT|CLOSE|CLUSTERED|COALESCE|COLLATE|COLUMN|COMMIT|COMPUTE|CONNECT|CONSTRAINT|CONTAINS|CONTAINSTABLE|CONTINUE|CONVERT|CREATE|CROSS|CURRENT|CURRENT_DATE|CURRENT_TIME|CURRENT_TIMESTAMP|CURRENT_USER|CURSOR|DATABASE|DBCC|DEALLOCATE|DECLARE|DEFAULT|DELETE|DENY|DESC|DISK|DISTINCT|DISTRIBUTED|DOUBLE|DROP|DUMMY|DUMP|ELSE|END|ERRLVL|ESCAPE|EXCEPT|EXEC|EXECUTE|EXISTS|EXIT|FETCH|FILE|FILLFACTOR|FOLLOWING|FOR|FOREIGN|FREETEXT|FREETEXTTABLE|FROM|FULL|FUNCTION|GOTO|GRANT|GROUP|HAVING|HOLDLOCK|IDENTITY|IDENTITYCOL|IDENTITY_INSERT|IF|IN|INDEX|INNER|INSERT|INTERSECT|INTO|IS|JOIN|KEY|KILL|LEFT|LIKE|LINENO|LOAD|MATCH|MERGE|NATIONAL|NOCHECK|NONCLUSTERED|NOT|NULL|NULLIF|OF|OFF|OFFSETS|ON|OPEN|OPENDATASOURCE|OPENQUERY|OPENROWSET|OPENXML|OPTION|OR|ORDER|OUTER|OVER|PERCENT|PLAN|PRECEDING|PRECISION|PRIMARY|PRINT|PROC|PROCEDURE|PUBLIC|RAISERROR|READ|READTEXT|RECONFIGURE|REFERENCES|REPLICATION|RESTORE|RESTRICT|RETURN|REVOKE|RIGHT|ROLLBACK|ROWCOUNT|ROWGUIDCOL|ROWS?|RULE|SAVE|SCHEMA|SELECT|SESSION_USER|SET|SETUSER|SHUTDOWN|SOME|STATISTICS|SYSTEM_USER|TABLE|TEXTSIZE|THEN|TO|TOP|TRAN|TRANSACTION|TRIGGER|TRUNCATE|TSEQUAL|UNBOUNDED|UNION|UNIQUE|UPDATE|UPDATETEXT|USE|USER|USING|VALUES|VARYING|VIEW|WAITFOR|WHEN|WHERE|WHILE|WITH|WRITETEXT)(?=[^\\w-]|$)", + RegexOption.IGNORE_CASE + ), null + ) + // A number is a hex integer literal, a decimal real literal, or in + // scientific notation. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "^[+-]?(?:0x[\\da-f]+|(?:(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:e[+\\-]?\\d+)?))", + RegexOption.IGNORE_CASE + ) + ) + // An identifier + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[a-z_][\\w-]*", RegexOption.IGNORE_CASE) + ) + // A run of punctuation + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0+\\-\\\"\\']*") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSwift.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSwift.kt new file mode 100644 index 0000000..d1e36ad --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangSwift.kt @@ -0,0 +1,75 @@ +package com.wakaztahir.codeeditor.prettify.lang + + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +class LangSwift : Lang() { + + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + companion object { + val fileExtensions = listOf("swift") + } + + override fun getFileExtensions(): List = fileExtensions + + init { + shortcutStylePatterns.new( + Prettify.PR_PLAIN, + Regex("^[ \\n\\r\\t\\v\\u000c\\\u0000]+"), + null + ) + shortcutStylePatterns.new( + Prettify.PR_STRING, + Regex("^\"(?:[^\"\\\\]|(?:\\\\.)|(?:\\\\\\((?:[^\"\\\\)]|\\\\.)*\\)))*\""), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:(?:0x[\\da-fA-F][\\da-fA-F_]*\\.[\\da-fA-F][\\da-fA-F_]*[pP]?)|(?:\\d[\\d_]*\\.\\d[\\d_]*[eE]?))[+-]?\\d[\\d_]*"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^-?(?:(?:0(?:(?:b[01][01_]*)|(?:o[0-7][0-7_]*)|(?:x[\\da-fA-F][\\da-fA-F_]*)))|(?:\\d[\\d_]*))"), + null + + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex("^(?:_|Any|true|false|nil)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\b(?:__COLUMN__|__FILE__|__FUNCTION__|__LINE__|#available|#colorLiteral|#column|#else|#elseif|#endif|#file|#fileLiteral|#function|#if|#imageLiteral|#line|#selector|#sourceLocation|arch|arm|arm64|associatedtype|associativity|as|break|case|catch|class|continue|convenience|default|defer|deinit|didSet|do|dynamic|dynamicType|else|enum|extension|fallthrough|fileprivate|final|for|func|get|guard|import|indirect|infix|init|inout|internal|i386|if|in|iOS|iOSApplicationExtension|is|lazy|left|let|mutating|none|nonmutating|open|operator|optional|OSX|OSXApplicationExtension|override|postfix|precedence|prefix|private|protocol|Protocol|public|repeat|required|rethrows|return|right|safe|Self|self|set|static|struct|subscript|super|switch|throw|throws|try|Type|typealias|unowned|unsafe|var|weak|watchOS|where|while|willSet|x86_64)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\/.*?[\\n\\r]"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^\\/\\*[\\s\\S]*?(?:\\*\\/|$)"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^<<=|<=|<<|>>=|>=|>>|===|==|\\.\\.\\.|&&=|\\.\\.<|!==|!=|&=|~=|~|\\(|\\)|\\[|\\]|\\{|}|@|#|;|\\.|,|:|\\|\\|=|\\?\\?|\\|\\||&&|&\\*|&\\+|&-|&=|\\+=|-=|\\/=|\\*=|\\^=|%=|\\|=|->|`|==|\\+\\+|--|\\/|\\+|!|\\*|%|<|>|&|\\||\\^|\\?|=|-|_"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_TYPE, + Regex("^\\b(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\\w+_t\\b)"), + null + ) + + + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTcl.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTcl.kt new file mode 100644 index 0000000..250cc26 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTcl.kt @@ -0,0 +1,95 @@ +// Copyright (C) 2012 Pyrios. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-tcl.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *
proc foo {} {puts bar}
+ * + * I copy-pasted lang-lisp.js, so this is probably not 100% accurate. + * I used http://wiki.tcl.tk/1019 for the keywords, but tried to only + * include as keywords that had more impact on the program flow + * rather than providing convenience. For example, I included 'if' + * since that provides branching, but left off 'open' since that is more + * like a proc. Add more if it makes sense. + * + * @author pyrios@gmail.com + */ +class LangTcl : Lang() { + companion object { + val fileExtensions: List + get() = listOf("tcl") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + shortcutStylePatterns.new("opn", Regex("^\\{+"), null) + shortcutStylePatterns.new("clo", Regex("^\\}+"), null) + // A line comment that starts with ; + shortcutStylePatterns.new( + Prettify.PR_COMMENT, Regex("^#[^\\r\\n]*"), null + ) + // Whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // A double quoted, possibly multi-line, string. + shortcutStylePatterns.new( + Prettify.PR_STRING, Regex("^\\\"(?:[^\\\"\\\\]|\\\\[\\s\\S])*(?:\\\"|$)"), null + ) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^(?:after|append|apply|array|break|case|catch|continue|error|eval|exec|exit|expr|for|foreach|if|incr|info|proc|return|set|switch|trace|uplevel|upvar|while)\\b"), + null + ) + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex( + "^[+\\-]?(?:[0#]x[0-9a-f]+|\\d+\\/\\d+|(?:\\.\\d+|\\d+(?:\\.\\d*)?)(?:[ed][+\\-]?\\d+)?)", + RegexOption.IGNORE_CASE + ) + ) + // A single quote possibly followed by a word that optionally ends with + // = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, Regex("^\\'(?:-*(?:\\w|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?)?") + ) + // A word that optionally ends with = ! or ?. + fallthroughStylePatterns.new( + Prettify.PR_PLAIN, Regex("^-*(?:[a-z_]|\\\\[\\x21-\\x7e])(?:[\\w-]*|\\\\[\\x21-\\x7e])[=!?]?") + ) + // A printable non-space non-special character + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, Regex("^[^\\w\\t\\n\\r \\xA0()\\\"\\\\\\';]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTex.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTex.kt new file mode 100644 index 0000000..1b5a360 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangTex.kt @@ -0,0 +1,86 @@ +// Copyright (C) 2011 Martin S. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-tex.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Support for tex highlighting as discussed on + * [meta.tex.stackexchange.com](http://meta.tex.stackexchange.com/questions/872/text-immediate-following-double-backslashes-is-highlighted-as-macro-inside-a-code/876#876). + * + * @author Martin S. + */ +class LangTex : Lang() { + companion object { + val fileExtensions: List + get() = listOf("latex", "tex") + } + + override val fallthroughStylePatterns = ArrayList() + override val shortcutStylePatterns = ArrayList() + + init { + + + + // whitespace + shortcutStylePatterns.new( + Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null + ) + // all comments begin with '%' + shortcutStylePatterns.new( + Prettify.PR_COMMENT, + Regex("^%[^\\r\\n]*"), + null + ) + //[PR['PR_DECLARATION'], /^\\([egx]?def|(new|renew|provide)(command|environment))\b/], + // any command starting with a \ and contains + // either only letters (a-z,A-Z), '@' (internal macros) + fallthroughStylePatterns.new( + Prettify.PR_KEYWORD, + Regex("^\\\\[a-zA-Z@]+") + ) + // or contains only one character + fallthroughStylePatterns.new(Prettify.PR_KEYWORD, Regex("^\\\\.")) + // Highlight dollar for math mode and ampersam for tabular + fallthroughStylePatterns.new(Prettify.PR_TYPE, Regex("^[$&]")) + // numeric measurement values with attached units + fallthroughStylePatterns.new( + Prettify.PR_LITERAL, + Regex( + "[+-]?(?:\\.\\d+|\\d+(?:\\.\\d*)?)(cm|em|ex|in|pc|pt|bp|mm)", + RegexOption.IGNORE_CASE + ) + ) + // punctuation usually occurring within commands + fallthroughStylePatterns.new( + Prettify.PR_PUNCTUATION, + Regex("^[{}()\\[\\]=]+") + ) + + + } + + override fun getFileExtensions(): List { + return fileExtensions + } +} \ No newline at end of file diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVb.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVb.kt new file mode 100644 index 0000000..afffff4 --- /dev/null +++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVb.kt @@ -0,0 +1,122 @@ +// Copyright (C) 2009 Google Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +package com.wakaztahir.codeeditor.prettify.lang + +import com.wakaztahir.codeeditor.prettify.parser.Prettify +import com.wakaztahir.codeeditor.prettify.parser.StylePattern +import com.wakaztahir.codeeditor.utils.new + +/** + * This is similar to the lang-vb.js in JavaScript Prettify. + * + * All comments are adapted from the JavaScript Prettify. + * + * + * + * Registers a language handler for various flavors of basic. + * + * + * To use, include prettify.js and this file in your HTML page. + * Then put your code in an HTML tag like + *

+ *
+ *
+ * http://msdn.microsoft.com/en-us/library/aa711638(VS.71).aspx defines the
+ * visual basic grammar lexical grammar.
+ *
+ * @author mikesamuel@gmail.com
+ */
+class LangVb : Lang() {
+    companion object {
+        val fileExtensions: List
+            get() = listOf("vb", "vbs")
+    }
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+    init {
+
+
+
+        // Whitespace
+        shortcutStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex("^[\\t\\n\\r \\xA0\\u2028\\u2029]+"),
+            null
+        )
+        // A double quoted string with quotes escaped by doubling them.
+        // A single character can be suffixed with C.
+        shortcutStylePatterns.new(
+            Prettify.PR_STRING, Regex(
+                "^(?:[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})(?:[\\\"\\u201C\\u201D]c|$)|[\\\"\\u201C\\u201D](?:[^\\\"\\u201C\\u201D]|[\\\"\\u201C\\u201D]{2})*(?:[\\\"\\u201C\\u201D]|$))",
+                RegexOption.IGNORE_CASE
+            ), null
+        )
+        // A comment starts with a single quote and runs until the end of the line.
+        // VB6 apparently allows _ as an escape sequence for newlines though
+        // this is not a documented feature of VB.net.
+        // http://meta.stackoverflow.com/q/121497/137403
+        shortcutStylePatterns.new(
+            Prettify.PR_COMMENT,
+            Regex("^[\\'\\u2018\\u2019](?:_(?:\r\n?|[^\r]?)|[^\\r\\n_\\u2028\\u2029])*"),
+            null
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_KEYWORD,
+            Regex(
+                "^(?:AddHandler|AddressOf|Alias|And|AndAlso|Ansi|As|Assembly|Auto|Boolean|ByRef|Byte|ByVal|Call|Case|Catch|CBool|CByte|CChar|CDate|CDbl|CDec|Char|CInt|Class|CLng|CObj|Const|CShort|CSng|CStr|CType|Date|Decimal|Declare|Default|Delegate|Dim|DirectCast|Do|Double|Each|Else|ElseIf|End|EndIf|Enum|Erase|Error|Event|Exit|Finally|For|Friend|Function|Get|GetType|GoSub|GoTo|Handles|If|Implements|Imports|In|Inherits|Integer|Interface|Is|Let|Lib|Like|Long|Loop|Me|Mod|Module|MustInherit|MustOverride|MyBase|MyClass|Namespace|New|Next|Not|NotInheritable|NotOverridable|Object|On|Option|Optional|Or|OrElse|Overloads|Overridable|Overrides|ParamArray|Preserve|Private|Property|Protected|Public|RaiseEvent|ReadOnly|ReDim|RemoveHandler|Resume|Return|Select|Set|Shadows|Shared|Short|Single|Static|Step|Stop|String|Structure|Sub|SyncLock|Then|Throw|To|Try|TypeOf|Unicode|Until|Variant|Wend|When|While|With|WithEvents|WriteOnly|Xor|EndIf|GoSub|Let|Variant|Wend)\\b",
+                RegexOption.IGNORE_CASE
+            ),
+            null
+        )
+        // A second comment form
+        fallthroughStylePatterns.new(
+            Prettify.PR_COMMENT,
+            Regex("^REM\\b[^\\r\\n\\u2028\\u2029]*", RegexOption.IGNORE_CASE)
+        )
+        // A boolean, numeric, or date literal.
+        fallthroughStylePatterns.new(
+            Prettify.PR_LITERAL,
+            Regex(
+                "^(?:True\\b|False\\b|Nothing\\b|\\d+(?:E[+\\-]?\\d+[FRD]?|[FRDSIL])?|(?:&H[0-9A-F]+|&O[0-7]+)[SIL]?|\\d*\\.\\d+(?:E[+\\-]?\\d+)?[FRD]?|#\\s+(?:\\d+[\\-\\/]\\d+[\\-\\/]\\d+(?:\\s+\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)?|\\d+:\\d+(?::\\d+)?(\\s*(?:AM|PM))?)\\s+#)",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        // An identifier.  Keywords can be turned into identifers
+        // with square brackets, and there may be optional type
+        // characters after a normal identifier in square brackets.
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex(
+                "^(?:(?:[a-z]|_\\w)\\w*(?:\\[[%&@!#]+\\])?|\\[(?:[a-z]|_\\w)\\w*\\])",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        // A run of punctuation
+        fallthroughStylePatterns.new(
+            Prettify.PR_PUNCTUATION,
+            Regex("^[^\\w\\t\\n\\r \\\"\\'\\[\\]\\xA0\\u2018\\u2019\\u201C\\u201D\\u2028\\u2029]+")
+        )
+        // Square brackets
+        fallthroughStylePatterns.new(
+            Prettify.PR_PUNCTUATION, Regex("^(?:\\[|\\])")
+        )
+
+
+    }
+
+    override fun getFileExtensions(): List {
+        return fileExtensions
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVhdl.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVhdl.kt
new file mode 100644
index 0000000..aa3cff4
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangVhdl.kt
@@ -0,0 +1,101 @@
+// Copyright (C) 2010 benoit@ryder.fr
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.lang
+
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+import com.wakaztahir.codeeditor.utils.new
+
+/**
+ * This is similar to the lang-vhdl.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Registers a language handler for VHDL '93.
+ *
+ * Based on the lexical grammar and keywords at
+ * http://www.iis.ee.ethz.ch/~zimmi/download/vhdl93_syntax.html
+ *
+ * @author benoit@ryder.fr
+ */
+class LangVhdl : Lang() {
+    companion object {
+        val fileExtensions: List
+            get() = listOf("vhdl", "vhd")
+    }
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+    init {
+
+
+
+        // Whitespace
+        shortcutStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+"), null
+        )
+        // String, character or bit string
+        fallthroughStylePatterns.new(
+            Prettify.PR_STRING, Regex("^(?:[BOX]?\"(?:[^\\\"]|\"\")*\"|'.')", RegexOption.IGNORE_CASE)
+        )
+        // Comment, from two dashes until end of line.
+        fallthroughStylePatterns.new(
+            Prettify.PR_COMMENT, Regex("^--[^\\r\\n]*")
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_KEYWORD, Regex(
+                "^(?:abs|access|after|alias|all|and|architecture|array|assert|attribute|begin|block|body|buffer|bus|case|component|configuration|constant|disconnect|downto|else|elsif|end|entity|exit|file|for|function|generate|generic|group|guarded|if|impure|in|inertial|inout|is|label|library|linkage|literal|loop|map|mod|nand|new|next|nor|not|null|of|on|open|or|others|out|package|port|postponed|procedure|process|pure|range|record|register|reject|rem|report|return|rol|ror|select|severity|shared|signal|sla|sll|sra|srl|subtype|then|to|transport|type|unaffected|units|until|use|variable|wait|when|while|with|xnor|xor)(?=[^\\w-]|$)",
+                RegexOption.IGNORE_CASE
+            ), null
+        )
+        // Type, predefined or standard
+        fallthroughStylePatterns.new(
+            Prettify.PR_TYPE, Regex(
+                "^(?:bit|bit_vector|character|boolean|integer|real|time|string|severity_level|positive|natural|signed|unsigned|line|text|std_u?logic(?:_vector)?)(?=[^\\w-]|$)",
+                RegexOption.IGNORE_CASE
+            ), null
+        )
+        // Predefined attributes
+        fallthroughStylePatterns.new(
+            Prettify.PR_TYPE, Regex(
+                "^\\'(?:ACTIVE|ASCENDING|BASE|DELAYED|DRIVING|DRIVING_VALUE|EVENT|HIGH|IMAGE|INSTANCE_NAME|LAST_ACTIVE|LAST_EVENT|LAST_VALUE|LEFT|LEFTOF|LENGTH|LOW|PATH_NAME|POS|PRED|QUIET|RANGE|REVERSE_RANGE|RIGHT|RIGHTOF|SIMPLE_NAME|STABLE|SUCC|TRANSACTION|VAL|VALUE)(?=[^\\w-]|$)",
+                RegexOption.IGNORE_CASE
+            ), null
+        )
+        // Number, decimal or based literal
+        fallthroughStylePatterns.new(
+            Prettify.PR_LITERAL, Regex(
+                "^\\d+(?:_\\d+)*(?:#[\\w\\\\.]+#(?:[+\\-]?\\d+(?:_\\d+)*)?|(?:\\.\\d+(?:_\\d+)*)?(?:E[+\\-]?\\d+(?:_\\d+)*)?)",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        // Identifier, basic or extended
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^(?:[a-z]\\w*|\\\\[^\\\\]*\\\\)", RegexOption.IGNORE_CASE)
+        )
+        // Punctuation
+        fallthroughStylePatterns.new(
+            Prettify.PR_PUNCTUATION, Regex("^[^\\w\\t\\n\\r \\xA0\\\"\\'][^\\w\\t\\n\\r \\xA0\\-\\\"\\']*")
+        )
+
+
+    }
+
+    override fun getFileExtensions(): List {
+        return fileExtensions
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangWiki.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangWiki.kt
new file mode 100644
index 0000000..61d2b52
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangWiki.kt
@@ -0,0 +1,112 @@
+// Copyright (C) 2009 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.lang
+
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+import com.wakaztahir.codeeditor.utils.new
+
+/**
+ * This is similar to the lang-wiki.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Registers a language handler for Wiki pages.
+ *
+ * Based on WikiSyntax at http://code.google.com/p/support/wiki/WikiSyntax
+ *
+ * @author mikesamuel@gmail.com
+ */
+class LangWiki : Lang() {
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+
+    companion object {
+        val fileExtensions: List
+            get() = listOf(("wiki"))
+    }
+
+    init {
+        // Whitespace
+        shortcutStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex("^[\\t \\xA0a-gi-z0-9]+"),
+            null
+        )
+        // Wiki formatting
+        shortcutStylePatterns.new(
+            Prettify.PR_PUNCTUATION,
+            Regex("^[=*~\\^\\[\\]]+"),
+            null
+        )
+        // Meta-info like #summary, #labels, etc.
+        fallthroughStylePatterns.new(
+            "lang-wiki.meta",
+            Regex("(?:^^|\r\n?|\n)(#[a-z]+)\\b")
+        )
+        // A WikiWord
+        fallthroughStylePatterns.new(
+            Prettify.PR_LITERAL,
+            Regex("^(?:[A-Z][a-z][a-z0-9]+[A-Z][a-z][a-zA-Z0-9]+)\\b")
+        )
+        // A preformatted block in an unknown language
+        fallthroughStylePatterns.new(
+            "lang-",
+            Regex("^\\{\\{\\{([\\s\\S]+?)\\}\\}\\}")
+        )
+        // A block of source code in an unknown language
+        fallthroughStylePatterns.new("lang-", Regex("^`([^\r\n`]+)`"))
+        // An inline URL.
+        fallthroughStylePatterns.new(
+            Prettify.PR_STRING,
+            Regex(
+                "^https?:\\/\\/[^\\/?#\\s]*(?:\\/[^?#\\s]*)?(?:\\?[^#\\s]*)?(?:#\\S*)?",
+                RegexOption.IGNORE_CASE
+            )
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex("^(?:\r\n|[\\s\\S])[^#=*~^A-Zh\\{`\\[\r\n]*")
+        )
+
+    }
+
+    override fun getFileExtensions(): List = fileExtensions
+
+    internal class LangWikiMeta : Lang() {
+        companion object {
+            val fileExtensions: List
+                get() = listOf(("wiki.meta"))
+        }
+
+        override val fallthroughStylePatterns = ArrayList()
+        override val shortcutStylePatterns = ArrayList()
+
+        init {
+            shortcutStylePatterns.new(
+                Prettify.PR_KEYWORD,
+                Regex("^#[a-z]+", RegexOption.IGNORE_CASE),
+                null
+            )
+        }
+
+        override fun getFileExtensions(): List {
+            return fileExtensions
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangXq.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangXq.kt
new file mode 100644
index 0000000..97ab805
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangXq.kt
@@ -0,0 +1,113 @@
+// Copyright (C) 2011 Patrick Wied
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.lang
+
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+import com.wakaztahir.codeeditor.utils.new
+
+/**
+ * This is similar to the lang-xq.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Registers a language handler for XQuery.
+ *
+ * To use, include prettify.js and this file in your HTML page.
+ * Then put your code in an HTML tag like
+ * 

+ *
+ *
+ * @author Patrick Wied ( patpa7p@live.de )
+ * @version 2010-09-28
+ */
+class LangXq : Lang() {
+    companion object {
+        const val PR_FUNCTION = "fun"
+        const val PR_VARIABLE = "var"
+        val fileExtensions: List
+            get() = listOf("xq", "xquery")
+    }
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+    init {
+
+
+
+        // Matching $var-ia_bles
+        shortcutStylePatterns.new(
+            PR_VARIABLE, Regex("^\\$[A-Za-z0-9_\\-]+"), null
+        )
+        // Matching lt and gt operators
+        // Not the best matching solution but you have to differentiate between the gt operator and the tag closing char
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^[\\s=][<>][\\s=]")
+        )
+        // Matching @Attributes
+        fallthroughStylePatterns.new(Prettify.PR_LITERAL, Regex("^\\@[\\w-]+"))
+        // Matching xml tags
+        fallthroughStylePatterns.new(
+            Prettify.PR_TAG, Regex("^<\\/?[a-z](?:[\\w.:-]*\\w)?|\\/?>$", RegexOption.IGNORE_CASE)
+        )
+        // Matching single or multiline xquery comments -> (:  :)
+        fallthroughStylePatterns.new(
+            Prettify.PR_COMMENT, Regex("^\\(:[\\s\\S]*?:\\)")
+        )
+        // Tokenizing /{}:=;*,[]() as plain
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^[\\/\\{\\};,\\[\\]\\(\\)]$")
+        )
+        // Matching a double or single quoted, possibly multi-line, string.
+        // with the special condition that a { in a string changes to xquery context 
+        fallthroughStylePatterns.new(
+            Prettify.PR_STRING, Regex(
+                "^(?:\\\"(?:[^\\\"\\\\\\{]|\\\\[\\s\\S])*(?:\\\"|$)|\\'(?:[^\\'\\\\\\{]|\\\\[\\s\\S])*(?:\\'|$))",
+                RegexOption.IGNORE_CASE
+            ), null
+        )
+        // Matching standard xquery keywords
+        fallthroughStylePatterns.new(
+            Prettify.PR_KEYWORD,
+            Regex("^(?:xquery|where|version|variable|union|typeswitch|treat|to|then|text|stable|sortby|some|self|schema|satisfies|returns|return|ref|processing-instruction|preceding-sibling|preceding|precedes|parent|only|of|node|namespace|module|let|item|intersect|instance|in|import|if|function|for|follows|following-sibling|following|external|except|every|else|element|descending|descendant-or-self|descendant|define|default|declare|comment|child|cast|case|before|attribute|assert|ascending|as|ancestor-or-self|ancestor|after|eq|order|by|or|and|schema-element|document-node|node|at)\\b")
+        )
+        // Matching standard xquery types
+        fallthroughStylePatterns.new(
+            Prettify.PR_TYPE,
+            Regex("^(?:xs:yearMonthDuration|xs:unsignedLong|xs:time|xs:string|xs:short|xs:QName|xs:Name|xs:long|xs:integer|xs:int|xs:gYearMonth|xs:gYear|xs:gMonthDay|xs:gDay|xs:float|xs:duration|xs:double|xs:decimal|xs:dayTimeDuration|xs:dateTime|xs:date|xs:byte|xs:boolean|xs:anyURI|xf:yearMonthDuration)\\b"),
+            null
+        )
+        // Matching standard xquery functions
+        fallthroughStylePatterns.new(
+            PR_FUNCTION, Regex(
+                "^(?:xp:dereference|xinc:node-expand|xinc:link-references|xinc:link-expand|xhtml:restructure|xhtml:clean|xhtml:add-lists|xdmp:zip-manifest|xdmp:zip-get|xdmp:zip-create|xdmp:xquery-version|xdmp:word-convert|xdmp:with-namespaces|xdmp:version|xdmp:value|xdmp:user-roles|xdmp:user-last-login|xdmp:user|xdmp:url-encode|xdmp:url-decode|xdmp:uri-is-file|xdmp:uri-format|xdmp:uri-content-type|xdmp:unquote|xdmp:unpath|xdmp:triggers-database|xdmp:trace|xdmp:to-json|xdmp:tidy|xdmp:subbinary|xdmp:strftime|xdmp:spawn-in|xdmp:spawn|xdmp:sleep|xdmp:shutdown|xdmp:set-session-field|xdmp:set-response-encoding|xdmp:set-response-content-type|xdmp:set-response-code|xdmp:set-request-time-limit|xdmp:set|xdmp:servers|xdmp:server-status|xdmp:server-name|xdmp:server|xdmp:security-database|xdmp:security-assert|xdmp:schema-database|xdmp:save|xdmp:role-roles|xdmp:role|xdmp:rethrow|xdmp:restart|xdmp:request-timestamp|xdmp:request-status|xdmp:request-cancel|xdmp:request|xdmp:redirect-response|xdmp:random|xdmp:quote|xdmp:query-trace|xdmp:query-meters|xdmp:product-edition|xdmp:privilege-roles|xdmp:privilege|xdmp:pretty-print|xdmp:powerpoint-convert|xdmp:platform|xdmp:permission|xdmp:pdf-convert|xdmp:path|xdmp:octal-to-integer|xdmp:node-uri|xdmp:node-replace|xdmp:node-kind|xdmp:node-insert-child|xdmp:node-insert-before|xdmp:node-insert-after|xdmp:node-delete|xdmp:node-database|xdmp:mul64|xdmp:modules-root|xdmp:modules-database|xdmp:merging|xdmp:merge-cancel|xdmp:merge|xdmp:md5|xdmp:logout|xdmp:login|xdmp:log-level|xdmp:log|xdmp:lock-release|xdmp:lock-acquire|xdmp:load|xdmp:invoke-in|xdmp:invoke|xdmp:integer-to-octal|xdmp:integer-to-hex|xdmp:http-put|xdmp:http-post|xdmp:http-options|xdmp:http-head|xdmp:http-get|xdmp:http-delete|xdmp:hosts|xdmp:host-status|xdmp:host-name|xdmp:host|xdmp:hex-to-integer|xdmp:hash64|xdmp:hash32|xdmp:has-privilege|xdmp:groups|xdmp:group-serves|xdmp:group-servers|xdmp:group-name|xdmp:group-hosts|xdmp:group|xdmp:get-session-field-names|xdmp:get-session-field|xdmp:get-response-encoding|xdmp:get-response-code|xdmp:get-request-username|xdmp:get-request-user|xdmp:get-request-url|xdmp:get-request-protocol|xdmp:get-request-path|xdmp:get-request-method|xdmp:get-request-header-names|xdmp:get-request-header|xdmp:get-request-field-names|xdmp:get-request-field-filename|xdmp:get-request-field-content-type|xdmp:get-request-field|xdmp:get-request-client-certificate|xdmp:get-request-client-address|xdmp:get-request-body|xdmp:get-current-user|xdmp:get-current-roles|xdmp:get|xdmp:function-name|xdmp:function-module|xdmp:function|xdmp:from-json|xdmp:forests|xdmp:forest-status|xdmp:forest-restore|xdmp:forest-restart|xdmp:forest-name|xdmp:forest-delete|xdmp:forest-databases|xdmp:forest-counts|xdmp:forest-clear|xdmp:forest-backup|xdmp:forest|xdmp:filesystem-file|xdmp:filesystem-directory|xdmp:exists|xdmp:excel-convert|xdmp:eval-in|xdmp:eval|xdmp:estimate|xdmp:email|xdmp:element-content-type|xdmp:elapsed-time|xdmp:document-set-quality|xdmp:document-set-property|xdmp:document-set-properties|xdmp:document-set-permissions|xdmp:document-set-collections|xdmp:document-remove-properties|xdmp:document-remove-permissions|xdmp:document-remove-collections|xdmp:document-properties|xdmp:document-locks|xdmp:document-load|xdmp:document-insert|xdmp:document-get-quality|xdmp:document-get-properties|xdmp:document-get-permissions|xdmp:document-get-collections|xdmp:document-get|xdmp:document-forest|xdmp:document-delete|xdmp:document-add-properties|xdmp:document-add-permissions|xdmp:document-add-collections|xdmp:directory-properties|xdmp:directory-locks|xdmp:directory-delete|xdmp:directory-create|xdmp:directory|xdmp:diacritic-less|xdmp:describe|xdmp:default-permissions|xdmp:default-collections|xdmp:databases|xdmp:database-restore-validate|xdmp:database-restore-status|xdmp:database-restore-cancel|xdmp:database-restore|xdmp:database-name|xdmp:database-forests|xdmp:database-backup-validate|xdmp:database-backup-status|xdmp:database-backup-purge|xdmp:database-backup-cancel|xdmp:database-backup|xdmp:database|xdmp:collection-properties|xdmp:collection-locks|xdmp:collection-delete|xdmp:collation-canonical-uri|xdmp:castable-as|xdmp:can-grant-roles|xdmp:base64-encode|xdmp:base64-decode|xdmp:architecture|xdmp:apply|xdmp:amp-roles|xdmp:amp|xdmp:add64|xdmp:add-response-header|xdmp:access|trgr:trigger-set-recursive|trgr:trigger-set-permissions|trgr:trigger-set-name|trgr:trigger-set-module|trgr:trigger-set-event|trgr:trigger-set-description|trgr:trigger-remove-permissions|trgr:trigger-module|trgr:trigger-get-permissions|trgr:trigger-enable|trgr:trigger-disable|trgr:trigger-database-online-event|trgr:trigger-data-event|trgr:trigger-add-permissions|trgr:remove-trigger|trgr:property-content|trgr:pre-commit|trgr:post-commit|trgr:get-trigger-by-id|trgr:get-trigger|trgr:document-scope|trgr:document-content|trgr:directory-scope|trgr:create-trigger|trgr:collection-scope|trgr:any-property-content|thsr:set-entry|thsr:remove-term|thsr:remove-synonym|thsr:remove-entry|thsr:query-lookup|thsr:lookup|thsr:load|thsr:insert|thsr:expand|thsr:add-synonym|spell:suggest-detailed|spell:suggest|spell:remove-word|spell:make-dictionary|spell:load|spell:levenshtein-distance|spell:is-correct|spell:insert|spell:double-metaphone|spell:add-word|sec:users-collection|sec:user-set-roles|sec:user-set-password|sec:user-set-name|sec:user-set-description|sec:user-set-default-permissions|sec:user-set-default-collections|sec:user-remove-roles|sec:user-privileges|sec:user-get-roles|sec:user-get-description|sec:user-get-default-permissions|sec:user-get-default-collections|sec:user-doc-permissions|sec:user-doc-collections|sec:user-add-roles|sec:unprotect-collection|sec:uid-for-name|sec:set-realm|sec:security-version|sec:security-namespace|sec:security-installed|sec:security-collection|sec:roles-collection|sec:role-set-roles|sec:role-set-name|sec:role-set-description|sec:role-set-default-permissions|sec:role-set-default-collections|sec:role-remove-roles|sec:role-privileges|sec:role-get-roles|sec:role-get-description|sec:role-get-default-permissions|sec:role-get-default-collections|sec:role-doc-permissions|sec:role-doc-collections|sec:role-add-roles|sec:remove-user|sec:remove-role-from-users|sec:remove-role-from-role|sec:remove-role-from-privileges|sec:remove-role-from-amps|sec:remove-role|sec:remove-privilege|sec:remove-amp|sec:protect-collection|sec:privileges-collection|sec:privilege-set-roles|sec:privilege-set-name|sec:privilege-remove-roles|sec:privilege-get-roles|sec:privilege-add-roles|sec:priv-doc-permissions|sec:priv-doc-collections|sec:get-user-names|sec:get-unique-elem-id|sec:get-role-names|sec:get-role-ids|sec:get-privilege|sec:get-distinct-permissions|sec:get-collection|sec:get-amp|sec:create-user-with-role|sec:create-user|sec:create-role|sec:create-privilege|sec:create-amp|sec:collections-collection|sec:collection-set-permissions|sec:collection-remove-permissions|sec:collection-get-permissions|sec:collection-add-permissions|sec:check-admin|sec:amps-collection|sec:amp-set-roles|sec:amp-remove-roles|sec:amp-get-roles|sec:amp-doc-permissions|sec:amp-doc-collections|sec:amp-add-roles|search:unparse|search:suggest|search:snippet|search:search|search:resolve-nodes|search:resolve|search:remove-constraint|search:parse|search:get-default-options|search:estimate|search:check-options|prof:value|prof:reset|prof:report|prof:invoke|prof:eval|prof:enable|prof:disable|prof:allowed|ppt:clean|pki:template-set-request|pki:template-set-name|pki:template-set-key-type|pki:template-set-key-options|pki:template-set-description|pki:template-in-use|pki:template-get-version|pki:template-get-request|pki:template-get-name|pki:template-get-key-type|pki:template-get-key-options|pki:template-get-id|pki:template-get-description|pki:need-certificate|pki:is-temporary|pki:insert-trusted-certificates|pki:insert-template|pki:insert-signed-certificates|pki:insert-certificate-revocation-list|pki:get-trusted-certificate-ids|pki:get-template-ids|pki:get-template-certificate-authority|pki:get-template-by-name|pki:get-template|pki:get-pending-certificate-requests-xml|pki:get-pending-certificate-requests-pem|pki:get-pending-certificate-request|pki:get-certificates-for-template-xml|pki:get-certificates-for-template|pki:get-certificates|pki:get-certificate-xml|pki:get-certificate-pem|pki:get-certificate|pki:generate-temporary-certificate-if-necessary|pki:generate-temporary-certificate|pki:generate-template-certificate-authority|pki:generate-certificate-request|pki:delete-template|pki:delete-certificate|pki:create-template|pdf:make-toc|pdf:insert-toc-headers|pdf:get-toc|pdf:clean|p:status-transition|p:state-transition|p:remove|p:pipelines|p:insert|p:get-by-id|p:get|p:execute|p:create|p:condition|p:collection|p:action|ooxml:runs-merge|ooxml:package-uris|ooxml:package-parts-insert|ooxml:package-parts|msword:clean|mcgm:polygon|mcgm:point|mcgm:geospatial-query-from-elements|mcgm:geospatial-query|mcgm:circle|math:tanh|math:tan|math:sqrt|math:sinh|math:sin|math:pow|math:modf|math:log10|math:log|math:ldexp|math:frexp|math:fmod|math:floor|math:fabs|math:exp|math:cosh|math:cos|math:ceil|math:atan2|math:atan|math:asin|math:acos|map:put|map:map|map:keys|map:get|map:delete|map:count|map:clear|lnk:to|lnk:remove|lnk:insert|lnk:get|lnk:from|lnk:create|kml:polygon|kml:point|kml:interior-polygon|kml:geospatial-query-from-elements|kml:geospatial-query|kml:circle|kml:box|gml:polygon|gml:point|gml:interior-polygon|gml:geospatial-query-from-elements|gml:geospatial-query|gml:circle|gml:box|georss:point|georss:geospatial-query|georss:circle|geo:polygon|geo:point|geo:interior-polygon|geo:geospatial-query-from-elements|geo:geospatial-query|geo:circle|geo:box|fn:zero-or-one|fn:years-from-duration|fn:year-from-dateTime|fn:year-from-date|fn:upper-case|fn:unordered|fn:true|fn:translate|fn:trace|fn:tokenize|fn:timezone-from-time|fn:timezone-from-dateTime|fn:timezone-from-date|fn:sum|fn:subtract-dateTimes-yielding-yearMonthDuration|fn:subtract-dateTimes-yielding-dayTimeDuration|fn:substring-before|fn:substring-after|fn:substring|fn:subsequence|fn:string-to-codepoints|fn:string-pad|fn:string-length|fn:string-join|fn:string|fn:static-base-uri|fn:starts-with|fn:seconds-from-time|fn:seconds-from-duration|fn:seconds-from-dateTime|fn:round-half-to-even|fn:round|fn:root|fn:reverse|fn:resolve-uri|fn:resolve-QName|fn:replace|fn:remove|fn:QName|fn:prefix-from-QName|fn:position|fn:one-or-more|fn:number|fn:not|fn:normalize-unicode|fn:normalize-space|fn:node-name|fn:node-kind|fn:nilled|fn:namespace-uri-from-QName|fn:namespace-uri-for-prefix|fn:namespace-uri|fn:name|fn:months-from-duration|fn:month-from-dateTime|fn:month-from-date|fn:minutes-from-time|fn:minutes-from-duration|fn:minutes-from-dateTime|fn:min|fn:max|fn:matches|fn:lower-case|fn:local-name-from-QName|fn:local-name|fn:last|fn:lang|fn:iri-to-uri|fn:insert-before|fn:index-of|fn:in-scope-prefixes|fn:implicit-timezone|fn:idref|fn:id|fn:hours-from-time|fn:hours-from-duration|fn:hours-from-dateTime|fn:floor|fn:false|fn:expanded-QName|fn:exists|fn:exactly-one|fn:escape-uri|fn:escape-html-uri|fn:error|fn:ends-with|fn:encode-for-uri|fn:empty|fn:document-uri|fn:doc-available|fn:doc|fn:distinct-values|fn:distinct-nodes|fn:default-collation|fn:deep-equal|fn:days-from-duration|fn:day-from-dateTime|fn:day-from-date|fn:data|fn:current-time|fn:current-dateTime|fn:current-date|fn:count|fn:contains|fn:concat|fn:compare|fn:collection|fn:codepoints-to-string|fn:codepoint-equal|fn:ceiling|fn:boolean|fn:base-uri|fn:avg|fn:adjust-time-to-timezone|fn:adjust-dateTime-to-timezone|fn:adjust-date-to-timezone|fn:abs|feed:unsubscribe|feed:subscription|feed:subscribe|feed:request|feed:item|feed:description|excel:clean|entity:enrich|dom:set-pipelines|dom:set-permissions|dom:set-name|dom:set-evaluation-context|dom:set-domain-scope|dom:set-description|dom:remove-pipeline|dom:remove-permissions|dom:remove|dom:get|dom:evaluation-context|dom:domains|dom:domain-scope|dom:create|dom:configuration-set-restart-user|dom:configuration-set-permissions|dom:configuration-set-evaluation-context|dom:configuration-set-default-domain|dom:configuration-get|dom:configuration-create|dom:collection|dom:add-pipeline|dom:add-permissions|dls:retention-rules|dls:retention-rule-remove|dls:retention-rule-insert|dls:retention-rule|dls:purge|dls:node-expand|dls:link-references|dls:link-expand|dls:documents-query|dls:document-versions-query|dls:document-version-uri|dls:document-version-query|dls:document-version-delete|dls:document-version-as-of|dls:document-version|dls:document-update|dls:document-unmanage|dls:document-set-quality|dls:document-set-property|dls:document-set-properties|dls:document-set-permissions|dls:document-set-collections|dls:document-retention-rules|dls:document-remove-properties|dls:document-remove-permissions|dls:document-remove-collections|dls:document-purge|dls:document-manage|dls:document-is-managed|dls:document-insert-and-manage|dls:document-include-query|dls:document-history|dls:document-get-permissions|dls:document-extract-part|dls:document-delete|dls:document-checkout-status|dls:document-checkout|dls:document-checkin|dls:document-add-properties|dls:document-add-permissions|dls:document-add-collections|dls:break-checkout|dls:author-query|dls:as-of-query|dbk:convert|dbg:wait|dbg:value|dbg:stopped|dbg:stop|dbg:step|dbg:status|dbg:stack|dbg:out|dbg:next|dbg:line|dbg:invoke|dbg:function|dbg:finish|dbg:expr|dbg:eval|dbg:disconnect|dbg:detach|dbg:continue|dbg:connect|dbg:clear|dbg:breakpoints|dbg:break|dbg:attached|dbg:attach|cvt:save-converted-documents|cvt:part-uri|cvt:destination-uri|cvt:basepath|cvt:basename|cts:words|cts:word-query-weight|cts:word-query-text|cts:word-query-options|cts:word-query|cts:word-match|cts:walk|cts:uris|cts:uri-match|cts:train|cts:tokenize|cts:thresholds|cts:stem|cts:similar-query-weight|cts:similar-query-nodes|cts:similar-query|cts:shortest-distance|cts:search|cts:score|cts:reverse-query-weight|cts:reverse-query-nodes|cts:reverse-query|cts:remainder|cts:registered-query-weight|cts:registered-query-options|cts:registered-query-ids|cts:registered-query|cts:register|cts:query|cts:quality|cts:properties-query-query|cts:properties-query|cts:polygon-vertices|cts:polygon|cts:point-longitude|cts:point-latitude|cts:point|cts:or-query-queries|cts:or-query|cts:not-query-weight|cts:not-query-query|cts:not-query|cts:near-query-weight|cts:near-query-queries|cts:near-query-options|cts:near-query-distance|cts:near-query|cts:highlight|cts:geospatial-co-occurrences|cts:frequency|cts:fitness|cts:field-words|cts:field-word-query-weight|cts:field-word-query-text|cts:field-word-query-options|cts:field-word-query-field-name|cts:field-word-query|cts:field-word-match|cts:entity-highlight|cts:element-words|cts:element-word-query-weight|cts:element-word-query-text|cts:element-word-query-options|cts:element-word-query-element-name|cts:element-word-query|cts:element-word-match|cts:element-values|cts:element-value-ranges|cts:element-value-query-weight|cts:element-value-query-text|cts:element-value-query-options|cts:element-value-query-element-name|cts:element-value-query|cts:element-value-match|cts:element-value-geospatial-co-occurrences|cts:element-value-co-occurrences|cts:element-range-query-weight|cts:element-range-query-value|cts:element-range-query-options|cts:element-range-query-operator|cts:element-range-query-element-name|cts:element-range-query|cts:element-query-query|cts:element-query-element-name|cts:element-query|cts:element-pair-geospatial-values|cts:element-pair-geospatial-value-match|cts:element-pair-geospatial-query-weight|cts:element-pair-geospatial-query-region|cts:element-pair-geospatial-query-options|cts:element-pair-geospatial-query-longitude-name|cts:element-pair-geospatial-query-latitude-name|cts:element-pair-geospatial-query-element-name|cts:element-pair-geospatial-query|cts:element-pair-geospatial-boxes|cts:element-geospatial-values|cts:element-geospatial-value-match|cts:element-geospatial-query-weight|cts:element-geospatial-query-region|cts:element-geospatial-query-options|cts:element-geospatial-query-element-name|cts:element-geospatial-query|cts:element-geospatial-boxes|cts:element-child-geospatial-values|cts:element-child-geospatial-value-match|cts:element-child-geospatial-query-weight|cts:element-child-geospatial-query-region|cts:element-child-geospatial-query-options|cts:element-child-geospatial-query-element-name|cts:element-child-geospatial-query-child-name|cts:element-child-geospatial-query|cts:element-child-geospatial-boxes|cts:element-attribute-words|cts:element-attribute-word-query-weight|cts:element-attribute-word-query-text|cts:element-attribute-word-query-options|cts:element-attribute-word-query-element-name|cts:element-attribute-word-query-attribute-name|cts:element-attribute-word-query|cts:element-attribute-word-match|cts:element-attribute-values|cts:element-attribute-value-ranges|cts:element-attribute-value-query-weight|cts:element-attribute-value-query-text|cts:element-attribute-value-query-options|cts:element-attribute-value-query-element-name|cts:element-attribute-value-query-attribute-name|cts:element-attribute-value-query|cts:element-attribute-value-match|cts:element-attribute-value-geospatial-co-occurrences|cts:element-attribute-value-co-occurrences|cts:element-attribute-range-query-weight|cts:element-attribute-range-query-value|cts:element-attribute-range-query-options|cts:element-attribute-range-query-operator|cts:element-attribute-range-query-element-name|cts:element-attribute-range-query-attribute-name|cts:element-attribute-range-query|cts:element-attribute-pair-geospatial-values|cts:element-attribute-pair-geospatial-value-match|cts:element-attribute-pair-geospatial-query-weight|cts:element-attribute-pair-geospatial-query-region|cts:element-attribute-pair-geospatial-query-options|cts:element-attribute-pair-geospatial-query-longitude-name|cts:element-attribute-pair-geospatial-query-latitude-name|cts:element-attribute-pair-geospatial-query-element-name|cts:element-attribute-pair-geospatial-query|cts:element-attribute-pair-geospatial-boxes|cts:document-query-uris|cts:document-query|cts:distance|cts:directory-query-uris|cts:directory-query-depth|cts:directory-query|cts:destination|cts:deregister|cts:contains|cts:confidence|cts:collections|cts:collection-query-uris|cts:collection-query|cts:collection-match|cts:classify|cts:circle-radius|cts:circle-center|cts:circle|cts:box-west|cts:box-south|cts:box-north|cts:box-east|cts:box|cts:bearing|cts:arc-intersection|cts:and-query-queries|cts:and-query-options|cts:and-query|cts:and-not-query-positive-query|cts:and-not-query-negative-query|cts:and-not-query|css:get|css:convert|cpf:success|cpf:failure|cpf:document-set-state|cpf:document-set-processing-status|cpf:document-set-last-updated|cpf:document-set-error|cpf:document-get-state|cpf:document-get-processing-status|cpf:document-get-last-updated|cpf:document-get-error|cpf:check-transition|alert:spawn-matching-actions|alert:rule-user-id-query|alert:rule-set-user-id|alert:rule-set-query|alert:rule-set-options|alert:rule-set-name|alert:rule-set-description|alert:rule-set-action|alert:rule-remove|alert:rule-name-query|alert:rule-insert|alert:rule-id-query|alert:rule-get-user-id|alert:rule-get-query|alert:rule-get-options|alert:rule-get-name|alert:rule-get-id|alert:rule-get-description|alert:rule-get-action|alert:rule-action-query|alert:remove-triggers|alert:make-rule|alert:make-log-action|alert:make-config|alert:make-action|alert:invoke-matching-actions|alert:get-my-rules|alert:get-all-rules|alert:get-actions|alert:find-matching-rules|alert:create-triggers|alert:config-set-uri|alert:config-set-trigger-ids|alert:config-set-options|alert:config-set-name|alert:config-set-description|alert:config-set-cpf-domain-names|alert:config-set-cpf-domain-ids|alert:config-insert|alert:config-get-uri|alert:config-get-trigger-ids|alert:config-get-options|alert:config-get-name|alert:config-get-id|alert:config-get-description|alert:config-get-cpf-domain-names|alert:config-get-cpf-domain-ids|alert:config-get|alert:config-delete|alert:action-set-options|alert:action-set-name|alert:action-set-module-root|alert:action-set-module-db|alert:action-set-module|alert:action-set-description|alert:action-remove|alert:action-insert|alert:action-get-options|alert:action-get-name|alert:action-get-module-root|alert:action-get-module-db|alert:action-get-module|alert:action-get-description|zero-or-one|years-from-duration|year-from-dateTime|year-from-date|upper-case|unordered|true|translate|trace|tokenize|timezone-from-time|timezone-from-dateTime|timezone-from-date|sum|subtract-dateTimes-yielding-yearMonthDuration|subtract-dateTimes-yielding-dayTimeDuration|substring-before|substring-after|substring|subsequence|string-to-codepoints|string-pad|string-length|string-join|string|static-base-uri|starts-with|seconds-from-time|seconds-from-duration|seconds-from-dateTime|round-half-to-even|round|root|reverse|resolve-uri|resolve-QName|replace|remove|QName|prefix-from-QName|position|one-or-more|number|not|normalize-unicode|normalize-space|node-name|node-kind|nilled|namespace-uri-from-QName|namespace-uri-for-prefix|namespace-uri|name|months-from-duration|month-from-dateTime|month-from-date|minutes-from-time|minutes-from-duration|minutes-from-dateTime|min|max|matches|lower-case|local-name-from-QName|local-name|last|lang|iri-to-uri|insert-before|index-of|in-scope-prefixes|implicit-timezone|idref|id|hours-from-time|hours-from-duration|hours-from-dateTime|floor|false|expanded-QName|exists|exactly-one|escape-uri|escape-html-uri|error|ends-with|encode-for-uri|empty|document-uri|doc-available|doc|distinct-values|distinct-nodes|default-collation|deep-equal|days-from-duration|day-from-dateTime|day-from-date|data|current-time|current-dateTime|current-date|count|contains|concat|compare|collection|codepoints-to-string|codepoint-equal|ceiling|boolean|base-uri|avg|adjust-time-to-timezone|adjust-dateTime-to-timezone|adjust-date-to-timezone|abs)\\b"
+            )
+        )
+        // Matching normal words if none of the previous regular expressions matched
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^[A-Za-z0-9_\\-\\:]+")
+        )
+        // Matching whitespaces
+        fallthroughStylePatterns.new(
+            Prettify.PR_PLAIN, Regex("^[\\t\\n\\r \\xA0]+")
+        )
+
+
+    }
+
+    override fun getFileExtensions(): List = fileExtensions
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangYaml.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangYaml.kt
new file mode 100644
index 0000000..b91ca5f
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/lang/LangYaml.kt
@@ -0,0 +1,98 @@
+// Copyright (C) 2010 ribrdb @ code.google.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.lang
+
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+import com.wakaztahir.codeeditor.utils.new
+
+/**
+ * This is similar to the lang-yaml.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Registers a language handler for YAML.
+ *
+ * @author ribrdb
+ */
+class LangYaml : Lang() {
+    companion object {
+        val fileExtensions: List
+            get() = listOf("yaml", "yml")
+    }
+
+    override val fallthroughStylePatterns = ArrayList()
+    override val shortcutStylePatterns = ArrayList()
+
+    init {
+        shortcutStylePatterns.new(
+            Prettify.PR_PUNCTUATION,
+            Regex("^[:|>?]+"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_DECLARATION,
+            Regex("^%(?:YAML|TAG)[^#\\r\\n]+"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_TYPE,
+            Regex("^[&]\\S+"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_TYPE,
+            Regex("^!\\S*"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_STRING,
+            Regex("^\"(?:[^\\\\\"]|\\\\.)*(?:\"|$)"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_STRING,
+            Regex("^'(?:[^']|'')*(?:'|$)"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_COMMENT,
+            Regex("^#[^\\r\\n]*"),
+            null
+        )
+        shortcutStylePatterns.new(
+            Prettify.PR_PLAIN,
+            Regex("^\\s+"),
+            null
+        )
+        fallthroughStylePatterns.new(
+            Prettify.PR_DECLARATION,
+            Regex("^(?:---|\\.\\.\\.)(?:[\\r\\n]|$)")
+        )
+        fallthroughStylePatterns.new(Prettify.PR_PUNCTUATION, Regex("^-"))
+        fallthroughStylePatterns.new(
+            Prettify.PR_KEYWORD,
+            Regex("^\\w+:[ \\r\\n]")
+        )
+        fallthroughStylePatterns.new(Prettify.PR_PLAIN, Regex("^\\w+"))
+
+
+    }
+
+    override fun getFileExtensions(): List {
+        return fileExtensions
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/CombinePrefixPattern.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/CombinePrefixPattern.kt
new file mode 100644
index 0000000..5d7f06d
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/CombinePrefixPattern.kt
@@ -0,0 +1,365 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.parser
+
+import com.wakaztahir.codeeditor.prettify.parser.Util.join
+import kotlin.Char.Companion.MIN_HIGH_SURROGATE
+import kotlin.Char.Companion.MIN_LOW_SURROGATE
+import kotlin.math.abs
+
+/**
+ * This is similar to the combinePrefixPattern.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ * @author mikesamuel@gmail.com
+ */
+class CombinePrefixPattern {
+    private var capturedGroupIndex = 0
+    private var needToFoldCase = false
+
+    private val escapeCharToCodeUnit: MutableMap = HashMap().apply {
+        this['b'] = 8
+        this['t'] = 9
+        this['n'] = 0xa
+        this['v'] = 0xb
+        this['f'] = 0xc
+        this['r'] = 0xf
+    }
+
+    /**
+     * Given a group of [Regex]s, returns a `RegExp` that globally
+     * matches the union of the sets of strings matched by the input RegExp.
+     * Since it matches globally, if the input strings have a start-of-input
+     * anchor (/^.../), it is ignored for the purposes of unioning.
+     * @param regexs non multiline, non-global regexs.
+     * @return Pattern a global regex.
+     */
+    @Throws(Exception::class)
+    fun combinePrefixPattern(regexs: List): Regex {
+        var ignoreCase = false
+        run {
+            var i: Int = 0
+            val n: Int = regexs.size
+            while (i < n) {
+                val regex: Regex = regexs[i]
+                if (regex.options.contains(RegexOption.IGNORE_CASE)) {
+                    ignoreCase = true
+                } else if (Util.test(
+                        Regex("[a-z]", RegexOption.IGNORE_CASE),
+                        regex.pattern.replace("\\\\[Uu][0-9A-Fa-f]{4}|\\\\[Xx][0-9A-Fa-f]{2}|\\\\[^UuXx]".toRegex(), "")
+                    )
+                ) {
+                    needToFoldCase = true
+                    ignoreCase = false
+                    break
+                }
+                ++i
+            }
+        }
+        val rewritten: MutableList = ArrayList()
+        var i = 0
+        val n = regexs.size
+        while (i < n) {
+            val regex = regexs[i]
+            if (regex.options.contains(RegexOption.MULTILINE)) {
+                throw Exception("Multiline Regex : " + regex.pattern)
+            }
+            rewritten.add("(?:" + allowAnywhereFoldCaseAndRenumberGroups(regex) + ")")
+            ++i
+        }
+        return if (ignoreCase) Regex(
+            join(rewritten, "|"),
+            RegexOption.IGNORE_CASE
+        ) else Regex(
+            join(rewritten, "|")
+        )
+    }
+
+    private fun decodeEscape(charsetPart: String): Int {
+        val cc0: Int = charsetPart[0].code
+        if (cc0 != 92  /* \\ */) {
+            return cc0
+        }
+        val c1 = charsetPart[1]
+        val charCode = escapeCharToCodeUnit[c1]
+        return when {
+            charCode != null -> charCode
+            c1 in '0'..'7' -> charsetPart.substring(1).toInt(8)
+            c1 == 'u' || c1 == 'x' -> charsetPart.substring(2).toInt(16)
+            else -> charsetPart[1].code
+        }
+    }
+
+    private fun encodeEscape(charCode: Int): String {
+        if (charCode < 0x20) {
+            return (if (charCode < 0x10) "\\x0" else "\\x") + charCode.toString(16)
+        }
+//        val ch = String(Character.toChars(charCode))
+        val ch = toChars(charCode).joinToString("")
+        return if (((charCode == '\\'.code) || (charCode == '-'.code) || (charCode == ']'.code) || (charCode == '^'.code))) "\\" + ch else ch
+    }
+
+    private fun toChars(codePoint: Int): CharArray {
+        return if (codePoint ushr 16 == 0) {
+            charArrayOf(codePoint.toChar())
+        } else if (codePoint ushr 16 < 0X10FFFF + 1 ushr 16) {
+            val charArray = CharArray(2)
+            charArray[1] = ((codePoint and 0x3ff) + MIN_LOW_SURROGATE.code).toChar()
+            charArray[0] =
+                ((codePoint ushr 10) + (MIN_HIGH_SURROGATE.code - (0x010000 ushr 10))).toChar()
+            charArray
+        } else {
+            throw IllegalArgumentException("Not a valid unicode code point")
+        }
+    }
+
+    private fun caseFoldCharset(charSet: String?): String {
+        val charsetParts = Util.match(
+            Regex(
+                ("\\\\u[0-9A-Fa-f]{4}"
+                        + "|\\\\x[0-9A-Fa-f]{2}"
+                        + "|\\\\[0-3][0-7]{0,2}"
+                        + "|\\\\[0-7]{1,2}"
+                        + "|\\\\[\\s\\S]"
+                        + "|-"
+                        + "|[^-\\\\]")
+            ), charSet!!.substring(1, charSet.length - 1), true
+        )
+        val ranges: MutableList> = ArrayList()
+        val inverse = charsetParts[0] == "^"
+        val out: MutableList = ArrayList(listOf("["))
+        if (inverse) {
+            out.add("^")
+        }
+        run {
+            var i: Int = if (inverse) 1 else 0
+            val n: Int = charsetParts.size
+            while (i < n) {
+                val p: String = charsetParts[i]
+                if (Util.test(Regex("\\\\[bdsw]", RegexOption.IGNORE_CASE), p)) {  // Don't muck with named groups.
+                    out.add(p)
+                } else {
+                    val start: Int = decodeEscape(p)
+                    var end: Int
+                    if (i + 2 < n && ("-" == charsetParts[i + 1])) {
+                        end = decodeEscape(charsetParts[i + 2])
+                        i += 2
+                    } else {
+                        end = start
+                    }
+                    ranges.add(mutableListOf(start, end))
+                    // If the range might intersect letters, then expand it.
+                    // This case handling is too simplistic.
+                    // It does not deal with non-latin case folding.
+                    // It works for latin source code identifiers though.
+                    if (!(end < 65 || start > 122)) {
+                        if (!(end < 65 || start > 90)) {
+                            ranges.add(mutableListOf(65.coerceAtLeast(start) or 32, end.coerceAtMost(90) or 32))
+                        }
+                        if (!(end < 97 || start > 122)) {
+                            ranges.add(
+                                mutableListOf(
+                                    97.coerceAtLeast(start) and 32.inv(), end.coerceAtMost(122) and 32.inv()
+                                )
+                            )
+                        }
+                    }
+                }
+                ++i
+            }
+        }
+
+        // [[1, 10], [3, 4], [8, 12], [14, 14], [16, 16], [17, 17]]
+        // -> [[1, 12], [14, 14], [16, 17]]
+        ranges.sortWith { a, b -> if (a[0] != b[0]) (a[0] - b[0]) else (b[1] - a[1]) }
+//        Collections.sort(ranges, Comparator { a, b -> if (a[0] !== b[0]) (a[0] - b[0]) else (b[1] - a[1]) })
+        val consolidatedRanges: MutableList> = ArrayList()
+        //        List lastRange = listOf(new Integer[]{0, 0});
+        var lastRange: MutableList = ArrayList(listOf(0, 0))
+        for (i in ranges.indices) {
+            val range = ranges[i]
+            if (range[0] <= lastRange[1] + 1) {
+                lastRange[1] = (lastRange[1]).coerceAtLeast(range[1])
+            } else {
+                // reference of lastRange is added
+                consolidatedRanges.add(range.also { lastRange = it })
+            }
+        }
+        for (i in consolidatedRanges.indices) {
+            val range = consolidatedRanges[i]
+            out.add(encodeEscape(range[0]))
+            if (range[1] > range[0]) {
+                if (range[1] + 1 > range[0]) {
+                    out.add("-")
+                }
+                out.add(encodeEscape(range[1]))
+            }
+        }
+        out.add("]")
+        return join(out)
+    }
+
+    private fun allowAnywhereFoldCaseAndRenumberGroups(regex: Regex): String {
+        // Split into character sets, escape sequences, punctuation strings
+        // like ('(', '(?:', ')', '^'), and runs of characters that do not
+        // include any of the above.
+        val parts = Util.match(
+            Regex(
+                ("(?:"
+                        + "\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]" // a character set
+                        + "|\\\\u[A-Fa-f0-9]{4}" // a unicode escape
+                        + "|\\\\x[A-Fa-f0-9]{2}" // a hex escape
+                        + "|\\\\[0-9]+" // a back-reference or octal escape
+                        + "|\\\\[^ux0-9]" // other escape sequence
+                        + "|\\(\\?[:!=]" // start of a non-capturing group
+                        + "|[\\(\\)\\^]" // start/end of a group, or line start
+                        + "|[^\\x5B\\x5C\\(\\)\\^]+" // run of other characters
+                        + ")")
+            ), regex.pattern, true
+        )
+        val n = parts.size
+
+        // Maps captured group numbers to the number they will occupy in
+        // the output or to -1 if that has not been determined, or to
+        // undefined if they need not be capturing in the output.
+        val capturedGroups: MutableMap = HashMap()
+
+        // Walk over and identify back references to build the capturedGroups
+        // mapping.
+        run {
+            var i: Int = 0
+            var groupIndex: Int = 0
+            while (i < n) {
+                val p: String = parts[i]
+                if ((p == "(")) {
+                    // groups are 1-indexed, so max group index is count of '('
+                    ++groupIndex
+                } else if ('\\' == p[0]) {
+                    try {
+                        val decimalValue: Int = abs(p.substring(1).toInt())
+                        if (decimalValue <= groupIndex) {
+                            capturedGroups[decimalValue] = -1
+                        } else {
+                            // Replace with an unambiguous escape sequence so that
+                            // an octal escape sequence does not turn into a backreference
+                            // to a capturing group from an earlier regex.
+                            parts[i] = encodeEscape(decimalValue)
+                        }
+                    } catch (ex: NumberFormatException) {
+                    }
+                }
+                ++i
+            }
+        }
+
+        // Renumber groups and reduce capturing groups to non-capturing groups
+        // where possible.
+        for (i: Int in capturedGroups.keys) {
+            if (-1 == capturedGroups[i]) {
+                capturedGroups[i] = ++capturedGroupIndex
+            }
+        }
+        run {
+            var i: Int = 0
+            var groupIndex: Int = 0
+            while (i < n) {
+                val p: String = parts[i]
+                if ((p == "(")) {
+                    ++groupIndex
+                    if (capturedGroups[groupIndex] == null) {
+                        parts[i] = "(?:"
+                    }
+                } else if ('\\' == p[0]) {
+                    try {
+                        val decimalValue: Int = abs(p.substring(1).toInt())
+                        if (decimalValue <= groupIndex) {
+                            parts[i] = "\\" + capturedGroups[decimalValue]
+                        }
+                    } catch (ex: NumberFormatException) {
+                    }
+                }
+                ++i
+            }
+        }
+
+        // Remove any prefix anchors so that the output will match anywhere.
+        // ^^ really does mean an anchored match though.
+        for (i in 0 until n) {
+            if (("^" == parts[i]) && "^" != parts[i + 1]) {
+                parts[i] = ""
+            }
+        }
+
+        // Expand letters to groups to handle mixing of case-sensitive and
+        // case-insensitive patterns if necessary.
+        if (regex.options.contains(RegexOption.IGNORE_CASE) && needToFoldCase) {
+            for (i in 0 until n) {
+                val p = parts[i]
+                val ch0: Char = if (p.isNotEmpty()) p[0] else '0'
+                if (p.length >= 2 && ch0 == '[') {
+                    parts[i] = caseFoldCharset(p)
+                } else if (ch0 != '\\') {
+                    // TODO: handle letters in numeric escapes.
+
+
+//                    val sb = StringBuilder()
+//                    val _matcher = Pattern.compile("[a-zA-Z]").matcher(p)
+//                    while (_matcher.find()) {
+//                        val cc = _matcher.group(0).codePointAt(0)
+//                        _matcher.appendReplacement(sb, "")
+//                        sb.append("[").append((cc and 32.inv()).toChar().toString()).append(
+//                            (cc or 32).toChar().toString()
+//                        ).append("]")
+//                    }
+//                    _matcher.appendTail(sb)
+
+                    //This code replaces the above commented code
+                    val mySb = StringBuilder()
+                    val regEx = Regex("[a-zA-Z]")
+                    var startIndex = 0
+                    var matchResult = regEx.find(p, startIndex)
+                    if (matchResult == null) {
+                        mySb.append(p.substring(startIndex, p.length))
+                    }
+                    var appendIndex = 0
+                    while (matchResult != null) {
+                        val cc = matchResult.groups[0]?.value?.get(0)?.code
+                        mySb.append(p.substring(appendIndex,matchResult.range.first))
+                        appendIndex = matchResult.range.last
+                        if (cc != null) {
+                            mySb.append("[").append((cc and 32.inv()).toChar().toString()).append(
+                                (cc or 32).toChar().toString()
+                            ).append("]")
+                        }
+                        startIndex = matchResult.range.last + 1
+                        matchResult = regEx.find(p, startIndex)
+                        if (matchResult == null) {
+                            if (startIndex < p.length) {
+                                mySb.append(p.substring(startIndex, p.length))
+                            }
+                        }
+                    }
+
+//                    println("-----------------")
+//                    println("finding in : $p")
+//                    println("His String : $sb")
+//                    println("Myy String : $mySb")
+                    parts[i] = mySb.toString()
+                }
+            }
+        }
+        return join(parts)
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Job.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Job.kt
new file mode 100644
index 0000000..95b90e4
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Job.kt
@@ -0,0 +1,108 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.parser
+
+/**
+ * This is the job object that similar to those in JavaScript Prettify.
+ *
+ * @author Chan Wai Shing @gmail.com>
+ */
+class Job constructor(basePos: Int = 0, sourceCode: String? = "") {
+    /**
+     * Set the starting point of the source code.
+     *
+     * @return the position
+     */
+    /**
+     * Set the starting point of the source code.
+     *
+     * @param basePos the position
+     */
+    /**
+     * The starting point of the source code.
+     */
+    var basePos: Int
+
+    /**
+     * The source code.
+     */
+    private var sourceCode: String
+
+    /**
+     * The parsed results. nth items are starting position position,
+     * n+1th items are the three-letter style keyword, where n start
+     * from 0.
+     */
+    internal var decorations: List
+
+    /**
+     * Get the source code.
+     *
+     * @return the source code
+     */
+    fun getSourceCode(): String {
+        return sourceCode
+    }
+
+    /**
+     * Set the source code.
+     *
+     * @param sourceCode the source code
+     */
+    fun setSourceCode(sourceCode: String?) {
+        if (sourceCode == null) {
+            throw NullPointerException("argument 'sourceCode' cannot be null")
+        }
+        this.sourceCode = sourceCode
+    }
+
+    /**
+     * Get the parsed results. see [.decorations].
+     *
+     * @return the parsed results
+     */
+    fun getDecorations(): List {
+        return ArrayList(decorations)
+    }
+
+    /**
+     * Set the parsed results. see [.decorations].
+     *
+     * @param decorations the parsed results
+     */
+    fun setDecorations(decorations: List?) {
+        if (decorations == null) {
+            this.decorations = ArrayList()
+            return
+        }
+        this.decorations = ArrayList(decorations)
+    }
+    /**
+     * Constructor.
+     *
+     * @param basePos the starting point of the source code
+     * @param sourceCode the source code
+     */
+    /**
+     * Constructor.
+     */
+    init {
+        if (sourceCode == null) {
+            throw NullPointerException("argument 'sourceCode' cannot be null")
+        }
+        this.basePos = basePos
+        this.sourceCode = sourceCode
+        decorations = ArrayList()
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Prettify.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Prettify.kt
new file mode 100644
index 0000000..bd2427b
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Prettify.kt
@@ -0,0 +1,978 @@
+// Copyright (C) 2006 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.parser
+
+import com.wakaztahir.codeeditor.prettify.lang.*
+import com.wakaztahir.codeeditor.utils.new
+
+open class StylePattern(
+    val tokenStyle: String,
+    val regExp: Regex,
+    val shortcutChars: String? = null
+)
+
+/**
+ * This is similar to the prettify.js in JavaScript Prettify.
+ *
+ * All comments are adapted from the JavaScript Prettify.
+ *
+ *
+ *
+ * Some functions for browser-side pretty printing of code contained in html.
+ *
+ *
+ *
+ *
+ * For a fairly comprehensive set of languages see the
+ * [README](http://google-code-prettify.googlecode.com/svn/trunk/README.html#langs)
+ * file that came with this source.  At a minimum, the lexer should work on a
+ * number of languages including C and friends, Java, Python, Bash, SQL, HTML,
+ * XML, CSS, Javascript, and Makefiles.  It works passably on Ruby, PHP and Awk
+ * and a subset of Perl, but, because of commenting conventions, doesn't work on
+ * Smalltalk, Lisp-like, or CAML-like languages without an explicit lang class.
+ *
+ *
+ * Usage:
+ *  1.  include this source file in an html page via
+ * ``
+ *  1.  define style rules.  See the example page for examples.
+ *  1.  mark the `
` and `` tags in your source with
+ * `class=prettyprint.`
+ * You can also use the (html deprecated) `` tag, but the pretty
+ * printer needs to do more substantial DOM manipulations to support that, so
+ * some css styles may not be preserved.
+ *
+ * That's it.  I wanted to keep the API as simple as possible, so there's no
+ * need to specify which language the code is in, but if you wish, you can add
+ * another class to the `<pre>` or `<code>` element to specify the
+ * language, as in `<pre class="prettyprint lang-java">`.  Any class that
+ * starts with "lang-" followed by a file extension, specifies the file type.
+ * See the "lang-*.js" files in this directory for code that implements
+ * per-language file handlers.
+ *
+ *
+ * Change log:<br></br>
+ * cbeust, 2006/08/22
+ * <blockquote>
+ * Java annotations (start with "@") are now captured as literals ("lit")
+</blockquote> *
+ */
+class Prettify {
+
+    fun interface LangProvider {
+        fun provide(): Lang
+    }
+
+    /**
+     * maps extensions of languages to their lang providers
+     * lang providers are objects that provide language instances for extensions
+     */
+    private val extensionMap by lazy {
+        hashMapOf<String, LangProvider>().apply {
+            // registering extensions for available languages
+            registerExtensions(LangAppollo.fileExtensions) { LangAppollo() }
+            registerExtensions(LangBasic.fileExtensions) { LangBasic() }
+            registerExtensions(LangClj.fileExtensions) { LangClj() }
+            registerExtensions(LangCss.fileExtensions) { LangCss() }
+            registerExtensions(LangDart.fileExtensions) { LangDart() }
+            registerExtensions(LangErlang.fileExtensions) { LangErlang() }
+            registerExtensions(LangGo.fileExtensions) { LangGo() }
+            registerExtensions(LangHs.fileExtensions) { LangHs() }
+            registerExtensions(LangLisp.fileExtensions) { LangLisp() }
+            registerExtensions(LangLlvm.fileExtensions) { LangLlvm() }
+            registerExtensions(LangLua.fileExtensions) { LangLua() }
+            registerExtensions(LangMatlab.fileExtensions) { LangMatlab() }
+            registerExtensions(LangMd.fileExtensions) { LangMd() }
+            registerExtensions(LangMl.fileExtensions) { LangMl() }
+            registerExtensions(LangMumps.fileExtensions) { LangMumps() }
+            registerExtensions(LangN.fileExtensions) { LangN() }
+            registerExtensions(LangPascal.fileExtensions) { LangPascal() }
+            registerExtensions(LangR.fileExtensions) { LangR() }
+            registerExtensions(LangRd.fileExtensions) { LangRd() }
+            registerExtensions(LangScala.fileExtensions) { LangScala() }
+            registerExtensions(LangSql.fileExtensions) { LangSql() }
+            registerExtensions(LangTex.fileExtensions) { LangTex() }
+            registerExtensions(LangVb.fileExtensions) { LangVb() }
+            registerExtensions(LangVhdl.fileExtensions) { LangVhdl() }
+            registerExtensions(LangTcl.fileExtensions) { LangTcl() }
+            registerExtensions(LangWiki.fileExtensions) { LangWiki() }
+            registerExtensions(LangXq.fileExtensions) { LangXq() }
+            registerExtensions(LangYaml.fileExtensions) { LangYaml() }
+            registerExtensions(LangEx.fileExtensions) { LangEx() }
+            registerExtensions(LangKotlin.fileExtensions) { LangKotlin() }
+            registerExtensions(LangLasso.fileExtensions) { LangLasso() }
+            registerExtensions(LangLogtalk.fileExtensions) { LangLogtalk() }
+            registerExtensions(LangSwift.fileExtensions) { LangSwift() }
+            registerExtensions(LangCss.LangCssKeyword.fileExtensions) { LangCss.LangCssKeyword() }
+            registerExtensions(LangCss.LangCssString.fileExtensions) { LangCss.LangCssString() }
+            registerExtensions(LangMatlab.LangMatlabIdentifier.fileExtensions) { LangMatlab.LangMatlabIdentifier() }
+            registerExtensions(LangMatlab.LangMatlabOperator.fileExtensions) { LangMatlab.LangMatlabOperator() }
+            registerExtensions(LangWiki.LangWikiMeta.fileExtensions) { LangWiki.LangWikiMeta() }
+        }
+    }
+
+    // registers extensions for languages
+    private fun MutableMap<String, LangProvider>.registerExtensions(extensions: List<String>, langCreator: () -> Lang) {
+        val provider = object : LangProvider {
+            val lang by lazy { langCreator() }
+            override fun provide(): Lang = lang
+        }
+        for (extension in extensions){
+            this[extension] = provider
+        }
+    }
+
+    private fun getLangFromExtension(extension: String): Lang = extensionMap[extension]?.provide() ?: run {
+        throw IllegalArgumentException("Missing language for extension : $extension")
+    }
+    fun isSupport(extension:String): Boolean {
+        return extensionMap.containsKey(extension)
+    }
+    /** Maps language-specific file extensions to handlers.  */
+    private val langHandlerRegistry: MutableMap<String, CreateSimpleLexer?> = HashMap()
+
+    /** returns a function that produces a list of decorations from source text.
+     *
+     * This code treats ", ', and ` as string delimiters, and \ as a string
+     * escape.  It does not recognize perl's qq() style strings.
+     * It has no special handling for double delimiter escapes as in basic, or
+     * the tripled delimiters used in python, but should work on those regardless
+     * although in those cases a single string literal may be broken up into
+     * multiple adjacent string literals.
+     *
+     * It recognizes C, C++, and shell style comments.
+     *
+     * @param options a set of optional parameters.
+     * @return a function that examines the source code
+     * in the input job and builds the decoration list.
+     */
+    @Throws(Exception::class)
+    internal fun sourceDecorator(options: Map<String?, Any?>): CreateSimpleLexer {
+        val shortcutStylePatterns: MutableList<StylePattern> = ArrayList()
+        val fallthroughStylePatterns: MutableList<StylePattern> = ArrayList()
+        if (Util.getVariableValueAsBoolean(options["tripleQuotedStrings"])) {
+            // '''multi-line-string''', 'single-line-string', and double-quoted
+            shortcutStylePatterns.new(
+                tokenStyle = PR_STRING,
+                regExp = Regex("^(?:'''(?:[^'\\\\]|\\\\[\\s\\S]|'{1,2}(?=[^']))*(?:'''|$)|\"\"\"(?:[^\"\\\\]|\\\\[\\s\\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|'(?:[^\\\\']|\\\\[\\s\\S])*(?:'|$)|\"(?:[^\\\\\"]|\\\\[\\s\\S])*(?:\"|$))"),
+                shortcutChars = null
+            )
+        } else if (Util.getVariableValueAsBoolean(options["multiLineStrings"])) {
+            // 'multi-line-string', "multi-line-string"
+            shortcutStylePatterns.new(
+                PR_STRING,
+                Regex("^(?:'(?:[^\\\\']|\\\\[\\s\\S])*(?:'|$)|\"(?:[^\\\\\"]|\\\\[\\s\\S])*(?:\"|$)|`(?:[^\\\\`]|\\\\[\\s\\S])*(?:`|$))"),
+                null
+            )
+        } else {
+            // 'single-line-string', "single-line-string"
+            shortcutStylePatterns.new(
+                PR_STRING,
+                Regex("^(?:'(?:[^\\\\'\r\n]|\\\\.)*(?:'|$)|\"(?:[^\\\\\"\r\n]|\\\\.)*(?:\"|$))"),
+                null
+            )
+        }
+        if (Util.getVariableValueAsBoolean(options["verbatimStrings"])) {
+            // verbatim-string-literal production from the C# grammar.  See issue 93.
+            fallthroughStylePatterns.new(
+                PR_STRING,
+                Regex("^@\"(?:[^\"]|\"\")*(?:\"|$)"),
+                null
+            )
+        }
+        val hc = options["hashComments"]
+        if (Util.getVariableValueAsBoolean(hc)) {
+            if (Util.getVariableValueAsBoolean(options["cStyleComments"])) {
+                if (hc is Int && hc > 1) {  // multiline hash comments
+                    shortcutStylePatterns.new(
+                        PR_COMMENT,
+                        Regex("^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)"),
+                        null
+                    )
+                } else {
+                    // Stop C preprocessor declarations at an unclosed open comment
+                    shortcutStylePatterns.new(
+                        PR_COMMENT,
+                        Regex("^#(?:(?:define|e(?:l|nd)if|else|error|ifn?def|include|line|pragma|undef|warning)\\b|[^\r\n]*)"),
+                        null
+                    )
+                }
+                // #include <stdio.h>
+                fallthroughStylePatterns.new(
+                    PR_STRING,
+                    Regex("^<(?:(?:(?:\\.\\./)*|/?)(?:[\\w-]+(?:/[\\w-]+)+)?[\\w-]+\\.h(?:h|pp|\\+\\+)?|[a-z]\\w*)>"),
+                    null
+                )
+            } else {
+                shortcutStylePatterns.new(
+                    PR_COMMENT,
+                    Regex("^#[^\r\n]*"),
+                    null
+                )
+            }
+        }
+        if (Util.getVariableValueAsBoolean(options["cStyleComments"])) {
+            fallthroughStylePatterns.new(
+                PR_COMMENT,
+                Regex("^//[^\r\n]*"),
+                null
+            )
+            fallthroughStylePatterns.new(
+                PR_COMMENT,
+                Regex("^/\\*[\\s\\S]*?(?:\\*/|$)"),
+                null
+            )
+        }
+        val regexLiterals = options["regexLiterals"]
+        if (Util.getVariableValueAsBoolean(regexLiterals)) {
+            /**
+             * @const
+             */
+            // Javascript treat true as 1
+            val regexExcls = if (Util.getVariableValueAsInteger(regexLiterals) > 1) "" // Multiline regex literals
+            else "\n\r"
+
+            /**
+             * @const
+             */
+            val regexAny = if (regexExcls.isNotEmpty()) "." else "[\\S\\s]"
+
+            /**
+             * @const
+             */
+            val REGEX_LITERAL =  // A regular expression literal starts with a slash that is
+            // not followed by * or / so that it is not confused with
+                // comments.
+                ("/(?=[^/*" + regexExcls + "])" // and then contains any number of raw characters,
+                        + "(?:[^/\\x5B\\x5C" + regexExcls + "]" // escape sequences (\x5C),
+                        + "|\\x5C" + regexAny // or non-nesting character sets (\x5B\x5D);
+                        + "|\\x5B(?:[^\\x5C\\x5D" + regexExcls + "]"
+                        + "|\\x5C" + regexAny + ")*(?:\\x5D|$))+" // finally closed by a /.
+                        + "/")
+            fallthroughStylePatterns.new(
+                "lang-regex",
+                Regex("^$REGEXP_PRECEDES_PATTERN($REGEX_LITERAL)")
+            )
+        }
+        val types = options["types"] as? Regex
+        if (Util.getVariableValueAsBoolean(types) && types != null) {
+            fallthroughStylePatterns.new(PR_TYPE, types)
+        }
+        var keywords = options["keywords"] as String?
+        if (keywords != null) {
+            keywords = keywords.replace("^ | $".toRegex(), "")
+            if (keywords.isNotEmpty()) {
+                fallthroughStylePatterns.new(
+                    PR_KEYWORD,
+                    Regex("^(?:" + keywords.replace("[\\s,]+".toRegex(), "|") + ")\\b"),
+                    null
+                )
+            }
+        }
+
+        shortcutStylePatterns.new(
+            PR_PLAIN,
+            Regex("^\\s+"),
+            null
+        )
+
+        // TODO(mikesamuel): recognize non-latin letters and numerals in idents
+        fallthroughStylePatterns.new(
+            PR_LITERAL,
+            Regex("^@[a-z_$][a-z_$@0-9]*", RegexOption.IGNORE_CASE),
+            null
+        )
+        fallthroughStylePatterns.new(
+            PR_TYPE,
+            Regex("^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\\w+_t\\b)"),
+            null
+        )
+        fallthroughStylePatterns.new(
+            PR_PLAIN,
+            Regex("^[a-z_$][a-z_$@0-9]*", RegexOption.IGNORE_CASE),
+            null
+        )
+        fallthroughStylePatterns.new(
+            PR_LITERAL,
+            Regex(
+                "^(?:" // A hex number
+                        + "0x[a-f0-9]+" // or an octal or decimal number,
+                        + "|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)" // possibly in scientific notation
+                        + "(?:e[+\\-]?\\d+)?"
+                        + ')' // with an optional modifier like UL for unsigned long
+                        + "[a-z]*", RegexOption.IGNORE_CASE
+            ),
+            null
+        )
+        // Don't treat escaped quotes in bash as starting strings.
+        // See issue 144.
+        fallthroughStylePatterns.new(
+            PR_PLAIN,
+            Regex("^\\\\[\\s\\S]?"),
+            null
+        )
+
+        // The Bash man page says
+
+        // A word is a sequence of characters considered as a single
+        // unit by GRUB. Words are separated by metacharacters,
+        // which are the following plus space, tab, and newline: { }
+        // | & $ ; < >
+        // ...
+
+        // A word beginning with # causes that word and all remaining
+        // characters on that line to be ignored.
+
+        // which means that only a '#' after /(?:^|[{}|&$;<>\s])/ starts a
+        // comment but empirically
+        // $ echo {#}
+        // {#}
+        // $ echo \$#
+        // $#
+        // $ echo }#
+        // }#
+
+        // so /(?:^|[|&;<>\s])/ is more appropriate.
+
+        // http://gcc.gnu.org/onlinedocs/gcc-2.95.3/cpp_1.html#SEC3
+        // suggests that this definition is compatible with a
+        // default mode that tries to use a single token definition
+        // to recognize both bash/python style comments and C
+        // preprocessor directives.
+
+        // This definition of punctuation does not include # in the list of
+        // follow-on exclusions, so # will not be broken before if preceeded
+        // by a punctuation character.  We could try to exclude # after
+        // [|&;<>] but that doesn't seem to cause many major problems.
+        // If that does turn out to be a problem, we should change the below
+        // when hc is truthy to include # in the run of punctuation characters
+        // only when not followint [|&;<>].
+        var punctuation = "^.[^\\s\\w.$@'\"`/\\\\]*"
+        if (Util.getVariableValueAsBoolean(options["regexLiterals"])) {
+            punctuation += "(?!\\s*/)"
+        }
+        fallthroughStylePatterns.new(
+            PR_PUNCTUATION,
+            Regex(punctuation),
+            null
+        )
+        return CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns)
+    }
+
+    /** Register a language handler for the given file extensions.
+     * @param handler a function from source code to a list
+     * of decorations.  Takes a single argument job which describes the
+     * state of the computation.   The single parameter has the form
+     * `{
+     * sourceCode: {string} as plain text.
+     * decorations: {Array.<number|string>} an array of style classes
+     * preceded by the position at which they start in
+     * job.sourceCode in order.
+     * The language handler should assigned this field.
+     * basePos: {int} the position of source in the larger source chunk.
+     * All positions in the output decorations array are relative
+     * to the larger source chunk.
+     * } `
+     * @param fileExtensions
+     */
+    @Throws(Exception::class)
+    internal fun registerLangHandler(handler: CreateSimpleLexer, fileExtensions: List<String>) {
+        for(extension in fileExtensions){
+            if (langHandlerRegistry[extension] == null) {
+                langHandlerRegistry[extension] = handler
+            } else {
+                throw Exception("cannot override language handler for extension $extension")
+            }
+        }
+    }
+
+    /**
+     * Get the parser for the extension specified.
+     * @param extension the file extension, if null, default parser will be returned
+     * @return the parser
+     */
+    fun getLexerForExtension(extension: String): CreateSimpleLexer {
+        val lang = getLangFromExtension(extension)
+        return langHandlerRegistry[extension] ?: getLexerForLanguage(lang)
+    }
+
+    fun getLexerForLanguageProvider(langProvider: LangProvider): CreateSimpleLexer {
+        val lang = langProvider.provide()
+        var lexer: CreateSimpleLexer? = null
+        for (extension in lang.getFileExtensions()) {
+            lexer = langHandlerRegistry[extension]
+            if (lexer != null) break
+        }
+        return lexer ?: getLexerForLanguage(lang)
+    }
+
+
+    private fun getLexerForLanguage(lang: Lang): CreateSimpleLexer {
+        return CreateSimpleLexer(lang.shortcutStylePatterns, lang.fallthroughStylePatterns).also { lexer ->
+            lang.getFileExtensions().forEach {
+                langHandlerRegistry[it] = lexer
+            }
+        }
+    }
+
+    // CAVEAT: this does not properly handle the case where a regular
+    // expression immediately follows another since a regular expression may
+    // have flags for case-sensitivity and the like.  Having regexp tokens
+    // adjacent is not valid in any language I'm aware of, so I'm punting.
+    // TODO: maybe style special characters inside a regexp as punctuation.
+    init {
+        try {
+            var decorateSourceMap: MutableMap<String?, Any?> = HashMap()
+            decorateSourceMap["keywords"] = ALL_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["multiLineStrings"] = true
+            decorateSourceMap["regexLiterals"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("default-code"))
+            var shortcutStylePatterns: MutableList<StylePattern> = ArrayList()
+            var fallthroughStylePatterns: MutableList<StylePattern> = ArrayList()
+            fallthroughStylePatterns.new(PR_PLAIN, Regex("^[^<?]+"))
+            fallthroughStylePatterns.new(
+                PR_DECLARATION, Regex("^<!\\w[^>]*(?:>|$)")
+            )
+            fallthroughStylePatterns.new(
+                PR_COMMENT, Regex("^<!--[\\s\\S]*?(?:-->|$)")
+            )
+            // Unescaped content in an unknown language
+            fallthroughStylePatterns.new(
+                "lang-", Regex("^<\\?([\\s\\S]+?)(?:\\?>|$)")
+            )
+            fallthroughStylePatterns.new(
+                "lang-", Regex("^<%([\\s\\S]+?)(?:%>|$)")
+            )
+            fallthroughStylePatterns.new(
+                PR_PUNCTUATION, Regex("^(?:<[%?]|[%?]>)")
+            )
+            fallthroughStylePatterns.new(
+                "lang-", Regex("^<xmp\\b[^>]*>([\\s\\S]+?)</xmp\\b[^>]*>", RegexOption.IGNORE_CASE)
+            )
+            // Unescaped content in javascript.  (Or possibly vbscript).
+            fallthroughStylePatterns.new(
+                "lang-js",
+                Regex("^<script\\b[^>]*>([\\s\\S]*?)(</script\\b[^>]*>)", RegexOption.IGNORE_CASE)
+            )
+            // Contains unescaped stylesheet content
+            fallthroughStylePatterns.new(
+                "lang-css",
+                Regex("^<style\\b[^>]*>([\\s\\S]*?)(</style\\b[^>]*>)", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-in.tag", Regex("^(</?[a-z][^<>]*>)", RegexOption.IGNORE_CASE)
+            )
+            registerLangHandler(
+                CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns),
+                listOf("default-markup", "htm", "html", "mxml", "xhtml", "xml", "xsl")
+            )
+            shortcutStylePatterns = ArrayList()
+            fallthroughStylePatterns = ArrayList()
+            shortcutStylePatterns.new(
+                PR_PLAIN,
+                Regex("^[\\s]+"),
+                null
+            )
+            shortcutStylePatterns.new(
+                PR_ATTRIB_VALUE,
+                Regex("^(?:\"[^\"]*\"?|'[^']*'?)"),
+                null
+            )
+            fallthroughStylePatterns.new(
+                PR_TAG, Regex("^^</?[a-z](?:[\\w.:-]*\\w)?|/?>$", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                PR_ATTRIB_NAME,
+                Regex("^(?!style[\\s=]|on)[a-z](?:[\\w:-]*\\w)?", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-uq.val", Regex(
+                    "^=\\s*([^>'\"\\s]*(?:[^>'\"\\s/]|/(?=\\s)))",
+                    RegexOption.IGNORE_CASE
+                )
+            )
+            fallthroughStylePatterns.new(PR_PUNCTUATION, Regex("^[=<>/]+"))
+            fallthroughStylePatterns.new(
+                "lang-js", Regex("^on\\w+\\s*=\\s*\"([^\"]+)\"", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-js", Regex("^on\\w+\\s*=\\s*'([^']+)'", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-js", Regex("^on\\w+\\s*=\\s*([^\"'>\\s]+)", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-css", Regex("^style\\s*=\\s*\"([^\"]+)\"", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-css", Regex("^style\\s*=\\s*'([^']+)'", RegexOption.IGNORE_CASE)
+            )
+            fallthroughStylePatterns.new(
+                "lang-css", Regex("^style\\s*=\\s\\*([^\"'>\\s]+)", RegexOption.IGNORE_CASE)
+            )
+            registerLangHandler(
+                CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns),
+                listOf("in.tag")
+            )
+            shortcutStylePatterns = ArrayList()
+            fallthroughStylePatterns = ArrayList()
+            fallthroughStylePatterns.new(PR_ATTRIB_VALUE, Regex("^[\\s\\S]+"))
+            registerLangHandler(
+                CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns),
+                listOf("uq.val")
+            )
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = CPP_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["types"] = C_TYPES
+            registerLangHandler(
+                sourceDecorator(decorateSourceMap),
+                listOf("c", "cc", "cpp", "cxx", "cyc", "m")
+            )
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = "null,true,false"
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("json"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = CSHARP_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["verbatimStrings"] = true
+            decorateSourceMap["types"] = C_TYPES
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("cs"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = JAVA_KEYWORDS
+            decorateSourceMap["cStyleComments"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("java"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = SH_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["multiLineStrings"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("bash", "bsh", "csh", "sh"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = PYTHON_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["multiLineStrings"] = true
+            decorateSourceMap["tripleQuotedStrings"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("cv", "py", "python"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = PERL_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["multiLineStrings"] = true
+            decorateSourceMap["regexLiterals"] = 2 // multiline regex literals
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("perl", "pl", "pm"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = RUBY_KEYWORDS
+            decorateSourceMap["hashComments"] = true
+            decorateSourceMap["multiLineStrings"] = true
+            decorateSourceMap["regexLiterals"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("rb", "ruby"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = JSCRIPT_KEYWORDS
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["regexLiterals"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("javascript", "js"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = COFFEE_KEYWORDS
+            decorateSourceMap["hashComments"] = 3 // ### style block comments
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["multilineStrings"] = true
+            decorateSourceMap["tripleQuotedStrings"] = true
+            decorateSourceMap["regexLiterals"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("coffee"))
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = RUST_KEYWORDS
+            decorateSourceMap["cStyleComments"] = true
+            decorateSourceMap["multilineStrings"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("rc", "rs", "rust"))
+            shortcutStylePatterns = ArrayList()
+            fallthroughStylePatterns = ArrayList()
+            fallthroughStylePatterns.new(PR_STRING, Regex("^[\\s\\S]+"))
+            registerLangHandler(
+                CreateSimpleLexer(shortcutStylePatterns, fallthroughStylePatterns),
+                listOf("regex")
+            )
+            /**
+             * Registers a language handler for Protocol Buffers as described at
+             * http://code.google.com/p/protobuf/.
+             *
+             * Based on the lexical grammar at
+             * http://research.microsoft.com/fsharp/manual/spec2.aspx#_Toc202383715
+             *
+             * @author mikesamuel@gmail.com
+             */
+            decorateSourceMap = HashMap()
+            decorateSourceMap["keywords"] = ("bytes,default,double,enum,extend,extensions,false,"
+                    + "group,import,max,message,option,"
+                    + "optional,package,repeated,required,returns,rpc,service,"
+                    + "syntax,to,true")
+            decorateSourceMap["types"] = Regex("^(bool|(double|s?fixed|[su]?int)(32|64)|float|string)\\b")
+            decorateSourceMap["cStyleComments"] = true
+            registerLangHandler(sourceDecorator(decorateSourceMap), listOf("proto"))
+        } catch (ex: Exception) {
+            throw ex
+        }
+    }
+
+    inner class CreateSimpleLexer(
+        shortcutStylePatterns: List<StylePattern>,
+        private var fallthroughStylePatterns: List<StylePattern>
+    ) {
+        private var shortcuts: MutableMap<Char, StylePattern> = HashMap()
+        private var tokenizer: Regex
+        private var nPatterns: Int
+
+        /** Given triples of [style, pattern, context] returns a lexing function,
+         * The lexing function interprets the patterns to find token boundaries and
+         * returns a decoration list of the form
+         * [index_0, style_0, index_1, style_1, ..., index_n, style_n]
+         * where index_n is an index into the sourceCode, and style_n is a style
+         * constant like PR_PLAIN.  index_n-1 <= index_n, and style_n-1 applies to
+         * all characters in sourceCode[index_n-1:index_n].
+         *
+         * The stylePatterns is a list whose elements have the form
+         * [style : string, pattern : RegExp, DEPRECATED, shortcut : string].
+         *
+         * Style is a style constant like PR_PLAIN, or can be a string of the
+         * form 'lang-FOO', where FOO is a language extension describing the
+         * language of the portion of the token in $1 after pattern executes.
+         * E.g., if style is 'lang-lisp', and group 1 contains the text
+         * '(hello (world))', then that portion of the token will be passed to the
+         * registered lisp handler for formatting.
+         * The text before and after group 1 will be restyled using this decorator
+         * so decorators should take care that this doesn't result in infinite
+         * recursion.  For example, the HTML lexer rule for SCRIPT elements looks
+         * something like ['lang-js', /<[s]cript>(.+?)<\/script>/].  This may match
+         * '<script>foo()<\/script>', which would cause the current decorator to
+        be called with '<script>' which would not match the same rule since
+        group 1 must not be empty, so it would be instead styled as PR_TAG by
+        the generic tag rule.  The handler registered for the 'js' extension would
+        then be called with 'foo()', and finally, the current decorator would
+        be called with '<\/script>' which would not match the original rule and
+        so the generic tag rule would identify it as a tag.
+
+        Pattern must only match prefixes, and if it matches a prefix, then that
+        match is considered a token with the same style.
+
+        Context is applied to the last non-whitespace, non-comment token
+        recognized.
+
+        Shortcut is an optional string of characters, any of which, if the first
+        character, gurantee that this pattern and only this pattern matches.
+
+        @param shortcutStylePatterns patterns that always start with
+        a known character.  Must have a shortcut string.
+        @param fallthroughStylePatterns patterns that will be tried in
+        order if the shortcut ones fail.  May have shortcuts.
+        </script> */
+        init {
+            val allPatterns: MutableList<StylePattern> = shortcutStylePatterns.toMutableList()
+            allPatterns.addAll(fallthroughStylePatterns)
+            val allRegexs: MutableList<Regex> = ArrayList()
+            val regexKeys: MutableMap<String, Any?> = HashMap()
+            allPatterns.forEach { pattern ->
+                val shortcutChars = pattern.shortcutChars
+                if (shortcutChars != null) {
+                    var c = shortcutChars.length
+                    while (--c >= 0) {
+                        shortcuts[shortcutChars[c]] = pattern
+                    }
+                }
+                val regex = pattern.regExp
+                val k = regex.pattern
+                if (regexKeys[k] == null) {
+                    allRegexs.add(regex)
+                    regexKeys[k] = Any()
+                }
+            }
+            allRegexs.add("[\u0000-\\uffff]".toRegex())
+            tokenizer = CombinePrefixPattern().combinePrefixPattern(allRegexs) //todo change this function
+            nPatterns = fallthroughStylePatterns.size
+        }
+
+        /**
+         * Lexes job.sourceCode and produces an output array job.decorations of
+         * style classes preceded by the position at which they start in
+         * job.sourceCode in order.
+         *
+         * @param job an object like <pre>{
+         * sourceCode: {string} sourceText plain text,
+         * basePos: {int} position of job.sourceCode in the larger chunk of
+         * sourceCode.
+         * }</pre>
+         */
+        fun decorate(job: Job) {
+            val sourceCode = job.getSourceCode()
+            val basePos = job.basePos
+
+            /** Even entries are positions in source in ascending order.  Odd enties
+             * are style markers (e.g., PR_COMMENT) that run from that position until
+             * the end.
+             * @type {Array.<number></number>|string>}
+             */
+            val decorations: MutableList<Any> = ArrayList(listOf(basePos, PR_PLAIN))
+            var pos = 0 // index into sourceCode
+            val tokens = Util.match(tokenizer, sourceCode, true)
+            val styleCache: MutableMap<String, String?> = HashMap()
+            var ti = 0
+            val nTokens = tokens.size
+            while (ti < nTokens) {
+                val token = tokens[ti]
+                var style = styleCache[token]
+                var match: List<String>? = null
+                var isEmbedded: Boolean
+                if (style != null) {
+                    isEmbedded = false
+                } else {
+                    var patternParts = shortcuts[token[0]]
+                    if (patternParts != null) {
+                        match = Util.match(patternParts.regExp, token, false)
+                        style = patternParts.tokenStyle
+                    } else {
+                        for (i in 0 until nPatterns) {
+                            patternParts = fallthroughStylePatterns[i]
+                            match = Util.match(patternParts.regExp, token, false)
+                            if (match.isNotEmpty()) {
+                                style = patternParts.tokenStyle
+                                break
+                            }
+                        }
+                        if (match!!.isEmpty()) {  // make sure that we make progress
+                            style = PR_PLAIN
+                        }
+                    }
+                    isEmbedded = style != null && style.length >= 5 && style.startsWith("lang-")
+                    if (isEmbedded && match.size <= 1) {
+                        isEmbedded = false
+                        style = PR_SOURCE
+                    }
+                    if (!isEmbedded) {
+                        styleCache[token] = style
+                    }
+                }
+                val tokenStart = pos
+                pos += token.length
+                if (!isEmbedded) {
+                    decorations.add(basePos + tokenStart)
+                    if (style != null) {
+                        decorations.add(style)
+                    }
+                } else {  // Treat group 1 as an embedded block of source code.
+                    val embeddedSource = match!![1]
+                    var embeddedSourceStart = token.indexOf(embeddedSource)
+                    var embeddedSourceEnd = embeddedSourceStart + embeddedSource.length
+                    if (match.size > 2) {
+                        // If embeddedSource can be blank, then it would match at the
+                        // beginning which would cause us to infinitely recurse on the
+                        // entire token, so we catch the right context in match[2].
+                        embeddedSourceEnd = token.length - match[2].length
+                        embeddedSourceStart = embeddedSourceEnd - embeddedSource.length
+                    }
+                    val lang = style!!.substring(5)
+                    // Decorate the left of the embedded source
+                    appendDecorations(
+                        basePos + tokenStart,
+                        token.substring(0, embeddedSourceStart),
+                        this, decorations
+                    )
+                    // Decorate the embedded source
+                    appendDecorations(
+                        basePos + tokenStart + embeddedSourceStart,
+                        embeddedSource,
+                        getLexerForExtension(lang),
+                        decorations
+                    )
+                    // Decorate the right of the embedded section
+                    appendDecorations(
+                        basePos + tokenStart + embeddedSourceEnd,
+                        token.substring(embeddedSourceEnd),
+                        this, decorations
+                    )
+                }
+                ++ti
+            }
+            job.setDecorations(Util.removeDuplicates(decorations, job.getSourceCode()))
+        }
+    }
+
+    companion object {
+
+        // Keyword lists for various languages.
+        private const val FLOW_CONTROL_KEYWORDS = "break,continue,do,else,for,if,return,while"
+        private const val C_KEYWORDS = (FLOW_CONTROL_KEYWORDS + "," + "auto,case,char,const,default,"
+                + "double,enum,extern,float,goto,inline,int,long,register,short,signed,"
+                + "sizeof,static,struct,switch,typedef,union,unsigned,void,volatile")
+        private const val COMMON_KEYWORDS = (C_KEYWORDS + "," + "catch,class,delete,false,import,"
+                + "new,operator,private,protected,public,this,throw,true,try,typeof")
+        const val CPP_KEYWORDS = (COMMON_KEYWORDS + "," + "alignof,align_union,asm,axiom,bool,"
+                + "concept,concept_map,const_cast,constexpr,decltype,delegate,"
+                + "dynamic_cast,explicit,export,friend,generic,late_check,"
+                + "mutable,namespace,nullptr,property,reinterpret_cast,static_assert,"
+                + "static_cast,template,typeid,typename,using,virtual,where")
+        const val JAVA_KEYWORDS = (COMMON_KEYWORDS + ","
+                + "abstract,assert,boolean,byte,extends,final,finally,implements,import,"
+                + "instanceof,interface,null,native,package,strictfp,super,synchronized,"
+                + "throws,transient")
+        private const val KOTLIN_KEYWORDS = (JAVA_KEYWORDS + ","
+                + "as,as?,fun,in,!in,object,typealias,val,var,when,by,constructor,delegate,dynamic,field,"
+                + "file,get,init,set,value,where,actual,annotation,companion,crossinline,data,enum,expect,"
+                + "external,field,infix,inline,inner,internal,it,lateinit,noinline,open,operator,out,override,"
+                + "reified,sealed,suspend,tailrec,vararg")
+        const val RUST_KEYWORDS = (FLOW_CONTROL_KEYWORDS + "," + "as,assert,const,copy,drop,"
+                + "enum,extern,fail,false,fn,impl,let,log,loop,match,mod,move,mut,priv,"
+                + "pub,pure,ref,self,static,struct,true,trait,type,unsafe,use")
+        const val CSHARP_KEYWORDS = (JAVA_KEYWORDS + ","
+                + "as,base,by,checked,decimal,delegate,descending,dynamic,event,"
+                + "fixed,foreach,from,group,implicit,in,internal,into,is,let,"
+                + "lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,"
+                + "sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,"
+                + "var,virtual,where")
+        const val COFFEE_KEYWORDS = ("all,and,by,catch,class,else,extends,false,finally,"
+                + "for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,"
+                + "throw,true,try,unless,until,when,while,yes")
+        const val JSCRIPT_KEYWORDS = (COMMON_KEYWORDS + ","
+                + "debugger,eval,export,function,get,null,set,undefined,var,with,"
+                + "Infinity,NaN")
+        const val PERL_KEYWORDS = ("caller,delete,die,do,dump,elsif,eval,exit,foreach,for,"
+                + "goto,if,import,last,local,my,next,no,our,print,package,redo,require,"
+                + "sub,undef,unless,until,use,wantarray,while,BEGIN,END")
+        const val PYTHON_KEYWORDS = (FLOW_CONTROL_KEYWORDS + "," + "and,as,assert,class,def,del,"
+                + "elif,except,exec,finally,from,global,import,in,is,lambda,"
+                + "nonlocal,not,or,pass,print,raise,try,with,yield,"
+                + "False,True,None")
+        const val RUBY_KEYWORDS = (FLOW_CONTROL_KEYWORDS + "," + "alias,and,begin,case,class,"
+                + "def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,"
+                + "rescue,retry,self,super,then,true,undef,unless,until,when,yield,"
+                + "BEGIN,END")
+        const val SH_KEYWORDS = (FLOW_CONTROL_KEYWORDS + "," + "case,done,elif,esac,eval,fi,"
+                + "function,in,local,set,then,until")
+        const val ALL_KEYWORDS = (CPP_KEYWORDS + "," + KOTLIN_KEYWORDS + "," + CSHARP_KEYWORDS
+                + "," + JSCRIPT_KEYWORDS + "," + PERL_KEYWORDS + "," + PYTHON_KEYWORDS + "," + RUBY_KEYWORDS
+                + "," + SH_KEYWORDS)
+        val C_TYPES =
+            Regex("^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\\d*)\\b")
+        // token style names.  correspond to css classes
+        /**
+         * token style for a string literal
+         */
+        const val PR_STRING = "str"
+
+        /**
+         * token style for a keyword
+         */
+        const val PR_KEYWORD = "kwd"
+
+        /**
+         * token style for a comment
+         */
+        const val PR_COMMENT = "com"
+
+        /**
+         * token style for a type
+         */
+        const val PR_TYPE = "typ"
+
+        /**
+         * token style for a literal value.  e.g. 1, null, true.
+         */
+        const val PR_LITERAL = "lit"
+
+        /**
+         * token style for a punctuation string.
+         */
+        const val PR_PUNCTUATION = "pun"
+
+        /**
+         * token style for a plain text.
+         */
+        const val PR_PLAIN = "pln"
+
+        /**
+         * token style for an sgml tag.
+         */
+        const val PR_TAG = "tag"
+
+        /**
+         * token style for a markup declaration such as a DOCTYPE.
+         */
+        const val PR_DECLARATION = "dec"
+
+        /**
+         * token style for embedded source.
+         */
+        const val PR_SOURCE = "src"
+
+        /**
+         * token style for an sgml attribute name.
+         */
+        const val PR_ATTRIB_NAME = "atn"
+
+        /**
+         * token style for an sgml attribute value.
+         */
+        const val PR_ATTRIB_VALUE = "atv"
+
+        /**
+         * A class that indicates a section of markup that is not code, e.g. to allow
+         * embedding of line numbers within code listings.
+         */
+        const val PR_NOCODE = "nocode"
+
+        /**
+         * A set of tokens that can precede a regular expression literal in
+         * javascript
+         * http://web.archive.org/web/20070717142515/http://www.mozilla.org/js/language/js20/rationale/syntax.html
+         * has the full list, but I've removed ones that might be problematic when
+         * seen in languages that don't support regular expression literals.
+         *
+         *
+         * Specifically, I've removed any keywords that can't precede a regexp
+         * literal in a syntactically legal javascript program, and I've removed the
+         * "in" keyword since it's not a keyword in many languages, and might be used
+         * as a count of inches.
+         *
+         *
+         * The link above does not accurately describe EcmaScript rules since
+         * it fails to distinguish between (a=++/b/i) and (a++/b/i) but it works
+         * very well in practice.
+         */
+        private const val REGEXP_PRECEDES_PATTERN =
+            "(?:^^\\.?|[+-]|[!=]=?=?|#|%=?|&&?=?|\\(|\\*=?|[+\\-]=|->|/=?|::?|<<?=?|>>?>?=?|,|;|\\?|@|\\[|~|\\{|\\^\\^?=?|\\|\\|?=?|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*"
+
+        /**
+         * Apply the given language handler to sourceCode and add the resulting
+         * decorations to out.
+         * @param basePos the index of sourceCode within the chunk of source
+         * whose decorations are already present on out.
+         */
+        private fun appendDecorations(
+            basePos: Int,
+            sourceCode: String?,
+            langHandler: CreateSimpleLexer?,
+            out: MutableList<Any>
+        ) {
+            if (sourceCode == null) {
+                throw NullPointerException("argument 'sourceCode' cannot be null")
+            }
+            val job = Job()
+            job.setSourceCode(sourceCode)
+            job.basePos = basePos
+            langHandler!!.decorate(job)
+            out.addAll(job.getDecorations())
+        }
+    }
+
+}
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Util.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Util.kt
new file mode 100644
index 0000000..4a0413e
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/prettify/parser/Util.kt
@@ -0,0 +1,244 @@
+// Copyright (C) 2011 Chan Wai Shing
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package com.wakaztahir.codeeditor.prettify.parser
+
+/**
+ * Common Utilities.
+ * Some of the functions are port from JavaScript.
+ *
+ * @author Chan Wai Shing <cws1989></cws1989>@gmail.com>
+ */
+object Util {
+    /**
+     * Treat a variable as an boolean in JavaScript style. Note this function can
+     * only handle string, integer and boolean currently. All other data type, if
+     * null, return false, not null return true.
+     *
+     * @param var the variable to get value from
+     * @return the boolean value
+     */
+    fun getVariableValueAsBoolean(`var`: Any?): Boolean {
+        return if (`var` == null) {
+            false
+        } else if (`var` is String) {
+            `var`.isNotEmpty()
+        } else if (`var` is Int) {
+            `var` != 0
+        } else if (`var` is Boolean) {
+            `var`
+        } else {
+            true
+        }
+    }
+
+    /**
+     * Treat a variable as an integer in JavaScript style. Note this function can
+     * only handle integer and boolean currently.
+     *
+     * @param var the variable to get value from
+     * @return the integer value
+     * @throws IllegalArgumentException the data type of `var` is neither
+     * integer nor boolean.
+     */
+    fun getVariableValueAsInteger(`var`: Any?): Int {
+        if (`var` == null) {
+            throw NullPointerException("argument 'var' cannot be null")
+        }
+        var returnResult: Int? = -1
+        returnResult = if (`var` is Int) {
+            `var`
+        } else if (`var` is Boolean) {
+            // Javascript treat true as 1
+            if (`var`) 1 else 0
+        } else {
+            throw IllegalArgumentException("'var' is neither integer nor boolean")
+        }
+        return returnResult
+    }
+
+    /**
+     * Get all the matches for `string` compiled by `pattern`. If
+     * `isGlobal` is true, the return results will only include the
+     * group 0 matches. It is similar to string.match(regexp) in JavaScript.
+     *
+     * @param regex the regexp
+     * @param string the string
+     * @param isGlobal similar to JavaScript /g flag
+     *
+     * @return all matches
+     */
+    fun match(regex: Regex, string: String, isGlobal: Boolean): MutableList<String> {
+//        val matcher = Pattern.compile(regex.pattern).matcher(string)
+//        val matchesList = mutableListOf<String>()
+//        while (matcher.find()) {
+//            matchesList.add(matcher.group(0))
+//            if (!isGlobal) {
+//                var i : Int = 1
+//                val iEnd : Int = matcher.groupCount()
+//                while (i <= iEnd) {
+//                    matchesList.add(matcher.group(i))
+//                    i++
+//                }
+//            }
+//        }
+//        println("His Matches : ${matchesList.joinToString(",")}")
+//        println("Myy Matches : ${myMatchesList.joinToString(",")}")
+
+        //This code replaces the above commented code
+        val matchesList = mutableListOf<String>()
+        var startIndex = 0
+        var matchResult = regex.find(string, startIndex)
+        while (matchResult != null) {
+            matchResult.groups[0]?.value?.let { matchesList.add(it) }
+            if (!isGlobal) {
+                var i = 1
+                while (i < matchResult.groups.size) {
+                    matchResult.groups[i]?.value?.let { matchesList.add(it) }
+                    i++
+                }
+            }
+            startIndex = matchResult.range.last + 1
+            matchResult = regex.find(string, startIndex)
+        }
+        return matchesList
+    }
+
+    /**
+     * Test whether the `string` has at least one match by
+     * `pattern`.
+     *
+     * @param pattern the regexp
+     * @param string the string to test
+     *
+     * @return true if at least one match, false if no match
+     */
+    fun test(pattern: Regex, string: String): Boolean {
+        return pattern.find(string) != null
+    }
+
+    /**
+     * Join the `strings` into one string.
+     *
+     * @param strings the string list to join
+     *
+     * @return the joined string
+     */
+    fun join(strings: List<String>): String {
+        return join(strings.toTypedArray())
+    }
+
+    /**
+     * Join the `strings` into one string with `delimiter` in
+     * between.
+     *
+     * @param strings the string list to join
+     * @param delimiter the delimiter
+     *
+     * @return the joined string
+     */
+    fun join(strings: List<String>?, delimiter: String?): String {
+        if (strings == null) {
+            throw NullPointerException("argument 'strings' cannot be null")
+        }
+        return join(strings.toTypedArray(), delimiter)
+    }
+    /**
+     * Join the `strings` into one string with `delimiter` in
+     * between. It is similar to RegExpObject.test(string) in JavaScript.
+     *
+     * @param strings the string list to join
+     * @param delimiter the delimiter
+     *
+     * @return the joined string
+     */
+    /**
+     * Join the `strings` into one string.
+     *
+     * @param strings the string list to join
+     *
+     * @return the joined string
+     */
+    fun join(strings: Array<String>?, delimiter: String? = null): String {
+        if (strings == null) {
+            throw NullPointerException("argument 'strings' cannot be null")
+        }
+        val sb = StringBuilder()
+        if (strings.isNotEmpty()) {
+            sb.append(strings[0])
+            var i = 1
+            val iEnd = strings.size
+            while (i < iEnd) {
+                if (delimiter != null) {
+                    sb.append(delimiter)
+                }
+                sb.append(strings[i])
+                i++
+            }
+        }
+        return sb.toString()
+    }
+
+    /**
+     * Remove identical adjacent tags from `decorations`.
+     *
+     * @param decorations see [prettify.parser.Job.decorations]
+     * @param source the source code
+     *
+     * @return the `decorations` after treatment
+     *
+     * @throws IllegalArgumentException the size of `decoration` is not
+     * a multiple of 2
+     */
+    fun removeDuplicates(decorations: List<Any>?, source: String?): List<Any> {
+        if (decorations == null) {
+            throw NullPointerException("argument 'decorations' cannot be null")
+        }
+        if (source == null) {
+            throw NullPointerException("argument 'source' cannot be null")
+        }
+        require(decorations.size and 0x1 == 0) { "the size of argument 'decorations' should be a multiple of 2" }
+        val returnList: MutableList<Any> = ArrayList()
+
+        // use TreeMap to remove entrys with same pos
+        val orderedMap: MutableMap<Int, Any> = mutableMapOf()
+        var i = 0
+        val iEnd = decorations.size
+        while (i < iEnd) {
+            orderedMap[decorations[i] as Int] = decorations[i + 1]
+            i += 2
+        }
+
+        // remove adjacent style
+        var previousStyle: String? = null
+        for (pos in orderedMap.keys) {
+            val style = orderedMap[pos] as String?
+            if (previousStyle != null && previousStyle == style) {
+                continue
+            }
+            returnList.add(pos)
+            if (style != null) {
+                returnList.add(style)
+            }
+            previousStyle = style
+        }
+
+        // remove last zero length tag
+        val returnListSize = returnList.size
+        if (returnListSize >= 4 && returnList[returnListSize - 2] == source.length) {
+            returnList.removeAt(returnListSize - 2)
+            returnList.removeAt(returnListSize - 2)
+        }
+        return returnList
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/CodeTheme.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/CodeTheme.kt
new file mode 100644
index 0000000..9cae120
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/CodeTheme.kt
@@ -0,0 +1,48 @@
+package com.wakaztahir.codeeditor.theme
+
+import androidx.compose.ui.text.SpanStyle
+import com.wakaztahir.codeeditor.parser.ParseResult
+import com.wakaztahir.codeeditor.prettify.parser.Prettify
+
+abstract class CodeTheme(private val colors: SyntaxColors) {
+
+    private val colorMap = hashMapOf(
+        Prettify.PR_TYPE to colors.type,
+        Prettify.PR_KEYWORD to colors.keyword,
+        Prettify.PR_LITERAL to colors.literal,
+        Prettify.PR_COMMENT to colors.comment,
+        Prettify.PR_STRING to colors.string,
+        Prettify.PR_PUNCTUATION to colors.punctuation,
+        Prettify.PR_PLAIN to colors.plain,
+        Prettify.PR_TAG to colors.tag,
+        Prettify.PR_DECLARATION to colors.declaration,
+        Prettify.PR_SOURCE to colors.source,
+        Prettify.PR_ATTRIB_NAME to colors.attrName,
+        Prettify.PR_ATTRIB_VALUE to colors.attrValue,
+        Prettify.PR_NOCODE to colors.nocode
+    )
+
+    open infix fun toSpanStyle(result: ParseResult): SpanStyle {
+        return SpanStyle(color = colorMap[result.styleKeysString] ?: kotlin.run {
+            Throwable("Missing color value for style key : ${result.styleKeysString}").printStackTrace()
+            colors.nocode
+        })
+    }
+}
+
+enum class CodeThemeType {
+    Default {
+        override fun getTheme(): CodeTheme = DefaultTheme()
+    },
+    Monokai {
+        override fun getTheme(): CodeTheme = MonokaiTheme()
+    };
+
+    internal abstract fun getTheme() : CodeTheme
+    private var _theme : CodeTheme? = null
+    val theme : CodeTheme
+        get() {
+            if(_theme == null) _theme = getTheme()
+            return _theme!!
+        }
+}
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/EditorThemes.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/EditorThemes.kt
new file mode 100644
index 0000000..db04b3d
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/EditorThemes.kt
@@ -0,0 +1,39 @@
+package com.wakaztahir.codeeditor.theme
+
+import androidx.compose.ui.graphics.Color
+
+class DefaultTheme : CodeTheme(
+    colors = SyntaxColors(
+        type = Color(0xFF859900),
+        keyword = Color(0xFF268BD2),
+        literal = Color(0xFF269186),
+        comment = Color(0xFF93A1A1),
+        string = Color(0xFF269186),
+        punctuation = Color(0xFF586E75),
+        plain = Color(0xFF586E75),
+        tag = Color(0xFF859900),
+        declaration = Color(0xFF268BD2),
+        source = Color(0xFF586E75),
+        attrName = Color(0xFF268BD2),
+        attrValue = Color(0xFF269186),
+        nocode = Color(0xFF586E75),
+    )
+)
+
+class MonokaiTheme : CodeTheme(
+    colors = SyntaxColors(
+        type = Color(0xFFA7E22E),
+        keyword = Color(0xFFFA2772),
+        literal = Color(0xFF66D9EE),
+        comment = Color(0xFF76715E),
+        string = Color(0xFFE6DB74),
+        punctuation = Color(0xFFC1C1C1),
+        plain = Color(0xFFF8F8F0),
+        tag = Color(0xFFF92672),
+        declaration = Color(0xFFFA2772),
+        source = Color(0xFFF8F8F0),
+        attrName = Color(0xFFA6E22E),
+        attrValue = Color(0xFFE6DB74),
+        nocode = Color(0xFFF8F8F0),
+    )
+)
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/SyntaxColors.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/SyntaxColors.kt
new file mode 100644
index 0000000..514f8ef
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/theme/SyntaxColors.kt
@@ -0,0 +1,19 @@
+package com.wakaztahir.codeeditor.theme
+
+import androidx.compose.ui.graphics.Color
+
+class SyntaxColors(
+    val type: Color,
+    val keyword: Color,
+    val literal: Color,
+    val comment: Color,
+    val string: Color,
+    val punctuation: Color,
+    val plain: Color,
+    val tag: Color,
+    val declaration: Color,
+    val source: Color,
+    val attrName: Color,
+    val attrValue: Color,
+    val nocode: Color,
+)
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/Annotation.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/Annotation.kt
new file mode 100644
index 0000000..4c04c72
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/Annotation.kt
@@ -0,0 +1,35 @@
+package com.wakaztahir.codeeditor.utils
+
+import androidx.compose.ui.text.AnnotatedString
+import com.wakaztahir.codeeditor.model.CodeLang
+import com.wakaztahir.codeeditor.parser.ParseResult
+import com.wakaztahir.codeeditor.prettify.PrettifyParser
+import com.wakaztahir.codeeditor.theme.CodeTheme
+
+fun List<ParseResult>.toAnnotatedString(theme: CodeTheme, source: String): AnnotatedString = AnnotatedString(
+    text = source,
+    spanStyles = map {
+        AnnotatedString.Range(theme toSpanStyle it, it.offset, it.offset + it.length)
+    }
+)
+
+fun parseCodeAsAnnotatedString(
+    parser: PrettifyParser,
+    theme: CodeTheme,
+    lang: String,
+    code: String
+): AnnotatedString = parser.parse(lang, code).toAnnotatedString(theme, code)
+
+fun parseCodeAsAnnotatedString(
+    parser: PrettifyParser,
+    theme: CodeTheme,
+    lang: CodeLang,
+    code: String
+): AnnotatedString = lang.langProvider?.let {
+    parser.parse(it, code).toAnnotatedString(theme, code)
+} ?: parseCodeAsAnnotatedString(
+    parser = parser,
+    theme = theme,
+    lang = lang.value.first(),
+    code = code,
+)
\ No newline at end of file
diff --git a/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/utils.kt b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/utils.kt
new file mode 100644
index 0000000..586dd52
--- /dev/null
+++ b/composeApp/src/commonMain/kotlin/com/wakaztahir/codeeditor/utils/utils.kt
@@ -0,0 +1,15 @@
+package com.wakaztahir.codeeditor.utils
+
+import com.wakaztahir.codeeditor.prettify.parser.StylePattern
+
+internal fun MutableList<StylePattern>.new(
+    tokenStyle: String,
+    regExp: Regex,
+    shortcutChars: String? = null,
+) = add(
+    StylePattern(
+        tokenStyle = tokenStyle,
+        regExp = regExp,
+        shortcutChars = shortcutChars
+    )
+)
diff --git a/composeApp/src/commonMain/resources/compose-multiplatform.xml b/composeApp/src/commonMain/resources/compose-multiplatform.xml
new file mode 100755
index 0000000..d7bf795
--- /dev/null
+++ b/composeApp/src/commonMain/resources/compose-multiplatform.xml
@@ -0,0 +1,36 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+    android:width="600dp"
+    android:height="600dp"
+    android:viewportWidth="600"
+    android:viewportHeight="600">
+  <path
+      android:pathData="M301.21,418.53C300.97,418.54 300.73,418.56 300.49,418.56C297.09,418.59 293.74,417.72 290.79,416.05L222.6,377.54C220.63,376.43 219,374.82 217.85,372.88C216.7,370.94 216.09,368.73 216.07,366.47L216.07,288.16C216.06,287.32 216.09,286.49 216.17,285.67C216.38,283.54 216.91,281.5 217.71,279.6L199.29,268.27L177.74,256.19C175.72,260.43 174.73,265.23 174.78,270.22L174.79,387.05C174.85,393.89 178.57,400.2 184.53,403.56L286.26,461.02C290.67,463.51 295.66,464.8 300.73,464.76C300.91,464.76 301.09,464.74 301.27,464.74C301.24,449.84 301.22,439.23 301.22,439.23L301.21,418.53Z"
+      android:fillColor="#041619"
+      android:fillType="nonZero"/>
+  <path
+      android:pathData="M409.45,242.91L312.64,188.23C303.64,183.15 292.58,183.26 283.68,188.51L187.92,245C183.31,247.73 179.93,251.62 177.75,256.17L177.74,256.19L199.29,268.27L217.71,279.6C217.83,279.32 217.92,279.02 218.05,278.74C218.24,278.36 218.43,277.98 218.64,277.62C219.06,276.88 219.52,276.18 220.04,275.51C221.37,273.8 223.01,272.35 224.87,271.25L289.06,233.39C290.42,232.59 291.87,231.96 293.39,231.51C295.53,230.87 297.77,230.6 300,230.72C302.98,230.88 305.88,231.73 308.47,233.2L373.37,269.85C375.54,271.08 377.49,272.68 379.13,274.57C379.68,275.19 380.18,275.85 380.65,276.53C380.86,276.84 381.05,277.15 381.24,277.47L397.79,266.39L420.34,252.93L420.31,252.88C417.55,248.8 413.77,245.35 409.45,242.91Z"
+      android:fillColor="#37BF6E"
+      android:fillType="nonZero"/>
+  <path
+      android:pathData="M381.24,277.47C381.51,277.92 381.77,278.38 382.01,278.84C382.21,279.24 382.39,279.65 382.57,280.06C382.91,280.88 383.19,281.73 383.41,282.59C383.74,283.88 383.92,285.21 383.93,286.57L383.93,361.1C383.96,363.95 383.35,366.77 382.16,369.36C381.93,369.86 381.69,370.35 381.42,370.83C379.75,373.79 377.32,376.27 374.39,378L310.2,415.87C307.47,417.48 304.38,418.39 301.21,418.53L301.22,439.23C301.22,439.23 301.24,449.84 301.27,464.74C306.1,464.61 310.91,463.3 315.21,460.75L410.98,404.25C419.88,399 425.31,389.37 425.22,379.03L425.22,267.85C425.17,262.48 423.34,257.34 420.34,252.93L397.79,266.39L381.24,277.47Z"
+      android:fillColor="#3870B2"
+      android:fillType="nonZero"/>
+  <path
+      android:pathData="M177.75,256.17C179.93,251.62 183.31,247.73 187.92,245L283.68,188.51C292.58,183.26 303.64,183.15 312.64,188.23L409.45,242.91C413.77,245.35 417.55,248.8 420.31,252.88L420.34,252.93L498.59,206.19C494.03,199.46 487.79,193.78 480.67,189.75L320.86,99.49C306.01,91.1 287.75,91.27 273.07,99.95L114.99,193.2C107.39,197.69 101.81,204.11 98.21,211.63L177.74,256.19L177.75,256.17ZM301.27,464.74C301.09,464.74 300.91,464.76 300.73,464.76C295.66,464.8 290.67,463.51 286.26,461.02L184.53,403.56C178.57,400.2 174.85,393.89 174.79,387.05L174.78,270.22C174.73,265.23 175.72,260.43 177.74,256.19L98.21,211.63C94.86,218.63 93.23,226.58 93.31,234.82L93.31,427.67C93.42,438.97 99.54,449.37 109.4,454.92L277.31,549.77C284.6,553.88 292.84,556.01 301.2,555.94L301.2,555.8C301.39,543.78 301.33,495.26 301.27,464.74Z"
+      android:strokeWidth="10"
+      android:fillColor="#00000000"
+      android:strokeColor="#083042"
+      android:fillType="nonZero"/>
+  <path
+      android:pathData="M498.59,206.19L420.34,252.93C423.34,257.34 425.17,262.48 425.22,267.85L425.22,379.03C425.31,389.37 419.88,399 410.98,404.25L315.21,460.75C310.91,463.3 306.1,464.61 301.27,464.74C301.33,495.26 301.39,543.78 301.2,555.8L301.2,555.94C309.48,555.87 317.74,553.68 325.11,549.32L483.18,456.06C497.87,447.39 506.85,431.49 506.69,414.43L506.69,230.91C506.6,222.02 503.57,213.5 498.59,206.19Z"
+      android:strokeWidth="10"
+      android:fillColor="#00000000"
+      android:strokeColor="#083042"
+      android:fillType="nonZero"/>
+  <path
+      android:pathData="M301.2,555.94C292.84,556.01 284.6,553.88 277.31,549.76L109.4,454.92C99.54,449.37 93.42,438.97 93.31,427.67L93.31,234.82C93.23,226.58 94.86,218.63 98.21,211.63C101.81,204.11 107.39,197.69 114.99,193.2L273.07,99.95C287.75,91.27 306.01,91.1 320.86,99.49L480.67,189.75C487.79,193.78 494.03,199.46 498.59,206.19C503.57,213.5 506.6,222.02 506.69,230.91L506.69,414.43C506.85,431.49 497.87,447.39 483.18,456.06L325.11,549.32C317.74,553.68 309.48,555.87 301.2,555.94Z"
+      android:strokeWidth="10"
+      android:fillColor="#00000000"
+      android:strokeColor="#083042"
+      android:fillType="nonZero"/>
+</vector>
diff --git a/composeApp/src/commonMain/resources/images/ic_chrome_close.png b/composeApp/src/commonMain/resources/images/ic_chrome_close.png
new file mode 100644
index 0000000000000000000000000000000000000000..1949491fe9516a8989f036896a3e154583671459
GIT binary patch
literal 298
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL
zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#{hlt4Ar*{oryS%xWWeJ(cawvghgkxP*(;7I
z%I3!&a=33h!C_g%eLqxLv1>(a@XB^OO|M(O>fQT-7xi8hbjmR7Tat2N>-_h#^ADce
z={LD0dEw54mq(i<k|(NMJhG~F^I284w9oNwQlIaJIGp?WZtr{kdFegO>%BE&y<WQf
zU-T|2dRm)U+9sDX7oOKfeyCXe^wFn@qOTQKpH;no=GiQX%>i>$&aJy=vUvZ?72OwP
rt9`;_78!G{oGxgyvF^gLRTYevucl@fym;yd^a_KgtDnm{r-UW|gdBMj

literal 0
HcmV?d00001

diff --git a/composeApp/src/commonMain/resources/images/ic_chrome_maximize.png b/composeApp/src/commonMain/resources/images/ic_chrome_maximize.png
new file mode 100644
index 0000000000000000000000000000000000000000..672e9e49d2f0c4c8aa44b496beb98f32cb36720e
GIT binary patch
literal 271
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL
zyA#8@b22Z19F}xPUq=Rpjs4tz5?O)#C7v#hAr*{or=I3*HV|+*uE846bl1_#!^(qm
z$)pR8ml&r^^5c=c&?xDx@KN-WSImWd-EXh1IbJQve2OuxYD0^6YEPwu1INO&{Fi(4
zkKf8&_%onj`G@1R?It?a@o%=?u{RezxATCa7DwWH^LvJCQ>AyY6@>+-TS%G)Fq`gJ
z<;Z7Q7CGan>EdtaPw39*y12wq>Zic<uj?2qg9ZM#Fd02!_SVq+QguW9?8?h?mjK<%
N;OXk;vd$@?2>?O{Vxj;5

literal 0
HcmV?d00001

diff --git a/composeApp/src/commonMain/resources/images/ic_chrome_minimize.png b/composeApp/src/commonMain/resources/images/ic_chrome_minimize.png
new file mode 100644
index 0000000000000000000000000000000000000000..05534b43693dcb6e9046de97b68d446ca86a90ef
GIT binary patch
literal 166
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL
zyA#8@b22Z19F}xPUq=Rpjs4tz5?O(KYfl%)kP61PXE$;(C<r)T6qY%X^J>|yla;*+
zW=hxpU2mD7l*aHO<eXBbi-~u^cgZc)dymbt<+#l7;5?JbG@j;go;USCV;DSL{an^L
HB{Ts5{^l`H

literal 0
HcmV?d00001

diff --git a/composeApp/src/commonMain/resources/images/ic_chrome_unmaximize.png b/composeApp/src/commonMain/resources/images/ic_chrome_unmaximize.png
new file mode 100644
index 0000000000000000000000000000000000000000..52b8f4edcd552daad699bb6c3dd51a16268302ba
GIT binary patch
literal 366
zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjoCO|{#S9GG!XV7ZFl!D-1!HlL
zyA#8@b22Z19F}xPUq=Rpjs4tz5?O(A%$_ceAr*{oL*Mc?83^nZV!Obb!0etNGRMJO
z!2AH`5zjXqc^CQ)uziiyS)w4H%Ve*zZ%Jn6`**SOFJ&*TclME0^DGi_5{Y=1!FFth
z__CnvzaNwwK6LZyJ=Dq63syApk<8JaAoKi<rf)FoBx}}&$xZVg&b}Pw@IhVudd=~@
z&iQ90Cpaf3d9FLR?TE%c4ky$6J2%+(nR?_ov*|MO6u*~$V#Hv!(Xr{o)}t3h_&kLt
zxot7}$8uZ6;|+7nyoS>(`_*+;r|B<aZG6C<QePJTbrFl#%G&z#Q&${2p&=uC_IE};
z!(ygzom~D4PuhE=d4KmwmRQYNyTY>e)g=BbKD#i_?TzLRQ&XK<Kb(343?2qgS3j3^
HP6<r_5c-7Z

literal 0
HcmV?d00001

diff --git a/composeApp/src/commonMain/resources/logo.png b/composeApp/src/commonMain/resources/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..029df083aa269f149774d9c4b405472204210827
GIT binary patch
literal 57311
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

literal 0
HcmV?d00001

diff --git a/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/create_table.sq b/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/create_table.sq
new file mode 100644
index 0000000..e95efe8
--- /dev/null
+++ b/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/create_table.sq
@@ -0,0 +1,34 @@
+import kotlin.Boolean;
+import com.github.springeye.memosc.model.MemosRowStatus;
+import com.github.springeye.memosc.model.MemosVisibility;
+
+
+CREATE TABLE  IF NOT EXISTS Memo (
+    id INTEGER PRIMARY KEY,
+    createdTs INTEGER  NOT NULL,
+    creatorId INTEGER  NOT NULL,
+    creatorName text,
+    content text NOT NULL,
+    pinned INTEGER AS Boolean NOT NULL ,
+    rowStatus text AS MemosRowStatus NOT NULL,
+    visibility text AS MemosVisibility NOT NULL,
+    updatedTs INTEGER  NOT NULL
+);
+CREATE TABLE  IF NOT EXISTS RemoteKey (
+    creatorId INTEGER,
+    rowStatus TEXT AS MemosRowStatus,
+    visibility TEXT AS MemosVisibility
+);
+CREATE TABLE  IF NOT EXISTS Resource (
+    id INTEGER PRIMARY KEY ,
+    createdTs INTEGER  NOT NULL,
+    creatorId INTEGER  NOT NULL,
+    filename TEXT NOT NULL,
+    size INTEGER  NOT NULL,
+    type TEXT  NOT NULL,
+    updatedTs INTEGER  NOT NULL,
+    externalLink TEXT,
+    publicId TEXT,
+    memoId INTEGER
+);
+
diff --git a/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/query.sq b/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/query.sq
new file mode 100644
index 0000000..4f42664
--- /dev/null
+++ b/composeApp/src/commonMain/sqldelight/com/github/springeye/memosc/db/model/query.sq
@@ -0,0 +1,41 @@
+
+selectAllMemo:
+SELECT *
+FROM Memo;
+
+
+insertMemo:
+INSERT OR REPLACE INTO Memo(id,createdTs,creatorId,creatorName,content,pinned,rowStatus,visibility,updatedTs)
+VALUES ?;
+
+insertResource:
+INSERT OR REPLACE INTO Resource(id,createdTs,creatorId,filename,size,type,updatedTs,externalLink,publicId,memoId)
+VALUES ?;
+
+selectResourceByMemoId:
+SELECT *
+FROM Resource
+WHERE memoId = ?;
+
+
+deleteAllMemo:
+DELETE FROM Memo;
+
+deleteAllResource:
+DELETE FROM Resource;
+
+countMemos:
+SELECT count(*) FROM Memo;
+
+memos:
+SELECT *
+FROM Memo
+LIMIT :limit OFFSET :offset;
+
+insertOrReplaceRemoteKey:
+INSERT OR REPLACE INTO RemoteKey(creatorId,rowStatus,visibility)
+VALUES?;
+selectRemoteKeyByQuery:
+SELECT * FROM RemoteKey WHERE creatorId =? AND rowStatus=? AND visibility=?;
+deleteRemoteKeyByQuery:
+DELETE FROM RemoteKey WHERE creatorId =? AND rowStatus=? AND visibility=?;
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Bootstrap.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Bootstrap.kt
new file mode 100644
index 0000000..3019e17
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Bootstrap.kt
@@ -0,0 +1,46 @@
+package com.github.springeye.memosc
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import com.github.springeye.memosc.core.Base64ImageFetcher
+import com.github.springeye.memosc.di.appModule
+import com.github.springeye.memosc.di.homeModule
+
+import io.kamel.core.config.KamelConfig
+import io.kamel.core.config.httpFetcher
+import io.kamel.core.config.takeFrom
+import io.kamel.image.config.Default
+import io.kamel.image.config.LocalKamelConfig
+import io.ktor.client.plugins.cookies.HttpCookies
+import org.koin.compose.KoinApplication
+import org.koin.compose.getKoin
+
+@Composable
+fun Bootstrap() {
+
+    KoinApplication(application = {
+        modules(appModule, homeModule)
+    }) {
+        val koin=getKoin()
+        val kamelConfig=KamelConfig {
+            takeFrom(KamelConfig.Default)
+            fetcher(Base64ImageFetcher())
+            httpFetcher {
+                install(HttpCookies){
+                    this.storage=koin.get()
+                }
+                httpCache(1024 * 1024 * 1024  /* 1024 MiB */)
+            }
+            // 100 by default
+            imageBitmapCacheSize = 500
+            // 100 by default
+            imageVectorCacheSize = 300
+            // 100 by default
+            svgCacheSize = 200
+        }
+        CompositionLocalProvider(LocalKamelConfig provides kamelConfig) {
+            DesktopApp()
+        }
+    }
+}
+
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DataStorePreferences.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DataStorePreferences.desktop.kt
new file mode 100644
index 0000000..b757958
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DataStorePreferences.desktop.kt
@@ -0,0 +1,24 @@
+package com.github.springeye.memosc
+
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.coroutines.CoroutineScope
+import java.io.File
+
+actual fun dataStorePreferences(
+    corruptionHandler: ReplaceFileCorruptionHandler<Preferences>?,
+    coroutineScope: CoroutineScope,
+    migrations: List<DataMigration<Preferences>>
+): DataStore<Preferences> = createDataStoreWithDefaults(
+    corruptionHandler = corruptionHandler,
+    migrations = migrations,
+    coroutineScope = coroutineScope,
+    path = { "${getPath()}/${SETTINGS_PREFERENCES}.preferences_pb" }
+)
+fun getPath():String{
+    val file = File(System.getProperty("user.home"), ".memosc")
+    if(!file.exists())file.mkdirs()
+    return file.toString()
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DesktopApp.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DesktopApp.kt
new file mode 100644
index 0000000..318f661
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/DesktopApp.kt
@@ -0,0 +1,57 @@
+package com.github.springeye.memosc
+
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Surface
+import androidx.compose.material3.lightColorScheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.staticCompositionLocalOf
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import cafe.adriel.voyager.navigator.Navigator
+import cafe.adriel.voyager.transitions.ScaleTransition
+import com.github.springeye.memosc.ui.app.AppScreen
+import org.koin.compose.getKoin
+
+
+@Composable
+fun DesktopApp() {
+     val colors = lightColorScheme(
+         primary = Color(11, 107, 203),
+//         onPrimary = Color.White,
+         background = Color(244, 244, 245),
+//         onBackground = Color.White,
+         surface=Color(244, 244, 245),
+//         onSurface = Color.White,
+
+    )
+    val toastState = remember { mutableStateOf<ToastState>(ToastState.Hidden) }
+    val notification= createPopupNotification(toastState)
+    val koin=getKoin()
+    CompositionLocalProvider(
+    ){
+    MaterialTheme(colorScheme = colors) {
+        Surface(
+            modifier = Modifier.fillMaxSize()
+        ) {
+            CompositionLocalProvider(
+                LocalNotification provides notification
+            ){
+                Navigator(AppScreen){ navigator->
+                    ScaleTransition(navigator)
+                }
+            }
+            Toast(toastState)
+        }
+    }
+    }
+}
+ val LocalNotification = staticCompositionLocalOf<PopupNotification> {
+    noLocalProvidedFor("LocalNotification")
+}
+private fun noLocalProvidedFor(name: String): Nothing {
+    error("CompositionLocal $name not present")
+}
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Platform.jvm.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Platform.jvm.kt
new file mode 100644
index 0000000..e24c646
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/Platform.jvm.kt
@@ -0,0 +1,7 @@
+package com.github.springeye.memosc
+
+class JVMPlatform: Platform {
+    override val name: String = "Java ${System.getProperty("java.version")}"
+}
+
+actual fun getPlatform(): Platform = JVMPlatform()
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/PopupNotification.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/PopupNotification.desktop.kt
new file mode 100644
index 0000000..690f914
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/PopupNotification.desktop.kt
@@ -0,0 +1,20 @@
+package com.github.springeye.memosc
+
+
+import androidx.compose.runtime.MutableState
+
+actual fun createPopupNotification(state: MutableState<ToastState>): PopupNotification {
+    return object: PopupNotification {
+        override fun showPopUpMessage(text: String) {
+            state.value= ToastState.Shown(text)
+        }
+
+        override fun showLoading(text: String) {
+            state.value= ToastState.Loading(text)
+        }
+
+        override fun hideLoading() {
+            state.value= ToastState.Hidden
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Card.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Card.kt
new file mode 100644
index 0000000..0fb316f
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Card.kt
@@ -0,0 +1,71 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.desktop.ui.tooling.preview.Preview
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.BoxScope
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.onPointerEvent
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+
+@Preview
+@Composable
+fun PreviewCard() {
+    Scaffold {
+        Column(modifier = Modifier.fillMaxSize().background(Color.Gray)) {
+            CardItem(radius = 10.dp,
+                hoverColor = Color.Blue,
+
+                ) {
+                Text("asdfasfas")
+            }
+        }
+    }
+}
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun  CardItem(
+    radius: Dp =15.dp,
+    color: Color=Color.White,
+    hoverColor: Color=color,
+    borderColor: Color=color,
+    hoverBorderColor: Color=color,
+    borderWidth:Dp=0.dp,
+    paddingValues: PaddingValues= PaddingValues(10.dp),
+    modifier:Modifier = Modifier, content: @Composable() (BoxScope.() -> Unit)) {
+    var hover by remember {
+        mutableStateOf(false)
+    }
+    var background = Modifier.clip(RoundedCornerShape(radius))
+        .onPointerEvent(PointerEventType.Enter) {
+            hover = true;
+        }
+        .onPointerEvent(PointerEventType.Exit) {
+            hover = false;
+        }
+        .background(if (hover) hoverColor else color)
+
+    Box(modifier = background
+        .then(modifier)
+        .border(borderWidth,if (hover) hoverBorderColor else borderColor,shape = RoundedCornerShape(radius))
+        .padding(paddingValues),
+        content = content)
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/EditMemo.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/EditMemo.kt
new file mode 100644
index 0000000..0bcac8a
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/EditMemo.kt
@@ -0,0 +1,355 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.animation.AnimatedContent
+import androidx.compose.desktop.ui.tooling.preview.Preview
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.defaultMinSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.foundation.layout.wrapContentWidth
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.DropdownMenu
+import androidx.compose.material.Icon
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Attachment
+import androidx.compose.material.icons.filled.Cancel
+import androidx.compose.material.icons.filled.Close
+import androidx.compose.material.icons.filled.ExpandLess
+import androidx.compose.material.icons.filled.ExpandMore
+import androidx.compose.material.icons.filled.Lock
+import androidx.compose.material.icons.filled.Photo
+import androidx.compose.material.icons.filled.Send
+import androidx.compose.material.icons.filled.Tag
+import androidx.compose.material3.Button
+import androidx.compose.material3.Divider
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextFieldDefaults
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateListOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.rememberUpdatedState
+import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshotFlow
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.ImageVector
+import androidx.compose.ui.input.pointer.PointerIcon
+import androidx.compose.ui.input.pointer.pointerHoverIcon
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import com.darkrockstudios.libraries.mpfilepicker.FilePicker
+import com.github.springeye.memosc.LocalNotification
+import com.github.springeye.memosc.model.Memo
+import com.github.springeye.memosc.model.MemosVisibility
+import com.github.springeye.memosc.model.Resource
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
+import javax.swing.Icon
+
+abstract class EditMemoCallback {
+    open suspend fun onSubmit(state:EditMemoState,editId:Long,content: String, resources: List<Resource>, visibility: MemosVisibility){}
+    open suspend fun onUpload(state:EditMemoState,path: String){}
+    open suspend fun onContentChange(state:EditMemoState,text:String){}
+    open suspend fun onRemoveResourced(state:EditMemoState, resource:Resource){}
+    open suspend fun onCancle(state:EditMemoState){}
+    companion object{
+        val DEFAULT = object:EditMemoCallback(){}
+    }
+}
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
+@Preview
+@Composable
+fun EditMemo(
+    state: EditMemoState = rememberEditMemoState(host = ""),
+    ) {
+    LaunchedEffect(state){
+        snapshotFlow{
+            state.content
+        }.collect{
+            state.callback.onContentChange(state,it)
+        }
+
+    }
+    val scope = state.scope
+    val notification = LocalNotification.current
+    CardItem(
+        radius = 10.dp,
+        borderColor = Color(0xccCCCCCC),
+        borderWidth = 1.dp,
+        paddingValues = PaddingValues(start = 10.dp, end = 10.dp, bottom = 10.dp),
+        hoverBorderColor = Color(0xccCCCCCC),
+    ) {
+        Column(Modifier.fillMaxWidth().wrapContentHeight()) {
+            ITextField(
+                state.content,
+                onValueChange = state::updateContent,
+                minLines = 2,
+                maxLines = 10,
+                placeholder = {
+                    Text(
+                        "任何想法",
+                        style = TextStyle.Default.copy(color = Color(156, 163, 175))
+                    )
+                },
+                modifier = Modifier
+                    .fillMaxWidth(), // Here I have decreased the height
+                shape = RoundedCornerShape(0.dp),
+                colors = TextFieldDefaults.colors(
+                    focusedContainerColor = Color.White,
+                    unfocusedContainerColor = Color.White,
+                    disabledContainerColor = Color.White,
+                    disabledIndicatorColor = Color.Transparent,
+                    focusedIndicatorColor = Color.Transparent,
+                    unfocusedIndicatorColor = Color.Transparent,
+
+                    ),
+
+                )
+            var showFilePicker by remember { mutableStateOf(false) }
+            Row {
+                Icon(Icons.Default.Tag, "")
+                Icon(Icons.Default.Photo, "", Modifier.padding(start = 10.dp).clickable {
+                    showFilePicker = true
+                })
+                Icon(Icons.Default.Attachment, "", Modifier.padding(start = 10.dp))
+            }
+            Row {
+
+
+                val fileType = listOf("jpg", "png")
+                FilePicker(show = showFilePicker, fileExtensions = fileType) { file ->
+                    showFilePicker = false
+//                        println(file?.path)
+                    file?.path?.let {
+                        scope.launch {
+                            state.callback.onUpload(state,it)
+                        }
+                    }
+                    // do something with the file
+                }
+            }
+            Divider(Modifier.padding(top = 10.dp, bottom = 10.dp))
+
+            FlowRow() {
+                state.resources.forEach { item ->
+                    AnimatedContent(targetState = item) {
+                        ResourceItem(item, state.host) {
+                            state.removeResource(it)
+                            scope.launch {
+                                state.callback.onRemoveResourced(state, it)
+                            }
+                        }
+                    }
+                }
+            }
+            Row(Modifier.padding(vertical = 10.dp)) {
+                VisibilityButton()
+                Spacer(Modifier.weight(1f))
+                if(state.editId>0){
+                    SubmitButton("取消",Icons.Default.Cancel,true,Modifier.padding(end = 10.dp)){
+                        scope.launch {
+                            state.callback.onCancle(state)
+                        }
+                    }
+                }
+                SubmitButton("保存",Icons.Default.Send,state.content.isNotEmpty()){
+                    scope.launch {
+                        state.callback.onSubmit(
+                            state,
+                            state.editId,
+                            state.content,
+                            state.resources,
+                            state.visibility
+                        )
+                    }
+                }
+            }
+
+        }
+    }
+}
+
+@Composable
+fun SubmitButton(text: String, icon: ImageVector, enable: Boolean,modifier: Modifier=Modifier, onSubmit: () -> Unit) {
+    Button(
+        enabled = enable,
+        onClick = onSubmit,
+        contentPadding = PaddingValues(horizontal = 10.dp),
+        shape = RoundedCornerShape(5.dp),
+        modifier = modifier
+            .defaultMinSize(minWidth = 1.dp, minHeight = 1.dp)
+            .height(30.dp)
+            .pointerHoverIcon(PointerIcon.Hand)
+    ) {
+        Row(verticalAlignment = Alignment.CenterVertically) {
+            Text(text)
+            Icon(
+                icon, "",
+                tint = Color.White,
+                modifier = Modifier
+                    .size(20.dp)
+                    .padding(start = 5.dp)
+            )
+        }
+    }
+}
+
+@Composable
+fun VisibilityButton() {
+    val notification = LocalNotification.current
+    var expanded by remember { mutableStateOf(false) }
+    Box(Modifier.clip(RoundedCornerShape(5.dp))
+        .background(Color(0x99eeeeee))
+        .clickable {
+            expanded = !expanded
+        }.padding(vertical = 5.dp, horizontal = 10.dp)
+    ) {
+        Row(
+            modifier = Modifier.wrapContentWidth()
+        ) {
+            val tint = Color(156, 163, 175)
+            Icon(
+                imageVector = Icons.Default.Lock,
+                contentDescription = "More",
+                modifier = Modifier.size(20.dp),
+                tint = tint
+            )
+            Text(
+                "私有",
+                style = TextStyle.Default.copy(color = tint),
+                modifier = Modifier.padding(horizontal = 5.dp)
+            )
+            Icon(
+                imageVector = if (expanded) Icons.Default.ExpandLess else Icons.Default.ExpandMore,
+                contentDescription = "More",
+                modifier = Modifier.size(20.dp),
+                tint = tint
+
+
+            )
+        }
+        DropdownMenu(
+            expanded = expanded,
+            onDismissRequest = { expanded = false }
+        ) {
+            DropdownMenuItem(
+                text = { Text("Load") },
+                onClick = { notification.showPopUpMessage("load") }
+            )
+            DropdownMenuItem(
+                text = { Text("Save") },
+                onClick = { notification.showPopUpMessage("save") }
+            )
+        }
+    }
+}
+
+@Composable
+fun ResourceItem(item: Resource, host: String,removeResource:  (Resource)->Unit) {
+
+    val scope = rememberCoroutineScope()
+    Row(
+        modifier = Modifier
+            .wrapContentWidth()
+            .padding(end = 5.dp)
+            .background(MaterialTheme.colorScheme.background),
+        verticalAlignment = Alignment.CenterVertically
+    ) {
+        KamelImage(
+            asyncPainterResource(item.uri(host)),
+            "",
+            Modifier.size(20.dp)
+
+        )
+        Text(
+            item.filename,
+            style = MaterialTheme.typography.bodySmall,
+            modifier = Modifier.padding(start = 5.dp)
+        )
+        Icon(Icons.Default.Close, "", modifier = Modifier.clickable {
+            removeResource(item)
+        })
+    }
+}
+
+class EditMemoState(
+    private val initContent:String="",
+    private val initResources:List<Resource> = listOf(),
+    private val initVisibility: MemosVisibility = MemosVisibility.PUBLIC,
+    internal val host:String,
+    internal val callback:EditMemoCallback=EditMemoCallback.DEFAULT,
+    internal val scope: CoroutineScope
+    ){
+    var editId by mutableStateOf(0L)
+    var content by mutableStateOf(initContent)
+        private set
+    var resources = mutableStateListOf<Resource>(*initResources.toTypedArray())
+        private set
+    var visibility by mutableStateOf(initVisibility)
+        private set
+    fun updateContent(content: String) {
+        this.content = content
+    }
+    fun addResource(resource: Resource){
+        resources.add(resource);
+    }
+    fun removeResource(resource: Resource) {
+        resources.remove(resource)
+    }
+    fun updateVisibility(visibility: MemosVisibility) {
+        this.visibility = visibility
+    }
+    fun setNewMemo(memo: Memo?=null){
+        if(memo!=null){
+            editId=memo.id;
+            this.content = memo.content
+            this.resources.apply {
+                clear()
+                addAll(memo.resourceList)
+            }
+            this.visibility = memo.visibility
+        }else{
+            editId=0;
+            this.content="";
+            this.resources.clear()
+        }
+    }
+
+}
+
+@Composable
+fun rememberEditMemoState(initMemo:Memo,host:String, callback:EditMemoCallback=EditMemoCallback.DEFAULT, scope: CoroutineScope = rememberCoroutineScope(),) :EditMemoState{
+    val onCallback by rememberUpdatedState(callback)
+    return remember(initMemo,host,scope) {EditMemoState(initMemo.content,initMemo.resourceList,initMemo.visibility,host,onCallback,scope) }
+}
+@Composable
+fun rememberEditMemoState(initContent:String="", initResources:List<Resource> = listOf(),
+                          initVisibility: MemosVisibility = MemosVisibility.PUBLIC,
+                          scope: CoroutineScope = rememberCoroutineScope(),
+                          host:String, callback:EditMemoCallback=EditMemoCallback.DEFAULT) :EditMemoState {
+    val onCallback by rememberUpdatedState(callback)
+    return remember(initContent,initResources,initVisibility,host,scope) {EditMemoState(initContent,initResources,initVisibility,host,onCallback,scope) }
+}
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Heatmap.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Heatmap.desktop.kt
new file mode 100644
index 0000000..96a465d
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Heatmap.desktop.kt
@@ -0,0 +1,31 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.unit.Constraints
+import java.time.LocalDate
+import java.time.temporal.ChronoUnit
+import java.time.temporal.TemporalAdjusters
+import java.time.temporal.WeekFields
+import java.util.Locale
+import kotlin.math.ceil
+
+actual fun countHeatmap(constraints: Constraints): Int {
+    val cellSize = ceil(constraints.maxWidth.toDouble() / 7).toInt()
+    if (cellSize <= 0) {
+        return 0
+    }
+    val rows = constraints.maxHeight / cellSize
+    val fullCells = rows * 7
+
+    val firstDayOfWeek = WeekFields.of(Locale.getDefault()).firstDayOfWeek
+    val firstDayOfThisWeek = LocalDate.now().with(TemporalAdjusters.previousOrSame(firstDayOfWeek))
+    val lastColumn = ChronoUnit.DAYS.between(firstDayOfThisWeek, LocalDate.now()).toInt() + 1
+    if (lastColumn % 7 == 0) {
+        return fullCells
+    }
+    return fullCells - 7 + lastColumn
+}
+
+@Composable
+actual fun aa() {
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.desktop.kt
new file mode 100644
index 0000000..709a756
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/HeatmapStat.desktop.kt
@@ -0,0 +1,52 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.unit.sp
+import com.github.springeye.memosc.model.DailyUsageStat
+import kotlinx.datetime.toJavaLocalDate
+import java.time.LocalDate
+
+@Composable
+actual fun HeatmapStat2(day: DailyUsageStat) {
+    val date = day.date.toJavaLocalDate()
+    val borderWidth = if (date == LocalDate.now()) 1.dp else 0.dp
+    val color = when (day.count) {
+        0 -> Color(0xffeaeaea)
+        1 -> Color(0xff9be9a8)
+        2 -> Color(0xff40c463)
+        in 3..4 -> Color(0xff30a14e)
+        else -> Color(0xff216e39)
+    }
+    var modifier = Modifier
+        .fillMaxSize()
+        .aspectRatio(1F, true)
+        .clip(RoundedCornerShape(2.dp))
+        .background(color = color)
+    var text=""
+    if (date == LocalDate.now()) {
+        modifier = modifier.border(
+            borderWidth,
+            MaterialTheme.colorScheme.onBackground,
+            shape = RoundedCornerShape(2.dp)
+        )
+//        text="${date.dayOfMonth}";
+    }
+
+    Box(modifier = modifier, contentAlignment = Alignment.Center){
+        Text(text, textAlign = TextAlign.Center, style = MaterialTheme.typography.bodySmall.copy(fontSize = 6.sp ),)
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Markdown.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Markdown.kt
new file mode 100644
index 0000000..a229b66
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Markdown.kt
@@ -0,0 +1,121 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.foundation.gestures.detectTapGestures
+import androidx.compose.foundation.layout.BoxWithConstraints
+import androidx.compose.foundation.text.InlineTextContent
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.text.*
+import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.unit.sp
+import com.github.springeye.memosc.ext.appendMarkdown
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
+import org.intellij.markdown.parser.MarkdownParser
+
+@OptIn(ExperimentalTextApi::class)
+@Composable
+fun Markdown(
+    text: String,
+    modifier: Modifier = Modifier,
+    textAlign: TextAlign? = null,
+    imageContent: @Composable (url: String) -> Unit = {},
+    checkboxChange: (checked: Boolean, startOffset: Int, endOffset: Int) -> Unit = { _, _, _ -> }
+) {
+    val linkColor = MaterialTheme.colorScheme.primary
+    val bulletColor = MaterialTheme.colorScheme.tertiary
+    val headlineLarge = MaterialTheme.typography.headlineLarge
+    val headlineMedium = MaterialTheme.typography.headlineMedium
+    val headlineSmall = MaterialTheme.typography.headlineSmall
+    val uriHandler = LocalUriHandler.current
+
+    BoxWithConstraints {
+        val (annotatedString, inlineContent) = remember(text, maxWidth) {
+            val markdownAst = MarkdownParser(GFMFlavourDescriptor()).parse(MarkdownElementTypes.MARKDOWN_FILE, text, true)
+            val builder = AnnotatedString.Builder()
+            val inlineContent = HashMap<String, InlineTextContent>()
+
+            builder.appendMarkdown(
+                markdownText = text,
+                node = markdownAst,
+                depth = 0,
+                linkColor = linkColor,
+                onImage = { key, url ->
+                    inlineContent[key] = InlineTextContent(
+                        Placeholder(maxWidth.value.sp, (maxWidth.value * 9f / 16f).sp, PlaceholderVerticalAlign.AboveBaseline),
+                    ) {
+                        imageContent(url)
+                    }
+                },
+                onCheckbox = { key, startOffset, endOffset ->
+                    inlineContent[key] = InlineTextContent(
+                        Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.Center)
+                    ) {
+                        val checkboxText = text.substring(startOffset, endOffset)
+                        Checkbox(checked = checkboxText.length > 1 && checkboxText[1] != ' ', onCheckedChange = {
+                            checkboxChange(it, startOffset, endOffset)
+                        })
+                    }
+                },
+                maxWidth = maxWidth.value,
+                bulletColor = bulletColor,
+                headlineLarge = headlineLarge,
+                headlineMedium = headlineMedium,
+                headlineSmall = headlineSmall
+            )
+
+            Pair(builder.toAnnotatedString(), inlineContent)
+        }
+
+        ClickableText(
+            text = annotatedString,
+            modifier = modifier,
+            textAlign = textAlign,
+            inlineContent = inlineContent,
+            onClick = {
+                annotatedString.getUrlAnnotations(it, it)
+                    .firstOrNull()?.let { url ->
+                        uriHandler.openUri(url.item.url)
+                    }
+            }
+        )
+    }
+
+
+}
+
+@Composable
+fun ClickableText(
+    text: AnnotatedString,
+    modifier: Modifier = Modifier,
+    textAlign: TextAlign? = null,
+    inlineContent: Map<String, InlineTextContent> = mapOf(),
+    onClick: (Int) -> Unit
+) {
+    val layoutResult = remember { mutableStateOf<TextLayoutResult?>(null) }
+    val pressIndicator = Modifier.pointerInput(onClick) {
+        detectTapGestures { pos ->
+            layoutResult.value?.let { layoutResult ->
+                onClick(layoutResult.getOffsetForPosition(pos))
+            }
+        }
+    }
+
+    Text(
+        text = text,
+        modifier = modifier.then(pressIndicator),
+        textAlign = textAlign,
+//        style = MaterialTheme.typography.bodyMedium.copy(color = MaterialTheme.colorScheme.onPrimary),
+        inlineContent = inlineContent,
+        onTextLayout = {
+            layoutResult.value = it
+        }
+    )
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/MemoList.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/MemoList.kt
new file mode 100644
index 0000000..aa36b24
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/MemoList.kt
@@ -0,0 +1,263 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.DropdownMenu
+import androidx.compose.material.Icon
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.MoreVert
+import androidx.compose.material3.DropdownMenuItem
+import androidx.compose.material3.Surface
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import app.cash.paging.compose.LazyPagingItems
+import com.github.springeye.memosc.model.Memo
+import com.mikepenz.markdown.compose.LocalMarkdownColors
+import com.mikepenz.markdown.compose.LocalMarkdownTypography
+import com.mikepenz.markdown.compose.components.MarkdownComponent
+import com.mikepenz.markdown.compose.components.markdownComponents
+import com.mikepenz.markdown.model.markdownColor
+import com.wakaztahir.codeeditor.prettify.PrettifyParser
+import com.wakaztahir.codeeditor.theme.CodeThemeType
+import com.wakaztahir.codeeditor.utils.parseCodeAsAnnotatedString
+import dev.snipme.highlights.Highlights
+import dev.snipme.highlights.model.BoldHighlight
+import dev.snipme.highlights.model.ColorHighlight
+import dev.snipme.highlights.model.SyntaxLanguage
+import dev.snipme.highlights.model.SyntaxThemes
+import kotlinx.datetime.Instant
+import kotlinx.datetime.TimeZone
+import kotlinx.datetime.toJavaLocalDateTime
+import kotlinx.datetime.toLocalDateTime
+import org.intellij.markdown.ast.ASTNode
+import java.time.format.DateTimeFormatter
+
+@Composable
+fun MemoList(lazyPagingItems: LazyPagingItems<Memo>,host:String,onEdit: (memo:Memo?) -> Unit={ },onPin: (memo: Memo) -> Unit,onRemove: (memo: Memo) -> Unit) {
+    LazyColumn(modifier = Modifier.fillMaxWidth()) {
+        items(lazyPagingItems.itemCount) { index ->
+            val it = lazyPagingItems[index]!!
+            Box(Modifier.padding(top = 10.dp)) {
+                CardItem(
+                    radius = 10.dp,
+                    modifier = Modifier.fillMaxWidth(),
+                    borderColor = Color.White,
+                    borderWidth = 1.dp,
+                    hoverBorderColor = Color(0xccCCCCCC),
+                ) {
+                    Column {
+                        val dateTime =
+                            Instant.fromEpochSeconds(it.createdTs)
+                                .toLocalDateTime(
+                                    TimeZone.currentSystemDefault()
+                                ).toJavaLocalDateTime()
+                                .format(DateTimeFormatter.ofPattern("YYYY-MM-DD HH:mm:ss"))
+                        Row(modifier = Modifier) {
+                            Text(
+                                dateTime,
+                                style = TextStyle.Default.copy(
+                                    color = Color(
+                                        156,
+                                        163,
+                                        175
+                                    )
+                                )
+                            )
+                            Spacer(Modifier.weight(1F))
+                            ItemEditMenu(it,onEdit,onRemove,onPin)
+                        }
+
+                        val components = markdownComponents(
+                            codeBlock = codeBlockComponent,
+                            codeFence = codeFenceBlockComponent
+                        )
+
+                        com.mikepenz.markdown.compose.Markdown(
+                            it.content, modifier = Modifier.fillMaxWidth(),
+                            components = components,
+                            colors = markdownColor(codeText = Color.Black),
+                            //                                typography = markdownTypography(code = MaterialTheme.typography.body2.copy(fontFamily = FontFamily.Monospace, color = Color.Black))
+                        )
+                        MemoResourceContent(memo = it,host);
+                    }
+                }
+            }
+        }
+    }
+}
+
+private val codeBlockComponent: MarkdownComponent = {
+    MarkdownCodeBlock(it.content, it.node)
+}
+private val codeFenceBlockComponent: MarkdownComponent = {
+    MarkdownCodeFenceBlock(it.content, it.node)
+}
+
+@Composable
+internal fun MarkdownCodeFenceBlock(
+    content: String,
+    node: ASTNode
+) {
+    // CODE_FENCE_START, FENCE_LANG, {content}, CODE_FENCE_END
+    if (node.children.size >= 3) {
+
+        val start = node.children[2].startOffset
+        val end = node.children[node.children.size - 2].endOffset
+        val langStart = node.children[1].startOffset
+        val langEnd = node.children[1].endOffset
+        val lang = content.substring(langStart, langEnd)
+        MarkdownCode(content.subSequence(start, end).toString().replaceIndent(), lang)
+    } else {
+        // invalid code block, skipping
+    }
+}
+
+@Composable
+internal fun MarkdownCodeBlock(
+    content: String,
+    node: ASTNode
+) {
+    val start = node.children[0].startOffset
+    val end = node.children[node.children.size - 1].endOffset
+    MarkdownCode(content.subSequence(start, end).toString().replaceIndent(), "txt")
+}
+
+@Composable
+private fun MarkdownCode(
+    code: String,
+    lang: String,
+    style: TextStyle = LocalMarkdownTypography.current.code
+) {
+    val color = LocalMarkdownColors.current.codeText
+    var language = lang
+    language = language.replace("^golang$".toRegex(), "go")
+//        language=language.replace("^java$".toRegex(),"kotlin")
+    val backgroundCodeColor = LocalMarkdownColors.current.codeBackground
+    Surface(
+        color = backgroundCodeColor,
+        shape = RoundedCornerShape(8.dp),
+        modifier = Modifier.fillMaxWidth().padding(top = 8.dp, bottom = 8.dp)
+    ) {
+
+        val syn = SyntaxLanguage.getByName(language)
+        if (syn != null) {
+            val highlights = Highlights.Builder()
+                .code(code)
+                .theme(SyntaxThemes.darcula())
+                .language(syn)
+                .build()
+                .getHighlights()
+            Text(
+                text = buildAnnotatedString {
+                    append(code)
+                    highlights
+                        .filterIsInstance<ColorHighlight>()
+                        .forEach {
+                            addStyle(
+                                SpanStyle(color = Color(it.rgb).copy(alpha = 1f)),
+                                start = it.location.start,
+                                end = it.location.end,
+                            )
+                        }
+
+                    highlights
+                        .filterIsInstance<BoldHighlight>()
+                        .forEach {
+                            addStyle(
+                                SpanStyle(fontWeight = FontWeight.Bold),
+                                start = it.location.start,
+                                end = it.location.end,
+                            )
+                        }
+                },
+                color = color,
+                modifier = Modifier.horizontalScroll(rememberScrollState()).padding(8.dp),
+                style = style
+            )
+        } else {
+
+            val parser = remember { PrettifyParser() }
+            var themeState by remember { mutableStateOf(CodeThemeType.Monokai) }
+            val theme = remember(themeState) { themeState.theme }
+            val parsedCode: AnnotatedString = remember {
+                if (!parser.isSupport(language)) {
+                    AnnotatedString(code)
+                } else {
+                    parseCodeAsAnnotatedString(
+                        parser = parser,
+                        theme = theme,
+                        lang = language,
+                        code = code
+                    )
+                }
+            }
+
+            Text(
+                parsedCode,
+                color = color,
+                modifier = Modifier.horizontalScroll(rememberScrollState()).padding(8.dp),
+                style = style
+            )
+        }
+
+    }
+}
+
+@Composable
+fun ItemEditMenu(memo: Memo,onEdit: (memo:Memo?) -> Unit={ },onRemove: (memo:Memo) -> Unit={ },onPin: (memo:Memo) -> Unit={ }) {
+    var expanded by remember { mutableStateOf(false) }
+    Box() {
+        Icon(Icons.Default.MoreVert, "More", Modifier.clickable {
+            expanded = !expanded
+        })
+        DropdownMenu(
+            expanded = expanded,
+            onDismissRequest = { expanded = false }
+        ) {
+            DropdownMenuItem(
+                text = { Text("编辑") },
+                onClick = {
+                    onEdit(memo)
+                    expanded = false
+                }
+            )
+            DropdownMenuItem(
+                text = { Text("置顶") },
+                onClick = {
+                    onPin(memo)
+                    expanded = false
+                }
+            )
+            DropdownMenuItem(
+                text = { Text("删除", style = TextStyle(color = Color.Red)) },
+                onClick = {
+                    onRemove(memo)
+                    expanded = false
+                },
+
+            )
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Resources.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Resources.kt
new file mode 100644
index 0000000..eb0ac63
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/components/Resources.kt
@@ -0,0 +1,98 @@
+package com.github.springeye.memosc.components
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Attachment
+import androidx.compose.material3.AssistChip
+import androidx.compose.material3.AssistChipDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.unit.dp
+import cafe.adriel.voyager.koin.getScreenModel
+import com.github.springeye.memosc.model.Memo
+import com.github.springeye.memosc.model.Resource
+import com.github.springeye.memosc.ui.AppModel
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+import kotlin.math.ceil
+import kotlin.math.min
+
+@Composable
+fun MemoResourceContent(memo: Memo,host:String) {
+    memo.resourceList?.let { resourceList ->
+        val cols = min(3, resourceList.size)
+        val imageList = resourceList.filter { it.type.startsWith("image/") }
+        if (imageList.isNotEmpty()) {
+            val rows = ceil(imageList.size.toFloat() / cols).toInt()
+            for (rowIndex in 0 until rows) {
+                Row {
+                    for (colIndex in 0 until cols) {
+                        val index = rowIndex * cols + colIndex
+                        if (index < imageList.size) {
+                            Box(modifier = Modifier.fillMaxWidth(1f / (cols - colIndex))) {
+                                val uri = imageList[index].uri(host)
+                                KamelImage(
+                                    asyncPainterResource(uri),
+                                    onLoading = {
+                                        Text("加载中")
+                                    },
+                                    onFailure = {
+                                        it.printStackTrace()
+                                        Text("加载失败")
+                                    },
+                                    contentDescription = null,
+                                    contentScale = ContentScale.FillWidth,
+                                    modifier = Modifier
+                                        .padding(2.dp)
+                                        .fillMaxWidth()
+                                        .clip(RoundedCornerShape(4.dp))
+                                )
+                            }
+                        } else {
+                            Spacer(modifier = Modifier.fillMaxWidth(1f / cols))
+                        }
+                    }
+                }
+            }
+        }
+        resourceList.filterNot { it.type.startsWith("image/") }.forEach { resource ->
+            Attachment(resource,host)
+        }
+    }
+}
+
+@Composable
+fun Attachment(
+    resource: Resource,
+    host:String
+) {
+    val uriHandler = LocalUriHandler.current
+
+    AssistChip(
+        modifier = Modifier.padding(bottom = 10.dp),
+        onClick = {
+            uriHandler.openUri(resource.uri(host))
+        },
+        label = { Text(resource.filename) },
+        leadingIcon = {
+            Icon(
+                Icons.Outlined.Attachment,
+                contentDescription = "附件",
+                Modifier.size(AssistChipDefaults.IconSize)
+            )
+        }
+    )
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/IFile.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/IFile.desktop.kt
new file mode 100644
index 0000000..602e01b
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/IFile.desktop.kt
@@ -0,0 +1,19 @@
+package com.github.springeye.memosc.core
+
+import java.io.File
+
+actual fun createIFile(path: String): IFile {
+    return IFileImpl(path)
+}
+internal class IFileImpl(val path:String): IFile {
+    val file=File(path)
+    override fun readBytes(): ByteArray {
+        return file.readBytes()
+    }
+
+    override val mimeType: String
+        get() = java.nio.file.Files.probeContentType(file.toPath())
+    override val filename: String
+        get() = file.name
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/Instant.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/Instant.desktop.kt
new file mode 100644
index 0000000..4b21f5e
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/Instant.desktop.kt
@@ -0,0 +1,23 @@
+package com.github.springeye.memosc.core
+import kotlinx.datetime.Instant
+import java.text.SimpleDateFormat
+import java.util.Date
+
+actual fun Instant.formatDate(
+    pattern: String,
+    defValue: String
+): String {
+    return try {
+        SimpleDateFormat(pattern).format(Date(this.toEpochMilliseconds()))
+    } catch (e: Exception) {
+        defValue
+    }
+}
+
+actual fun String.parseDate(pattern: String, defValue: Long): Long {
+    return try {
+        SimpleDateFormat(pattern).parse(this).time
+    } catch (e: Exception) {
+        defValue
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/LinkBuilder.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/LinkBuilder.kt
new file mode 100644
index 0000000..fcbb7e8
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/core/LinkBuilder.kt
@@ -0,0 +1,142 @@
+package com.github.springeye.memosc.core
+
+import java.net.URI
+
+
+class LinkBuilder {
+    protected var scheme: String?
+    protected var host: String?
+    protected var port: Int
+    protected var args: MutableMap<String, String?>  = HashMap<String, String?>()
+    protected var path: String?
+    protected var hash: String?
+
+    constructor() : this(null, null, 0, java.util.HashMap<String, String>(), null, null)
+    protected constructor(other: LinkBuilder) {
+        scheme = other.scheme
+        host = other.host
+        port = other.port
+        args = HashMap<String, String?>()
+        args.putAll(other.args)
+        path = other.path
+        hash = other.hash
+    }
+
+    protected constructor(
+        schema: String?,
+        host: String?,
+        port: Int,
+        args: Map<String, String?>?,
+        path: String?,
+        hash: String?
+    ) {
+        scheme = schema
+        this.host = host
+        this.port = port
+        this.args = java.util.HashMap<String, String?>()
+        if (args != null) {
+            this.args!!.putAll(args)
+        }
+        this.path = path
+        this.hash = hash
+    }
+
+    constructor(url: URI) {
+        val query: String = url.rawQuery
+        for (argLine in query.split("&".toRegex()).dropLastWhile { it.isEmpty() }
+            .toTypedArray()) {
+            if (argLine.isNotEmpty()) {
+                val i = argLine.indexOf('=')
+                if (i != -1) {
+                    args[argLine.substring(0, i)] = argLine.substring(i + 1)
+                } else {
+                    args[argLine] = null
+                }
+            }
+        }
+        scheme = url.getScheme()
+        host = url.getHost()
+        port = url.getPort()
+        path = url.getRawPath()
+        hash = url.getRawFragment()
+    }
+
+    fun url(url: URI): LinkBuilder {
+        return LinkBuilder(url)
+    }
+
+    fun scheme(schema: String?): LinkBuilder {
+        return LinkBuilder(schema, host, port, args, path, hash)
+    }
+
+    fun host(host: String): LinkBuilder {
+        if (host.indexOf('/') != -1) {
+            throw java.lang.IllegalArgumentException("Wrong host name: $host")
+        }
+        return LinkBuilder(scheme, host, port, args, path, hash)
+    }
+
+    fun port(port: Int): LinkBuilder {
+        return LinkBuilder(scheme, host, port, args, path, hash)
+    }
+
+    fun hash(hash: String?): LinkBuilder {
+        return LinkBuilder(scheme, host, port, args, path, hash)
+    }
+
+    fun path(path: String?): LinkBuilder {
+        return LinkBuilder(scheme, host, port, args, path, hash)
+    }
+
+    @JvmOverloads
+    fun arg(name: String, value: Any? = null): LinkBuilder {
+        val newArgs: MutableMap<String, String?> = java.util.HashMap<String, String?>(args)
+        newArgs[name] = value?.toString()
+        return LinkBuilder(scheme, host, port, newArgs, path, hash)
+    }
+
+    fun build(): String {
+        val buf: java.lang.StringBuilder = java.lang.StringBuilder()
+        if (scheme != null) {
+            buf.append(scheme)
+        }
+        buf.append("://")
+        if (host != null) {
+            buf.append(host)
+        }
+        if (port > 0 && "https" != scheme) {
+            buf.append(':').append(port)
+        }
+        if (path != null) {
+            if (path!![0] != '/') {
+                buf.append('/')
+            }
+            buf.append(path)
+        } else if (args!!.size > 0 || hash != null) {
+            buf.append('/')
+        }
+        if (args!!.size > 0) {
+            buf.append('?')
+            var first = true
+            for ((key, value) in args!!) {
+                if (!first) {
+                    buf.append('&')
+                } else {
+                    first = false
+                }
+                buf.append(java.net.URLEncoder.encode(key, "UTF-8"))
+                if (value != null && value.length > 0) {
+                    buf.append("=").append(java.net.URLEncoder.encode(value, "UTF-8"))
+                }
+            }
+        }
+        if (hash != null) {
+            buf.append('#').append(hash)
+        }
+        return buf.toString()
+    }
+
+    override fun toString(): String {
+        return build()
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/db/AppDatabase.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/db/AppDatabase.desktop.kt
new file mode 100644
index 0000000..2f7e49e
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/db/AppDatabase.desktop.kt
@@ -0,0 +1,24 @@
+package com.github.springeye.memosc.db
+
+import app.cash.sqldelight.EnumColumnAdapter
+import app.cash.sqldelight.db.SqlDriver
+import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver
+import app.cash.sqldelight.logs.LogSqliteDriver
+import com.github.springeye.memosc.db.model.AppDatabase
+import io.github.aakira.napier.Napier
+
+actual fun createAppDatabase(): AppDatabase {
+    val driver: SqlDriver = JdbcSqliteDriver("jdbc:sqlite:test.db").run {
+        LogSqliteDriver(this as SqlDriver){
+            Napier.d(it)
+        }
+    }
+    AppDatabase.Schema.create(driver)
+    return AppDatabase(driver,com.github.springeye.memosc.db.model.Memo.Adapter(
+        rowStatusAdapter = EnumColumnAdapter(),
+        visibilityAdapter = EnumColumnAdapter()
+    ),com.github.springeye.memosc.db.model.RemoteKey.Adapter(
+        rowStatusAdapter = EnumColumnAdapter(),
+        visibilityAdapter = EnumColumnAdapter()
+    ));
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.desktop.kt
new file mode 100644
index 0000000..b93ffa1
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/di/CacheCookiesStorage.desktop.kt
@@ -0,0 +1,57 @@
+package com.github.springeye.memosc.di
+
+import com.github.springeye.memosc.AppPreferences
+import io.ktor.client.plugins.cookies.CookiesStorage
+import io.ktor.http.Cookie
+import io.ktor.http.Url
+import io.ktor.util.date.getTimeMillis
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import java.util.concurrent.atomic.AtomicLong
+import kotlin.math.min
+
+
+actual class PersistentCookiesStorage actual constructor(val store: AppPreferences) :
+    CookiesStorage {
+    override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
+        val items = getCookiesFromStorage()
+        items.removeAll { it.name == cookie.name && it.matches(requestUrl) }
+        items.add(cookie.fillDefaults(requestUrl))
+        store.setString("cookies", Json.encodeToString(items.map { it.toSaveString() }.toList()));
+    }
+
+    private suspend fun getCookiesFromStorage(): MutableList<Cookie> {
+        val old = store.getString("cookies") ?: "[]"
+        val items = Json.decodeFromString<MutableList<String>>(old)
+        val cookies = mutableListOf<Cookie>()
+        return cookies.apply {
+            addAll(items.map { fromString(it) })
+        };
+    }
+    private val oldestCookie: AtomicLong = AtomicLong(0L)
+    override suspend fun get(requestUrl: Url): List<Cookie> {
+        val now = getTimeMillis()
+        if (now >= oldestCookie.get()) cleanup(now)
+        val list = getCookiesFromStorage().filter {
+            it.matches(requestUrl)
+        }
+        return list
+    }
+    private suspend fun cleanup(timestamp: Long) {
+        val cookies = getCookiesFromStorage()
+        cookies.removeAll { cookie ->
+            val expires = cookie.expires?.timestamp ?: return@removeAll false
+            expires < timestamp
+        }
+
+        val newOldest = cookies.fold(Long.MAX_VALUE) { acc, cookie ->
+            cookie.expires?.timestamp?.let { min(acc, it) } ?: acc
+        }
+
+        oldestCookie.set(newOldest)
+        store.setString("cookies", Json.encodeToString(cookies.map { it.toSaveString() }.toList()));
+    }
+    override fun close() {
+    }
+}
+
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ext/AnnotatedStringExt.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ext/AnnotatedStringExt.kt
new file mode 100644
index 0000000..a2eff69
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ext/AnnotatedStringExt.kt
@@ -0,0 +1,273 @@
+package com.github.springeye.memosc.ext
+
+import androidx.compose.foundation.text.appendInlineContent
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.*
+import androidx.compose.ui.text.font.FontFamily
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.sp
+import org.intellij.markdown.MarkdownElementTypes
+import org.intellij.markdown.MarkdownTokenTypes
+import org.intellij.markdown.ast.ASTNode
+import org.intellij.markdown.ast.getTextInNode
+import org.intellij.markdown.flavours.gfm.GFMTokenTypes
+import java.util.UUID
+
+@OptIn(ExperimentalTextApi::class)
+fun AnnotatedString.Builder.appendMarkdown(
+    markdownText: String,
+    node: ASTNode,
+    depth: Int = 0,
+    linkColor: Color,
+    onImage: (id: String, link: String) -> Unit,
+    onCheckbox: (id: String, startOffset: Int, endOffset: Int) -> Unit,
+    maxWidth: Float,
+    bulletColor: Color,
+    headlineLarge: TextStyle,
+    headlineMedium: TextStyle,
+    headlineSmall: TextStyle,
+): AnnotatedString.Builder {
+    when (node.type) {
+        MarkdownElementTypes.MARKDOWN_FILE, MarkdownElementTypes.PARAGRAPH, MarkdownElementTypes.UNORDERED_LIST, MarkdownElementTypes.ORDERED_LIST -> {
+            // Remove EOL after a headline
+            val headlineTypes = listOf(
+                MarkdownElementTypes.ATX_1,
+                MarkdownElementTypes.ATX_2,
+                MarkdownElementTypes.ATX_3,
+                MarkdownElementTypes.SETEXT_1,
+                MarkdownElementTypes.SETEXT_2
+            )
+            val children = node.children.filterIndexed { index, childNode ->
+                !(childNode.type == MarkdownTokenTypes.EOL && index > 0 && headlineTypes.contains(node.children[index - 1].type))
+            }
+
+            children.forEach { childNode ->
+                appendMarkdown(
+                    markdownText = markdownText,
+                    node = childNode,
+                    depth = depth + 1,
+                    linkColor = linkColor,
+                    onImage = onImage,
+                    onCheckbox = onCheckbox,
+                    maxWidth = maxWidth,
+                    bulletColor = bulletColor,
+                    headlineLarge = headlineLarge,
+                    headlineMedium = headlineMedium,
+                    headlineSmall = headlineSmall
+                )
+            }
+        }
+
+        MarkdownElementTypes.INLINE_LINK -> {
+            val linkDestination =
+                node.children.findLast { it.type == MarkdownElementTypes.LINK_DESTINATION }
+                    ?: return this
+            val linkText = node.children.find { it.type == MarkdownElementTypes.LINK_TEXT }?.children
+
+            withAnnotation(UrlAnnotation(linkDestination.getTextInNode(markdownText).toString())) {
+                withStyle(SpanStyle(linkColor)) {
+                    linkText?.filterIndexed { index, _ -> index != 0 && index != linkText.size - 1 }?.forEach { childNode ->
+                        appendMarkdown(
+                            markdownText = markdownText,
+                            node = childNode,
+                            depth = depth + 1,
+                            linkColor = linkColor,
+                            onImage = onImage,
+                            onCheckbox = onCheckbox,
+                            maxWidth = maxWidth,
+                            bulletColor = bulletColor,
+                            headlineLarge = headlineLarge,
+                            headlineMedium = headlineMedium,
+                            headlineSmall = headlineSmall
+                        )
+                    } ?: Unit
+                }
+            }
+        }
+
+        MarkdownElementTypes.AUTOLINK, GFMTokenTypes.GFM_AUTOLINK -> {
+            val linkDestination = node.getTextInNode(markdownText).toString()
+            withAnnotation(UrlAnnotation(linkDestination)) {
+                withStyle(SpanStyle(linkColor)) {
+                    append(linkDestination)
+                }
+            }
+        }
+
+        MarkdownElementTypes.EMPH -> {
+            withStyle(SpanStyle(fontStyle = FontStyle.Italic)) {
+                node.children.filter { it.type != MarkdownTokenTypes.EMPH }.forEach { childNode ->
+                    appendMarkdown(
+                        markdownText = markdownText,
+                        node = childNode,
+                        depth = depth + 1,
+                        linkColor = linkColor,
+                        onImage = onImage,
+                        onCheckbox = onCheckbox,
+                        maxWidth = maxWidth,
+                        bulletColor = bulletColor,
+                        headlineLarge = headlineLarge,
+                        headlineMedium = headlineMedium,
+                        headlineSmall = headlineSmall
+                    )
+                }
+            }
+        }
+
+        MarkdownElementTypes.STRONG -> {
+            withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
+                node.children.filter { it.type != MarkdownTokenTypes.EMPH }.forEach { childNode ->
+                    appendMarkdown(
+                        markdownText = markdownText,
+                        node = childNode,
+                        depth = depth + 1,
+                        linkColor = linkColor,
+                        onImage = onImage,
+                        onCheckbox = onCheckbox,
+                        maxWidth = maxWidth,
+                        bulletColor = bulletColor,
+                        headlineLarge = headlineLarge,
+                        headlineMedium = headlineMedium,
+                        headlineSmall = headlineSmall
+                    )
+                }
+            }
+        }
+
+        MarkdownElementTypes.CODE_SPAN -> {
+            withStyle(SpanStyle(fontFamily = FontFamily.Monospace)) {
+                node.children.filter { it.type != MarkdownTokenTypes.BACKTICK }.forEach { childNode ->
+                    appendMarkdown(
+                        markdownText = markdownText,
+                        node = childNode,
+                        depth = depth + 1,
+                        linkColor = linkColor,
+                        onImage = onImage,
+                        onCheckbox = onCheckbox,
+                        maxWidth = maxWidth,
+                        bulletColor = bulletColor,
+                        headlineLarge = headlineLarge,
+                        headlineMedium = headlineMedium,
+                        headlineSmall = headlineSmall
+                    )
+                }
+            }
+        }
+
+        MarkdownElementTypes.CODE_FENCE -> {
+            withStyle(SpanStyle(fontFamily = FontFamily.Monospace)) {
+                node.children.filter {
+                    it.type != MarkdownTokenTypes.CODE_FENCE_START
+                        && it.type != MarkdownTokenTypes.CODE_FENCE_END
+                        && it.type != MarkdownTokenTypes.FENCE_LANG
+                }.drop(1).dropLast(1).forEach { childNode ->
+                    appendMarkdown(
+                        markdownText = markdownText,
+                        node = childNode,
+                        depth = depth + 1,
+                        linkColor = linkColor,
+                        onImage = onImage,
+                        onCheckbox = onCheckbox,
+                        maxWidth = maxWidth,
+                        bulletColor = bulletColor,
+                        headlineLarge = headlineLarge,
+                        headlineMedium = headlineMedium,
+                        headlineSmall = headlineSmall
+                    )
+                }
+            }
+        }
+
+        MarkdownElementTypes.IMAGE -> {
+            val linkNode = node.children.findLast { it.type == MarkdownElementTypes.INLINE_LINK } ?: return this
+            val imageUrlNode =
+                linkNode.children.findLast { it.type == MarkdownElementTypes.LINK_DESTINATION }
+                    ?: return this
+            val imageUrl = imageUrlNode.getTextInNode(markdownText).toString()
+            val id = UUID.randomUUID().toString()
+
+            onImage(id, imageUrl)
+            withStyle(ParagraphStyle(lineHeight = (maxWidth * 9f / 16f).sp)) {
+                appendInlineContent(id, imageUrl)
+            }
+        }
+
+        MarkdownElementTypes.ATX_1,
+        MarkdownElementTypes.SETEXT_1,
+        MarkdownElementTypes.ATX_2,
+        MarkdownElementTypes.SETEXT_2,
+        MarkdownElementTypes.ATX_3 -> {
+            var content = node.children.find { it.type == MarkdownTokenTypes.ATX_CONTENT || it.type == MarkdownTokenTypes.SETEXT_CONTENT }
+
+            val textStyle = when (node.type) {
+                MarkdownElementTypes.ATX_1, MarkdownElementTypes.SETEXT_1 -> headlineLarge
+                MarkdownElementTypes.ATX_2, MarkdownElementTypes.SETEXT_2 -> headlineMedium
+                else -> headlineSmall
+            }
+
+            if (content != null) {
+                var children = content.children
+                if (children.firstOrNull()?.type == MarkdownTokenTypes.WHITE_SPACE) {
+                    children = children.drop(1)
+                }
+
+                withStyle(textStyle.toParagraphStyle()) {
+                    withStyle(textStyle.toSpanStyle()) {
+                        children.forEach {
+                            appendMarkdown(
+                                markdownText = markdownText,
+                                node = it,
+                                depth = depth + 1,
+                                linkColor = linkColor,
+                                onImage = onImage,
+                                onCheckbox = onCheckbox,
+                                maxWidth = maxWidth,
+                                bulletColor = bulletColor,
+                                headlineLarge = headlineLarge,
+                                headlineMedium = headlineMedium,
+                                headlineSmall = headlineSmall
+                            )
+                        }
+                    }
+                }
+            }
+        }
+
+        MarkdownElementTypes.LIST_ITEM -> {
+            var children = node.children
+            if (node.children.size >= 2 && node.children[1].type == GFMTokenTypes.CHECK_BOX) {
+                val id = UUID.randomUUID().toString()
+                onCheckbox(id, node.children[1].startOffset, node.children[1].endOffset)
+                appendInlineContent(id, node.children[1].getTextInNode(markdownText).toString())
+                append(' ')
+                children = children.drop(2)
+            } else {
+                withStyle(SpanStyle(color = bulletColor, fontWeight = FontWeight.Bold)) {
+                    append(children[0].getTextInNode(markdownText).toString())
+                }
+                children = children.drop(1)
+            }
+            children.forEach { childNode ->
+                appendMarkdown(
+                    markdownText = markdownText,
+                    node = childNode,
+                    depth = depth + 1,
+                    linkColor = linkColor,
+                    onImage = onImage,
+                    onCheckbox = onCheckbox,
+                    maxWidth = maxWidth,
+                    bulletColor = bulletColor,
+                    headlineLarge = headlineLarge,
+                    headlineMedium = headlineMedium,
+                    headlineSmall = headlineSmall
+                )
+            }
+        }
+
+        else -> {
+            append(node.getTextInNode(markdownText).toString())
+        }
+    }
+    return this
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/logger.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/logger.desktop.kt
new file mode 100644
index 0000000..aa9db7d
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/logger.desktop.kt
@@ -0,0 +1,8 @@
+package com.github.springeye.memosc
+
+import io.github.aakira.napier.DebugAntilog
+import io.github.aakira.napier.Napier
+
+actual fun initLogger() {
+    Napier.base(DebugAntilog())
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/main.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/main.kt
new file mode 100644
index 0000000..808bf7d
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/main.kt
@@ -0,0 +1,156 @@
+package com.github.springeye.memosc
+
+import androidx.compose.desktop.ui.tooling.preview.Preview
+import androidx.compose.foundation.*
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.window.WindowDraggableArea
+import androidx.compose.material.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.input.pointer.PointerEventType
+import androidx.compose.ui.input.pointer.onPointerEvent
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Window
+import androidx.compose.ui.window.WindowPlacement
+import androidx.compose.ui.window.WindowPosition
+import androidx.compose.ui.window.application
+import androidx.compose.ui.window.rememberWindowState
+import javax.swing.JFrame
+
+@OptIn(ExperimentalComposeUiApi::class, ExperimentalFoundationApi::class)
+fun main() = application {
+    initLogger()
+    val state= rememberWindowState(position = WindowPosition(Alignment.Center), width = 900.dp, height =600.dp )
+
+    Window(state=state,
+        resizable = false,
+        icon = painterResource("logo.png"),
+        onCloseRequest = ::exitApplication, title = "Memosc", undecorated = true) {
+        Column(modifier = Modifier) {
+
+            Row(
+                modifier = Modifier/*.background(color = Color(75, 75, 75))*/
+                    .fillMaxWidth()
+                    .height(35.dp)
+                    .background(Color(0xffdddddd))
+                    .onClick(onDoubleClick = {
+                        if (window.extendedState == JFrame.MAXIMIZED_BOTH) {
+                            window.extendedState = JFrame.NORMAL
+                        } else {
+                            window.extendedState = JFrame.MAXIMIZED_BOTH
+                        }
+
+                    }){}
+                    .padding(start = 20.dp),
+                verticalAlignment = Alignment.CenterVertically
+            ) {
+                WindowDraggableArea(
+                    modifier = Modifier.weight(1f)
+                ) {
+                    Text(text = window.title, color = Color.Black)
+                }
+                Row {
+                    WinButton(
+                        onClick = {
+                            window.extendedState = JFrame.ICONIFIED
+                        },
+                        icon = painterResource("images/ic_chrome_minimize.png")
+                    )
+                    Spacer(modifier = Modifier.width(5.dp))
+                    WinButton(
+                        onClick = {
+                            if (window.extendedState == JFrame.MAXIMIZED_BOTH) {
+                                window.extendedState = JFrame.NORMAL
+                            } else {
+                                window.extendedState = JFrame.MAXIMIZED_BOTH
+                            }
+
+                        },
+                        icon = if(state.placement!=WindowPlacement.Maximized)painterResource("images/ic_chrome_maximize.png") else  painterResource("images/ic_chrome_unmaximize.png")
+                    )
+                    Spacer(modifier = Modifier.width(5.dp))
+                    WinButton(
+                        onClick = {
+                            exitApplication()
+                        },
+                        icon = painterResource("images/ic_chrome_close.png"),
+                        backgroundColor = ColorScheme(Color.Transparent,Color.Red.copy(alpha =0.7F),Color.Red.copy(alpha =1.0F)),
+                        iconColor = ColorScheme(Color.Black.copy(alpha = 0.8956F),Color.White.copy(alpha =0.7F),Color.White.copy(alpha =1.0F))
+                    )
+                }
+            }
+                    Box(Modifier.background(Color(0xffdddddd)).padding(start = 1.dp, end = 1.dp, bottom = 1.dp)){
+                        Bootstrap()
+                    }
+        }
+
+    }
+
+}
+ data class ColorScheme(
+    val normal:Color,
+    val hovered:Color,
+    val pressed:Color,
+)
+@OptIn(ExperimentalComposeUiApi::class)
+@Composable
+fun WinButton(
+    text: String = "",
+    icon: Painter,
+    backgroundColor: ColorScheme = ColorScheme(Color.Transparent,Color.Black.copy(alpha =0.0373F),Color.Black.copy(alpha =0.0241F)),
+    iconColor: ColorScheme = ColorScheme(Color.Black.copy(alpha = 0.8956F),Color.Black.copy(alpha =0.8956F),Color.Black.copy(alpha =0.6063F)),
+    onClick: () -> Unit = {},
+) {
+    var bgColor by remember { mutableStateOf(backgroundColor.normal) }
+    var icColor by remember { mutableStateOf(iconColor.normal) }
+    var selected by remember { mutableStateOf(false) }
+    Box(modifier = Modifier
+        .background(bgColor)
+        .clickable {
+            selected=!selected;
+            bgColor = if(selected){
+                backgroundColor.pressed
+            }else{
+                backgroundColor.normal
+            }
+            icColor = if(selected){
+                iconColor.pressed
+            }else{
+                iconColor.normal
+            }
+            onClick.invoke()
+        }
+        .onPointerEvent(PointerEventType.Enter) {
+            bgColor = backgroundColor.hovered
+            icColor = iconColor.hovered
+        }
+        .onPointerEvent(PointerEventType.Exit) {
+            bgColor = backgroundColor.normal
+            icColor = iconColor.normal
+        }        .padding(12.dp)) {
+        Image(icon,"", colorFilter = ColorFilter.tint(icColor))
+    }
+    }
+
+@Preview
+@Composable
+fun AppDesktopPreview() {
+    DesktopApp()
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.desktop.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.desktop.kt
new file mode 100644
index 0000000..911f7e4
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/model/DailyUsageStat.desktop.kt
@@ -0,0 +1,43 @@
+package com.github.springeye.memosc.model
+
+import kotlinx.datetime.toKotlinLocalDate
+import java.time.DayOfWeek
+import java.time.LocalDate
+import java.time.LocalDateTime
+import java.time.OffsetDateTime
+import java.time.format.TextStyle
+import java.time.temporal.WeekFields
+import java.util.Locale
+
+actual fun initialMatrix(): List<DailyUsageStat> {
+    var now = LocalDate.now()
+    now=now.plusDays(15)
+    val lengthOfYear = now.lengthOfYear()
+    val reversed = (0..lengthOfYear).map { day ->
+        val stat = DailyUsageStat(date = now.minusDays(day - 1L).toKotlinLocalDate())
+        stat
+    }.reversed()
+    return reversed
+}
+
+actual val weekDays: List<String>
+    get(){
+        val day = WeekFields.of(Locale.getDefault()).firstDayOfWeek
+        return DayOfWeek.entries.toTypedArray().mapIndexed { index, _ ->
+            day.plus(index.toLong()).getDisplayName(TextStyle.NARROW, Locale.getDefault())
+        }
+    }
+
+actual fun calculateMatrix(memos: Map<kotlinx.datetime.LocalDate, List<kotlinx.datetime.LocalDate>>): List<DailyUsageStat> {
+//    val countMap = HashMap<kotlinx.datetime.LocalDate, Int>()
+    val countMap=memos.mapValues { it.value.size }
+
+//    for (memo in memos) {
+//        val date = LocalDateTime.ofEpochSecond(memo.createdTs, 0, OffsetDateTime.now().offset).toLocalDate().toKotlinLocalDate()
+//        countMap[date] = (countMap[date] ?: 0) + 1
+//    }
+
+    return initialMatrix().map {
+        it.copy(count = countMap[it.date] ?: 0)
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/app/AppScreen.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/app/AppScreen.kt
new file mode 100644
index 0000000..d716334
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/app/AppScreen.kt
@@ -0,0 +1,69 @@
+package com.github.springeye.memosc.ui.app
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import com.github.springeye.memosc.LoadingAnimation
+import com.github.springeye.memosc.LocalNotification
+import com.github.springeye.memosc.ui.home.HomeScreen
+import com.github.springeye.memosc.ui.login.LoginScreen
+import org.jetbrains.compose.resources.ExperimentalResourceApi
+
+object AppScreen : Screen {
+
+    @OptIn(ExperimentalResourceApi::class)
+    @Composable
+    override fun Content() {
+//        val screenModel = rememberScreenModel { AppScreenModel() }
+        val no= LocalNotification.current
+        val screenModel = getScreenModel<AppScreenModel>()
+        val state by screenModel.state.collectAsState()
+        val navigator = LocalNavigator.currentOrThrow
+        LaunchedEffect(state){
+            println(state)
+            when (state) {
+                is AppScreenModel.State.Loading->{
+//                    no.showLoading()
+                }
+                is AppScreenModel.State.Result->{
+//                    no.hideLoading()
+                    if((state as AppScreenModel.State.Result).isLogin){
+                        navigator.replace(HomeScreen)
+                    }else{
+                        navigator.replace(LoginScreen)
+                    }
+                }
+
+                else -> {
+
+                }
+            }
+        }
+
+        MaterialTheme {
+
+            Column(Modifier.fillMaxWidth().fillMaxHeight(),
+                horizontalAlignment = Alignment.CenterHorizontally,
+                verticalArrangement = Arrangement.Center
+                ) {
+                if(state is AppScreenModel.State.Loading) {
+                    LoadingAnimation()
+                }
+            }
+        }
+        LaunchedEffect(Unit){
+            screenModel.check()
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/HomeScreen.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/HomeScreen.kt
new file mode 100644
index 0000000..f3fc63b
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/HomeScreen.kt
@@ -0,0 +1,168 @@
+package com.github.springeye.memosc.ui.home
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.horizontalScroll
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.RowScope
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyItemScope
+import androidx.compose.foundation.lazy.items
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.CornerSize
+import androidx.compose.material3.Card
+import androidx.compose.material3.CardDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.input.pointer.PointerIcon
+import androidx.compose.ui.input.pointer.pointerHoverIcon
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.Dp
+import androidx.compose.ui.unit.dp
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.CurrentTab
+import cafe.adriel.voyager.navigator.tab.LocalTabNavigator
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabNavigator
+import com.github.springeye.memosc.LocalNotification
+import com.github.springeye.memosc.components.Heatmap
+import com.github.springeye.memosc.components.ITextField
+import com.github.springeye.memosc.core.Base64Image
+import com.github.springeye.memosc.model.weekDays
+import com.github.springeye.memosc.ui.home.tab.ArchivedTab
+import com.github.springeye.memosc.ui.home.tab.DaysReviewTab
+import com.github.springeye.memosc.ui.home.tab.ExploreTab
+import com.github.springeye.memosc.ui.home.tab.HomeTab
+import com.github.springeye.memosc.ui.home.tab.InboxTab
+import com.github.springeye.memosc.ui.home.tab.ProfileTab
+import com.github.springeye.memosc.ui.home.tab.ResourcesTab
+import com.github.springeye.memosc.ui.home.tab.SettingsTab
+
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+
+object HomeScreen : Screen {
+    private fun readResolve(): Any = HomeScreen
+
+    @Composable
+    override fun Content() {
+        val screenModel = getScreenModel<MemoModel>()
+        val user = screenModel.user
+        val notification = LocalNotification.current
+        val homeTab = remember {
+            HomeTab()
+        }
+        val weekDays = remember { weekDays }
+        TabNavigator(homeTab) {
+            Scaffold() {
+                Row() {
+//                    val menus = listOf(
+//                        MenuItem(Icons.Filled.Home, "主页"),
+//                        MenuItem(Icons.Filled.Call, "每日回顾"),
+//                        MenuItem(Icons.Filled.Call, "资源库"),
+//                        MenuItem(Icons.Filled.Call, "探索"),
+//                        MenuItem(Icons.Filled.Call, "通知"),
+//                        MenuItem(Icons.Filled.Call, "已归档"),
+//                        MenuItem(Icons.Filled.Call, "设置"),
+//                    )
+                    NavMenu(listOf(
+                        homeTab,
+//                        DaysReviewTab,
+                        ResourcesTab,
+//                        ExploreTab,
+//                        InboxTab,
+                        ProfileTab,
+                        ArchivedTab,
+                        SettingsTab
+                    ), width = 200.dp, header = {
+                        Row(
+                            modifier = Modifier.padding(horizontal = 10.dp, vertical = 20.dp)
+                                .height(40.dp), verticalAlignment = Alignment.CenterVertically
+                        ) {
+                            KamelImage(
+                                asyncPainterResource(Base64Image(user?.avatarUrl ?: "")),
+                                contentDescription = null,
+                                contentScale = ContentScale.Crop,            // crop the image if it's not a square
+                                modifier = Modifier
+                                    .aspectRatio(1F)                        // set the image aspect ratio to 1:1
+                                    .clip(CircleShape)                       // clip to the circle shape
+                            )
+                            Text(
+                                user?.displayName ?: "",
+                                modifier = Modifier.padding(start = 5.dp),
+                                style = MaterialTheme.typography.titleMedium
+                            )
+                        }
+                    })
+                    Box(modifier = Modifier.weight(1F)) {
+                        CurrentTab()
+                    }
+
+                }
+            }
+        }
+    }
+}
+
+@Composable
+fun RowScope.NavMenu(
+    menus: List<Tab>,
+    width: Dp = 180.dp,
+    header: (@Composable LazyItemScope.() -> Unit) = {}
+) {
+    val tabNavigator = LocalTabNavigator.current
+    LazyColumn(modifier = Modifier.width(width), horizontalAlignment = Alignment.Start) {
+        item(content = header)
+        items(menus) { item ->
+            NavButton(item, tabNavigator.current == item) {
+                tabNavigator.current = item;
+            }
+        }
+    }
+}
+
+@Composable
+fun NavButton(tab: Tab, selected: Boolean = false, onClick: () -> Unit) {
+    val colors = CardDefaults.cardColors(
+        containerColor = if (selected) MaterialTheme.colorScheme.surfaceVariant else Color.Transparent, //Card background color
+    )
+    Card(elevation = CardDefaults.cardElevation(0.dp),
+        shape = MaterialTheme.shapes.medium.copy(CornerSize(12.dp)),
+        colors = colors,
+        modifier = Modifier.padding(vertical = 5.dp, horizontal = 10.dp)
+            .clip(CardDefaults.shape)
+            .clickable { onClick.invoke() }
+            .pointerHoverIcon(PointerIcon.Hand)
+
+    ) {
+        Row(modifier = Modifier.padding(10.dp).fillMaxWidth()) {
+            val icon = tab.options.icon
+            if (icon != null) Image(icon, "", modifier = Modifier.padding(end = 10.dp).size(24.dp))
+            Text(tab.options.title)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ArchivedTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ArchivedTab.kt
new file mode 100644
index 0000000..67cafc9
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ArchivedTab.kt
@@ -0,0 +1,77 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.outlined.Archive
+import androidx.compose.material.icons.outlined.Inventory2
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import app.cash.paging.compose.collectAsLazyPagingItems
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.components.MemoList
+import com.github.springeye.memosc.core.Base64Image
+import com.github.springeye.memosc.ui.AppModel
+import com.github.springeye.memosc.ui.home.ArchivedModel
+import com.github.springeye.memosc.ui.home.MemoModel
+import com.github.springeye.memosc.ui.home.ProfileModel
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+
+object ArchivedTab:Tab{
+    @Composable
+    override fun Content() {
+        val screenModel = getScreenModel<MemoModel>()
+        val memoModel  =getScreenModel<ArchivedModel>()
+        val settings   by getScreenModel<AppModel>().state.collectAsState()
+        val user =screenModel.user
+        val items=memoModel.getPagerByUserId(user?.id?:0L).collectAsLazyPagingItems()
+        Scaffold {
+            if(items.itemCount<=0){
+                Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center){
+                    Text("没有任何数据")
+                }
+            }else{
+                MemoList(items, settings.host,{},{},{})
+            }
+
+        }
+    }
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "已归档"
+            val icon = rememberVectorPainter(Icons.Outlined.Archive)
+
+            return remember {
+                TabOptions(
+                    index = 5u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/DaysReviewTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/DaysReviewTab.kt
new file mode 100644
index 0000000..dd27333
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/DaysReviewTab.kt
@@ -0,0 +1,40 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.outlined.Archive
+import androidx.compose.material.icons.outlined.CalendarToday
+import androidx.compose.material.icons.outlined.CalendarViewDay
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import org.jetbrains.compose.resources.ExperimentalResourceApi
+import org.jetbrains.compose.resources.painterResource
+
+@OptIn(ExperimentalResourceApi::class)
+object DaysReviewTab:Tab{
+    @Composable
+    override fun Content() {
+        Text("home2")
+    }
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "每次回顾"
+            val icon = rememberVectorPainter(Icons.Outlined.CalendarToday)
+
+            return remember {
+                TabOptions(
+                    index = 1u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ExploreTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ExploreTab.kt
new file mode 100644
index 0000000..bb69e42
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ExploreTab.kt
@@ -0,0 +1,63 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.outlined.Explore
+import androidx.compose.material.pullrefresh.PullRefreshIndicator
+import androidx.compose.material.pullrefresh.pullRefresh
+import androidx.compose.material.pullrefresh.rememberPullRefreshState
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import app.cash.paging.LoadStateLoading
+import app.cash.paging.compose.collectAsLazyPagingItems
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.ui.home.MemoModel
+
+object ExploreTab:Tab{
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    override fun Content() {
+        val model = getScreenModel<MemoModel>()
+        val lazyPagingItems = model.pager.collectAsLazyPagingItems()
+        val refreshing = lazyPagingItems.loadState.refresh == LoadStateLoading
+        val pullRefreshState = rememberPullRefreshState(refreshing, onRefresh = {
+            lazyPagingItems.refresh()
+        })
+        Box(modifier = Modifier.fillMaxWidth().fillMaxHeight()
+            .background(Color.Red)
+            .pullRefresh(pullRefreshState)) {
+            Text("探索")
+            PullRefreshIndicator(refreshing, pullRefreshState, Modifier.align(Alignment.TopCenter))
+        }
+
+    }
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "探索"
+            val icon = rememberVectorPainter(Icons.Outlined.Explore)
+
+            return remember {
+                TabOptions(
+                    index = 3u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/HomeTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/HomeTab.kt
new file mode 100644
index 0000000..f35b323
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/HomeTab.kt
@@ -0,0 +1,215 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxHeight
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.width
+import androidx.compose.material.ExperimentalMaterialApi
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Refresh
+import androidx.compose.material.icons.outlined.Home
+import androidx.compose.material3.FloatingActionButtonDefaults
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.SmallFloatingActionButton
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.text.TextStyle
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import app.cash.paging.LoadStateLoading
+import app.cash.paging.compose.collectAsLazyPagingItems
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.components.EditMemo
+import com.github.springeye.memosc.components.EditMemoCallback
+import com.github.springeye.memosc.components.EditMemoState
+import com.github.springeye.memosc.components.Heatmap
+import com.github.springeye.memosc.components.ITextField
+import com.github.springeye.memosc.components.MemoList
+import com.github.springeye.memosc.components.rememberEditMemoState
+import com.github.springeye.memosc.model.MemosVisibility
+import com.github.springeye.memosc.model.Resource
+import com.github.springeye.memosc.model.weekDays
+import com.github.springeye.memosc.ui.AppModel
+import com.github.springeye.memosc.ui.home.MemoModel
+import kotlinx.coroutines.launch
+
+class HomeTab : Tab {
+    @OptIn(ExperimentalMaterialApi::class)
+    @Composable
+    override fun Content() {
+
+        val model = getScreenModel<MemoModel>()
+        val lazyPagingItems = model.pager.collectAsLazyPagingItems()
+        val refreshing = lazyPagingItems.loadState.refresh == LoadStateLoading
+        val appSettings = getScreenModel<AppModel>().state.collectAsState()
+
+        val scope = rememberCoroutineScope()
+        val state = rememberEditMemoState(host = appSettings.value.host, callback = object:EditMemoCallback(){
+            override suspend fun onSubmit(
+                state:EditMemoState,
+                editId: Long,
+                content: String,
+                resources: List<Resource>,
+                visibility: MemosVisibility
+            ) {
+                model.submit(editId,content,resources,visibility)
+                    state.setNewMemo(null)
+                    lazyPagingItems.refresh()
+            }
+
+            override suspend fun onCancle(state: EditMemoState) {
+                super.onCancle(state)
+                state.setNewMemo(null)
+            }
+            override suspend fun onUpload(state: EditMemoState, path: String) {
+                val resource=model.upload(path)
+                state.addResource(resource)
+            }
+
+            override suspend fun onRemoveResourced(state: EditMemoState, resource: Resource) {
+            }
+
+        })
+        LaunchedEffect(lazyPagingItems.itemSnapshotList.items){
+            model.fetchExtraInfo()
+        }
+        Scaffold(modifier = Modifier, floatingActionButton = {
+            SmallFloatingActionButton(onClick = {
+                lazyPagingItems.refresh()
+
+            }, shape = FloatingActionButtonDefaults.smallShape) {
+                androidx.compose.material3.Icon(Icons.Default.Refresh, "")
+            }
+        }) {
+            Row(modifier = Modifier) {
+                Box(modifier = Modifier.weight(1f)) {
+                    Column(modifier = Modifier.fillMaxWidth()) {
+                        Box(Modifier.padding(top = 20.dp)) {
+
+                            EditMemo(state)
+                        }
+                        MemoList(
+                            lazyPagingItems,
+                            appSettings.value.host,
+                            onEdit = {
+                                     state.setNewMemo(it)
+                            } ,
+                            onPin = {
+                                scope.launch {
+                                    model.setPininned(it)
+                                    lazyPagingItems.refresh()
+                                }
+                            },
+                            onRemove = {
+                                scope.launch {
+                                    model.remove(it)
+                                    lazyPagingItems.refresh()
+                                }
+                            })
+                    }
+
+                }
+                Column(
+                    modifier = Modifier.fillMaxHeight().width(200.dp).padding(10.dp)
+                ) {
+                    ITextField(
+                        value = model.query.content?:"",
+                        onValueChange = {
+                            model.filterContent(it)
+                        },
+                        singleLine = true,
+                        placeholder = {
+                            Text(
+                                "搜索备忘录",
+                                style = TextStyle.Default.copy(color = Color(156, 163, 175))
+                            )
+                        },
+                        modifier = Modifier.fillMaxWidth()
+                    )
+                    Box(modifier = Modifier.height(175.dp)) {
+                        Column (
+                            modifier = Modifier
+                                .fillMaxWidth()
+                                .padding(top = 10.dp),
+                        ) {
+                            Row(
+                                modifier = Modifier
+                                    .fillMaxWidth()
+                                    .padding(end = 5.dp),
+                                horizontalArrangement = Arrangement.SpaceBetween
+                            ) {
+                                for (day in weekDays) {
+                                    Text(
+                                        day,
+                                        style = MaterialTheme.typography.labelSmall,
+                                        color = MaterialTheme.colorScheme.outline
+                                    )
+                                }
+//                                    Text(
+//                                        weekDays[0],
+//                                        style = MaterialTheme.typography.labelSmall,
+//                                        color = MaterialTheme.colorScheme.outline
+//                                    )
+//                                    Text(
+//                                        weekDays[3],
+//                                        style = MaterialTheme.typography.labelSmall,
+//                                        color = MaterialTheme.colorScheme.outline
+//                                    )
+//                                    Text(
+//                                        weekDays[6],
+//                                        style = MaterialTheme.typography.labelSmall,
+//                                        color = MaterialTheme.colorScheme.outline
+//                                    )
+                            }
+                            Heatmap(model.matrix)
+                        }
+//                            Text("")
+                    }
+                }
+            }
+            if(state.editId>0){
+                Dialog(onDismissRequest = {
+                    state.setNewMemo(null)
+                }){
+                    EditMemo(state)
+                }
+            }
+        }
+
+    }
+
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "主页"
+            val icon = rememberVectorPainter(Icons.Outlined.Home)
+
+            return remember {
+                TabOptions(
+                    index = 0u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+
+
+
+}
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/InboxTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/InboxTab.kt
new file mode 100644
index 0000000..1e4cb86
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/InboxTab.kt
@@ -0,0 +1,49 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Notifications
+import androidx.compose.material.icons.outlined.Inbox
+import androidx.compose.material3.Button
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.ui.home.NotifiModel
+
+object InboxTab:Tab{
+    @Composable
+    override fun Content() {
+        val model=getScreenModel<NotifiModel>()
+
+        Column {
+            Text("Inbox ${model.counter}")
+            Button(onClick = {
+                model.addItem("aa")
+            }){
+                Text("aa")
+            }
+        }
+    }
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "通知"
+            val icon = rememberVectorPainter(Icons.Outlined.Inbox)
+
+            return remember {
+                TabOptions(
+                    index = 4u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ProfileTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ProfileTab.kt
new file mode 100644
index 0000000..319c518
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ProfileTab.kt
@@ -0,0 +1,121 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.aspectRatio
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.Person
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.window.Dialog
+import app.cash.paging.compose.collectAsLazyPagingItems
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.components.EditMemo
+import com.github.springeye.memosc.components.EditMemoCallback
+import com.github.springeye.memosc.components.EditMemoState
+import com.github.springeye.memosc.components.MemoList
+import com.github.springeye.memosc.components.rememberEditMemoState
+import com.github.springeye.memosc.core.Base64Image
+import com.github.springeye.memosc.model.Memo
+import com.github.springeye.memosc.model.MemosVisibility
+import com.github.springeye.memosc.model.Resource
+import com.github.springeye.memosc.ui.AppModel
+import com.github.springeye.memosc.ui.home.MemoModel
+import com.github.springeye.memosc.ui.home.ProfileModel
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+
+object ProfileTab : Tab {
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "个人资料"
+            val icon = rememberVectorPainter(Icons.Outlined.Person);
+            return remember {
+                TabOptions(
+                    index = 2u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+    @Composable
+    override fun Content() {
+        val profileModel  =getScreenModel<ProfileModel>()
+        val memoModel  =getScreenModel<MemoModel>()
+
+        val settings   by getScreenModel<AppModel>().state.collectAsState()
+        val user =memoModel.user
+        val items=profileModel.getPagerByUserId(user?.id?:0L).collectAsLazyPagingItems()
+        val editState= rememberEditMemoState(Memo.empty(), settings.host, callback = object:
+            EditMemoCallback() {
+            override suspend fun onCancle(state: EditMemoState) {
+                super.onCancle(state)
+                state.setNewMemo(null)
+            }
+            override suspend fun onUpload(state: EditMemoState, path: String) {
+                val resource=memoModel.upload(path)
+                state.addResource(resource)
+            }
+            override suspend fun onSubmit(
+                state: EditMemoState,
+                editId: Long,
+                content: String,
+                resources: List<Resource>,
+                visibility: MemosVisibility
+            ) {
+                memoModel.submit(editId,content,resources,visibility)
+            }
+            })
+        Scaffold {
+            Column(
+                modifier = Modifier.padding(horizontal = 10.dp, vertical = 20.dp).fillMaxSize()
+                , horizontalAlignment = Alignment.CenterHorizontally
+            ) {
+                KamelImage(
+                    asyncPainterResource(Base64Image(user?.avatarUrl ?: "")),
+                    contentDescription = null,
+                    contentScale = ContentScale.Crop,            // crop the image if it's not a square
+                    modifier = Modifier
+                        .height(80.dp)
+                        .aspectRatio(1F)                        // set the image aspect ratio to 1:1
+                        .clip(CircleShape)                       // clip to the circle shape
+                )
+                Text(
+                    user?.displayName ?: "",
+                    modifier = Modifier.padding(start = 5.dp),
+                    style = MaterialTheme.typography.titleMedium
+                )
+                MemoList(items, settings.host,{
+                    editState.setNewMemo(it)
+                },{},{})
+                if(editState.editId>0){
+                    Dialog(onDismissRequest = {
+                        editState.setNewMemo(null)
+                    }){
+                        EditMemo(editState)
+                    }
+                }
+            }
+
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ResourcesTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ResourcesTab.kt
new file mode 100644
index 0000000..efd7f37
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/ResourcesTab.kt
@@ -0,0 +1,90 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.ExperimentalLayoutApi
+import androidx.compose.foundation.layout.FlowRow
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.PhotoLibrary
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.unit.dp
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+import com.github.springeye.memosc.components.CardItem
+import com.github.springeye.memosc.core.formatDate
+import com.github.springeye.memosc.ui.AppModel
+import com.github.springeye.memosc.ui.home.MemoModel
+import io.kamel.image.KamelImage
+import io.kamel.image.asyncPainterResource
+import org.jetbrains.compose.resources.ExperimentalResourceApi
+
+object ResourcesTab : Tab {
+    @OptIn(ExperimentalLayoutApi::class)
+    @Composable
+    override fun Content() {
+        val model = getScreenModel<MemoModel>()
+        val settings = getScreenModel<AppModel>().state.value
+        val group by model.resourcesGroup.collectAsState(mapOf())
+        CardItem(modifier = Modifier.fillMaxSize()) {
+            Column {
+            for (entry in group) {
+                    Row {
+                        Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(horizontal = 15.dp).padding(top = 10.dp)) {
+                            Text("${entry.key.formatDate("yyyy")}", style = MaterialTheme.typography.bodySmall)
+                            Text("${entry.key.formatDate("MM")}", style = MaterialTheme.typography.titleMedium)
+                        }
+
+                        FlowRow(modifier = Modifier.verticalScroll(rememberScrollState())) {
+                            for (resource in entry.value) {
+                                KamelImage(
+                                    asyncPainterResource(resource.uri(settings.host)),
+                                    "",
+                                    modifier = Modifier.size(100.dp)
+                                        .padding(5.dp)
+                                        .fillMaxWidth()
+                                        .clip(RoundedCornerShape(4.dp)),
+                                    contentScale = ContentScale.FillWidth,
+
+                                )
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    @OptIn(ExperimentalResourceApi::class)
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "资源库"
+            val icon = rememberVectorPainter(Icons.Outlined.PhotoLibrary);
+            return remember {
+                TabOptions(
+                    index = 2u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/SettingsTab.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/SettingsTab.kt
new file mode 100644
index 0000000..c4d64d3
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/home/tab/SettingsTab.kt
@@ -0,0 +1,36 @@
+package com.github.springeye.memosc.ui.home.tab
+
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Add
+import androidx.compose.material.icons.filled.Home
+import androidx.compose.material.icons.filled.Settings
+import androidx.compose.material.icons.outlined.Settings
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
+import androidx.compose.ui.graphics.vector.rememberVectorPainter
+import cafe.adriel.voyager.navigator.tab.Tab
+import cafe.adriel.voyager.navigator.tab.TabOptions
+
+object SettingsTab:Tab{
+    @Composable
+    override fun Content() {
+        Text("home2")
+    }
+
+    override val options: TabOptions
+        @Composable
+        get() {
+            val title = "设置"
+            val icon = rememberVectorPainter(Icons.Outlined.Settings)
+
+            return remember {
+                TabOptions(
+                    index = 6u,
+                    title = title,
+                    icon = icon
+                )
+            }
+        }
+
+}
\ No newline at end of file
diff --git a/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreen.kt b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreen.kt
new file mode 100644
index 0000000..12a0461
--- /dev/null
+++ b/composeApp/src/desktopMain/kotlin/com/github/springeye/memosc/ui/login/LoginScreen.kt
@@ -0,0 +1,131 @@
+package com.github.springeye.memosc.ui.login
+
+import com.github.springeye.memosc.LocalNotification
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.wrapContentSize
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.Button
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.LocalMinimumInteractiveComponentEnforcement
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.OutlinedTextField
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.CompositionLocalProvider
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Alignment.Companion.CenterVertically
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.layout.ContentScale
+import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.unit.dp
+import cafe.adriel.voyager.core.screen.Screen
+import cafe.adriel.voyager.koin.getScreenModel
+import cafe.adriel.voyager.navigator.LocalNavigator
+import cafe.adriel.voyager.navigator.currentOrThrow
+import com.github.springeye.memosc.ui.home.HomeScreen
+import org.jetbrains.compose.resources.ExperimentalResourceApi
+import org.jetbrains.compose.resources.painterResource
+
+object LoginScreen:Screen {
+    @OptIn(ExperimentalMaterial3Api::class, ExperimentalResourceApi::class)
+    @Composable
+    override fun Content() {
+        val screenModel = getScreenModel<LoginScreenModel>()
+        val state by screenModel.state.collectAsState()
+        val navigator = LocalNavigator.currentOrThrow
+        var host:String by remember { mutableStateOf("") }
+        var username:String by remember { mutableStateOf("") }
+        var password:String by remember { mutableStateOf("") }
+        var rememberPassword by remember { mutableStateOf(true) }
+        val localNotification= LocalNotification.current
+        LaunchedEffect(state){
+            when (state) {
+
+                is LoginScreenModel.State.Result ->{
+                    localNotification.hideLoading()
+                    if((state as LoginScreenModel.State.Result).isSuccess){
+                        navigator.replace(HomeScreen)
+                    }else{
+                        localNotification.showPopUpMessage("登录失败")
+//                        navigator.replace(LoginScreen)
+                    }
+                }
+                is LoginScreenModel.State.Loading ->{
+                    localNotification.showLoading("登录中")
+                }
+
+                else -> {
+
+                }
+            }
+        }
+            val focusManager = LocalFocusManager.current
+            CompositionLocalProvider(LocalMinimumInteractiveComponentEnforcement provides false) {
+                Scaffold {
+
+                    Box(contentAlignment = Alignment.Center, modifier = Modifier.fillMaxSize()) {
+
+                        Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.width(350.dp)) {
+                            Row(Modifier.wrapContentSize(), verticalAlignment = Alignment.CenterVertically) {
+                                Image(
+                                    painterResource("logo.png"),
+                                    null,
+                                    contentScale = ContentScale.Crop,
+                                    modifier = Modifier.size(50.dp)
+                                        .clip(CircleShape)
+                                    ,)
+                                Text("Memos", style = MaterialTheme.typography.headlineMedium, modifier = Modifier.padding(start = 10.dp))
+                            }
+                            OutlinedTextField(host, onValueChange = {
+                                host = it
+                            }, label = {
+                                Text("服务器")
+                            },  singleLine = true,modifier = Modifier.fillMaxWidth().padding(top = 30.dp),
+
+                                )
+                            OutlinedTextField(username, onValueChange = {
+                                username = it
+                            }, label = {
+                                Text("用户名")
+                            }, singleLine = true, modifier = Modifier.fillMaxWidth().padding(top = 30.dp))
+                            OutlinedTextField(password, onValueChange = {
+                                password = it
+                            }, label = {
+                                Text("密码")
+                            },  singleLine = true,modifier = Modifier.fillMaxWidth().padding(top = 10.dp))
+                            Row(modifier = Modifier.align(Alignment.Start).padding(top = 10.dp)) {
+                                Checkbox(
+                                    rememberPassword,
+                                    onCheckedChange = { rememberPassword = it },
+                                )
+                                Text("保持登录", modifier = Modifier.align(CenterVertically))
+                            }
+                            Button(onClick = {
+                                screenModel.login(host,username,password)
+                            }, modifier = Modifier.fillMaxWidth().padding(top = 10.dp)){
+                                Text("登录")
+                            }
+                        }
+                    }
+
+                }
+            }
+    }
+}
diff --git a/composeApp/src/iosMain/kotlin/DataStorePreferences.ios.kt b/composeApp/src/iosMain/kotlin/DataStorePreferences.ios.kt
new file mode 100644
index 0000000..c870f73
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/DataStorePreferences.ios.kt
@@ -0,0 +1,31 @@
+import androidx.datastore.core.DataMigration
+import androidx.datastore.core.DataStore
+import androidx.datastore.core.handlers.ReplaceFileCorruptionHandler
+import androidx.datastore.preferences.core.Preferences
+import kotlinx.cinterop.ExperimentalForeignApi
+import kotlinx.coroutines.CoroutineScope
+import platform.Foundation.NSDocumentDirectory
+import platform.Foundation.NSFileManager
+import platform.Foundation.NSURL
+import platform.Foundation.NSUserDomainMask
+
+@OptIn(ExperimentalForeignApi::class)
+actual fun dataStorePreferences(
+    corruptionHandler: ReplaceFileCorruptionHandler<Preferences>?,
+    coroutineScope: CoroutineScope,
+    migrations: List<DataMigration<Preferences>>,
+): DataStore<Preferences> = createDataStoreWithDefaults(
+    corruptionHandler = corruptionHandler,
+    migrations = migrations,
+    coroutineScope = coroutineScope,
+    path = {
+        val documentDirectory: NSURL? = NSFileManager.defaultManager.URLForDirectory(
+            directory = NSDocumentDirectory,
+            inDomain = NSUserDomainMask,
+            appropriateForURL = null,
+            create = false,
+            error = null,
+        )
+        (requireNotNull(documentDirectory).path + "/$SETTINGS_PREFERENCES.preferences_pb")
+    }
+)
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/Database.ios.kt b/composeApp/src/iosMain/kotlin/Database.ios.kt
new file mode 100644
index 0000000..3197f50
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/Database.ios.kt
@@ -0,0 +1,5 @@
+import com.ctrip.sqllin.driver.DatabasePath
+
+actual fun getGlobalDatabasePath(): DatabasePath {
+    TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/MainViewController.kt b/composeApp/src/iosMain/kotlin/MainViewController.kt
new file mode 100755
index 0000000..1182225
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/MainViewController.kt
@@ -0,0 +1,3 @@
+import androidx.compose.ui.window.ComposeUIViewController
+
+fun MainViewController() = ComposeUIViewController { Bootstrap() }
diff --git a/composeApp/src/iosMain/kotlin/Platform.ios.kt b/composeApp/src/iosMain/kotlin/Platform.ios.kt
new file mode 100755
index 0000000..5cef987
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/Platform.ios.kt
@@ -0,0 +1,7 @@
+import platform.UIKit.UIDevice
+
+class IOSPlatform: Platform {
+    override val name: String = UIDevice.currentDevice.systemName() + " " + UIDevice.currentDevice.systemVersion
+}
+
+actual fun getPlatform(): Platform = IOSPlatform()
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/PopupNotification.ios.kt b/composeApp/src/iosMain/kotlin/PopupNotification.ios.kt
new file mode 100644
index 0000000..72a4675
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/PopupNotification.ios.kt
@@ -0,0 +1,15 @@
+import androidx.compose.runtime.MutableState
+
+actual fun createPopupNotification(state: MutableState<ToastState>): PopupNotification {
+//    TODO("Not yet implemented")
+    return object:PopupNotification{
+        override fun showPopUpMessage(text: String) {
+        }
+
+        override fun showLoading(text: String) {
+        }
+
+        override fun hideLoading() {
+        }
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/components/Heatmap.ios.kt b/composeApp/src/iosMain/kotlin/components/Heatmap.ios.kt
new file mode 100644
index 0000000..d0f9515
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/components/Heatmap.ios.kt
@@ -0,0 +1,7 @@
+package components
+
+import androidx.compose.ui.unit.Constraints
+
+actual fun countHeatmap(constraints: Constraints): Int {
+    TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/core/IFile.ios.kt b/composeApp/src/iosMain/kotlin/core/IFile.ios.kt
new file mode 100644
index 0000000..adda091
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/core/IFile.ios.kt
@@ -0,0 +1,6 @@
+package core
+
+
+actual fun createIFile(path: String): IFile {
+    TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/core/Instant.ios.kt b/composeApp/src/iosMain/kotlin/core/Instant.ios.kt
new file mode 100644
index 0000000..e6c447d
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/core/Instant.ios.kt
@@ -0,0 +1,32 @@
+import kotlinx.datetime.Instant
+import kotlinx.datetime.toNSDate
+import platform.Foundation.NSDateFormatter
+import platform.Foundation.timeIntervalSince1970
+
+actual fun Instant.formatDate(pattern: String, defValue: String): String {
+    return try {
+        val dateFormatter = NSDateFormatter()
+        dateFormatter.dateFormat = pattern
+        dateFormatter.stringFromDate(
+            toNSDate()
+        )
+    } catch (e: Exception) {
+        defValue
+    }
+
+}
+
+actual fun String.parseDate(pattern: String, defValue: Long): Long {
+    return try {
+        val dateFormatter = NSDateFormatter()
+        dateFormatter.dateFormat = pattern
+        val result = dateFormatter.dateFromString(this)?.timeIntervalSince1970?.toLong()
+        if (result != null) {
+            result * 1000
+        } else {
+            defValue
+        }
+    } catch (e: Exception) {
+        defValue
+    }
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/core/loader/MemoDao.ios.kt b/composeApp/src/iosMain/kotlin/core/loader/MemoDao.ios.kt
new file mode 100644
index 0000000..286aec3
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/core/loader/MemoDao.ios.kt
@@ -0,0 +1,5 @@
+package core.loader
+
+actual fun createMemoDao(): MemoDao {
+    return MemoDao()
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/di/CacheCookiesStorage.ios.kt b/composeApp/src/iosMain/kotlin/di/CacheCookiesStorage.ios.kt
new file mode 100644
index 0000000..d24b741
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/di/CacheCookiesStorage.ios.kt
@@ -0,0 +1,64 @@
+package di
+
+import AppPreferences
+import io.ktor.client.plugins.cookies.CookiesStorage
+import io.ktor.http.Cookie
+import io.ktor.http.CookieEncoding
+import io.ktor.http.Url
+import io.ktor.util.date.GMTDate
+import io.ktor.util.date.getTimeMillis
+import kotlinx.datetime.Clock
+import kotlinx.serialization.encodeToString
+import kotlinx.serialization.json.Json
+import okio.FileSystem
+import okio.Path
+import okio.Path.Companion.toPath
+import okio.buffer
+import okio.use
+import kotlin.concurrent.AtomicLong
+import kotlin.math.min
+
+
+actual class PersistentCookiesStorage actual constructor(val store: AppPreferences) :
+    CookiesStorage {
+    override suspend fun addCookie(requestUrl: Url, cookie: Cookie) {
+        val items = getCookiesFromStorage()
+        items.removeAll { it.name == cookie.name && it.matches(requestUrl) }
+        items.add(cookie.fillDefaults(requestUrl))
+        store.setString("cookies", Json.encodeToString(items.map { it.toSaveString() }.toList()));
+    }
+
+    private suspend fun getCookiesFromStorage(): MutableList<Cookie> {
+        val old = store.getString("cookies") ?: "[]"
+        val items = Json.decodeFromString<MutableList<String>>(old)
+        val cookies = mutableListOf<Cookie>()
+        return cookies.apply {
+            addAll(items.map { fromString(it) })
+        };
+    }
+    private val oldestCookie: AtomicLong = AtomicLong(0L)
+    override suspend fun get(requestUrl: Url): List<Cookie> {
+        val now = getTimeMillis()
+        if (now >= oldestCookie.value) cleanup(now)
+        return getCookiesFromStorage().filter {
+            it.matches(requestUrl)
+        }
+    }
+    private suspend fun cleanup(timestamp: Long) {
+        val cookies = getCookiesFromStorage()
+        cookies.removeAll { cookie ->
+            val expires = cookie.expires?.timestamp ?: return@removeAll false
+            expires < timestamp
+        }
+
+        val newOldest = cookies.fold(Long.MAX_VALUE) { acc, cookie ->
+            cookie.expires?.timestamp?.let { min(acc, it) } ?: acc
+        }
+
+        oldestCookie.value=newOldest
+        store.setString("cookies", Json.encodeToString(cookies.map { it.toSaveString() }.toList()));
+    }
+    override fun close() {
+    }
+}
+
diff --git a/composeApp/src/iosMain/kotlin/logger.ios.kt b/composeApp/src/iosMain/kotlin/logger.ios.kt
new file mode 100644
index 0000000..8fc3a0f
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/logger.ios.kt
@@ -0,0 +1,6 @@
+import io.github.aakira.napier.DebugAntilog
+import io.github.aakira.napier.Napier
+
+actual fun initLogger() {
+    Napier.base(DebugAntilog())
+}
\ No newline at end of file
diff --git a/composeApp/src/iosMain/kotlin/model/DailyUsageStat.ios.kt b/composeApp/src/iosMain/kotlin/model/DailyUsageStat.ios.kt
new file mode 100644
index 0000000..3d38af1
--- /dev/null
+++ b/composeApp/src/iosMain/kotlin/model/DailyUsageStat.ios.kt
@@ -0,0 +1,5 @@
+package model
+
+actual fun initialMatrix(): List<DailyUsageStat> {
+    TODO("Not yet implemented")
+}
\ No newline at end of file
diff --git a/gradle.properties b/gradle.properties
new file mode 100755
index 0000000..61ff430
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,19 @@
+kotlin.code.style=official
+
+#Gradle
+org.gradle.jvmargs=-Xmx2048M -Dfile.encoding=UTF-8 -Dkotlin.daemon.jvm.options\="-Xmx2048M"
+
+
+#Android
+android.nonTransitiveRClass=true
+android.useAndroidX=true
+
+#MPP
+kotlin.mpp.androidSourceSetLayoutVersion=2
+kotlin.mpp.enableCInteropCommonization=true
+
+#Development
+development=true
+
+
+kotlin.native.ignoreDisabledTargets=true
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100755
index 0000000..c3c8b65
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,135 @@
+[versions]
+agp = "8.2.1"
+android-compileSdk = "34"
+android-minSdk = "26"
+android-targetSdk = "34"
+androidDriver = "2.0.1"
+androidx-activityCompose = "1.8.2"
+androidx-appcompat = "1.6.1"
+androidx-constraintlayout = "2.1.4"
+androidx-core-ktx = "1.12.0"
+androidx-espresso-core = "3.5.1"
+androidx-material = "1.10.0"
+androidx-test-junit = "1.1.5"
+compose = "1.5.4"
+compose-compiler = "1.5.6"
+compose-plugin = "1.5.11"
+datastorePreferences = "1.1.0-alpha07"
+github-napier = "2.7.1"
+highlights = "0.7.1"
+jetbrains-uiToolingPreviewDesktop = "1.5.11"
+junit = "4.13.2"
+kamelImage = "0.9.1"
+kotlin = "1.9.21"
+kotlinxCoroutinesSwing = "1.7.3"
+kotlinxDatetime = "0.5.0"
+kotlinxSerializationJson = "1.6.0"
+ktor = "2.3.7"
+mpfilepicker = "3.1.0"
+multiplatformMarkdownRenderer = "0.10.0"
+ktorfitVersion = "1.11.0"
+napier = "2.6.1"
+okhttp = "4.12.0"
+okio = "3.7.0"
+pagingComposeCommon = "3.3.0-alpha02-0.4.0"
+retrofit = "2.9.0"
+statelyCommon = "2.0.5"
+voyager = "1.0.0"
+koin="3.5.3"
+koin-compose="1.1.2"
+annotationJvm = "1.7.1"
+coroutines = "1.7.3"
+androidx-startup = "1.1.1"
+composeIcons = "1.1.0"
+uiGraphicsDesktop = "1.6.0-beta03"
+packageVersion="1.0.0"
+
+
+[libraries]
+android-driver = { module = "app.cash.sqldelight:android-driver", version.ref = "androidDriver" }
+androidx-paging3-extensions = { module = "app.cash.sqldelight:androidx-paging3-extensions", version.ref = "androidDriver" }
+androidx-startup = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup" }
+androidx-datastore-preferences-core = { module = "androidx.datastore:datastore-preferences-core", version.ref = "datastorePreferences" }
+highlights = { module = "dev.snipme:highlights", version.ref = "highlights" }
+jetbrains-ui-tooling-preview-desktop = { module = "org.jetbrains.compose.ui:ui-tooling-preview-desktop", version.ref = "jetbrains-uiToolingPreviewDesktop" }
+kamel-image = { module = "media.kamel:kamel-image", version.ref = "kamelImage" }
+koin-android = { module = "io.insert-koin:koin-android", version.ref = "koin" }
+koin-androidx-compose = { module = "io.insert-koin:koin-androidx-compose", version.ref = "koin" }
+koin-androidx-navigation = { module = "io.insert-koin:koin-androidx-navigation", version.ref = "koin" }
+koin-androidx-workmanager = { module = "io.insert-koin:koin-androidx-workmanager", version.ref = "koin" }
+kotlin-stdlib = { module = "org.jetbrains.kotlin:kotlin-stdlib", version.ref = "kotlin" }
+kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" }
+kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" }
+androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" }
+androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" }
+androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" }
+androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" }
+androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" }
+compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
+compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
+compose-ui-tooling-preview-android = { module = "androidx.compose.ui:ui-tooling-preview-android", version.ref = "compose" }
+
+compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
+compose-material = { module = "androidx.compose.material:material", version.ref = "compose" }
+kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "kotlinxCoroutinesSwing" }
+kotlinx-coroutines-core = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-core", version.ref = "coroutines" }
+kotlinx-coroutines-android = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-android", version.ref = "coroutines" }
+
+
+kotlinx-datetime = { module = "org.jetbrains.kotlinx:kotlinx-datetime", version.ref = "kotlinxDatetime" }
+kotlinx-serialization-json = { module = "org.jetbrains.kotlinx:kotlinx-serialization-json", version.ref = "kotlinxSerializationJson" }
+ktor-client-cio = { module = "io.ktor:ktor-client-cio", version.ref = "ktor" }
+ktor-client-content-negotiation = { module = "io.ktor:ktor-client-content-negotiation", version.ref = "ktor" }
+ktor-client-core = { module = "io.ktor:ktor-client-core", version.ref = "ktor" }
+ktor-client-logging = { module = "io.ktor:ktor-client-logging", version.ref = "ktor" }
+ktor-client-okhttp = { module = "io.ktor:ktor-client-okhttp", version.ref = "ktor" }
+ktor-client-darwin = { module = "io.ktor:ktor-client-darwin", version.ref = "ktor" }
+
+ktor-serialization-kotlinx-json = { module = "io.ktor:ktor-serialization-kotlinx-json", version.ref = "ktor" }
+mpfilepicker = { module = "com.darkrockstudios:mpfilepicker", version.ref = "mpfilepicker" }
+multiplatform-markdown-renderer = { module = "com.mikepenz:multiplatform-markdown-renderer", version.ref = "multiplatformMarkdownRenderer" }
+ktorfit-lib = { module = "de.jensklingenberg.ktorfit:ktorfit-lib", version.ref = "ktorfitVersion" }
+napier-v271 = { module = "io.github.aakira:napier", version.ref = "github-napier" }
+okhttp-interceptor-logging = { module = "com.squareup.okhttp3:logging-interceptor", version.ref = "okhttp" }
+napier = { module = "io.github.aakira:napier", version.ref = "napier" }
+okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
+okio = { module = "com.squareup.okio:okio", version.ref = "okio" }
+paging-compose-common = { module = "app.cash.paging:paging-compose-common", version.ref = "pagingComposeCommon" }
+retrofit = { module = "com.squareup.retrofit2:retrofit", version.ref = "retrofit" }
+retrofit-converter-gson = { module = "com.squareup.retrofit2:converter-gson", version.ref = "retrofit" }
+
+sqlite-driver = { module = "app.cash.sqldelight:sqlite-driver", version.ref = "androidDriver" }
+stately-common = { module = "co.touchlab:stately-common", version.ref = "statelyCommon" }
+voyager-navigator = { module = "cafe.adriel.voyager:voyager-navigator", version.ref = "voyager" }
+voyager-screenModel = { module = "cafe.adriel.voyager:voyager-screenmodel", version.ref = "voyager" }
+voyager-bottomSheetNavigator = { module = "cafe.adriel.voyager:voyager-bottom-sheet-navigator", version.ref = "voyager" }
+voyager-tabNavigator = { module = "cafe.adriel.voyager:voyager-tab-navigator", version.ref = "voyager" }
+voyager-transitions = { module = "cafe.adriel.voyager:voyager-transitions", version.ref = "voyager" }
+voyager-koin = { module = "cafe.adriel.voyager:voyager-koin", version.ref = "voyager" }
+voyager-hilt = { module = "cafe.adriel.voyager:voyager-hilt", version.ref = "voyager" }
+voyager-kodein = { module = "cafe.adriel.voyager:voyager-kodein", version.ref = "voyager" }
+voyager-rxjava = { module = "cafe.adriel.voyager:voyager-rxjava", version.ref = "voyager" }
+koin-core = { module = "io.insert-koin:koin-core", version.ref = "koin" }
+koin-compose = { module = "io.insert-koin:koin-compose", version.ref = "koin-compose" }
+androidx-annotation-jvm = { group = "androidx.annotation", name = "annotation-jvm", version.ref = "annotationJvm" }
+composeIcons-cssGg = { module = "br.com.devsrsouza.compose.icons:css-gg", version.ref = "composeIcons" }
+composeIcons-weatherIcons = { module = "br.com.devsrsouza.compose.icons:erikflowers-weather-icons", version.ref = "composeIcons" }
+composeIcons-evaIcons = { module = "br.com.devsrsouza.compose.icons:eva-icons", version.ref = "composeIcons" }
+composeIcons-feather = { module = "br.com.devsrsouza.compose.icons:feather", version.ref = "composeIcons" }
+composeIcons-fontAwesome = { module = "br.com.devsrsouza.compose.icons:font-awesome", version.ref = "composeIcons" }
+composeIcons-lineAwesome = { module = "br.com.devsrsouza.compose.icons:line-awesome", version.ref = "composeIcons" }
+composeIcons-linea = { module = "br.com.devsrsouza.compose.icons:linea", version.ref = "composeIcons" }
+composeIcons-octicons = { module = "br.com.devsrsouza.compose.icons:octicons", version.ref = "composeIcons" }
+composeIcons-simpleIcons = { module = "br.com.devsrsouza.compose.icons:simple-icons", version.ref = "composeIcons" }
+composeIcons-tablerIcons = { module = "br.com.devsrsouza.compose.icons:tabler-icons", version.ref = "composeIcons" }
+androidx-ui-graphics-desktop = { group = "androidx.compose.ui", name = "ui-graphics-desktop", version.ref = "uiGraphicsDesktop" }
+
+
+[plugins]
+androidApplication = { id = "com.android.application", version.ref = "agp" }
+androidLibrary = { id = "com.android.library", version.ref = "agp" }
+jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" }
+kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100755
index 0000000000000000000000000000000000000000..033e24c4cdf41af1ab109bc7f253b2b887023340
GIT binary patch
literal 63375
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
E0aA=+Q2+n{

literal 0
HcmV?d00001

diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100755
index 0000000..3fa8f86
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,7 @@
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..fcb6fca
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,248 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    if ! command -v java >/dev/null 2>&1
+    then
+        die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100755
index 0000000..93e3f59
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,92 @@
+@rem
+@rem Copyright 2015 the original author or authors.
+@rem
+@rem Licensed under the Apache License, Version 2.0 (the "License");
+@rem you may not use this file except in compliance with the License.
+@rem You may obtain a copy of the License at
+@rem
+@rem      https://www.apache.org/licenses/LICENSE-2.0
+@rem
+@rem Unless required by applicable law or agreed to in writing, software
+@rem distributed under the License is distributed on an "AS IS" BASIS,
+@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+@rem See the License for the specific language governing permissions and
+@rem limitations under the License.
+@rem
+
+@if "%DEBUG%"=="" @echo off
+@rem ##########################################################################
+@rem
+@rem  Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+set DIRNAME=%~dp0
+if "%DIRNAME%"=="" set DIRNAME=.
+@rem This is normally unused
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Resolve any "." and ".." in APP_HOME to make it shorter.
+for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if %ERRORLEVEL% equ 0 goto execute
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 0 goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig
new file mode 100755
index 0000000..50f07bb
--- /dev/null
+++ b/iosApp/Configuration/Config.xcconfig
@@ -0,0 +1,3 @@
+TEAM_ID=
+BUNDLE_ID=com.github.springeye.memosc.memosc
+APP_NAME=memosc
\ No newline at end of file
diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj
new file mode 100755
index 0000000..66c59c0
--- /dev/null
+++ b/iosApp/iosApp.xcodeproj/project.pbxproj
@@ -0,0 +1,383 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 50;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; };
+		058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; };
+		2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; };
+		7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
+		058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
+		2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = "<group>"; };
+		7555FF7B242A565900829871 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; };
+		7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
+		7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXGroup section */
+		058557D7273AAEEB004C7B11 /* Preview Content */ = {
+			isa = PBXGroup;
+			children = (
+				058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */,
+			);
+			path = "Preview Content";
+			sourceTree = "<group>";
+		};
+		42799AB246E5F90AF97AA0EF /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		7555FF72242A565900829871 = {
+			isa = PBXGroup;
+			children = (
+				AB1DB47929225F7C00F7AF9C /* Configuration */,
+				7555FF7D242A565900829871 /* iosApp */,
+				7555FF7C242A565900829871 /* Products */,
+				42799AB246E5F90AF97AA0EF /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		7555FF7C242A565900829871 /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				7555FF7B242A565900829871 /* .app */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		7555FF7D242A565900829871 /* iosApp */ = {
+			isa = PBXGroup;
+			children = (
+				058557BA273AAA24004C7B11 /* Assets.xcassets */,
+				7555FF82242A565900829871 /* ContentView.swift */,
+				7555FF8C242A565B00829871 /* Info.plist */,
+				2152FB032600AC8F00CF470E /* iOSApp.swift */,
+				058557D7273AAEEB004C7B11 /* Preview Content */,
+			);
+			path = iosApp;
+			sourceTree = "<group>";
+		};
+		AB1DB47929225F7C00F7AF9C /* Configuration */ = {
+			isa = PBXGroup;
+			children = (
+				AB3632DC29227652001CCB65 /* Config.xcconfig */,
+			);
+			path = Configuration;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		7555FF7A242A565900829871 /* iosApp */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */;
+			buildPhases = (
+				F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */,
+				7555FF77242A565900829871 /* Sources */,
+				7555FF79242A565900829871 /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = iosApp;
+			productName = iosApp;
+			productReference = 7555FF7B242A565900829871 /* .app */;
+			productType = "com.apple.product-type.application";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		7555FF73242A565900829871 /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastSwiftUpdateCheck = 1130;
+				LastUpgradeCheck = 1130;
+				ORGANIZATIONNAME = orgName;
+				TargetAttributes = {
+					7555FF7A242A565900829871 = {
+						CreatedOnToolsVersion = 11.3.1;
+					};
+				};
+			};
+			buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */;
+			compatibilityVersion = "Xcode 9.3";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 7555FF72242A565900829871;
+			productRefGroup = 7555FF7C242A565900829871 /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				7555FF7A242A565900829871 /* iosApp */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		7555FF79242A565900829871 /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */,
+				058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputFileListPaths = (
+			);
+			inputPaths = (
+			);
+			name = "Compile Kotlin Framework";
+			outputFileListPaths = (
+			);
+			outputPaths = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n  echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n  exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		7555FF77242A565900829871 /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */,
+				7555FF83242A565900829871 /* ContentView.swift in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		7555FFA3242A565B00829871 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
+				MTL_FAST_MATH = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+			};
+			name = Debug;
+		};
+		7555FFA4242A565B00829871 /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_ENABLE_OBJC_WEAK = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				MTL_FAST_MATH = YES;
+				SDKROOT = iphoneos;
+				SWIFT_COMPILATION_MODE = wholemodule;
+				SWIFT_OPTIMIZATION_LEVEL = "-O";
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		7555FFA6242A565B00829871 /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "Apple Development";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+				DEVELOPMENT_TEAM = "${TEAM_ID}";
+				ENABLE_PREVIEWS = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+				);
+				INFOPLIST_FILE = iosApp/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				OTHER_LDFLAGS = (
+					"$(inherited)",
+					"-framework",
+					composeApp,
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
+				PRODUCT_NAME = "${APP_NAME}";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Debug;
+		};
+		7555FFA7242A565B00829871 /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+				CODE_SIGN_IDENTITY = "Apple Development";
+				CODE_SIGN_STYLE = Automatic;
+				DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\"";
+				DEVELOPMENT_TEAM = "${TEAM_ID}";
+				ENABLE_PREVIEWS = YES;
+				FRAMEWORK_SEARCH_PATHS = (
+					"$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)",
+				);
+				INFOPLIST_FILE = iosApp/Info.plist;
+				IPHONEOS_DEPLOYMENT_TARGET = 14.1;
+				LD_RUNPATH_SEARCH_PATHS = (
+					"$(inherited)",
+					"@executable_path/Frameworks",
+				);
+				OTHER_LDFLAGS = (
+					"$(inherited)",
+					"-framework",
+					composeApp,
+				);
+				PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}";
+				PRODUCT_NAME = "${APP_NAME}";
+				PROVISIONING_PROFILE_SPECIFIER = "";
+				SWIFT_VERSION = 5.0;
+				TARGETED_DEVICE_FAMILY = "1,2";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7555FFA3242A565B00829871 /* Debug */,
+				7555FFA4242A565B00829871 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				7555FFA6242A565B00829871 /* Debug */,
+				7555FFA7242A565B00829871 /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 7555FF73242A565900829871 /* Project object */;
+}
diff --git a/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000..18d9810
--- /dev/null
+++ b/iosApp/iosApp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>IDEDidComputeMac32BitWarning</key>
+	<true/>
+</dict>
+</plist>
diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
new file mode 100755
index 0000000..ee7e3ca
--- /dev/null
+++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json
@@ -0,0 +1,11 @@
+{
+  "colors" : [
+    {
+      "idiom" : "universal"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
\ No newline at end of file
diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100755
index 0000000..8edf56e
--- /dev/null
+++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,14 @@
+{
+  "images" : [
+    {
+      "filename" : "app-icon-1024.png",
+      "idiom" : "universal",
+      "platform" : "ios",
+      "size" : "1024x1024"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png
new file mode 100755
index 0000000000000000000000000000000000000000..53fc536fb9ac5c1dbb27c7e1da13db3760070a11
GIT binary patch
literal 67285
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
BXNCX(

literal 0
HcmV?d00001

diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json
new file mode 100755
index 0000000..4aa7c53
--- /dev/null
+++ b/iosApp/iosApp/Assets.xcassets/Contents.json
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
\ No newline at end of file
diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift
new file mode 100755
index 0000000..3cd5c32
--- /dev/null
+++ b/iosApp/iosApp/ContentView.swift
@@ -0,0 +1,21 @@
+import UIKit
+import SwiftUI
+import ComposeApp
+
+struct ComposeView: UIViewControllerRepresentable {
+    func makeUIViewController(context: Context) -> UIViewController {
+        MainViewControllerKt.MainViewController()
+    }
+
+    func updateUIViewController(_ uiViewController: UIViewController, context: Context) {}
+}
+
+struct ContentView: View {
+    var body: some View {
+        ComposeView()
+                .ignoresSafeArea(.keyboard) // Compose has own keyboard handler
+    }
+}
+
+
+
diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist
new file mode 100755
index 0000000..412e378
--- /dev/null
+++ b/iosApp/iosApp/Info.plist
@@ -0,0 +1,50 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+	<key>LSRequiresIPhoneOS</key>
+	<true/>
+	<key>CADisableMinimumFrameDurationOnPhone</key>
+	<true/>
+	<key>UIApplicationSceneManifest</key>
+	<dict>
+		<key>UIApplicationSupportsMultipleScenes</key>
+		<false/>
+	</dict>
+	<key>UILaunchScreen</key>
+	<dict/>
+	<key>UIRequiredDeviceCapabilities</key>
+	<array>
+		<string>armv7</string>
+	</array>
+	<key>UISupportedInterfaceOrientations</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+	<key>UISupportedInterfaceOrientations~ipad</key>
+	<array>
+		<string>UIInterfaceOrientationPortrait</string>
+		<string>UIInterfaceOrientationPortraitUpsideDown</string>
+		<string>UIInterfaceOrientationLandscapeLeft</string>
+		<string>UIInterfaceOrientationLandscapeRight</string>
+	</array>
+</dict>
+</plist>
diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json
new file mode 100755
index 0000000..4aa7c53
--- /dev/null
+++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json	
@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}
\ No newline at end of file
diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift
new file mode 100755
index 0000000..0648e86
--- /dev/null
+++ b/iosApp/iosApp/iOSApp.swift
@@ -0,0 +1,10 @@
+import SwiftUI
+
+@main
+struct iOSApp: App {
+	var body: some Scene {
+		WindowGroup {
+			ContentView()
+		}
+	}
+}
\ No newline at end of file
diff --git a/launcher/logo.ico b/launcher/logo.ico
new file mode 100644
index 0000000000000000000000000000000000000000..8c9100dfecf623599966d9121165756b282cda87
GIT binary patch
literal 4286
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

literal 0
HcmV?d00001

diff --git a/launcher/logo.png b/launcher/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..029df083aa269f149774d9c4b405472204210827
GIT binary patch
literal 57311
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

literal 0
HcmV?d00001

diff --git a/settings.gradle.kts b/settings.gradle.kts
new file mode 100755
index 0000000..2d074b5
--- /dev/null
+++ b/settings.gradle.kts
@@ -0,0 +1,25 @@
+rootProject.name = "memosc"
+enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
+
+pluginManagement {
+    repositories {
+//        maven("https://maven.aliyun.com/repository/public/")
+        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+        google()
+        gradlePluginPortal()
+        mavenCentral()
+    }
+}
+
+dependencyResolutionManagement {
+    repositories {
+
+        google()
+        mavenCentral()
+        maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
+
+
+    }
+}
+
+include(":composeApp")
\ No newline at end of file