@quolu/lattice 0.50.0 → 0.50.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/todo-gantt-live.mjs +12 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quolu/lattice",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.1",
|
|
4
4
|
"description": "Schedulability compiler for multi-agent development: observe real code boundaries, refactor the conflicting seam, recompile the plan for parallel execution",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Quo / クオ at kitepon.dev",
|
package/src/todo-gantt-live.mjs
CHANGED
|
@@ -4,6 +4,9 @@ import { TODO_DASHBOARD_CODE_VERSION } from './todo-dashboard-registry.mjs';
|
|
|
4
4
|
|
|
5
5
|
const LOOPBACK = '127.0.0.1';
|
|
6
6
|
const POLL_MS = 500;
|
|
7
|
+
const SSE_HEARTBEAT_MS = 25_000;
|
|
8
|
+
const SSE_STALE_MS = 62_500;
|
|
9
|
+
const SSE_WATCHDOG_MS = 12_500;
|
|
7
10
|
const HTTP_ERROR_SCHEMA = 'lattice.todo_gantt_http_error.v1';
|
|
8
11
|
const PROJECT_ID = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/u;
|
|
9
12
|
|
|
@@ -58,7 +61,7 @@ function withExternalPane(html, pane) {
|
|
|
58
61
|
}
|
|
59
62
|
|
|
60
63
|
function liveHtml(html, headDigest, eventsPath, externalPane = null) {
|
|
61
|
-
const controller = `<script>(()=>{const badge=document.createElement('div');badge.setAttribute('role','status');badge.style.cssText='position:fixed;right:12px;bottom:12px;z-index:99;padding:6px 10px;border:1px solid #d9d8d4;border-radius:4px;background:#fcfcfb;font:600 12px system-ui';badge.textContent='進捗: 接続中';document.body.append(badge);
|
|
64
|
+
const controller = `<script>(()=>{const badge=document.createElement('div');badge.setAttribute('role','status');badge.style.cssText='position:fixed;right:12px;bottom:12px;z-index:99;padding:6px 10px;border:1px solid #d9d8d4;border-radius:4px;background:#fcfcfb;font:600 12px system-ui';badge.textContent='進捗: 接続中';document.body.append(badge);const head=${JSON.stringify(headDigest)};let lastReceipt=Date.now();let stream=null;const receive=event=>{const next=JSON.parse(event.data);if(typeof next.head_digest!=='string')return;lastReceipt=Date.now();badge.textContent='進捗: 最新';if(next.head_digest!==head){badge.textContent='進捗: 更新を反映中';location.reload();}};const connect=()=>{if(stream)stream.close();badge.textContent='進捗: 接続中';stream=new EventSource(${JSON.stringify(eventsPath)});stream.onopen=()=>{lastReceipt=Date.now();};stream.addEventListener('state',receive);stream.addEventListener('ping',receive);stream.addEventListener('lattice-error',event=>{lastReceipt=Date.now();const detail=JSON.parse(event.data);badge.textContent='進捗: エラー '+detail.code;badge.style.borderColor='#d03b3b';});stream.onerror=()=>{badge.textContent='進捗: 再接続中';};};connect();setInterval(()=>{if(Date.now()-lastReceipt>${SSE_STALE_MS})connect();},${SSE_WATCHDOG_MS});})();</script>`;
|
|
62
65
|
const publicMetadata = '<meta name="description" content="Latticeで管理しているプロジェクトの依存工程と進捗を確認できます。"><meta name="robots" content="noindex, nofollow"><meta name="theme-color" content="#f7f3ea">';
|
|
63
66
|
const publicStyle = '<style>body[data-gantt-root]{grid-template-rows:auto minmax(0,1fr)}.lattice-live-brand{z-index:10;display:flex;align-items:center;gap:8px;min-width:0;padding:10px 16px;border-bottom:1px solid #d8d0c5;background:#f7f3ea;color:#6c655d;font:600 12px/1.5 system-ui,-apple-system,"Hiragino Sans","Yu Gothic UI",sans-serif}.lattice-live-brand a{color:#201d19;text-decoration:none}.lattice-live-brand a:hover{color:#315cbe}.lattice-live-brand strong{color:#201d19}.lattice-live-brand-note{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.lattice-live-back{margin-left:auto!important;color:#315cbe!important;font-weight:750}@media(max-width:560px){.lattice-live-brand{padding:9px 12px}.lattice-live-brand-note{display:none}}</style>';
|
|
64
67
|
const publicHeader = '<header class="lattice-live-brand"><a href="https://kitepon.dev/">kitepon.dev</a><span aria-hidden="true">/</span><strong>Lattice</strong><span class="lattice-live-brand-note">公開工程表</span><a class="lattice-live-back" href="/projects/">一覧へ戻る</a></header>';
|
|
@@ -184,6 +187,7 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
|
|
|
184
187
|
|| typeof registry.list !== 'function') throw new TypeError('registry required');
|
|
185
188
|
const clientsByProject = new Map();
|
|
186
189
|
const lastHeads = new Map();
|
|
190
|
+
const lastHeartbeats = new Map();
|
|
187
191
|
let eventId = 0;
|
|
188
192
|
let checking = false;
|
|
189
193
|
let closed = false;
|
|
@@ -194,6 +198,7 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
|
|
|
194
198
|
for (const client of clients) client.end();
|
|
195
199
|
clientsByProject.delete(projectId);
|
|
196
200
|
lastHeads.delete(projectId);
|
|
201
|
+
lastHeartbeats.delete(projectId);
|
|
197
202
|
}
|
|
198
203
|
};
|
|
199
204
|
const unsubscribe = typeof registry.subscribe === 'function'
|
|
@@ -255,6 +260,7 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
|
|
|
255
260
|
try {
|
|
256
261
|
const head = await project.readHead();
|
|
257
262
|
lastHeads.set(project.projectId, head);
|
|
263
|
+
if (!lastHeartbeats.has(project.projectId)) lastHeartbeats.set(project.projectId, Date.now());
|
|
258
264
|
sendEvent(response, 'state', ++eventId, { head_digest: head });
|
|
259
265
|
} catch (error) {
|
|
260
266
|
sendEvent(response, 'lattice-error', ++eventId, { code: error?.code ?? 'STORE_READ_FAILED' });
|
|
@@ -301,6 +307,11 @@ export async function startTodoGanttDashboardServer({ registry, port = 0, redire
|
|
|
301
307
|
lastHeads.set(project.projectId, head);
|
|
302
308
|
for (const client of clients) sendEvent(client, 'state', ++eventId, { head_digest: head });
|
|
303
309
|
}
|
|
310
|
+
const now = Date.now();
|
|
311
|
+
if (now - (lastHeartbeats.get(project.projectId) ?? now) >= SSE_HEARTBEAT_MS) {
|
|
312
|
+
lastHeartbeats.set(project.projectId, now);
|
|
313
|
+
for (const client of clients) sendEvent(client, 'ping', ++eventId, { head_digest: head });
|
|
314
|
+
}
|
|
304
315
|
} catch (error) {
|
|
305
316
|
for (const client of clients) sendEvent(client, 'lattice-error', ++eventId,
|
|
306
317
|
{ code: error?.code ?? 'STORE_READ_FAILED' });
|