chore: remove outdated markdown content

This commit is contained in:
infi 2026-06-06 06:55:44 +02:00
parent ac5c91c0d0
commit 1c88229d4e
19 changed files with 12 additions and 1070 deletions

View File

@ -1,16 +1,13 @@
{ {
"image": "mcr.microsoft.com/devcontainers/universal:2", "image": "mcr.microsoft.com/devcontainers/universal:2",
"features": { "features": {
"ghcr.io/devcontainers-community/features/deno:1": {}, "ghcr.io/nordcominc/devcontainer-features/android-sdk:1": {},
"ghcr.io/nordcominc/devcontainer-features/android-sdk:1": {
"extra_packages": "ndk;28.1.13356709"
},
"ghcr.io/devcontainers/features/java:1": { "ghcr.io/devcontainers/features/java:1": {
"version": "21", "version": "21",
"jdkDistro": "oracle" "jdkDistro": "oracle"
} }
}, },
"postCreateCommand": "chmod +x gradlew && deno run -A scripts/download_deps.ts --yes && cp revoltbuild.properties.example revoltbuild.properties && cp sentry.properties.example sentry.properties && cp app/google-services.json.example app/google-services.json && git submodule update --init --recursive", "postCreateCommand": "chmod +x gradlew && cp revoltbuild.properties.example revoltbuild.properties && cp sentry.properties.example sentry.properties && cp app/google-services.json.example app/google-services.json",
"customizations": { "customizations": {
"vscode": { "vscode": {
"extensions": [ "extensions": [

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "app/src/main/cpp/external/cmark"]
path = app/src/main/cpp/external/cmark
url = https://github.com/commonmark/cmark

View File

@ -70,13 +70,6 @@ android {
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true
} }
externalNativeBuild {
cmake {
arguments += listOf("-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES=ON")
cppFlags("")
}
}
} }
buildTypes { buildTypes {
@ -141,12 +134,6 @@ android {
androidResources { androidResources {
generateLocaleConfig = true generateLocaleConfig = true
} }
externalNativeBuild {
cmake {
path(file("src/main/cpp/CMakeLists.txt"))
version = "3.22.1"
}
}
lint { lint {
abortOnError = false abortOnError = false
disable += "MissingTranslation" disable += "MissingTranslation"
@ -299,7 +286,6 @@ aboutLibraries {
"BSD-2-Clause", "BSD-2-Clause",
"BSD-3-Clause", "The 3-Clause BSD License", "BSD-3-Clause", "The 3-Clause BSD License",
"BSD License", "BSD License",
"cmark",
"EPL-1.0", "EPL-1.0",
"MIT", "MIT",
"ML Kit Terms of Service", "ML Kit Terms of Service",

View File

@ -1,95 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
<style>
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_regular.ttf");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Inter Display";
src: url("/_android_res/font/inter_display_semibold.ttf");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_bold.ttf");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Fragment Mono";
src: url("/_android_res/font/fragmentmono_regular.ttf");
font-weight: 400;
font-style: normal;
}
body, html {
font-family: "Inter", sans-serif;
margin: 0;
padding: 0;
color: {content};
}
a:link, a:visited {
color: {primary};
text-decoration: none;
}
pre, code {
font-family: "Fragment Mono", monospace;
}
#markdown {
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
hyphens: auto;
max-width: 100vw;
overflow-x: hidden;
}
#markdown img {
max-width: 100%;
height: auto;
}
#markdown h2 {
font-size: 1.2em;
font-family: "Inter Display", sans-serif;
font-weight: 600;
}
#markdown p, #markdown li {
line-height: 1.5;
}
</style>
</head>
<body>
<div id="markdown"></div>
<script>
const renderMarkdown = () => {
markdown.innerHTML = Bridge.getMarkdown()
}
window.renderMarkdown = renderMarkdown
window.addEventListener("load", () => {
const style = document.querySelector("style")
style.innerHTML = style.innerHTML
.replace("{content}", Bridge.getContentColour())
.replace("{primary}", Bridge.getPrimaryColour())
renderMarkdown()
})
</script>
</body>
</html>

View File

@ -1,54 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
<style>
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_regular.ttf");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_bold.ttf");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Fragment Mono";
src: url("/_android_res/font/fragmentmono_regular.ttf");
font-weight: 400;
font-style: normal;
}
body, html {
font-family: "Inter", sans-serif;
margin: 0;
padding: 0;
}
pre, code {
font-family: "Fragment Mono", monospace;
}
</style>
<script src="/_android_assets/embedded/katex.min.js"></script>
<link rel="stylesheet" href="/_android_assets/embedded/katex.min.css">
</head>
<body>
<div id="katex"></div>
<script>
window.addEventListener("load", () => {
katex.render(Bridge.getSource(), document.querySelector("#katex"), {
throwOnError: false,
maxExpand: 50
})
document.body.style.color = Bridge.getForegroundColour()
})
</script>
</body>
</html>

View File

@ -1,206 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title></title>
<style>
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_regular.ttf");
font-weight: 400;
font-style: normal;
}
@font-face {
font-family: "Inter Display";
src: url("/_android_res/font/inter_display_semibold.ttf");
font-weight: 600;
font-style: normal;
}
@font-face {
font-family: "Inter";
src: url("/_android_res/font/inter_bold.ttf");
font-weight: 700;
font-style: normal;
}
@font-face {
font-family: "Fragment Mono";
src: url("/_android_res/font/fragmentmono_regular.ttf");
font-weight: 400;
font-style: normal;
}
body, html {
font-family: "Inter", sans-serif;
}
/* No margins anywhere we don't want them */
body, html, div, table {
margin: 0;
padding: 0;
}
a:link, a:visited {
color: {primary};
text-decoration: none;
}
pre, code {
font-family: "Fragment Mono", monospace;
}
#markdown {
overflow-wrap: break-word;
word-wrap: break-word;
word-break: break-word;
hyphens: auto;
max-width: 100vw;
overflow-x: hidden;
}
#markdown img {
max-width: 100%;
height: auto;
}
#markdown h2 {
font-size: 1.2em;
font-family: "Inter Display", sans-serif;
font-weight: 600;
}
#markdown p, #markdown li {
line-height: 1.5;
}
#markdown table {
border-collapse: collapse;
width: 100%;
margin: 1em 0;
}
#markdown table, #markdown th, #markdown td {
border: 1px solid #000;
}
#markdown th, #markdown td {
padding: 0.5em;
}
img.user-avatar {
width: calc(1.5em * 0.9);
height: calc(1.5em * 0.9);
vertical-align: middle;
border-radius: 9999px;
margin-right: 0.5em;
}
</style>
</head>
<body>
<script defer type="module">
import {micromark} from "/_android_assets/embedded/micromark.bundle.js"
import {gfmHtml, gfm} from "/_android_assets/embedded/micromark-gfm.bundle.js"
window.addEventListener("load", () => {
document.body.style.color = Bridge.getForegroundColour()
const dynStyles = `
#markdown a {
color: ${Bridge.getPrimaryColour()};
}
#markdown table {
border-color: ${Bridge.getForegroundColour()};
}
#markdown th, #markdown td {
border-color: ${Bridge.getForegroundColour()};
}
a.mention {
color: ${Bridge.getPrimaryColour()};
background-color: ${Bridge.getMentionBackgroundColour()};
padding: 0.25em;
}
`
const style = document.createElement("style")
style.innerHTML = dynStyles
document.head.appendChild(style)
const htmlSource = micromark(Bridge.getSource(), {
extensions: [gfm()],
htmlExtensions: [gfmHtml()]
})
const divWithMarkdown = document.createElement("div")
divWithMarkdown.id = "markdown"
divWithMarkdown.innerHTML = htmlSource
// Remove all image tags, as they are not supported
divWithMarkdown.querySelectorAll("img").forEach(img => img.remove())
// For all <a> tags, rewrite any that include the private _android_assets and _android_res
// directories
divWithMarkdown.querySelectorAll("a").forEach(a => {
if (a.href.includes("_android_assets") || a.href.includes("_android_res")) {
a.href = "#"
}
})
const ULID_REGEXP = /[0-9A-HJKMNP-TV-Z]{26}/g
// Custom emote regex, a ULID wrapped in colons
const CUSTOM_EMOTE_REGEXP = new RegExp(`:${ULID_REGEXP.source}:`, "g")
// User mention regex, a ULID wrapped in angle brackets with a @ before the ULID
const USER_MENTION_REGEXP = new RegExp(`<@${ULID_REGEXP.source}>`, "g")
// Channel mention regex, a ULID wrapped in angle brackets with a # before the ULID
const CHANNEL_MENTION_REGEXP = new RegExp(`<#${ULID_REGEXP.source}>`, "g")
// For anything that contains text, replace the custom emote and user/channel mentions
// First, replace the custom emotes
divWithMarkdown.querySelectorAll("p, li, h1, h2, h3, h4, h5, h6, th, td").forEach(element => {
element.innerHTML = element.innerHTML
.replace(CUSTOM_EMOTE_REGEXP, match => {
const ulid = match.slice(1, -1)
const img = document.createElement("img")
img.src = Bridge.getCustomEmoteUrl(ulid)
img.style.verticalAlign = "middle"
img.style.width = "1.5em"
img.style.height = "1.5em"
// Screen readers must ignore the image as we cannot reliably provide a name
img.alt = ""
img.setAttribute("aria-hidden", "true")
return img.outerHTML
})
})
// Next we replace the user mentions. Micromark thinks they are emails so we search
// for <a> tags that lead to @<ULID>
divWithMarkdown.querySelectorAll("a").forEach(a => {
if (a.href.startsWith("mailto:")) {
const remainingIsUlid = a.href.slice(7).match(ULID_REGEXP)
if (remainingIsUlid) {
const ulid = remainingIsUlid[0]
// First we empty the <a> tag
a.href = "#"
a.innerHTML = ""
// Then we add an image with the user's avatar
const img = document.createElement("img")
img.src = Bridge.userAvatar(ulid)
img.classList.add("user-avatar")
a.appendChild(img)
// Finally we put the user's name
const textNode = document.createTextNode(Bridge.resolveUserMention(ulid))
a.appendChild(textNode)
// We also add a class to the <a> tag so we can style it
a.classList.add("mention")
}
}
})
document.body.appendChild(divWithMarkdown)
})
</script>
</body>
</html>

View File

@ -1,43 +0,0 @@
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html.
# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
# Since this is the top level CMakeLists.txt, the project name is also accessible
# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
# build script scope).
project("stoat")
# Compile cmark in external/cmark
add_subdirectory(external/cmark)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
#
# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
# is preferred for the same purpose.
#
# In order to load a library into your app from Java/Kotlin, you must call
# System.loadLibrary() and pass the name of the library defined here;
# for GameActivity/NativeActivity derived applications, the same library name must be
# used in the AndroidManifest.xml file.
add_library(stendal SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
stendal/stendal.cpp
)
# Specifies libraries CMake should link to your target library. You
# can link libraries from various origins, such as libraries defined in this
# build script, prebuilt third-party libraries, or Android system libraries.
target_link_libraries(stendal
# List libraries link to the target library
cmark
android
log)

View File

@ -1 +0,0 @@
Subproject commit 3337a30715a641274a5a14aa167d6e51ba4066c0

View File

@ -1,162 +0,0 @@
#include <android/log.h>
#include <jni.h>
#include <string>
#include <vector>
#include <cmark.h>
#define STENDAL_ASTNODE_CONSTRUCTOR_SIGNATURE "(ILjava/lang/String;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Integer;)V"
namespace Stendal {
jclass arrayListClass = nullptr;
jmethodID constructArrayListMethod = nullptr;
jmethodID addArrayListMethod = nullptr;
jclass astNodeClass = nullptr;
jclass integerWrapperClass = nullptr;
jmethodID astNodeConstructor = nullptr;
jmethodID integerWrapperConstructor = nullptr;
jclass booleanWrapperClass = nullptr;
jmethodID booleanWrapperConstructor = nullptr;
inline bool string_starts_with(std::string const &value, std::string const &prefix) {
return value.rfind(prefix, 0) == 0;
}
inline bool string_ends_with(std::string const &value, std::string const &suffix) {
if (suffix.size() > value.size()) return false;
return std::equal(suffix.rbegin(), suffix.rend(), value.rbegin());
}
void init(JNIEnv *env) {
jclass localArrayListClass = env->FindClass("java/util/ArrayList");
arrayListClass = (jclass) env->NewGlobalRef(localArrayListClass);
constructArrayListMethod = env->GetMethodID(localArrayListClass, "<init>", "(I)V");
addArrayListMethod = env->GetMethodID(localArrayListClass, "add", "(Ljava/lang/Object;)Z");
jclass localAstNodeClass = env->FindClass("chat/stoat/ndk/AstNode");
astNodeClass = (jclass) env->NewGlobalRef(localAstNodeClass);
astNodeConstructor = env->GetMethodID(localAstNodeClass, "<init>",
STENDAL_ASTNODE_CONSTRUCTOR_SIGNATURE);
jclass localIntegerWrapperClass = env->FindClass("java/lang/Integer");
integerWrapperClass = (jclass) env->NewGlobalRef(localIntegerWrapperClass);
integerWrapperConstructor = env->GetMethodID(localIntegerWrapperClass, "<init>", "(I)V");
jclass localBooleanWrapperClass = env->FindClass("java/lang/Boolean");
booleanWrapperClass = (jclass) env->NewGlobalRef(localBooleanWrapperClass);
booleanWrapperConstructor = env->GetMethodID(localBooleanWrapperClass, "<init>", "(Z)V");
}
jobject node_instance(JNIEnv *env, cmark_node *node, jobject children) {
jstring typeStr = env->NewStringUTF(cmark_node_get_type_string(node));
jstring literalStr = env->NewStringUTF(cmark_node_get_literal(node));
jstring actionStr = env->NewStringUTF(cmark_node_get_url(node));
jobject headingLevelIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_heading_level(
node));
jobject listTypeIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_list_type(node));
jobject delimiterTypeIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_list_delim(
node));
jobject startIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_list_start(node));
jobject tightBoln = env->NewObject(Stendal::booleanWrapperClass,
Stendal::booleanWrapperConstructor,
(bool) cmark_node_get_list_tight(node));
jstring fenceStr = env->NewStringUTF(cmark_node_get_fence_info(node));
jstring titleStr = env->NewStringUTF(cmark_node_get_title(node));
jstring onEnterStr = env->NewStringUTF(cmark_node_get_on_enter(node));
jstring onExitStr = env->NewStringUTF(cmark_node_get_on_exit(node));
jobject startLineIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_start_line(node));
jobject endLineIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_end_line(node));
jobject startColumnIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_start_column(
node));
jobject endColumnIntg = env->NewObject(Stendal::integerWrapperClass,
Stendal::integerWrapperConstructor,
(int) cmark_node_get_end_column(node));
jobject inst = env->NewObject(Stendal::astNodeClass, Stendal::astNodeConstructor,
(int) cmark_node_get_type(node),
typeStr, children,
literalStr, actionStr, headingLevelIntg,
listTypeIntg, delimiterTypeIntg, startIntg, tightBoln,
fenceStr, titleStr, onEnterStr, onExitStr, startLineIntg,
endLineIntg,
startColumnIntg, endColumnIntg);
env->DeleteLocalRef(typeStr);
env->DeleteLocalRef(literalStr);
env->DeleteLocalRef(actionStr);
env->DeleteLocalRef(headingLevelIntg);
env->DeleteLocalRef(listTypeIntg);
env->DeleteLocalRef(delimiterTypeIntg);
env->DeleteLocalRef(startIntg);
env->DeleteLocalRef(tightBoln);
env->DeleteLocalRef(fenceStr);
env->DeleteLocalRef(titleStr);
env->DeleteLocalRef(onEnterStr);
env->DeleteLocalRef(onExitStr);
env->DeleteLocalRef(startLineIntg);
env->DeleteLocalRef(endLineIntg);
env->DeleteLocalRef(startColumnIntg);
env->DeleteLocalRef(endColumnIntg);
return inst;
}
jobject collect_nodes(JNIEnv *env, cmark_node *doc) {
std::vector<std::pair<cmark_node *, jobject>> children;
{
cmark_node *child = cmark_node_first_child(doc);
while (child) {
children.push_back(std::pair(child, Stendal::collect_nodes(env, child)));
child = cmark_node_next(child);
}
}
jobject list = env->NewObject(Stendal::arrayListClass, Stendal::constructArrayListMethod,
(int) children.size());
for (auto child: children) {
jobject inst = Stendal::node_instance(env, child.first, child.second);
env->DeleteLocalRef(child.second);
env->CallBooleanMethod(list, Stendal::addArrayListMethod, inst);
env->DeleteLocalRef(inst);
}
return list;
}
}
extern "C"
JNIEXPORT void JNICALL
Java_chat_stoat_ndk_Stendal_init(JNIEnv *env, [[maybe_unused]] jobject thiz) {
Stendal::init(env);
}
extern "C" JNIEXPORT jobject JNICALL
Java_chat_stoat_ndk_Stendal_render(JNIEnv *env, [[maybe_unused]] jobject thiz, jstring input) {
const char *inputStr = env->GetStringUTFChars(input, nullptr);
cmark_node *doc = cmark_parse_document(inputStr, strlen(inputStr),
CMARK_OPT_DEFAULT | CMARK_OPT_HARDBREAKS |
CMARK_OPT_VALIDATE_UTF8);
jobject nodes = Stendal::collect_nodes(env, doc);
jobject inst = Stendal::node_instance(env, doc, nodes);
cmark_node_free(doc);
env->ReleaseStringUTFChars(input, inputStr);
return inst;
}

View File

@ -90,7 +90,6 @@ import chat.stoat.composables.voice.VoicePermissionSwitch
import chat.stoat.composables.voice.VoiceSheet import chat.stoat.composables.voice.VoiceSheet
import chat.stoat.core.model.schemas.HealthNotice import chat.stoat.core.model.schemas.HealthNotice
import chat.stoat.material.EasingTokens import chat.stoat.material.EasingTokens
import chat.stoat.ndk.NativeLibraries
import chat.stoat.persistence.KVStorage import chat.stoat.persistence.KVStorage
import chat.stoat.screens.DefaultDestinationScreen import chat.stoat.screens.DefaultDestinationScreen
import chat.stoat.screens.about.AboutScreen import chat.stoat.screens.about.AboutScreen
@ -422,11 +421,6 @@ class MainActivity : AppCompatActivity() {
data?.add(messaging) data?.add(messaging)
} }
companion object {
init {
NativeLibraries.init()
}
}
} }
val StoatTweenInt: FiniteAnimationSpec<IntOffset> = tween(400, easing = EaseInOutExpo) val StoatTweenInt: FiniteAnimationSpec<IntOffset> = tween(400, easing = EaseInOutExpo)

View File

@ -1,14 +0,0 @@
package chat.stoat.ndk
annotation class NativeLibrary(val name: String) {
companion object {
const val LIB_NAME_NATIVE_MARKDOWN = "stendal"
}
}
object NativeLibraries {
fun init() {
System.loadLibrary(NativeLibrary.LIB_NAME_NATIVE_MARKDOWN)
Stendal.init()
}
}

View File

@ -1,42 +0,0 @@
package chat.stoat.ndk
import kotlinx.serialization.Serializable
object AstNodeListType {
const val NONE = 0
const val BULLET = 1
const val ORDERED = 2
}
object AstNodeDelimiterType {
const val NONE = 0
const val PERIOD = 1
const val PARENTHESIS = 2
}
@Serializable
data class AstNode(
val type: Int,
val stringType: String,
val children: List<AstNode>?,
val text: String?,
val url: String?,
val level: Int?,
val listType: Int?,
val delimiterType: Int?,
val listStart: Int?,
val listTight: Boolean?,
val fence: String?,
val title: String?,
val onEnter: String?,
val onExit: String?,
val startLine: Int?,
val endLine: Int?,
val startColumn: Int?,
val endColumn: Int?,
)
object Stendal {
external fun init()
external fun render(input: String): AstNode
}

View File

@ -1,11 +0,0 @@
{
"uniqueId": "org.commonmark.cmark:cmark:0.0.0",
"developers": [],
"artifactVersion": "0.0.0",
"description": "CommonMark parsing and rendering library and program in C",
"name": "cmark",
"website": "https://github.com/commonmark/cmark",
"licenses": [
"cmark"
]
}

View File

@ -1,20 +0,0 @@
{
"uniqueId": "org.katex:katex:0.0.0",
"developers": [
{
"name": "Khan Academy",
"organisationUrl": "https://www.khanacademy.org/"
},
{
"name": "The Katex Team",
"organisationUrl": "https://katex.org/"
}
],
"artifactVersion": "0.0.0",
"description": "KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the web.",
"name": "KaTeX",
"website": "https://katex.org/",
"licenses": [
"MIT"
]
}

View File

@ -1,20 +0,0 @@
{
"uniqueId": "com.unifiedjs.micromark:micromark:0.0.0",
"developers": [
{
"name": "Unified",
"organisationUrl": "https://unifiedjs.com/"
},
{
"name": "The micromark Contributors",
"organisationUrl": "https://github.com/micromark/micromark/graphs/contributors"
}
],
"artifactVersion": "0.0.0",
"description": "small, safe, and great commonmark (optionally gfm) compliant markdown parser",
"name": "micromark",
"website": "https://github.com/micromark/micromark",
"licenses": [
"MIT"
]
}

File diff suppressed because one or more lines are too long

View File

@ -11,7 +11,7 @@ If you want to compile the app yourself, you can follow these guidelines. You ma
:::danger :::danger
It may be tempting to skip some of these steps, but make sure you follow them to ensure that your development environment is set up correctly. It may be tempting to skip some of these steps, but make sure you follow them to ensure that your development environment is set up correctly.
Note: If you are doing this in Github Codespaces, steps 1-10 will be done for you. Also note that while small codespace instances can build this app, it's recommend to use 8-core or above for best results. Note: If you are doing this in Github Codespaces, steps 1-9 will be done for you. Also note that while small codespace instances can build this app, it's recommend to use 8-core or above for best results.
::: :::
import { Tabs, TabItem, Steps } from "@astrojs/starlight/components" import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
@ -80,47 +80,21 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
</Tabs> </Tabs>
3. Download [Deno](https://deno.com). 3. Clone the repository.
_Please see the [Deno Runtime Manual](https://docs.deno.com/runtime/getting_started/installation/) for installation guidance._
4. Clone the repository.
```sh ```sh
git clone --recursive https://github.com/stoatchat/for-android.git git clone https://github.com/stoatchat/for-android.git
``` ```
Specify `--recursive` to ensure that submodules are cloned as well. 4. Open the project in Android Studio.
5. Open the project in Android Studio. 5. Install the required dependencies.
6. Install the required dependencies.
- Android SDK, latest version - Android SDK, latest version
- Android NDK, latest version
You can install these from the SDK Manager in Android Studio. You can install these from the SDK Manager in Android Studio.
7. Download required additional, embedded dependencies using the provided script. 6. Copy the `stoatbuild.properties.example` file to `stoatbuild.properties` and fill in the required values.
```sh
deno run -A scripts/download_deps.ts
```
:::tip
You can run this script with the `-y` flag to skip confirmation
prompts.
:::
:::note
This script will query the Stoat version control server for the latest version of the
final-markdown native library and download it. If you do not wish to use pre-built libraries,
you can build final-markdown yourself from the source code at https://git.revolt.chat/android/final-markdown,
and copy the files to the `app/src/main/jniLibs` folder. Note the files that are downloaded are
exactly the same as the ones we use for the app on Google Play, so you can be sure they are safe to use.
:::
8. Copy the `stoatbuild.properties.example` file to `stoatbuild.properties` and fill in the required values.
```sh ```sh
cp stoatbuild.properties.example stoatbuild.properties cp stoatbuild.properties.example stoatbuild.properties
@ -135,7 +109,7 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
| `build.debug.app_name` | The name of the app in debug builds, arbitrary | | `build.debug.app_name` | The name of the app in debug builds, arbitrary |
| `build.flavour_id` | Leave as `ZZUU` | | `build.flavour_id` | Leave as `ZZUU` |
9. Copy the `sentry.properties.example` file to `sentry.properties` and fill in the required values. 7. Copy the `sentry.properties.example` file to `sentry.properties` and fill in the required values.
```sh ```sh
cp sentry.properties.example sentry.properties cp sentry.properties.example sentry.properties
@ -152,7 +126,7 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
You can get these values from the Sentry dashboard. You can get these values from the Sentry dashboard.
10. Copy the `google-services.json.example` file within the `app` directory to `google-services.json`. 8. Copy the `google-services.json.example` file within the `app` directory to `google-services.json`.
Firebase services are integrated into the project, so we need a `google-services.json` file for the build to succeed. For development purposes, use the provided example file: Firebase services are integrated into the project, so we need a `google-services.json` file for the build to succeed. For development purposes, use the provided example file:
@ -164,7 +138,7 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
This is a mock configuration file for development purposes only. In a production environment, you would use a real Firebase project configuration. This is a mock configuration file for development purposes only. In a production environment, you would use a real Firebase project configuration.
::: :::
11. Build the project. 9. Build the project.
You can build the project by clicking on the 'Run' button in Android Studio. You can build the project by clicking on the 'Run' button in Android Studio.
If asked, build the `:app` module. If asked, build the `:app` module.
@ -172,6 +146,6 @@ import { Tabs, TabItem, Steps } from "@astrojs/starlight/components"
If building within Github Codespaces, you can build a fresh debug apk by running `./gradlew assembledebug --no-daemon` from the root of the project. If building within Github Codespaces, you can build a fresh debug apk by running `./gradlew assembledebug --no-daemon` from the root of the project.
Upon completion, the apk will be within `app/build/outputs/apk/debug/` Upon completion, the apk will be within `app/build/outputs/apk/debug/`
12. **You're all set!** You can now start contributing to Stoat for Android. 10. **You're all set!** You can now start contributing to Stoat for Android.
</Steps> </Steps>

View File

@ -1,332 +0,0 @@
import { resolve } from "jsr:@std/path"
import { parseArgs } from "jsr:@std/cli/parse-args"
const args = parseArgs(Deno.args, {
boolean: ["yes", "y", "auto-accept"],
alias: {
"y": "yes"
}
})
const autoAccept = args.yes || args["auto-accept"]
const outputFolderParent = resolve(Deno.cwd(), "app", "src", "main", "assets")
try {
Deno.statSync(outputFolderParent)
} catch (_) {
console.error(
"\x1b[31m" + // red
"Did you run this script from the correct directory?" +
"\x1b[0m"
)
console.error(
"Usage: " +
"\x1b[35m" + // magenta
"deno run -A scripts/download_deps.ts [--yes|-y|--auto-accept]" +
"\x1b[0m" +
" from the " +
"\x1b[1;31;4m" + // bold red underline
"root" +
"\x1b[0m" +
" directory of the project."
)
Deno.exit(1)
}
const outputFolder = resolve(outputFolderParent, "embedded")
// If it exists, delete it
try {
Deno.removeSync(outputFolder, { recursive: true })
} catch (_) {
// Ignore, might not exist
}
// Create the output folder
Deno.mkdirSync(outputFolder, { recursive: true })
const deps = [
{
file: "katex.min.css",
url: "https://cdn.jsdelivr.net/npm/katex@0.16.19/dist/katex.min.css",
},
{
file: "katex.min.js",
url: "https://cdn.jsdelivr.net/npm/katex@0.16.19/dist/katex.min.js",
},
{
file: "fonts/KaTeX_AMS-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_AMS-Regular.ttf",
},
{
file: "fonts/KaTeX_AMS-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_AMS-Regular.woff",
},
{
file: "fonts/KaTeX_AMS-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_AMS-Regular.woff2",
},
{
file: "fonts/KaTeX_Caligraphic-Bold.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Bold.ttf",
},
{
file: "fonts/KaTeX_Caligraphic-Bold.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Bold.woff",
},
{
file: "fonts/KaTeX_Caligraphic-Bold.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Bold.woff2",
},
{
file: "fonts/KaTeX_Caligraphic-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Regular.ttf",
},
{
file: "fonts/KaTeX_Caligraphic-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Regular.woff",
},
{
file: "fonts/KaTeX_Caligraphic-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Caligraphic-Regular.woff2",
},
{
file: "fonts/KaTeX_Fraktur-Bold.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Bold.ttf",
},
{
file: "fonts/KaTeX_Fraktur-Bold.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Bold.woff",
},
{
file: "fonts/KaTeX_Fraktur-Bold.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Bold.woff2",
},
{
file: "fonts/KaTeX_Fraktur-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Regular.ttf",
},
{
file: "fonts/KaTeX_Fraktur-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Regular.woff",
},
{
file: "fonts/KaTeX_Fraktur-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Fraktur-Regular.woff2",
},
{
file: "fonts/KaTeX_Main-Bold.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Bold.ttf",
},
{
file: "fonts/KaTeX_Main-Bold.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Bold.woff",
},
{
file: "fonts/KaTeX_Main-Bold.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Bold.woff2",
},
{
file: "fonts/KaTeX_Main-BoldItalic.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-BoldItalic.ttf",
},
{
file: "fonts/KaTeX_Main-BoldItalic.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-BoldItalic.woff",
},
{
file: "fonts/KaTeX_Main-BoldItalic.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-BoldItalic.woff2",
},
{
file: "fonts/KaTeX_Main-Italic.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Italic.ttf",
},
{
file: "fonts/KaTeX_Main-Italic.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Italic.woff",
},
{
file: "fonts/KaTeX_Main-Italic.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Italic.woff2",
},
{
file: "fonts/KaTeX_Main-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Regular.ttf",
},
{
file: "fonts/KaTeX_Main-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Regular.woff",
},
{
file: "fonts/KaTeX_Main-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Main-Regular.woff2",
},
{
file: "fonts/KaTeX_Math-BoldItalic.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-BoldItalic.ttf",
},
{
file: "fonts/KaTeX_Math-BoldItalic.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-BoldItalic.woff",
},
{
file: "fonts/KaTeX_Math-BoldItalic.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-BoldItalic.woff2",
},
{
file: "fonts/KaTeX_Math-Italic.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-Italic.ttf",
},
{
file: "fonts/KaTeX_Math-Italic.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-Italic.woff",
},
{
file: "fonts/KaTeX_Math-Italic.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Math-Italic.woff2",
},
{
file: "fonts/KaTeX_SansSerif-Bold.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Bold.ttf",
},
{
file: "fonts/KaTeX_SansSerif-Bold.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Bold.woff",
},
{
file: "fonts/KaTeX_SansSerif-Bold.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Bold.woff2",
},
{
file: "fonts/KaTeX_SansSerif-Italic.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Italic.ttf",
},
{
file: "fonts/KaTeX_SansSerif-Italic.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Italic.woff",
},
{
file: "fonts/KaTeX_SansSerif-Italic.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Italic.woff2",
},
{
file: "fonts/KaTeX_SansSerif-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Regular.ttf",
},
{
file: "fonts/KaTeX_SansSerif-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Regular.woff",
},
{
file: "fonts/KaTeX_SansSerif-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_SansSerif-Regular.woff2",
},
{
file: "fonts/KaTeX_Script-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Script-Regular.ttf",
},
{
file: "fonts/KaTeX_Script-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Script-Regular.woff",
},
{
file: "fonts/KaTeX_Script-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Script-Regular.woff2",
},
{
file: "fonts/KaTeX_Size1-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size1-Regular.ttf",
},
{
file: "fonts/KaTeX_Size1-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size1-Regular.woff",
},
{
file: "fonts/KaTeX_Size1-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size1-Regular.woff2",
},
{
file: "fonts/KaTeX_Size2-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size2-Regular.ttf",
},
{
file: "fonts/KaTeX_Size2-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size2-Regular.woff",
},
{
file: "fonts/KaTeX_Size2-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size2-Regular.woff2",
},
{
file: "fonts/KaTeX_Size3-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size3-Regular.ttf",
},
{
file: "fonts/KaTeX_Size3-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size3-Regular.woff",
},
{
file: "fonts/KaTeX_Size3-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size3-Regular.woff2",
},
{
file: "fonts/KaTeX_Size4-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size4-Regular.ttf",
},
{
file: "fonts/KaTeX_Size4-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size4-Regular.woff",
},
{
file: "fonts/KaTeX_Size4-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Size4-Regular.woff2",
},
{
file: "fonts/KaTeX_Typewriter-Regular.ttf",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Typewriter-Regular.ttf",
},
{
file: "fonts/KaTeX_Typewriter-Regular.woff",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Typewriter-Regular.woff",
},
{
file: "fonts/KaTeX_Typewriter-Regular.woff2",
url: "https://cdn.jsdelivr.net/npm/katex@0.11.1/dist/fonts/KaTeX_Typewriter-Regular.woff2",
},
{
file: "micromark.bundle.js",
url: "https://esm.sh/v135/micromark@3.2.0/es2022/micromark.bundle.mjs",
},
{
file: "micromark-gfm.bundle.js",
url: "https://esm.sh/v135/micromark-extension-gfm@3.0.0/es2022/micromark-extension-gfm.bundle.mjs",
}
]
for (const dep of deps) {
console.log(`- ${dep.file} from ${dep.url}`)
}
if (!autoAccept) {
console.log("Will download the above files.")
if (!confirm("Continue?")) {
console.log("Aborted.")
Deno.exit(0)
}
} else {
console.log("Will download the above files. (auto-accepted)")
}
const fontsFolder = resolve(outputFolder, "fonts")
Deno.mkdirSync(fontsFolder, { recursive: true })
for (const dep of deps) {
const response = await fetch(dep.url)
const data = await response.arrayBuffer()
const file = resolve(outputFolder, dep.file)
Deno.writeFileSync(file, new Uint8Array(data))
console.log(`Downloaded ${dep.file} to ${file}`)
}