├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ ├── colors.xml
│ │ │ │ ├── ic_launcher_background.xml
│ │ │ │ ├── themes.xml
│ │ │ │ └── strings.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.webp
│ │ │ │ └── ic_launcher_foreground.webp
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ └── ic_launcher.xml
│ │ │ ├── xml
│ │ │ │ ├── backup_rules.xml
│ │ │ │ └── data_extraction_rules.xml
│ │ │ ├── raw
│ │ │ │ ├── hide_stories.js
│ │ │ │ ├── pinch_to_zoom.js
│ │ │ │ ├── hide_groups.js
│ │ │ │ ├── hide_pymk.js
│ │ │ │ ├── hide_reels.js
│ │ │ │ ├── hide_suggested.js
│ │ │ │ ├── amoled_black.js
│ │ │ │ ├── adblock.js
│ │ │ │ ├── sticky_navbar.js
│ │ │ │ ├── scripts.js
│ │ │ │ └── download_content.js
│ │ │ ├── values-zh-rTW
│ │ │ │ └── strings.xml
│ │ │ ├── values-ar
│ │ │ │ └── strings.xml
│ │ │ ├── values-bn
│ │ │ │ └── strings.xml
│ │ │ ├── values-pt
│ │ │ │ └── strings.xml
│ │ │ ├── values-de
│ │ │ │ └── strings.xml
│ │ │ ├── values-es
│ │ │ │ └── strings.xml
│ │ │ └── values-fr
│ │ │ │ └── strings.xml
│ │ ├── ic_launcher-playstore.png
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── ycngmn
│ │ │ │ └── nobook
│ │ │ │ ├── ui
│ │ │ │ ├── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Type.kt
│ │ │ │ │ └── Theme.kt
│ │ │ │ ├── components
│ │ │ │ │ ├── NetworkErrorDialog.kt
│ │ │ │ │ └── settings
│ │ │ │ │ │ ├── SettingsDialog.kt
│ │ │ │ │ │ ├── SettingsItem.kt
│ │ │ │ │ │ └── SettingsContent.kt
│ │ │ │ └── screens
│ │ │ │ │ ├── SplashLoading.kt
│ │ │ │ │ └── NobookWV.kt
│ │ │ │ ├── utils
│ │ │ │ ├── desktopUserAgent.kt
│ │ │ │ ├── jsBridge
│ │ │ │ │ ├── NobookSettings.kt
│ │ │ │ │ ├── ThemeChange.kt
│ │ │ │ │ └── DownloadBridge.kt
│ │ │ │ ├── isAutoDesktop.kt
│ │ │ │ ├── ExternalRequestInterceptor.kt
│ │ │ │ ├── fetchScripts.kt
│ │ │ │ ├── fbRedirectSanitizer.kt
│ │ │ │ ├── rememberImeHeight.kt
│ │ │ │ └── FilePicker.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainNavigation.kt
│ │ │ │ ├── NobookDataStore.kt
│ │ │ │ └── NobookViewModel.kt
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── com
│ │ │ └── ycngmn
│ │ │ └── nobook
│ │ │ └── ExampleUnitTest.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── ycngmn
│ │ └── nobook
│ │ └── ExampleInstrumentedTest.kt
├── proguard-rules.pro
└── build.gradle.kts
├── images
├── get-it-on-github.png
└── nobook_github_cover.png
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
└── libs.versions.toml
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.yml
│ └── bug_report.yml
└── workflows
│ └── create-release.yml
├── .gitignore
├── settings.gradle.kts
├── gradle.properties
├── README.md
├── gradlew.bat
├── gradlew
└── LICENSE
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/images/get-it-on-github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/images/get-it-on-github.png
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/images/nobook_github_cover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/images/nobook_github_cover.png
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-playstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/ic_launcher-playstore.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ycngmn/Nobook/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.webp
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #09547A
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
3 | contact_links:
4 | - name: 💬 Ask a Question
5 | url: https://github.com/ycngmn/Nobook/discussions
6 | about: Ask general questions or get help
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val GoogleDark = Color(0xFF121212)
6 | val FacebookBlue = Color(0xFF1877F2)
7 | val FacebookDark = Color(0XFF232425)
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/desktopUserAgent.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils
2 |
3 | fun getDesktopUserAgent(): String {
4 | return "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0"
5 | }
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Apr 16 13:00:47 CEST 2025
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | distributionUrl=https\://services.gradle.org/distributions/gradle-9.2.0-bin.zip
5 | zipStoreBase=GRADLE_USER_HOME
6 | zipStorePath=wrapper/dists
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/jsBridge/NobookSettings.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils.jsBridge
2 |
3 | import android.webkit.JavascriptInterface
4 |
5 | class NobookSettings (
6 | private val toggleSettings: () -> Unit,
7 | ) {
8 | @JavascriptInterface
9 | fun onSettingsToggle() = toggleSettings()
10 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /app/release
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
17 | # Android build outputs
18 | /build/
19 | /app/build/
20 | *.apk
21 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ycngmn/nobook/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/jsBridge/ThemeChange.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils.jsBridge
2 |
3 | import android.webkit.JavascriptInterface
4 | import androidx.core.graphics.toColorInt
5 |
6 | class ThemeChange(
7 | private val setThemeColor: (Int) -> Unit
8 | ) {
9 | @JavascriptInterface
10 | fun onThemeColorChanged(newColor: String?) {
11 | if (newColor.isNullOrBlank()) return
12 | runCatching { setThemeColor(newColor.toColorInt()) }
13 | }
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/isAutoDesktop.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils
2 |
3 | import android.content.res.Configuration
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.ui.platform.LocalConfiguration
6 |
7 | @Composable
8 | fun isAutoDesktop(): Boolean {
9 | val configuration = LocalConfiguration.current
10 | return if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
11 | return true
12 | } else configuration.smallestScreenWidthDp >= 600
13 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/backup_rules.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook
2 |
3 | import android.os.Bundle
4 | import androidx.activity.ComponentActivity
5 | import androidx.activity.compose.setContent
6 | import androidx.activity.enableEdgeToEdge
7 | import androidx.core.view.WindowCompat
8 |
9 | class MainActivity : ComponentActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | WindowCompat.setDecorFitsSystemWindows(window, false)
12 | enableEdgeToEdge()
13 | super.onCreate(savedInstanceState)
14 | setContent { MainNavigation(intent?.data) }
15 | }
16 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/hide_stories.js:
--------------------------------------------------------------------------------
1 | // Hide "story" container from feed.
2 | (function() {
3 | const hideTargetElement = () => {
4 | const storyContainer = isDesktopMode() ? document.querySelector('.x193iq5w.xgmub6v.x1ceravr')
5 | : document.querySelector('[data-mcomponent="MContainer"][data-srat="43"]');
6 |
7 | if (storyContainer) storyContainer.style.display = 'none';
8 | };
9 |
10 | hideTargetElement();
11 |
12 | const observer = new MutationObserver(() => {
13 | hideTargetElement();
14 | });
15 |
16 | observer.observe(document.body, {
17 | childList: true,
18 | subtree: true
19 | });
20 | })();
21 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | google {
4 | content {
5 | includeGroupByRegex("com\\.android.*")
6 | includeGroupByRegex("com\\.google.*")
7 | includeGroupByRegex("androidx.*")
8 | }
9 | }
10 | mavenCentral()
11 | gradlePluginPortal()
12 | }
13 | }
14 | dependencyResolutionManagement {
15 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
16 | repositories {
17 | google()
18 | mavenCentral()
19 | }
20 | }
21 |
22 | rootProject.name = "Nobook"
23 | include(":app")
24 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/data_extraction_rules.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
12 |
13 |
19 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ycngmn/nobook/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("com.ycngmn.nobook", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -dontwarn org.slf4j.impl.StaticLoggerBinder
--------------------------------------------------------------------------------
/app/src/main/res/raw/pinch_to_zoom.js:
--------------------------------------------------------------------------------
1 | // Zoom Disable Script
2 | (function() {
3 | function applyViewportLock() {
4 | let viewport = document.querySelector('meta[name="viewport"]');
5 | if (!viewport) {
6 | viewport = document.createElement('meta');
7 | viewport.name = "viewport";
8 | document.head.appendChild(viewport);
9 | }
10 | // allow zooming on photos.
11 | if (!window.location.href.includes("facebook.com/photo.php"))
12 | viewport.content = "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no";
13 | }
14 |
15 | applyViewportLock();
16 |
17 | const observer = new MutationObserver(() => {
18 | applyViewportLock();
19 | });
20 |
21 | observer.observe(document.head || document.documentElement, {
22 | childList: true,
23 | subtree: true
24 | });
25 | })();
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/MainNavigation.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook
2 |
3 | import android.net.Uri
4 | import androidx.compose.runtime.Composable
5 | import androidx.compose.runtime.collectAsState
6 | import androidx.compose.runtime.remember
7 | import androidx.compose.ui.graphics.toArgb
8 | import androidx.core.graphics.ColorUtils
9 | import androidx.lifecycle.viewmodel.compose.viewModel
10 | import com.ycngmn.nobook.ui.screens.NobookWebView
11 | import com.ycngmn.nobook.ui.theme.NobookTheme
12 |
13 | @Composable
14 | fun MainNavigation(data: Uri?) {
15 |
16 | val viewModel: NobookViewModel = viewModel()
17 | val themeColor = viewModel.themeColor.collectAsState().value
18 | val isDarkTheme = remember(themeColor) {
19 | ColorUtils.calculateLuminance(
20 | themeColor.toArgb()
21 | ) < 0.5
22 | }
23 |
24 | NobookTheme(darkTheme = isDarkTheme) {
25 | NobookWebView(
26 | data?.toString() ?: "https://facebook.com/",
27 | viewModel = viewModel
28 | )
29 | }
30 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/ExternalRequestInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils
2 |
3 | import com.multiplatform.webview.request.RequestInterceptor
4 | import com.multiplatform.webview.request.WebRequest
5 | import com.multiplatform.webview.request.WebRequestInterceptResult
6 | import com.multiplatform.webview.web.WebViewNavigator
7 |
8 | class ExternalRequestInterceptor(
9 | private val handleExternalUrl: (String) -> Unit
10 | ) : RequestInterceptor {
11 |
12 | override fun onInterceptUrlRequest(
13 | request: WebRequest,
14 | navigator: WebViewNavigator
15 | ): WebRequestInterceptResult {
16 |
17 | val internalUrlRegex = Regex(
18 | """https?://(?!(?:l|lm)\.)[^/]*(?:facebook|messenger)\.com/.*"""
19 | )
20 | return if (internalUrlRegex.containsMatchIn(request.url) && request.isForMainFrame) {
21 | WebRequestInterceptResult.Allow
22 | } else {
23 | handleExternalUrl(fbRedirectSanitizer(request.url))
24 | WebRequestInterceptResult.Reject
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.ui.theme
2 |
3 | import androidx.compose.material3.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | bodyLarge = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp,
15 | lineHeight = 24.sp,
16 | letterSpacing = 0.5.sp
17 | )
18 | /* Other default text styles to override
19 | titleLarge = TextStyle(
20 | fontFamily = FontFamily.Default,
21 | fontWeight = FontWeight.Normal,
22 | fontSize = 22.sp,
23 | lineHeight = 28.sp,
24 | letterSpacing = 0.sp
25 | ),
26 | labelSmall = TextStyle(
27 | fontFamily = FontFamily.Default,
28 | fontWeight = FontWeight.Medium,
29 | fontSize = 11.sp,
30 | lineHeight = 16.sp,
31 | letterSpacing = 0.5.sp
32 | )
33 | */
34 | )
--------------------------------------------------------------------------------
/app/src/main/java/com/ycngmn/nobook/utils/fetchScripts.kt:
--------------------------------------------------------------------------------
1 | package com.ycngmn.nobook.utils
2 |
3 | import io.ktor.client.HttpClient
4 | import io.ktor.client.call.body
5 | import io.ktor.client.engine.okhttp.OkHttp
6 | import io.ktor.client.request.get
7 | import io.ktor.http.HttpStatusCode
8 |
9 |
10 | const val SCRIPT_SRC = "https://raw.githubusercontent.com/ycngmn/Nobook/refs/heads/main/app/src/main/res/raw/"
11 |
12 | data class Script(
13 | val condition: Boolean,
14 | val scriptRes: Int,
15 | val scriptTitle: String
16 | )
17 |
18 | suspend fun fetchScripts(
19 | scripts: List