@floless/app 0.19.0 → 0.20.0
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/dist/floless-server.cjs +12 -3
- package/dist/web/app.css +3 -1
- package/dist/web/aware.js +76 -18
- package/dist/web/index.html +7 -0
- package/package.json +1 -1
package/dist/floless-server.cjs
CHANGED
|
@@ -52683,7 +52683,7 @@ function appVersion() {
|
|
|
52683
52683
|
return resolveVersion({
|
|
52684
52684
|
isSea: isSea2(),
|
|
52685
52685
|
sqVersionXml: readSqVersionXml(),
|
|
52686
|
-
define: true ? "0.
|
|
52686
|
+
define: true ? "0.20.0" : void 0,
|
|
52687
52687
|
pkgVersion: readPkgVersion()
|
|
52688
52688
|
});
|
|
52689
52689
|
}
|
|
@@ -52693,7 +52693,7 @@ function resolveChannel(s) {
|
|
|
52693
52693
|
return "dev";
|
|
52694
52694
|
}
|
|
52695
52695
|
function appChannel() {
|
|
52696
|
-
return resolveChannel({ isSea: isSea2(), define: true ? "0.
|
|
52696
|
+
return resolveChannel({ isSea: isSea2(), define: true ? "0.20.0" : void 0 });
|
|
52697
52697
|
}
|
|
52698
52698
|
|
|
52699
52699
|
// oauth-presets.ts
|
|
@@ -57810,7 +57810,16 @@ async function startServer() {
|
|
|
57810
57810
|
broadcast({ type: "run-started", id, dryRun: !!dryRun, simulate: !!simulate, runId: result.runId });
|
|
57811
57811
|
for (const ev of events) broadcast({ type: "trace", id, event: ev });
|
|
57812
57812
|
broadcast({ type: "run-ended", id, runId: result.runId });
|
|
57813
|
-
const
|
|
57813
|
+
const extracted = extractReportHtml(events);
|
|
57814
|
+
let report = null;
|
|
57815
|
+
if (extracted) {
|
|
57816
|
+
let interactive = false;
|
|
57817
|
+
try {
|
|
57818
|
+
interactive = readApp(id).nodes.find((n) => n.id === extracted.nodeId)?.agent === "viewer-3d";
|
|
57819
|
+
} catch {
|
|
57820
|
+
}
|
|
57821
|
+
report = { ...interactive ? extracted : withBadge(extracted), interactive };
|
|
57822
|
+
}
|
|
57814
57823
|
const credentialIssue = !simulate && hasAuthFailure(events) ? await detectCredentialIssue(id) : null;
|
|
57815
57824
|
return { ok: true, runId: result.runId, tracePath: result.tracePath, events, report, credentialIssue };
|
|
57816
57825
|
}
|
package/dist/web/app.css
CHANGED
|
@@ -2209,7 +2209,9 @@ body {
|
|
|
2209
2209
|
.report-actions button.report-stop:hover { background: color-mix(in srgb, var(--warn) 16%, transparent); border-color: var(--warn); color: var(--text); }
|
|
2210
2210
|
#report-close { font-size: 18px; line-height: 1; padding: 4px 10px; }
|
|
2211
2211
|
.report-stage { position: relative; flex: 1; background: #0f172a; }
|
|
2212
|
-
|
|
2212
|
+
/* Only the SHOWN frame fills the stage — `:not([hidden])` so `display:block` never defeats the
|
|
2213
|
+
`hidden` attribute on the inactive frame (else it keeps its height and pushes the other off-screen). */
|
|
2214
|
+
#report-frame:not([hidden]), #report-frame-3d:not([hidden]) { width: 100%; height: 100%; border: 0; display: block; background: #0f172a; }
|
|
2213
2215
|
|
|
2214
2216
|
/* Live "On trigger" report: a pulsing pip in the viewer subtitle + a brief edge
|
|
2215
2217
|
flash on the stage when a trigger routine (e.g. tekla-selection-report) pushes a
|
package/dist/web/aware.js
CHANGED
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
const $notesStrip = document.getElementById('notes-strip');
|
|
19
19
|
const $reportModal = document.getElementById('report-modal');
|
|
20
20
|
const $reportFrame = document.getElementById('report-frame');
|
|
21
|
+
const $reportFrame3d = document.getElementById('report-frame-3d');
|
|
21
22
|
const $reportOverlay = document.getElementById('report-overlay');
|
|
22
23
|
const $reportTitle = document.getElementById('report-title');
|
|
23
24
|
const $reportSub = document.getElementById('report-sub');
|
|
@@ -838,10 +839,10 @@
|
|
|
838
839
|
function reportNodeId() {
|
|
839
840
|
const app = currentId && apps.get(currentId);
|
|
840
841
|
if (!app || !app.nodes.length) return null;
|
|
841
|
-
// An html-report agent node renders
|
|
842
|
-
// an `html` field,
|
|
843
|
-
// report producer regardless of exec code; prefer it directly as the viewer node.
|
|
844
|
-
const htmlReportNode = app.nodes.find((n) => n.agent === 'html-report');
|
|
842
|
+
// An html-report (static) or viewer-3d (interactive 3D) agent node renders HTML — its
|
|
843
|
+
// `render` command returns an `html` field, exactly what extractReportHtml keys on. So
|
|
844
|
+
// it's a report producer regardless of exec code; prefer it directly as the viewer node.
|
|
845
|
+
const htmlReportNode = app.nodes.find((n) => n.agent === 'html-report' || n.agent === 'viewer-3d');
|
|
845
846
|
if (htmlReportNode) return htmlReportNode.id;
|
|
846
847
|
// Otherwise, qualify the app only if some node actually PRODUCES report HTML — its exec
|
|
847
848
|
// code returns an `html` field (the `data.result.html` extractReportHtml keys
|
|
@@ -953,7 +954,11 @@
|
|
|
953
954
|
// clear anything we injected last render, so re-renders never stack
|
|
954
955
|
card.querySelectorAll('.node-action, .node-inputs').forEach((b) => b.remove());
|
|
955
956
|
if (isInput) addNodeInputs(card); // current values, above the button
|
|
956
|
-
if (isReport)
|
|
957
|
+
if (isReport) {
|
|
958
|
+
const rNode = app && app.nodes.find((n) => n.id === rid);
|
|
959
|
+
const viewLabel = rNode && rNode.agent === 'viewer-3d' ? 'View 3D ▸' : 'View report ▸';
|
|
960
|
+
addNodeAction(card, viewLabel, () => showReport(id));
|
|
961
|
+
}
|
|
957
962
|
if (isInput) addNodeAction(card, 'Set inputs ▸', () => openInputsDialog());
|
|
958
963
|
if (isRebake) addNodeAction(card, 'Re-read & re-bake ▸', () => openRebakeDialog());
|
|
959
964
|
});
|
|
@@ -1245,7 +1250,37 @@
|
|
|
1245
1250
|
|
|
1246
1251
|
// Share is visible exactly when the frame holds a report (the empty state HIDES it —
|
|
1247
1252
|
// never a disabled affordance with no path forward).
|
|
1248
|
-
|
|
1253
|
+
// The last HTML painted (static or 3D) — so ↗ Open works for both surfaces.
|
|
1254
|
+
let lastPaintedHtml = '';
|
|
1255
|
+
// Paint a report into the viewer. A STATIC report uses the scriptless srcdoc frame; an
|
|
1256
|
+
// INTERACTIVE 3D doc (viewer-3d) uses the script-enabled frame via a data: URL (opaque
|
|
1257
|
+
// origin, cannot touch the app page) and keeps a "Loading 3D view…" overlay until the doc
|
|
1258
|
+
// posts `viewer-ready`. Exactly one frame is shown at a time.
|
|
1259
|
+
function paintReport(html, interactive) {
|
|
1260
|
+
lastPaintedHtml = html || '';
|
|
1261
|
+
if (interactive) {
|
|
1262
|
+
$reportFrame.hidden = true; $reportFrame.srcdoc = '';
|
|
1263
|
+
$reportFrame3d.hidden = false;
|
|
1264
|
+
$reportFrame3d.src = 'data:text/html;charset=utf-8,' + encodeURIComponent(html);
|
|
1265
|
+
$reportShare.hidden = true; // a script doc can't be statically hosted — hide, don't disable
|
|
1266
|
+
$reportOverlay.hidden = false; $reportOverlay.replaceChildren();
|
|
1267
|
+
$reportOverlay.append(mkEl('div', 'spinner'), mkEl('div', null, 'Loading 3D view…'));
|
|
1268
|
+
} else {
|
|
1269
|
+
$reportFrame3d.hidden = true; $reportFrame3d.removeAttribute('src');
|
|
1270
|
+
$reportFrame.hidden = false; $reportFrame.srcdoc = html;
|
|
1271
|
+
$reportOverlay.hidden = true; $reportOverlay.replaceChildren();
|
|
1272
|
+
$reportShare.hidden = false;
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
// Reset the viewer to "no report shown": clear BOTH frames + the Open buffer, so a stale
|
|
1277
|
+
// interactive 3D doc can't linger (its WebGL loop running) behind an overlay, and ↗ Open
|
|
1278
|
+
// can't reopen a previous app's report. Used by every empty / transition state.
|
|
1279
|
+
function clearReportFrames() {
|
|
1280
|
+
$reportFrame.srcdoc = ''; $reportFrame.hidden = false;
|
|
1281
|
+
$reportFrame3d.hidden = true; $reportFrame3d.removeAttribute('src');
|
|
1282
|
+
lastPaintedHtml = '';
|
|
1283
|
+
}
|
|
1249
1284
|
|
|
1250
1285
|
// Double-click the HTML Viewer node → LOAD the last report (no run; running is
|
|
1251
1286
|
// the header "▶ Run workflow" button's job). Shows a prompt if nothing has run yet.
|
|
@@ -1258,10 +1293,16 @@
|
|
|
1258
1293
|
if (reportRunning) return; // a run is in flight — its overlay already shows progress
|
|
1259
1294
|
const cached = lastReportByApp.get(currentId);
|
|
1260
1295
|
if (cached && cached.html) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1296
|
+
const sfx = cached.label ? ' · ' + cached.label : '';
|
|
1297
|
+
if (cached.interactive) {
|
|
1298
|
+
$reportTitle.textContent = `3D Viewer · ${app.displayName}`;
|
|
1299
|
+
$reportSub.textContent = `Last 3D view${sfx} — drag to orbit, scroll to zoom, click an element to inspect.`;
|
|
1300
|
+
} else {
|
|
1301
|
+
$reportSub.textContent = `Last report${sfx} — rendered from the run's output, never composed by the UI.`;
|
|
1302
|
+
}
|
|
1303
|
+
paintReport(cached.html, cached.interactive);
|
|
1263
1304
|
} else {
|
|
1264
|
-
|
|
1305
|
+
clearReportFrames();
|
|
1265
1306
|
$reportShare.hidden = true; // nothing to share — hide, don't disable
|
|
1266
1307
|
$reportOverlay.hidden = false;
|
|
1267
1308
|
$reportOverlay.innerHTML = `<div>No report yet. Click <strong>▶ Run workflow</strong> (top right) to run it against the live model, then double-click to view it any time.</div>`;
|
|
@@ -1286,7 +1327,7 @@
|
|
|
1286
1327
|
const inputBadge = Object.entries(inputs).map(([k, v]) => `${k}=${v}`).join(' · ');
|
|
1287
1328
|
$reportTitle.textContent = `HTML Viewer · ${app.displayName}`;
|
|
1288
1329
|
$reportSub.innerHTML = `Live run of <code>${escapeHtml(nodeId)}</code>${inputBadge ? ' · ' + escapeHtml(inputBadge) : ''} — rendered from the node's output, never composed by the UI.`;
|
|
1289
|
-
|
|
1330
|
+
clearReportFrames(); // clear any previous report (static or 3D) before this run paints a fresh one
|
|
1290
1331
|
$reportShare.hidden = true; // the frame is blank — only a painted report is shareable
|
|
1291
1332
|
$reportOverlay.hidden = false;
|
|
1292
1333
|
$reportOverlay.innerHTML = opts.debug
|
|
@@ -1307,14 +1348,19 @@
|
|
|
1307
1348
|
// body, not real data) — prompt reconnect instead of rendering the noise.
|
|
1308
1349
|
if (res.credentialIssue) { surfaceCredentialIssue(res.credentialIssue); return; }
|
|
1309
1350
|
const html = res.report && res.report.html;
|
|
1351
|
+
const interactive = !!(res.report && res.report.interactive);
|
|
1310
1352
|
if (!html) {
|
|
1311
1353
|
$reportOverlay.innerHTML = `<div>Run completed but <code>${escapeHtml(nodeId)}</code> returned no <code>html</code>. Check the Execution tab.</div>`;
|
|
1312
1354
|
return;
|
|
1313
1355
|
}
|
|
1314
|
-
|
|
1315
|
-
|
|
1356
|
+
if (interactive) {
|
|
1357
|
+
$reportTitle.textContent = `3D Viewer · ${app.displayName}`;
|
|
1358
|
+
$reportSub.innerHTML = `Live 3D view from <code>${escapeHtml(nodeId)}</code>${inputBadge ? ' · ' + escapeHtml(inputBadge) : ''} — drag to orbit, scroll to zoom, click an element to inspect.`;
|
|
1359
|
+
}
|
|
1360
|
+
paintReport(html, interactive);
|
|
1361
|
+
lastReportByApp.set(currentId, { nodeId, label: inputBadge, html, interactive });
|
|
1316
1362
|
$reportModal.dataset.html = '1';
|
|
1317
|
-
appendNarration(`Rendered the report from <strong>${escapeHtml(nodeId)}</strong>${inputBadge ? ' (' + escapeHtml(inputBadge) + ')' : ''} — live run against the model.`);
|
|
1363
|
+
appendNarration(`Rendered the ${interactive ? '3D view' : 'report'} from <strong>${escapeHtml(nodeId)}</strong>${inputBadge ? ' (' + escapeHtml(inputBadge) + ')' : ''} — live run against the model.`);
|
|
1318
1364
|
} catch (e) {
|
|
1319
1365
|
const msg = e && e.message ? e.message : String(e);
|
|
1320
1366
|
if (cancelRequested) {
|
|
@@ -1362,7 +1408,7 @@
|
|
|
1362
1408
|
const CRED_BODY_IDLE = 'Sign in again and then run the workflow — it takes about 30 seconds.';
|
|
1363
1409
|
|
|
1364
1410
|
function showCredentialReconnect(cred) {
|
|
1365
|
-
|
|
1411
|
+
clearReportFrames();
|
|
1366
1412
|
$reportShare.hidden = true;
|
|
1367
1413
|
$reportOverlay.hidden = false;
|
|
1368
1414
|
$reportOverlay.replaceChildren();
|
|
@@ -1674,10 +1720,22 @@
|
|
|
1674
1720
|
// The Stop button is rebuilt into the overlay each run — delegate so one
|
|
1675
1721
|
// listener survives every innerHTML swap.
|
|
1676
1722
|
$reportOverlay.addEventListener('click', (e) => { if (e.target.closest('.overlay-stop')) stopRun(); });
|
|
1723
|
+
// The interactive 3D doc (viewer-3d) posts a load handshake from its opaque-origin frame:
|
|
1724
|
+
// `viewer-ready` clears the loading overlay; `viewer-error` (CDN/script/timeout) shows a
|
|
1725
|
+
// retry. Guard: only when the 3D frame is the active surface and the message is from it.
|
|
1726
|
+
window.addEventListener('message', (e) => {
|
|
1727
|
+
if ($reportFrame3d.hidden || e.source !== $reportFrame3d.contentWindow) return;
|
|
1728
|
+
const t = e.data && e.data.type;
|
|
1729
|
+
if (t === 'viewer-ready') { $reportOverlay.hidden = true; $reportOverlay.replaceChildren(); }
|
|
1730
|
+
else if (t === 'viewer-error') {
|
|
1731
|
+
$reportOverlay.hidden = false; $reportOverlay.replaceChildren();
|
|
1732
|
+
$reportOverlay.append(mkEl('div', null, '3D view failed to load — check your connection, then click ▶ Run workflow to try again.'));
|
|
1733
|
+
}
|
|
1734
|
+
});
|
|
1677
1735
|
// The header ■ Stop run — the always-reachable twin of the overlay Stop (#39).
|
|
1678
1736
|
if ($stopRunBtn) $stopRunBtn.onclick = () => stopRun();
|
|
1679
1737
|
$reportOpen.onclick = () => {
|
|
1680
|
-
const html = $reportFrame.srcdoc;
|
|
1738
|
+
const html = lastPaintedHtml || $reportFrame.srcdoc;
|
|
1681
1739
|
if (!html) return;
|
|
1682
1740
|
// Open via a blob: URL — an opaque origin, so the report can't script the
|
|
1683
1741
|
// app origin (mirrors the iframe's sandbox intent).
|
|
@@ -4033,7 +4091,7 @@
|
|
|
4033
4091
|
$reportModal.dataset.nodeId = reportNodeId() || '';
|
|
4034
4092
|
const cached = lastReportByApp.get(id);
|
|
4035
4093
|
if (cached && cached.html) paintReport(cached.html);
|
|
4036
|
-
else {
|
|
4094
|
+
else { clearReportFrames(); $reportShare.hidden = true; }
|
|
4037
4095
|
$reportOverlay.hidden = false;
|
|
4038
4096
|
$reportOverlay.replaceChildren();
|
|
4039
4097
|
$reportOverlay.append(mkEl('div', 'spinner'), mkEl('div', null, 'Starting live session…'));
|
|
@@ -4118,7 +4176,7 @@
|
|
|
4118
4176
|
$reportShare.hidden = false; // the cached report is the primary content
|
|
4119
4177
|
$reportSub.innerHTML = `<span class="live-pip" aria-hidden="true"></span>Live · waiting for the next change — change your selection in Tekla to refresh the report.`;
|
|
4120
4178
|
} else {
|
|
4121
|
-
|
|
4179
|
+
clearReportFrames();
|
|
4122
4180
|
$reportShare.hidden = true;
|
|
4123
4181
|
$reportOverlay.hidden = false;
|
|
4124
4182
|
$reportOverlay.replaceChildren();
|
|
@@ -4146,7 +4204,7 @@
|
|
|
4146
4204
|
? `Run complete · last report above — click <strong>▶ Run workflow</strong> to run again.`
|
|
4147
4205
|
: `Live session stopped · last report above — click <strong>▶ Run workflow</strong> to start again.`;
|
|
4148
4206
|
} else {
|
|
4149
|
-
|
|
4207
|
+
clearReportFrames();
|
|
4150
4208
|
$reportShare.hidden = true;
|
|
4151
4209
|
$reportOverlay.hidden = false;
|
|
4152
4210
|
$reportOverlay.replaceChildren();
|
package/dist/web/index.html
CHANGED
|
@@ -319,6 +319,13 @@
|
|
|
319
319
|
Scripts stay BLOCKED (no allow-scripts), so even though the report is now
|
|
320
320
|
same-origin it has no JS to touch the app page — static reports only. -->
|
|
321
321
|
<iframe id="report-frame" sandbox="allow-same-origin allow-popups allow-popups-to-escape-sandbox" title="Report"></iframe>
|
|
322
|
+
<!-- Interactive output (the viewer-3d agent) needs scripts, so it cannot use the
|
|
323
|
+
scriptless static frame above. Loaded via a `data:text/html` URL → an opaque
|
|
324
|
+
origin (cannot reach the app page, cookies, or same-origin APIs). Minimal
|
|
325
|
+
sandbox: `allow-scripts` ONLY — the 3D doc has no links/popups of its own
|
|
326
|
+
(↗ Open is a parent-side action), and NO allow-same-origin. Shown only for
|
|
327
|
+
interactive reports; the static frame is used otherwise. -->
|
|
328
|
+
<iframe id="report-frame-3d" sandbox="allow-scripts" title="3D view" hidden></iframe>
|
|
322
329
|
<div class="report-overlay" id="report-overlay" hidden></div>
|
|
323
330
|
</div>
|
|
324
331
|
</div>
|