openvibers/openvibe.codes Commits
Codes: a task's repository heading breaks only after a slash; a stream that cannot open reloads every 15 s (#64)
Changes
@@@ -31,3 +31,5 @@.run-patch .add { color: var(--success, #22c55e); }.run-patch .del { color: var(--danger, #e5484d); }.run-patch .hunk { color: var(--accent-light, #60a5fa); }++.run-repo { overflow-wrap: break-word; }++.run-host { display: block; font-size: 0.55em; font-weight: 500; color: var(--text-muted, #8b9bb4); }@@@ -60,8 +60,9 @@setTimeout(function () { window.location.reload(); }, 800);});source.onerror = function () {-- // The stream ended or the link dropped: the page itself shows where the task is.-- if (source.readyState === 2) setTimeout(function () { window.location.reload(); }, 3000);++ // The stream ended or could not open (Run busy or down): the page itself shows where the task is, so it is++ // reloaded, not too often while the stream stays away.++ if (source.readyState === 2) setTimeout(function () { window.location.reload(); }, 15000);};}@@@ -16,6 +16,13 @@ const express = require('express');const { asyncRouter } = require('./router');const { send } = require('../render/layout');const { html, raw, esc } = require('../render/html');++++/** A repository URL as a heading: the host small, then owner/name, which may break after a slash but never mid-word. */++function repoName(url) {++ const [host, ...path] = String(url).replace(/^https:\/\//, '').split('/');++ if (!path.length) return host;++ return html`<small class="run-host">${host}/</small>${path.map((part, i) => html`${i ? raw('/<wbr>') : ''}${part}`)}`;++}const { parseArgs } = require('../../harness/job');const { RunError } = require('../domain/run-client');@@@ -90,7 +97,7 @@ ${error ? html`<p class="run-notice run-notice-error" role="alert">${error}</p>`function tasksHtml(rows) {if (!rows.length) return html`<p class="muted">No tasks yet.</p>`;-- return html`<ul class="run-tasks">${rows.map((t) => html`<li><a href="/run/tasks/${t.id}"><span class="run-pill run-pill-${t.state}">${STATE_LABEL[t.state] || t.state}</span> <b>${t.repo.replace(/^https:\/\//, '')}</b> <span class="muted">${t.prompt.split('\n')[0].slice(0, 90)}</span></a></li>`)}</ul>`;++ return html`<ul class="run-tasks">${rows.map((t) => html`<li><a href="/run/tasks/${t.id}"><span class="run-pill run-pill-${t.state}">${STATE_LABEL[t.state] || t.state}</span> <b>${repoName(t.repo)}</b> <span class="muted">${t.prompt.split('\n')[0].slice(0, 90)}</span></a></li>`)}</ul>`;}async function runPage(req, res, { values, error, status = 200 } = {}) {@@@ -200,7 +207,7 @@ ${typeof d.duration_ms === 'number' ? html`<p class="muted">${Math.round(d.duratrow = await refresh(row);const live = !TERMINAL.has(row.state);const body = html`<p class="crumbs"><a href="/run">Run on your machine</a></p>--<h1>${row.repo.replace(/^https:\/\//, '')}${row.ref ? html` <small>${row.ref}</small>` : ''}</h1>++<h1 class="run-repo">${repoName(row.repo)}${row.ref ? html` <small>${row.ref}</small>` : ''}</h1><p><span class="run-pill run-pill-${row.state}" data-state>${STATE_LABEL[row.state] || row.state}</span>${row.agent ? html` <span class="run-pill">${row.agent}</span>` : ''}</p><pre class="run-prompt">${row.prompt}</pre>${live ? html`<section class="run-live" data-events="/run/tasks/${row.id}/events" aria-live="polite"><h2>Live</h2><ol class="run-log" data-log></ol></section>
@@ -31,3 +31,5 @@
@@ -31,3 +31,5 @@
.run-patch .add { color: var(--success, #22c55e); }
.run-patch .add { color: var(--success, #22c55e); }
.run-patch .del { color: var(--danger, #e5484d); }
.run-patch .del { color: var(--danger, #e5484d); }
.run-patch .hunk { color: var(--accent-light, #60a5fa); }
.run-patch .hunk { color: var(--accent-light, #60a5fa); }
+.run-repo { overflow-wrap: break-word; }
+.run-host { display: block; font-size: 0.55em; font-weight: 500; color: var(--text-muted, #8b9bb4); }
@@ -60,8 +60,9 @@
@@ -60,8 +60,9 @@
setTimeout(function () { window.location.reload(); }, 800);
setTimeout(function () { window.location.reload(); }, 800);
});
});
source.onerror = function () {
source.onerror = function () {
- // The stream ended or the link dropped: the page itself shows where the task is.
- if (source.readyState === 2) setTimeout(function () { window.location.reload(); }, 3000);
+ // The stream ended or could not open (Run busy or down): the page itself shows where the task is, so it is
+ // reloaded, not too often while the stream stays away.
+ if (source.readyState === 2) setTimeout(function () { window.location.reload(); }, 15000);
};
};
}
}
@@ -16,6 +16,13 @@ const express = require('express');
@@ -16,6 +16,13 @@ const express = require('express');
const { asyncRouter } = require('./router');
const { asyncRouter } = require('./router');
const { send } = require('../render/layout');
const { send } = require('../render/layout');
const { html, raw, esc } = require('../render/html');
const { html, raw, esc } = require('../render/html');
+
+/** A repository URL as a heading: the host small, then owner/name, which may break after a slash but never mid-word. */
+function repoName(url) {
+ const [host, ...path] = String(url).replace(/^https:\/\//, '').split('/');
+ if (!path.length) return host;
+ return html`<small class="run-host">${host}/</small>${path.map((part, i) => html`${i ? raw('/<wbr>') : ''}${part}`)}`;
+}
const { parseArgs } = require('../../harness/job');
const { parseArgs } = require('../../harness/job');
const { RunError } = require('../domain/run-client');
const { RunError } = require('../domain/run-client');
@@ -90,7 +97,7 @@ ${error ? html`<p class="run-notice run-notice-error" role="alert">${error}</p>`
@@ -90,7 +97,7 @@ ${error ? html`<p class="run-notice run-notice-error" role="alert">${error}</p>`
function tasksHtml(rows) {
function tasksHtml(rows) {
if (!rows.length) return html`<p class="muted">No tasks yet.</p>`;
if (!rows.length) return html`<p class="muted">No tasks yet.</p>`;
- return html`<ul class="run-tasks">${rows.map((t) => html`<li><a href="/run/tasks/${t.id}"><span class="run-pill run-pill-${t.state}">${STATE_LABEL[t.state] || t.state}</span> <b>${t.repo.replace(/^https:\/\//, '')}</b> <span class="muted">${t.prompt.split('\n')[0].slice(0, 90)}</span></a></li>`)}</ul>`;
+ return html`<ul class="run-tasks">${rows.map((t) => html`<li><a href="/run/tasks/${t.id}"><span class="run-pill run-pill-${t.state}">${STATE_LABEL[t.state] || t.state}</span> <b>${repoName(t.repo)}</b> <span class="muted">${t.prompt.split('\n')[0].slice(0, 90)}</span></a></li>`)}</ul>`;
}
}
async function runPage(req, res, { values, error, status = 200 } = {}) {
async function runPage(req, res, { values, error, status = 200 } = {}) {
@@ -200,7 +207,7 @@ ${typeof d.duration_ms === 'number' ? html`<p class="muted">${Math.round(d.durat
@@ -200,7 +207,7 @@ ${typeof d.duration_ms === 'number' ? html`<p class="muted">${Math.round(d.durat
row = await refresh(row);
row = await refresh(row);
const live = !TERMINAL.has(row.state);
const live = !TERMINAL.has(row.state);
const body = html`<p class="crumbs"><a href="/run">Run on your machine</a></p>
const body = html`<p class="crumbs"><a href="/run">Run on your machine</a></p>
-<h1>${row.repo.replace(/^https:\/\//, '')}${row.ref ? html` <small>${row.ref}</small>` : ''}</h1>
+<h1 class="run-repo">${repoName(row.repo)}${row.ref ? html` <small>${row.ref}</small>` : ''}</h1>
<p><span class="run-pill run-pill-${row.state}" data-state>${STATE_LABEL[row.state] || row.state}</span>${row.agent ? html` <span class="run-pill">${row.agent}</span>` : ''}</p>
<p><span class="run-pill run-pill-${row.state}" data-state>${STATE_LABEL[row.state] || row.state}</span>${row.agent ? html` <span class="run-pill">${row.agent}</span>` : ''}</p>
<pre class="run-prompt">${row.prompt}</pre>
<pre class="run-prompt">${row.prompt}</pre>
${live ? html`<section class="run-live" data-events="/run/tasks/${row.id}/events" aria-live="polite"><h2>Live</h2><ol class="run-log" data-log></ol></section>
${live ? html`<section class="run-live" data-events="/run/tasks/${row.id}/events" aria-live="polite"><h2>Live</h2><ol class="run-log" data-log></ol></section>