mirror of
https://github.com/arabine/open-story-teller.git
synced 2025-12-08 09:43:32 +01:00
dedicated directory for story web version
This commit is contained in:
parent
432d72c80c
commit
0ad614699c
15 changed files with 204 additions and 137 deletions
|
|
@ -1,39 +0,0 @@
|
||||||
import apiClient from './classes/api-client.js'
|
|
||||||
import eventBus from './classes/event-bus.js';
|
|
||||||
import storage from './classes/storage.js';
|
|
||||||
|
|
||||||
|
|
||||||
import { render } from 'preact';
|
|
||||||
import { html } from 'htm/preact';
|
|
||||||
import TopMenu from './components/TopMenu.js'
|
|
||||||
import ParametersDialog from './components/ParametersDialog.js';
|
|
||||||
|
|
||||||
export function App() {
|
|
||||||
|
|
||||||
this.params = storage.getItem('server') || {
|
|
||||||
serverUrl: '127.0.0.1',
|
|
||||||
serverPort: 8081,
|
|
||||||
};
|
|
||||||
|
|
||||||
storage.setItem('server', this.params);
|
|
||||||
|
|
||||||
// try to connect to the server
|
|
||||||
apiClient.setBaseUrl(`http://${this.params.serverUrl}:${this.params.serverPort}/api/v1`);
|
|
||||||
apiClient.get('/library/list')
|
|
||||||
.then(data => {
|
|
||||||
console.log('Server is up and running', data);
|
|
||||||
eventBus.publish('server-state-changed', {connected: true});
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
console.error('Server is down', error);
|
|
||||||
eventBus.publish('server-state-changed', {connected: false});
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return html`
|
|
||||||
<${TopMenu} />
|
|
||||||
<${ParametersDialog} />
|
|
||||||
`;
|
|
||||||
}
|
|
||||||
|
|
||||||
render(html`<${App} />`, document.getElementById('app'));
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
||||||
<!doctype html>
|
|
||||||
<html lang="en-us">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
|
||||||
<meta name="title" content="OpenStoryTeller Web Player">
|
|
||||||
<meta name="description" content="OpenStoryTeller Web Player">
|
|
||||||
<meta name="keywords" content="story, player, teller">
|
|
||||||
|
|
||||||
<title>OpenStoryTeller Web Player</title>
|
|
||||||
|
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="style.css">
|
|
||||||
<link rel="stylesheet" href="main.css">
|
|
||||||
|
|
||||||
<script type="importmap">
|
|
||||||
{
|
|
||||||
"imports": {
|
|
||||||
"preact": "https://esm.sh/preact@10.23.1",
|
|
||||||
"preact/hooks": "https://esm.sh/preact@10.23.1/hooks",
|
|
||||||
"htm/preact": "https://esm.sh/htm@3.1.1/preact?external=preact"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<!-- Favicon -->
|
|
||||||
<link rel="shortcut icon" href="https://www.raylib.com/favicon.ico">
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
font-family: 'Inter', sans-serif;
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
line-height: 1.6;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas.emscripten { border: 0px none; background-color: black; display: inline; }
|
|
||||||
#canvas-container {
|
|
||||||
width: 100%;
|
|
||||||
text-align:center;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body style="min-width: min-content;">
|
|
||||||
<main>
|
|
||||||
<div id="app"></div>
|
|
||||||
|
|
||||||
<div id="canvas-container" class="block fixed">
|
|
||||||
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
|
|
||||||
</div>
|
|
||||||
<p id="output" />
|
|
||||||
|
|
||||||
</main>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
var Module = {
|
|
||||||
print: (function() {
|
|
||||||
var element = document.getElementById('output');
|
|
||||||
if (element) element.value = ''; // clear browser cache
|
|
||||||
return function(text) {
|
|
||||||
if (arguments.length > 1) text = Array.prototype.slice.call(arguments).join(' ');
|
|
||||||
console.log(text);
|
|
||||||
if (element) {
|
|
||||||
element.value += text + "\n";
|
|
||||||
element.scrollTop = element.scrollHeight; // focus on bottom
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})(),
|
|
||||||
canvas: (function() {
|
|
||||||
var canvas = document.getElementById('canvas');
|
|
||||||
return canvas;
|
|
||||||
})(),
|
|
||||||
locateFile: function(s) {
|
|
||||||
return 'bin/' + s;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
<script async type="text/javascript" src="bin/story-player.js"></script>
|
|
||||||
<script src="app.js" type="module"></script>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
55
story-player-web/app.js
Normal file
55
story-player-web/app.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import apiClient from './classes/api-client.js'
|
||||||
|
import eventBus from './classes/event-bus.js';
|
||||||
|
import storage from './classes/storage.js';
|
||||||
|
|
||||||
|
|
||||||
|
import { render } from 'preact';
|
||||||
|
import { createContext } from "preact";
|
||||||
|
import { html } from 'htm/preact';
|
||||||
|
import TopMenu from './components/TopMenu.js'
|
||||||
|
import ParametersDialog from './components/ParametersDialog.js';
|
||||||
|
import StoryPlayer from './components/StoryPlayer.js';
|
||||||
|
import StoriesList from './components/StoriesList.js';
|
||||||
|
|
||||||
|
export const AppContext = createContext("appContext");
|
||||||
|
|
||||||
|
export function App() {
|
||||||
|
|
||||||
|
const DefaultContext = {
|
||||||
|
stories: []
|
||||||
|
};
|
||||||
|
|
||||||
|
this.params = storage.getItem('server') || {
|
||||||
|
serverUrl: '127.0.0.1',
|
||||||
|
serverPort: 8081,
|
||||||
|
};
|
||||||
|
|
||||||
|
storage.setItem('server', this.params);
|
||||||
|
|
||||||
|
// try to connect to the server
|
||||||
|
apiClient.setBaseUrl(`http://${this.params.serverUrl}:${this.params.serverPort}/api/v1`);
|
||||||
|
apiClient.get('/library/list')
|
||||||
|
.then(data => {
|
||||||
|
console.log('Server is up and running', data);
|
||||||
|
eventBus.publish('server-state-changed', {connected: true});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log('Server is down');
|
||||||
|
eventBus.publish('server-state-changed', {connected: false});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<AppContext.Provider value="${DefaultContext}">
|
||||||
|
<${TopMenu} />
|
||||||
|
<${ParametersDialog} />
|
||||||
|
|
||||||
|
<div style="display: flex; flex-direction:row;">
|
||||||
|
<${StoryPlayer} />
|
||||||
|
<${StoriesList} />
|
||||||
|
</div>
|
||||||
|
</AppContext.Provider>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
render(html`<${App} />`, document.getElementById('app'));
|
||||||
BIN
story-player-web/assets/Inter-Regular.ttf
Normal file
BIN
story-player-web/assets/Inter-Regular.ttf
Normal file
Binary file not shown.
BIN
story-player-web/assets/logo-color.png
Normal file
BIN
story-player-web/assets/logo-color.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 116 KiB |
|
|
@ -8,6 +8,9 @@ class ApiClient {
|
||||||
}
|
}
|
||||||
|
|
||||||
async request(endpoint, method = 'GET', data = null, headers = {}) {
|
async request(endpoint, method = 'GET', data = null, headers = {}) {
|
||||||
|
const controller = new AbortController();
|
||||||
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
method,
|
method,
|
||||||
headers: {
|
headers: {
|
||||||
|
|
@ -20,17 +23,18 @@ class ApiClient {
|
||||||
config.body = JSON.stringify(data);
|
config.body = JSON.stringify(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(`${this.baseURL}${endpoint}`, config);
|
const promise = fetch(`${this.baseURL}${endpoint}`, config).then(async (response) => {
|
||||||
if (!response.ok) {
|
|
||||||
const errorData = await response.json();
|
const data = await response.json();
|
||||||
throw new Error(errorData.message || 'Something went wrong');
|
if (response.ok) {
|
||||||
|
return data;
|
||||||
|
} else {
|
||||||
|
return Promise.reject(data);
|
||||||
}
|
}
|
||||||
return await response.json();
|
});
|
||||||
} catch (error) {
|
return promise.finally(() => clearTimeout(timeout));
|
||||||
console.error('API request error:', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get(endpoint, headers = {}) {
|
get(endpoint, headers = {}) {
|
||||||
59
story-player-web/components/StoriesList.js
Normal file
59
story-player-web/components/StoriesList.js
Normal file
|
|
@ -0,0 +1,59 @@
|
||||||
|
import { html } from 'htm/preact';
|
||||||
|
import eventBus from '../classes/event-bus.js';
|
||||||
|
import { useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
|
||||||
|
function StoriesList() {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
.card-container {
|
||||||
|
max-height: 80vh;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-title {
|
||||||
|
font-size: 1.2em;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-description {
|
||||||
|
font-size: 0.9em;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-image {
|
||||||
|
width: 120px;
|
||||||
|
height: 90px;
|
||||||
|
object-fit: cover;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div>
|
||||||
|
<div class="card-container">
|
||||||
|
<div class="block card">
|
||||||
|
<div class="card-content">
|
||||||
|
<h2 class="card-title">Titre de la Carte 1</h2>
|
||||||
|
<p class="card-description">Description de la carte 1. Voici une brève description pour cette carte.</p>
|
||||||
|
</div>
|
||||||
|
<img src="https://placehold.co/100x75" alt="Image de la carte 1" class="card-image" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
export default StoriesList;
|
||||||
26
story-player-web/components/StoryPlayer.js
Normal file
26
story-player-web/components/StoryPlayer.js
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import { html } from 'htm/preact';
|
||||||
|
import eventBus from '../classes/event-bus.js';
|
||||||
|
import { useState } from 'preact/hooks';
|
||||||
|
|
||||||
|
|
||||||
|
function StoryPlayer() {
|
||||||
|
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
|
||||||
|
</style>
|
||||||
|
<div style="width: 400px; height: 500px;">
|
||||||
|
<div class="block fixed" style="width: 320px; height: 240px;">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div style="display: flex; ">
|
||||||
|
<div class="block accent btn">⇦</div>
|
||||||
|
<div class="block accent btn">✓</div>
|
||||||
|
<div class="block accent btn">⇨</div>
|
||||||
|
<div class="block accent btn">⏎</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
export default StoryPlayer;
|
||||||
|
|
@ -56,9 +56,7 @@ function TopMenu() {
|
||||||
</style>
|
</style>
|
||||||
<div class="flexRow">
|
<div class="flexRow">
|
||||||
<${MessageComponent} show=${error} message=${message} />
|
<${MessageComponent} show=${error} message=${message} />
|
||||||
<div class="block accent" onClick="${handleClick}">
|
<div class="block accent btn" onClick="${handleClick}">Paramètres</div>
|
||||||
Paramètres
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
45
story-player-web/index.html
Normal file
45
story-player-web/index.html
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<!doctype html>
|
||||||
|
<html lang="en-us">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||||
|
<meta name="title" content="OpenStoryTeller Web Player">
|
||||||
|
<meta name="description" content="OpenStoryTeller Web Player">
|
||||||
|
<meta name="keywords" content="story, player, teller">
|
||||||
|
|
||||||
|
<title>OpenStoryTeller Web Player</title>
|
||||||
|
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<link rel="stylesheet" href="main.css">
|
||||||
|
|
||||||
|
<script type="importmap">
|
||||||
|
{
|
||||||
|
"imports": {
|
||||||
|
"preact": "https://esm.sh/preact@10.23.1",
|
||||||
|
"preact/hooks": "https://esm.sh/preact@10.23.1/hooks",
|
||||||
|
"htm/preact": "https://esm.sh/htm@3.1.1/preact?external=preact"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<link rel="shortcut icon" href="https://www.raylib.com/favicon.ico">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body style="min-width: min-content;">
|
||||||
|
<div id="app"></div>
|
||||||
|
|
||||||
|
<script src="storyvm.js"></script>
|
||||||
|
<script src="app.js" type="module"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -199,7 +199,6 @@ body {
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: var(--block-background-color);
|
background: var(--block-background-color);
|
||||||
font-weight: bold;
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
|
@ -283,6 +282,10 @@ body {
|
||||||
cursor: initial !important;
|
cursor: initial !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.block.btn {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
/* FIXED STYLES */
|
/* FIXED STYLES */
|
||||||
.block.fixed:hover,
|
.block.fixed:hover,
|
||||||
.block.fixed:hover::before,
|
.block.fixed:hover::before,
|
||||||
Loading…
Reference in a new issue