feat: add PWA support and refine creativity parameter handling
- Register service worker (`sw.js`) with cache-first static assets and network-first HTML pages - Update `site.webmanifest` with app name, colors, icons at `/static/`, and standalone display mode - Add `<meta name="theme-color">` in layout for browser UI theming - Simplify creativity slider: change range from 0–10 to 3.0–7.0 (step 0.5), aligning frontend and backend logic - Adjust `generate.js` route and `generation.js` service to accept direct temperature values (3.0–7.0) instead of scaled inputs
This commit is contained in:
parent
4602087400
commit
428cfb1008
|
|
@ -1 +1,13 @@
|
|||
{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}
|
||||
{
|
||||
"name": "Bri-Tunes",
|
||||
"short_name": "Bri-Tunes",
|
||||
"description": "Retrofuture music player",
|
||||
"start_url": "/",
|
||||
"display": "standalone",
|
||||
"background_color": "#05060d",
|
||||
"theme_color": "#ff2bd6",
|
||||
"icons": [
|
||||
{ "src": "/static/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" },
|
||||
{ "src": "/static/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" }
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
const CACHE = 'bri-tunes-v1';
|
||||
const STATIC_PRECACHE = [
|
||||
'/',
|
||||
'/static/css/app.css',
|
||||
'/static/js/player.js',
|
||||
'/static/js/navigate.js',
|
||||
'/static/js/bg-grid.js',
|
||||
'/static/js/player-viz.js',
|
||||
'/static/js/boot-sequence.js',
|
||||
'/static/vendor/fonts/Orbitron.woff2',
|
||||
'/static/vendor/fonts/Inter.woff2',
|
||||
'/static/vendor/three/three.module.min.js',
|
||||
];
|
||||
|
||||
self.addEventListener('install', (e) => {
|
||||
e.waitUntil(caches.open(CACHE).then((c) => c.addAll(STATIC_PRECACHE)));
|
||||
self.skipWaiting();
|
||||
});
|
||||
|
||||
self.addEventListener('activate', (e) => {
|
||||
e.waitUntil(
|
||||
caches.keys().then((keys) =>
|
||||
Promise.all(keys.filter((k) => k !== CACHE).map((k) => caches.delete(k)))
|
||||
)
|
||||
);
|
||||
self.clients.claim();
|
||||
});
|
||||
|
||||
self.addEventListener('fetch', (e) => {
|
||||
const { request } = e;
|
||||
const url = new URL(request.url);
|
||||
|
||||
// Never intercept audio streams or cross-origin requests.
|
||||
if (url.pathname.startsWith('/stream/') || url.origin !== self.location.origin) return;
|
||||
|
||||
// Static assets: cache-first.
|
||||
if (url.pathname.startsWith('/static/')) {
|
||||
e.respondWith(
|
||||
caches.match(request).then((cached) => cached || fetch(request))
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// HTML pages: network-first, fall back to cache.
|
||||
e.respondWith(
|
||||
fetch(request)
|
||||
.then((res) => {
|
||||
const clone = res.clone();
|
||||
caches.open(CACHE).then((c) => c.put(request, clone));
|
||||
return res;
|
||||
})
|
||||
.catch(() => caches.match(request))
|
||||
);
|
||||
});
|
||||
|
|
@ -24,6 +24,12 @@ app.use(pinoHttp());
|
|||
app.use(express.urlencoded({ extended: false }));
|
||||
app.use(express.json());
|
||||
|
||||
// Service worker — must be at root scope to intercept all page requests.
|
||||
app.get('/sw.js', (req, res) => {
|
||||
res.set('Service-Worker-Allowed', '/');
|
||||
res.sendFile(path.join(__dirname, '..', 'public', 'sw.js'));
|
||||
});
|
||||
|
||||
// Static assets (CSS, JS, vendor).
|
||||
app.use('/static', express.static(path.join(__dirname, '..', 'public'), { maxAge: '1h' }));
|
||||
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ module.exports = function generateRoutes(csrfProtection) {
|
|||
bpm: parseInt(req.body.bpm, 10) || null,
|
||||
keyscale: req.body.keyscale || null,
|
||||
duration_seconds: parseInt(req.body.duration, 10) || null,
|
||||
creativity: parseInt(req.body.creativity, 10) ?? 50,
|
||||
creativity: parseFloat(req.body.creativity) || 3.5,
|
||||
seed: parseInt(req.body.seed, 10) === -1 ? -1 : (parseInt(req.body.seed, 10) || -1),
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -112,8 +112,8 @@ async function submitToComfyUI(baseUrl, workflowJson, params, nodeIds) {
|
|||
? Math.floor(Math.random() * 2 ** 32)
|
||||
: params.seed;
|
||||
|
||||
// Scale creativity 0–10 (slider) → temperature 3.0–7.0
|
||||
const temperature = 3.0 + ((params.creativity ?? 5) / 10) * 4.0;
|
||||
// Creativity slider sends value directly in 3.0–7.0 range (step 0.5)
|
||||
const temperature = params.creativity != null ? parseFloat(params.creativity) : 3.5;
|
||||
|
||||
const paramValues = {
|
||||
style_prompt: params.style_prompt || '',
|
||||
|
|
|
|||
|
|
@ -50,9 +50,9 @@
|
|||
<label>
|
||||
Creativity
|
||||
<div class="gen-slider-wrap">
|
||||
<input type="range" id="gen-creativity" min="0" max="10" step="1" value="5"
|
||||
oninput="document.getElementById('gen-creativity-val').textContent = (3 + this.value * 0.4).toFixed(1)">
|
||||
<span id="gen-creativity-val" class="gen-slider-val">5.0</span>
|
||||
<input type="range" id="gen-creativity" min="3" max="7" step="0.5" value="3.5"
|
||||
oninput="document.getElementById('gen-creativity-val').textContent = parseFloat(this.value).toFixed(1)">
|
||||
<span id="gen-creativity-val" class="gen-slider-val">3.5</span>
|
||||
</div>
|
||||
</label>
|
||||
|
||||
|
|
@ -137,7 +137,7 @@
|
|||
data-bpm="<%= g.bpm || '' %>"
|
||||
data-keyscale="<%- encodeURIComponent(g.keyscale || '') %>"
|
||||
data-duration="<%= g.duration_seconds || '' %>"
|
||||
data-creativity="<%= g.creativity != null ? g.creativity : 5 %>"
|
||||
data-creativity="<%= g.creativity != null ? g.creativity : 3.5 %>"
|
||||
data-seed="<%= g.seed != null ? g.seed : -1 %>">
|
||||
<td class="muted small"><%= new Date(g.created_at).toLocaleDateString() %></td>
|
||||
<td class="gen-prompt-cell"><%= (g.style_prompt || '—').slice(0, 60) %><% if ((g.style_prompt || '').length > 60) { %>…<% } %></td>
|
||||
|
|
@ -441,9 +441,9 @@
|
|||
durationEl.value = row.dataset.duration || '';
|
||||
seedEl.value = row.dataset.seed || '-1';
|
||||
|
||||
var creativity = row.dataset.creativity != null ? row.dataset.creativity : '5';
|
||||
var creativity = row.dataset.creativity != null ? row.dataset.creativity : '3.5';
|
||||
creativityEl.value = creativity;
|
||||
if (creativityValEl) creativityValEl.textContent = (3 + parseFloat(creativity) * 0.4).toFixed(1);
|
||||
if (creativityValEl) creativityValEl.textContent = parseFloat(creativity).toFixed(1);
|
||||
|
||||
// Set keyscale select
|
||||
var keyscale = decodeURIComponent(row.dataset.keyscale || '');
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta name="theme-color" content="#ff2bd6">
|
||||
<meta name="csrf-token" content="<%= csrfToken %>">
|
||||
<title><%= typeof title !== 'undefined' ? title : siteName %></title>
|
||||
<% if (typeof og !== 'undefined' && og) { %>
|
||||
|
|
@ -54,5 +55,6 @@
|
|||
<script type="module" src="/static/js/overlay-objects.js"></script>
|
||||
<script type="module" src="/static/js/player-viz.js"></script>
|
||||
<script type="module" src="/static/js/boot-sequence.js"></script>
|
||||
<script>if ('serviceWorker' in navigator) navigator.serviceWorker.register('/sw.js');</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue