From 002fdf0b42ae7a93477d2848a70c5981aecb2ea3 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Fri, 17 May 2024 15:59:23 -0500
Subject: [PATCH 01/48] Initial Commit. Floof Chat moved to script archive.
 Fixed local message distance check.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../chat/FloofChat.html                       |   0
 .../chat/FloofChat.js                         |   0
 .../chat/FloofChat.qml                        |   0
 .../chat/chat.png                             | Bin
 .../chat/css/FloofChat.css                    |   0
 .../chat/css/materialize.css                  |   0
 .../chat/js/materialize.min.js                |   0
 .../chat/resources/bubblepop.wav              | Bin
 .../armored-chat/armored_chat.js              | 200 +++++++++
 .../armored-chat/armored_chat.qml             | 403 ++++++++++++++++++
 .../armored-chat/img/icon_black.png           | Bin 0 -> 400 bytes
 .../armored-chat/img/icon_white.png           | Bin 0 -> 778 bytes
 .../armored-chat/img/ui/send.svg              |  42 ++
 .../armored-chat/img/ui/send_black.png        | Bin 0 -> 1950 bytes
 .../armored-chat/img/ui/send_white.png        | Bin 0 -> 1956 bytes
 .../armored-chat/img/ui/settings_black.png    | Bin 0 -> 1561 bytes
 .../armored-chat/img/ui/settings_white.png    | Bin 0 -> 1568 bytes
 .../armored-chat/img/ui/social_black.png      | Bin 0 -> 3485 bytes
 .../armored-chat/img/ui/social_white.png      | Bin 0 -> 3491 bytes
 .../armored-chat/img/ui/world_black.png       | Bin 0 -> 4066 bytes
 .../armored-chat/img/ui/world_white.png       | Bin 0 -> 3960 bytes
 scripts/defaultScripts.js                     |   4 +-
 22 files changed, 647 insertions(+), 2 deletions(-)
 rename {scripts/communityScripts => script-archive}/chat/FloofChat.html (100%)
 rename {scripts/communityScripts => script-archive}/chat/FloofChat.js (100%)
 rename {scripts/communityScripts => script-archive}/chat/FloofChat.qml (100%)
 rename {scripts/communityScripts => script-archive}/chat/chat.png (100%)
 rename {scripts/communityScripts => script-archive}/chat/css/FloofChat.css (100%)
 rename {scripts/communityScripts => script-archive}/chat/css/materialize.css (100%)
 rename {scripts/communityScripts => script-archive}/chat/js/materialize.min.js (100%)
 rename {scripts/communityScripts => script-archive}/chat/resources/bubblepop.wav (100%)
 create mode 100644 scripts/communityScripts/armored-chat/armored_chat.js
 create mode 100644 scripts/communityScripts/armored-chat/armored_chat.qml
 create mode 100644 scripts/communityScripts/armored-chat/img/icon_black.png
 create mode 100644 scripts/communityScripts/armored-chat/img/icon_white.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/send.svg
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/send_black.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/send_white.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/settings_black.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/settings_white.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/social_black.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/social_white.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/world_black.png
 create mode 100644 scripts/communityScripts/armored-chat/img/ui/world_white.png

diff --git a/scripts/communityScripts/chat/FloofChat.html b/script-archive/chat/FloofChat.html
similarity index 100%
rename from scripts/communityScripts/chat/FloofChat.html
rename to script-archive/chat/FloofChat.html
diff --git a/scripts/communityScripts/chat/FloofChat.js b/script-archive/chat/FloofChat.js
similarity index 100%
rename from scripts/communityScripts/chat/FloofChat.js
rename to script-archive/chat/FloofChat.js
diff --git a/scripts/communityScripts/chat/FloofChat.qml b/script-archive/chat/FloofChat.qml
similarity index 100%
rename from scripts/communityScripts/chat/FloofChat.qml
rename to script-archive/chat/FloofChat.qml
diff --git a/scripts/communityScripts/chat/chat.png b/script-archive/chat/chat.png
similarity index 100%
rename from scripts/communityScripts/chat/chat.png
rename to script-archive/chat/chat.png
diff --git a/scripts/communityScripts/chat/css/FloofChat.css b/script-archive/chat/css/FloofChat.css
similarity index 100%
rename from scripts/communityScripts/chat/css/FloofChat.css
rename to script-archive/chat/css/FloofChat.css
diff --git a/scripts/communityScripts/chat/css/materialize.css b/script-archive/chat/css/materialize.css
similarity index 100%
rename from scripts/communityScripts/chat/css/materialize.css
rename to script-archive/chat/css/materialize.css
diff --git a/scripts/communityScripts/chat/js/materialize.min.js b/script-archive/chat/js/materialize.min.js
similarity index 100%
rename from scripts/communityScripts/chat/js/materialize.min.js
rename to script-archive/chat/js/materialize.min.js
diff --git a/scripts/communityScripts/chat/resources/bubblepop.wav b/script-archive/chat/resources/bubblepop.wav
similarity index 100%
rename from scripts/communityScripts/chat/resources/bubblepop.wav
rename to script-archive/chat/resources/bubblepop.wav
diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
new file mode 100644
index 0000000000..40c4510409
--- /dev/null
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -0,0 +1,200 @@
+//
+//  armored_chat.js
+//
+//  Created by Armored Dragon, 2024.
+//  Copyright 2024 Overte e.V.
+//
+//  Distributed under the Apache License, Version 2.0.
+//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+
+(() => {
+    "use strict";
+
+    var app_is_visible = false;
+    var settings = {
+        compact_chat: false,
+        external_window: false,
+    };
+
+    // Global vars
+    var tablet;
+    var chat_overlay_window;
+    var app_button;
+    const channels = ["domain", "local"];
+    var message_history = Settings.getValue("ArmoredChat-Messages", []);
+    var max_local_distance = 20; // Maximum range for the local chat
+    var pal_data = AvatarManager.getPalData().data;
+
+    Messages.subscribe("chat");
+    Messages.messageReceived.connect(receivedMessage);
+    AvatarManager.avatarAddedEvent.connect((session_id) => {
+        _avatarAction("connected", session_id);
+    });
+    AvatarManager.avatarRemovedEvent.connect((session_id) => {
+        _avatarAction("left", session_id);
+    });
+
+    startup();
+
+    function startup() {
+        tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
+
+        app_button = tablet.addButton({
+            icon: Script.resolvePath("./img/icon_white.png"),
+            activeIcon: Script.resolvePath("./img/icon_black.png"),
+            text: "CHAT",
+            isActive: app_is_visible,
+        });
+
+        // When script ends, remove itself from tablet
+        Script.scriptEnding.connect(function () {
+            console.log("Shutting Down");
+            tablet.removeButton(app_button);
+            chat_overlay_window.close();
+        });
+
+        // Overlay button toggle
+        app_button.clicked.connect(toggleMainChatWindow);
+
+        _openWindow();
+    }
+    function toggleMainChatWindow() {
+        app_is_visible = !app_is_visible;
+        console.log(`App is now ${app_is_visible ? "visible" : "hidden"}`);
+        app_button.editProperties({ isActive: app_is_visible });
+        chat_overlay_window.visible = app_is_visible;
+
+        // External window was closed; the window does not exist anymore
+        if (chat_overlay_window.title == "" && app_is_visible) {
+            _openWindow();
+        }
+    }
+    function _openWindow() {
+        chat_overlay_window = new Desktop.createWindow(
+            Script.resolvePath("./armored_chat.qml"),
+            {
+                title: "Chat",
+                size: { x: 550, y: 400 },
+                additionalFlags: Desktop.ALWAYS_ON_TOP,
+                visible: app_is_visible,
+                presentationMode: Desktop.PresentationMode.VIRTUAL,
+            }
+        );
+
+        chat_overlay_window.closed.connect(toggleMainChatWindow);
+        chat_overlay_window.fromQml.connect(fromQML);
+    }
+    function receivedMessage(channel, message) {
+        // Is the message a chat message?
+        channel = channel.toLowerCase();
+        if (channel !== "chat") return;
+        console.log(`Received message:\n${message}`);
+        var message = JSON.parse(message);
+
+        message.channel = message.channel.toLowerCase(); // Make sure the "local", "domain", etc. is formatted consistently
+
+        if (!channels.includes(message.channel)) return; // Check the channel
+        if (
+            message.channel == "local" &&
+            Vec3.distance(MyAvatar.position, message.position) >
+                max_local_distance
+        )
+            return; // If message is local, and if player is too far away from location, don't do anything
+
+        // Update qml view of to new message
+        _emitEvent({ type: "show_message", ...message });
+
+        Messages.sendLocalMessage(
+            "Floof-Notif",
+            JSON.stringify({
+                sender: message.displayName,
+                text: message.message,
+            })
+        );
+
+        // Save message to history
+        let saved_message = message;
+        delete saved_message.position;
+        saved_message.timeString = new Date().toLocaleTimeString(undefined, {
+            hour12: false,
+        });
+        saved_message.dateString = new Date().toLocaleDateString(undefined, {
+            month: "long",
+            day: "numeric",
+        });
+        message_history.push(saved_message);
+        if (message_history.length > settings.max_history)
+            message_history.shift();
+        Settings.setValue("ArmoredChat-Messages", message_history);
+    }
+
+    function fromQML(event) {
+        console.log(`New web event:\n${JSON.stringify(event)}`);
+
+        switch (event.type) {
+            case "send_message":
+                _sendMessage(event.message, event.channel);
+                break;
+            case "initialized":
+                // https://github.com/overte-org/overte/issues/824
+                chat_overlay_window.visible = app_is_visible; // The "visible" field in the Desktop.createWindow does not seem to work. Force set it to the initial state (false)
+                _loadSettings();
+                break;
+        }
+    }
+    function _sendMessage(message, channel) {
+        Messages.sendMessage(
+            "chat",
+            JSON.stringify({
+                position: MyAvatar.position,
+                message: message,
+                displayName: MyAvatar.sessionDisplayName,
+                channel: channel,
+                action: "send_chat_message",
+            })
+        );
+    }
+    function _avatarAction(type, session_id) {
+        Script.setTimeout(() => {
+            if (type == "connected") {
+                pal_data = AvatarManager.getPalData().data;
+            }
+
+            // Get the display name of the user
+            let display_name = "";
+            display_name =
+                AvatarManager.getPalData([session_id])?.data[0]
+                    ?.sessionDisplayName || null;
+            if (display_name == null) {
+                for (let i = 0; i < pal_data.length; i++) {
+                    if (pal_data[i].sessionUUID == session_id) {
+                        display_name = pal_data[i].sessionDisplayName;
+                    }
+                }
+            }
+
+            // Format the packet
+            let message = {};
+            message.message = `${display_name} ${type}`;
+
+            _emitEvent({ type: "avatar_connected", ...message });
+        }, 1500);
+    }
+
+    function _loadSettings() {
+        message_history.forEach((message) => {
+            delete message.action;
+            _emitEvent({ type: "show_message", ...message });
+        });
+    }
+    function _saveSettings() {}
+
+    /**
+     * Emit a packet to the HTML front end. Easy communication!
+     * @param {Object} packet - The Object packet to emit to the HTML
+     * @param {("setting_update"|"show_message")} packet.type - The type of packet it is
+     */
+    function _emitEvent(packet = { type: "" }) {
+        chat_overlay_window.sendToQml(packet);
+    }
+})();
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
new file mode 100644
index 0000000000..29f43a4616
--- /dev/null
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -0,0 +1,403 @@
+import QtQuick 2.7
+import QtQuick.Controls 2.0
+import QtQuick.Layouts 1.3
+
+Rectangle {
+    color: Qt.rgba(0.1,0.1,0.1,1)
+    signal sendToScript(var message);
+
+    property string pageVal: "local"
+    property string last_message_user: ""
+    property date last_message_time: new Date()
+
+    // TODO: Find a better way to do this
+    // When the window is created on the script side, the window starts open.
+    // Once the QML window is created wait, then send the initialized signal.
+    // This signal is mostly used to close the "Desktop overlay window" script side
+    // https://github.com/overte-org/overte/issues/824
+    Timer {
+        interval: 100
+        running: true
+        onTriggered: {
+           toScript({type: "initialized"})
+        }
+    }
+    // Component.onCompleted: {
+    //     toScript({type: "initialized"})
+    // }
+
+    Column {
+        anchors.fill: parent
+        spacing: 0
+
+        // Navigation Bar
+        Rectangle {
+            id: navigation_bar
+            width: parent.width
+            height: 40
+            color:Qt.rgba(0,0,0,1)
+
+            Item {
+                height: parent.height
+                width: parent.width
+                anchors.fill: parent
+
+                Rectangle {
+                    width: pageVal === "local" ? 100 : 60
+                    height: parent.height
+                    color: pageVal === "local" ? "#505186" : "white"
+                    id: local_page
+
+                    Image {
+                        source: "./img/ui/" + (pageVal === "local" ? "social_white.png" : "social_black.png")
+                        sourceSize.width: 40
+                        sourceSize.height: 40
+                        anchors.centerIn: parent
+                    }
+
+                    Behavior on width {
+                        NumberAnimation {
+                            duration: 50
+                            // easing.type: Easeing.InOutQuad
+                        }
+                    }
+
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            pageVal = "local";
+                        }
+                    }
+                }
+                Rectangle {
+                    width: pageVal === "domain" ? 100 : 60
+                    height: parent.height
+                    color: pageVal === "domain" ? "#505186" : "white"
+                    anchors.left: local_page.right
+                    anchors.leftMargin: 5
+                    id: domain_page
+
+                    Image {
+                        source: "./img/ui/" + (pageVal === "domain" ? "world_white.png" : "world_black.png")
+                        sourceSize.width: 30
+                        sourceSize.height: 30
+                        anchors.centerIn: parent
+                    }
+
+                    Behavior on width {
+                        NumberAnimation {
+                            duration: 50
+                        }
+                    }
+
+                    MouseArea {
+                        anchors.fill: parent
+                        onClicked: {
+                            // addMessage("usertest", "Clicked", "Now", "domain", "notification");
+                            pageVal = "domain"
+                        }
+                    }
+                }
+
+                Rectangle {
+                        width: pageVal === "settings" ? 100 : 60
+                        height: parent.height
+                        color: pageVal === "settings" ? "#505186" : "white"
+                        anchors.right: parent.right
+                        id: settings_page
+
+                        Image {
+                            source: "./img/ui/" + (pageVal === "settings" ? "settings_white.png" : "settings_black.png")
+                            sourceSize.width: 30
+                            sourceSize.height: 30
+                            anchors.centerIn: parent
+                        }
+
+                        Behavior on width {
+                        NumberAnimation {
+                            duration: 50
+                        }
+                        }
+
+                        MouseArea {
+                            anchors.fill: parent
+                            onClicked: {
+                                pageVal = "settings"
+                            }
+                        }
+                    }
+                }
+
+        }
+
+        // Pages
+        Item {
+            width: parent.width
+            height: parent.height - 40
+            anchors.top: navigation_bar.bottom
+
+
+            // Chat Message History
+            ListView {
+                width: parent.width
+                height: parent.height - 40
+                clip: true
+                interactive: true
+                spacing: 5
+                id: listview
+
+                delegate: Loader {
+                    width: parent.width
+                    property int delegateIndex: index
+                    property string delegateText: model.text
+                    property string delegateUsername: model.username
+                    property string delegateDate: model.date
+
+                    sourceComponent: {
+                        if (model.type === "chat") {
+                            return template_chat_message;
+                        } else if (model.type === "notification") {
+                            return template_notification;
+                        }
+                    }
+                }
+
+                model: getChannel(pageVal)
+
+            }
+
+            ListModel {
+                id: local
+            }
+
+            ListModel {
+                id: domain
+             }
+
+            // Chat Entry
+            Rectangle {
+                width: parent.width
+                height: 40
+                color: Qt.rgba(0.9,0.9,0.9,1)
+                anchors.bottom: parent.bottom
+                visible: ["local", "domain"].includes(pageVal) ? true : false
+
+                Row {
+                    width: parent.width
+                    height: parent.height
+
+
+                    TextField {
+                        width: parent.width - 60
+                        height: parent.height
+                        placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
+
+                        onAccepted: {
+                            toScript({type: "send_message", message: text, channel: pageVal});
+                            text = ""
+                        }
+                    }
+
+                    Button {
+                        width: 60
+                        height:parent.height
+
+                        Image {
+                            source: "./img/ui/send_black.png"
+                            sourceSize.width: 30
+                            sourceSize.height: 30
+                            anchors.centerIn: parent
+                        }
+
+                        onClicked: {
+                            toScript({type: "send_message", message: parent.children[0].text, channel: pageVal});
+                            parent.children[0].text = ""
+                        }
+                        Keys.onPressed: {
+                            if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) {
+                                toScript({type: "send_message", message: parent.children[0].text, channel: pageVal});
+                                parent.children[0].text = ""
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+    }
+
+    Component {
+        id: template_chat_message
+
+        Rectangle{
+            property int index: delegateIndex
+            property string texttest: delegateText
+            property string username: delegateUsername
+            property string date: delegateDate
+
+            width: parent.width
+            height: Math.max(65, children[1].height + 30)
+            color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1)
+
+            Item {
+                width: parent.width
+                height: 22
+
+                Text{
+                    text: username
+                    color: "lightgray"
+                }
+
+                Text{
+                    anchors.right: parent.right
+                    text: date
+                    color: "lightgray"
+                }
+            }
+
+            TextEdit{
+                anchors.top: parent.children[0].bottom
+                text: texttest
+                color:"white"
+                font.pointSize: 12
+                readOnly: true
+                selectByMouse: true
+                selectByKeyboard: true
+                width: parent.width * 0.8
+                height: contentHeight // Adjust height to fit content
+                wrapMode: Text.Wrap
+            }
+        }
+    }
+
+    Component {
+        id: template_notification
+
+        // width: (Math.min(parent.width * 0.8, Math.max(contentWidth, parent.width))) - parent.children[0].width
+
+        Rectangle{
+            property int index: delegateIndex
+            property string texttest: delegateText
+            property string username: delegateUsername
+            property string date: delegateDate
+            color: "#171717"
+            width: parent.width
+            height: 40
+
+            Item {
+                width: 10
+                height: parent.height
+
+                Rectangle {
+                    height: parent.height
+                    width: 5
+                    color: "#505186"
+                }
+            }
+
+
+            Item {
+                width: parent.width - parent.children[0].width - 5
+                height: parent.height
+                anchors.left: parent.children[0].right
+
+                TextEdit{
+                    text: texttest
+                    color:"white"
+                    font.pointSize: 12
+                    readOnly: true
+                    width: parent.width
+                    selectByMouse: true
+                    selectByKeyboard: true
+                    height: parent.height
+                    wrapMode: Text.Wrap
+                    verticalAlignment: Text.AlignVCenter
+                    font.italic: true
+                }
+
+                Text {
+                    text: date
+                    color:"white"
+                    font.pointSize: 12
+                    anchors.right: parent.children[0].right
+                    height: parent.height
+                    wrapMode: Text.Wrap
+                    horizontalAlignment: Text.AlignRight
+                    verticalAlignment: Text.AlignVCenter
+                    font.italic: true
+                }
+            }
+
+        }
+
+    }
+
+
+
+    property var channels: {
+        "local": local,
+        "domain": domain,
+    }
+
+    function scrollToBottom() {
+        listview.positionViewAtIndex(listview.count - 1, ListView.End);
+        listview.positionViewAtEnd();
+        listview.contentY = listview.contentY + 50;
+    }
+
+
+    function addMessage(username, message, date, channel, type){
+        channel = getChannel(channel)
+
+        if (type === "notification"){
+            channel.append({ text: message, date: date, type: "notification" });
+            last_message_user = "";
+            scrollToBottom();
+            last_message_time = new Date();
+            return;
+        }
+
+        var current_time = new Date();
+        var elapsed_time = current_time - last_message_time;
+        var elapsed_minutes = elapsed_time / (1000 * 60); 
+
+        var last_item_index = channel.count - 1;
+        var last_item = channel.get(last_item_index);
+
+        if (last_message_user === username && elapsed_minutes < 1 && last_item){
+            last_item.text = last_item.text += "\n" + message;
+            scrollToBottom()
+            last_message_time = new Date();
+            return;
+        }
+
+        last_message_user = username;
+        last_message_time = new Date();
+        channel.append({ text: message, username: username, date: date, type: type });
+        scrollToBottom();
+    }
+
+    function getChannel(id) {
+        return channels[id];
+    }
+
+    // Messages from script
+    function fromScript(message) {
+        let time = new Date().toLocaleTimeString(undefined, { hour12: false });
+        let date = new Date().toLocaleDateString(undefined, {  month: "long", day: "numeric", });
+
+        switch (message.type){
+            case "show_message":
+                addMessage(message.displayName, message.message, `[ ${time} - ${date} ]`, message.channel, "chat");
+                break;
+            case "avatar_connected":
+                addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");
+                break;
+        }
+    }
+
+    // Send message to script
+    function toScript(packet){
+        sendToScript(packet)
+    }
+}
diff --git a/scripts/communityScripts/armored-chat/img/icon_black.png b/scripts/communityScripts/armored-chat/img/icon_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..410dc40b5933b126b7a996926e9c2a98a6471316
GIT binary patch
literal 400
zcmV;B0dM|^P)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm00043Nkl<ZXhZE;
ziwy!H487aH5p)Aba1=*y19by8P)BhBSI`M`11-trf*faQDWN1KAtu_E_bD7|-!(q^
z#_@MXptgf^4(tKg4(6gZz+wG2yq;hlLp(782MBTmG!93=;TuPcz)O+xp@<K7V}%j;
zAOveTGm9zafDtHQkvUL6R8mP%2mE3r6p6v4eMt%)02l$p36iL!;KUXv5hl6^N&v)a
z8UbIS(igqSjT06Zb-+%c+NaRZeH`0;jL&2BMO%o0DLcgoWJ{P?K-7VpQ|JanHViUG
zz!$A~2N)fYog&&o*=QIAi~vJ4(G+vQ$$5vl7S)wr><KALKP~ku<r$K;^!3Cul78F`
z`%MFQr~C!x<fe)_;F5ghuS%R4t;GoVJ)jP}1D8rbCHPbVYQd)vPz@d<pdS1`0!@IQ
uBM=^);!h*Vg7#0nq?(*s<^V@E$(%1C9bz#GCuddw0000<MNUMnLSTaNX_?vp

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/icon_white.png b/scripts/communityScripts/armored-chat/img/icon_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..e29bf9970640d90d418e1bcf168f3d2b819591e0
GIT binary patch
literal 778
zcmV+l1NHogP)<h;3K|Lk000e1NJLTq001xm001xu1^@s6R|5Hm0004mX+uL$Nkc;*
zaB^>EX>4Tx04R}tkv&MmKpe$iTcs)$5j#k6$WWc^qD35Q6^c+H)C#RSm|Xe=O&XFE
z7e~Rh;NZt%)xpJCR|i)?5c~jfc5qU3krMxx6k5c1aNLh~_a1le0DrT}RI?`msG4PD
zQb{3~UloF{2w)ifh#)R8Q=b#XG(5-GJ$!t<i}5V)bAOIOrC>6^ClDu?Zdk+{#50?g
z&Uv3W%*v8Nd`>)R&;^Mfxh}i>#<}FMpJ#@RY-XM~Oe~bTSngt0HdNv`aa2(?%J=77
zRyc2QR;zW^z9)ZSsGzMZbDicGQdq<iL<o>kM+H?_h|#K%Vj@HPNe_R-@r&e=$yEU(
z#{z0lAvu2VKlt6PS)877lR`<L_r<n9#)052&}!KB_pxoaPJqBOaHVzpwI(q8NqVEB
zMUQ}?ZQ$a%qse=~<qk0Vq)Ue6NCBGuVi9;hqi@OsBe%f7n%i4@AEysMmbyyc00)P_
zM2WK3J>K2d-P^xs+Wq|isK9c(_I@@V00006VoOIv00000008+zyMF)x010qNS#tmY
z4c7nw4c7reD4Tcy000McNliru=mQZ69uX+!UB>_b0S8G$K~!ko?U*|b!Y~j-XCb1Y
zf(lN=O}GLj2xxK#L`g-(o%mE7fM#eA3K5?Gvb-zJbe5ZW$;{d^AW71LNERfQBoFkX
z1IY!+q6xr)el@TAwI={a00XB1Ihon;69Dg<X67W;FFm!QcUAxb_*2CXg*vA+WM=)O
z004T5P|gSe=z9XtK^AceO%hL`GHHaW`-uQd5&EFg6M&|WPU2vfxviOvbC>v^%Uo9x
z%6yr-XpCx%3Lry?Np=A20E{yG-GBWDNVWlN9GC}yb)&{tWF%J(UZwXW=S`p2t14^j
znfp?_`3lnztqyJe1wb70PXJ<>uK~m}Ujc||{su6EBuQa(1+-JEwmsQ(G5`Po07*qo
IM6N<$f=@n5P5=M^

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/send.svg b/scripts/communityScripts/armored-chat/img/ui/send.svg
new file mode 100644
index 0000000000..82c70a6daf
--- /dev/null
+++ b/scripts/communityScripts/armored-chat/img/ui/send.svg
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   height="32"
+   viewBox="0 -960 760 640"
+   width="38"
+   version="1.1"
+   id="svg4"
+   sodipodi:docname="send.svg"
+   inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
+   inkscape:export-filename="send_black.png"
+   inkscape:export-xdpi="300"
+   inkscape:export-ydpi="300"
+   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="#505050"
+     bordercolor="#eeeeee"
+     borderopacity="1"
+     inkscape:showpageshadow="0"
+     inkscape:pageopacity="0"
+     inkscape:pagecheckerboard="0"
+     inkscape:deskcolor="#505050"
+     showgrid="false"
+     inkscape:zoom="21.395833"
+     inkscape:cx="17.363194"
+     inkscape:cy="16.031159"
+     inkscape:window-width="2560"
+     inkscape:window-height="1366"
+     inkscape:window-x="0"
+     inkscape:window-y="0"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="svg4" />
+  <path
+     d="m 0,-320 v -640 l 760,320 z M 60,-413 604,-640 60,-870 v 168 l 242,62 -242,60 z m 0,0 v -457 z"
+     id="path2"
+     style="fill:#000000" />
+</svg>
diff --git a/scripts/communityScripts/armored-chat/img/ui/send_black.png b/scripts/communityScripts/armored-chat/img/ui/send_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..bc9ece7a1186440aefa69560047589a94f5a2efe
GIT binary patch
literal 1950
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

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/send_white.png b/scripts/communityScripts/armored-chat/img/ui/send_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..2730d2f84c769991885250798ac5e453a5c47d19
GIT binary patch
literal 1956
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

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/settings_black.png b/scripts/communityScripts/armored-chat/img/ui/settings_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..f6481a85f8f3d8d659011114e22e8283c4a895f1
GIT binary patch
literal 1561
zcmV+!2Il#RP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T700009a7bBm000id
z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11*S<v
zK~z|Ut(bjimsJ$UKfgJ(aK4oWnV2;#)3RBCl%P`wg=Bpr3Pk<GG*XA;+agRV!OEQ4
z{!xk2{zwfB>_nwfPWDG>1=$v9FPo(ml3D50LB5Rj$Gy*X_w4@MZ@+oqf%BYm?>XP+
z-us-Fn~_AmWM;d7$2u9dOVZ={`Y6bEqNh<gjJcyb4)O+QW`#g;4ns$nS<ig^6y`e-
z2d<art0k3%N;9hlrT{&GTY;bQanhYF4!7=DL?6oX-sTPf&8%OJb8ip$;}QL_fX@kF
z%y(>yKQy!cz&c>3q{|UQlYkjnz{F&;%&ZpJE2$-As}Oh+SPnD-lS6$$YOsJ(U>vX=
zXfU(IW>)0&W6bOTPy^iE1%RW0-M}F;EAh69%xp2x;C+n)N<D^F$|P~n%z6Qbfe{gB
zr-4s^CxG68yczfkkfc+oj~xu`1jYl`2C9p|`@l3{V8mAgFh<fvXupDs0&q=0U7#BX
z%nHCY0j~i!Nt%!bheLcy(q!NUU@4G{=>^P8fvfX2@fa2*iUR+&gMgL6gmsciaOdFz
z+)5>M02nK2g`|^_6-Wa;FX;_nEKnV&n!WAG-q*T7x6)&3{Yh2<13Z5MsPx8bfEqKq
z1t<rG08dH!uQOl~pnsa#G++yG64)r|*I-B`@U-_az+)=U;6lK60ob{cwssZ3)gUvQ
z3%u=Ln}L!-U<2*LO@A}Xq{BfchWi3#xQx*P{DR9a=el4c1B7v~2T+*-W&<06zJc;1
zV2-4JIx$)RQ~)oe<^!AsUI8jQ`&cu(9(V_s9*7%(@&ZZk0`~%k0_Aj|*32e$0&W|y
zCISxJYP=3?_p6i5&P1R#1?~%=MAG&G07>=013(pSivWfK`^{`Y7TXoH&nET2oBnAC
z<TG$tA_i^&u;2RvxD31uOp<gAf;cGYf}|zDBf#HY+yl5dVz@7`A`oo_?vgZL(tO}f
z+?FM@GRa0M8tQ!j{0>Z%v`SKISnE(MNctF;$`U#fF+7#F16>a+lyo7maRFEW9QC4p
zxY8?4-v|8Hk`AT^WGlC>3;4ex`jG*@F9eqYxi8C{giZ(ifQUY)dN6jtTRPFlbg@6V
zQ~%ip{2TCtBl_?sC^NI(=>cYT4elt!<|H&I;7>>N(Sr41T*;Nt=*YZkT$}@!+&6nb
zA-GMn^(>9Jzco#v?FnYq;heT+5Wfm*fM;pjnr0%^vZQlnwi;OLMRS0OW|o|}GTJv(
zt0kR}06gw}^Z;%IJ~Okmz>AVDL0dI55?7@x4#52ZOp&xXVi;WY@m}y=3_RnX5Zn%6
zeawKjJ;nP<@H`KEZf3=JFx^RY8QM}^x}RtSRs-wvOm%1R)yy8?p+K<|sOn;|P()j?
z051MjXW7<lHZr&!+z^QOCgoNgu73rXCuvKrhOZ`>**xGa|Jn?cWF%o`8-ayhaRRta
zQd2N7DOiW$3f7!gCIz4fmll5`Rj`^_AK+(P!3wZRQh5?)UINZ|ehBVdVP>WNf~X!?
zhHIJ*B%N6o!HokBdK=4tdNbQ)W~F{eMF8%M$JDN?TSi-7(1Z)DVQU%ECEQ9>N;;Zl
zOk%efcn%kevfa_t;o@l@|Gr$(`ZU0N+Rhf>8(><NwsSYG)(lf#er=~0RHOzU2bKWC
zB+d5Dz6|sk;6CElaHG5~1Xl};moy7^o|n)u5mY4L+5z++4OeP?i|buSNUBPH3Q310
z-48rS`!Z~J1YjsTf?KhBB-MIb=OtC)c5J5iS>rLZQtWQ#QAuYb^6*1GHM4ENt2qMP
zCTUL=TbFU!W=HJuC|0mu5nMn&27HIAI|cBETnxs1cjtaEbUHku4`n$4j8S)PSdZeu
zO;2E|nPp3jmvDV@UX=F=`f})sG0;;(Qaa^B-M!_{W$W_ZUV#4r)&K&2Lvuzo00000
LNkvXXu0mjfD_g-8

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/settings_white.png b/scripts/communityScripts/armored-chat/img/ui/settings_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..12a35ad58c9149f9c3d07155798130e30fd971fa
GIT binary patch
literal 1568
zcmV+*2H*LKP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T700009a7bBm000id
z000id0mpBsWB>pF8FWQhbW?9;ba!ELWdL_~cP?peYja~^aAhuUa%Y?FJQ@H11+7U$
zK~z|Ut(aSgR#g<of8RK2;k`7-#H`WsQZ`l~C3qbuBy0Xefv69rkryOy6Jb&bR_3V9
zi%OLCkQx}cib|y%nTOH}vN6&wGnQ6JW~HMJ<YoAJ*!#@vIkP|Kn{QljV4b!0Uh9AM
z-e+BQro6nILdnc_0cvO1E=i9U>Z7R8iJpe%F=j+(925-D%!+}MJcbT6v#y2uDK2y(
z4qP|SS4%1jm1b5AOa{6Fw*x;F;-oWM9B$q5h(44Ryv=O@npy8W=iV9cCnEae0iPGZ
znD5vYe`scXfOWu5Nmn9<CIHiOfQiXwnprKdS5h`*s~C6^SPnD*lR|w_YH&F)5f}q(
z2kOmiv6=Ps`cY<f0H^`(=>Wiyz;57>nU#85J<V(}Q15+>0VaA3&6G*vpqX_8jsQa=
z&dvaz08aqj19=nh6;Lkebn0UV06T%P!1aOZ67W7S73df7RS%4kbO~Co;GzIrBXBs-
z^#f)G;2MF~fLkPuOM?TDbXw9R;3i-xkc{aD%t(Pd>}}#PEJ_qb{%iXKD}xE^B$eXM
zgW*;xp##8ZNh>6simX5y=mkk{0HcBGK-J`JPx8Lj1-g|UQ}a)<3h3+klR%|6UIWya
z*=@i=U?A|6r2pCj76JOFnN0<@0H=VBl70<_R02<XAALQhg_&$NI|BGF06Rz0){X+W
z7G!2~fVcf?6Hr<VY@l_x>0@S@bU0|oa4(<?moc)yFSzV-z5_NgKo|$R0F{|+HaiR0
z2=oe+9|5x^{nL)oBA^0zAvGW19PkQI+1|&R*^R(Ez_dWz04yw$^e%88a41kt18U7|
zd^_N_0c#@Qz^%sXz;?enx$KMwYE$6807@loF9MKM2RsN=;kF215U}6O=I5|oLF;T%
z2fXQ@`anJ%mnCB0<^%h^FMun+%fJLl$03M=k}gVG0z3-*?ZsVyTO)>h0V@L0R^V<)
z^CZm!?!s+ZLMxMOl%hf22f**ZcuA`yHHWn}#e$@daj7h!qY=YXXgSbzzye7Z0~;5C
z`M@zR>WwSC()4}6e=X@?dO)sn>xO{;E21A3@cTk=DUkbe%t`1>!1s;l^Qs492Rz%3
zKBkNP$?f{jHQ?WX9}v-pKS7z9bx#j4v+Hn2AvPzW{sDg`qK_7=kKjtKghoc@Rpa6u
zxa7Xs0}8=yqNQhP!2PXh3T=-wvo`0nIfM9BSOYvu%hogzsg@<3H?!5iS}&Rnj5o97
z%$3o)p;|5JLImIm@1qNFGw_+2tp#3`bQxNznPIppWpM!R4`8yS%@M=ks*m@A_Y&Y4
z|AgRn0PAB0yzR-}SAyqx;Bzx80qAV1%g~bQ()~mOuo_rjV5&QZuV(fD4+n~+Kvf5e
zg`Tt&3*h2kb&hS#Wg~;z!3}|UZ&Gd@#`UiNb0uxb*YLF@Gn)&%<zJhC(oA`IIcByI
zSl|^WfjcBM1{0Hlbug}A&3k210D9um;%}r1Rx|4X{ERDD0X9imn1q>^fU}+-h&xx9
z*+hRqR0k}>HBASS&a8vr#sCMsjb%Wcne8&OiGD~$0Pd{E)T*mnMoVANhzqP?YZ=mI
z+)7kRI+kNhVz&f%4i}1Y-O(Jz#nT@CeYvFdX@G^aomt=;U}}!Gb2qNm3{zfVZKoGh
zqz0b=mH>k#&GOE^4D=b`e&W|~!@Vv9R||}lG!u88m(Vs5R3zY90rVh0z?E9x;(FJi
zlB$xQLeddQ4*(C*x(pi<0T{}T;#TZlNwwbA1xZ!79h>2O)_4rf6uX;wOwzfCJp7PP
z&1@U+YMubMN!pXc))ic~*%7-uiWRI^1sBkd0pF(TP67NOAA>R9ow*+joeqiULs?z`
zW7L@&)?>JE(-oLvX1P-1Wn7<J5EZ<Fz8t!04D?i=lum_EXK(rQ*}8(a7vO&&I}wDu
SmNOCn0000<MNUMnLSTaLqsw^!

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/social_black.png b/scripts/communityScripts/armored-chat/img/ui/social_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..16777af4629604b1ede14b8b8637b4601fdb0391
GIT binary patch
literal 3485
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
Bh}!@F

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/social_white.png b/scripts/communityScripts/armored-chat/img/ui/social_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..7677bd54696ae317fcf03436102f46fa69fc4e4b
GIT binary patch
literal 3491
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
H<dyti<TZu>

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/world_black.png b/scripts/communityScripts/armored-chat/img/ui/world_black.png
new file mode 100644
index 0000000000000000000000000000000000000000..c983e5df28df328c581ae424242aa3308d13de35
GIT binary patch
literal 4066
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
Uao=#1VE_OC07*qoM6N<$g5Qv^mjD0&

literal 0
HcmV?d00001

diff --git a/scripts/communityScripts/armored-chat/img/ui/world_white.png b/scripts/communityScripts/armored-chat/img/ui/world_white.png
new file mode 100644
index 0000000000000000000000000000000000000000..1f152b47b2ceb6144197886a1ed20a14fafff56b
GIT binary patch
literal 3960
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
SF%fA10000<MNUMnLSTXf8D=v8

literal 0
HcmV?d00001

diff --git a/scripts/defaultScripts.js b/scripts/defaultScripts.js
index c3b432cea4..a9bc2be591 100644
--- a/scripts/defaultScripts.js
+++ b/scripts/defaultScripts.js
@@ -7,7 +7,7 @@
 //
 //  Copyright 2014 High Fidelity, Inc.
 //  Copyright 2020 Vircadia contributors.
-//  Copyright 2022 Overte e.V.
+//  Copyright 2024 Overte e.V.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -47,7 +47,7 @@ var DEFAULT_SCRIPTS_SEPARATE = [
     "communityScripts/notificationCore/notificationCore.js",
     "simplifiedUI/ui/simplifiedNametag/simplifiedNametag.js",
     {"stable": "system/more/app-more.js", "beta": "https://more.overte.org/more/app-more.js"},
-    {"stable": "communityScripts/chat/FloofChat.js", "beta": "https://content.fluffy.ws/scripts/chat/FloofChat.js"}
+    "communityScripts/armored-chat/armored_chat.js",
     //"system/chat.js"
 ];
 

From 0d5eb93241f8732d06bc0d79cbf63a56ed103d1d Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 18 May 2024 13:46:06 -0500
Subject: [PATCH 02/48] Added Settings. Erase messages. Toggle external window.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.js              | 23 ++++++-
 .../armored-chat/armored_chat.qml             | 65 ++++++++++++++++++-
 2 files changed, 84 insertions(+), 4 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 40c4510409..27f40b3a15 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -129,12 +129,33 @@
     }
 
     function fromQML(event) {
-        console.log(`New web event:\n${JSON.stringify(event)}`);
+        console.log(`New QML event:\n${JSON.stringify(event)}`);
 
         switch (event.type) {
             case "send_message":
                 _sendMessage(event.message, event.channel);
                 break;
+            case "setting_change":
+                settings[event.setting] = event.value; // Update local settings
+                _saveSettings(); // Save local settings
+
+                switch (event.setting) {
+                    case "external_window":
+                        chat_overlay_window.presentationMode = event.value
+                            ? Desktop.PresentationMode.NATIVE
+                            : Desktop.PresentationMode.VIRTUAL;
+                        break;
+                }
+
+                break;
+            case "action":
+                switch (event.action) {
+                    case "erase_history":
+                        Settings.setValue("ArmoredChat-Messages", []);
+                        break;
+                }
+                break;
+
             case "initialized":
                 // https://github.com/overte-org/overte/issues/824
                 chat_overlay_window.visible = app_is_visible; // The "visible" field in the Desktop.createWindow does not seem to work. Force set it to the initial state (false)
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 29f43a4616..af3314cc99 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -26,9 +26,8 @@ Rectangle {
     //     toScript({type: "initialized"})
     // }
 
-    Column {
+    Item {
         anchors.fill: parent
-        spacing: 0
 
         // Navigation Bar
         Rectangle {
@@ -135,6 +134,7 @@ Rectangle {
             width: parent.width
             height: parent.height - 40
             anchors.top: navigation_bar.bottom
+            visible: ["local", "domain"].includes(pageVal) ? true : false
 
 
             // Chat Message History
@@ -180,7 +180,6 @@ Rectangle {
                 height: 40
                 color: Qt.rgba(0.9,0.9,0.9,1)
                 anchors.bottom: parent.bottom
-                visible: ["local", "domain"].includes(pageVal) ? true : false
 
                 Row {
                     width: parent.width
@@ -224,6 +223,66 @@ Rectangle {
             }
         }
 
+        Item {
+            width: parent.width
+            height: parent.height - 40
+            anchors.top: navigation_bar.bottom
+            visible: ["local", "domain"].includes(pageVal) ? false : true
+
+            Column {
+                width: parent.width - 10
+                height: parent.height - 10
+                anchors.centerIn: parent
+                spacing: 0
+
+                Rectangle {
+                    width: parent.width
+                    height: 40
+                    color: "transparent"
+
+                    Text{
+                        text: "External window"
+                        color: "white"
+                        font.pointSize: 12
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+
+                    CheckBox{
+                        anchors.right: parent.right
+                        anchors.verticalCenter: parent.verticalCenter
+
+                        onCheckedChanged: {
+                            toScript({type: 'setting_change', setting: 'external_window', value: checked})
+                        }
+                    }
+                }
+
+                Rectangle {
+                    width: parent.width
+                    height: 40
+                    color: Qt.rgba(0.15,0.15,0.15,1);
+
+                    Text{
+                        text: "Erase chat history"
+                        color: "white"
+                        font.pointSize: 12
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+
+                    Button {
+                        anchors.right: parent.right
+                        text: "Erase"
+                        height: parent.height
+                        anchors.verticalCenter: parent.verticalCenter
+
+                        onClicked: {
+                            toScript({type: "action", action: "erase_history"})
+                        }
+                    }
+                }
+            }
+        }
+
     }
 
     Component {

From 7b50f9184fb46469cde2bb297035dadc99400434 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 18 May 2024 14:12:00 -0500
Subject: [PATCH 03/48] Clear already logged messages.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.js  | 5 +++--
 scripts/communityScripts/armored-chat/armored_chat.qml | 3 +++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 27f40b3a15..4bc75f9345 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -152,10 +152,12 @@
                 switch (event.action) {
                     case "erase_history":
                         Settings.setValue("ArmoredChat-Messages", []);
+                        _emitEvent({
+                            type: "clear_messages",
+                        });
                         break;
                 }
                 break;
-
             case "initialized":
                 // https://github.com/overte-org/overte/issues/824
                 chat_overlay_window.visible = app_is_visible; // The "visible" field in the Desktop.createWindow does not seem to work. Force set it to the initial state (false)
@@ -201,7 +203,6 @@
             _emitEvent({ type: "avatar_connected", ...message });
         }, 1500);
     }
-
     function _loadSettings() {
         message_history.forEach((message) => {
             delete message.action;
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index af3314cc99..9b313162fe 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -452,6 +452,9 @@ Rectangle {
             case "avatar_connected":
                 addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");
                 break;
+            case "clear_messages":
+                local.clear()
+                domain.clear()
         }
     }
 

From 66fd21777cf62318e328cf6147a901a116f33f4e Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Tue, 21 May 2024 12:19:03 -0500
Subject: [PATCH 04/48] Maximum messages setting. Settings now initialize
 properly. Settings now save properly.  Removed compact_chat setting as
 deprecated.  Added spacing between settings.  Message history now prunes
 itself if the number of messages exceeds maximum.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.js              | 21 +++++++--
 .../armored-chat/armored_chat.qml             | 43 ++++++++++++++++++-
 2 files changed, 60 insertions(+), 4 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 4bc75f9345..4130c05d2f 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -12,8 +12,8 @@
 
     var app_is_visible = false;
     var settings = {
-        compact_chat: false,
         external_window: false,
+        maximum_messages: 200,
     };
 
     // Global vars
@@ -123,8 +123,9 @@
             day: "numeric",
         });
         message_history.push(saved_message);
-        if (message_history.length > settings.max_history)
+        while (message_history.length > settings.maximum_messages) {
             message_history.shift();
+        }
         Settings.setValue("ArmoredChat-Messages", message_history);
     }
 
@@ -145,6 +146,9 @@
                             ? Desktop.PresentationMode.NATIVE
                             : Desktop.PresentationMode.VIRTUAL;
                         break;
+                    case "maximum_messages":
+                        // Do nothing
+                        break;
                 }
 
                 break;
@@ -204,12 +208,23 @@
         }, 1500);
     }
     function _loadSettings() {
+        settings = Settings.getValue("ArmoredChat-Config", settings);
+
+        // Load message history
         message_history.forEach((message) => {
             delete message.action;
             _emitEvent({ type: "show_message", ...message });
         });
+
+        // Send current settings to the app
+        _emitEvent({ type: "initial_settings", settings: settings });
+
+        console.log(`\n\n\n` + JSON.stringify(settings));
+    }
+    function _saveSettings() {
+        console.log("Saving config");
+        Settings.setValue("ArmoredChat-Config", settings);
     }
-    function _saveSettings() {}
 
     /**
      * Emit a packet to the HTML front end. Easy communication!
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 9b313162fe..84b8ed5d01 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -1,6 +1,7 @@
 import QtQuick 2.7
 import QtQuick.Controls 2.0
 import QtQuick.Layouts 1.3
+import controlsUit 1.0 as HifiControlsUit
 
 Rectangle {
     color: Qt.rgba(0.1,0.1,0.1,1)
@@ -18,6 +19,7 @@ Rectangle {
     Timer {
         interval: 100
         running: true
+        repeat: false
         onTriggered: {
            toScript({type: "initialized"})
         }
@@ -26,6 +28,7 @@ Rectangle {
     //     toScript({type: "initialized"})
     // }
 
+    // User view
     Item {
         anchors.fill: parent
 
@@ -233,8 +236,9 @@ Rectangle {
                 width: parent.width - 10
                 height: parent.height - 10
                 anchors.centerIn: parent
-                spacing: 0
+                spacing: 10
 
+                // External Window
                 Rectangle {
                     width: parent.width
                     height: 40
@@ -248,6 +252,7 @@ Rectangle {
                     }
 
                     CheckBox{
+                        id: s_external_window
                         anchors.right: parent.right
                         anchors.verticalCenter: parent.verticalCenter
 
@@ -257,6 +262,38 @@ Rectangle {
                     }
                 }
 
+                // Maximum saved messages
+                Rectangle {
+                    width: parent.width
+                    height: 40
+                    color: "transparent"
+
+                    Text{
+                        text: "Maximum saved messages"
+                        color: "white"
+                        font.pointSize: 12
+                        anchors.verticalCenter: parent.verticalCenter
+                    }
+
+                    
+                    HifiControlsUit.SpinBox {
+                        id: s_maximum_messages
+                        anchors.right: parent.right
+                        anchors.verticalCenter: parent.verticalCenter
+                        decimals: 0
+                        width: 100
+                        height: parent.height
+                        realFrom: 1
+                        realTo: 1000
+                        backgroundColor: "#cccccc"
+
+                        onValueChanged: {
+                            toScript({type: 'setting_change', setting: 'maximum_messages', value: value})
+                        }
+                    }
+                }
+
+                // Erase History
                 Rectangle {
                     width: parent.width
                     height: 40
@@ -285,6 +322,7 @@ Rectangle {
 
     }
 
+    // Templates
     Component {
         id: template_chat_message
 
@@ -455,6 +493,9 @@ Rectangle {
             case "clear_messages":
                 local.clear()
                 domain.clear()
+            case "initial_settings":
+                if (message.settings.external_window) s_external_window.checked = true
+                if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages
         }
     }
 

From 391a33d14921fcef47fa0c6e6942dfe0b6df7d68 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Tue, 21 May 2024 16:46:41 -0500
Subject: [PATCH 05/48] Fixed saved date on messages. Hotfix for empty
 message_history setting.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/armored_chat.js | 15 +++++++++------
 .../armored-chat/armored_chat.qml                 | 13 +++++++++++--
 2 files changed, 20 insertions(+), 8 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 4130c05d2f..c479899c49 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -21,7 +21,7 @@
     var chat_overlay_window;
     var app_button;
     const channels = ["domain", "local"];
-    var message_history = Settings.getValue("ArmoredChat-Messages", []);
+    var message_history = Settings.getValue("ArmoredChat-Messages", []) || [];
     var max_local_distance = 20; // Maximum range for the local chat
     var pal_data = AvatarManager.getPalData().data;
 
@@ -119,6 +119,7 @@
             hour12: false,
         });
         saved_message.dateString = new Date().toLocaleDateString(undefined, {
+            year: "numeric",
             month: "long",
             day: "numeric",
         });
@@ -210,11 +211,13 @@
     function _loadSettings() {
         settings = Settings.getValue("ArmoredChat-Config", settings);
 
-        // Load message history
-        message_history.forEach((message) => {
-            delete message.action;
-            _emitEvent({ type: "show_message", ...message });
-        });
+        if (message_history) {
+            // Load message history
+            message_history.forEach((message) => {
+                delete message.action;
+                _emitEvent({ type: "show_message", ...message });
+            });
+        }
 
         // Send current settings to the app
         _emitEvent({ type: "initial_settings", settings: settings });
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 84b8ed5d01..462966da11 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -150,11 +150,11 @@ Rectangle {
                 id: listview
 
                 delegate: Loader {
-                    width: parent.width
                     property int delegateIndex: index
                     property string delegateText: model.text
                     property string delegateUsername: model.username
                     property string delegateDate: model.date
+                    width: parent.width // FIXME: Causes warning, but required for style?
 
                     sourceComponent: {
                         if (model.type === "chat") {
@@ -165,6 +165,12 @@ Rectangle {
                     }
                 }
 
+                ScrollBar.vertical: ScrollBar {
+                    id: chat_scrollbar
+                    height: 100
+                    size: 0.05
+                }
+
                 model: getChannel(pageVal)
 
             }
@@ -437,9 +443,12 @@ Rectangle {
     }
 
     function scrollToBottom() {
+        if (listview.count == 0) return;
+        // if (chat_scrollbar.visualPosition > 0.85) {
         listview.positionViewAtIndex(listview.count - 1, ListView.End);
         listview.positionViewAtEnd();
         listview.contentY = listview.contentY + 50;
+        // }
     }
 
 
@@ -485,7 +494,7 @@ Rectangle {
 
         switch (message.type){
             case "show_message":
-                addMessage(message.displayName, message.message, `[ ${time} - ${date} ]`, message.channel, "chat");
+                addMessage(message.displayName, message.message, `[ ${message.timeString || time} - ${message.dateString || date} ]`, message.channel, "chat");
                 break;
             case "avatar_connected":
                 addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");

From c48e9d3a69e9e9d3a66ffa52fe1f3106cbfcbad2 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 02:50:29 -0500
Subject: [PATCH 06/48] Clickable links. Messages are now rich text. Fix
 clear_message command flow over.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.qml             | 22 ++++++++++++++++++-
 1 file changed, 21 insertions(+), 1 deletion(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 462966da11..706d431a95 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -369,6 +369,11 @@ Rectangle {
                 width: parent.width * 0.8
                 height: contentHeight // Adjust height to fit content
                 wrapMode: Text.Wrap
+                textFormat: TextEdit.RichText
+
+                onLinkActivated: {
+                    Window.openWebBrowser(link)
+                }
             }
         }
     }
@@ -455,6 +460,14 @@ Rectangle {
     function addMessage(username, message, date, channel, type){
         channel = getChannel(channel)
 
+        // Linkify
+        var regex = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)?(?=\s|$)/g;
+        message = message.replace(regex, (match) => {
+            return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a>";
+        });
+
+        message = sanatizeContent(message); // Make sure we don't have any nasties
+
         if (type === "notification"){
             channel.append({ text: message, date: date, type: "notification" });
             last_message_user = "";
@@ -471,6 +484,7 @@ Rectangle {
         var last_item = channel.get(last_item_index);
 
         if (last_message_user === username && elapsed_minutes < 1 && last_item){
+            message = "<br>" + message 
             last_item.text = last_item.text += "\n" + message;
             scrollToBottom()
             last_message_time = new Date();
@@ -487,10 +501,15 @@ Rectangle {
         return channels[id];
     }
 
+    function sanatizeContent(mess) {
+        var script_tag = /<script\b[^>]*>/gi;
+        return mess.replace(script_tag, "script");
+    }
+
     // Messages from script
     function fromScript(message) {
         let time = new Date().toLocaleTimeString(undefined, { hour12: false });
-        let date = new Date().toLocaleDateString(undefined, {  month: "long", day: "numeric", });
+        let date = new Date().toLocaleDateString(undefined, { year: "numeric", month: "long", day: "numeric", });
 
         switch (message.type){
             case "show_message":
@@ -502,6 +521,7 @@ Rectangle {
             case "clear_messages":
                 local.clear()
                 domain.clear()
+                break;
             case "initial_settings":
                 if (message.settings.external_window) s_external_window.checked = true
                 if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages

From 2a42bceaec76633973c947d1457aa2267daf91e4 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 03:03:02 -0500
Subject: [PATCH 07/48] Minor formatting adjustments. Hopefully makes code
 pasting easier. Added missing break to prevent unintended executions.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.qml             | 29 ++++++++++---------
 1 file changed, 16 insertions(+), 13 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 706d431a95..87c255663f 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -460,13 +460,8 @@ Rectangle {
     function addMessage(username, message, date, channel, type){
         channel = getChannel(channel)
 
-        // Linkify
-        var regex = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)?(?=\s|$)/g;
-        message = message.replace(regex, (match) => {
-            return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a>";
-        });
-
-        message = sanatizeContent(message); // Make sure we don't have any nasties
+        // Format content
+        message = formatContent(message);
 
         if (type === "notification"){
             channel.append({ text: message, date: date, type: "notification" });
@@ -501,9 +496,16 @@ Rectangle {
         return channels[id];
     }
 
-    function sanatizeContent(mess) {
+    function formatContent(mess) {
+        var link = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)?(?=\s|$)/g;
+        mess = mess.replace(link, () => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a>"});
+
         var script_tag = /<script\b[^>]*>/gi;
-        return mess.replace(script_tag, "script");
+        mess = mess.replace(script_tag, "script");
+
+        var newline = /\n/gi;
+        mess = mess.replace(newline, "<br>");
+        return mess
     }
 
     // Messages from script
@@ -519,12 +521,13 @@ Rectangle {
                 addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");
                 break;
             case "clear_messages":
-                local.clear()
-                domain.clear()
+                local.clear();
+                domain.clear();
                 break;
             case "initial_settings":
-                if (message.settings.external_window) s_external_window.checked = true
-                if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages
+                if (message.settings.external_window) s_external_window.checked = true;
+                if (message.settings.maximum_messages) s_maximum_messages.value = message.settings.maximum_messages;
+                break;
         }
     }
 

From 9d9b57dae432a71f9b0399fe3e38f03caf42b589 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 03:14:17 -0500
Subject: [PATCH 08/48] Fix notification width. Allows for kinetic mouse
 scrolling.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 87c255663f..e8540ed3ae 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -414,7 +414,7 @@ Rectangle {
                     color:"white"
                     font.pointSize: 12
                     readOnly: true
-                    width: parent.width
+                    width: parent.width * 0.8
                     selectByMouse: true
                     selectByKeyboard: true
                     height: parent.height
@@ -427,7 +427,7 @@ Rectangle {
                     text: date
                     color:"white"
                     font.pointSize: 12
-                    anchors.right: parent.children[0].right
+                    anchors.right: parent.right
                     height: parent.height
                     wrapMode: Text.Wrap
                     horizontalAlignment: Text.AlignRight

From 7920610d4dac392a49ff91df6ed41a74ff7d2baf Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 03:50:34 -0500
Subject: [PATCH 09/48] README

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/README.md   | 57 +++++++++++++++++++
 1 file changed, 57 insertions(+)
 create mode 100644 scripts/communityScripts/armored-chat/README.md

diff --git a/scripts/communityScripts/armored-chat/README.md b/scripts/communityScripts/armored-chat/README.md
new file mode 100644
index 0000000000..16cd538915
--- /dev/null
+++ b/scripts/communityScripts/armored-chat/README.md
@@ -0,0 +1,57 @@
+# Armored Chat
+
+1. What is Armored Chat (AC) and what AC is not
+2. User manual
+    - Installation
+    - Settings
+    - Usability tips
+
+## What is Armored Chat
+
+Armored Chat is a chat application strictly made to communicate between players in the same domain. It is made using QML and to be as light weight as reasonably possible.
+AC uses the Overte [Messages](https://apidocs.overte.org/Messages.html) API to communicate.
+
+## User manual
+
+### Installation
+
+Armored Chat is preinstalled courtesy of [defaultScripts.js](https://github.com/overte-org/overte/blob/8661e8a858663b48e8485c2cd7120dc3e2d7b87e/scripts/defaultScripts.js).
+
+If AC is not preinstalled, or for some other reason it can not be automatically installed, you can install it manually by following [these instructions](https://github.com/overte-org/overte/blob/8661e8a858663b48e8485c2cd7120dc3e2d7b87e/scripts/defaultScripts.js) to open your script management application, and loading the script url:
+
+```
+https://raw.githubusercontent.com/overte-org/overte/master/scripts/communityScripts/armored-chat/armored_chat.js
+```
+
+---
+
+### Settings
+
+Armored Chat comes with basic settings for managing itself.
+
+#### External window
+
+This boolean setting toggles whether AC will be a in-game overlay window, or whether AC will be a external floating window.
+
+Default is `false`.
+
+#### Maximum saved messages
+
+This integer represents the amount of messages to save in the AC history. More messages may be present if AC is left on long enough. This setting only sets the number of saved messages and not the maximum amount of messages that can be viewed at any time.
+
+This means if you set the value to `5`, your history will save a maximum of 5 messages, however you will still be able to see a longer history in the session should you receive more. Once AC completely closes and fetches your message history as it initializes, you will only see the last 5 messages.
+
+Default value is `200`
+
+#### Erase chat history
+
+This action immediately clears the AC history and the session. Functionally this will set the message list to a empty Array.
+
+---
+
+### Usability tips
+
+You can scroll quickly using kinetic scrolling! Try "grabbing" the right side of messages, where the timestamp is, and flinging yourself in a direction.
+
+You can format messages using basic HTML elements. Try `<div style="color: red"> Red text! </div>` to color your text red.
+Find the full list of Qt rich text tags [here](https://doc.qt.io/qt-6/richtext-html-subset.html). Please note that some of these tags may be intentionally restricted.

From 9bf5f3d0d372ac7a5290553a9fda7956a94c0e27 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 04:47:37 -0500
Subject: [PATCH 10/48] Linking fix. Disabled sending on Shift + Enter to
 prevent confusion.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/armored_chat.qml | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index e8540ed3ae..0cdf154b2f 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -194,18 +194,18 @@ Rectangle {
                     width: parent.width
                     height: parent.height
 
-
                     TextField {
                         width: parent.width - 60
                         height: parent.height
                         placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
-
-                        onAccepted: {
-                            toScript({type: "send_message", message: text, channel: pageVal});
-                            text = ""
+                        Keys.onPressed: {
+                            if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
+                                event.accepted = true;
+                                toScript({type: "send_message", message: text, channel: pageVal});
+                                text = ""
+                            }
                         }
                     }
-
                     Button {
                         width: 60
                         height:parent.height
@@ -498,7 +498,7 @@ Rectangle {
 
     function formatContent(mess) {
         var link = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)?(?=\s|$)/g;
-        mess = mess.replace(link, () => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a>"});
+        mess = mess.replace(link, (match) => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a> <a onclick='Window.openUrl("+match+")'>⮺</a>"});
 
         var script_tag = /<script\b[^>]*>/gi;
         mess = mess.replace(script_tag, "script");

From 86f8e290965d554610d7f50569d2dd14c04b89c8 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 20:37:10 -0500
Subject: [PATCH 11/48] Quick Message hotbar.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.js              | 17 +++-
 .../armored-chat/armored_chat.qml             |  1 +
 .../armored_chat_quick_message.qml            | 99 +++++++++++++++++++
 3 files changed, 116 insertions(+), 1 deletion(-)
 create mode 100644 scripts/communityScripts/armored-chat/armored_chat_quick_message.qml

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index c479899c49..6674a74538 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -20,11 +20,13 @@
     var tablet;
     var chat_overlay_window;
     var app_button;
+    var quick_message;
     const channels = ["domain", "local"];
     var message_history = Settings.getValue("ArmoredChat-Messages", []) || [];
     var max_local_distance = 20; // Maximum range for the local chat
     var pal_data = AvatarManager.getPalData().data;
 
+    Controller.keyPressEvent.connect(keyPressEvent);
     Messages.subscribe("chat");
     Messages.messageReceived.connect(receivedMessage);
     AvatarManager.avatarAddedEvent.connect((session_id) => {
@@ -56,6 +58,10 @@
         // Overlay button toggle
         app_button.clicked.connect(toggleMainChatWindow);
 
+        quick_message = new OverlayWindow({
+            source: Script.resolvePath("./armored_chat_quick_message.qml"),
+        });
+
         _openWindow();
     }
     function toggleMainChatWindow() {
@@ -83,6 +89,7 @@
 
         chat_overlay_window.closed.connect(toggleMainChatWindow);
         chat_overlay_window.fromQml.connect(fromQML);
+        quick_message.fromQml.connect(fromQML);
     }
     function receivedMessage(channel, message) {
         // Is the message a chat message?
@@ -129,7 +136,6 @@
         }
         Settings.setValue("ArmoredChat-Messages", message_history);
     }
-
     function fromQML(event) {
         console.log(`New QML event:\n${JSON.stringify(event)}`);
 
@@ -170,6 +176,15 @@
                 break;
         }
     }
+    function keyPressEvent(event) {
+        switch (JSON.stringify(event.key)) {
+            case "16777220": // Enter key
+                quick_message.sendToQml({
+                    type: "change_visibility",
+                    value: true,
+                });
+        }
+    }
     function _sendMessage(message, channel) {
         Messages.sendMessage(
             "chat",
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 0cdf154b2f..826e848370 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -198,6 +198,7 @@ Rectangle {
                         width: parent.width - 60
                         height: parent.height
                         placeholderText: pageVal.charAt(0).toUpperCase() + pageVal.slice(1) + " chat message..."
+                        clip: false
                         Keys.onPressed: {
                             if ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && !(event.modifiers & Qt.ShiftModifier)) {
                                 event.accepted = true;
diff --git a/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml b/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml
new file mode 100644
index 0000000000..a3b897083d
--- /dev/null
+++ b/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml
@@ -0,0 +1,99 @@
+import QtQuick 2.5
+import QtQuick.Controls 1.4
+
+Rectangle {
+    id: root
+    property var window
+
+    Binding { target: root; property:'window'; value: parent.parent; when: Boolean(parent.parent) }
+    Binding { target: window; property: 'shown'; value: false; when: Boolean(window) }
+    Component.onDestruction: chat_bar && chat_bar.destroy()
+
+    property alias chat_bar: chat_bar
+
+    Rectangle {
+        id: chat_bar
+        parent: desktop
+        x: 0
+        y: parent.height - height
+        width: parent.width
+        height: 50
+        z: 99
+        visible: false
+
+        TextArea {
+            id: textArea
+            x: 0
+            width: parent.width
+            height: parent.height
+            text:""
+            textColor: "#ffffff"
+            clip: false
+            font.pointSize: 18
+
+            Keys.onReturnPressed: { _onEnterPressed(); }
+            Keys.onEnterPressed: { _onEnterPressed(); }
+        }
+
+        Text {
+            text: "Local message..."
+            font.pointSize: 16
+            color: "gray"
+            x: 0
+            width: parent.width
+            anchors.verticalCenter: parent.verticalCenter
+            visible: textArea.text == ""
+        }
+
+        Button {
+            id: button
+            x: parent.width - width
+            y: 0
+            width: 64
+            height: parent.height
+            clip: false
+            visible: true
+            
+            Image {
+                id: image
+                width: 30
+                height: 30
+                fillMode: Image.PreserveAspectFit
+                visible: true
+                anchors.centerIn: parent
+                source: "./img/ui/send_white.png"
+            }
+
+            onClicked: {
+                _onEnterPressed();
+            }
+        }
+
+    }
+
+    function _onEnterPressed() {
+        changeVisibility(false)
+        toScript({type: "send_message", message: textArea.text, channel: "local"})
+        textArea.text = "";
+    }
+
+    function changeVisibility(state){
+        chat_bar.visible = state
+        if (state) textArea.forceActiveFocus();
+        else root.parent.forceActiveFocus();
+    }
+
+    // Messages from script
+    function fromScript(message) {
+        switch (message.type){
+            case "change_visibility":
+                changeVisibility(message.value)
+                break;
+        }
+    }
+
+    // Send message to script
+    function toScript(packet){
+        sendToScript(packet)
+    }
+}
\ No newline at end of file

From 0bc11e8da44428206ffbc57259145f6899f4f34a Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 20:55:31 -0500
Subject: [PATCH 12/48] Fix for #975 sticky keys.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat_quick_message.qml     | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml b/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml
index a3b897083d..ae110db379 100644
--- a/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat_quick_message.qml
@@ -33,6 +33,19 @@ Rectangle {
 
             Keys.onReturnPressed: { _onEnterPressed(); }
             Keys.onEnterPressed: { _onEnterPressed(); }
+            Keys.onLeftPressed: { moveLeft(); }
+            Keys.onRightPressed: { moveRight(); }
+
+            function moveLeft(){
+                if (cursorPosition > 0){
+                    cursorPosition--
+                }
+            }
+            function moveRight(){
+                if (cursorPosition < text.length){
+                    cursorPosition++
+                }
+            }
         }
 
         Text {

From 1cddc5a3980d00dba1cf2b115edd3dea861ee8c1 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 21:18:08 -0500
Subject: [PATCH 13/48] Added message padding. Resolved "listview" rendering
 warnings. Prevent sending empty messages.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/armored_chat.js    |  2 ++
 .../communityScripts/armored-chat/armored_chat.qml   | 12 +++++-------
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 6674a74538..49c4ba0790 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -186,6 +186,8 @@
         }
     }
     function _sendMessage(message, channel) {
+        if (message.length == 0) return;
+
         Messages.sendMessage(
             "chat",
             JSON.stringify({
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 826e848370..b34d941fc5 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -11,7 +11,6 @@ Rectangle {
     property string last_message_user: ""
     property date last_message_time: new Date()
 
-    // TODO: Find a better way to do this
     // When the window is created on the script side, the window starts open.
     // Once the QML window is created wait, then send the initialized signal.
     // This signal is mostly used to close the "Desktop overlay window" script side
@@ -154,7 +153,7 @@ Rectangle {
                     property string delegateText: model.text
                     property string delegateUsername: model.username
                     property string delegateDate: model.date
-                    width: parent.width // FIXME: Causes warning, but required for style?
+                    width: listview.width
 
                     sourceComponent: {
                         if (model.type === "chat") {
@@ -339,12 +338,12 @@ Rectangle {
             property string username: delegateUsername
             property string date: delegateDate
 
-            width: parent.width
             height: Math.max(65, children[1].height + 30)
             color: index % 2 === 0 ? "transparent" : Qt.rgba(0.15,0.15,0.15,1)
 
             Item {
-                width: parent.width
+                width: parent.width - 10
+                anchors.horizontalCenter: parent.horizontalCenter
                 height: 22
 
                 Text{
@@ -361,6 +360,7 @@ Rectangle {
 
             TextEdit{
                 anchors.top: parent.children[0].bottom
+                x: 5
                 text: texttest
                 color:"white"
                 font.pointSize: 12
@@ -368,7 +368,7 @@ Rectangle {
                 selectByMouse: true
                 selectByKeyboard: true
                 width: parent.width * 0.8
-                height: contentHeight // Adjust height to fit content
+                height: contentHeight
                 wrapMode: Text.Wrap
                 textFormat: TextEdit.RichText
 
@@ -382,8 +382,6 @@ Rectangle {
     Component {
         id: template_notification
 
-        // width: (Math.min(parent.width * 0.8, Math.max(contentWidth, parent.width))) - parent.children[0].width
-
         Rectangle{
             property int index: delegateIndex
             property string texttest: delegateText

From ed3e629f19d61a8e0da3f165fb349f472bb455f1 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 22 May 2024 22:31:28 -0500
Subject: [PATCH 14/48] Image embedding.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.qml             | 21 +++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index b34d941fc5..3bfb7aa95d 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -462,6 +462,8 @@ Rectangle {
         // Format content
         message = formatContent(message);
 
+        message = embedImages(message);
+
         if (type === "notification"){
             channel.append({ text: message, date: date, type: "notification" });
             last_message_user = "";
@@ -507,6 +509,25 @@ Rectangle {
         return mess
     }
 
+    function embedImages(mess){
+        var image_link = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)(?:png|jpe?g|gif|bmp|svg|webp)/g;
+        var matches = mess.match(image_link);
+        var new_message = ""
+        var listed = []
+        var total_emeds = 0
+
+        new_message += mess
+
+        for (var i = 0; matches && matches.length > i && total_emeds < 3; i++){
+            if (!listed.includes(matches[i])) {
+                new_message += "<br><img src="+ matches[i] +" width='250' >"
+                listed.push(matches[i]);
+                total_emeds++
+            } 
+        }
+        return new_message;
+    }
+
     // Messages from script
     function fromScript(message) {
         let time = new Date().toLocaleTimeString(undefined, { hour12: false });

From 0a7b64d7be20bf547d2f74c73e339e8c01faf4c3 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Thu, 23 May 2024 01:50:38 -0500
Subject: [PATCH 15/48] Prevent VR quick_message bar.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.js | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 49c4ba0790..2e45610aeb 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -179,6 +179,8 @@
     function keyPressEvent(event) {
         switch (JSON.stringify(event.key)) {
             case "16777220": // Enter key
+                if (HMD.active) return; // Don't allow in VR
+
                 quick_message.sendToQml({
                     type: "change_visibility",
                     value: true,

From c1e511572d3e78880636315d7077888f233d47fb Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Thu, 23 May 2024 03:32:38 -0500
Subject: [PATCH 16/48] Updated README. Document image embedding. Declared
 notificationCore as dependency.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/README.md   | 28 ++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/scripts/communityScripts/armored-chat/README.md b/scripts/communityScripts/armored-chat/README.md
index 16cd538915..35f190fd59 100644
--- a/scripts/communityScripts/armored-chat/README.md
+++ b/scripts/communityScripts/armored-chat/README.md
@@ -1,6 +1,6 @@
 # Armored Chat
 
-1. What is Armored Chat (AC) and what AC is not
+1. What is Armored Chat
 2. User manual
     - Installation
     - Settings
@@ -9,8 +9,13 @@
 ## What is Armored Chat
 
 Armored Chat is a chat application strictly made to communicate between players in the same domain. It is made using QML and to be as light weight as reasonably possible.
+
+### Dependencies
+
 AC uses the Overte [Messages](https://apidocs.overte.org/Messages.html) API to communicate.
 
+For notifications, AC uses [notificationCore.js](https://github.com/overte-org/overte/blob/bb8bac43eadd3b20956a2ff7b0b21c28844b0f77/scripts/communityScripts/notificationCore/notificationCore.js).
+
 ## User manual
 
 ### Installation
@@ -51,7 +56,28 @@ This action immediately clears the AC history and the session. Functionally this
 
 ### Usability tips
 
+#### Navigation
+
 You can scroll quickly using kinetic scrolling! Try "grabbing" the right side of messages, where the timestamp is, and flinging yourself in a direction.
 
+#### Formatting
+
 You can format messages using basic HTML elements. Try `<div style="color: red"> Red text! </div>` to color your text red.
 Find the full list of Qt rich text tags [here](https://doc.qt.io/qt-6/richtext-html-subset.html). Please note that some of these tags may be intentionally restricted.
+
+#### Media embedding
+
+Images can be embedded when linked directly.
+
+Try it out by linking to the Overte logo! `https://github.com/overte-org/overte/raw/master/interface/resources/images/brand-banner.svg`
+
+In order for images to be embedded, URLs must end in a image filetype.
+Supported filetypes are:
+
+-   `.png`
+-   `.jpg`
+-   `.jpeg`
+-   `.gif`
+-   `.bmp`
+-   `.svg`
+-   `.webp`

From 7ef33224e3d069a0fa17806e7ac175ebe3debe0e Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Thu, 23 May 2024 17:26:59 -0500
Subject: [PATCH 17/48] Moved system chat to script-archive. Updated systemchat
 html script path. Renamed "chat" to "floofChat" to avoid confusion.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 script-archive/{chat => floofChat}/FloofChat.html   |   0
 script-archive/{chat => floofChat}/FloofChat.js     |   0
 script-archive/{chat => floofChat}/FloofChat.qml    |   0
 script-archive/{chat => floofChat}/chat.png         | Bin
 .../{chat => floofChat}/css/FloofChat.css           |   0
 .../{chat => floofChat}/css/materialize.css         |   0
 .../{chat => floofChat}/js/materialize.min.js       |   0
 .../{chat => floofChat}/resources/bubblepop.wav     | Bin
 .../systemChat}/ChatPage.html                       |   0
 .../system => script-archive/systemChat}/chat.js    |   4 ++--
 10 files changed, 2 insertions(+), 2 deletions(-)
 rename script-archive/{chat => floofChat}/FloofChat.html (100%)
 rename script-archive/{chat => floofChat}/FloofChat.js (100%)
 rename script-archive/{chat => floofChat}/FloofChat.qml (100%)
 rename script-archive/{chat => floofChat}/chat.png (100%)
 rename script-archive/{chat => floofChat}/css/FloofChat.css (100%)
 rename script-archive/{chat => floofChat}/css/materialize.css (100%)
 rename script-archive/{chat => floofChat}/js/materialize.min.js (100%)
 rename script-archive/{chat => floofChat}/resources/bubblepop.wav (100%)
 rename {scripts/system/html => script-archive/systemChat}/ChatPage.html (100%)
 rename {scripts/system => script-archive/systemChat}/chat.js (99%)

diff --git a/script-archive/chat/FloofChat.html b/script-archive/floofChat/FloofChat.html
similarity index 100%
rename from script-archive/chat/FloofChat.html
rename to script-archive/floofChat/FloofChat.html
diff --git a/script-archive/chat/FloofChat.js b/script-archive/floofChat/FloofChat.js
similarity index 100%
rename from script-archive/chat/FloofChat.js
rename to script-archive/floofChat/FloofChat.js
diff --git a/script-archive/chat/FloofChat.qml b/script-archive/floofChat/FloofChat.qml
similarity index 100%
rename from script-archive/chat/FloofChat.qml
rename to script-archive/floofChat/FloofChat.qml
diff --git a/script-archive/chat/chat.png b/script-archive/floofChat/chat.png
similarity index 100%
rename from script-archive/chat/chat.png
rename to script-archive/floofChat/chat.png
diff --git a/script-archive/chat/css/FloofChat.css b/script-archive/floofChat/css/FloofChat.css
similarity index 100%
rename from script-archive/chat/css/FloofChat.css
rename to script-archive/floofChat/css/FloofChat.css
diff --git a/script-archive/chat/css/materialize.css b/script-archive/floofChat/css/materialize.css
similarity index 100%
rename from script-archive/chat/css/materialize.css
rename to script-archive/floofChat/css/materialize.css
diff --git a/script-archive/chat/js/materialize.min.js b/script-archive/floofChat/js/materialize.min.js
similarity index 100%
rename from script-archive/chat/js/materialize.min.js
rename to script-archive/floofChat/js/materialize.min.js
diff --git a/script-archive/chat/resources/bubblepop.wav b/script-archive/floofChat/resources/bubblepop.wav
similarity index 100%
rename from script-archive/chat/resources/bubblepop.wav
rename to script-archive/floofChat/resources/bubblepop.wav
diff --git a/scripts/system/html/ChatPage.html b/script-archive/systemChat/ChatPage.html
similarity index 100%
rename from scripts/system/html/ChatPage.html
rename to script-archive/systemChat/ChatPage.html
diff --git a/scripts/system/chat.js b/script-archive/systemChat/chat.js
similarity index 99%
rename from scripts/system/chat.js
rename to script-archive/systemChat/chat.js
index 94a26dc632..14696946b3 100644
--- a/scripts/system/chat.js
+++ b/script-archive/systemChat/chat.js
@@ -4,7 +4,7 @@
 //
 // By Don Hopkins (dhopkins@donhopkins.com) on May 5th, 2017
 //  Copyright 2017 High Fidelity, Inc.
-//  Copyright 2023 Overte e.V.
+//  Copyright 2024 Overte e.V.
 //
 //
 // Distributed under the Apache License, Version 2.0.
@@ -13,7 +13,7 @@
 
 (function() {
 
-    var webPageURL = Script.resolvePath("html/ChatPage.html"); // URL of tablet web page.
+    var webPageURL = Script.resolvePath("ChatPage.html"); // URL of tablet web page.
     var randomizeWebPageURL = true; // Set to true for debugging.
     var lastWebPageURL = ""; // Last random URL of tablet web page.
     var onChatPage = false; // True when chat web page is opened.

From c734e0de80acdee7c630edddd2691b0e3fcba043 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 25 May 2024 05:30:37 -0500
Subject: [PATCH 18/48] Updated Readme. More documentation on link related
 features.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/README.md | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/scripts/communityScripts/armored-chat/README.md b/scripts/communityScripts/armored-chat/README.md
index 35f190fd59..6ecf36316f 100644
--- a/scripts/communityScripts/armored-chat/README.md
+++ b/scripts/communityScripts/armored-chat/README.md
@@ -54,6 +54,14 @@ This action immediately clears the AC history and the session. Functionally this
 
 ---
 
+### Usage
+
+AC has two chat modes: Local, and Domain. Local chat displays all other local chat messages that are within 20 units of you. Domain chat will display all other Domain messages sent though that channel regardless of distance.
+
+AC also handles link embedding. When you send an HTTP(S) link, it will automatically parse it using Qt RichText and allow everyone to click on the message. Next to the link you will also see a "⮺" symbol. Clicking on this symbol will open the link in an external window.
+
+---
+
 ### Usability tips
 
 #### Navigation

From 60a46626d946b3f59ef5829e3665d7a6c7b8654e Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 25 May 2024 05:43:53 -0500
Subject: [PATCH 19/48] Updated URL regex. Seems to preform better at picking
 out what is and what is not a URL.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 3bfb7aa95d..f7f5e3b642 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -498,7 +498,7 @@ Rectangle {
     }
 
     function formatContent(mess) {
-        var link = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)?(?=\s|$)/g;
+        var link = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
         mess = mess.replace(link, (match) => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a> <a onclick='Window.openUrl("+match+")'>⮺</a>"});
 
         var script_tag = /<script\b[^>]*>/gi;
@@ -510,7 +510,7 @@ Rectangle {
     }
 
     function embedImages(mess){
-        var image_link = /(https?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)(?:png|jpe?g|gif|bmp|svg|webp)/g;
+        var image_link = /(https?:(\/){2})[\w.-]+(?:\.[\w\.-]+)+(?:\/[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]*)(?:png|jpe?g|gif|bmp|svg|webp)/g;
         var matches = mess.match(image_link);
         var new_message = ""
         var listed = []

From 8301bfd626cf09e643153225278d3ef2049e5ac3 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 25 May 2024 22:41:17 -0500
Subject: [PATCH 20/48] Added development manual. Changed "avatar_connected"
 type to generic "notification" type. Removed unused commented code.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/README.md   | 122 +++++++++++++++++-
 .../armored-chat/armored_chat.js              |   4 +-
 .../armored-chat/armored_chat.qml             |   4 +-
 3 files changed, 121 insertions(+), 9 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/README.md b/scripts/communityScripts/armored-chat/README.md
index 6ecf36316f..2385494676 100644
--- a/scripts/communityScripts/armored-chat/README.md
+++ b/scripts/communityScripts/armored-chat/README.md
@@ -5,6 +5,7 @@
     - Installation
     - Settings
     - Usability tips
+3. Development
 
 ## What is Armored Chat
 
@@ -52,16 +53,12 @@ Default value is `200`
 
 This action immediately clears the AC history and the session. Functionally this will set the message list to a empty Array.
 
----
-
 ### Usage
 
 AC has two chat modes: Local, and Domain. Local chat displays all other local chat messages that are within 20 units of you. Domain chat will display all other Domain messages sent though that channel regardless of distance.
 
 AC also handles link embedding. When you send an HTTP(S) link, it will automatically parse it using Qt RichText and allow everyone to click on the message. Next to the link you will also see a "⮺" symbol. Clicking on this symbol will open the link in an external window.
 
----
-
 ### Usability tips
 
 #### Navigation
@@ -89,3 +86,120 @@ Supported filetypes are:
 -   `.bmp`
 -   `.svg`
 -   `.webp`
+
+## Development
+
+### To QML communication
+
+Here are the signals needed to communicate from the JavaScript core to the QML interface.
+
+AC calls a `_emitEvent()` function that also includes a `type` key in the object. This `type` tells the QML and/or the JS core what the packet is for.
+When you call the `_emitEvent()` function be sure to include the following signals as a `type`. In the examples below, the `type` is being excluded for brevity.
+
+Example:
+
+```json
+{ type: "show_message", displayName: "username", ...}
+```
+
+#### "show_message"
+
+This signal tells the QML to add a new message to the ListView element list.
+
+Supply a `JSON` object.
+
+```json
+{
+    "displayName": "username",
+    "message": "chat message",
+    "channel": "domain", // Channel to send message on. By default it should only be "domain" or "local".
+    "date": "[ time and date string ]" // Optional, defaults to current time and date.
+}
+```
+
+#### "clear_messages"
+
+Clear all messages displayed in the ListView elements. Note this does not clear the history and this is only a visual erasure.
+
+No payload required.
+
+#### "notification"
+
+Renders a notification to the domain channel.
+The intended use is to provide updates about the domain and make the notifications accessible.
+
+Supply a `JSON` object.
+
+```json
+{
+    "message": "notification message" // Notification to render
+}
+```
+
+#### "initial_settings"
+
+Visually set the settings in the QML interface based on the supplied object.
+
+Supply a `JSON` object.
+
+```json
+{
+    "settings": {
+        // JSON object of current AC settings
+        "external_window": false,
+        "maximum_messages": 200
+    }
+}
+```
+
+### To JS communication
+
+Here are the signals needed to communicate from the QML interface to the JavaScript core. AC is developed in a way that all actions that are not style related are preformed though the JavaScript core.
+This means that what ever action you want to preform must go though the JavaScript core for processing.
+
+This is formatted the same was as the communication packets to the QML interface. Supply the following entries as "type"s in your packet.
+
+#### "send_message"
+
+Tell AC to broadcast a message to the domain.
+
+Supply a `JSON` object.
+
+```json
+{
+    "message": "message content", // The contents of the message to send.
+    "channel": "domain" // Channel to emit the message to.
+}
+```
+
+#### "setting_change"
+
+Tell AC to change a setting. Exercise caution when using this as you can add new settings unintentionally if you are not careful.
+
+Supply a `JSON` object
+
+```json
+{
+    "setting": "external_window", // The name of the setting to change
+    "value": true // The value to change the setting to
+}
+```
+
+#### "action"
+
+Tell AC to preform a generic action. This is normally reserved for functions that would get called on a button onClicked event in the QML.
+
+Supply a `JSON` object
+
+```json
+{
+    "action": "erase_history" // The action to preform
+}
+```
+
+#### "initialized"
+
+Tell AC the QML overlay has loaded successfully.
+This is called to hide the overlay on creation.
+
+No payload required.
diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 2e45610aeb..220f6bcc89 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -224,7 +224,7 @@
             let message = {};
             message.message = `${display_name} ${type}`;
 
-            _emitEvent({ type: "avatar_connected", ...message });
+            _emitEvent({ type: "notification", ...message });
         }, 1500);
     }
     function _loadSettings() {
@@ -251,7 +251,7 @@
     /**
      * Emit a packet to the HTML front end. Easy communication!
      * @param {Object} packet - The Object packet to emit to the HTML
-     * @param {("setting_update"|"show_message")} packet.type - The type of packet it is
+     * @param {("show_message"|"clear_messages"|"notification"|"initial_settings")} packet.type - The type of packet it is
      */
     function _emitEvent(packet = { type: "" }) {
         chat_overlay_window.sendToQml(packet);
diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index f7f5e3b642..51ee44297e 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -59,7 +59,6 @@ Rectangle {
                     Behavior on width {
                         NumberAnimation {
                             duration: 50
-                            // easing.type: Easeing.InOutQuad
                         }
                     }
 
@@ -94,7 +93,6 @@ Rectangle {
                     MouseArea {
                         anchors.fill: parent
                         onClicked: {
-                            // addMessage("usertest", "Clicked", "Now", "domain", "notification");
                             pageVal = "domain"
                         }
                     }
@@ -537,7 +535,7 @@ Rectangle {
             case "show_message":
                 addMessage(message.displayName, message.message, `[ ${message.timeString || time} - ${message.dateString || date} ]`, message.channel, "chat");
                 break;
-            case "avatar_connected":
+            case "notification":
                 addMessage("SYSTEM", message.message, `[ ${time} - ${date} ]`, "domain", "notification");
                 break;
             case "clear_messages":

From 82605c50c421794d12328f3999ca2d993189ccf2 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sun, 26 May 2024 20:30:33 -0500
Subject: [PATCH 21/48] Scroll to bottom on launch.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../communityScripts/armored-chat/armored_chat.qml  | 13 +++++++++++--
 1 file changed, 11 insertions(+), 2 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 51ee44297e..37dd760360 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -20,11 +20,20 @@ Rectangle {
         running: true
         repeat: false
         onTriggered: {
-           toScript({type: "initialized"})
+            toScript({type: "initialized"});
+        }
+    }
+    Timer {
+        id: load_scroll_timer
+        interval: 1000
+        running: true
+        repeat: false
+        onTriggered: {
+           scrollToBottom();
         }
     }
     // Component.onCompleted: {
-    //     toScript({type: "initialized"})
+    //     toScript({type: "initialized"});
     // }
 
     // User view

From 01306b39711070621b2d3c6015dde7bedbfc787f Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sun, 26 May 2024 20:34:53 -0500
Subject: [PATCH 22/48] Scroll to bottom when changing page.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 37dd760360..a928391da7 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -16,17 +16,18 @@ Rectangle {
     // This signal is mostly used to close the "Desktop overlay window" script side
     // https://github.com/overte-org/overte/issues/824
     Timer {
-        interval: 100
+        interval: 10
         running: true
         repeat: false
         onTriggered: {
             toScript({type: "initialized"});
+            load_scroll_timer.running = true
         }
     }
     Timer {
         id: load_scroll_timer
-        interval: 1000
-        running: true
+        interval: 100
+        running: false
         repeat: false
         onTriggered: {
            scrollToBottom();
@@ -75,6 +76,7 @@ Rectangle {
                         anchors.fill: parent
                         onClicked: {
                             pageVal = "local";
+                            load_scroll_timer.running = true;
                         }
                     }
                 }
@@ -103,6 +105,7 @@ Rectangle {
                         anchors.fill: parent
                         onClicked: {
                             pageVal = "domain"
+                            load_scroll_timer.running = true;
                         }
                     }
                 }

From b27f24e4a9ca91a1a92a2e057361fcfca1d594cd Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Wed, 5 Jun 2024 17:21:41 -0500
Subject: [PATCH 23/48] Initial Commit.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/system/controllers/mouseLook.js | 240 +++++++-----------------
 1 file changed, 65 insertions(+), 175 deletions(-)

diff --git a/scripts/system/controllers/mouseLook.js b/scripts/system/controllers/mouseLook.js
index 751bda2150..bb777c8f58 100644
--- a/scripts/system/controllers/mouseLook.js
+++ b/scripts/system/controllers/mouseLook.js
@@ -1,204 +1,95 @@
-/*
-mouseLook.js – mouse look switching script
-by rampa3 (https://github.com/rampa3) and vegaslon (https://github.com/vegaslon)
-*/
-(function() { // BEGIN LOCAL_SCOPE
+//
+// mouseLook.js
+//
+// By Armored Dragon (June 6). Refactored from Rampa3 & Vegaslon work
+//  Copyright 2024 Overte e.V.
+//
+//
+// Distributed under the Apache License, Version 2.0.
+// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
+//
 
-    var away;
+(() => {
+    // States ----
+    let mouse_look_active = Settings.getValue("mouselook-active", false);
+    let mouse_look_enabled = Camera.getMouseLook();
+    let hmd_active = HMD.active;
+    let overlay_active = Desktop.isOverlayWindowFocused();
 
-    var hmd = HMD.active;
-
-    var mouseLookEnabled = Camera.getMouseLook();
-
-    var tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
-
-    var tabletUp;
-    
-    var keysOnOverlay = Desktop.isOverlayWindowFocused();
-
-    var tempOff = false;
-
-    var altMode = false;
+    // Resources ----
+    let tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
 
+    // Events ----
     Camera.mouseLookChanged.connect(onMouseLookChanged);
-
-    function onMouseLookChanged(newMouseLook) {
-        mouseLookEnabled = newMouseLook;
-    }
-
-    if (!hmd){
-        if (mouseLookEnabled) {
-            if (!keysOnOverlay) {
-                if (!tablet.tabletShown){
-                    Window.displayAnnouncement("Mouse look: ON");
-                    mouseLookOn();
-                } else {
-                    Window.displayAnnouncement("Tablet is up – mouse look temporarily OFF.");
-                }
-            }
-        }
-    }
-
     Controller.keyPressEvent.connect(onKeyPressEvent);
+    Desktop.uiFocusChanged.connect(onUiFocusChanged);
+    HMD.displayModeChanged.connect(onDisplayModeChanged);
+    MyAvatar.wentActive.connect(onWentActive);
+    MyAvatar.wentAway.connect(onWentAway);
+    tablet.tabletShownChanged.connect(onTabletShownChanged);
+    Script.scriptEnding.connect(onScriptEnding);
+
+    // Program ----
+    function onMouseLookChanged(newMouseLook) {
+        mouse_look_enabled = newMouseLook;
+    }
 
     function onKeyPressEvent(event) {
-        if (!hmd){
-            if(event.isAlt){
-                if (keysOnOverlay) return;
-                if (!mouseLookEnabled) return;
-                mouseLookOff();
-                Window.displayAnnouncement("Mouse look: Temporarily OFF");
-                tempOff = true;
-                altMode = true;
-            }
-            if (tempOff && altMode && ['left', 'right', 'up', 'down', 'esc', 'w', 'a', 's', 'd'].includes(event.text.toLowerCase())){
-                if (keysOnOverlay) return;
-                if (!mouseLookEnabled) return;
-                mouseLookOn();
-                tempOff = false;
-                altMode = false
-            }
-            if (event.text.toLowerCase() === 'm') {
-                if (!keysOnOverlay) {
-                    if (mouseLookEnabled) {
-                        if (!Camera.getCaptureMouse()){
-                            tempOff = false;
-                            Window.displayAnnouncement("Mouse look: ON");
-                            mouseLookOn();
-                        } else {
-                            tempOff = true;
-                            Window.displayAnnouncement("Mouse look: Temporarily OFF");
-                            mouseLookOff();
-                        }
-                    }
-                }
+        // Toggle using the m key
+        if (event.text.toLowerCase() === "m") {
+            if (Camera.captureMouse) {
+                mouse_look_active = false;
+                Settings.setValue("mouselook-active", false);
+                disableMouseLook();
+            } else {
+                mouse_look_active = true;
+                Settings.setValue("mouselook-active", true);
+                enableMouseLook();
             }
         }
     }
 
-    tablet.tabletShownChanged.connect(onTabletShownChanged);
-
     function onTabletShownChanged() {
-        if (!hmd) {
-            if (mouseLookEnabled) {
-                if (!tablet.toolbarMode) {
-                    if (!keysOnOverlay) {
-                        if (tablet.tabletShown) {
-                            tabletUp = true;
-                            if (!tempOff) {
-                                if (!away) {
-                                    Window.displayAnnouncement("Tablet is up – mouse look temporarily OFF.");
-                                    mouseLookOff();
-                                }
-                            }
-                        } else if (!tablet.tabletShown) {
-                            tabletUp = false;
-                            if (!tempOff) {
-                                if (!away && !keysOnOverlay) {
-                                    Window.displayAnnouncement("Tablet hidden – mouse look ON.");
-                                    mouseLookOn();
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
+        if (tablet.tabletShown) disableMouseLook();
+        else enableMouseLook();
     }
 
-    MyAvatar.wentAway.connect(onWentAway);
-
     function onWentAway() {
-        if (!hmd) {
-            if (mouseLookEnabled) {
-                away = true;
-                if (!keysOnOverlay) {
-                    if (!tabletUp){
-                        Window.displayAnnouncement("Away state ON – mouse look temporarily OFF.")
-                        tempOff = false;
-                        mouseLookOff()
-                    }
-                }
-            }
-        }
+        disableMouseLook();
     }
 
-    MyAvatar.wentActive.connect(onWentActive);
-
     function onWentActive() {
-        if (!hmd) {
-            if (mouseLookEnabled) {
-                away = false;
-                if (!keysOnOverlay) {
-                    if (!tabletUp) {
-                        Window.displayAnnouncement("Away state OFF – mouse look ON.");
-                        mouseLookOn();
-                    }
-                }
-            }
-        }
+        enableMouseLook();
     }
 
-    HMD.displayModeChanged.connect(onDisplayModeChanged);
-
     function onDisplayModeChanged() {
-        if (mouseLookEnabled) {
-            if (HMD.active) {
-                hmd = true;
-                mouseLookOff();
-            } else {
-                hmd = false;
-                if (!tempOff) {
-                    if (!keysOnOverlay) {
-                        if (!tabletUp) {
-                            mouseLookOn();
-                        }
-                    }
-                }
-            }
+        hmd_active = HMD.active;
+        if (hmd_active) disableMouseLook();
+        else enableMouseLook();
+    }
+
+    function onUiFocusChanged(keyFocus) {
+        if (keyFocus) {
+            overlay_active = true;
+            disableMouseLook();
+        } else {
+            overlay_active = false;
+            enableMouseLook();
         }
     }
 
-    function mouseLookOn() {
-        if (mouseLookEnabled)
-            Camera.captureMouse = true;
+    function enableMouseLook() {
+        if (hmd_active) return;
+        if (tablet.tabletShown) return;
+        if (overlay_active) return;
+        if (!mouse_look_active) return; // Mouse look disabled via the hotkey
+
+        Camera.captureMouse = true;
     }
 
-    function mouseLookOff() {
+    function disableMouseLook() {
         Camera.captureMouse = false;
     }
-    
-    Desktop.uiFocusChanged.connect(onUiFocusChanged);
-    
-    function onUiFocusChanged(keyFocus) {
-        if (!hmd) {
-            if (mouseLookEnabled) {
-                if (keyFocus) {
-                    keysOnOverlay = true;
-                    if (Camera.captureMouse) {
-                        mouseLookOff();
-                    }
-                } else {
-                    keysOnOverlay = false;
-                    if (!tablet.tabletShown) {
-                        if (!tempOff) {
-                            if (!away) {
-                                mouseLookOn();
-                            }
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    Messages.messageReceived.connect(onMessageReceived);
-    function onMessageReceived(channel, message, sender, localOnly) {
-        if (channel === "Hifi-Away-Enable")
-            if (message === 'enable') mouseLookOn();
-    }
-
-    Script.scriptEnding.connect(onScriptEnding);
 
     function onScriptEnding() {
         Camera.captureMouse = false;
@@ -211,5 +102,4 @@ by rampa3 (https://github.com/rampa3) and vegaslon (https://github.com/vegaslon)
         Desktop.uiFocusChanged.disconnect(onUiFocusChanged);
         Script.scriptEnding.disconnect(onScriptEnding);
     }
-
-}()); // END LOCAL_SCOPE
+})();

From 8dce22792b4a26ba3cf1a95cfb30f41cb22edf87 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 8 Jun 2024 07:50:08 -0500
Subject: [PATCH 24/48] Fixed variable names.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/system/controllers/mouseLook.js | 28 ++++++++++++-------------
 1 file changed, 14 insertions(+), 14 deletions(-)

diff --git a/scripts/system/controllers/mouseLook.js b/scripts/system/controllers/mouseLook.js
index bb777c8f58..e29a741865 100644
--- a/scripts/system/controllers/mouseLook.js
+++ b/scripts/system/controllers/mouseLook.js
@@ -11,10 +11,10 @@
 
 (() => {
     // States ----
-    let mouse_look_active = Settings.getValue("mouselook-active", false);
-    let mouse_look_enabled = Camera.getMouseLook();
-    let hmd_active = HMD.active;
-    let overlay_active = Desktop.isOverlayWindowFocused();
+    let mouseLookActive = Settings.getValue("mouselook-active", false);
+    let mouseLookEnabled = Camera.getMouseLook();
+    let hmdActive = HMD.active;
+    let overlayActive = Desktop.isOverlayWindowFocused();
 
     // Resources ----
     let tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
@@ -31,18 +31,18 @@
 
     // Program ----
     function onMouseLookChanged(newMouseLook) {
-        mouse_look_enabled = newMouseLook;
+        mouseLookEnabled = newMouseLook;
     }
 
     function onKeyPressEvent(event) {
         // Toggle using the m key
         if (event.text.toLowerCase() === "m") {
             if (Camera.captureMouse) {
-                mouse_look_active = false;
+                mouseLookActive = false;
                 Settings.setValue("mouselook-active", false);
                 disableMouseLook();
             } else {
-                mouse_look_active = true;
+                mouseLookActive = true;
                 Settings.setValue("mouselook-active", true);
                 enableMouseLook();
             }
@@ -63,26 +63,26 @@
     }
 
     function onDisplayModeChanged() {
-        hmd_active = HMD.active;
-        if (hmd_active) disableMouseLook();
+        hmdActive = HMD.active;
+        if (hmdActive) disableMouseLook();
         else enableMouseLook();
     }
 
     function onUiFocusChanged(keyFocus) {
         if (keyFocus) {
-            overlay_active = true;
+            overlayActive = true;
             disableMouseLook();
         } else {
-            overlay_active = false;
+            overlayActive = false;
             enableMouseLook();
         }
     }
 
     function enableMouseLook() {
-        if (hmd_active) return;
+        if (hmdActive) return;
         if (tablet.tabletShown) return;
-        if (overlay_active) return;
-        if (!mouse_look_active) return; // Mouse look disabled via the hotkey
+        if (overlayActive) return;
+        if (!mouseLookActive) return; // Mouse look disabled via the hotkey
 
         Camera.captureMouse = true;
     }

From 6e73a0e4f6323598f7e2a42481fc7cfa36b0d4ce Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 8 Jun 2024 08:07:33 -0500
Subject: [PATCH 25/48] Don't run inspect.js when mouse look is enabled.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/system/inspect.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/scripts/system/inspect.js b/scripts/system/inspect.js
index 9aacfd14c2..b14107b3de 100644
--- a/scripts/system/inspect.js
+++ b/scripts/system/inspect.js
@@ -253,6 +253,9 @@ function keyPressEvent(event) {
         if (isEditUsingCamera) {
             return;
         }
+        if (Settings.getValue("mouselook-active", false)){
+            return;
+        }
         alt = true;
         changed = true;
         Picks.enablePick(pick);

From 7fc1a3d17541b2e14afb17b0b5303849caaeca72 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 8 Jun 2024 08:34:04 -0500
Subject: [PATCH 26/48] Fixed scrolling too far.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index a928391da7..e153c2fec9 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -458,11 +458,7 @@ Rectangle {
 
     function scrollToBottom() {
         if (listview.count == 0) return;
-        // if (chat_scrollbar.visualPosition > 0.85) {
-        listview.positionViewAtIndex(listview.count - 1, ListView.End);
         listview.positionViewAtEnd();
-        listview.contentY = listview.contentY + 50;
-        // }
     }
 
 

From eb84de84b404e4148dd0158e30c2d4f980abace2 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 8 Jun 2024 08:41:53 -0500
Subject: [PATCH 27/48] Corrected case on variables.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.js              | 111 +++++++++---------
 1 file changed, 53 insertions(+), 58 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 220f6bcc89..6525b83645 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -10,7 +10,7 @@
 (() => {
     "use strict";
 
-    var app_is_visible = false;
+    var appIsVisible = false;
     var settings = {
         external_window: false,
         maximum_messages: 200,
@@ -18,22 +18,22 @@
 
     // Global vars
     var tablet;
-    var chat_overlay_window;
-    var app_button;
-    var quick_message;
+    var chatOverlayWindow;
+    var appButton;
+    var quickMessage;
     const channels = ["domain", "local"];
-    var message_history = Settings.getValue("ArmoredChat-Messages", []) || [];
-    var max_local_distance = 20; // Maximum range for the local chat
-    var pal_data = AvatarManager.getPalData().data;
+    var messageHistory = Settings.getValue("ArmoredChat-Messages", []) || [];
+    var maxLocalDistance = 20; // Maximum range for the local chat
+    var palData = AvatarManager.getPalData().data;
 
     Controller.keyPressEvent.connect(keyPressEvent);
     Messages.subscribe("chat");
     Messages.messageReceived.connect(receivedMessage);
-    AvatarManager.avatarAddedEvent.connect((session_id) => {
-        _avatarAction("connected", session_id);
+    AvatarManager.avatarAddedEvent.connect((sessionId) => {
+        _avatarAction("connected", sessionId);
     });
-    AvatarManager.avatarRemovedEvent.connect((session_id) => {
-        _avatarAction("left", session_id);
+    AvatarManager.avatarRemovedEvent.connect((sessionId) => {
+        _avatarAction("left", sessionId);
     });
 
     startup();
@@ -41,62 +41,61 @@
     function startup() {
         tablet = Tablet.getTablet("com.highfidelity.interface.tablet.system");
 
-        app_button = tablet.addButton({
+        appButton = tablet.addButton({
             icon: Script.resolvePath("./img/icon_white.png"),
             activeIcon: Script.resolvePath("./img/icon_black.png"),
             text: "CHAT",
-            isActive: app_is_visible,
+            isActive: appIsVisible,
         });
 
         // When script ends, remove itself from tablet
         Script.scriptEnding.connect(function () {
             console.log("Shutting Down");
-            tablet.removeButton(app_button);
-            chat_overlay_window.close();
+            tablet.removeButton(appButton);
+            chatOverlayWindow.close();
         });
 
         // Overlay button toggle
-        app_button.clicked.connect(toggleMainChatWindow);
+        appButton.clicked.connect(toggleMainChatWindow);
 
-        quick_message = new OverlayWindow({
+        quickMessage = new OverlayWindow({
             source: Script.resolvePath("./armored_chat_quick_message.qml"),
         });
 
         _openWindow();
     }
     function toggleMainChatWindow() {
-        app_is_visible = !app_is_visible;
-        console.log(`App is now ${app_is_visible ? "visible" : "hidden"}`);
-        app_button.editProperties({ isActive: app_is_visible });
-        chat_overlay_window.visible = app_is_visible;
+        appIsVisible = !appIsVisible;
+        console.log(`App is now ${appIsVisible ? "visible" : "hidden"}`);
+        appButton.editProperties({ isActive: appIsVisible });
+        chatOverlayWindow.visible = appIsVisible;
 
         // External window was closed; the window does not exist anymore
-        if (chat_overlay_window.title == "" && app_is_visible) {
+        if (chatOverlayWindow.title == "" && appIsVisible) {
             _openWindow();
         }
     }
     function _openWindow() {
-        chat_overlay_window = new Desktop.createWindow(
+        chatOverlayWindow = new Desktop.createWindow(
             Script.resolvePath("./armored_chat.qml"),
             {
                 title: "Chat",
                 size: { x: 550, y: 400 },
                 additionalFlags: Desktop.ALWAYS_ON_TOP,
-                visible: app_is_visible,
+                visible: appIsVisible,
                 presentationMode: Desktop.PresentationMode.VIRTUAL,
             }
         );
 
-        chat_overlay_window.closed.connect(toggleMainChatWindow);
-        chat_overlay_window.fromQml.connect(fromQML);
-        quick_message.fromQml.connect(fromQML);
+        chatOverlayWindow.closed.connect(toggleMainChatWindow);
+        chatOverlayWindow.fromQml.connect(fromQML);
+        quickMessage.fromQml.connect(fromQML);
     }
     function receivedMessage(channel, message) {
         // Is the message a chat message?
         channel = channel.toLowerCase();
         if (channel !== "chat") return;
-        console.log(`Received message:\n${message}`);
-        var message = JSON.parse(message);
+        message = JSON.parse(message);
 
         message.channel = message.channel.toLowerCase(); // Make sure the "local", "domain", etc. is formatted consistently
 
@@ -104,7 +103,7 @@
         if (
             message.channel == "local" &&
             Vec3.distance(MyAvatar.position, message.position) >
-                max_local_distance
+                maxLocalDistance
         )
             return; // If message is local, and if player is too far away from location, don't do anything
 
@@ -120,25 +119,23 @@
         );
 
         // Save message to history
-        let saved_message = message;
-        delete saved_message.position;
-        saved_message.timeString = new Date().toLocaleTimeString(undefined, {
+        let savedMessage = message;
+        delete savedMessage.position;
+        savedMessage.timeString = new Date().toLocaleTimeString(undefined, {
             hour12: false,
         });
-        saved_message.dateString = new Date().toLocaleDateString(undefined, {
+        savedMessage.dateString = new Date().toLocaleDateString(undefined, {
             year: "numeric",
             month: "long",
             day: "numeric",
         });
-        message_history.push(saved_message);
-        while (message_history.length > settings.maximum_messages) {
-            message_history.shift();
+        messageHistory.push(savedMessage);
+        while (messageHistory.length > settings.maximum_messages) {
+            messageHistory.shift();
         }
-        Settings.setValue("ArmoredChat-Messages", message_history);
+        Settings.setValue("ArmoredChat-Messages", messageHistory);
     }
     function fromQML(event) {
-        console.log(`New QML event:\n${JSON.stringify(event)}`);
-
         switch (event.type) {
             case "send_message":
                 _sendMessage(event.message, event.channel);
@@ -149,7 +146,7 @@
 
                 switch (event.setting) {
                     case "external_window":
-                        chat_overlay_window.presentationMode = event.value
+                        chatOverlayWindow.presentationMode = event.value
                             ? Desktop.PresentationMode.NATIVE
                             : Desktop.PresentationMode.VIRTUAL;
                         break;
@@ -171,7 +168,7 @@
                 break;
             case "initialized":
                 // https://github.com/overte-org/overte/issues/824
-                chat_overlay_window.visible = app_is_visible; // The "visible" field in the Desktop.createWindow does not seem to work. Force set it to the initial state (false)
+                chatOverlayWindow.visible = appIsVisible; // The "visible" field in the Desktop.createWindow does not seem to work. Force set it to the initial state (false)
                 _loadSettings();
                 break;
         }
@@ -181,7 +178,7 @@
             case "16777220": // Enter key
                 if (HMD.active) return; // Don't allow in VR
 
-                quick_message.sendToQml({
+                quickMessage.sendToQml({
                     type: "change_visibility",
                     value: true,
                 });
@@ -201,28 +198,28 @@
             })
         );
     }
-    function _avatarAction(type, session_id) {
+    function _avatarAction(type, sessionId) {
         Script.setTimeout(() => {
             if (type == "connected") {
-                pal_data = AvatarManager.getPalData().data;
+                palData = AvatarManager.getPalData().data;
             }
 
             // Get the display name of the user
-            let display_name = "";
-            display_name =
-                AvatarManager.getPalData([session_id])?.data[0]
+            let displayName = "";
+            displayName =
+                AvatarManager.getPalData([sessionId])?.data[0]
                     ?.sessionDisplayName || null;
-            if (display_name == null) {
-                for (let i = 0; i < pal_data.length; i++) {
-                    if (pal_data[i].sessionUUID == session_id) {
-                        display_name = pal_data[i].sessionDisplayName;
+            if (displayName == null) {
+                for (let i = 0; i < palData.length; i++) {
+                    if (palData[i].sessionUUID == sessionId) {
+                        displayName = palData[i].sessionDisplayName;
                     }
                 }
             }
 
             // Format the packet
             let message = {};
-            message.message = `${display_name} ${type}`;
+            message.message = `${displayName} ${type}`;
 
             _emitEvent({ type: "notification", ...message });
         }, 1500);
@@ -230,9 +227,9 @@
     function _loadSettings() {
         settings = Settings.getValue("ArmoredChat-Config", settings);
 
-        if (message_history) {
+        if (messageHistory) {
             // Load message history
-            message_history.forEach((message) => {
+            messageHistory.forEach((message) => {
                 delete message.action;
                 _emitEvent({ type: "show_message", ...message });
             });
@@ -240,8 +237,6 @@
 
         // Send current settings to the app
         _emitEvent({ type: "initial_settings", settings: settings });
-
-        console.log(`\n\n\n` + JSON.stringify(settings));
     }
     function _saveSettings() {
         console.log("Saving config");
@@ -254,6 +249,6 @@
      * @param {("show_message"|"clear_messages"|"notification"|"initial_settings")} packet.type - The type of packet it is
      */
     function _emitEvent(packet = { type: "" }) {
-        chat_overlay_window.sendToQml(packet);
+        chatOverlayWindow.sendToQml(packet);
     }
 })();

From 192d80aece5bc628a87afea2e0be456e9c1f7ba9 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 8 Jun 2024 11:51:25 -0500
Subject: [PATCH 28/48] Floofchat compatibility. Made conversion functions to
 allow communication between apps. Removed developer console.log function.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 .../armored-chat/armored_chat.js              | 36 ++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index 6525b83645..df8c3fb03f 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -66,7 +66,6 @@
     }
     function toggleMainChatWindow() {
         appIsVisible = !appIsVisible;
-        console.log(`App is now ${appIsVisible ? "visible" : "hidden"}`);
         appButton.editProperties({ isActive: appIsVisible });
         chatOverlayWindow.visible = appIsVisible;
 
@@ -97,6 +96,9 @@
         if (channel !== "chat") return;
         message = JSON.parse(message);
 
+        // Floofchat compatibility hook
+        message = floofChatCompatibilityConversion(message);
+
         message.channel = message.channel.toLowerCase(); // Make sure the "local", "domain", etc. is formatted consistently
 
         if (!channels.includes(message.channel)) return; // Check the channel
@@ -197,6 +199,8 @@
                 action: "send_chat_message",
             })
         );
+
+        floofChatCompatibilitySendMessage(message, channel);
     }
     function _avatarAction(type, sessionId) {
         Script.setTimeout(() => {
@@ -251,4 +255,34 @@
     function _emitEvent(packet = { type: "" }) {
         chatOverlayWindow.sendToQml(packet);
     }
+
+    //
+    // Floofchat compatibility functions
+    // Added to ease the transition between Floofchat to ArmoredChat
+    // These functions can be safely removed at a much later date.
+    function floofChatCompatibilityConversion(message) {
+        if (message.type === "TransmitChatMessage" && !message.forApp) {
+            return {
+                position: message.position,
+                message: message.message,
+                displayName: message.displayName,
+                channel: message.channel.toLowerCase(),
+            };
+        }
+        return message;
+    }
+
+    function floofChatCompatibilitySendMessage(message, channel) {
+        Messages.sendMessage(
+            "Chat",
+            JSON.stringify({
+                position: MyAvatar.position,
+                message: message,
+                displayName: MyAvatar.sessionDisplayName,
+                channel: channel.charAt(0).toUpperCase() + channel.slice(1),
+                type: "TransmitChatMessage",
+                forApp: "Floof",
+            })
+        );
+    }
 })();

From 7e100a18702192b3e6d958e0ee8c27fc719834e4 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Fri, 28 Jun 2024 10:42:39 -0500
Subject: [PATCH 29/48] Raise VR Keyboard when textfield is selected

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index e153c2fec9..2f8670fc6d 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -215,6 +215,10 @@ Rectangle {
                                 text = ""
                             }
                         }
+                        onFocusChanged: {
+                            if (focus) return ApplicationInterface.showVRKeyboardForHudUI(true);
+                            ApplicationInterface.showVRKeyboardForHudUI(false);
+                        }
                     }
                     Button {
                         width: 60

From 6cbacc1d86d8d0742e8abad2c570757db14f45a9 Mon Sep 17 00:00:00 2001
From: HifiExperiments <thingsandstuffblog@gmail.com>
Date: Sat, 29 Jun 2024 14:02:57 -0700
Subject: [PATCH 30/48] fix mtoon issue on GLES

---
 libraries/graphics/src/graphics/MaterialTextures.slh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/libraries/graphics/src/graphics/MaterialTextures.slh b/libraries/graphics/src/graphics/MaterialTextures.slh
index 083a1146be..425b8deed8 100644
--- a/libraries/graphics/src/graphics/MaterialTextures.slh
+++ b/libraries/graphics/src/graphics/MaterialTextures.slh
@@ -465,7 +465,7 @@ float fetchUVAnimationMaskMap(vec2 uv) {
 <@func evalMaterialShadingShift(fetchedShadingShift, materialShadingShift, matKey, shadingShift)@>
 {
     <$shadingShift$> = mix(0.0, <$materialShadingShift$>, float((<$matKey$> & SHADING_SHIFT_VAL_BIT) != 0));
-    <$shadingShift$> += mix(0.0, <$fetchedShadingShift$>.r, float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0));
+    <$shadingShift$> += mix(0.0, <$fetchedShadingShift$>, float((<$matKey$> & SHADING_SHIFT_MAP_BIT) != 0));
 }
 <@endfunc@>
 

From e917427ca8026d37377df794a209ae5139f5cee7 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 30 Jun 2024 00:27:47 +0200
Subject: [PATCH 31/48] Fix Fedora 40 server packages.

---
 .github/workflows/linux_server_build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/linux_server_build.yml b/.github/workflows/linux_server_build.yml
index 1931726120..575b443a6e 100644
--- a/.github/workflows/linux_server_build.yml
+++ b/.github/workflows/linux_server_build.yml
@@ -93,12 +93,12 @@ jobs:
             runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
 
           - os: fedora-40
-            image: docker.io/overte/overte-server-build:0.1.4-fedora-39-amd64
+            image: docker.io/overte/overte-server-build:0.1.4-fedora-40-amd64
             arch: amd64
             runner: [self_hosted, type-cx52, image-x86-app-docker-ce]
 
           - os: fedora-40
-            image: docker.io/overte/overte-server-build:0.1.4-fedora-39-aarch64
+            image: docker.io/overte/overte-server-build:0.1.4-fedora-40-aarch64
             arch: aarch64
             runner: [self_hosted, type-cax41, image-arm-app-docker-ce]
 

From 69105e716a148d6aa14db51f20a048e501b726a7 Mon Sep 17 00:00:00 2001
From: Karol Suprynowicz <ksuprynowicz@post.pl>
Date: Sun, 30 Jun 2024 20:59:32 +0200
Subject: [PATCH 32/48] Increase default vertical FOV

---
 libraries/shared/src/ViewFrustum.h    | 2 +-
 scripts/system/libraries/WebTablet.js | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libraries/shared/src/ViewFrustum.h b/libraries/shared/src/ViewFrustum.h
index 9c80538e60..672ad74d36 100644
--- a/libraries/shared/src/ViewFrustum.h
+++ b/libraries/shared/src/ViewFrustum.h
@@ -28,7 +28,7 @@ const int NUM_FRUSTUM_CORNERS = 8;
 const int NUM_FRUSTUM_PLANES = 6;
 
 const float DEFAULT_CENTER_SPHERE_RADIUS = 3.0f;
-const float DEFAULT_FIELD_OF_VIEW_DEGREES = 45.0f;
+const float DEFAULT_FIELD_OF_VIEW_DEGREES = 55.0f;
 const float DEFAULT_ASPECT_RATIO = 16.0f/9.0f;
 const float DEFAULT_NEAR_CLIP = 0.08f;
 const float DEFAULT_FAR_CLIP = 16384.0f;
diff --git a/scripts/system/libraries/WebTablet.js b/scripts/system/libraries/WebTablet.js
index 3e5f487695..0adf182321 100644
--- a/scripts/system/libraries/WebTablet.js
+++ b/scripts/system/libraries/WebTablet.js
@@ -19,7 +19,7 @@ var Y_AXIS = {x: 0, y: 1, z: 0};
 var X_AXIS = {x: 1, y: 0, z: 0};
 var DEFAULT_DPI = 31;
 var DEFAULT_WIDTH = 0.4375;
-var DEFAULT_VERTICAL_FIELD_OF_VIEW = 45; // degrees
+var DEFAULT_VERTICAL_FIELD_OF_VIEW = 55; // degrees
 var SENSOR_TO_ROOM_MATRIX = -2;
 var CAMERA_MATRIX = -7;
 var ROT_Y_180 = {x: 0.0, y: 1.0, z: 0, w: 0};

From ab878f8813dbc35113026393c2233da2297c0eb2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Mon, 1 Jul 2024 23:33:02 +0200
Subject: [PATCH 33/48] Update make-rpm-server to remove `-` characters. This
 should prevent errors such as: Illegal char '-' (0x2d) in: Version:       
 2024.07.1-rc1.3ddd205

---
 pkg-scripts/make-rpm-server | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/pkg-scripts/make-rpm-server b/pkg-scripts/make-rpm-server
index cf84bb97f2..2767b3b8c5 100755
--- a/pkg-scripts/make-rpm-server
+++ b/pkg-scripts/make-rpm-server
@@ -13,7 +13,8 @@ fi
 # The regex below extracts the path from the VCPKG_INSTALL_ROOT variable. Said variable gets populated during the CMake step.
 VCPKG_INSTALL_ROOT=`grep VCPKG_INSTALL_ROOT $OVERTE/build/vcpkg.cmake | perl -ne 'm/set\(VCPKG_INSTALL_ROOT\s+\"(.*?)\"/; print $1'`
 
-VERSION=${RPMVERSION}
+# Remove minus character from version numbers, because rpmtool doesn't allow them.
+VERSION=${RPMVERSION//-}
 
 if [ "$OVERTE_USE_SYSTEM_QT" = "" ]; then
 SOFILES=`ls \

From f6f9d37527cbab30806f0ad40ee9c699c2bacd12 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Tue, 2 Jul 2024 13:44:21 +0200
Subject: [PATCH 34/48] Update CHANGELOG to version 2023.11.1.

---
 CHANGELOG.md | 77 ++++++++++++++++++++++++++++++++++++++++++++--------
 1 file changed, 66 insertions(+), 11 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9a1d98ebb0..d0b2053028 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -12,14 +12,13 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 This project does **not** adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
 
-<!-- ## [Unreleased] 2023.07.22 -->
-<!-- ## [2023.07.1] 2023.07.22 -->
+<!-- ## [Unreleased] 2023.07.2 -->
 
-<!--
-### Misc
-- Updated the Unity Avatar Exporter and added Linux support
-- Added Linux support to the Unity Avatar Exporter
--->
+<!-- ## [2023.07.1] 2023.07.2 -->
+
+<!-- ## [2023.06.1] 2023.06.12 -->
+
+## [2023.11.1] 2023.11.24
 
 ### Fixes
 - Fixed color conversion for glTF material colors (PR307)
@@ -36,14 +35,37 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
 - Hugely improved Create app performance in Domains with many entities (PR498)
 - Fixed an issue that could cause laser pointers to rapidly flash (PR495)
 - Fixed the connection failure dialog mentioning "Explore" instead of "Places" (PR515)
+- Fixed broken documentation and code-completion of the "Script" API namespace (PR450)
+- Fixed warning about ForceHtmlAudioOutputDeviceUpdate (PR473)
+- Fixed Shield bubble dimensions (PR510)
+- Fixed Places app connecting to IP instead of Place name (PR522)
+- Fixed Interface scripts failing to shut down (PR521)
+- Fixed deadlock related to entity script engines (PR540)
+- Fixed leave and join messages in Chat (PR573)
+- Fixed crash when closing script log window (PR520)
+- Fixed some API documentation (PR598)
+- Fixed some missing assets, notably the sound when successfully shaking hands (PR590)
+- Fixed multiple script engine reload and shutdown related crashes (PR574)
+- Fixed flow bones on avatars with scale ≠ 100 (PR604)
+- Fixed curved flow bone chains (PR604)
+- Fixed invisible cursor (PR629)
+- Fixed loading avatars from URLs containing" =" such as Dropbox (PR634)
+- Fixed MicBar type error spam on Windows (PR669)
+- Fixed grabbing local entities in VR (PR671)
+- Fixed memory leak in entity server and improved its performance (PR690)
+- Fixed chat bar appearing in VR (PR672)
+- Fixed issues with third-party apps such as ALVR, Virtual Desktop and Streaming Assistant (PR700,PR714)
+- Fixed custom graphics settings not being saved (PR706)
+- Fixed Script.require behavior  (PR697)
+- Fixed Entities.setLocalJointRotation not updating (PR708)
+- Improved client performance by moving squeezeHands.js to separate thread (PR737)
 
 ### Changes
-- Replaced Vircadia Metaverse Server with a testing server as federation default (PR330)
 - An empty audio device list now throws a warning instead of just a debug message (PR347)
 - Increased the maximum log file size from 512 kiB to 10 MiB (PR342,PR513)
 - Decreased the amount of retained log files from 100 to 20 (PR342)
 - Pressing the Return key with the the address/search bar in the Places App selected now navigates you to that address (PR403)
-- Replaced QT Script with V8 scripting engine (PR185,PR507,PR519)
+- Replaced QT Script with V8 scripting engine (PR185,PR507,PR519,PR566)
 	This is a huge change under the hood, which ended up fixing a lot of issues.
 	Since the new scripting engine does not behave exactly the same as the old one,
 	some scripts might need fixing. The new scripting engine is especially picky when it comes to undefined behaviour.
@@ -54,6 +76,14 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
 	It will also ask once in case of a non-stable build.
 - Changed the VR overlay to only recenter when moving (PR478)
 - Added a workaround that prevents most users from needing to press down on the thumbstick to move (PR481,PR512)
+- Lowered inertia while moving (PR542)
+- Lowered control delays in VR (PR542)
+	Configurable under Settings → Controls → Calibration
+- Changed Home button in Places app to lead to the tutorial by default (PR560)
+- Rewritten tutorial wizard in QML (PR645,PR737)
+- Disabled Oculus VR plugin by default (PR700,PR714)
+- Changed gravity constant to be more realistic (PR729)
+	This fixes being catapulted into the air when moving up a slope. It also improves taking off, flying, and general movement.
 
 ### Additions
 - Added option to graphics menu for choosing which screen to use for full screen mode (PR302)
@@ -62,12 +92,28 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
 	This allows typing in languages like Japanese or Chinese that make use of an IME.
 - Added vertical Field Of View setting to graphics menu (PR465)
 - Added crash reporting to the Domain server, Assignment client, and Oven (PR482)
+- Added JavaScript profiling API (PR564)
+- Added require() to global scope in scipting API (PR585)
+- Added support for HDR lightmaps (PR611)
+- Added mouse look (PR607,PR624,PR627,PR662)
+- Dropbox URLs to assets now get rewritten to DDL URLs (PR636)
+- Added development script to configure avatar smoothing (PR579)
+- Added distance based LOD (PR663)
+	Configurable under Settings → Graphics → Target frame rate
+- Added support for QML inside web-entities (PR645)
+	QML files must be whitelisted in the settings.
+- Added Discord rich presence support (PR686,PR723)
+- Added command line arguments to ICE server (PR722)
 
 ### Removals
 - Removed outdated Inventory and Marketplace options from Wearables UI (PR303)
 - Removed outdated Beacon system (PR327)
 - Removed long deprecated styles-uit and controls-uit QML modules (PR380)
 - Removed outdated Marketplace and Wallet code (PR381,PR477,PR487)
+- Removed Appreciate app from defaults (PR563)
+- Removed debug messages from Places app (PR561)
+- Removed JQuery dependency from Emote app (PR560)
+- Removed File API (PR691)
 
 ### Build system
 - Fixed error in configuration step on some rolling release Linux distributions (PR301)
@@ -78,12 +124,21 @@ This project does **not** adhere to [Semantic Versioning](https://semver.org/spe
 - Updated TBB dependency from version 2019_U8-1 to 2021.5.0 (PR412)
 - Fixed NVTT compilation on Visual Studio 2022 (PR374)
 - Disabled libOVR on MSVC 2022 (PR430)
-- Added Qt 5.15.9 package for aarch64 Ubuntu 20.04 (PR409)
 - Fixed build error on aarch64 (PR409)
-- Replaced QT Script with V8/libnode (PR185,PR409,PR443)
+- Replaced QT Script with V8/libnode (PR185,PR409,PR443,PR535,PR566)
 - Updated Qt on Windows to 5.15.10 with KDE patches (PR448)
 - Updated included OpenSSL to 3.0.5 (PR448)
+- Updated OpenSSL Windwos dependency (PR448)
+- Changed libnode dependency to be built from source (PR452)
+- Disabled Crashpad on aarch64 Linux by default (PR526)
+- Added discord-rpc dependency (PR686)
+- Fixed building with memory debugging (PR704)
+- Updated VCPKG on Windows to version 2023.10.19 (PR730)
 
+### Security
+- Updated Qt packages to fix CVE-2023-4863 (PR630,PR631)
+- Updated Qt packages to fix CVE-2023-5217(PR652,PR653)
+- Limited audio recording location (PR691)
 
 
 ## [2022.12.1] 2022.12.24

From d786308aab64b55f8a5401fc809e1c0ff310f080 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Thu, 4 Jul 2024 22:04:49 +0200
Subject: [PATCH 35/48] Always enable crash reporting.

---
 .github/workflows/linux_server_build.yml | 16 +++++++---------
 1 file changed, 7 insertions(+), 9 deletions(-)

diff --git a/.github/workflows/linux_server_build.yml b/.github/workflows/linux_server_build.yml
index 575b443a6e..407e9993cc 100644
--- a/.github/workflows/linux_server_build.yml
+++ b/.github/workflows/linux_server_build.yml
@@ -199,15 +199,13 @@ jobs:
 
         echo "BUILD_NUMBER=$GIT_COMMIT_SHORT" >> $GITHUB_ENV
 
-        if [ -z "$CMAKE_BACKTRACE_URL" ]; then
-          if [ "${{ github.ref_type }}" == "tag" ]; then
-            export CMAKE_BACKTRACE_URL="${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}"
-            export CMAKE_BACKTRACE_TOKEN="${{ github.ref_name }}_${{ matrix.os }}_${{ github.sha }}"
-          else
-            # We're building a PR, default to the PR endpoint
-            export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32"
-            export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}"
-          fi
+        if [ "${{ github.ref_type }}" == "tag" ]; then
+          export CMAKE_BACKTRACE_URL="${{ secrets.SENTRY_MINIDUMP_ENDPOINT }}"
+          export CMAKE_BACKTRACE_TOKEN="${{ github.ref_name }}_${{ matrix.os }}_${{ github.sha }}"
+        else
+          # We're building a PR, default to the PR endpoint
+          export CMAKE_BACKTRACE_URL="https://o4504831972343808.ingest.sentry.io/api/4504832427950080/minidump/?sentry_key=f511de295975461b8f92a36f4a4a4f32"
+          export CMAKE_BACKTRACE_TOKEN="server_pr_${{ github.event.number }}_${{ github.sha }}"
         fi
 
     - name: Configure Build Environment 3

From 34f308ac481d29b782b8496e6f7d550b76101800 Mon Sep 17 00:00:00 2001
From: Dale Glass <dale@daleglass.net>
Date: Thu, 4 Jul 2024 22:14:34 +0200
Subject: [PATCH 36/48] Fix crash in setCrashReporterEnabled

---
 .../src/crash-handler/CrashHandlerBackend_Crashpad.cpp       | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/libraries/networking/src/crash-handler/CrashHandlerBackend_Crashpad.cpp b/libraries/networking/src/crash-handler/CrashHandlerBackend_Crashpad.cpp
index 1b93ebecb4..70b0189f29 100644
--- a/libraries/networking/src/crash-handler/CrashHandlerBackend_Crashpad.cpp
+++ b/libraries/networking/src/crash-handler/CrashHandlerBackend_Crashpad.cpp
@@ -457,6 +457,11 @@ bool startCrashHandler(std::string appPath, std::string crashURL, std::string cr
 }
 
 void setCrashReportingEnabled(bool enabled) {
+    if (!crashpadDatabase) {
+        qCCritical(crash_handler) << "Can't set to enabled, crash handler not initialized!";
+        return;
+    }
+
     auto settings = crashpadDatabase->GetSettings();
     settings->SetUploadsEnabled(enabled);
 

From c6d2e567f6596315d9955f1d14f3a5b1587aa938 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Sat, 6 Jul 2024 10:26:42 -0500
Subject: [PATCH 37/48] Fix keyboard being invoked when not in VR.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 2f8670fc6d..8d43d92395 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -216,6 +216,7 @@ Rectangle {
                             }
                         }
                         onFocusChanged: {
+                            if (!HMD.active) return;
                             if (focus) return ApplicationInterface.showVRKeyboardForHudUI(true);
                             ApplicationInterface.showVRKeyboardForHudUI(false);
                         }

From 9c4cfd2eda0a427d99f6a48e5ed8c342589e383d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 09:58:22 +0200
Subject: [PATCH 38/48] Fix GHA building servers individually for every tag,
 instead of just on the server tag.

---
 .github/workflows/linux_server_build.yml | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/.github/workflows/linux_server_build.yml b/.github/workflows/linux_server_build.yml
index 407e9993cc..dad77c75da 100644
--- a/.github/workflows/linux_server_build.yml
+++ b/.github/workflows/linux_server_build.yml
@@ -26,8 +26,8 @@ env:
 
 jobs:
   build:
-    # Only run master or tagged builds, or PRs if labeled as "server"
-    if: contains( github.event.pull_request.labels.*.name, 'server') || github.event_name != 'pull_request'
+    # Only run master or tagged builds, or PRs when labeled as "server"
+    if: github.event.label.name == 'server'|| github.event_name != 'pull_request'
     name: "${{matrix.os}}, ${{matrix.arch}}"
     strategy:
       matrix:

From d13d1c64c52ea7d4e76fecb7fdcaaa6147f00aaf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 09:45:05 +0200
Subject: [PATCH 39/48] Fix broken upload paths for RPM distributions.

---
 .github/workflows/linux_server_build.yml | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/linux_server_build.yml b/.github/workflows/linux_server_build.yml
index 407e9993cc..c39b976255 100644
--- a/.github/workflows/linux_server_build.yml
+++ b/.github/workflows/linux_server_build.yml
@@ -132,6 +132,8 @@ jobs:
           echo "GIT_COMMIT_SHORT=`echo ${{ github.sha }} | cut -c1-7`" >> $GITHUB_ENV
         fi
 
+        echo "REFNAME=${{ github.ref_name }}" >> $GITHUB_ENV
+
         echo "JOB_NAME=${{matrix.os}}, ${{matrix.arch}}" >> $GITHUB_ENV
 
         echo "CMAKE_BUILD_EXTRA=-- -j$(nproc)" >> $GITHUB_ENV
@@ -184,7 +186,8 @@ jobs:
           echo "RELEASE_NUMBER=${{ github.run_number }}" >> $GITHUB_ENV
         else # tagged
           echo "DEBVERSION=${{ github.ref_name }}-$GIT_COMMIT_SHORT-${{ matrix.os }}" >> $GITHUB_ENV
-          echo "RPMVERSION=${{ github.ref_name }}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV
+          # We remove all dash characters from RPM versions, because rpmbuild doesn't allow dashes in version numbers.
+          echo "RPMVERSION=${REFNAME//-}.$GIT_COMMIT_SHORT" >> $GITHUB_ENV
         fi
 
         if [ "${{ github.ref_type }}" == "tag" ]; then  # tagged

From d214d8fc666d3eadb9cc68c6df2b7ab70f9de5a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 12:29:46 +0200
Subject: [PATCH 40/48] Update VCPKG on Windows, so it can find newer versions
 of Visual Studio.

---
 hifi_vcpkg.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/hifi_vcpkg.py b/hifi_vcpkg.py
index 4dc9268980..ff1549ab1e 100644
--- a/hifi_vcpkg.py
+++ b/hifi_vcpkg.py
@@ -101,7 +101,7 @@ endif()
         if 'Windows' == system:
             self.exe = os.path.join(self.path, 'vcpkg.exe')
             self.bootstrapCmds = [ os.path.join(self.path, 'bootstrap-vcpkg.bat'), '-disableMetrics' ]
-            self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-windows_x86_64_2023.10.19.zip'
+            self.vcpkgUrl = self.assets_url + '/dependencies/vcpkg/vcpkg-windows_x86_64_2024.06.15.zip'
             self.vcpkgHash = 'f335234f0722c15376fb10747f558c18c83a3e1e3b6565cf0dabfb18c9625a99234d054457fd05190c0ecd7a59ca43305bc93b50dbf764a4e1f567a15168d051'
             self.hostTriplet = 'x64-windows'
             if usePrebuilt:

From 3894330e2f61a599f52b2267dee52b711e6e41f6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 14:53:09 +0200
Subject: [PATCH 41/48] Add note about extra Windows SDK dependency.

---
 cmake/ports/webrtc/README.md | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/cmake/ports/webrtc/README.md b/cmake/ports/webrtc/README.md
index 398d6e7e04..79b79eff0d 100644
--- a/cmake/ports/webrtc/README.md
+++ b/cmake/ports/webrtc/README.md
@@ -180,6 +180,8 @@ Also use the path in the `gn gen` commands, below, making sure to escape `\`s as
 
 Use a VS2019 developer command prompt in the *src* directory.
 
+If it complains about the debugger being missing from your Windows SDK, modify your Windows "Software Development Kit" in your installed programs (See: https://stackoverflow.com/questions/46237620/how-to-install-debugging-tools-with-visual-studio-2017-installer).
+
 Create a release build of the WebRTC library:
 - `gn gen --ide=vs2019 out\Release --filters=//:webrtc "--args=is_debug=false is_clang=false use_custom_libcxx=false libcxx_is_shared=true symbol_level=2 use_lld=false rtc_include_tests=false rtc_build_tools=false rtc_build_examples=false proprietary_codecs=true rtc_use_h264=true enable_libaom=false rtc_enable_protobuf=false rtc_build_ssl=false rtc_ssl_root=\"<path>\""`
 - `ninja -C out\Release`

From c82b9cdee9a46f31def14a295720bfa32bae3771 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 23:45:01 +0200
Subject: [PATCH 42/48] Remove ancient update code. I think it is save to say
 that no one will be updating a HiFi installation from 2019 at this point.

---
 cmake/templates/NSIS.template.in | 47 --------------------------------
 1 file changed, 47 deletions(-)

diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in
index ae5397770b..9a7fd78bbe 100644
--- a/cmake/templates/NSIS.template.in
+++ b/cmake/templates/NSIS.template.in
@@ -1207,53 +1207,6 @@ Section "-Core installation"
   ; 2016-02-25 - The following delete blocks are temporary and can be removed once users who had the initial installer have updated
   ; 2019-09-10 - (3 and a half years later) Sure they are buddy.  Sure they are.
 
-  ; MessageBox MB_OK|MB_ICONEXCLAMATION "installer type is @INSTALLER_TYPE@"
-
-  ;Delete any server executables that might have been installed by bad versions of the client-only installer, but ONLY if we are a client-only installer
-  ${If} "@INSTALLER_TYPE@" == "client_only"
-    ; MessageBox MB_OK|MB_ICONEXCLAMATION "trying to delete server binaries"
-    Delete "$INSTDIR\assignment-client.exe"
-    Delete "$INSTDIR\domain-server.exe"
-  ${EndIf}
-
-  ;Delete any server-console files installed before it was placed in sub-folder
-  Delete "$INSTDIR\server-console.exe"
-  RMDir /r "$INSTDIR\locales"
-  RMDir /r "$INSTDIR\resources\app"
-  RMDir /r "$INSTDIR\client"
-  Delete "$INSTDIR\resources\atom.asar"
-  Delete "$INSTDIR\build-info.json"
-  Delete "$INSTDIR\content_resources_200_percent.pak"
-  Delete "$INSTDIR\content_shell.pak"
-  Delete "$INSTDIR\LICENSE"
-  Delete "$INSTDIR\LICENSES.chromium.html"
-  Delete "$INSTDIR\natives_blob.bin"
-  Delete "$INSTDIR\node.dll"
-  Delete "$INSTDIR\pdf.dll"
-  Delete "$INSTDIR\snapshot_blob.bin"
-  Delete "$INSTDIR\ui_resources_200_percent.pak"
-  Delete "$INSTDIR\vccorlib120.dll"
-  Delete "$INSTDIR\version"
-  Delete "$INSTDIR\xinput1_3.dll"
-
-  ; Delete old desktop shortcuts before they were renamed during Sandbox rename
-  Delete "$DESKTOP\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
-  Delete "$DESKTOP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
-
-  ; Delete old Start Menu shortcuts before Sandbox rename
-  Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@PRE_SANDBOX_INTERFACE_SHORTCUT_NAME@.lnk"
-  Delete "$SMPROGRAMS\$STARTMENU_FOLDER\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
-
-  ; Delete old startup item for Server Console before Sandbox rename
-  SetShellVarContext current
-  Delete "$SMSTARTUP\@PRE_SANDBOX_CONSOLE_SHORTCUT_NAME@.lnk"
-  SetShellVarContext all
-
-  ; Rename the incorrectly cased Raleway font
-  Rename "$INSTDIR\resources\qml\styles-uit\RalewaySemibold.qml" "$INSTDIR\resources\qml\styles-uit\RalewaySemiBold.qml"
-
-  ; Remove the Old Interface directory and vcredist_x64.exe (from installs prior to Server Console)
-  RMDir /r "$INSTDIR\Interface"
 
   ;Use the entire tree produced by the INSTALL target.  Keep the
   ;list of directories here in sync with the RMDir commands below.

From 6e92053007d3b5789d7c44100a7b58a7fcf944c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Sun, 7 Jul 2024 23:46:26 +0200
Subject: [PATCH 43/48] Delete the old hifiNeuron.dll. We stopped providing
 hifiNeuron and the newest release candidate doesn't start with the old plugin
 file present.

---
 cmake/templates/NSIS.template.in | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/cmake/templates/NSIS.template.in b/cmake/templates/NSIS.template.in
index 9a7fd78bbe..68897dee2c 100644
--- a/cmake/templates/NSIS.template.in
+++ b/cmake/templates/NSIS.template.in
@@ -1207,6 +1207,8 @@ Section "-Core installation"
   ; 2016-02-25 - The following delete blocks are temporary and can be removed once users who had the initial installer have updated
   ; 2019-09-10 - (3 and a half years later) Sure they are buddy.  Sure they are.
 
+  ; Delete old hifiNeuron.dll, since we dropped support for it and it causes a crash on startup.
+  Delete "$INSTDIR\plugins\hifiNeuron.dll"
 
   ;Use the entire tree produced by the INSTALL target.  Keep the
   ;list of directories here in sync with the RMDir commands below.

From 6d3d90f6a29f94ebf69293aca6339338ea97c5bd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Julian=20Gro=C3=9F?= <julian.g@posteo.de>
Date: Mon, 8 Jul 2024 12:31:25 +0200
Subject: [PATCH 44/48] Remove remnants of RELEASE_NAME.

---
 cmake/macros/GenerateInstallers.cmake                      | 5 ++---
 interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml | 3 ++-
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/cmake/macros/GenerateInstallers.cmake b/cmake/macros/GenerateInstallers.cmake
index 8e873738b5..564475f406 100644
--- a/cmake/macros/GenerateInstallers.cmake
+++ b/cmake/macros/GenerateInstallers.cmake
@@ -5,7 +5,7 @@
 #  Created by Leonardo Murillo on 12/16/2015.
 #  Copyright 2015 High Fidelity, Inc.
 #  Copyright 2021 Vircadia contributors.
-#  Copyright 2022 Overte e.V.
+#  Copyright 2022-2024 Overte e.V.
 #
 #  Distributed under the Apache License, Version 2.0.
 #  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -33,8 +33,7 @@ macro(GENERATE_INSTALLERS)
   set(CPACK_PACKAGE_NAME ${_DISPLAY_NAME})
   set(CPACK_PACKAGE_VENDOR "Overte")
   set(CPACK_PACKAGE_VERSION ${BUILD_VERSION})
-  # There is some sort of bug which adds a "-" between the BUILD_VERSION and the RELEASE_NAME.
-  set(CPACK_PACKAGE_FILE_NAME "Overte${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}${RELEASE_NAME}")
+  set(CPACK_PACKAGE_FILE_NAME "Overte${_PACKAGE_NAME_EXTRA}-${BUILD_VERSION}")
   set(CPACK_NSIS_DISPLAY_NAME ${_DISPLAY_NAME})
   set(CPACK_NSIS_PACKAGE_NAME ${_DISPLAY_NAME})
   set(CPACK_NSIS_COMPRESSOR "LZMA")
diff --git a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml
index cac6ab6865..1ac2c878bb 100644
--- a/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml
+++ b/interface/resources/qml/hifi/dialogs/TabletAboutDialog.qml
@@ -4,6 +4,7 @@
 //  Created by David Rowe on 18 Apr 2017
 //  Copyright 2017 High Fidelity, Inc.
 //  Copyright 2020 Vircadia contributors.
+//  Copyright 2024 Overte e.V.
 //
 //  Distributed under the Apache License, Version 2.0.
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
@@ -39,7 +40,7 @@ Rectangle {
                 color: "white"
             }
             RalewayRegular {
-                text: "Build " + About.buildVersion + " " + About.releaseName
+                text: "Build " + About.buildVersion
                 size: 16
                 color: "white"
             }

From 6cf6e6c9ec945037690fd25667e6229b71d4845c Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Mon, 8 Jul 2024 11:21:04 -0500
Subject: [PATCH 45/48] Disallow all text formatting by default. Removed now
 redundant <script> filtering.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index 8d43d92395..ff945f6a73 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -509,12 +509,12 @@ Rectangle {
     }
 
     function formatContent(mess) {
+        var arrow = /\</gi
+        mess = mess.replace(arrow, "&lt;");
+
         var link = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
         mess = mess.replace(link, (match) => {return "<a onclick='Window.openUrl("+match+")' href='" + match + "'>" + match + "</a> <a onclick='Window.openUrl("+match+")'>⮺</a>"});
 
-        var script_tag = /<script\b[^>]*>/gi;
-        mess = mess.replace(script_tag, "script");
-
         var newline = /\n/gi;
         mess = mess.replace(newline, "<br>");
         return mess

From 252b33b73fbbebeac203a68ba637a8aaf660dfd8 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Mon, 8 Jul 2024 11:25:19 -0500
Subject: [PATCH 46/48] Removed commented code.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.qml | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.qml b/scripts/communityScripts/armored-chat/armored_chat.qml
index ff945f6a73..13506c226a 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.qml
+++ b/scripts/communityScripts/armored-chat/armored_chat.qml
@@ -33,9 +33,6 @@ Rectangle {
            scrollToBottom();
         }
     }
-    // Component.onCompleted: {
-    //     toScript({type: "initialized"});
-    // }
 
     // User view
     Item {

From fe4625839b40c3002d497160eeaf2204f657dfc9 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Tue, 9 Jul 2024 13:23:44 -0500
Subject: [PATCH 47/48] Sanity check on message channel.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.js | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index df8c3fb03f..d74f1203ac 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -96,9 +96,10 @@
         if (channel !== "chat") return;
         message = JSON.parse(message);
 
+        if (!message.channel) message.channel = 'domain'; // We don't know where to put this message. Assume it is a domain wide message.
+
         // Floofchat compatibility hook
         message = floofChatCompatibilityConversion(message);
-
         message.channel = message.channel.toLowerCase(); // Make sure the "local", "domain", etc. is formatted consistently
 
         if (!channels.includes(message.channel)) return; // Check the channel

From 9964d91729b1c63d5c1ab857b2f5c76e162f3ff5 Mon Sep 17 00:00:00 2001
From: Armored Dragon <publicmail@armoreddragon.com>
Date: Thu, 11 Jul 2024 14:21:32 -0500
Subject: [PATCH 48/48] Fixed message subscription.

Signed-off-by: Armored Dragon <publicmail@armoreddragon.com>
---
 scripts/communityScripts/armored-chat/armored_chat.js | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/scripts/communityScripts/armored-chat/armored_chat.js b/scripts/communityScripts/armored-chat/armored_chat.js
index d74f1203ac..fe66da7a54 100644
--- a/scripts/communityScripts/armored-chat/armored_chat.js
+++ b/scripts/communityScripts/armored-chat/armored_chat.js
@@ -8,7 +8,7 @@
 //  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
 
 (() => {
-    "use strict";
+    ("use strict");
 
     var appIsVisible = false;
     var settings = {
@@ -27,6 +27,7 @@
     var palData = AvatarManager.getPalData().data;
 
     Controller.keyPressEvent.connect(keyPressEvent);
+    Messages.subscribe("Chat"); // Floofchat
     Messages.subscribe("chat");
     Messages.messageReceived.connect(receivedMessage);
     AvatarManager.avatarAddedEvent.connect((sessionId) => {
@@ -96,7 +97,8 @@
         if (channel !== "chat") return;
         message = JSON.parse(message);
 
-        if (!message.channel) message.channel = 'domain'; // We don't know where to put this message. Assume it is a domain wide message.
+        if (!message.channel) message.channel = "domain"; // We don't know where to put this message. Assume it is a domain wide message.
+        if (message.forApp) return; // Floofchat
 
         // Floofchat compatibility hook
         message = floofChatCompatibilityConversion(message);