latex bot discord
use GodoTeX
unfortunately it cant go in submodules because it trys to compile submodules/*
bendn 2022-11-11
parent 2689137 · commit f5d4775
-rw-r--r--.gitattributes4
-rw-r--r--.github/example.pngbin0 -> 46817 bytes
-rw-r--r--.github/workflows/export.yml80
-rw-r--r--.gitignore1
-rw-r--r--.gitmodules3
-rw-r--r--Latex.gd106
-rw-r--r--README.md4
-rw-r--r--addons/GodoTeX/GodoTeX.cs33
-rw-r--r--addons/GodoTeX/GodoTeX.csproj21
-rw-r--r--addons/GodoTeX/LaTeX.cs99
-rw-r--r--addons/GodoTeX/LaTeX3D.cs98
-rw-r--r--addons/GodoTeX/LaTeXButton.cs105
-rw-r--r--addons/GodoTeX/LaTeXture.cs53
-rw-r--r--addons/GodoTeX/icon.svg1
-rw-r--r--addons/GodoTeX/icon.svg.import38
-rw-r--r--addons/GodoTeX/iconButton.svg1
-rw-r--r--addons/GodoTeX/iconButton.svg.import35
-rw-r--r--addons/GodoTeX/iconGrey.svg1
-rw-r--r--addons/GodoTeX/iconGrey.svg.import35
-rw-r--r--addons/GodoTeX/iconRed.svg41
-rw-r--r--addons/GodoTeX/iconRed.svg.import35
-rw-r--r--addons/GodoTeX/plugin.cfg7
-rw-r--r--latex bot.csproj15
-rw-r--r--latex bot.sln19
-rw-r--r--project.godot4
25 files changed, 668 insertions, 171 deletions
diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index a05dc57..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1,4 +0,0 @@
-*.png filter=lfs diff=lfs merge=lfs -text
-*.ogg filter=lfs diff=lfs merge=lfs -text
-*.ttf filter=lfs diff=lfs merge=lfs -text
-*.svg filter=lfs diff=lfs merge=lfs -text
diff --git a/.github/example.png b/.github/example.png
new file mode 100644
index 0000000..6de1b95
--- /dev/null
+++ b/.github/example.png
Binary files differ
diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml
deleted file mode 100644
index 80bc4ec..0000000
--- a/.github/workflows/export.yml
+++ /dev/null
@@ -1,80 +0,0 @@
-name: "export"
-on:
- workflow_dispatch:
- push:
- paths:
- - "**.gd"
- - "**.tscn"
- - "**.import"
- - "**.tres"
- - "**.ttf"
- - "**.yml"
- branches:
- - main
-
-env:
- GODOT_VERSION: 3.5
- NAME: ${{ github.event.repository.name }}
-
-jobs:
- export:
- runs-on: ubuntu-latest
- container:
- image: ghcr.io/bend-n/godot-2d:3.5
- name: ${{ matrix.name }}
- strategy:
- matrix:
- include:
- - name: Windows export
- platform: windows
-
- - name: Linux export
- platform: linux
-
- - name: Mac export
- platform: mac
-
- - name: Web export
- platform: web
-
- - name: Android export
- platform: android
-
- steps:
- - name: Build (Windows)
- if: matrix.platform == 'windows'
- uses: bend-n/godot-actions/.github/actions/export-windows@main
-
- - name: Build (Linux)
- if: matrix.platform == 'linux'
- uses: bend-n/godot-actions/.github/actions/export-linux@main
-
- - name: Build (Mac)
- if: matrix.platform == 'mac'
- uses: bend-n/godot-actions/.github/actions/export-mac@main
-
- - name: Build (Web)
- if: matrix.platform == 'web'
- uses: bend-n/godot-actions/.github/actions/export-web@main
-
- - name: Build (Android)
- if: matrix.platform == 'android'
- uses: bend-n/godot-actions/.github/actions/export-android@main
- with:
- android-keystore-base64: ${{ secrets.ANDROID_KEYSTORE_BASE64 }}
- android-password: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }}
-
- push-itch:
- needs: [export]
- name: Push to itch.io
- runs-on: ubuntu-20.04
- steps:
- - name: Check for api key
- id: secret
- run: echo '::set-output name=secret::${{ secrets.BUTLER_CREDENTIALS }}'
-
- - name: Push
- if: steps.secret.outputs.secret
- uses: bend-n/godot-actions/.github/actions/itch-push@main
- with:
- api-key: ${{ secrets.BUTLER_CREDENTIALS }}
diff --git a/.gitignore b/.gitignore
index 0c9aec6..1e45693 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,3 +12,4 @@ token
*.dvi
*.log
texs/
+.mono/
diff --git a/.gitmodules b/.gitmodules
index f941959..a2aa44f 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,6 @@
[submodule "submodules/discord.gd"]
path = submodules/discord.gd
url = https://github.com/3ddelano/discord.gd
+[submodule "submodules/GodoTeX"]
+ path = submodules/GodoTeX
+ url = https://github.com/file-acomplaint/GodoTeX/
diff --git a/Latex.gd b/Latex.gd
index 5ac2be9..28b880e 100644
--- a/Latex.gd
+++ b/Latex.gd
@@ -1,21 +1,6 @@
extends Node
-var template_tex := """
-\\documentclass[varwidth=true]{standalone}
-\\usepackage[utf8]{inputenc}
-\\usepackage{xcolor}
-\\usepackage{amsmath}
-
-\\color{white}
-\\begin{document}
-
-%s
-
-\\end{document}
-"""
-
-var f := File.new()
-var thread_pool := []
+const laTeXture := preload("./addons/GodoTeX/LaTeXture.cs")
func compile(source: String) -> RegEx:
@@ -24,19 +9,12 @@ func compile(source: String) -> RegEx:
return reg
-func _ready():
- for _i in range(5):
- thread_pool.append(Thread.new())
- var dir = Directory.new()
- if !dir.dir_exists("res://texs"):
- dir.make_dir("texs")
- f.open("res://texs/.gdignore", File.WRITE) # touch .gdignore
- f.close()
+func _ready() -> void:
var bot := DiscordBot.new()
add_child(bot)
- var file = File.new()
- var err = file.open("res://token", File.READ)
- var token
+ var file := File.new()
+ var err := file.open("res://token", File.READ)
+ var token: String
if err == OK:
token = file.get_as_text()
elif OS.has_environment("TOKEN"):
@@ -50,13 +28,13 @@ func _ready():
bot.login()
-func _on_bot_ready(bot: DiscordBot):
+func _on_bot_ready(bot: DiscordBot) -> void:
bot.set_presence({"activity": {"type": "Game", "name": "Printing LaTeX"}})
print("Logged in as " + bot.user.username + "#" + bot.user.discriminator)
print("Listening on " + str(bot.channels.size()) + " channels and " + str(bot.guilds.size()) + " guilds.")
-func _on_message_create(bot: DiscordBot, message: Message, _channel: Dictionary):
+func _on_message_create(bot: DiscordBot, message: Message, _channel: Dictionary) -> void:
if message.author.bot:
return
var msg: String
@@ -73,61 +51,19 @@ func _on_message_create(bot: DiscordBot, message: Message, _channel: Dictionary)
msg = msg.strip_edges()
- print("----\n%s\n----" % msg)
if !msg:
return
-
- var th: Thread
- for thread in thread_pool:
- if !thread.is_alive():
- th = thread
- break
- if !th:
- thread_pool.append(Thread.new())
- th = thread_pool[-1]
-
- th.start(self, "latex2img", template_tex % msg)
- while true:
- yield(get_tree(), "idle_frame")
- if !th.is_alive():
- var img = th.wait_to_finish()
- if img is Dictionary:
- bot.reply(message, "No({err}): `{output}`".format(img))
- return
- bot.reply(
- message,
- "Tex:",
- {"files": [{"name": "code.png", "media_type": "image/png", "data": img.save_png_to_buffer()}]}
- )
- return
-
-
-func _notification(what):
- if what == NOTIFICATION_EXIT_TREE:
- OS.execute("bash", ["-c", "rm -r texs"], false)
-
-
-func latex2img(latex: String):
- randomize()
- var name := ("%s-%s" % [randi() % 201, randi() % 201]).c_escape()
- f.open("res://texs/%s.tex" % name, File.WRITE_READ)
- f.store_string(latex)
- f.close()
- var output: PoolStringArray = []
- var err = OS.execute(
- "bash", ["-c", "cd texs && latex -interaction=nonstopmode '%s.tex'" % name], true, output, true
- )
- if err:
- return {err = err, output = "(la)" + output.join(" ")}
- output.resize(0)
- var dvipng = [
- "-c",
- "dvipng -strict -bg Transparent --png -Q 250 -D 250 -T tight -o 'texs/%s.png' 'texs/%s.dvi'" % [name, name]
- ]
- err = OS.execute("bash", dvipng, true, output, true)
- if err:
- return {err = err, output = "(dvi)" + output.join(" ")}
- var img := Image.new()
- err = img.load("res://texs/%s.png" % name)
- OS.execute("bash", ["-c", "rm 'texs/%s.'*" % name], false)
- return img
+
+ print("----\n%s\n----" % msg)
+ var img := latex2img(msg)
+ bot.reply(message, "Tex:", {"files": [{"name": "code.png", "media_type": "image/png", "data": img}]})
+
+
+func latex2img(latex: String) -> PoolByteArray:
+ var tex := laTeXture.new()
+ tex.LatexExpression = latex
+ tex.MathColor = Color.white
+ tex.Fill = true
+ tex.FontSize = 80
+ tex.Render()
+ return tex.get_data().save_png_to_buffer()
diff --git a/README.md b/README.md
index 1e1708c..ccaa905 100644
--- a/README.md
+++ b/README.md
@@ -3,4 +3,8 @@
[![version](https://img.shields.io/badge/3.x-blue?logo=godot-engine&logoColor=white&label=godot&style=for-the-badge)](https://godotengine.org "Made with godot")
<a href='https://ko-fi.com/bendn' title='Buy me a coffee' target='_blank'><img height='28' src='https://storage.ko-fi.com/cdn/brandasset/kofi_button_red.png' alt='Buy me a coffee'> </a>
+![example](https://raw.githubusercontent.com/bend-n/latex-bot/main/.github/example.png)
+
+---
+
Discord bot for printing latex.
diff --git a/addons/GodoTeX/GodoTeX.cs b/addons/GodoTeX/GodoTeX.cs
new file mode 100644
index 0000000..a783376
--- /dev/null
+++ b/addons/GodoTeX/GodoTeX.cs
@@ -0,0 +1,33 @@
+#if TOOLS
+using Godot;
+using System;
+
+[Tool]
+public class GodoTeX : EditorPlugin {
+ public override void _EnterTree() {
+ var texture_grey = GD.Load<Texture>("addons/GodoTeX/iconGrey.svg");
+ var texture_script = GD.Load<Script>("addons/GodoTeX/LaTeXture.cs");
+ AddCustomType("LaTeXture", "ImageTexture", texture_script, texture_grey);
+
+ var texture_red = GD.Load<Texture>("addons/GodoTeX/iconRed.svg");
+ var spatial_script = GD.Load<Script>("addons/GodoTeX/LaTeX3D.cs");
+ AddCustomType("LaTeX3D", "Sprite3D", spatial_script, texture_red);
+
+ var texture = GD.Load<Texture>("addons/GodoTeX/icon.svg");
+ var script = GD.Load<Script>("addons/GodoTeX/LaTeX.cs");
+ AddCustomType("LaTeX", "Sprite", script, texture);
+
+ var texture_button = GD.Load<Texture>("addons/GodoTeX/iconButton.svg");
+ var button_script = GD.Load<Script>("addons/GodoTeX/LaTeXButton.cs");
+ AddCustomType("LaTeXButton", "TextureButton", button_script, texture_button);
+ }
+
+ public override void _ExitTree() {
+ RemoveCustomType("LaTeX");
+ RemoveCustomType("LaTeX3D");
+ RemoveCustomType("LaTeXture");
+ RemoveCustomType("LaTeXButton");
+ }
+}
+#endif
+
diff --git a/addons/GodoTeX/GodoTeX.csproj b/addons/GodoTeX/GodoTeX.csproj
new file mode 100644
index 0000000..bf0f1cc
--- /dev/null
+++ b/addons/GodoTeX/GodoTeX.csproj
@@ -0,0 +1,21 @@
+<Project Sdk="Godot.NET.Sdk/3.3.0">
+ <PropertyGroup>
+ <TargetFramework>net472</TargetFramework>
+ <RootNamespace>GodoTeX</RootNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="CSharpMath" Version="0.5.1" />
+ <PackageReference Include="CSharpMath.SkiaSharp" Version="0.5.1" />
+ </ItemGroup>
+
+ <ItemGroup Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.1-preview.108"/>
+ </ItemGroup>
+
+ <!-- This Include statement for MacOS is a complete blind guess and could not be tested-->
+ <ItemGroup Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::OSX)))' ">
+ <PackageReference Include="SkiaSharp.NativeAssets.macOS" Version="2.88.1-preview.108" />
+ </ItemGroup>
+
+</Project>
diff --git a/addons/GodoTeX/LaTeX.cs b/addons/GodoTeX/LaTeX.cs
new file mode 100644
index 0000000..2aefa1e
--- /dev/null
+++ b/addons/GodoTeX/LaTeX.cs
@@ -0,0 +1,99 @@
+using Godot;
+
+
+[Tool]
+public class LaTeX : Sprite {
+ // The following wordy declarations ensure that changing the properties
+ // inside the editor causes the expression to re-render.
+
+ public string LatexExpression;
+ [Export(PropertyHint.MultilineText)]
+ private string _latexExpression {
+ get {return LatexExpression;}
+ set {
+ // This runs when LatexExpression is set in the editor.
+
+ LatexExpression = value;
+ Render();
+ }
+ }
+
+ public float FontSize = 40f;
+ [Export(PropertyHint.Range, "10,60,1,or_greater,or_lesser")]
+ private float _fontSize {
+ get {return FontSize;}
+ set {
+ FontSize = value;
+ Render();
+ }
+ }
+
+ public Color MathColor = new Color(0,0,0,1);
+ [Export]
+ private Color _mathColor {
+ get {return MathColor;}
+ set {
+ MathColor = value;
+ Render();
+ }
+ }
+
+ public bool AntiAliasing = true;
+ [Export]
+ private bool _antiAliasing {
+ get {return AntiAliasing;}
+ set {
+ AntiAliasing = value;
+ Render();
+ }
+ }
+
+ public bool Fill = true;
+ [Export]
+ private bool _fill {
+ get {return Fill;}
+ set {
+ Fill = value;
+ Render();
+ }
+ }
+
+ public bool ShowError = true;
+ [Export]
+ private bool _showError {
+ get {return ShowError;}
+ set {
+ ShowError = value;
+ Render();
+ }
+ }
+
+ // These are the measures of the generated image.
+ public float Width;
+ public float Height;
+ public float OffsetX;
+ public float OffsetY;
+
+ // You may call this from outside to re-render the expression.
+ // This is not done automatically so that you can change the values very
+ // often, say multiple times a millisecond, without significant slowdown.
+ public void Render() {
+ var texture = new LaTeXture();
+ texture.LatexExpression = this.LatexExpression;
+ texture.FontSize = this.FontSize;
+ texture.AntiAliasing = this.AntiAliasing;
+ texture.Fill = this.Fill;
+ texture.MathColor = this.MathColor;
+ texture.ShowError = this.ShowError;
+ texture.Render();
+ this.Texture = texture;
+ this.Width = texture.Width;
+ this.Height = texture.Height;
+ this.OffsetX = texture.OffsetX;
+ this.OffsetY = texture.OffsetY;
+ }
+
+ public override void _Ready() {
+ Render();
+ }
+}
diff --git a/addons/GodoTeX/LaTeX3D.cs b/addons/GodoTeX/LaTeX3D.cs
new file mode 100644
index 0000000..b3a9de4
--- /dev/null
+++ b/addons/GodoTeX/LaTeX3D.cs
@@ -0,0 +1,98 @@
+using Godot;
+
+[Tool]
+public class LaTeX3D : Sprite3D {
+ // The following wordy declarations ensure that changing the properties
+ // inside the editor causes the expression to re-render.
+
+ public string LatexExpression;
+ [Export(PropertyHint.MultilineText)]
+ private string _latexExpression {
+ get {return LatexExpression;}
+ set {
+ // This runs when LatexExpression is set in the editor.
+
+ LatexExpression = value;
+ Render();
+ }
+ }
+
+ public float FontSize = 40f;
+ [Export(PropertyHint.Range, "10,60,1,or_greater,or_lesser")]
+ private float _fontSize {
+ get {return FontSize;}
+ set {
+ FontSize = value;
+ Render();
+ }
+ }
+
+ public Color MathColor = new Color(0,0,0,1);
+ [Export]
+ private Color _mathColor {
+ get {return MathColor;}
+ set {
+ MathColor = value;
+ Render();
+ }
+ }
+
+ public bool AntiAliasing = true;
+ [Export]
+ private bool _antiAliasing {
+ get {return AntiAliasing;}
+ set {
+ AntiAliasing = value;
+ Render();
+ }
+ }
+
+ public bool Fill = true;
+ [Export]
+ private bool _fill {
+ get {return Fill;}
+ set {
+ Fill = value;
+ Render();
+ }
+ }
+
+ public bool ShowError = true;
+ [Export]
+ private bool _showError {
+ get {return ShowError;}
+ set {
+ ShowError = value;
+ Render();
+ }
+ }
+
+ // These are the measures of the generated image.
+ public float Width;
+ public float Height;
+ public float OffsetX;
+ public float OffsetY;
+
+ // You may call this from outside to re-render the expression.
+ // This is not done automatically so that you can change the values very
+ // often, say multiple times a millisecond, without significant slowdown.
+ public void Render() {
+ var texture = new LaTeXture();
+ texture.LatexExpression = this.LatexExpression;
+ texture.FontSize = this.FontSize;
+ texture.AntiAliasing = this.AntiAliasing;
+ texture.Fill = this.Fill;
+ texture.MathColor = this.MathColor;
+ texture.ShowError = this.ShowError;
+ texture.Render();
+ this.Texture = texture;
+ this.Width = texture.Width;
+ this.Height = texture.Height;
+ this.OffsetX = texture.OffsetX;
+ this.OffsetY = texture.OffsetY;
+ }
+
+ public override void _Ready() {
+ Render();
+ }
+}
diff --git a/addons/GodoTeX/LaTeXButton.cs b/addons/GodoTeX/LaTeXButton.cs
new file mode 100644
index 0000000..166a6c7
--- /dev/null
+++ b/addons/GodoTeX/LaTeXButton.cs
@@ -0,0 +1,105 @@
+using Godot;
+
+[Tool]
+public class LaTeXButton : TextureButton {
+ // The following wordy declarations ensure that changing the properties
+ // inside the editor causes the expression to re-render.
+
+ public string LatexExpression;
+ [Export(PropertyHint.MultilineText)]
+ private string _latexExpression {
+ get {return LatexExpression;}
+ set {
+ // This runs when LatexExpression is set in the editor.
+
+ LatexExpression = value;
+ Render();
+ }
+ }
+
+ public float FontSize = 40f;
+ [Export(PropertyHint.Range, "10,60,1,or_greater,or_lesser")]
+ private float _fontSize {
+ get {return FontSize;}
+ set {
+ FontSize = value;
+ Render();
+ }
+ }
+
+ public Color MathColor = new Color(0,0,0,1);
+ [Export]
+ private Color _mathColor {
+ get {return MathColor;}
+ set {
+ MathColor = value;
+ Render();
+ }
+ }
+
+ public bool AntiAliasing = true;
+ [Export]
+ private bool _antiAliasing {
+ get {return AntiAliasing;}
+ set {
+ AntiAliasing = value;
+ Render();
+ }
+ }
+
+ public bool ShowError = true;
+ [Export]
+ private bool _showError {
+ get {return ShowError;}
+ set {
+ ShowError = value;
+ Render();
+ }
+ }
+
+ public void Render() {
+ var texture = new LaTeXture();
+ texture.LatexExpression = this.LatexExpression;
+ texture.FontSize = this.FontSize;
+ texture.AntiAliasing = this.AntiAliasing;
+ texture.Fill = false;
+ texture.MathColor = this.MathColor;
+ texture.ShowError = this.ShowError;
+ texture.Render();
+
+ this.TextureNormal = texture;
+
+ var texture2 = new LaTeXture();
+ texture2.LatexExpression = this.LatexExpression;
+ texture2.FontSize = this.FontSize;
+ texture2.AntiAliasing = this.AntiAliasing;
+ texture2.Fill = true;
+ texture2.MathColor = this.MathColor;
+ texture2.ShowError = this.ShowError;
+ texture2.Render();
+
+ this.TextureHover = texture2;
+
+ // A bit of a hack, we increase the top spacing in the LaTeX expression
+ // to give a 'pressed down' effect.
+ var texture3 = new LaTeXture();
+ texture3.LatexExpression = @"\raisebox{41mu}{}" + this.LatexExpression;
+ texture3.FontSize = this.FontSize;
+ texture3.AntiAliasing = this.AntiAliasing;
+ texture3.Fill = true;
+ texture3.MathColor = this.MathColor;
+ texture3.ShowError = this.ShowError;
+ texture3.Render();
+
+ this.TexturePressed = texture3;
+
+ var clickMask = new BitMap();
+ clickMask.Create(new Vector2(texture.Width, texture.Height));
+ clickMask.SetBitRect(new Rect2(0, 35, texture.Width, texture.Height - 70), true);
+ this.TextureClickMask = clickMask;
+ }
+
+ public override void _Ready() {
+ Render();
+ }
+}
diff --git a/addons/GodoTeX/LaTeXture.cs b/addons/GodoTeX/LaTeXture.cs
new file mode 100644
index 0000000..87bd293
--- /dev/null
+++ b/addons/GodoTeX/LaTeXture.cs
@@ -0,0 +1,53 @@
+using Godot;
+using System.IO;
+using CSharpMath.SkiaSharp;
+using CSharpMath.Rendering;
+using SkiaSharp;
+
+[Tool]
+public class LaTeXture : ImageTexture {
+ public string LatexExpression;
+ public float FontSize = 40f;
+ public Color MathColor = new Color(0,0,0,1);
+ public bool AntiAliasing = true;
+ public bool Fill = true;
+ public bool ShowError = true;
+
+ private MathPainter Painter = new MathPainter{};
+
+ public float Width;
+ public float Height;
+ public float OffsetX;
+ public float OffsetY;
+
+ public void Render() {
+ var r = (byte)(255*this.MathColor.r);
+ var g = (byte)(255*this.MathColor.g);
+ var b = (byte)(255*this.MathColor.b);
+ var a = (byte)(255*this.MathColor.a);
+
+ var paintStyle = CSharpMath.Rendering.FrontEnd.PaintStyle.Stroke;
+ if (this.Fill) {
+ paintStyle = CSharpMath.Rendering.FrontEnd.PaintStyle.Fill;
+ }
+
+ this.Painter = new MathPainter {AntiAlias = this.AntiAliasing, TextColor = new SKColor(r, g, b, a), FontSize = this.FontSize, LaTeX = @"\raisebox{40mu}{}\raisebox{-40mu}{}" + this.LatexExpression + @"\:\raisebox{1mu}", DisplayErrorInline = this.ShowError, PaintStyle = paintStyle};
+
+ var measure = this.Painter.Measure();
+ this.Width = measure.Width;
+ this.Height = measure.Height;
+ this.OffsetX = measure.X;
+ this.OffsetY = measure.Y;
+
+ using (var png = this.Painter.DrawAsStream()) {
+ var image = new Godot.Image();
+
+ using (var memoryStream = new MemoryStream()) {
+ png.CopyTo(memoryStream);
+ image.LoadPngFromBuffer(memoryStream.ToArray());
+ }
+
+ this.CreateFromImage(image);
+ };
+ }
+}
diff --git a/addons/GodoTeX/icon.svg b/addons/GodoTeX/icon.svg
new file mode 100644
index 0000000..4a43173
--- /dev/null
+++ b/addons/GodoTeX/icon.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 -706.60651 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m128 619q-7 7-11 9-4 2-16 3-12 1-43 3h-33v46h529v-4q2-6 14-116 12-110 14-116v-4h-40v4q0 1-4 34-4 33-15 67-11 34-31 53-38 36-143 36h-15q-70 0-85-1-15-1-16-12-1-3-1-282l1-278q7-7 12-9 5-2 25-4 20-2 63-2h27v-46h-12q-24 3-166 3-131 0-146-3h-11v46h33q42 1 51 3 9 2 19 12z" fill="#8ca4f3" transform="matrix(.01834526 0 0 -.01834526 2.718073 -692.23766)"/></svg> \ No newline at end of file
diff --git a/addons/GodoTeX/icon.svg.import b/addons/GodoTeX/icon.svg.import
new file mode 100644
index 0000000..4611ddd
--- /dev/null
+++ b/addons/GodoTeX/icon.svg.import
@@ -0,0 +1,38 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path.s3tc="res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.s3tc.stex"
+path.etc2="res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.etc2.stex"
+path.etc="res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.etc.stex"
+metadata={
+"imported_formats": [ "s3tc", "etc2", "etc" ],
+"vram_texture": true
+}
+
+[deps]
+
+source_file="res://addons/GodoTeX/icon.svg"
+dest_files=[ "res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.s3tc.stex", "res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.etc2.stex", "res://.import/icon.svg-23b8536dd78fd33b44f4332a84a25246.etc.stex" ]
+
+[params]
+
+compress/mode=2
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=true
+flags/filter=true
+flags/mipmaps=true
+flags/anisotropic=false
+flags/srgb=1
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=false
+svg/scale=1.0
diff --git a/addons/GodoTeX/iconButton.svg b/addons/GodoTeX/iconButton.svg
new file mode 100644
index 0000000..db0873b
--- /dev/null
+++ b/addons/GodoTeX/iconButton.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><g fill="#8eef97"><path d="m5.8327617 9.7013925c-.7207587 0-1.3018428.5810885-1.3018428 1.3018425v1.298843h-1.7337907v1.736791h10.4057438v-1.736791h-1.733792v-1.298843c0-.720754-.581088-1.3018425-1.301842-1.3018425z" stroke-width=".867735"/><path d="m7.0957548 2.6994227q-.0758199-.0758199-.1191456-.0974827-.0433256-.0216628-.1733025-.0324943-.129977-.0108314-.4657508-.0324942h-.3574366v-.498245h5.7298167v.043326q.02166.064988.15164 1.2564438.129977 1.1914554.15164 1.2564439v.043326h-.433257v-.043326q0-.010831-.04333-.3682681-.04332-.3574369-.162467-.7257049-.119145-.368268-.335773-.5740649-.411594-.3899308-1.5488921-.3899308h-.1624712q-.7581989 0-.9206701.010831-.1624712.010831-.1733025.1299769-.010831.032494-.010831 3.0544583l.010831 3.0111331q.07582.07582.1299769.097482.054157.021662.2707853.043325.2166283.021662.682379.021662h.2924481v.4982475h-.1299769q-.259954-.032497-1.7980145-.032497-1.418915 0-1.5813861.032497h-.1191456v-.498245h.3574366q.4549193-.010834.552402-.032497.097483-.021662.2057969-.1299769z" stroke-width=".010831"/></g></svg> \ No newline at end of file
diff --git a/addons/GodoTeX/iconButton.svg.import b/addons/GodoTeX/iconButton.svg.import
new file mode 100644
index 0000000..58dc5e5
--- /dev/null
+++ b/addons/GodoTeX/iconButton.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/iconButton.svg-299f9c2f548f426910b50b78618b5517.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/GodoTeX/iconButton.svg"
+dest_files=[ "res://.import/iconButton.svg-299f9c2f548f426910b50b78618b5517.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/GodoTeX/iconGrey.svg b/addons/GodoTeX/iconGrey.svg
new file mode 100644
index 0000000..a7f42b7
--- /dev/null
+++ b/addons/GodoTeX/iconGrey.svg
@@ -0,0 +1 @@
+<svg height="16" viewBox="0 -706.60651 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="m128 619q-7 7-11 9-4 2-16 3-12 1-43 3h-33v46h529v-4q2-6 14-116 12-110 14-116v-4h-40v4q0 1-4 34-4 33-15 67-11 34-31 53-38 36-143 36h-15q-70 0-85-1-15-1-16-12-1-3-1-282l1-278q7-7 12-9 5-2 25-4 20-2 63-2h27v-46h-12q-24 3-166 3-131 0-146-3h-11v46h33q42 1 51 3 9 2 19 12z" fill="#e0e0e0" transform="matrix(.01834526 0 0 -.01834526 2.718073 -692.23766)"/></svg> \ No newline at end of file
diff --git a/addons/GodoTeX/iconGrey.svg.import b/addons/GodoTeX/iconGrey.svg.import
new file mode 100644
index 0000000..230fe1a
--- /dev/null
+++ b/addons/GodoTeX/iconGrey.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/iconGrey.svg-d7f71b9d92f2105081bc8ed7609ec7db.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/GodoTeX/iconGrey.svg"
+dest_files=[ "res://.import/iconGrey.svg-d7f71b9d92f2105081bc8ed7609ec7db.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/GodoTeX/iconRed.svg b/addons/GodoTeX/iconRed.svg
new file mode 100644
index 0000000..6543c70
--- /dev/null
+++ b/addons/GodoTeX/iconRed.svg
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ height="16"
+ viewBox="0 -706.60651 16 16"
+ width="16"
+ version="1.1"
+ id="svg4"
+ sodipodi:docname="iconRed.svg"
+ inkscape:version="1.2 (dc2aedaf03, 2022-05-15)"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:svg="http://www.w3.org/2000/svg">
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ id="namedview6"
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1.0"
+ inkscape:showpageshadow="2"
+ inkscape:pageopacity="0.0"
+ inkscape:pagecheckerboard="0"
+ inkscape:deskcolor="#d1d1d1"
+ showgrid="false"
+ inkscape:zoom="52.3125"
+ inkscape:cx="8.0191159"
+ inkscape:cy="8.0095579"
+ inkscape:window-width="1920"
+ inkscape:window-height="1017"
+ inkscape:window-x="-8"
+ inkscape:window-y="-8"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg4" />
+ <path
+ d="m128 619q-7 7-11 9-4 2-16 3-12 1-43 3h-33v46h529v-4q2-6 14-116 12-110 14-116v-4h-40v4q0 1-4 34-4 33-15 67-11 34-31 53-38 36-143 36h-15q-70 0-85-1-15-1-16-12-1-3-1-282l1-278q7-7 12-9 5-2 25-4 20-2 63-2h27v-46h-12q-24 3-166 3-131 0-146-3h-11v46h33q42 1 51 3 9 2 19 12z"
+ fill="#e0e0e0"
+ transform="matrix(.01834526 0 0 -.01834526 2.718073 -692.23766)"
+ id="path2"
+ style="fill:#fb7e7e;fill-opacity:1" />
+</svg>
diff --git a/addons/GodoTeX/iconRed.svg.import b/addons/GodoTeX/iconRed.svg.import
new file mode 100644
index 0000000..5388489
--- /dev/null
+++ b/addons/GodoTeX/iconRed.svg.import
@@ -0,0 +1,35 @@
+[remap]
+
+importer="texture"
+type="StreamTexture"
+path="res://.import/iconRed.svg-c5d138126a0c1922a0b6358d3c63aec4.stex"
+metadata={
+"vram_texture": false
+}
+
+[deps]
+
+source_file="res://addons/GodoTeX/iconRed.svg"
+dest_files=[ "res://.import/iconRed.svg-c5d138126a0c1922a0b6358d3c63aec4.stex" ]
+
+[params]
+
+compress/mode=0
+compress/lossy_quality=0.7
+compress/hdr_mode=0
+compress/bptc_ldr=0
+compress/normal_map=0
+flags/repeat=0
+flags/filter=true
+flags/mipmaps=false
+flags/anisotropic=false
+flags/srgb=2
+process/fix_alpha_border=true
+process/premult_alpha=false
+process/HDR_as_SRGB=false
+process/invert_color=false
+process/normal_map_invert_y=false
+stream=false
+size_limit=0
+detect_3d=true
+svg/scale=1.0
diff --git a/addons/GodoTeX/plugin.cfg b/addons/GodoTeX/plugin.cfg
new file mode 100644
index 0000000..dc91cd1
--- /dev/null
+++ b/addons/GodoTeX/plugin.cfg
@@ -0,0 +1,7 @@
+[plugin]
+
+name="GodoTeX"
+description="LaTeX rendering in the Godot Engine"
+author="fi-le"
+version="0.0.2"
+script="GodoTeX.cs"
diff --git a/latex bot.csproj b/latex bot.csproj
new file mode 100644
index 0000000..2a3af0b
--- /dev/null
+++ b/latex bot.csproj
@@ -0,0 +1,15 @@
+<Project Sdk="Godot.NET.Sdk/3.3.0">
+ <PropertyGroup>
+ <TargetFramework>net472</TargetFramework>
+ <RootNamespace>latexbot</RootNamespace>
+ </PropertyGroup>
+
+ <ItemGroup>
+ <PackageReference Include="CSharpMath" Version="0.5.1" />
+ <PackageReference Include="CSharpMath.SkiaSharp" Version="0.5.1" />
+ </ItemGroup>
+
+ <ItemGroup Condition=" '$([System.Runtime.InteropServices.RuntimeInformation]::IsOSPlatform($([System.Runtime.InteropServices.OSPlatform]::Linux)))'">
+ <PackageReference Include="SkiaSharp.NativeAssets.Linux.NoDependencies" Version="2.88.1-preview.108" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/latex bot.sln b/latex bot.sln
new file mode 100644
index 0000000..4586c52
--- /dev/null
+++ b/latex bot.sln
@@ -0,0 +1,19 @@
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "latex bot", "latex bot.csproj", "{1634190E-8C4D-4F57-AA45-993D45035766}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ ExportDebug|Any CPU = ExportDebug|Any CPU
+ ExportRelease|Any CPU = ExportRelease|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1634190E-8C4D-4F57-AA45-993D45035766}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1634190E-8C4D-4F57-AA45-993D45035766}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1634190E-8C4D-4F57-AA45-993D45035766}.ExportDebug|Any CPU.ActiveCfg = ExportDebug|Any CPU
+ {1634190E-8C4D-4F57-AA45-993D45035766}.ExportDebug|Any CPU.Build.0 = ExportDebug|Any CPU
+ {1634190E-8C4D-4F57-AA45-993D45035766}.ExportRelease|Any CPU.ActiveCfg = ExportRelease|Any CPU
+ {1634190E-8C4D-4F57-AA45-993D45035766}.ExportRelease|Any CPU.Build.0 = ExportRelease|Any CPU
+ EndGlobalSection
+EndGlobal
diff --git a/project.godot b/project.godot
index f1f35ec..a2e1d17 100644
--- a/project.godot
+++ b/project.godot
@@ -92,10 +92,10 @@ _global_script_class_icons={
[application]
-config/name="Godot Template"
+config/name="latex bot"
run/main_scene="res://Latex.tscn"
config/use_custom_user_dir=true
-config/custom_user_dir_name="GodotTemplate"
+config/custom_user_dir_name="latex-bot"
[debug]