diff options
author | nea <nea@nea.moe> | 2023-07-31 18:00:32 +0200 |
---|---|---|
committer | nea <nea@nea.moe> | 2023-07-31 18:00:32 +0200 |
commit | e61e7a38a047d1c0e073d1b47461a884f975e733 (patch) | |
tree | 3e6755bb84467d8a4dea8ac0bf6f91074b57efb5 /src/main/kotlin/moe/nea | |
parent | a5155e33f226fea9d1684db4b5831952c081dde2 (diff) | |
download | firmament-e61e7a38a047d1c0e073d1b47461a884f975e733.tar.gz firmament-e61e7a38a047d1c0e073d1b47461a884f975e733.tar.bz2 firmament-e61e7a38a047d1c0e073d1b47461a884f975e733.zip |
Clickable chat links
Diffstat (limited to 'src/main/kotlin/moe/nea')
-rw-r--r-- | src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt | 4 | ||||
-rw-r--r-- | src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt (renamed from src/main/kotlin/moe/nea/firmament/features/chat/ImagePreview.kt) | 70 |
2 files changed, 42 insertions, 32 deletions
diff --git a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt index 416da18..f1600a5 100644 --- a/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt +++ b/src/main/kotlin/moe/nea/firmament/features/FeatureManager.kt @@ -21,7 +21,7 @@ package moe.nea.firmament.features import kotlinx.serialization.Serializable import kotlinx.serialization.serializer import moe.nea.firmament.Firmament -import moe.nea.firmament.features.chat.ImagePreview +import moe.nea.firmament.features.chat.ChatLinks import moe.nea.firmament.features.debug.DebugView import moe.nea.firmament.features.debug.DeveloperFeatures import moe.nea.firmament.features.fishing.FishingWarning @@ -58,7 +58,7 @@ object FeatureManager : DataHolder<FeatureManager.Config>(serializer(), "feature loadFeature(SlotLocking) loadFeature(StorageOverlay) loadFeature(CraftingOverlay) - loadFeature(ImagePreview) + loadFeature(ChatLinks) loadFeature(SaveCursorPosition) loadFeature(CustomSkyBlockTextures) loadFeature(Fixes) diff --git a/src/main/kotlin/moe/nea/firmament/features/chat/ImagePreview.kt b/src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt index e612995..41b2e96 100644 --- a/src/main/kotlin/moe/nea/firmament/features/chat/ImagePreview.kt +++ b/src/main/kotlin/moe/nea/firmament/features/chat/ChatLinks.kt @@ -9,11 +9,11 @@ import moe.nea.jarvis.api.Point import kotlinx.coroutines.Deferred import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.async -import kotlin.math.max import kotlin.math.min import net.minecraft.client.gui.screen.ChatScreen import net.minecraft.client.texture.NativeImage import net.minecraft.client.texture.NativeImageBackedTexture +import net.minecraft.text.ClickEvent import net.minecraft.text.HoverEvent import net.minecraft.text.Style import net.minecraft.text.Text @@ -28,12 +28,13 @@ import moe.nea.firmament.util.MC import moe.nea.firmament.util.transformEachRecursively import moe.nea.firmament.util.unformattedString -object ImagePreview : FirmamentFeature { +object ChatLinks : FirmamentFeature { override val identifier: String - get() = "image-preview" + get() = "chat-links" object TConfig : ManagedConfig(identifier) { - val enabled by toggle("enabled") { true } + val enableLinks by toggle("links-enabled") { true } + val imageEnabled by toggle("image-enabled") { true } val allowAllHosts by toggle("allow-all-hosts") { false } val allowedHosts by string("allowed-hosts") { "cdn.discordapp.com,media.discordapp.com,media.discordapp.net,i.imgur.com" } val actualAllowedHosts get() = allowedHosts.split(",").map { it.trim() } @@ -46,7 +47,7 @@ object ImagePreview : FirmamentFeature { private fun isUrlAllowed(url: String) = isHostAllowed(url.removePrefix("https://").substringBefore("/")) override val config get() = TConfig - val urlRegex = "https://[^. ]+\\.[^ ]+(\\.(png|gif|jpe?g))(\\?[^ ]*)?( |$)".toRegex() + val urlRegex = "https://[^. ]+\\.[^ ]+( |$)".toRegex() data class Image( val texture: Identifier, @@ -84,45 +85,54 @@ object ImagePreview : FirmamentFeature { } } + val imageExtensions = listOf("jpg", "png", "gif", "jpeg") + fun isImageUrl(url: String): Boolean { + return (url.substringAfterLast('.').lowercase() in imageExtensions) + } + @OptIn(ExperimentalCoroutinesApi::class) override fun onLoad() { ClientChatLineReceivedEvent.subscribe { - it.replaceWith = it.text.transformEachRecursively { child -> - val text = child.string - if ("://" !in text) return@transformEachRecursively child - val s = Text.empty().setStyle(child.style) - var index = 0 - while (index < text.length) { - val nextMatch = urlRegex.find(text, index) - if (nextMatch == null) { - s.append(Text.literal(text.substring(index, text.length))) - break - } - val range = nextMatch.groups[0]!!.range - val url = nextMatch.groupValues[0] - s.append(Text.literal(text.substring(index, range.first))) - s.append( - Text.literal(url).setStyle( - Style.EMPTY.withUnderline(true).withColor( - Formatting.AQUA - ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url))) + if (TConfig.enableLinks) + it.replaceWith = it.text.transformEachRecursively { child -> + val text = child.string + if ("://" !in text) return@transformEachRecursively child + val s = Text.empty().setStyle(child.style) + var index = 0 + while (index < text.length) { + val nextMatch = urlRegex.find(text, index) + if (nextMatch == null) { + s.append(Text.literal(text.substring(index, text.length))) + break + } + val range = nextMatch.groups[0]!!.range + val url = nextMatch.groupValues[0] + s.append(Text.literal(text.substring(index, range.first))) + s.append( + Text.literal(url).setStyle( + Style.EMPTY.withUnderline(true).withColor( + Formatting.AQUA + ).withHoverEvent(HoverEvent(HoverEvent.Action.SHOW_TEXT, Text.literal(url))) + .withClickEvent(ClickEvent(ClickEvent.Action.OPEN_URL, url)) + ) ) - ) - tryCacheUrl(url) - index = range.last + 1 + if (isImageUrl(url)) + tryCacheUrl(url) + index = range.last + 1 + } + s } - s - } } ScreenRenderPostEvent.subscribe { - if (!TConfig.enabled) return@subscribe + if (!TConfig.imageEnabled) return@subscribe if (it.screen !is ChatScreen) return@subscribe val hoveredComponent = MC.inGameHud.chatHud.getTextStyleAt(it.mouseX.toDouble(), it.mouseY.toDouble()) ?: return@subscribe val hoverEvent = hoveredComponent.hoverEvent ?: return@subscribe val value = hoverEvent.getValue(HoverEvent.Action.SHOW_TEXT) ?: return@subscribe val url = urlRegex.matchEntire(value.unformattedString)?.groupValues?.get(0) ?: return@subscribe + if (!isImageUrl(url)) return@subscribe val imageFuture = imageCache[url] ?: return@subscribe if (!imageFuture.isCompleted) return@subscribe val image = imageFuture.getCompleted() ?: return@subscribe |