From df1219c7be0df0f6b068f6aad82d641844ee95b6 Mon Sep 17 00:00:00 2001
From: Janne Mareike Koschinski <janne@kuschku.de>
Date: Fri, 4 Mar 2022 03:04:13 +0100
Subject: [PATCH] feat: extract irc format deserializer to libquassel

---
 .gitlab-ci.yml                                |   63 +-
 app/build.gradle.kts                          |    3 +-
 app/proguard-rules.pro                        |    4 +-
 .../sample/SampleMessageProvider.kt           |    2 +-
 .../quasseldroid/ui/components/MessageBase.kt |   19 +-
 .../quasseldroid/ui/components/MessageList.kt |   12 +-
 .../util/extensions/SequenceExtensions.kt     |   21 -
 .../quasseldroid/util/format/IrcFormat.kt     |   59 -
 .../util/format/IrcFormatDeserializer.kt      |  161 --
 .../util/format/IrcFormatRenderer.kt          |   13 +-
 .../util/irc/SenderColorUtilTest.kt           |    2 +-
 .../util/irc/TextFormatterTest.kt             |    2 +-
 .../util/irc/IrcFormatDeserializerTest.kt     | 1593 -----------------
 gradle/libs.versions.toml                     |    3 +-
 14 files changed, 65 insertions(+), 1892 deletions(-)
 delete mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/util/extensions/SequenceExtensions.kt
 delete mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormat.kt
 delete mode 100644 app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatDeserializer.kt
 rename app/src/test/kotlin/de/{kuschku => }/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt (88%)
 rename app/src/test/kotlin/de/{kuschku => }/justjanne/quasseldroid/util/irc/TextFormatterTest.kt (98%)
 delete mode 100644 app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/IrcFormatDeserializerTest.kt

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index b6f90900d..9dd3cd351 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -14,7 +14,7 @@ before_script:
 
 stages:
   - "build"
-  - "deploy"
+#  - "deploy"
 
 test:
   stage: "build"
@@ -48,33 +48,34 @@ version:
     - if: "$CI_COMMIT_BRANCH == 'main'"
       when: on_success
 
-deploy-local:
-  stage: "deploy"
-  image: "k8r.eu/justjanne/docker-s3cmd:latest"
-  cache: { }
-  dependencies:
-    - "test"
-    - "version"
-  script:
-    - "echo $S3_CONFIG | base64 -d > $HOME/.s3cfg"
-    - "export VERSION=$(ls *.apk)"
-    - "s3cmd put $VERSION s3://releases/quasseldroid-ng/$VERSION"
-    - "s3cmd put version.json s3://releases/quasseldroid-ng/version.json"
-    - "s3cmd cp s3://releases/quasseldroid-ng/$VERSION s3://releases/quasseldroid-ng/Quasseldroid-latest.apk"
-  rules:
-    - if: "$CI_COMMIT_BRANCH == 'main' && $S3_CONFIG != ''"
-      when: on_success
-
-deploy-beta:
-  stage: "deploy"
-  image: "k8r.eu/justjanne/docker-fastlane:latest"
-  cache: { }
-  dependencies:
-    - "test"
-  script:
-    - "echo $FASTLANE_CONFIG | base64 -d > $HOME/key.json"
-    - "export VERSION=$(ls *.apk)"
-    - "fastlane supply --apk $VERSION --track beta --json_key $HOME/key.json --package_name com.iskrembilen.quasseldroid --skip_upload_metadata=true --skip_upload_images=true --skip_upload_screenshots=true"
-  rules:
-    - if: "$CI_COMMIT_BRANCH == 'main' && $FASTLANE_CONFIG != ''"
-      when: on_success
+#deploy-local:
+#  stage: "deploy"
+#  image: "k8r.eu/justjanne/docker-s3cmd:latest"
+#  cache: { }
+#  dependencies:
+#    - "test"
+#    - "version"
+#  script:
+#    - "echo $S3_CONFIG | base64 -d > $HOME/.s3cfg"
+#    - "export VERSION=$(ls *.apk)"
+#    - "s3cmd put $VERSION s3://releases/quasseldroid-ng/$VERSION"
+#    - "s3cmd put version.json s3://releases/quasseldroid-ng/version.json"
+#    - "s3cmd cp s3://releases/quasseldroid-ng/$VERSION s3://releases/quasseldroid-ng/Quasseldroid-latest.apk"
+#  rules:
+#    - if: "$CI_COMMIT_BRANCH == 'main' && $S3_CONFIG != ''"
+#      when: on_success
+#
+#deploy-beta:
+#  stage: "deploy"
+#  image: "k8r.eu/justjanne/docker-fastlane:latest"
+#  cache: { }
+#  dependencies:
+#    - "test"
+#  script:
+#    - "echo $FASTLANE_CONFIG | base64 -d > $HOME/key.json"
+#    - "export VERSION=$(ls *.apk)"
+#    - "fastlane supply --apk $VERSION --track beta --json_key $HOME/key.json --package_name com.iskrembilen.quasseldroid --skip_upload_metadata=true --skip_upload_images=true --skip_upload_screenshots=true"
+#  rules:
+#    - if: "$CI_COMMIT_BRANCH == 'main' && $FASTLANE_CONFIG != ''"
+#      when: on_success
+#
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 5dc226b3e..00fbf423d 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -25,7 +25,7 @@ plugins {
 android {
   defaultConfig {
     vectorDrawables.useSupportLibrary = true
-    testInstrumentationRunner = "de.kuschku.quasseldroid.util.TestRunner"
+    testInstrumentationRunner = "de.justjanne.quasseldroid.util.TestRunner"
   }
 
   buildTypes {
@@ -75,6 +75,7 @@ dependencies {
   implementation(libs.androidx.navigation.compose)
 
   implementation(libs.libquassel.client)
+  implementation(libs.libquassel.irc)
 
   implementation(libs.compose.htmltext)
 
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index 88a5604d1..aa004edcf 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -28,10 +28,10 @@
 -dontobfuscate
 
 # Keep our invokers
--keep class * implements de.kuschku.libquassel.quassel.syncables.interfaces.invokers.Invoker {
+-keep class * implements de.justjanne.libquassel.protocol.syncables.invoker.Invoker {
     static ** INSTANCE;
 }
--keep class * implements de.kuschku.libquassel.quassel.syncables.interfaces.invokers.InvokerRegistry {
+-keep class * implements de.justjanne.libquassel.protocol.syncables.invoker.InvokerRegistry {
     static ** INSTANCE;
 }
 
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleMessageProvider.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleMessageProvider.kt
index bff8b7e40..83eaf37bc 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleMessageProvider.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/sample/SampleMessageProvider.kt
@@ -10,7 +10,7 @@ import de.justjanne.libquassel.protocol.models.flags.MessageType
 import de.justjanne.libquassel.protocol.models.ids.BufferId
 import de.justjanne.libquassel.protocol.models.ids.MsgId
 import de.justjanne.libquassel.protocol.models.ids.NetworkId
-import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
+import de.justjanne.libquassel.irc.HostmaskHelper
 import org.threeten.bp.Instant
 
 class SampleNickProvider : PreviewParameterProvider<String> {
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageBase.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageBase.kt
index f43b50fda..49fef6157 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageBase.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageBase.kt
@@ -1,13 +1,6 @@
 package de.justjanne.quasseldroid.ui.components
 
-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.layout.paddingFromBaseline
-import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.layout.*
 import androidx.compose.material.ContentAlpha
 import androidx.compose.material.LocalContentAlpha
 import androidx.compose.material.MaterialTheme
@@ -16,16 +9,18 @@ import androidx.compose.runtime.Composable
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.Alignment
 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.buildAnnotatedString
 import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextOverflow
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import androidx.compose.ui.unit.dp
 import androidx.compose.ui.unit.sp
+import de.justjanne.libquassel.irc.HostmaskHelper
 import de.justjanne.libquassel.protocol.models.Message
-import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
 import de.justjanne.quasseldroid.sample.SampleMessageProvider
 import de.justjanne.quasseldroid.ui.icons.AvatarIcon
 import de.justjanne.quasseldroid.ui.theme.QuasselTheme
@@ -58,6 +53,7 @@ fun MessageBase(
   message: Message,
   followUp: Boolean = false,
   // avatarSize: Dp = 32.dp
+  backgroundColor: Color = MaterialTheme.colors.surface,
   content: @Composable () -> Unit = { Text(message.content, style = Typography.body2) }
 ) {
   val avatarSize = 32.dp
@@ -92,7 +88,9 @@ fun MessageBase(
               append(message.realName)
               pop()
             },
-            style = Typography.body2
+            style = Typography.body2,
+            maxLines = 1,
+            overflow = TextOverflow.Ellipsis
           )
         }
       }
@@ -122,6 +120,7 @@ fun MessageBaseSmall(
   message: Message,
   followUp: Boolean = false,
   // avatarSize: Dp = 32.dp,
+  backgroundColor: Color = MaterialTheme.colors.surface,
   content: @Composable () -> Unit = {
     val nick = HostmaskHelper.nick(message.sender)
 
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageList.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageList.kt
index 08c7542c8..fd6d5e7d2 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageList.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/ui/components/MessageList.kt
@@ -11,10 +11,12 @@ import androidx.compose.ui.text.AnnotatedString
 import androidx.compose.ui.tooling.preview.Preview
 import androidx.compose.ui.tooling.preview.PreviewParameter
 import de.justjanne.bitflags.of
+import de.justjanne.libquassel.irc.HostmaskHelper
+import de.justjanne.libquassel.irc.IrcFormat
+import de.justjanne.libquassel.irc.IrcFormatDeserializer
 import de.justjanne.libquassel.protocol.models.Message
 import de.justjanne.libquassel.protocol.models.flags.MessageType
 import de.justjanne.libquassel.protocol.models.ids.MsgId
-import de.justjanne.libquassel.protocol.util.irc.HostmaskHelper
 import de.justjanne.quasseldroid.R
 import de.justjanne.quasseldroid.sample.SampleMessagesProvider
 import de.justjanne.quasseldroid.ui.theme.QuasselTheme
@@ -23,8 +25,6 @@ import de.justjanne.quasseldroid.util.extensions.OnBottomReached
 import de.justjanne.quasseldroid.util.extensions.OnTopReached
 import de.justjanne.quasseldroid.util.extensions.format
 import de.justjanne.quasseldroid.util.extensions.getPrevious
-import de.justjanne.quasseldroid.util.format.IrcFormat
-import de.justjanne.quasseldroid.util.format.IrcFormatDeserializer
 import de.justjanne.quasseldroid.util.format.IrcFormatRenderer
 import de.justjanne.quasseldroid.util.format.TextFormatter
 import org.threeten.bp.ZoneId
@@ -70,7 +70,7 @@ fun MessageList(
           }
         }
         MessageType.of(MessageType.Action) -> {
-          MessageBaseSmall(message) {
+          MessageBaseSmall(message, backgroundColor = QuasselTheme.chat.action) {
             val nick = HostmaskHelper.nick(message.sender)
 
             Text(
@@ -78,7 +78,9 @@ fun MessageList(
                 AnnotatedString(stringResource(R.string.message_format_action)),
                 buildNick(nick, message.senderPrefixes),
                 IrcFormatRenderer.render(
-                  data = parsed.map { it.copy(style = it.style.flipFlag(IrcFormat.Flag.ITALIC)) }
+                  data = parsed.map { it.copy(style = it.style.flipFlag(IrcFormat.Flag.ITALIC)) },
+                  textColor = QuasselTheme.chat.onAction,
+                  backgroundColor = QuasselTheme.chat.action
                 )
               ),
               style = Typography.body2,
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/util/extensions/SequenceExtensions.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/util/extensions/SequenceExtensions.kt
deleted file mode 100644
index 9ea6f2db5..000000000
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/util/extensions/SequenceExtensions.kt
+++ /dev/null
@@ -1,21 +0,0 @@
-package de.justjanne.quasseldroid.util.extensions
-
-fun <T> Sequence<T>.collapse(callback: (T, T) -> T?) = sequence<T> {
-  var prev: T? = null
-  for (item in iterator()) {
-    if (prev != null) {
-      val collapsed = callback(prev, item)
-      if (collapsed == null) {
-        yield(prev)
-        prev = item
-      } else {
-        prev = collapsed
-      }
-    } else {
-      prev = item
-    }
-  }
-  if (prev != null) {
-    yield(prev)
-  }
-}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormat.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormat.kt
deleted file mode 100644
index 063858668..000000000
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormat.kt
+++ /dev/null
@@ -1,59 +0,0 @@
-package de.justjanne.quasseldroid.util.format
-
-import de.justjanne.quasseldroid.util.extensions.joinString
-import androidx.compose.ui.graphics.Color as AndroidColor
-
-object IrcFormat {
-  data class Span(
-    val content: String,
-    val style: Style = Style()
-  ) {
-    override fun toString(): String = joinString(", ", "Info(", ")") {
-      append(content)
-      if (style != Style()) {
-        append("style=$style")
-      }
-    }
-  }
-
-  data class Style(
-    val flags: Set<Flag> = emptySet(),
-    val foreground: Color? = null,
-    val background: Color? = null,
-  ) {
-    fun flipFlag(flag: Flag) = copy(
-      flags = if (flags.contains(flag)) flags - flag else flags + flag
-    )
-
-    override fun toString(): String = joinString(", ", "Info(", ")") {
-      if (flags.isNotEmpty()) {
-        append("flags=$flags")
-      }
-      if (foreground != null) {
-        append("foreground=$foreground")
-      }
-      if (background != null) {
-        append("background=$background")
-      }
-    }
-  }
-
-  sealed class Color {
-    data class Mirc(val index: Int) : Color() {
-      override fun toString(): String = "Mirc($index)"
-    }
-
-    data class Hex(val color: AndroidColor) : Color() {
-      override fun toString(): String = "Hex(#${color.value.toString(16)})"
-    }
-  }
-
-  enum class Flag {
-    BOLD,
-    ITALIC,
-    UNDERLINE,
-    STRIKETHROUGH,
-    MONOSPACE,
-    INVERSE
-  }
-}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatDeserializer.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatDeserializer.kt
deleted file mode 100644
index 7e8087211..000000000
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatDeserializer.kt
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * Quasseldroid - Quassel client for Android
- *
- * Copyright (c) 2022 Janne Mareike Koschinski
- * Copyright (c) 2022 The Quassel Project
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 3 as published
- * by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package de.justjanne.quasseldroid.util.format
-
-import androidx.compose.ui.graphics.Color
-import de.justjanne.quasseldroid.util.extensions.collapse
-import kotlin.math.min
-
-/**
- * A helper class to turn mIRC formatted Strings into Android’s SpannableStrings with the same
- * color and format codes
- */
-object IrcFormatDeserializer {
-
-  fun parse(content: String) = sequence {
-    var i = 0
-    var lastProcessed = 0
-    var current = IrcFormat.Style()
-
-    suspend fun SequenceScope<IrcFormat.Span>.emit() {
-      if (lastProcessed != i) {
-        yield(IrcFormat.Span(content.substring(lastProcessed, i), current))
-        lastProcessed = i
-      }
-    }
-
-    suspend fun SequenceScope<IrcFormat.Span>.processFlag(flag: IrcFormat.Flag) {
-      emit()
-      current = current.flipFlag(flag)
-      lastProcessed = ++i
-    }
-
-    suspend fun SequenceScope<IrcFormat.Span>.processColor(
-      length: Int,
-      radix: Int = 10,
-      range: IntRange? = null,
-      matcher: (Char) -> Boolean
-    ): Pair<Int, Int?>? {
-      emit()
-
-      // Skip Color Code
-      lastProcessed = ++i
-
-      val foregroundData = content.substring(i, min(i + length, content.length))
-        .takeWhile(matcher)
-      val foreground = foregroundData.toIntOrNull(radix)
-        ?.takeIf { range == null || it in range }
-        ?: return null
-
-      // Skip foreground
-      i += foregroundData.length
-
-      val backgroundData =
-        if (i < content.length && content[i] == ',')
-          content.substring(i + 1, min(i + length + 1, content.length))
-            .takeWhile(matcher)
-        else null
-      val background = backgroundData
-        ?.toIntOrNull(radix)
-        ?.takeIf { range == null || it in range }
-
-      if (background != null) {
-        // Skip background and separator
-        i += backgroundData.length + 1
-      }
-
-      lastProcessed = i
-
-      return Pair(foreground, background)
-    }
-
-
-    while (i < content.length) {
-      when (content[i]) {
-        CODE_BOLD -> processFlag(IrcFormat.Flag.BOLD)
-        CODE_ITALIC -> processFlag(IrcFormat.Flag.ITALIC)
-        CODE_UNDERLINE -> processFlag(IrcFormat.Flag.UNDERLINE)
-        CODE_STRIKETHROUGH -> processFlag(IrcFormat.Flag.STRIKETHROUGH)
-        CODE_MONOSPACE -> processFlag(IrcFormat.Flag.MONOSPACE)
-        CODE_SWAP, CODE_SWAP_KVIRC -> processFlag(IrcFormat.Flag.INVERSE)
-        CODE_COLOR -> {
-          val color = processColor(length = 2, range = 0..99) {
-            it in '0'..'9'
-          }
-
-          current = if (color == null) {
-            current.copy(foreground = null, background = null)
-          } else {
-            val (foreground, background) = color
-            current.copy(
-              foreground = foreground.takeUnless { it == 99 }?.let { IrcFormat.Color.Mirc(it) },
-              background = if (background == null) current.background
-              else background.takeUnless { it == 99 }?.let { IrcFormat.Color.Mirc(it) }
-            )
-          }
-        }
-        CODE_HEXCOLOR -> {
-          val color = processColor(length = 6, radix = 16) {
-            it in '0'..'9' || it in 'a'..'f' || it in 'A'..'F'
-          }
-
-          current = if (color == null) {
-            current.copy(foreground = null, background = null)
-          } else {
-            val (foreground, background) = color
-            current.copy(
-              foreground = IrcFormat.Color.Hex(Color(foreground).copy(alpha = 1f)),
-              background = background?.let {
-                IrcFormat.Color.Hex(Color(it).copy(alpha = 1f))
-              } ?: current.background
-            )
-          }
-        }
-        CODE_RESET -> {
-          emit()
-          current = IrcFormat.Style()
-          lastProcessed = ++i
-        }
-        else -> {
-          // Regular Character
-          i++
-        }
-      }
-    }
-
-    if (lastProcessed != content.length) {
-      yield(IrcFormat.Span(content.substring(lastProcessed), current))
-    }
-  }.collapse { prev, current ->
-    if (prev.style == current.style) prev.copy(content = prev.content + current.content)
-    else null
-  }
-
-  private const val CODE_BOLD = 0x02.toChar()
-  private const val CODE_COLOR = 0x03.toChar()
-  private const val CODE_HEXCOLOR = 0x04.toChar()
-  private const val CODE_ITALIC = 0x1D.toChar()
-  private const val CODE_UNDERLINE = 0x1F.toChar()
-  private const val CODE_STRIKETHROUGH = 0x1E.toChar()
-  private const val CODE_MONOSPACE = 0x11.toChar()
-  private const val CODE_SWAP_KVIRC = 0x12.toChar()
-  private const val CODE_SWAP = 0x16.toChar()
-  private const val CODE_RESET = 0x0F.toChar()
-}
diff --git a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatRenderer.kt b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatRenderer.kt
index 2cc81b47e..87233d91f 100644
--- a/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatRenderer.kt
+++ b/app/src/main/kotlin/de/justjanne/quasseldroid/util/format/IrcFormatRenderer.kt
@@ -11,6 +11,7 @@ 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.text.style.TextDecoration
+import de.justjanne.libquassel.irc.IrcFormat
 import de.justjanne.quasseldroid.ui.theme.QuasselTheme
 
 object IrcFormatRenderer {
@@ -33,7 +34,7 @@ object IrcFormatRenderer {
   private fun toColor(color: IrcFormat.Color?): Color? = when (color) {
     null -> null
     is IrcFormat.Color.Mirc -> QuasselTheme.mirc.colors[color.index]
-    is IrcFormat.Color.Hex -> color.color
+    is IrcFormat.Color.Hex -> Color(color.color).copy(alpha = 1.0f)
   }
 
   @Composable
@@ -42,8 +43,8 @@ object IrcFormatRenderer {
     textColor: Color,
     backgroundColor: Color
   ): SpanStyle {
-    val foreground = toColor(style.foreground) ?: textColor
-    val background = toColor(style.background) ?: backgroundColor
+    val foreground = toColor(style.foreground)
+    val background = toColor(style.background)
 
     return SpanStyle(
       fontWeight = if (style.flags.contains(IrcFormat.Flag.BOLD)) FontWeight.Bold else FontWeight.Normal,
@@ -55,8 +56,10 @@ object IrcFormatRenderer {
         )
       ),
       fontFamily = if (style.flags.contains(IrcFormat.Flag.MONOSPACE)) FontFamily.Monospace else null,
-      color = if (style.flags.contains(IrcFormat.Flag.INVERSE)) background else foreground,
-      background = if (style.flags.contains(IrcFormat.Flag.INVERSE)) foreground else background,
+      color = if (style.flags.contains(IrcFormat.Flag.INVERSE)) background ?: backgroundColor
+      else foreground ?: Color.Unspecified,
+      background = if (style.flags.contains(IrcFormat.Flag.INVERSE)) foreground ?: textColor
+      else background ?: Color.Unspecified,
     )
   }
 }
diff --git a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt b/app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt
similarity index 88%
rename from app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt
rename to app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt
index 7c94ce20e..5662b24a2 100644
--- a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt
+++ b/app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/SenderColorUtilTest.kt
@@ -1,4 +1,4 @@
-package de.kuschku.justjanne.quasseldroid.util.irc
+package de.justjanne.quasseldroid.util.irc
 
 import de.justjanne.quasseldroid.util.irc.SenderColorUtil
 import org.junit.jupiter.api.Assertions.assertEquals
diff --git a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt b/app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
similarity index 98%
rename from app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
rename to app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
index cb9d0d480..0c78006bc 100644
--- a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
+++ b/app/src/test/kotlin/de/justjanne/quasseldroid/util/irc/TextFormatterTest.kt
@@ -1,4 +1,4 @@
-package de.kuschku.justjanne.quasseldroid.util.irc
+package de.justjanne.quasseldroid.util.irc
 
 import androidx.compose.ui.graphics.Color
 import androidx.compose.ui.text.SpanStyle
diff --git a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/IrcFormatDeserializerTest.kt b/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/IrcFormatDeserializerTest.kt
deleted file mode 100644
index 541ac0571..000000000
--- a/app/src/test/kotlin/de/kuschku/justjanne/quasseldroid/util/irc/IrcFormatDeserializerTest.kt
+++ /dev/null
@@ -1,1593 +0,0 @@
-package de.kuschku.justjanne.quasseldroid.util.irc
-
-import androidx.compose.ui.graphics.Color
-import de.justjanne.quasseldroid.util.format.IrcFormat
-import de.justjanne.quasseldroid.util.format.IrcFormatDeserializer
-import org.junit.jupiter.api.Test
-import kotlin.test.assertEquals
-
-class IrcFormatDeserializerTest {
-  @Test
-  fun testBroken() {
-    assertEquals(
-      emptyList(),
-      IrcFormatDeserializer.parse(
-        "\u000f"
-      ).toList()
-    )
-
-    assertEquals(
-      emptyList(),
-      IrcFormatDeserializer.parse(
-        "\u0003\u000f"
-      ).toList()
-    )
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "["
-        ),
-        IrcFormat.Span(
-          "hdf-us",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.ITALIC),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "] ["
-        ),
-        IrcFormat.Span(
-          "nd",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "] blah blah blah"
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "[\u001d\u000304hdf-us\u0003\u000f] [\u000307nd\u0003] blah blah blah"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "New Break set to: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "Target: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("388 "),
-        IrcFormat.Span(
-          "| ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "Type: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("GS | "),
-        IrcFormat.Span(
-          "Break: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("58,000 "),
-        IrcFormat.Span(
-          "| ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "120%: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("48,000 | "),
-        IrcFormat.Span(
-          "135%: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("43,000 "),
-        IrcFormat.Span(
-          "| ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "145%: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("40,000 "),
-        IrcFormat.Span(
-          "| ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "180%: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("32,000"),
-        IrcFormat.Span(
-          " | ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "Pop: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span("73819"),
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000302New Break set to: \u000303Target: \u000399388 \u000302| \u000303Type: " +
-          "\u000399GS | \u000303Break: \u00039958,000 \u000302| \u000303120%: \u00039948,000 | " +
-          "\u000303135%: \u00039943,000 \u000302| \u000303145%: \u00039940,000 \u000302| " +
-          "\u000303180%: \u00039932,000\u000302 | \u000303Pop: \u00039973819\u000f"
-      ).toList()
-    )
-  }
-
-  @Test
-  fun testStrikethrough() {
-    assertEquals(
-      listOf(
-        IrcFormat.Span("Normal"),
-        IrcFormat.Span(
-          "Strikethrough",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.STRIKETHROUGH)
-          )
-        ),
-        IrcFormat.Span("Normal")
-      ),
-      IrcFormatDeserializer.parse(
-        "Normal\u001eStrikethrough\u001eNormal"
-      ).toList()
-    )
-  }
-
-  @Test
-  fun testInverse() {
-    assertEquals(
-      listOf(
-        IrcFormat.Span("First"),
-        IrcFormat.Span(
-          "Second",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE)
-          )
-        ),
-        IrcFormat.Span(
-          "Red/Green",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE),
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Green/Red",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Green/Magenta",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(6),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Magenta/Green",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE),
-            foreground = IrcFormat.Color.Mirc(6),
-            background = IrcFormat.Color.Mirc(3),
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "First\u0016Second\u00034,3Red/Green\u0016Green/Red\u00036Green/Magenta\u0016Magenta/Green"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("First"),
-        IrcFormat.Span(
-          "Second",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE)
-          )
-        ),
-        IrcFormat.Span(
-          "Third",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE),
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        ),
-        IrcFormat.Span(
-          "Red/Green",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Green/Red",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE),
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Green/Magenta",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.INVERSE),
-            foreground = IrcFormat.Color.Mirc(6),
-            background = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "Magenta/Green",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(6),
-            background = IrcFormat.Color.Mirc(3),
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "First\u0012Second\u00032Third\u0012\u00034,3Red/Green\u0012Green/Red\u00036Green/Magenta\u0016Magenta/Green"
-      ).toList()
-    )
-  }
-
-  @Test
-  fun testMonospace() {
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "test ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "test",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.MONOSPACE),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00034test \u0011test"
-      ).toList()
-    )
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "test ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.MONOSPACE)
-          )
-        ),
-        IrcFormat.Span(
-          "test",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.MONOSPACE),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u0011test \u00034test"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("`test "),
-        IrcFormat.Span(
-          "test`",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "`test \u00034test`"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "[test ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "nick`name",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        ),
-        IrcFormat.Span(
-          "] [nick`name]",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00034[test \u0002nick`name\u0002] [nick`name]"
-      ).toList()
-    )
-  }
-
-  @Test
-  fun testColors() {
-    assertEquals(
-      listOf(
-        IrcFormat.Span("Test 1: "),
-        IrcFormat.Span(
-          "[",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(
-          "6,7,3,9,10,4,8,10,5",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(6)
-          )
-        ),
-        IrcFormat.Span(
-          "]",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "Test2: ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          " ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          "[",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span("2,9"),
-        IrcFormat.Span(
-          "]",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "Test 1: \u0002\u000312[\u00036\u00026,7,3,9,10,4,8,10,5\u0002\u000312]" +
-          "\u0003\u0002 \u000314Test2: \u0002 \u000312[\u0003\u00022,9\u0002\u000312]\u0003\u0002"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "Extended colors",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(55),
-            background = IrcFormat.Color.Mirc(25)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000355,25Extended colors\u0003"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "Transparent extended colors",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE),
-            foreground = IrcFormat.Color.Mirc(55),
-            background = IrcFormat.Color.Mirc(25)
-          )
-        ),
-        IrcFormat.Span(
-          " cleared fg",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE),
-            background = IrcFormat.Color.Mirc(25)
-          )
-        ),
-        IrcFormat.Span(
-          " cleared bg",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE),
-            foreground = IrcFormat.Color.Mirc(55)
-          )
-        ),
-        IrcFormat.Span(
-          " cleared both",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE)
-          )
-        ),
-        IrcFormat.Span(
-          " cleared bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.UNDERLINE)
-          )
-        ),
-        IrcFormat.Span(" cleared all")
-      ),
-      IrcFormatDeserializer.parse(
-        "\u001f\u0002\u000355,25Transparent extended colors\u000399,25 cleared fg\u000355,99 cleared bg\u000399,99 cleared both\u0002 cleared bold\u000f cleared all",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "Sniper_ShooterCZ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "(1)",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(":"),
-        IrcFormat.Span(
-          " kokote",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00030,1\u0002Sniper_ShooterCZ\u0002(1)\u000f:\u00032 kokote"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "uncurry",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(9)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "Vect",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(" : "),
-        IrcFormat.Span(
-          "(Nat,",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "Type)",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(" -> "),
-        IrcFormat.Span(
-          "Type",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000309uncurry\u000f \u000312Vect\u000f : \u000312(\u000f\u000312Nat\u000f\u000312," +
-          "\u000f \u000312Type\u000f\u000312)\u000f -> \u000312Type\u000f"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("*** ("),
-        IrcFormat.Span(
-          "ACTIVITIES",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        ),
-        IrcFormat.Span("): Mugging: "),
-        IrcFormat.Span(
-          "||||||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3),
-            background = IrcFormat.Color.Mirc(3),
-          )
-        ),
-        IrcFormat.Span(
-          "||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "34%",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "||||||||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(" | [under dev] Piracy: "),
-        IrcFormat.Span(
-          "|||||||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "0.9%",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "||||||||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(" (exploring) | At this rate, you will get: Fined")
-      ),
-      IrcFormatDeserializer.parse(
-        "*** (\u0002ACTIVITIES\u000f): Mugging: \u000303,03|\u000303,03|\u000303,03|\u000303,03|" +
-          "\u000303,03|\u000303,03|\u000304,04|\u000304,04|\u000300,043\u000300,044\u000300,04%" +
-          "\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|" +
-          "\u000304,04|\u000300,04\u000f | [under dev] Piracy: \u000304,04|\u000304,04|" +
-          "\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000300,040\u000300,04." +
-          "\u000300,049\u000300,04%\u000304,04|\u000304,04|\u000304,04|\u000304,04|\u000304,04|" +
-          "\u000304,04|\u000304,04|\u000304,04|\u000300,04\u000f (exploring) | At this rate, you " +
-          "will get: Fined",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "\\u000308 ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(8)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u000310 ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u0002 ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u000304 ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u0002 ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u000309 ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(9)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u0002 ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(9)
-          )
-        ),
-        IrcFormat.Span(
-          "\\u0002",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(9)
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000308\\u000308 \u000310\\u000310 \u0002\\u0002 \u000304\\u000304 \u0002\\u0002 " +
-          "\u000309\\u000309 \u0002\\u0002 \u0002\\u0002"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "teal",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "boldteal",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "boldred",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "red",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000310teal\u0002boldteal\u000304boldred\u0002red",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "The channel for help with general IRC things such as ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "clients",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(13)
-          )
-        ),
-        IrcFormat.Span(
-          ", ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "BNCs",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          ", ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "bots",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          ", ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          "scripting",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(6)
-          )
-        ),
-        IrcFormat.Span(
-          " ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(6)
-          )
-        ),
-        IrcFormat.Span(
-          "etc.",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00033The channel for help with general IRC things such as \u0002\u000313clients" +
-          "\u0002\u00033, \u0002\u00037BNCs\u0002\u00033, \u0002\u00034bots\u0002\u00033, " +
-          "\u0002\u00036scripting\u0002 \u00033etc.",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "hi ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "hola",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u0002\u000310hi \u0002hola"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "hi ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "hola",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000310\u0002hi \u0003hola"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "h",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(10)
-          )
-        ),
-        IrcFormat.Span(
-          "i ",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "hola",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u0002\u000310h\u00034i \u0002hola"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "__",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "(",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          "✰",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(8),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          ")",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          "__",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2),
-            background = IrcFormat.Color.Mirc(2),
-          )
-        ),
-        IrcFormat.Span(
-          " ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(1),
-          )
-        ),
-        IrcFormat.Span(
-          "Ejercito Paraguayo",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(1),
-          )
-        ),
-        IrcFormat.Span(
-          " ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(1),
-          )
-        ),
-        IrcFormat.Span(
-          "__",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(
-          "(",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          "✰",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(8),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          ")",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3),
-            background = IrcFormat.Color.Mirc(0),
-          )
-        ),
-        IrcFormat.Span(
-          "__",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(2),
-            background = IrcFormat.Color.Mirc(2),
-          )
-        ),
-        IrcFormat.Span("***** Lord Commander: mdmg - Sub-Comandantes: Sgto_Galleta ***** "),
-        IrcFormat.Span(
-          " Vencer o Morir!!!  Que alguien pase una nueva xd",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(0),
-            background = IrcFormat.Color.Mirc(4),
-          )
-        ),
-        IrcFormat.Span(" http://i.imgur.com/bTWzTuA.jpg"),
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00034,4__\u00033,0(\u00038,0✰\u00033,0)\u00032,2__\u00030,1 \u0002Ejercito Paraguayo" +
-          "\u0002 \u00034,4__\u00033,0(\u00038,0✰\u00033,0)\u00032,2__" +
-          "\u00031\u0003***** Lord Commander: mdmg - Sub-Comandantes: Sgto_Galleta ***** " +
-          "\u00030,4 Vencer o Morir!!!  Que alguien pase una nueva xd" +
-          "\u0003 http://i.imgur.com/bTWzTuA.jpg"
-      ).toList()
-    )
-
-    assertEquals(
-      emptyList(),
-      IrcFormatDeserializer.parse(
-        "\u00034\u000f"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("hello")
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00034\u000fhello"
-      ).toList()
-    )
-
-    assertEquals(
-      emptyList(),
-      IrcFormatDeserializer.parse(
-        "\u00031"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ">bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span("test")
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000304\u0002>bold\u0002\u0003test"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "P",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span("layers"),
-        IrcFormat.Span(
-          "(",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "1/12",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          ")",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "Kenzi",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(15)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "C",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span("urrent votewinner: none | ?? help for all the commands | www.no1gaming.eu | #no1"),
-      ),
-      IrcFormatDeserializer.parse(
-        "\u00037P\u000flayers\u00037(\u0003141/12\u00037)\u000f \u000315Kenzi\u0003 \u00037C" +
-          "\u000furrent votewinner: none | ?? help for all the commands | www.no1gaming.eu | #no1",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("First "),
-        IrcFormat.Span(
-          "Red ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "Green",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(3)
-          )
-        ),
-        IrcFormat.Span(
-          " Bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "First \u00034Red \u00033Green\u0003\u0002 Bold\u0002\u000f",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("First "),
-        IrcFormat.Span(
-          "Color",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          " Bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        ),
-        IrcFormat.Span(" unnecessary: "),
-        IrcFormat.Span(
-          "Color",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span("\u0000 plain "),
-        IrcFormat.Span(
-          "Color",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "Bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "No space color New color",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "First \u00034Color\u0003\u0002 Bold\u0002 unnecessary:\u0003 \u00034Color" +
-          "\u0003\u0000 plain \u00034Color\u0003\u000f \u0002Bold\u000f \u00034No space color" +
-          "\u0003\u00034 New color\u000f",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("DALnet's recommended mIRC scripting & bot help channel. "),
-        IrcFormat.Span(
-          "Visit us at ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "www.dalnethelpdesk.com",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.UNDERLINE),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(" for scripting info, forums, and searchable logs/stats "),
-        IrcFormat.Span(
-          "Looking for a script/bot/addon?",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "mircscripts.org",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "or",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          "mirc.net",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD, IrcFormat.Flag.UNDERLINE)
-          )
-        ),
-        IrcFormat.Span(" "),
-        IrcFormat.Span(
-          " Writing your own?",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(
-          " Ask ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "here.",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD),
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          " ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(" - "),
-        IrcFormat.Span(
-          "m",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(
-          "IR",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "Casdsaa",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(8)
-          )
-        ),
-        IrcFormat.Span("asdasd v7.14 has been released")
-      ),
-      IrcFormatDeserializer.parse(
-        "DALnet's recommended mIRC scripting & bot help channel. \u00034Visit us at " +
-          "\u001fwww.dalnethelpdesk.com\u000f for \u0003scripting info, forums, and searchable " +
-          "logs/stats \u000312Looking for a script/bot/addon?\u000f \u0002\u001fmircscripts.org" +
-          "\u000f \u00034or\u000f \u0002\u001fmirc.net\u000f \u000312 Writing your own?\u0003" +
-          "\u00034 Ask \u0002here.\u0002 \u000f - \u000312m\u00034IR\u00038Casdsaa\u0003asdasd" +
-          "\u000f v7.14 has been released",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span("i was last seen \\ \\"),
-        IrcFormat.Span(
-          "             ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "test^",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "      ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "._",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "   ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          " '--' ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          "'-.\\__/ ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(
-          "_",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          "l",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(12)
-          )
-        ),
-        IrcFormat.Span(
-          "       ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "\\",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "        ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "\\",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "      ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "||",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(13)
-          )
-        ),
-        IrcFormat.Span(
-          "       ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "/",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "       ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "test",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD), foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "  ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "^",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "    ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          ")",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(9)
-          )
-        ),
-        IrcFormat.Span(
-          "\\",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(14)
-          )
-        ),
-        IrcFormat.Span(
-          "((((",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(15)
-          )
-        ),
-        IrcFormat.Span(
-          "\\",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "   ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          ".",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(7)
-          )
-        ),
-        IrcFormat.Span(
-          "            ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          " :;;,,",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(8)
-          )
-        ),
-        IrcFormat.Span(
-          "'-._",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "  ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-        IrcFormat.Span(
-          "\\",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4)
-          )
-        ),
-        IrcFormat.Span(
-          "                        ",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(1)
-          )
-        ),
-      ),
-      IrcFormatDeserializer.parse(
-        "i was last seen \\ \\\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u00037test^\u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00037._\u00031 \u00031 \u00031 \u000314 '--' \u000312'-.\\__/ " +
-          "\u000314_\u000312l\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00034\\" +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00034\\\u00031 " +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u000313||\u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00031 \u00037/\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00034\u0002test\u0002\u00031 \u00031 \u00037^\u00031 \u00031 \u00031 " +
-          "\u00031 \u00039)\u000314\\\u000315((((\u00037\\\u00031 \u00031 \u00031 \u00037." +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00038 :;;,,\u00034'-._\u00031 \u00031 \u00034\\\u00031 \u00031 " +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 \u00031 " +
-          "\u00031 \u00031 \u00031"
-      ).toList()
-    )
-  }
-
-  @Test
-  fun testHexColors() {
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "some text in 55ee22 rgb",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22))
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22some text in 55ee22 rgb\u0004"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ",some text in 55ee22 rgb",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22))
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,some text in 55ee22 rgb\u0004"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          "some text in 55ee22 rgb on aaaaaa bg",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22)),
-            background = IrcFormat.Color.Hex(Color(0xffaaaaaa))
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,aaaaaasome text in 55ee22 rgb on aaaaaa bg\u0004"
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ",some text in 55ee22 rgb on aaaaaa bg",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22)),
-            background = IrcFormat.Color.Hex(Color(0xffaaaaaa))
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ",some text in 55ee22 rgb on aaaaaa bg",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22)),
-            background = IrcFormat.Color.Hex(Color(0xffaaaaaa))
-          )
-        ),
-        IrcFormat.Span(
-          " Bold",
-          IrcFormat.Style(
-            flags = setOf(IrcFormat.Flag.BOLD)
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u0002 Bold\u0002",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ",some text in 55ee22 rgb on aaaaaa bg",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22)),
-            background = IrcFormat.Color.Hex(Color(0xffaaaaaa))
-          )
-        ),
-        IrcFormat.Span("\u0000")
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u0000",
-      ).toList()
-    )
-
-    assertEquals(
-      listOf(
-        IrcFormat.Span(
-          ",some text in 55ee22 rgb on aaaaaa bg",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Hex(Color(0xff55ee22)),
-            background = IrcFormat.Color.Hex(Color(0xffaaaaaa))
-          )
-        ),
-        IrcFormat.Span(
-          " Red",
-          IrcFormat.Style(
-            foreground = IrcFormat.Color.Mirc(4),
-          )
-        )
-      ),
-      IrcFormatDeserializer.parse(
-        "\u000455ee22,aaaaaa,some text in 55ee22 rgb on aaaaaa bg\u0004\u00034 Red\u0003",
-      ).toList()
-    )
-  }
-}
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index 6594147bc..ff603593b 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,5 +1,5 @@
 [versions]
-libquassel = "0.9.2"
+libquassel = "0.10.1"
 androidx-activity = "1.4.0"
 androidx-appcompat = "1.4.1"
 androidx-compose = "1.1.1"
@@ -8,6 +8,7 @@ androidx-navigation = "2.4.1"
 [libraries]
 libquassel-protocol = { module = "de.justjanne.libquassel:libquassel-protocol", version.ref = "libquassel" }
 libquassel-client = { module = "de.justjanne.libquassel:libquassel-client", version.ref = "libquassel" }
+libquassel-irc = { module = "de.justjanne.libquassel:libquassel-irc", version.ref = "libquassel" }
 
 androidx-activity = { module = "androidx.activity:activity-ktx", version.ref = "androidx-activity" }
 androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activity" }
-- 
GitLab