more messageboxes on caught exceptions, update readme, bump to 0.2.2

This commit is contained in:
Iris Lightshard 2023-07-21 23:49:14 -06:00
parent 7a294373c5
commit 9cf0de40cb
Signed by: nilix
GPG key ID: 3B7FBC22144E6398
9 changed files with 57 additions and 49 deletions

View file

@ -2,9 +2,11 @@
-- virtual tabletop for distributed cooperative storytelling -- -- virtual tabletop for distributed cooperative storytelling --
![Screenshot of Felt showing a hexgrid map on which a party of 4 humans and an alien dog thing facing off against two giant centipedes. Various UI windows are overlayed, some of them collapsed, showing the dice roller with dice log, and admin windows showing map and token management interfaces.](./screenshot.jpg)
## about ## about
Felt is a lightweight webapp written in Go and vanilla Javascript which provides an agnostic virtual tabletop for battle maps, visual puzzles, or any other situation you may need a shared map in a tabletop RPG over voice/video chat. Felt is a lightweight (~210KB frontend!) webapp written in Go and vanilla Javascript which provides an agnostic virtual tabletop for battle maps, visual puzzles, or any other situation you may need a shared map in a tabletop RPG over voice/video chat.
## usage ## usage
@ -20,7 +22,7 @@ The `status` panel is updated when the admin changes the table's status, and can
The `token select` panel provides a list of every existing token at the table. Clicking the name of the token shows a to-scale preview at the top of the list (with a button to dismiss it), which is resized according to the zoom level of the map. The button to the right of each token's name can place the token on or remove it from the map. The `token select` panel provides a list of every existing token at the table. Clicking the name of the token shows a to-scale preview at the top of the list (with a button to dismiss it), which is resized according to the zoom level of the map. The button to the right of each token's name can place the token on or remove it from the map.
Any user can move any token on the map by dragging it around. The map is pannable and zoomable as well. Any user can move any token on the map by dragging it around. The map is pannable and zoomable as well. The UI theme is changeable from the drawer at bottom-left of the application, and it persists to `localStorage`.
### admin ### admin
@ -48,8 +50,8 @@ Creating or destroying tokens as well as changing the map causes updates to be s
Requirements: Requirements:
- go - go 1.19
- docker - docker (for the containerized database)
1. Clone this repository and `cd` into it. 1. Clone this repository and `cd` into it.
2. `go mod tidy` 2. `go mod tidy`

BIN
screenshot.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

View file

@ -65,7 +65,6 @@ async function loadAdmin(name, pass) {
console.log(res.status); console.log(res.status);
} }
} catch (err) { } catch (err) {
console.dir(err)
setErr(`${err.name}: ${err.message}`); setErr(`${err.name}: ${err.message}`);
} }
} }
@ -162,26 +161,26 @@ function drawTokenOrigin() {
if (dropdown && tokenWidth && tokenHeight && tokenCX && tokenCY && preview if (dropdown && tokenWidth && tokenHeight && tokenCX && tokenCY && preview
&& dropdown.selectedIndex >= 0) { && dropdown.selectedIndex >= 0) {
tokenCX.value = Math.floor(Number(tokenCX.value)) tokenCX.value = Math.floor(Number(tokenCX.value))
tokenCY.value = Math.floor(Number(tokenCY.value)) tokenCY.value = Math.floor(Number(tokenCY.value))
const img = preview.children[0]; const img = preview.children[0];
const x = Number(tokenWidth.value) / Number(tokenCX.value); const x = Number(tokenWidth.value) / Number(tokenCX.value);
const y = Number(tokenHeight.value) / Number(tokenCY.value); const y = Number(tokenHeight.value) / Number(tokenCY.value);
const origin = {x: img.width/x, y: img.height/y}; const origin = {x: img.width/x, y: img.height/y};
const originImg = document.createElement("img"); const originImg = document.createElement("img");
originImg.src="/table/origin.png"; originImg.src="/table/origin.png";
originImg.style.position = "absolute"; originImg.style.position = "absolute";
originImg.style.left = (origin.x - 2) + "px"; originImg.style.left = (origin.x - 2) + "px";
originImg.style.top = (origin.y - 2) + "px"; originImg.style.top = (origin.y - 2) + "px";
if (preview.children.length > 1) { if (preview.children.length > 1) {
preview.replaceChild(originImg, preview.children[1]); preview.replaceChild(originImg, preview.children[1]);
} else { } else {
preview.appendChild(originImg); preview.appendChild(originImg);
} }
} }
} }
@ -252,7 +251,9 @@ function createToken() {
return; return;
} }
setErr("All token fields are required"); setErr("All token fields are required");
} catch {} } catch (err) {
setErr(`${err.name}: ${err.message}`);
}
} }
function destroyToken(id) { function destroyToken(id) {
@ -270,7 +271,7 @@ function previewExistingToken(id) {
try { try {
const existing = tokens.find(t=>t.t.id == id); const existing = tokens.find(t=>t.t.id == id);
const dropdown = $("sprite_dropdown"); const dropdown = $("sprite_dropdown");
if (existing) { if (existing && dropdown) {
$("token_width").value = existing.t.w; $("token_width").value = existing.t.w;
$("token_height").value = existing.t.h; $("token_height").value = existing.t.h;
$("token_cx").value = existing.t.oX; $("token_cx").value = existing.t.oX;
@ -285,7 +286,7 @@ function previewExistingToken(id) {
reinitializeSpritePreview(true); reinitializeSpritePreview(true);
} }
} catch (err) { } catch (err) {
console.log(err); setErr(`${err.name}: ${err.message}`);
} }
} }
@ -486,7 +487,9 @@ function setTableCreateFormVisible(v) {
$("new_table_name").value = ""; $("new_table_name").value = "";
$("new_table_pass").value = ""; $("new_table_pass").value = "";
} }
} catch {} } catch (err) {
setErr(`${err.name}: ${err.message}`);
}
} }
function setTokenCreateFormVisible(v) { function setTokenCreateFormVisible(v) {
@ -502,7 +505,9 @@ function setTokenCreateFormVisible(v) {
$("token_creation_form").style.display = v ? "block" : "none"; $("token_creation_form").style.display = v ? "block" : "none";
$("token_list").style.display = v ? "none" : "block"; $("token_list").style.display = v ? "none" : "block";
$("token_admin_preview").innerHTML = ""; $("token_admin_preview").innerHTML = "";
} catch {} } catch (err) {
setErr(`${err.name}: ${err.message}`);
}
} }
async function createTable() { async function createTable() {
@ -538,8 +543,8 @@ async function createTable() {
} else { } else {
setErr("Error creating table"); setErr("Error creating table");
} }
} catch { } catch (err) {
setErr("Error creating table"); setErr(`${err.name}: ${err.message}`);
} }
} }
} }

View file

@ -6,7 +6,7 @@
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="shortcut icon" href="./favicon.png"/> <link rel="shortcut icon" href="./favicon.png"/>
<link href="./leaflet.css?v=1.9.4" rel="stylesheet" /> <link href="./leaflet.css?v=1.9.4" rel="stylesheet" />
<link href="./style.css?v=0.2.0" rel="stylesheet" /> <link href="./style.css?v=0.2.2" rel="stylesheet" />
</head> </head>
<body> <body>
<noscript><div id="noscript_container"> <noscript><div id="noscript_container">
@ -146,13 +146,13 @@
</form> </form>
</details> </details>
<div id="lag" style="display:none;">lag...</div> <div id="lag" style="display:none;">lag...</div>
<div class="ui_win" id="felt_info"><a href="https://hacklab.nilfm.cc/felt">felt v0.2.1</a> (<a href="https://hacklab.nilfm.cc/felt/raw/main/LICENSE">license</a>) | built with <a href="https://leafletjs.com">leaflet</a> (<a href="https://hacklab.nilfm.cc/felt/raw/main/LEAFLET_LICENSE">license</a>) </div> <div class="ui_win" id="felt_info"><a href="https://hacklab.nilfm.cc/felt">felt v0.2.2</a> (<a href="https://hacklab.nilfm.cc/felt/raw/main/LICENSE">license</a>) | built with <a href="https://leafletjs.com">leaflet</a> (<a href="https://hacklab.nilfm.cc/felt/raw/main/LEAFLET_LICENSE">license</a>) </div>
</nav> </nav>
</body> </body>
<script src="./leaflet.js?v=1.9.4" type="text/javascript"></script> <script src="./leaflet.js?v=1.9.4" type="text/javascript"></script>
<script src="./util.js?v=0.2.1" type="text/javascript"></script> <script src="./util.js?v=0.2.2" type="text/javascript"></script>
<script src="./map.js?v=0.2.1" type="text/javascript"></script> <script src="./map.js?v=0.2.2" type="text/javascript"></script>
<script src="./socket.js?v=0.2.1" type="text/javascript"></script> <script src="./socket.js?v=0.2.2" type="text/javascript"></script>
<script src="./dice.js?v=0.2.1" type="text/javascript"></script> <script src="./dice.js?v=0.2.2" type="text/javascript"></script>
<script src="./admin.js?v=0.2.1" type="text/javascript"></script> <script src="./admin.js?v=0.2.2" type="text/javascript"></script>
</html> </html>

View file

@ -57,6 +57,14 @@ function resizeMarkers() {
} }
function sortByTokenName(a, b) {
return (a.t.name < b.t.name)
? -1
: ((a.t.name > b.t.name)
? 1
: 0)
}
function processTokens(tokenChanges) { function processTokens(tokenChanges) {
for (const t of tokenChanges) { for (const t of tokenChanges) {
const i = tokens.findIndex(tk=>tk.t.id == t.id); const i = tokens.findIndex(tk=>tk.t.id == t.id);
@ -84,14 +92,7 @@ function processTokens(tokenChanges) {
if (t.x != null && t.y != null) { if (t.x != null && t.y != null) {
const self = NewToken(t); const self = NewToken(t);
tokens.push(self); tokens.push(self);
tokens.sort((a,b)=>{ tokens.sort(sortByTokenName);
if (a.t.name < b.t.name) {
return -1;
} else if (a.t.name > b.t.name) {
return 1;
}
return 0;
});
if (t.active) { if (t.active) {
self.m.addTo(map); self.m.addTo(map);
} }

View file

@ -171,7 +171,7 @@ a:hover, a:active {
} }
.ui_win ul { .ui_win ul {
max-height: 10em; max-height: 16em;
overflow: auto; overflow: auto;
} }
@ -212,7 +212,7 @@ nav {
.single_btn_list li { .single_btn_list li {
display: grid; display: grid;
grid-template-columns: 1fr auto; grid-template-columns: 1fr auto;
} }
.two_btn_list li { .two_btn_list li {

View file

@ -6,7 +6,7 @@
<title>Felt &mdash; Error</title> <title>Felt &mdash; Error</title>
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="shortcut icon" href="/table/favicon.png"/> <link rel="shortcut icon" href="/table/favicon.png"/>
<link href="/table/style.css?v=0.2.0" rel="stylesheet" /> <link href="/table/style.css?v=0.2.2" rel="stylesheet" />
</head> </head>
<body> <body>
<main id="registration"> <main id="registration">

View file

@ -7,7 +7,7 @@
<title>Felt &mdash; Admin Registration</title> <title>Felt &mdash; Admin Registration</title>
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="shortcut icon" href="/table/favicon.png"/> <link rel="shortcut icon" href="/table/favicon.png"/>
<link href="/table/style.css?v=0.2.0" rel="stylesheet" /> <link href="/table/style.css?v=0.2.2" rel="stylesheet" />
</head> </head>
<body> <body>
<main id="registration"> <main id="registration">
@ -23,5 +23,5 @@
{{end}} {{end}}
</main> </main>
</body> </body>
<script src="/table/util.js?v=0.2.1" type="text/javascript"></script> <script src="/table/util.js?v=0.2.2" type="text/javascript"></script>
</html> </html>

View file

@ -6,7 +6,7 @@
<title>Felt &mdash; Registration Complete</title> <title>Felt &mdash; Registration Complete</title>
<meta name="viewport" content="width=device-width" /> <meta name="viewport" content="width=device-width" />
<link rel="shortcut icon" href="/table/favicon.png"/> <link rel="shortcut icon" href="/table/favicon.png"/>
<link href="/table/style.css?v=0.2.0" rel="stylesheet" /> <link href="/table/style.css?v=0.2.2" rel="stylesheet" />
</head> </head>
<body> <body>
<main id="registration"> <main id="registration">