Re-release of Multistream Title Updater

Minor Printer.bot improvements
This commit is contained in:
nuttylmao
2025-09-13 21:12:33 +10:00
parent d95462f65a
commit c5e9890eb2
36 changed files with 1389 additions and 593 deletions
+3 -3
View File
@@ -16,9 +16,9 @@
<img src="../../resources/logo.png" style="height: 40px;" />
<label id="title" class="title"></label>
<div id="header-end">
<button id="sb-actions-button" onclick="OpenRequiredActionsDialog()">
<button id="sb-actions-button" class="icon-button" onclick="OpenRequiredActionsDialog()">
</button>
<button id="sb-status-button" onclick="OpenConnectDialog()">
<button id="sb-status-button" class="icon-button" onclick="OpenConnectDialog()">
<img id="sb-status-icon" style="height: 1em;" />
</button>
</div>
@@ -29,7 +29,7 @@
</iframe>
<!-- Blur Layer -->
<div id="blur-layer"></div>
<div id="blur-layer" class="blur"></div>
<!-- Streamer.bot Required Actions Dialog -->
<div id="sb-required-actions-dialog" class="dialog">
+37 -14
View File
@@ -7,6 +7,7 @@ const urlParams = new URLSearchParams(queryString);
const configJson = urlParams.get("config") || "";
///////////////////
// PAGE ELEMENTS //
///////////////////
@@ -22,18 +23,25 @@ const sbRequiredActionsSuccessLabel = document.getElementById('sb-required-actio
const sbRequiredActionsFailureLabel = document.getElementById('sb-required-actions-failure');
const sbRequiredActionsFailureSubtext = document.getElementById('sb-required-actions-failure-subtext');
const sbRequiredActionsList = document.getElementById('sb-required-actions-list');
const sbImportCodeLabel = document.getElementById('sb-import-code');
const sbImportCopyButton = document.getElementById('sb-import-copy-button');
const sbActionsButton = document.getElementById('sb-actions-button');
const sbStatusButton = document.getElementById('sb-status-button');
const sbStatusIcon = document.getElementById('sb-status-icon');
const blurLayer = document.getElementById('blur-layer');
const contentIFrame = document.getElementById('content');
//////////////////////
// GLOBAL VARIABLES //
//////////////////////
let sbClientListeners;
/////////////////////////
@@ -54,7 +62,7 @@ const sbServerAddress = sbAddressInput.value;
const sbServerPort = sbPortInput.value;
const sbServerPassword = sbPasswordInput.value;
window.sbClient = new StreamerbotClient({
sbClient = new StreamerbotClient({
host: sbServerAddress,
port: sbServerPort,
password: sbServerPassword,
@@ -65,6 +73,16 @@ window.sbClient = new StreamerbotClient({
console.debug(data);
SetConnectionState(true);
// Notify iframe
contentIFrame.addEventListener("load", () => {
NotifyTheContentIFrameThatSBHasConnectedSuccessfullySoItCanDoStuff(data);
});
NotifyTheContentIFrameThatSBHasConnectedSuccessfullySoItCanDoStuff();
// Idk why but listeners get cleared when re-connecting, so copy them back in
if (sbClientListeners)
sbClient.listeners = sbClientListeners;
},
onDisconnect: () => {
@@ -89,7 +107,7 @@ function SetConnectionState(isConnected) {
sbErrorLabel.style.display = 'none';
sbStatusIcon.src = 'icons/connected.svg';
sbStatusButton.title = `Connected to ${window.sbClient.info.name} (${window.sbClient.info.version})`;
sbStatusButton.title = `Connected to ${sbClient.info.name} (${sbClient.info.version})`;
// Check required actions
CheckRequiredActions();
@@ -157,7 +175,7 @@ async function CheckRequiredActions() {
// Iterate over required SB actions and check if they're present
config.requiredSbActions.forEach(req => {
const exists = response.actions.some(act => act.id === req.id);
console.debug(`${req.name}: ${exists ? 'Found' : 'Missing'}`)
// As soon as one is found that doesn't exist, throw up warning
@@ -188,8 +206,7 @@ async function CheckRequiredActions() {
sbRequiredActionsList.appendChild(item);
});
}
else
{
else {
// There are no required actions, so hide the button
sbActionsButton.style.display = 'none';
}
@@ -199,8 +216,7 @@ async function CheckRequiredActions() {
}
function SetRequiredActionState(isSuccess) {
if (isSuccess)
{
if (isSuccess) {
sbRequiredActionsSuccessLabel.style.display = 'block';
sbRequiredActionsFailureLabel.style.display = 'none';
sbRequiredActionsFailureSubtext.style.display = 'none';
@@ -209,12 +225,11 @@ function SetRequiredActionState(isSuccess) {
sbActionsButton.textContent = '✅';
}
else
{
else {
sbRequiredActionsSuccessLabel.style.display = 'none';
sbRequiredActionsFailureLabel.style.display = 'block';
sbRequiredActionsFailureSubtext.style.display = 'block';
sbActionsButton.title = `You are missing Streamer.bot actions`;
sbActionsButton.textContent = '⚠️';
@@ -228,7 +243,8 @@ function SetRequiredActionState(isSuccess) {
// PAGE INTERACTIONS //
///////////////////////
function Connect() {
function Connect() {
sbClientListeners = sbClient.listeners;
sbClient.options.host = sbAddressInput.value;
sbClient.options.port = sbPortInput.value;
sbClient.options.password = sbPasswordInput.value;
@@ -240,7 +256,7 @@ function CopyImportCode() {
navigator.clipboard.writeText(textToCopy)
.then(() => {
console.debug('Copied to clipboard!');
// Click feedback
const root = document.documentElement;
const successColor = getComputedStyle(root).getPropertyValue('--success-color').trim();
@@ -273,4 +289,11 @@ function OpenRequiredActionsDialog() {
function CloseRequiredActionsDialog() {
sbRequiredActionsDialog.style.display = "none";
blurLayer.style.display = "none";
}
function NotifyTheContentIFrameThatSBHasConnectedSuccessfullySoItCanDoStuff(data) {
contentIFrame.contentWindow.postMessage(
{ type: "sbClientConnected", data },
"*" // replace with iframe origin for security if needed
);
}
+2 -21
View File
@@ -17,7 +17,8 @@ body {
align-items: center;
padding: 0em 1em;
gap: 1em;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 1);
box-shadow: 0 0 10px rgba(0,0,0,1);
z-index: 1;
}
#header-end {
@@ -25,11 +26,6 @@ body {
align-items: center;
display: flex;
flex-direction: row;
gap: 1em;
}
#header-end button {
padding: 0.2em;
}
@@ -41,21 +37,6 @@ body {
#content {
flex: 1;
overflow: auto;
margin: 1em;
}
/******************/
/*** BLUR LAYER ***/
/******************/
#blur-layer {
position: fixed;
inset: 0; /* top:0; left:0; bottom:0; right:0 */
backdrop-filter: blur(10px); /* blur the content behind */
-webkit-backdrop-filter: blur(10px); /* Safari support */
z-index: 1; /* behind the modal */
}
+122 -35
View File
@@ -34,20 +34,25 @@ body {
/************/
.title {
font-weight: 900;
font-size: 1.2em;
text-transform: uppercase;
font-weight: 900;
font-size: 1.2em;
text-transform: uppercase;
}
.field {
display: flex;
flex-direction: column;
gap: 0.5em;
display: flex;
flex-direction: column;
gap: 0.5em;
}
.setting-description {
font-size: 0.9em;
font-weight: 100;
font-size: 0.9em;
font-weight: 200;
}
.setting-attribute {
font-size: 0.8em;
font-weight: 200;
}
@@ -80,6 +85,9 @@ button {
padding: 0.5em 1em;
width: 100%;
transition: all 0.2s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
}
button:hover {
@@ -92,26 +100,66 @@ button:disabled {
cursor: inherit;
}
.icon-button {
margin-left: auto;
width: 1em;
height: 1em;
background: transparent;
border: none;
font-size: 1.5em;
font-weight: 100;
padding: 1em;
border-radius: 50%;
}
.icon-button:hover {
background: var(--button-color);
}
/************************/
/*** INPUT TEXT BOXES ***/
/************************/
textarea,
input {
font-family: inherit;
border-radius: 0.5em;
width: auto;
padding: 0.5em;
background-color: #171717;
border: 1px solid #404040;
border: none;
outline: 1px solid #404040;
color: white;
font-size: 0.9em;
transition: 0.2s;
}
textarea:disabled,
input:disabled {
opacity: 0.5;
}
textarea:focus,
input:focus,
select:focus {
outline: 1px solid var(--accent-color);
}
textarea {
resize: vertical;
}
.textarea-description {
min-height: 10em;
}
textarea::-webkit-resizer {
display: none;
}
/***********************/
@@ -226,16 +274,16 @@ iframe {
/***************/
.callout {
font-size: 0.9em;
font-weight: 100;
background-color: var(--callout-background);
display: flex;
flex-direction: column;
gap: 1em;
padding: 1em;
font-size: 0.9em;
font-weight: 100;
background-color: var(--callout-background);
display: flex;
flex-direction: column;
gap: 1em;
padding: 1em;
border-radius: 0.5em;
border: 1px solid #40404080;
border-radius: 0.5em;
border: 1px solid #40404080;
}
@@ -245,21 +293,22 @@ iframe {
/**************/
.dialog {
font-size: 1em;
background-color: var(--dialog-background);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
gap: 1em;
padding: 1em;
font-size: 1em;
background-color: var(--dialog-background);
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
gap: 1em;
padding: 1em;
border-radius: 0.5em;
border: 1px solid #40404080;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
z-index: 1000; /* above overlay */
border-radius: 0.5em;
border: 1px solid #40404080;
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5);
z-index: 1000;
/* above overlay */
}
.dialog-nav-button {
@@ -271,11 +320,49 @@ iframe {
width: 1em;
height: 1em;
position: absolute;
position: fixed; /* fixed to viewport */
top: 0.5em; /* small offset from top */
right: 0.5em; /* small offset from right */
position: fixed;
/* fixed to viewport */
top: 0.5em;
/* small offset from top */
right: 0.5em;
/* small offset from right */
}
.dialog-nav-button:hover {
background: var(--button-color);
}
/******************/
/*** BLUR LAYER ***/
/******************/
.blur {
position: fixed;
inset: 0;
/* top:0; left:0; bottom:0; right:0 */
backdrop-filter: blur(10px);
/* blur the content behind */
-webkit-backdrop-filter: blur(10px);
/* Safari support */
z-index: 1;
/* behind the modal */
}
/***************/
/*** SELECTS ***/
/***************/
select {
padding: 0.5em;
font-size: 0.9em;
border-radius: 0.5em;
border: none;
outline: 1px solid #404040;
background-color: var(--callout-background);
color: #fff;
cursor: pointer;
min-width: 8em;
}