@launchsecure/launch-kit 0.0.40 → 0.0.41
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/chart-client/assets/index-Dd6IotOZ.css +1 -0
- package/dist/chart-client/index.html +2 -2
- package/dist/client/assets/index-DE0uje6k.css +32 -0
- package/dist/client/index.html +2 -2
- package/dist/council-client/assets/index-CGYusOCK.css +1 -0
- package/dist/council-client/assets/{index-B1v46vTE.js → index-DkTFX53U.js} +1 -1
- package/dist/council-client/index.html +2 -2
- package/dist/deck-client/assets/_baseUniq-mvYvzeEJ.js +1 -0
- package/dist/deck-client/assets/arc-CX4ylnp2.js +1 -0
- package/dist/deck-client/assets/architectureDiagram-Q4EWVU46-BkR-5IRK.js +36 -0
- package/dist/deck-client/assets/{blockDiagram-DXYQGD6D-CUdblaWk.js → blockDiagram-DXYQGD6D-DVNQht7c.js} +2 -2
- package/dist/deck-client/assets/{c4Diagram-AHTNJAMY-MfAO5lak.js → c4Diagram-AHTNJAMY-Cbq1rlG8.js} +2 -2
- package/dist/deck-client/assets/channel-B9GC-CLn.js +1 -0
- package/dist/deck-client/assets/chunk-4BX2VUAB-D58Co4lU.js +1 -0
- package/dist/deck-client/assets/{chunk-4TB4RGXK-BUJtZ7jO.js → chunk-4TB4RGXK-BYvhTm3d.js} +1 -1
- package/dist/deck-client/assets/chunk-55IACEB6-oWukUhYg.js +1 -0
- package/dist/deck-client/assets/chunk-EDXVE4YY-Cm58kVnZ.js +1 -0
- package/dist/deck-client/assets/{chunk-FMBD7UC4-PnZ9v6ey.js → chunk-FMBD7UC4-Dg-i7kzi.js} +1 -1
- package/dist/deck-client/assets/{chunk-OYMX7WX6-DXrWNOsV.js → chunk-OYMX7WX6-C72wigPl.js} +1 -1
- package/dist/deck-client/assets/chunk-QZHKN3VN-CLgeuAKw.js +1 -0
- package/dist/deck-client/assets/chunk-YZCP3GAM-HDDlJ5oI.js +1 -0
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-CFBvYQ9j.js +1 -0
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-CFBvYQ9j.js +1 -0
- package/dist/deck-client/assets/clone-n-WQlAGe.js +1 -0
- package/dist/deck-client/assets/cose-bilkent-S5V4N54A-CUXQKg2M.js +1 -0
- package/dist/deck-client/assets/dagre-KV5264BT-C5M-fVDc.js +4 -0
- package/dist/deck-client/assets/diagram-5BDNPKRD-CcVsQ0S8.js +10 -0
- package/dist/deck-client/assets/diagram-G4DWMVQ6-DJswXyep.js +24 -0
- package/dist/deck-client/assets/diagram-MMDJMWI5-CGT76fm1.js +43 -0
- package/dist/deck-client/assets/diagram-TYMM5635-BBsYUNN6.js +24 -0
- package/dist/deck-client/assets/{erDiagram-SMLLAGMA-56pn_93p.js → erDiagram-SMLLAGMA-DKWYEHQS.js} +2 -2
- package/dist/deck-client/assets/{flowDiagram-DWJPFMVM-BtV3M5xJ.js → flowDiagram-DWJPFMVM-DLuDYIKT.js} +2 -2
- package/dist/deck-client/assets/ganttDiagram-T4ZO3ILL-B19b6Qtj.js +292 -0
- package/dist/deck-client/assets/gitGraphDiagram-UUTBAWPF-BYLAfYVS.js +106 -0
- package/dist/deck-client/assets/graph-CfzQUfPh.js +1 -0
- package/dist/deck-client/assets/index-DlwdTgE_.js +892 -0
- package/dist/deck-client/assets/index-evAPhGvM.css +1 -0
- package/dist/deck-client/assets/infoDiagram-42DDH7IO-Dp3mUA9c.js +2 -0
- package/dist/deck-client/assets/{ishikawaDiagram-UXIWVN3A-DXYkdO3T.js → ishikawaDiagram-UXIWVN3A-BhrNX_jI.js} +5 -5
- package/dist/deck-client/assets/{journeyDiagram-VCZTEJTY-C2zBr-J5.js → journeyDiagram-VCZTEJTY-B5lJI492.js} +2 -2
- package/dist/deck-client/assets/{kanban-definition-6JOO6SKY-CdoYLS4Z.js → kanban-definition-6JOO6SKY-D9-lmhQf.js} +2 -2
- package/dist/deck-client/assets/layout-CfIe_Su8.js +1 -0
- package/dist/deck-client/assets/linear-09ZFRoh_.js +1 -0
- package/dist/deck-client/assets/mermaid.core-BaQyIOvj.js +309 -0
- package/dist/deck-client/assets/min-CYwCzYaL.js +1 -0
- package/dist/deck-client/assets/{mindmap-definition-QFDTVHPH-oAybLedr.js → mindmap-definition-QFDTVHPH-CouFxf6C.js} +2 -2
- package/dist/deck-client/assets/pieDiagram-DEJITSTG-DMB1ufC0.js +30 -0
- package/dist/deck-client/assets/{quadrantDiagram-34T5L4WZ-dtluDZXs.js → quadrantDiagram-34T5L4WZ-CBiOKudN.js} +2 -2
- package/dist/deck-client/assets/{requirementDiagram-MS252O5E-Cq8l7bOl.js → requirementDiagram-MS252O5E-BMc3GJkx.js} +2 -2
- package/dist/deck-client/assets/sankeyDiagram-XADWPNL6-CxACUncm.js +10 -0
- package/dist/deck-client/assets/{sequenceDiagram-FGHM5R23-CYkd7oQK.js → sequenceDiagram-FGHM5R23-Ch-P3Mzc.js} +2 -2
- package/dist/deck-client/assets/stateDiagram-FHFEXIEX-Cy8n7Yzk.js +1 -0
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-C14VKCzi.js +1 -0
- package/dist/deck-client/assets/{timeline-definition-GMOUNBTQ-DZIxSyd1.js → timeline-definition-GMOUNBTQ-C2V4sSkm.js} +2 -2
- package/dist/deck-client/assets/{vennDiagram-DHZGUBPP-Ct4JVRDM.js → vennDiagram-DHZGUBPP-YOqt4VbE.js} +2 -2
- package/dist/deck-client/assets/{wardley-RL74JXVD-V29ycxOW.js → wardley-RL74JXVD-Bxo5x40D.js} +3 -3
- package/dist/deck-client/assets/{wardleyDiagram-NUSXRM2D-D-Ua6Cmi.js → wardleyDiagram-NUSXRM2D-DW9SOqbx.js} +2 -2
- package/dist/deck-client/assets/{xychartDiagram-5P7HB3ND-BPCOuRVl.js → xychartDiagram-5P7HB3ND-D-rZvZOL.js} +2 -2
- package/dist/deck-client/index.html +2 -2
- package/dist/server/cli.js +488 -215
- package/dist/server/council-entry.js +7 -1
- package/dist/server/council-serve.js +7 -1
- package/dist/server/deck-mcp-entry.js +287 -49
- package/dist/server/deck-serve.js +258 -48
- package/dist/server/graph-mcp-entry.js +331 -37
- package/dist/server/init-entry.js +17 -7
- package/dist/server/rover-entry.js +1 -1
- package/package.json +1 -1
- package/scaffolds/ls-marketplace/plugins/kit/skills/comms/SKILL.md +34 -1
- package/scaffolds/ls-marketplace/plugins/kit/skills/gen-test/SKILL.md +126 -0
- package/dist/chart-client/assets/index-CWJFFDPu.css +0 -1
- package/dist/client/assets/index-CTzFcfGV.css +0 -32
- package/dist/council-client/assets/index-ArgRc5mN.css +0 -1
- package/dist/deck-client/assets/_baseUniq-BZP7n41F.js +0 -1
- package/dist/deck-client/assets/arc-31biU3Az.js +0 -1
- package/dist/deck-client/assets/architectureDiagram-Q4EWVU46-DHg6Ss--.js +0 -36
- package/dist/deck-client/assets/channel-BBkRLdnC.js +0 -1
- package/dist/deck-client/assets/chunk-4BX2VUAB-DQ1MrGgN.js +0 -1
- package/dist/deck-client/assets/chunk-55IACEB6-BdSnXB6g.js +0 -1
- package/dist/deck-client/assets/chunk-EDXVE4YY-94yZIUI8.js +0 -1
- package/dist/deck-client/assets/chunk-QZHKN3VN-CsIGIDKX.js +0 -1
- package/dist/deck-client/assets/chunk-YZCP3GAM-DVkBO9tn.js +0 -1
- package/dist/deck-client/assets/classDiagram-6PBFFD2Q-DFCaeF-7.js +0 -1
- package/dist/deck-client/assets/classDiagram-v2-HSJHXN6E-DFCaeF-7.js +0 -1
- package/dist/deck-client/assets/clone-GCEVRScB.js +0 -1
- package/dist/deck-client/assets/cose-bilkent-S5V4N54A-m126Oh3b.js +0 -1
- package/dist/deck-client/assets/dagre-KV5264BT-C2aig8U5.js +0 -4
- package/dist/deck-client/assets/diagram-5BDNPKRD-CKpoRfGn.js +0 -10
- package/dist/deck-client/assets/diagram-G4DWMVQ6-Cjh115Ep.js +0 -24
- package/dist/deck-client/assets/diagram-MMDJMWI5-DKlBv_2L.js +0 -43
- package/dist/deck-client/assets/diagram-TYMM5635-CdBh4cEn.js +0 -24
- package/dist/deck-client/assets/ganttDiagram-T4ZO3ILL-DTIsC6Zg.js +0 -292
- package/dist/deck-client/assets/gitGraphDiagram-UUTBAWPF-CJYeyCLe.js +0 -106
- package/dist/deck-client/assets/graph-BDvMu1Ss.js +0 -1
- package/dist/deck-client/assets/index-D4eSxcBn.css +0 -1
- package/dist/deck-client/assets/index-QnGVE9PZ.js +0 -1196
- package/dist/deck-client/assets/infoDiagram-42DDH7IO-BWyKJnpW.js +0 -2
- package/dist/deck-client/assets/layout-vOnxnCQU.js +0 -1
- package/dist/deck-client/assets/linear-B0J0WCGz.js +0 -1
- package/dist/deck-client/assets/min-B0AXlT9L.js +0 -1
- package/dist/deck-client/assets/pieDiagram-DEJITSTG-BjHyHxGk.js +0 -30
- package/dist/deck-client/assets/sankeyDiagram-XADWPNL6-C1Vih91z.js +0 -10
- package/dist/deck-client/assets/stateDiagram-FHFEXIEX-CtyG8wBK.js +0 -1
- package/dist/deck-client/assets/stateDiagram-v2-QKLJ7IA2-BLyKWfcN.js +0 -1
- /package/dist/chart-client/assets/{index-Dzlj-oCj.js → index-CrYM1-ac.js} +0 -0
- /package/dist/client/assets/{index-tTg_ezUF.js → index-BoIjawzY.js} +0 -0
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var deck_serve_exports = {};
|
|
32
32
|
__export(deck_serve_exports, {
|
|
33
33
|
broadcastToClients: () => broadcastToClients,
|
|
34
|
+
buildSharePayload: () => buildSharePayload,
|
|
34
35
|
consumeRenderError: () => consumeRenderError,
|
|
35
36
|
createFeedbackWaiter: () => createFeedbackWaiter,
|
|
36
37
|
resolveFeedback: () => resolveFeedback,
|
|
@@ -1399,7 +1400,13 @@ async function callTool(toolName, args) {
|
|
|
1399
1400
|
}
|
|
1400
1401
|
const textContent = result.result?.content?.[0]?.text;
|
|
1401
1402
|
if (!textContent) return null;
|
|
1402
|
-
|
|
1403
|
+
try {
|
|
1404
|
+
return JSON.parse(textContent);
|
|
1405
|
+
} catch {
|
|
1406
|
+
const match = textContent.match(/──+\s*Error\s*──+\s*\n([\s\S]*?)(?:\n\n──|$)/i);
|
|
1407
|
+
const message = (match?.[1] ?? textContent).trim();
|
|
1408
|
+
throw new Error(message || "MCP tool returned an unparseable response");
|
|
1409
|
+
}
|
|
1403
1410
|
}
|
|
1404
1411
|
async function writeDeck(input) {
|
|
1405
1412
|
const result = await callTool("communication_write", {
|
|
@@ -1408,6 +1415,7 @@ async function writeDeck(input) {
|
|
|
1408
1415
|
resource_type: "deck",
|
|
1409
1416
|
fields: {
|
|
1410
1417
|
deckHtml: input.html,
|
|
1418
|
+
deckBlocks: input.blocks,
|
|
1411
1419
|
sessionId: input.sessionId,
|
|
1412
1420
|
blockCount: input.blockCount,
|
|
1413
1421
|
sharedFrom: "launch-deck"
|
|
@@ -1422,6 +1430,7 @@ async function updateDeck(commentId, input) {
|
|
|
1422
1430
|
body: input.body,
|
|
1423
1431
|
fields: {
|
|
1424
1432
|
deckHtml: input.html,
|
|
1433
|
+
deckBlocks: input.blocks,
|
|
1425
1434
|
sessionId: input.sessionId,
|
|
1426
1435
|
blockCount: input.blockCount,
|
|
1427
1436
|
sharedFrom: "launch-deck"
|
|
@@ -1432,6 +1441,31 @@ async function updateDeck(commentId, input) {
|
|
|
1432
1441
|
async function deleteDeck(commentId) {
|
|
1433
1442
|
await callTool("communication_delete", { comment_id: commentId });
|
|
1434
1443
|
}
|
|
1444
|
+
function currentMcpOrigin() {
|
|
1445
|
+
const config = _config;
|
|
1446
|
+
if (!config) throw new Error("MCP config not loaded \u2014 call loadMcpConfig first");
|
|
1447
|
+
return new URL(config.url).origin;
|
|
1448
|
+
}
|
|
1449
|
+
async function createDeckShareLink(commentId) {
|
|
1450
|
+
const result = await callTool("deck_share_link", {
|
|
1451
|
+
comment_id: commentId,
|
|
1452
|
+
action: "create"
|
|
1453
|
+
});
|
|
1454
|
+
const token = result.link.token;
|
|
1455
|
+
return {
|
|
1456
|
+
linkId: result.link.id,
|
|
1457
|
+
token,
|
|
1458
|
+
shareUrl: `${currentMcpOrigin()}/s/deck/${token}`,
|
|
1459
|
+
reused: Boolean(result.reused)
|
|
1460
|
+
};
|
|
1461
|
+
}
|
|
1462
|
+
async function revokeDeckShareLink(commentId, linkId) {
|
|
1463
|
+
await callTool("deck_share_link", {
|
|
1464
|
+
comment_id: commentId,
|
|
1465
|
+
action: "revoke",
|
|
1466
|
+
link_id: linkId
|
|
1467
|
+
});
|
|
1468
|
+
}
|
|
1435
1469
|
|
|
1436
1470
|
// src/server/deck-serve.ts
|
|
1437
1471
|
var DEFAULT_PORT = 52829;
|
|
@@ -1496,7 +1530,9 @@ function listPersistedSessions(cwd) {
|
|
|
1496
1530
|
createdAt: persisted.createdAt,
|
|
1497
1531
|
updatedAt: persisted.updatedAt,
|
|
1498
1532
|
shared: Boolean(sync),
|
|
1499
|
-
stale: Boolean(sync) && persisted.version > (sync?.version ?? 0)
|
|
1533
|
+
stale: Boolean(sync) && persisted.version > (sync?.version ?? 0),
|
|
1534
|
+
shareUrl: sync?.publicShareUrl,
|
|
1535
|
+
course: sync?.course
|
|
1500
1536
|
});
|
|
1501
1537
|
}
|
|
1502
1538
|
sessions.sort((a, b) => b.updatedAt.localeCompare(a.updatedAt));
|
|
@@ -1524,6 +1560,24 @@ function readSyncRecord(cwd, session) {
|
|
|
1524
1560
|
function writeSyncRecord(cwd, session, rec) {
|
|
1525
1561
|
import_node_fs4.default.writeFileSync(syncJsonPath(cwd, session), JSON.stringify(rec, null, 2));
|
|
1526
1562
|
}
|
|
1563
|
+
function resolveShareConfig(cwd, course) {
|
|
1564
|
+
if (course) {
|
|
1565
|
+
const override = mcpConfigForProfile(cwd, course);
|
|
1566
|
+
if (!override) {
|
|
1567
|
+
return { error: `Course "${course}" is not a usable profile in .launch-secure.cred.config (missing pat/serverUrl/orgSlug/projectSlug?).` };
|
|
1568
|
+
}
|
|
1569
|
+
return { override };
|
|
1570
|
+
}
|
|
1571
|
+
try {
|
|
1572
|
+
loadMcpConfig(cwd);
|
|
1573
|
+
} catch (err) {
|
|
1574
|
+
return { error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}` };
|
|
1575
|
+
}
|
|
1576
|
+
return { override: null };
|
|
1577
|
+
}
|
|
1578
|
+
function runWithShareConfig(override, fn) {
|
|
1579
|
+
return override ? withMcpConfig(override, fn) : fn();
|
|
1580
|
+
}
|
|
1527
1581
|
function escHtml2(s) {
|
|
1528
1582
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
1529
1583
|
}
|
|
@@ -1540,31 +1594,49 @@ function readIframeArtifact(cwd, src) {
|
|
|
1540
1594
|
return null;
|
|
1541
1595
|
}
|
|
1542
1596
|
}
|
|
1543
|
-
function renderShareSection(cwd, block) {
|
|
1544
|
-
const label = block?.label ? `<h2 class="db-title">${escHtml2(String(block.label))}</h2>` : "";
|
|
1597
|
+
function renderShareSection(cwd, block, showLabel = true) {
|
|
1598
|
+
const label = showLabel && block?.label ? `<h2 class="db-title">${escHtml2(String(block.label))}</h2>` : "";
|
|
1545
1599
|
switch (block?.type) {
|
|
1546
1600
|
case "iframe": {
|
|
1547
1601
|
const doc = readIframeArtifact(cwd, block.src) ?? "<p>(missing artifact)</p>";
|
|
1548
|
-
return
|
|
1602
|
+
return `${label}<iframe class="db-frame" sandbox="allow-scripts allow-popups" srcdoc="${escAttr(doc)}"></iframe>`;
|
|
1549
1603
|
}
|
|
1550
1604
|
case "rich-html":
|
|
1551
1605
|
case "html":
|
|
1552
|
-
return
|
|
1606
|
+
return `${label}<div class="db-html">${typeof block.content === "string" ? block.content : ""}</div>`;
|
|
1553
1607
|
case "markdown":
|
|
1554
|
-
return
|
|
1608
|
+
return `${label}<div class="db-md" data-md="${escAttr(typeof block.content === "string" ? block.content : "")}"></div>`;
|
|
1555
1609
|
case "mermaid":
|
|
1556
|
-
|
|
1610
|
+
if (typeof block.renderedSvg === "string" && block.renderedSvg.trim()) {
|
|
1611
|
+
return `${label}<div class="mermaid-svg">${block.renderedSvg}</div>`;
|
|
1612
|
+
}
|
|
1613
|
+
return `${label}<pre class="mermaid">${escHtml2(typeof block.content === "string" ? block.content : "")}</pre>`;
|
|
1557
1614
|
case "options": {
|
|
1558
1615
|
const opts = Array.isArray(block.options) ? block.options : [];
|
|
1559
|
-
const
|
|
1560
|
-
(o) => `<
|
|
1616
|
+
const cards = opts.map(
|
|
1617
|
+
(o) => `<div class="db-option"><div class="db-option-label">${escHtml2(String(o?.label ?? o?.id ?? ""))}</div>${o?.description ? `<div class="db-option-desc">${escHtml2(String(o.description))}</div>` : ""}${o?.preview ? `<pre class="db-option-pre">${escHtml2(String(o.preview))}</pre>` : ""}</div>`
|
|
1561
1618
|
).join("");
|
|
1562
|
-
return
|
|
1619
|
+
return `${label}<div class="db-options">${cards}</div>`;
|
|
1563
1620
|
}
|
|
1564
1621
|
default:
|
|
1565
|
-
return
|
|
1622
|
+
return label;
|
|
1566
1623
|
}
|
|
1567
1624
|
}
|
|
1625
|
+
function buildSharePayload(cwd, session, blocks, svgByIndex = {}) {
|
|
1626
|
+
const enriched = blocks.map((b, i) => {
|
|
1627
|
+
const svg = svgByIndex[String(i)];
|
|
1628
|
+
return b?.type === "mermaid" && typeof svg === "string" && svg.trim() ? { ...b, renderedSvg: svg } : b;
|
|
1629
|
+
});
|
|
1630
|
+
const html = buildShareHtml(cwd, session, enriched);
|
|
1631
|
+
const cloudBlocks = enriched.map((b) => {
|
|
1632
|
+
if (b?.type === "iframe" && typeof b.src === "string") {
|
|
1633
|
+
const content = readIframeArtifact(cwd, b.src);
|
|
1634
|
+
return content ? { ...b, content } : b;
|
|
1635
|
+
}
|
|
1636
|
+
return b;
|
|
1637
|
+
});
|
|
1638
|
+
return { html, cloudBlocks };
|
|
1639
|
+
}
|
|
1568
1640
|
function buildShareHtml(cwd, session, blocks) {
|
|
1569
1641
|
if (blocks.length === 1) {
|
|
1570
1642
|
const only = blocks[0];
|
|
@@ -1574,30 +1646,61 @@ function buildShareHtml(cwd, session, blocks) {
|
|
|
1574
1646
|
}
|
|
1575
1647
|
}
|
|
1576
1648
|
const list = blocks;
|
|
1577
|
-
const
|
|
1649
|
+
const multi = list.length > 1;
|
|
1650
|
+
const tabBar = multi ? `<nav class="deck-tabs">${list.map(
|
|
1651
|
+
(b, i) => `<button class="deck-tab${i === 0 ? " active" : ""}" data-tab-btn="${i}">${escHtml2(String(b?.label ?? `Tab ${i + 1}`))}</button>`
|
|
1652
|
+
).join("")}</nav>` : "";
|
|
1653
|
+
const sections = list.map(
|
|
1654
|
+
(b, i) => `<section class="db" data-tab="${i}"${multi && i > 0 ? " hidden" : ""}>${renderShareSection(cwd, b, !multi)}</section>`
|
|
1655
|
+
).join("\n");
|
|
1578
1656
|
const hasMd = list.some((b) => b?.type === "markdown");
|
|
1579
|
-
const
|
|
1657
|
+
const hasUnrenderedMermaid = list.some(
|
|
1658
|
+
(b) => b?.type === "mermaid" && !(typeof b?.renderedSvg === "string" && b.renderedSvg.trim())
|
|
1659
|
+
);
|
|
1580
1660
|
return `<!DOCTYPE html>
|
|
1581
1661
|
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1">
|
|
1582
1662
|
<title>${escHtml2(session)}</title>
|
|
1663
|
+
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,100..1000&display=swap" rel="stylesheet">
|
|
1583
1664
|
<style>
|
|
1584
|
-
body { margin:0; font-family:
|
|
1665
|
+
body { margin:0; font-family: 'DM Sans', system-ui, sans-serif; background:#0b0e14; color:#e6e9ef; }
|
|
1585
1666
|
.db { padding:20px 24px; border-bottom:1px solid #1e2433; }
|
|
1586
1667
|
.db-title { font-size:13px; font-weight:600; margin:0 0 12px; color:#9aa4b2; text-transform:uppercase; letter-spacing:.05em; }
|
|
1587
1668
|
.db-frame { width:100%; height:70vh; border:0; border-radius:8px; background:#fff; }
|
|
1588
1669
|
.db-html { background:#fff; color:#111; border-radius:8px; padding:16px; overflow:auto; }
|
|
1589
1670
|
ul { line-height:1.7; }
|
|
1590
|
-
|
|
1671
|
+
/* The embedded SVG is dark-themed (captured from the deck client); keep its
|
|
1672
|
+
backdrop transparent so it sits on the dark page, matching the in-app
|
|
1673
|
+
DeckView \u2014 not a white card. The CDN-source fallback also renders dark. */
|
|
1674
|
+
.mermaid { background:transparent; border-radius:8px; padding:16px; }
|
|
1675
|
+
.mermaid-svg { background:transparent; border-radius:8px; padding:16px; overflow:auto; }
|
|
1676
|
+
.mermaid-svg svg { max-width:100%; height:auto; }
|
|
1677
|
+
.db-options { display:grid; gap:12px; grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); }
|
|
1678
|
+
.db-option { border:2px solid #1e2433; border-radius:8px; padding:16px; }
|
|
1679
|
+
.db-option-label { font-size:14px; font-weight:600; color:#e6e9ef; }
|
|
1680
|
+
.db-option-desc { margin-top:4px; font-size:12px; color:#9aa4b2; }
|
|
1681
|
+
.db-option-pre { margin-top:8px; padding:8px; border-radius:6px; background:#11151f; font-size:12px; overflow:auto; }
|
|
1682
|
+
.deck-tabs { position:sticky; top:0; z-index:5; display:flex; flex-wrap:wrap; gap:2px; background:#0b0e14; border-bottom:1px solid #1e2433; padding:0 16px; }
|
|
1683
|
+
.deck-tab { appearance:none; border:0; background:transparent; color:#9aa4b2; font:inherit; font-size:13px; font-weight:500; padding:12px 16px; cursor:pointer; border-bottom:2px solid transparent; }
|
|
1684
|
+
.deck-tab:hover { color:#e6e9ef; }
|
|
1685
|
+
.deck-tab.active { color:#c084fc; border-bottom-color:#c084fc; }
|
|
1686
|
+
.db[hidden] { display:none; }
|
|
1591
1687
|
</style></head>
|
|
1592
1688
|
<body>
|
|
1689
|
+
${tabBar}
|
|
1593
1690
|
${sections}
|
|
1691
|
+
${multi ? `<script>(function(){var b=[].slice.call(document.querySelectorAll('[data-tab-btn]')),s=[].slice.call(document.querySelectorAll('[data-tab]'));b.forEach(function(x){x.addEventListener('click',function(){var i=x.getAttribute('data-tab-btn');s.forEach(function(y){y.hidden=y.getAttribute('data-tab')!==i;});b.forEach(function(z){z.classList.toggle('active',z===x);});});});})();</script>` : ""}
|
|
1594
1692
|
${hasMd ? `<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
1595
1693
|
<script>document.querySelectorAll('.db-md').forEach(function(el){try{el.innerHTML=marked.parse(el.dataset.md||'')}catch(e){el.textContent=el.dataset.md||''}});</script>` : ""}
|
|
1596
|
-
${
|
|
1694
|
+
${hasUnrenderedMermaid ? `<script type="module">import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';mermaid.initialize({startOnLoad:true,theme:document.documentElement.classList.contains('light')?'default':'dark',securityLevel:'loose',fontFamily:"'DM Sans', system-ui, sans-serif"});</script>` : ""}
|
|
1597
1695
|
</body></html>`;
|
|
1598
1696
|
}
|
|
1599
1697
|
var pendingFeedback = /* @__PURE__ */ new Map();
|
|
1600
1698
|
var lastRenderError = null;
|
|
1699
|
+
var activeSession = null;
|
|
1700
|
+
var discussSessions = [];
|
|
1701
|
+
function broadcastDiscuss() {
|
|
1702
|
+
broadcastToClients({ type: "discuss_session", sessions: discussSessions.map((d) => d.id) });
|
|
1703
|
+
}
|
|
1601
1704
|
function consumeRenderError() {
|
|
1602
1705
|
const err = lastRenderError;
|
|
1603
1706
|
lastRenderError = null;
|
|
@@ -1837,45 +1940,35 @@ async function startDeckServer(opts = {}) {
|
|
|
1837
1940
|
return;
|
|
1838
1941
|
}
|
|
1839
1942
|
let course;
|
|
1943
|
+
let svgByIndex = {};
|
|
1944
|
+
let publicLink = false;
|
|
1840
1945
|
try {
|
|
1841
1946
|
const raw = await readBody(req);
|
|
1842
1947
|
if (raw.trim()) {
|
|
1843
|
-
|
|
1948
|
+
const parsed = JSON.parse(raw);
|
|
1949
|
+
course = parsed.course?.trim() || void 0;
|
|
1950
|
+
if (parsed.svgByIndex && typeof parsed.svgByIndex === "object") svgByIndex = parsed.svgByIndex;
|
|
1951
|
+
publicLink = parsed.publicLink === true;
|
|
1844
1952
|
}
|
|
1845
1953
|
} catch {
|
|
1846
1954
|
}
|
|
1847
|
-
|
|
1848
|
-
if (
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
jsonResponse(res, 400, {
|
|
1852
|
-
ok: false,
|
|
1853
|
-
error: `Course "${course}" is not a usable profile in .launch-secure.cred.config (missing pat/serverUrl/orgSlug/projectSlug?).`
|
|
1854
|
-
});
|
|
1855
|
-
return;
|
|
1856
|
-
}
|
|
1857
|
-
} else {
|
|
1858
|
-
try {
|
|
1859
|
-
loadMcpConfig(cwd);
|
|
1860
|
-
} catch (err) {
|
|
1861
|
-
jsonResponse(res, 400, {
|
|
1862
|
-
ok: false,
|
|
1863
|
-
error: `Cloud sharing needs an active LaunchSecure course (.launch-secure.cred.config) or a "launch-secure" entry in .mcp.json: ${String(err)}`
|
|
1864
|
-
});
|
|
1865
|
-
return;
|
|
1866
|
-
}
|
|
1955
|
+
const cfg = resolveShareConfig(cwd, course);
|
|
1956
|
+
if ("error" in cfg) {
|
|
1957
|
+
jsonResponse(res, 400, { ok: false, error: cfg.error });
|
|
1958
|
+
return;
|
|
1867
1959
|
}
|
|
1960
|
+
const { override } = cfg;
|
|
1868
1961
|
try {
|
|
1869
1962
|
const blocks = Array.isArray(persisted.blocks) ? persisted.blocks : [];
|
|
1870
|
-
const html =
|
|
1963
|
+
const { html, cloudBlocks } = buildSharePayload(cwd, id, blocks, svgByIndex);
|
|
1871
1964
|
const types = blocks.map((b) => b?.type).filter(Boolean);
|
|
1872
1965
|
const body = `Shared deck "${id}" \u2014 ${blocks.length} block${blocks.length === 1 ? "" : "s"}${types.length ? ` (${types.join(", ")})` : ""}. Open the Decks tab in the Communication Center to view.`;
|
|
1873
|
-
const input = { title: id, body, html, sessionId: id, blockCount: blocks.length };
|
|
1874
|
-
const
|
|
1966
|
+
const input = { title: id, body, html, blocks: cloudBlocks, sessionId: id, blockCount: blocks.length };
|
|
1967
|
+
const existingSync = readSyncRecord(cwd, id);
|
|
1875
1968
|
const doShare = async () => {
|
|
1876
|
-
if (
|
|
1969
|
+
if (existingSync?.resourceId) {
|
|
1877
1970
|
try {
|
|
1878
|
-
const { id: rid2 } = await updateDeck(
|
|
1971
|
+
const { id: rid2 } = await updateDeck(existingSync.resourceId, input);
|
|
1879
1972
|
return { resourceId: rid2, updated: true };
|
|
1880
1973
|
} catch {
|
|
1881
1974
|
const { id: rid2 } = await writeDeck(input);
|
|
@@ -1885,14 +1978,89 @@ async function startDeckServer(opts = {}) {
|
|
|
1885
1978
|
const { id: rid } = await writeDeck(input);
|
|
1886
1979
|
return { resourceId: rid, updated: false };
|
|
1887
1980
|
};
|
|
1888
|
-
const
|
|
1981
|
+
const result = await runWithShareConfig(override, async () => {
|
|
1982
|
+
const shared = await doShare();
|
|
1983
|
+
let link = null;
|
|
1984
|
+
let linkError;
|
|
1985
|
+
if (publicLink) {
|
|
1986
|
+
try {
|
|
1987
|
+
const minted = await createDeckShareLink(shared.resourceId);
|
|
1988
|
+
link = { shareUrl: minted.shareUrl, linkId: minted.linkId };
|
|
1989
|
+
} catch (err) {
|
|
1990
|
+
linkError = err instanceof Error ? err.message : String(err);
|
|
1991
|
+
}
|
|
1992
|
+
}
|
|
1993
|
+
return { shared, link, linkError };
|
|
1994
|
+
});
|
|
1995
|
+
if (publicLink && result.linkError && !result.shared.updated) {
|
|
1996
|
+
try {
|
|
1997
|
+
await runWithShareConfig(override, () => deleteDeck(result.shared.resourceId));
|
|
1998
|
+
} catch {
|
|
1999
|
+
}
|
|
2000
|
+
jsonResponse(res, 200, {
|
|
2001
|
+
ok: false,
|
|
2002
|
+
error: `Couldn't create the public link, so the deck wasn't shared: ${result.linkError}`
|
|
2003
|
+
});
|
|
2004
|
+
return;
|
|
2005
|
+
}
|
|
2006
|
+
const publicShareUrl = result.link?.shareUrl ?? existingSync?.publicShareUrl;
|
|
2007
|
+
const publicLinkId = result.link?.linkId ?? existingSync?.publicLinkId;
|
|
1889
2008
|
writeSyncRecord(cwd, id, {
|
|
1890
|
-
resourceId,
|
|
2009
|
+
resourceId: result.shared.resourceId,
|
|
1891
2010
|
sharedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1892
2011
|
version: persisted.version,
|
|
1893
|
-
course
|
|
2012
|
+
course,
|
|
2013
|
+
...publicShareUrl ? { publicShareUrl } : {},
|
|
2014
|
+
...publicLinkId ? { publicLinkId } : {}
|
|
2015
|
+
});
|
|
2016
|
+
jsonResponse(res, 200, {
|
|
2017
|
+
ok: true,
|
|
2018
|
+
resourceId: result.shared.resourceId,
|
|
2019
|
+
updated: result.shared.updated,
|
|
2020
|
+
course,
|
|
2021
|
+
shareUrl: publicShareUrl ?? null,
|
|
2022
|
+
...result.linkError ? { linkWarning: result.linkError } : {}
|
|
1894
2023
|
});
|
|
1895
|
-
|
|
2024
|
+
} catch (err) {
|
|
2025
|
+
jsonResponse(res, 500, { ok: false, error: String(err) });
|
|
2026
|
+
}
|
|
2027
|
+
return;
|
|
2028
|
+
}
|
|
2029
|
+
const publicLinkMatch = url2.pathname.match(/^\/api\/sessions\/([^/]+)\/public-link$/);
|
|
2030
|
+
if (publicLinkMatch && (req.method === "POST" || req.method === "DELETE")) {
|
|
2031
|
+
const id = decodeURIComponent(publicLinkMatch[1]);
|
|
2032
|
+
const sync = readSyncRecord(cwd, id);
|
|
2033
|
+
if (!sync?.resourceId) {
|
|
2034
|
+
jsonResponse(res, 409, { ok: false, error: "Share this deck to the cloud first, then create a public link." });
|
|
2035
|
+
return;
|
|
2036
|
+
}
|
|
2037
|
+
const cfg = resolveShareConfig(cwd, sync.course);
|
|
2038
|
+
if ("error" in cfg) {
|
|
2039
|
+
jsonResponse(res, 400, { ok: false, error: cfg.error });
|
|
2040
|
+
return;
|
|
2041
|
+
}
|
|
2042
|
+
const { override } = cfg;
|
|
2043
|
+
if (req.method === "DELETE") {
|
|
2044
|
+
if (!sync.publicLinkId) {
|
|
2045
|
+
if (sync.publicShareUrl) {
|
|
2046
|
+
writeSyncRecord(cwd, id, { ...sync, publicShareUrl: void 0, publicLinkId: void 0 });
|
|
2047
|
+
}
|
|
2048
|
+
jsonResponse(res, 200, { ok: true, revoked: false });
|
|
2049
|
+
return;
|
|
2050
|
+
}
|
|
2051
|
+
try {
|
|
2052
|
+
await runWithShareConfig(override, () => revokeDeckShareLink(sync.resourceId, sync.publicLinkId));
|
|
2053
|
+
writeSyncRecord(cwd, id, { ...sync, publicShareUrl: void 0, publicLinkId: void 0 });
|
|
2054
|
+
jsonResponse(res, 200, { ok: true, revoked: true });
|
|
2055
|
+
} catch (err) {
|
|
2056
|
+
jsonResponse(res, 500, { ok: false, error: String(err) });
|
|
2057
|
+
}
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2060
|
+
try {
|
|
2061
|
+
const minted = await runWithShareConfig(override, () => createDeckShareLink(sync.resourceId));
|
|
2062
|
+
writeSyncRecord(cwd, id, { ...sync, publicShareUrl: minted.shareUrl, publicLinkId: minted.linkId });
|
|
2063
|
+
jsonResponse(res, 200, { ok: true, shareUrl: minted.shareUrl });
|
|
1896
2064
|
} catch (err) {
|
|
1897
2065
|
jsonResponse(res, 500, { ok: false, error: String(err) });
|
|
1898
2066
|
}
|
|
@@ -1920,7 +2088,16 @@ async function startDeckServer(opts = {}) {
|
|
|
1920
2088
|
}
|
|
1921
2089
|
jsonResponse(res, 200, { ok: true, deleted: true });
|
|
1922
2090
|
} catch (err) {
|
|
1923
|
-
|
|
2091
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
2092
|
+
if (/not\s*found/i.test(msg)) {
|
|
2093
|
+
try {
|
|
2094
|
+
import_node_fs4.default.rmSync(syncJsonPath(cwd, id), { force: true });
|
|
2095
|
+
} catch {
|
|
2096
|
+
}
|
|
2097
|
+
jsonResponse(res, 200, { ok: true, deleted: false, reason: "already removed" });
|
|
2098
|
+
return;
|
|
2099
|
+
}
|
|
2100
|
+
jsonResponse(res, 500, { ok: false, error: msg });
|
|
1924
2101
|
}
|
|
1925
2102
|
return;
|
|
1926
2103
|
}
|
|
@@ -2011,6 +2188,10 @@ async function startDeckServer(opts = {}) {
|
|
|
2011
2188
|
deletePersistedSession(cwd, session);
|
|
2012
2189
|
} catch {
|
|
2013
2190
|
}
|
|
2191
|
+
const beforePins = discussSessions.length;
|
|
2192
|
+
discussSessions = session === "all" ? [] : discussSessions.filter((d) => d.id !== session);
|
|
2193
|
+
if (discussSessions.length !== beforePins) broadcastDiscuss();
|
|
2194
|
+
if (session === "all" || activeSession?.id === session) activeSession = null;
|
|
2014
2195
|
jsonResponse(res, 200, { ok: true });
|
|
2015
2196
|
return;
|
|
2016
2197
|
}
|
|
@@ -2019,6 +2200,16 @@ async function startDeckServer(opts = {}) {
|
|
|
2019
2200
|
jsonResponse(res, 200, err ?? null);
|
|
2020
2201
|
return;
|
|
2021
2202
|
}
|
|
2203
|
+
if (req.method === "GET" && url2.pathname === "/api/active-session") {
|
|
2204
|
+
jsonResponse(res, 200, {
|
|
2205
|
+
discuss: discussSessions.map((d) => d.id),
|
|
2206
|
+
// ordered = pin number
|
|
2207
|
+
active: activeSession?.id ?? null,
|
|
2208
|
+
focused: activeSession?.focused ?? false,
|
|
2209
|
+
activeAt: activeSession?.at ?? null
|
|
2210
|
+
});
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2022
2213
|
if (req.method === "GET" && url2.pathname.startsWith("/deck-files/")) {
|
|
2023
2214
|
const relative = decodeURIComponent(url2.pathname.slice("/deck-files/".length));
|
|
2024
2215
|
if (relative.includes("..") || relative.startsWith("/")) {
|
|
@@ -2091,6 +2282,24 @@ async function startDeckServer(opts = {}) {
|
|
|
2091
2282
|
source: msg.source ?? ""
|
|
2092
2283
|
};
|
|
2093
2284
|
}
|
|
2285
|
+
if (msg.type === "active_session" && msg.session) {
|
|
2286
|
+
activeSession = {
|
|
2287
|
+
id: msg.session,
|
|
2288
|
+
focused: msg.focused ?? false,
|
|
2289
|
+
at: Date.now()
|
|
2290
|
+
};
|
|
2291
|
+
}
|
|
2292
|
+
if (msg.type === "discuss_session" && msg.session) {
|
|
2293
|
+
const id = msg.session;
|
|
2294
|
+
const idx = discussSessions.findIndex((d) => d.id === id);
|
|
2295
|
+
if (idx >= 0) discussSessions.splice(idx, 1);
|
|
2296
|
+
else discussSessions.push({ id, at: Date.now() });
|
|
2297
|
+
broadcastDiscuss();
|
|
2298
|
+
}
|
|
2299
|
+
if (msg.type === "discuss_clear_all") {
|
|
2300
|
+
discussSessions = [];
|
|
2301
|
+
broadcastDiscuss();
|
|
2302
|
+
}
|
|
2094
2303
|
} catch {
|
|
2095
2304
|
}
|
|
2096
2305
|
});
|
|
@@ -2159,6 +2368,7 @@ function runServeCli(argv) {
|
|
|
2159
2368
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2160
2369
|
0 && (module.exports = {
|
|
2161
2370
|
broadcastToClients,
|
|
2371
|
+
buildSharePayload,
|
|
2162
2372
|
consumeRenderError,
|
|
2163
2373
|
createFeedbackWaiter,
|
|
2164
2374
|
resolveFeedback,
|