23 Commits

Author SHA1 Message Date
arindy 8a212a5044 Merge pull request 'adds html rich preview' (#31) from rich-preview into main
CI / deploy (push) Successful in 10m46s
Reviewed-on: #31
2025-02-16 13:28:16 +01:00
arindy 09473f627b adds html rich preview
CI / deploy (push) Successful in 5m15s
CI / deploy (pull_request) Successful in 10m10s
2025-02-16 12:56:33 +01:00
gitea fbfd23d335 [no ci] prepare new Version 2025-02-12 17:07:20 +00:00
gitea 0ccbd9dd0b [no ci] release 1.1.1 2025-02-12 17:07:14 +00:00
arindy b79c2b1e42 adds user to chatoverlay
CI / deploy (push) Successful in 4m50s
2025-02-12 18:02:24 +01:00
gitea f3f788ea51 [no ci] prepare new Version 2025-02-12 15:20:36 +00:00
gitea cd5417d1d1 [no ci] release 1.1.0 2025-02-12 15:20:29 +00:00
arindy f3e8432452 Merge pull request 'creates chatoverlay' (#30) from chatoverlay into main
CI / deploy (push) Successful in 4m48s
Reviewed-on: #30
2025-02-12 16:15:48 +01:00
arindy 1eae3a8bbf creates chatoverlay
CI / deploy (push) Successful in 4m25s
CI / deploy (pull_request) Successful in 4m28s
2025-02-12 16:06:22 +01:00
gitea dd1497c432 [no ci] prepare new Version 2025-02-12 12:28:34 +00:00
gitea 867883b1e7 [no ci] release 1.0.7 2025-02-12 12:28:28 +00:00
arindy af04783685 Merge pull request 'cleans up code' (#29) from clean-up into main
CI / deploy (push) Successful in 4m47s
Reviewed-on: #29
2025-02-12 13:23:47 +01:00
arindy 9a9aa95cd1 cleans up code
CI / deploy (push) Successful in 4m27s
CI / deploy (pull_request) Successful in 4m26s
2025-02-12 13:13:11 +01:00
arindy 9e32ed95d1 [no ci] updates preview 2025-02-12 06:17:07 +01:00
gitea 8089bbeebd [no ci] prepare new Version 2025-02-12 04:55:38 +00:00
gitea 52d16cfee5 [no ci] release 1.0.6 2025-02-12 04:55:31 +00:00
arindy e23592b6a4 moves git tag before snapshot
CI / deploy (push) Successful in 8m36s
2025-02-12 05:46:56 +01:00
arindy a166eecd60 Merge pull request 'removes redundant rejoin button' (#28) from redesign into main
CI / deploy (push) Has been cancelled
Reviewed-on: #28
2025-02-12 05:44:40 +01:00
arindy 4b8b2ce5f3 removes redundant rejoin button
CI / deploy (push) Has been cancelled
CI / deploy (pull_request) Has been cancelled
2025-02-12 05:43:19 +01:00
gitea 022186964d [no ci] prepare new Version 2025-02-12 04:26:25 +00:00
gitea 4604be151b [no ci] release 1.0.5 2025-02-12 04:26:19 +00:00
arindy e906c38630 Merge pull request 'redesigns page to fit inside 800x600' (#27) from redesign into main
CI / deploy (push) Successful in 4m53s
Reviewed-on: #27
2025-02-12 05:21:31 +01:00
arindy 035445ac3f redesigns page to fit inside 800x600
CI / deploy (push) Successful in 4m25s
CI / deploy (pull_request) Successful in 4m32s
2025-02-12 05:12:16 +01:00
17 changed files with 894 additions and 631 deletions
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

+1 -1
View File
@@ -62,9 +62,9 @@ jobs:
git config user.name "gitea"
git add ./pom.xml
git commit -m "[no ci] release ${{ steps.version.outputs.VERSION }}"
git tag ${{ steps.version.outputs.VERSION }} -m "release ${{ steps.version.outputs.VERSION }}"
./mvnw -B --no-transfer-progress clean validate -Pnew-snapshot
git add ./pom.xml
git commit -m "[no ci] prepare new Version"
git tag ${{ steps.version.outputs.VERSION }} -m "release ${{ steps.version.outputs.VERSION }}"
git push origin main
git push origin ${{ steps.version.outputs.VERSION }}
+1 -1
View File
@@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>de.arindy</groupId>
<artifactId>dice-tower</artifactId>
<version>1.0.5-SNAPSHOT</version>
<version>1.1.2-SNAPSHOT</version>
<properties>
<compiler-plugin.version>3.13.0</compiler-plugin.version>
@@ -0,0 +1,40 @@
package de.arindy.dicetower
import io.quarkus.qute.TemplateInstance
import jakarta.ws.rs.GET
import jakarta.ws.rs.Path
import jakarta.ws.rs.PathParam
import jakarta.ws.rs.Produces
import jakarta.ws.rs.QueryParam
import jakarta.ws.rs.core.MediaType
@Path("chatoverlay")
class ChatOverlayResource {
@GET
@Path("/{channel}")
@Produces(MediaType.TEXT_HTML)
fun get(
@PathParam("channel") channel: String,
@QueryParam("scale") scale: Int? = 7,
@QueryParam("maxDice") maxDice: Int? = 20,
@QueryParam("modsAllowed") modsAllowed: Boolean = false,
@QueryParam("vipAllowed") vipAllowed: Boolean = false,
@QueryParam("subsAllowed") subsAllowed: Boolean = false,
@QueryParam("cmd") cmd: String? = "roll",
@QueryParam("theme") theme: String? = "default",
@QueryParam("themeColor") themeColor: String? = "default",
@QueryParam("clearAfter") clearAfter: Long? = -1,
@QueryParam("timeout") timeout: Long? = -1
): TemplateInstance {
return Templates.chatoverlay(channel, scale ?: 7, maxDice ?: 20, modsAllowed, vipAllowed, subsAllowed, cmd ?: "roll", theme ?: "default", themeColor ?: "ff0202", clearAfter ?: 10, timeout ?: 60)
}
@GET
@Produces(MediaType.TEXT_HTML)
fun config(
): TemplateInstance {
return Templates.chatoverlayconfig()
}
}
@@ -1,23 +1,12 @@
package de.arindy.dicetower
import io.quarkus.qute.TemplateInstance
import io.quarkus.runtime.annotations.RegisterForReflection
import jakarta.enterprise.context.ApplicationScoped
import jakarta.ws.rs.Consumes
import jakarta.ws.rs.GET
import jakarta.ws.rs.POST
import jakarta.ws.rs.Path
import jakarta.ws.rs.PathParam
import jakarta.ws.rs.Produces
import jakarta.ws.rs.QueryParam
import jakarta.ws.rs.core.Context
import jakarta.ws.rs.core.MediaType
import jakarta.ws.rs.sse.OutboundSseEvent
import jakarta.ws.rs.sse.Sse
import jakarta.ws.rs.sse.SseBroadcaster
import jakarta.ws.rs.sse.SseEventSink
import org.eclipse.microprofile.config.inject.ConfigProperty
import java.util.UUID
@Path("/")
@ApplicationScoped
@@ -7,8 +7,28 @@ import io.quarkus.qute.TemplateInstance
object Templates {
@JvmStatic
external fun overlay(diceid: String, scale: Int?, clearAfter: Long?): TemplateInstance
@JvmStatic
external fun results(room: String, name: String?, user: String?): TemplateInstance
@JvmStatic
external fun index(version: String): TemplateInstance
@JvmStatic
external fun chatoverlayconfig(): TemplateInstance
@JvmStatic
external fun chatoverlay(
channel: String,
scale: Int?,
maxDice: Int?,
modsAllowed: Boolean,
vipAllowed: Boolean,
subsAllowed: Boolean,
cmd: String?,
theme: String?,
themeColor: String?,
clearAfter: Long?,
timeout: Long?
): TemplateInstance
}
@@ -0,0 +1,199 @@
function addDice() {
let amount = +document.getElementById('dice-amount').innerText
document.getElementById('dice-amount').innerText = `${amount + 1}`
}
function removeDice() {
let amount = +document.getElementById('dice-amount').innerText
if (amount > 1) {
document.getElementById('dice-amount').innerText = `${amount - 1}`
}
}
function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
}
function start(event = undefined) {
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30';
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId');
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
document.getElementById('roll').hidden = false;
document.getElementById('start-container').hidden = true;
document.getElementById('options-container').hidden = false;
document.getElementById('dice-tower').hidden = false;
document.getElementById('name').hidden = true;
document.getElementById('room').hidden = true;
document.getElementById('how-to').hidden = true;
document.getElementById('chatOverlay').hidden = true;
document.getElementById('results').hidden = false;
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
document.getElementById('nameH').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
document.getElementById('save-dice-hint-name').innerHTML = '<strong>' + document.getElementById('name').value + '</strong>';
document.getElementById('roomLabel').hidden = true;
document.getElementById('nameLabel').hidden = true;
document.getElementById('nameH').hidden = false;
document.getElementById('room-hint').innerHTML = '<p>Room: <strong style="font-size:medium;">' + document.getElementById('room').value + '</strong></p>';
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
document.title = document.getElementById('name').value + ' - Dice-Tower';
localStorage.setItem('last-name', document.getElementById('name').value)
localStorage.setItem('last-room', document.getElementById('room').value)
localStorage.setItem('last-gm', document.getElementById('gm').checked)
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
}
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
}
if (!localStorage.getItem(document.getElementById('name').value + '-started')) {
document.getElementById('urls-overlay').showPopover();
}
localStorage.setItem(document.getElementById('name').value + '-started', "true")
let httpRequest = new XMLHttpRequest();
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
httpRequest.setRequestHeader('Content-Type', 'application/json')
httpRequest.send(JSON.stringify({
name: document.getElementById('name').value,
overlay: document.getElementById('overlayId').value,
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
}))
if (document.getElementById('gm').checked) {
document.getElementById('resultSwitch').checked = true;
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users');
evtSource.addEventListener('message', function (event) {
let data = JSON.parse(event.data);
if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) {
let overlays = document.getElementById('overlay-urls');
let newOverlay = document.getElementById(data.id) ?? document.createElement('div');
newOverlay.replaceChildren(...[]);
newOverlay.id = data.id;
newOverlay.style.display = "flex";
newOverlay.style.flexDirection = "row";
newOverlay.style.justifyContent = "space-between";
newOverlay.style.alignItems = "baseline";
let newLabel = document.createElement('label');
newLabel.for = data.id + 'url';
newLabel.innerHTML = "Dice-Overlay for <strong>" + data.name + "</strong>";
let newInput = document.getElementById('overlayId').cloneNode();
newInput.type = "text";
newInput.readOnly = true;
newInput.id = data.id + 'url';
newInput.style.flexGrow = '1';
newInput.value = data.overlay;
newInput.onclick = () => copyToClipboard(newInput.id)
newOverlay.appendChild(newLabel);
newOverlay.appendChild(newInput);
overlays.appendChild(newOverlay);
}
configurePopover();
});
}
}
}
function rollEasy(dice) {
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
roll();
}
function roll(event) {
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
let httpRequest = new XMLHttpRequest();
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
httpRequest.setRequestHeader('Content-Type', 'application/json')
httpRequest.send(JSON.stringify({
name: document.getElementById('name').value,
command: document.getElementById('command').value,
themeColor: document.getElementById('themeColor').value,
theme: document.getElementById('theme').value
}))
}
}
function saveDice() {
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
}
function configurePopover() {
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
popover.forEach((e) => {
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
e.addEventListener("mouseover", () => {
showSnackbar(target.innerHTML);
});
e.addEventListener("mouseout", () => {
hideSnackbar();
});
});
}
function copyToClipboard(id) {
let copyText = document.getElementById(id);
copyText.select();
copyText.setSelectionRange(0, 99999);
navigator.clipboard.writeText(copyText.value).then(() => {
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
})
}
function showSnackbar(message) {
let snackbar = document.getElementById("snackbar");
let snackbarContainer = document.getElementById("snackbar-container");
snackbar.innerHTML = message;
snackbar.className = "show";
snackbarContainer.className = "show";
}
function hideSnackbar() {
let snackbar = document.getElementById("snackbar");
let snackbarContainer = document.getElementById("snackbar-container");
snackbar.className = snackbar.className.replace("show", "");
snackbarContainer.className = snackbarContainer.className.replace("show", "");
}
document.addEventListener("DOMContentLoaded", async () => {
document.querySelector('meta[property="og:url"]').setAttribute("content", url());
document.querySelector('meta[property="twitter:url"]').setAttribute("content", url());
document.querySelector('meta[property="og:image"]').setAttribute("content", url() + '/rich.png');
document.querySelector('meta[name="twitter:image"]').setAttribute("content", url() + '/rich.png');
document.querySelector('meta[property="twitter:domain"]').setAttribute("content", window.location.hostname);
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
document.getElementById('name').value = localStorage.getItem('last-name');
document.getElementById('room').value = localStorage.getItem('last-room');
document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true';
}
document.getElementById('resultSwitch').addEventListener('change', function () {
if (!this.checked) {
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
} else {
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
}
})
document.getElementById('chatOverlayLink').href = url() + '/chatoverlay'
document.addEventListener("DOMContentLoaded", async () => {
if (!localStorage.getItem("userId")) {
localStorage.setItem("userId", self.crypto.randomUUID());
}
})
configurePopover();
})
@@ -0,0 +1,15 @@
import DiceBox from "/vendor/dice-box/dice-box.es.js";
document.addEventListener("DOMContentLoaded", async () => {
document.getElementById('preview').onclick = async () => {
document.getElementById('dice-box').replaceChildren(...[])
const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/assets/",
theme: document.getElementById('theme').value,
themeColor: document.getElementById('themeColor').value,
scale: 14
});
await diceBox.init()
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
}
})
@@ -0,0 +1,36 @@
html,
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
perspective: 1000px;
}
#dice-box {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
background: transparent;
background-size: cover;
}
#dice-box canvas {
width: 100%;
height: 100%;
}
.tooltip {
position: fixed;
border-radius: 0.5rem;
padding: 15px;
max-width: 80%;
font-size: 400%;
color: #fff; !important;
background-color: #333333dd; !important
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

@@ -0,0 +1,209 @@
.w3-theme-l6 {
color: #000 !important;
background-color: #999999 !important;
border-radius: 10px;
}
.w3-theme-l4 {
color: #fff !important;
background-color: #666666 !important;
border-radius: 10px;
}
.w3-theme-l1 {
color: #fff !important;
background-color: #333333 !important;
border-radius: 10px;
}
button {
padding: 10px;
border: #333333 3px solid;
border-radius: 10px;
background: #333333;
color: #fff
}
button:hover {
background: #444444;
}
button:active {
background: #222222;
}
input {
margin: 10px;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 25px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #333333;
}
input:focus + .slider {
box-shadow: 0 0 1px #333333;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.tooltip {
position: fixed;
border-radius: 0.5rem;
padding: 15px;
color: #fff !important;
background-color: #333333dd !important
}
#dice-box {
position: relative;
justify-self: center;
box-sizing: border-box;
width: 100%;
height: 100%;
background: transparent;
background-size: cover;
}
#dice-box canvas {
width: 100%;
height: 100%;
}
.checkbox {
position: relative;
padding-left: 50px;
margin-bottom: 12px;
margin-left: 10px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.checkbox input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkmark {
margin-left: 25px;
position: absolute;
top: 0;
left: 0;
height: 15px;
width: 15px;
background-color: #eee;
}
.checkbox:hover input ~ .checkmark {
background-color: #ccc;
}
/* When the checkbox is checked, add a blue background */
.checkbox input:checked ~ .checkmark {
background-color: #333333;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.checkbox input:checked ~ .checkmark:after {
display: block;
}
/* Style the checkmark/indicator */
.checkbox .checkmark:after {
left: 5px;
top: 2px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
#snackbar-container {
visibility: hidden;
position: fixed;
width: 100%;
z-index: 1;
bottom: 10%;
display: flex;
justify-content: center;
align-items: center;
}
#snackbar {
visibility: hidden;
text-align: center;
border-radius: 0.5rem;
padding: 15px;
color: #fff !important;
border: #fff 5px solid;
background-color: #333333dd !important
}
#snackbar-container.show {
visibility: visible;
}
#snackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dice-Tower - Overlay</title>
<link rel="icon" type="image/png" href="/favicon.png">
<link rel="stylesheet" href="/overlay/style.css">
<script src="/vendor/comfy.js/comfy.min.js"></script>
</head>
<body>
<div id="dice-box"></div>
<div popover id="results" class="tooltip">
</div>
<script type="module">
import DiceBox from "/vendor/dice-box/dice-box.es.js";
const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/assets/",
theme: '{theme}',
themeColor: '{themeColor}',
scale: {scale}
});
diceBox.init()
ComfyJS.Init('{channel}');
//maxDice
ComfyJS.onCommand = async (user, command, message, flags) => {
if ((
flags.broadcaster || ({modsAllowed} && flags.mod) || ({vipAllowed} && flags.vip) || ({subsAllowed} && flags.subscriber
)) && '{cmd}' === command && !shouldWait() && message.match(/^\d+d(4|6|8|10|12|20|100)$/) && (+message.split('d')[0] <= {maxDice})
) {
toggleWait(true);
diceBox.onRollComplete = (rollResult) => {
rollResult.forEach(result => {
let values = []
result.rolls.forEach(roll => {
values.push(roll.value);
})
document.getElementById('results').innerHTML = '<strong>' + user + '</strong> rolls <strong>' + message + '</strong>:<br/> [' + values.map(value => value === 1 ? '<strong style="text-shadow: 2px 2px 10px red">' + value + '</strong>' : value === result.sides ? '<strong style="text-shadow: 2px 2px 10px green">' + value + '</strong>' : value).join(' + ') + (result.modifier > 0 ? ' <a style="text-decoration: underline">+' + result.modifier + '</a>' : result.modifier < 0 ? ' <a style="text-decoration: underline">' + result.modifier + '</a>' : '') + '] = <strong>' + result.value + '</strong> '
})
document.getElementById('results').showPopover()
setTimeout(() => {
diceBox.clear();
document.getElementById('results').hidePopover()
}, {clearAfter} * 1000)
}
diceBox.roll(message);
setTimeout(() => {
toggleWait(false);
}, {clearAfter} * 1000 + {timeout} * 1000)
} else {
console.log('Not a valid command or not ready yet');
}
};
let wait;
function shouldWait() {
return wait;
}
function toggleWait(value) {
wait = value;
}
</script>
</body>
</html>
@@ -0,0 +1,142 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Dice-Tower - Chatoverlay</title>
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
<link rel="stylesheet" href="/style.css">
<link rel="icon" type="image/png" href="/favicon.png">
<script src="/vendor/color-picker.js"></script>
</head>
<body class="w3-theme-l1">
<div class="w3-container w3-content"
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower - Chatoverlay <i
class="fa-solid fa-dice-d20"></i></h1>
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; text-align: center;">
<p>Allows Chat to roll a given amount of one dice</p>
<p>Example: "!roll 5d20"</p>
</div>
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
style="padding: 25px; text-align: center; margin-bottom: auto;">
<label for="theme">Theme </label>
<select name="theme" id="theme" style="margin: 25px">
<option value="default">Default</option>
<option value="blueGreenMetal">Blue-Green Metal</option>
<option value="diceOfRolling">Dice of Rolling</option>
<option value="gemstone">Gemstone</option>
<option value="gemstoneMarble">Marble Gemstone</option>
<option value="rock">Rock</option>
<option value="rust">Rust</option>
<option value="smooth">Smooth</option>
<option value="wooden">Wooden</option>
</select>
<color-picker id="themeColor"></color-picker>
<div>
<label for="channel">Channel </label>
<input type="text" id="channel" style="width: 400px; margin-top: 20px" value="arindy"/>
<label for="cmd">Command !</label>
<input type="text" id="cmd" style="width: 100px; margin-top: 20px; margin-left: 0" value="roll"/>
</div>
<div>
<label class="checkbox" id="modsAllowed-container">Allow mods to roll
<input type="checkbox" id="modsAllowed">
<span class="checkmark"></span>
</label>
<label class="checkbox" id="vipAllowed-container">Allow VIPs to roll
<input type="checkbox" id="vipAllowed">
<span class="checkmark"></span>
</label>
<label class="checkbox" id="subsAllowed-container">Allow subs to roll
<input type="checkbox" id="subsAllowed">
<span class="checkmark"></span>
</label>
</div>
<div>
<label for="scale">Dice-Scale </label>
<input type="number" id="scale" style="width: 50px; margin-top: 20px" value="9"/>
<label for="maxDice">Max number of dice </label>
<input type="number" id="maxDice" style="width: 50px; margin-top: 20px" value="20"/>
</div>
<div>
<label for="clearAfter">Clear dice after (in seconds)</label>
<input type="number" id="clearAfter" style="width: 50px; margin-top: 20px" value="10"/>
<label for="timeout">Command-timeout (in seconds)</label>
<input type="number" id="timeout" style="width: 50px; margin-top: 20px" value="60"/>
</div>
<div id="dice-box" style="height: 400px"></div>
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
<button style="margin: 10px" id="generate">Generate overlay-link <i class="fa-solid fa-link"></i></button>
<div>
<input type="text" readonly id="link" style="width: 80%; margin-top: 20px"
onclick="copyToClipboard(this.id)"/>
</div>
</div>
</div>
<div id="snackbar-container">
<div id="snackbar">.. they see them rolling</div>
</div>
<script>
function copyToClipboard(id) {
let copyText = document.getElementById(id)
if (copyText.value.length > 0) {
copyText.select();
copyText.setSelectionRange(0, 99999);
navigator.clipboard.writeText(copyText.value).then(() => {
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
})
}
}
function showSnackbar(message) {
let snackbar = document.getElementById("snackbar");
let snackbarContainer = document.getElementById("snackbar-container");
snackbar.innerHTML = message;
snackbar.className = "show";
snackbarContainer.className = "show";
setTimeout(() => {
snackbar.className = "";
snackbarContainer.className = "";
}, 3000)
}
</script>
<script type="module">
import DiceBox from "/vendor/dice-box/dice-box.es.js";
function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
}
document.addEventListener("DOMContentLoaded", async () => {
document.getElementById('preview').onclick = async () => {
document.getElementById('dice-box').replaceChildren(...[])
const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/assets/",
theme: document.getElementById('theme').value,
themeColor: document.getElementById('themeColor').value,
scale: +document.getElementById('scale').value
});
await diceBox.init()
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
}
document.getElementById('generate').onclick = async () => {
document.getElementById('link').value = url() +
"/chatoverlay/" + document.getElementById('channel').value +
"?cmd=" + document.getElementById('cmd').value +
"&theme=" + document.getElementById('theme').value +
"&themeColor=" + encodeURIComponent(document.getElementById('themeColor').value) +
"&scale=" + document.getElementById('scale').value +
"&maxDice=" + document.getElementById('maxDice').value +
"&clearAfter=" + document.getElementById('clearAfter').value +
"&timeout=" + document.getElementById('timeout').value +
"&modsAllowed=" + document.getElementById('modsAllowed').checked +
"&vipAllowed=" + document.getElementById('vipAllowed').checked +
"&subsAllowed=" + document.getElementById('subsAllowed').checked
}
})
</script>
</body>
</html>
+115 -554
View File
@@ -3,312 +3,125 @@
<head>
<meta charset="UTF-8">
<title>Dice-Tower</title>
<meta property="og:title" content="Dice-Tower">
<meta name="twitter:title" content="Dice-Tower">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<meta property="og:url" content="https://dice-tower.com">
<meta property="twitter:url" content="https://dice-tower.com">
<meta property="twitter:domain" content="dice-tower.com">
<meta property="og:image" content="https://dice-tower.com/rich.png">
<meta name="twitter:image" content="https://dice-tower.com/rich.png">
<meta name="description" content="
Easy to use online dice rolling with customizable overlays.
">
<link rel="stylesheet" href="/vendor/w3css/4/w3.css">
<link rel="stylesheet" href="/vendor/font-awesome/css/fontawesome.css">
<link rel="stylesheet" href="/vendor/font-awesome/css/all.css">
<link rel="stylesheet" href="/style.css">
<link rel="icon" type="image/png" href="/favicon.png">
<script src="/vendor/color-picker.js"></script>
<style>
.w3-theme-l6 {
color: #000 !important;
background-color: #999999 !important;
border-radius: 10px;
}
.w3-theme-l4 {
color: #fff !important;
background-color: #666666 !important;
border-radius: 10px;
}
.w3-theme-l1 {
color: #fff !important;
background-color: #333333 !important;
border-radius: 10px;
}
.collapsible {
background-color: #333333;
color: black;
margin-top: 10px;
padding: 18px;
width: 100%;
border: none;
text-align: left;
outline: none;
font-size: 15px;
}
.content {
padding: 0 18px;
display: none;
overflow: hidden;
background-color: #999999;
color: #000;
border-radius: 10px;
}
button {
padding: 10px;
border: #333333 3px solid;
border-radius: 10px;
background: #333333;
color: #fff
}
button:hover {
background: #444444;
}
button:active {
background: #222222;
}
input {
margin: 10px;
}
/* The switch - the box around the slider */
.switch {
position: relative;
display: inline-block;
width: 60px;
height: 25px;
}
/* Hide default HTML checkbox */
.switch input {
opacity: 0;
width: 0;
height: 0;
}
/* The slider */
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
-webkit-transition: .4s;
transition: .4s;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
-webkit-transition: .4s;
transition: .4s;
}
input:checked + .slider {
background-color: #333333;
}
input:focus + .slider {
box-shadow: 0 0 1px #333333;
}
input:checked + .slider:before {
-webkit-transform: translateX(26px);
-ms-transform: translateX(26px);
transform: translateX(26px);
}
.overlayButton {
background: transparent;
border: none;
width: 5px;
height: 5px;
font-size: large;
color: #000
}
.overlayButton:hover {
background: transparent;
}
.tooltip {
position: fixed;
border-radius: 0.5rem;
padding: 15px;
color: #fff !important;
background-color: #333333dd !important
}
#dice-box {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
background: transparent;
background-size: cover;
}
#dice-box canvas {
width: 100%;
height: 100%;
margin: 20px
}
.checkbox {
position: relative;
padding-left: 50px;
margin-bottom: 12px;
margin-left: 10px;
cursor: pointer;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
.checkbox input {
position: absolute;
opacity: 0;
cursor: pointer;
height: 0;
width: 0;
}
.checkmark {
margin-left: 25px;
position: absolute;
top: 0;
left: 0;
height: 15px;
width: 15px;
background-color: #eee;
}
.checkbox:hover input ~ .checkmark {
background-color: #ccc;
}
/* When the checkbox is checked, add a blue background */
.checkbox input:checked ~ .checkmark {
background-color: #333333;
}
/* Create the checkmark/indicator (hidden when not checked) */
.checkmark:after {
content: "";
position: absolute;
display: none;
}
/* Show the checkmark when checked */
.checkbox input:checked ~ .checkmark:after {
display: block;
}
/* Style the checkmark/indicator */
.checkbox .checkmark:after {
left: 5px;
top: 2px;
width: 5px;
height: 10px;
border: solid white;
border-width: 0 3px 3px 0;
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
}
#snackbar-container {
visibility: hidden;
position: fixed;
width: 100%;
z-index: 1;
bottom: 200px;
display: flex;
justify-content: center;
align-items: center;
}
#snackbar {
visibility: hidden;
text-align: center;
border-radius: 0.5rem;
padding: 15px;
color: #fff !important;
border: #fff 5px solid;
background-color: #333333dd !important
}
#snackbar-container.show {
visibility: visible;
}
#snackbar.show {
visibility: visible;
-webkit-animation: fadein 0.5s, fadeout 0.5s 2.5s;
animation: fadein 0.5s, fadeout 0.5s 2.5s;
}
</style>
<script type="module" src="/dice-preview.js"></script>
<script type="text/javascript" src="/app.js"></script>
</head>
<body class="w3-theme-l1">
<div class="w3-container w3-content"
style="height: 95vh; display: flex; flex-direction: column; justify-content: space-between; padding: 25px">
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i></h1>
<h1 style="text-align: center"><i class="fa-solid fa-dice-d20"></i> Dice-Tower <i class="fa-solid fa-dice-d20"></i>
</h1>
<div class="w3-panel w3-theme-l4 w3-card w3-display-container"
style="padding: 25px; text-align: center; margin-bottom: auto;">
<label for="name" id="nameLabel">Name </label><input type="text" id="name" style="width: 50%; margin-top: 20px"
required onkeyup="start(event)"/><br/>
<label for="room" id="roomLabel">Room </label><input type="text" id="room" style="width: 50%" required
onkeyup="start(event)"/><br/>
<div id="start-container" >
<h2 id="nameH" popovertarget="room-hint" data-trigger="hover" style="margin: 0" hidden>Name</h2>
<div>
<label for="name" id="nameLabel">Name </label>
<input type="text" id="name" style="width: 50%; margin-top: 20px" required onkeyup="start(event)"/>
</div>
<div>
<label for="room" id="roomLabel">Room </label>
<input type="text" id="room" style="width: 50%" required onkeyup="start(event)"/>
</div>
<div id="start-container">
<div>
<button id="start" onclick="start()" style="align-self: center; margin-top: 20px">Join <i
class="fa-solid fa-right-to-bracket"></i></button>
<label class="checkbox" id="gm-container">Join as GM
<input type="checkbox" id="gm" >
<input type="checkbox" id="gm">
<span class="checkmark"></span>
</label>
<br/>
</div>
</div>
<div id="options-container" hidden style="position: absolute; right: 25px; top: 5px">
<button id="customize" popovertarget="customize-overlay" style="align-self: center; margin-top: 20px"><i class="fa-solid fa-palette"></i>
</button>
<button id="urls" popovertarget="urls-overlay" style="align-self: center; margin-top: 20px"><i class="fa-solid fa-link"></i></button>
</div>
</div>
<div id="dice-tower" hidden class="w3-panel w3-theme-l4 w3-card w3-display-container"
style="padding: 25px; margin-bottom: auto">
<button type="button" class="collapsible" style="color: white; font-weight: bold">Overlay URLs <a><i
class="fa-solid fa-chevron-down"></i></a></button>
<div class="content">
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin: 10px; padding-right: 25px; padding-left: 25px">
<label style="font-size: large; font-weight: bold; margin: 10px">Roll </label>
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
onclick="removeDice()">-
</button>
<label style="font-size: large; font-weight: bold" id="dice-amount">1</label>
<button style="border: transparent; border-radius: 100%; font-size: large; font-weight: bold; height: 50px; width: 50px"
onclick="addDice()">+
</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d10')">D10</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d12')">D12</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d20')">D20</button>
<button style="font-size: large; font-weight: bold;" onclick="rollEasy('d100')">D100</button>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline; margin: 20px 25px 0;">
<label for="command">Command </label>
<input popovertarget="command-hint" data-trigger="hover" type="text" id="command" style="flex-grow: 1" onkeyup="roll(event)"/>
<button hidden id="roll" onclick="roll()">Roll <i class="fa-solid fa-dice"></i></button>
<div style="margin: 20px 25px" id="all-results">
<label for="resultSwitch">Show all results </label>
<label class="switch">
<input type="checkbox" id="resultSwitch">
<span class="slider"></span>
</label>
</div>
</div>
</div>
<div id="results" hidden class="w3-panel w3-theme-l6 w3-card w3-display-container"
style="padding: 25px; flex-grow: 1; margin-bottom: auto">
<iframe id="resultFrame" title="results" style="width: 100%; height: 100%; overflow: hidden; border: 0"
onload="this.height=this.contentWindow.document.body.scrollHeight;"></iframe>
</div>
<div popover id="urls-overlay" class="tooltip" style="width: 600px">
<div id="overlay-urls">
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
<label for="overlayId" id="overlayLabel">Dice-Overlay </label>
<input type="text" readonly id="overlayId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
<button id="overlay-hint-button" popovertarget="overlay-hint" data-trigger="hover"
class="overlayButton"><i class="fa-solid fa-circle-info"></i>
</button>
<input popovertarget="overlay-hint" data-trigger="hover" type="text" readonly id="overlayId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
</div>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;"
hidden id="all-results-urls">
<label for="resultsId">All-Results-Overlay </label>
<input type="text" readonly id="resultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
<button popovertarget="all-results-hint" data-trigger="hover" class="overlayButton"><i
class="fa-solid fa-circle-info"></i></button>
<input popovertarget="all-results-hint" data-trigger="hover" type="text" readonly id="resultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline;">
<label for="myResultsId">My-Results-Overlay </label>
<input type="text" readonly id="myResultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
<button popovertarget="my-results-hint" data-trigger="hover" class="overlayButton"><i
class="fa-solid fa-circle-info"></i></button>
<input popovertarget="my-results-hint" data-trigger="hover" type="text" readonly id="myResultsId" style="flex-grow: 1" onclick="copyToClipboard(this.id)"/>
</div>
</div>
<button type="button" class="collapsible" style="color: white; font-weight: bold">Customize Dice <a><i
class="fa-solid fa-chevron-down"></i></a>
</button>
<div class="content">
<div popover id="customize-overlay" class="tooltip" style="width: 680px;">
<label for="theme">Theme </label>
<select name="theme" id="theme" style="margin: 25px">
<option value="default">Default</option>
@@ -325,50 +138,10 @@
<div id="dice-box"></div>
<button style="margin: 10px" id="preview">Preview <i class="fa-solid fa-magnifying-glass"></i></button>
<button style="margin: 10px" onclick="saveDice()">Save <i class="fa-solid fa-floppy-disk"></i></button>
<button popovertarget="save-dice-hint" data-trigger="hover" class="overlayButton"><i
class="fa-solid fa-circle-info"></i></button>
<button popovertarget="save-dice-hint" data-trigger="hover" style="margin: 10px" onclick="saveDice()">Save <i class="fa-solid fa-floppy-disk"></i></button>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: center; margin: 20px 25px;">
<label style="font-size: x-large; font-weight: bold;">Roll </label>
<button style="border: transparent; border-radius: 100%; font-size: x-large; font-weight: bold; height: 50px; width: 50px"
onclick="removeDice()">-
</button>
<label style="font-size: x-large; font-weight: bold;" id="dice-amount">1</label>
<button style="border: transparent; border-radius: 100%; font-size: x-large; font-weight: bold; height: 50px; width: 50px"
onclick="addDice()">+
</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d4')">D4</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d6')">D6</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d8')">D8</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d10')">D10</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d12')">D12</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d20')">D20</button>
<button style="font-size: x-large; font-weight: bold;" onclick="rollEasy('d100')">D100</button>
</div>
<div style="display: flex; flex-direction: row; justify-content: space-between; align-items: baseline; margin: 20px 25px;">
<label for="command">Command </label>
<input type="text" id="command" style="flex-grow: 1" onkeyup="roll(event)"/>
<button hidden id="roll" onclick="roll()">Roll <i class="fa-solid fa-dice"></i></button>
<button popovertarget="command-hint" data-trigger="hover" class="overlayButton"><i
class="fa-solid fa-circle-info"></i></button>
</div>
<div style="margin: 20px 25px" id="all-results">
<label for="resultSwitch">Show all results </label>
<label class="switch">
<input type="checkbox" id="resultSwitch">
<span class="slider"></span>
</label>
</div>
</div>
<div id="results" hidden class="w3-panel w3-theme-l6 w3-card w3-display-container"
style="padding: 25px; flex-grow: 1; margin-bottom: auto">
<iframe id="resultFrame" title="results" style="width: 100%; height: 85%; overflow: hidden; border: 0"
onload="this.height=this.contentWindow.document.body.scrollHeight;"></iframe>
</div>
<div popover id="overlay-hint" class="tooltip">
<div hidden id="overlay-hint">
<div style="text-align: left">
<p style="color: red; font-weight: bold">Only the last loaded instance of that overlay rolls the dice!</p>
<p>Query Params you can Change:</p>
<ul>
@@ -378,21 +151,26 @@
</li>
</ul>
</div>
<div popover id="all-results-hint" class="tooltip">
<p>Shows all Results in this room</p>
</div>
<div popover id="my-results-hint" class="tooltip">
<p>Shows only my Results in this room</p>
</div>
<div popover id="save-dice-hint" class="tooltip">
This saves your current theme and theme color for current Name
</div>
<div popover id="command-hint" class="tooltip">
Example Commands: "1d6", "2d8 1d100", "1d4 and 1d6", "2d20 & 1d2, "5d6+10"
</div>
<div style="margin-top: 20px; flex-grow: 1" id="how-to">
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
<div hidden id="all-results-hint">
<p>Shows all Results in this room</p>
</div>
<div hidden id="my-results-hint">
<p>Shows only my Results in this room</p>
</div>
<div hidden id="save-dice-hint">
<p>This saves your current theme and theme color for <a id="save-dice-hint-name"></a></p>
</div>
<div hidden id="command-hint">
<p>Example Commands: "1d6", "2d8 1d100", "1d4 and 1d6", "2d20 & 1d2, "5d6+10"</p>
</div>
<div hidden id="room-hint">
<p>How is your character called?</p>
</div>
<div style="margin-top: 20px" id="how-to">
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px;">
<h2 style="text-align: center">How-To</h2>
<ul>
<li>
@@ -406,8 +184,7 @@
<li style="color: red; font-weight: bold">Only the last loaded instance of that overlay rolls the
dice!
</li>
<li>You can configure your Overlay with query parameters (more information at the info element next
to the link)
<li>You can configure your Overlay with query parameters (for more information hover over the link)
</li>
</ul>
<li>Configure your dice</li>
@@ -416,234 +193,18 @@
</ul>
</div>
</div>
<div style="margin-top: 20px; flex-grow: 1" id="chatOverlay">
<div class="w3-panel w3-theme-l4 w3-card w3-display-container" style="padding: 25px; margin-bottom: auto">
If you are looking for a way to let your twitch chat roll click <a href target="_blank" id="chatOverlayLink">here</a>
</div>
</div>
</div>
<div id="snackbar-container">
<div id="snackbar">.. they see them rolling</div>
</div>
<script>
function addDice() {
let amount = +document.getElementById('dice-amount').innerText
document.getElementById('dice-amount').innerText = amount + 1
}
function removeDice() {
let amount = +document.getElementById('dice-amount').innerText
if (amount > 1) {
document.getElementById('dice-amount').innerText = amount - 1
}
}
function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
}
function start(event) {
if ((!event || event.keyCode === 13) && document.getElementById('name').value.length > 0 && document.getElementById('room').value.length > 0) {
document.getElementById('overlayId').value = url() + '/overlay/' + document.getElementById('room').value + ':' + localStorage.getItem('userId') + '?scale=7&clearAfter=30';
document.getElementById('resultsId').value = url() + '/overlay/' + document.getElementById('room').value + '/results';
document.getElementById('myResultsId').value = document.getElementById('resultsId').value + '?name=' + encodeURIComponent(document.getElementById('name').value) + '&user=' + localStorage.getItem('userId');
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
document.getElementById('roll').hidden = false;
if (document.getElementById('rejoin')) {
document.getElementById('rejoin').hidden = true;
}
document.getElementById('start').hidden = true;
document.getElementById('dice-tower').hidden = false;
document.getElementById('name').hidden = true;
document.getElementById('room').hidden = true;
document.getElementById('gm-container').hidden = true;
document.getElementById('how-to').hidden = true;
document.getElementById('results').hidden = false;
document.getElementById('all-results').hidden = !document.getElementById('gm').checked;
document.getElementById('all-results-urls').style.display = document.getElementById('gm').checked ? 'fles' : 'none';
document.getElementById('nameLabel').innerHTML = '<strong style="font-size:x-large;">' + document.getElementById('name').value + '</strong>';
document.getElementById('roomLabel').innerHTML = '<strong style="font-size:medium;">' + document.getElementById('room').value + '</strong>';
document.getElementById('overlayLabel').innerHTML = 'Dice-Overlay for <strong>' + document.getElementById('name').value + '</strong>';
document.title = document.getElementById('name').value + ' - Dice-Tower';
localStorage.setItem('last-name', document.getElementById('name').value)
localStorage.setItem('last-room', document.getElementById('room').value)
localStorage.setItem('last-gm', document.getElementById('gm').checked)
if (localStorage.getItem(document.getElementById('name').value + "-theme")) {
document.getElementById('theme').value = localStorage.getItem(document.getElementById('name').value + "-theme")
}
if (localStorage.getItem(document.getElementById('name').value + "-themeColor")) {
document.getElementById('themeColor').setColor(localStorage.getItem(document.getElementById('name').value + "-themeColor"));
}
let httpRequest = new XMLHttpRequest();
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + '/register')
httpRequest.setRequestHeader('Content-Type', 'application/json')
httpRequest.send(JSON.stringify({
name: document.getElementById('name').value,
overlay: document.getElementById('overlayId').value,
id: document.getElementById('room').value + ':' + localStorage.getItem('userId')
}))
if (document.getElementById('gm').checked) {
document.getElementById('resultSwitch').checked = true;
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
const evtSource = new EventSource(url() + '/dice/' + document.getElementById('room').value + '/users');
evtSource.addEventListener('message', function (event) {
let data = JSON.parse(event.data);
if (data.id !== document.getElementById('room').value + ':' + localStorage.getItem('userId')) {
let overlays = document.getElementById('overlay-urls');
let newOverlay = document.getElementById(data.id) ?? document.createElement('div');
newOverlay.replaceChildren(...[]);
newOverlay.id = data.id;
newOverlay.style.display = "flex";
newOverlay.style.flexDirection = "row";
newOverlay.style.justifyContent = "space-between";
newOverlay.style.alignItems = "baseline";
let newLabel = document.createElement('label');
newLabel.for = data.id + 'url';
newLabel.innerHTML = "Dice-Overlay for <strong>" + data.name + "</strong>";
let newInput = document.createElement('input');
newInput.type = "text";
newInput.readOnly = true;
newInput.id = data.id + 'url';
newInput.style.flexGrow = '1';
newInput.value = data.overlay;
newInput.onclick = () => copyToClipboard(newInput.id)
let hint = document.getElementById('overlay-hint-button').cloneNode(true);
newOverlay.appendChild(newLabel);
newOverlay.appendChild(newInput);
newOverlay.appendChild(hint);
overlays.appendChild(newOverlay);
}
showPopover();
});
}
}
}
function rollEasy(dice) {
document.getElementById('command').value = document.getElementById('dice-amount').innerText + dice;
roll();
}
function roll(event) {
if ((!event || event.keyCode === 13) && document.getElementById('command').value?.length > 0) {
let httpRequest = new XMLHttpRequest();
httpRequest.open('POST', url() + '/dice/' + document.getElementById('room').value + ':' + localStorage.getItem(`userId`))
httpRequest.setRequestHeader('Content-Type', 'application/json')
httpRequest.send(JSON.stringify({
name: document.getElementById('name').value,
command: document.getElementById('command').value,
themeColor: document.getElementById('themeColor').value,
theme: document.getElementById('theme').value
}))
}
}
function saveDice() {
localStorage.setItem(document.getElementById('name').value + "-theme", document.getElementById('theme').value)
localStorage.setItem(document.getElementById('name').value + "-themeColor", document.getElementById('themeColor').value)
}
let coll = document.getElementsByClassName("collapsible");
for (let i = 0; i < coll.length; i++) {
coll[i].addEventListener("click", function () {
const content = this.nextElementSibling;
if (content.style.display === "block") {
content.style.display = "none";
this.children[0].innerHTML = "<i class='fa-solid fa-chevron-down'></i>"
} else {
content.style.display = "block";
this.children[0].innerHTML = "<i class='fa-solid fa-chevron-up'></i>"
}
});
}
if (localStorage.getItem('last-name') && localStorage.getItem('last-room')) {
let rejoin = document.createElement('button');
rejoin.id = 'rejoin';
rejoin.style.alignSelf = 'center';
rejoin.style.marginTop = '20px';
rejoin.style.marginRight = '20px';
rejoin.innerHTML = 'Rejoin as <strong>' + (localStorage.getItem('last-gm') === 'true' ? 'GM ' : '') + localStorage.getItem('last-name') + '</strong> in <strong>' + localStorage.getItem('last-room') + '</strong> <i class="fa-solid fa-right-to-bracket"></i>'
rejoin.onclick = () => {
document.getElementById('name').value = localStorage.getItem('last-name');
document.getElementById('room').value = localStorage.getItem('last-room');
document.getElementById('gm').checked = localStorage.getItem('last-gm') === 'true';
start();
}
document.getElementById('start-container').appendChild(rejoin)
}
function showPopover() {
const popover = document.querySelectorAll("[popovertarget][data-trigger='hover']");
popover.forEach((e) => {
const target = document.querySelector("#" + e.getAttribute("popovertarget"));
e.addEventListener("mouseover", () => {
target.showPopover();
});
e.addEventListener("mouseout", () => {
target.hidePopover();
});
});
}
showPopover();
document.getElementById('resultSwitch').addEventListener('change', function () {
if (!this.checked) {
document.getElementById('resultFrame').src = document.getElementById('myResultsId').value;
} else {
document.getElementById('resultFrame').src = document.getElementById('resultsId').value;
}
})
document.addEventListener("DOMContentLoaded", async () => {
if (!localStorage.getItem("userId")) {
localStorage.setItem("userId", self.crypto.randomUUID());
}
})
function copyToClipboard(id) {
let copyText = document.getElementById(id);
copyText.select();
copyText.setSelectionRange(0, 99999);
navigator.clipboard.writeText(copyText.value);
showSnackbar("<i class='fa-regular fa-copy'></i> Link copied to clipboard: <br/>" + copyText.value);
}
function showSnackbar(message) {
let snackbar = document.getElementById("snackbar");
let snackbarContainer = document.getElementById("snackbar-container");
snackbar.innerHTML = message;
snackbar.className = "show";
snackbarContainer.className = "show";
setTimeout(function () {
snackbar.className = snackbar.className.replace("show", "");
snackbarContainer.className = snackbarContainer.className.replace("show", "");
}, 5000);
}
</script>
<script type="module">
import DiceBox from "/vendor/dice-box/dice-box.es.js";
document.addEventListener("DOMContentLoaded", async () => {
document.getElementById('preview').onclick = async () => {
document.getElementById('dice-box').replaceChildren(...[])
const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/assets/",
theme: document.getElementById('theme').value,
themeColor: document.getElementById('themeColor').value,
scale: 14
});
await diceBox.init()
diceBox.roll(['1d2', '1d4', '1d6', '1d8', '1d10', '1d12', '1d20', '1d100']);
}
})
</script>
</body>
<footer class="w3-theme-l1 w3-center w3-padding-16">
Version {version} ::
<a href="https://git.arindy.de/arindy/dice-tower" target="_blank" class="w3-hover-text-black">Dice-Tower on my
GitTea</a>
<footer class="w3-theme-l1 w3-center">
Version {version}
<a href="https://git.arindy.de/arindy/dice-tower" target="_blank" class="w3-hover-text-black"><i class="fa-solid fa-code"></i> Source on GitTea</a>
</footer>
</html>
+16 -35
View File
@@ -4,35 +4,7 @@
<meta charset="UTF-8">
<title>Dice-Tower - Overlay</title>
<link rel="icon" type="image/png" href="/favicon.png">
<style>
html,
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
overflow: hidden;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
perspective: 1000px;
}
#dice-box {
position: relative;
box-sizing: border-box;
width: 100%;
height: 100%;
background: transparent;
background-size: cover;
}
#dice-box canvas {
width: 100%;
height: 100%;
}
</style>
<link rel="stylesheet" href="/overlay/style.css">
</head>
<body>
<div id="dice-box"></div>
@@ -40,7 +12,9 @@
function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
}
import DiceBox from "/vendor/dice-box/dice-box.es.js";
const evtSource = new EventSource(url() + "/dice/{diceid??}/stream");
const diceBox = new DiceBox("#dice-box", {
assetPath: "/vendor/assets/",
@@ -59,7 +33,7 @@
scale: {scale}
});
document.addEventListener("DOMContentLoaded", async() => {
document.addEventListener("DOMContentLoaded", async () => {
await diceBox.init()
let timeout = 0;
@@ -68,20 +42,27 @@
let data = JSON.parse(event.data);
diceBox.onRollComplete = (rollResult) => {
let httpRequest = new XMLHttpRequest();
httpRequest.open('POST',url() + '/dice/' + data.room + '/results')
httpRequest.open('POST', url() + '/dice/' + data.room + '/results')
httpRequest.setRequestHeader('Content-Type', 'application/json')
httpRequest.send(JSON.stringify({
name: data.name,
user: data.user,
themeColor: data.themeColor,
results: rollResult,
} ))
if ({clearAfter} > 0) {
timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000)
}))
if ({clearAfter} >
0
)
{
timeout = setTimeout(() => diceBox.clear(), {clearAfter} * 1000
)
}
}
diceBox.roll(data.roll, { theme: data.theme?.length > 0 ? data.theme : 'default', themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF' });
diceBox.roll(data.roll, {
theme: data.theme?.length > 0 ? data.theme : 'default',
themeColor: data.themeColor.length > 0 ? data.themeColor : '#4545FF'
});
})
})
</script>
+2 -2
View File
@@ -8,12 +8,12 @@
<link rel="icon" type="image/png" href="/favicon.png">
</head>
<body>
<div id="results" style="font-size: x-large">
</div>
<div id="results" style="font-size: x-large"></div>
<script type="module">
function url() {
return window.location.protocol + '//' + window.location.hostname + (window.location.port?.length > 0 ? ':' + window.location.port : '');
}
const evtSource = new EventSource(url() + '/dice/{room}/results');
evtSource.addEventListener('message', function (event) {
let data = JSON.parse(event.data);