@matterfact/embed 0.11.3 → 0.13.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.
Files changed (44) hide show
  1. package/README.md +41 -0
  2. package/dist/chunk-C7DXO37G.js +2 -0
  3. package/dist/{chunk-CTZEDOH7.js → chunk-JLJG3MDX.js} +101 -51
  4. package/dist/chunk-JLJG3MDX.js.map +1 -0
  5. package/dist/chunk-QZCUQZJK.js +3 -0
  6. package/dist/chunk-QZCUQZJK.js.map +7 -0
  7. package/dist/{chunk-AANODHBV.js → chunk-S6OT47DL.js} +100 -49
  8. package/dist/chunk-S6OT47DL.js.map +1 -0
  9. package/dist/{chunk-JO3GWFMJ.js → chunk-UA4Y2O64.js} +2 -2
  10. package/dist/{context-FR7VFENN.js → context-DAXACLSP.js} +3 -3
  11. package/dist/{context-FR7VFENN.js.map → context-DAXACLSP.js.map} +1 -1
  12. package/dist/{context-IK5MECUW.js → context-IJK345PN.js} +4 -2
  13. package/dist/embed.js +1 -1
  14. package/dist/embed.js.map +3 -3
  15. package/dist/index.cjs +167 -53
  16. package/dist/index.cjs.map +1 -1
  17. package/dist/index.d.cts +83 -10
  18. package/dist/index.d.ts +83 -10
  19. package/dist/index.js +63 -6
  20. package/dist/index.js.map +1 -1
  21. package/dist/react.cjs +159 -52
  22. package/dist/react.cjs.map +1 -1
  23. package/dist/react.d.cts +29 -2
  24. package/dist/react.d.ts +29 -2
  25. package/dist/react.js +54 -5
  26. package/dist/react.js.map +1 -1
  27. package/dist/{snapshot-2V5SDSH2.js → snapshot-EVBNVVCT.js} +2 -2
  28. package/dist/{snapshot-Y5BF2UJR.js → snapshot-P5SVKSPB.js} +3 -3
  29. package/dist/{snapshot-Y5BF2UJR.js.map → snapshot-P5SVKSPB.js.map} +1 -1
  30. package/examples/embed-demo/.env.example +7 -3
  31. package/examples/embed-demo/README.md +25 -4
  32. package/examples/embed-demo/src/App.tsx +67 -7
  33. package/examples/embed-demo/src/styles.css +52 -0
  34. package/examples/embed-demo/vite.config.ts +3 -2
  35. package/package.json +1 -1
  36. package/dist/chunk-AANODHBV.js.map +0 -1
  37. package/dist/chunk-CTZEDOH7.js.map +0 -1
  38. package/dist/chunk-SKJFF7RD.js +0 -2
  39. package/dist/chunk-Y7I25VHL.js +0 -3
  40. package/dist/chunk-Y7I25VHL.js.map +0 -7
  41. /package/dist/{chunk-SKJFF7RD.js.map → chunk-C7DXO37G.js.map} +0 -0
  42. /package/dist/{chunk-JO3GWFMJ.js.map → chunk-UA4Y2O64.js.map} +0 -0
  43. /package/dist/{context-IK5MECUW.js.map → context-IJK345PN.js.map} +0 -0
  44. /package/dist/{snapshot-2V5SDSH2.js.map → snapshot-EVBNVVCT.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -12,6 +12,36 @@ var __export = (target, all) => {
12
12
  __defProp(target, name, { get: all[name], enumerable: true });
13
13
  };
14
14
 
15
+ // src/pageContextMode.ts
16
+ function parsePageContextMode(value) {
17
+ if (typeof value === "boolean") return value ? "full" : "off";
18
+ if (value == null) return "full";
19
+ const s = String(value).trim().toLowerCase();
20
+ if (s === "full" || s === "declared" || s === "off") return s;
21
+ if (s === "true" || s === "on" || s === "1" || s === "yes") return "full";
22
+ if (s === "false" || s === "0" || s === "no") return "off";
23
+ return "full";
24
+ }
25
+ function clampPageContextMode(adminMax, hostRequest) {
26
+ return RANK[adminMax] <= RANK[hostRequest] ? adminMax : hostRequest;
27
+ }
28
+ function allowsDeclared(mode) {
29
+ return mode !== "off";
30
+ }
31
+ function allowsAuto(mode) {
32
+ return mode === "full";
33
+ }
34
+ var RANK;
35
+ var init_pageContextMode = __esm({
36
+ "src/pageContextMode.ts"() {
37
+ RANK = {
38
+ off: 0,
39
+ declared: 1,
40
+ full: 2
41
+ };
42
+ }
43
+ });
44
+
15
45
  // src/redact.ts
16
46
  function redact(text) {
17
47
  let out = text;
@@ -436,7 +466,7 @@ function mfTools(win) {
436
466
  if (typeof document2 === "function") {
437
467
  out.push({
438
468
  name: "mf.resolveDocument",
439
- description: 'Resolve a matterfact document (dossier, report, briefing) that is NOT on the current page to a concrete id. Call it with the document type and a key (e.g. a ticker) when the site map shows the document lives on another route. Returns {id, label, href}. Cite the result inline as <MFRef kind="document" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the document in the side panel; href is its "open on page" deeplink.',
469
+ description: `Resolve a matterfact document (dossier, report, briefing) that is NOT on the current page to a concrete id. Call it with the document type and the subject's key, when the site map shows the document lives on another route. For the key, use the current route parameter VERBATIM when the page context states one (e.g. ticker = BP~+LN+Equity \u2192 key "BP~+LN+Equity"): it is the host's own identifier and often differs from the ticker or name a person would write. Do not normalise, decode or re-spell it, and do not substitute what the user typed. Returns {id, label, href}. Cite the result inline as <MFRef kind="document" id="<id>" label="<label>" /> \u2014 it renders as a chip that opens the document in the side panel; href is its "open on page" deeplink.`,
440
470
  inputSchema: {
441
471
  type: "object",
442
472
  properties: {
@@ -1043,9 +1073,16 @@ __export(context_exports, {
1043
1073
  redact: () => redact,
1044
1074
  sendRegion: () => sendRegion,
1045
1075
  sendSnapshot: () => sendSnapshot,
1076
+ setPageContextMax: () => setPageContextMax,
1046
1077
  start: () => start,
1047
1078
  stop: () => stop
1048
1079
  });
1080
+ function effectivePageContextMode() {
1081
+ return clampPageContextMode(adminPageContextMax, hostPageContextMode);
1082
+ }
1083
+ function pageContextOn() {
1084
+ return allowsDeclared(effectivePageContextMode());
1085
+ }
1049
1086
  function isPrivate(el) {
1050
1087
  if (el.closest("[data-mf-private]")) return true;
1051
1088
  const tag = el.tagName;
@@ -1132,8 +1169,9 @@ async function resolveDeclaredContext() {
1132
1169
  return mf?.context ?? {};
1133
1170
  }
1134
1171
  async function publishContext() {
1135
- if (!pageContextOn) return;
1172
+ if (!pageContextOn()) return;
1136
1173
  const declared = await resolveDeclaredContext();
1174
+ if (!pageContextOn()) return;
1137
1175
  send?.({ type: "host.context", context: buildPageContext(declared) });
1138
1176
  }
1139
1177
  function provideContext() {
@@ -1194,7 +1232,7 @@ function readSitemap2() {
1194
1232
  return Array.isArray(s) ? s : [];
1195
1233
  }
1196
1234
  function publishSitemap() {
1197
- if (!pageContextOn) return;
1235
+ if (!pageContextOn()) return;
1198
1236
  const sitemap = readSitemap2();
1199
1237
  if (sitemap.length) send?.({ type: "host.sitemap", sitemap });
1200
1238
  }
@@ -1211,6 +1249,7 @@ function navigateHost(href) {
1211
1249
  location.assign(target.href);
1212
1250
  }
1213
1251
  function pushActivity(e) {
1252
+ if (!allowsAuto(effectivePageContextMode())) return;
1214
1253
  activity.push({ ...e, seq: ++activitySeq, ts: Date.now() });
1215
1254
  if (activity.length > MAX_ACTIVITY) activity.shift();
1216
1255
  send?.({ type: "host.activity", events: [activity[activity.length - 1]] });
@@ -1275,6 +1314,7 @@ function watchNavigation() {
1275
1314
  publishSitemap();
1276
1315
  publishTools();
1277
1316
  };
1317
+ if (navFire) return;
1278
1318
  navFire = fire;
1279
1319
  for (const name of ["pushState", "replaceState"]) {
1280
1320
  const orig = history[name];
@@ -1328,7 +1368,7 @@ function scheduleFocus() {
1328
1368
  }, 250);
1329
1369
  }
1330
1370
  async function sendSnapshot(emit) {
1331
- if (!pageContextOn) return;
1371
+ if (!allowsAuto(effectivePageContextMode())) return;
1332
1372
  const hoist = detectHoist(() => readHoistRuntime());
1333
1373
  if (hoist) {
1334
1374
  const { rows } = readHoistConfig(
@@ -1344,6 +1384,7 @@ async function sendSnapshot(emit) {
1344
1384
  return;
1345
1385
  }
1346
1386
  const { snapshot: snapshot2 } = await loadSnapshotModule();
1387
+ if (!allowsAuto(effectivePageContextMode())) return;
1347
1388
  const { yaml, truncated, visibleRefs } = snapshot2();
1348
1389
  emit({
1349
1390
  type: "host.snapshot",
@@ -1353,8 +1394,9 @@ async function sendSnapshot(emit) {
1353
1394
  emit({ type: "host.focus", focus: lastFocus });
1354
1395
  }
1355
1396
  async function sendRegion(ref, emit) {
1356
- if (!pageContextOn) return;
1397
+ if (!allowsAuto(effectivePageContextMode())) return;
1357
1398
  const { snapshotRegion: snapshotRegion2, resolveRef: resolveRef2 } = await loadSnapshotModule();
1399
+ if (!allowsAuto(effectivePageContextMode())) return;
1358
1400
  const el = resolveRef2(ref);
1359
1401
  if (!el) {
1360
1402
  emit({
@@ -1368,15 +1410,6 @@ async function sendRegion(ref, emit) {
1368
1410
  emit({ type: "host.region", ref, yaml: redact(yaml) });
1369
1411
  }
1370
1412
  async function callTool(call, emit) {
1371
- if (!pageContextOn) {
1372
- emit({
1373
- type: "host.toolResult",
1374
- callId: call.callId,
1375
- ok: false,
1376
- error: "host tools are disabled"
1377
- });
1378
- return;
1379
- }
1380
1413
  const win = typeof window !== "undefined" ? window : void 0;
1381
1414
  const cls = toolClass(call.name);
1382
1415
  const startedAt = Date.now();
@@ -1403,13 +1436,39 @@ async function callTool(call, emit) {
1403
1436
  error: r.error
1404
1437
  });
1405
1438
  }
1406
- function stop() {
1439
+ function installAutoObservers() {
1440
+ document.addEventListener("click", onClick, { capture: true, passive: true });
1441
+ document.addEventListener("submit", onSubmit, {
1442
+ capture: true,
1443
+ passive: true
1444
+ });
1445
+ document.addEventListener("change", onChange, {
1446
+ capture: true,
1447
+ passive: true
1448
+ });
1449
+ document.addEventListener("selectionchange", scheduleFocus, { passive: true });
1450
+ document.addEventListener("focusin", scheduleFocus, {
1451
+ capture: true,
1452
+ passive: true
1453
+ });
1454
+ window.addEventListener("scroll", scheduleFocus, {
1455
+ capture: true,
1456
+ passive: true
1457
+ });
1458
+ }
1459
+ function removeAutoObservers() {
1407
1460
  document.removeEventListener("click", onClick, { capture: true });
1408
1461
  document.removeEventListener("submit", onSubmit, { capture: true });
1409
1462
  document.removeEventListener("change", onChange, { capture: true });
1410
1463
  document.removeEventListener("selectionchange", scheduleFocus);
1411
1464
  document.removeEventListener("focusin", scheduleFocus, { capture: true });
1412
1465
  window.removeEventListener("scroll", scheduleFocus, { capture: true });
1466
+ if (focusTimer) {
1467
+ clearTimeout(focusTimer);
1468
+ focusTimer = null;
1469
+ }
1470
+ }
1471
+ function unwatchNavigation() {
1413
1472
  if (navFire) {
1414
1473
  window.removeEventListener("popstate", navFire);
1415
1474
  navFire = null;
@@ -1422,10 +1481,10 @@ function stop() {
1422
1481
  history.replaceState = origReplaceState;
1423
1482
  origReplaceState = null;
1424
1483
  }
1425
- if (focusTimer) {
1426
- clearTimeout(focusTimer);
1427
- focusTimer = null;
1428
- }
1484
+ }
1485
+ function stop() {
1486
+ removeAutoObservers();
1487
+ unwatchNavigation();
1429
1488
  send = null;
1430
1489
  contextProvider = void 0;
1431
1490
  activity.length = 0;
@@ -1433,45 +1492,41 @@ function stop() {
1433
1492
  widgetOrigin = "";
1434
1493
  snapshotModule?.clearRefs();
1435
1494
  }
1495
+ function setPageContextMax(max) {
1496
+ adminPageContextMax = parsePageContextMode(max);
1497
+ applyObservation();
1498
+ }
1499
+ function applyObservation() {
1500
+ const mode = effectivePageContextMode();
1501
+ if (allowsAuto(mode)) {
1502
+ installAutoObservers();
1503
+ } else {
1504
+ removeAutoObservers();
1505
+ activity.length = 0;
1506
+ lastFocus = {};
1507
+ }
1508
+ if (allowsDeclared(mode)) {
1509
+ watchNavigation();
1510
+ } else {
1511
+ unwatchNavigation();
1512
+ }
1513
+ }
1436
1514
  function start(emit, origin, pageContext = true, provider) {
1437
1515
  stop();
1438
1516
  send = emit;
1439
1517
  widgetOrigin = origin || "";
1440
1518
  lastUrl = location.pathname;
1441
- pageContextOn = pageContext;
1519
+ hostPageContextMode = parsePageContextMode(pageContext);
1442
1520
  contextProvider = provider;
1443
- if (pageContextOn) {
1521
+ if (allowsDeclared(effectivePageContextMode())) {
1444
1522
  void publishContext();
1445
1523
  publishArtifactGrants();
1446
1524
  publishSitemap();
1447
- watchNavigation();
1448
- document.addEventListener("click", onClick, {
1449
- capture: true,
1450
- passive: true
1451
- });
1452
- document.addEventListener("submit", onSubmit, {
1453
- capture: true,
1454
- passive: true
1455
- });
1456
- document.addEventListener("change", onChange, {
1457
- capture: true,
1458
- passive: true
1459
- });
1460
- document.addEventListener("selectionchange", scheduleFocus, {
1461
- passive: true
1462
- });
1463
- document.addEventListener("focusin", scheduleFocus, {
1464
- capture: true,
1465
- passive: true
1466
- });
1467
- window.addEventListener("scroll", scheduleFocus, {
1468
- capture: true,
1469
- passive: true
1470
- });
1471
1525
  }
1526
+ applyObservation();
1472
1527
  const theme = matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
1473
1528
  emit({ type: "host.theme", mode: theme });
1474
- if (pageContextOn) {
1529
+ {
1475
1530
  const w = typeof window !== "undefined" ? window : void 0;
1476
1531
  const tools = advertise(w);
1477
1532
  if (tools.length) {
@@ -1498,9 +1553,10 @@ function publishTools() {
1498
1553
  });
1499
1554
  }
1500
1555
  }
1501
- var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, pageContextOn, contextProvider, navFire, origPushState, origReplaceState, snapshotModule, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
1556
+ var widgetOrigin, MAX_ACTIVITY, send, activitySeq, activity, lastUrl, hostPageContextMode, adminPageContextMax, contextProvider, navFire, origPushState, origReplaceState, snapshotModule, FORM_CONTROLS, ACTIONABLE, lastFocus, focusTimer, snapshotSeq;
1502
1557
  var init_context = __esm({
1503
1558
  "src/context.ts"() {
1559
+ init_pageContextMode();
1504
1560
  init_redact();
1505
1561
  init_hoist();
1506
1562
  init_hoist_runtime();
@@ -1512,7 +1568,8 @@ var init_context = __esm({
1512
1568
  activitySeq = 0;
1513
1569
  activity = [];
1514
1570
  lastUrl = "";
1515
- pageContextOn = true;
1571
+ hostPageContextMode = "full";
1572
+ adminPageContextMax = "full";
1516
1573
  navFire = null;
1517
1574
  origPushState = null;
1518
1575
  origReplaceState = null;
@@ -1751,11 +1808,19 @@ function dockBox(g, vw, _vh) {
1751
1808
  var DEFAULT_ORIGIN = "https://app.matterfact.com";
1752
1809
  function devRequested() {
1753
1810
  try {
1754
- return new URLSearchParams(location.search).get("mfdev") === "1";
1811
+ if (new URLSearchParams(location.search).get("mfdev") === "1") return true;
1812
+ try {
1813
+ return localStorage.getItem("mfdev") === "1";
1814
+ } catch {
1815
+ return false;
1816
+ }
1755
1817
  } catch {
1756
1818
  return false;
1757
1819
  }
1758
1820
  }
1821
+ function alog(m, x) {
1822
+ if (devRequested()) console.info("[embed auth] host: " + m, x ?? "");
1823
+ }
1759
1824
  function readConfig() {
1760
1825
  const el = document.currentScript ?? document.querySelector('script[data-key][src*="embed"]');
1761
1826
  const publishableKey = el?.dataset.key;
@@ -1771,13 +1836,21 @@ function readConfig() {
1771
1836
  );
1772
1837
  }
1773
1838
  const pageContextAttr = el?.dataset.pageContext?.toLowerCase();
1839
+ let pageContext = true;
1840
+ if (pageContextAttr === "off" || pageContextAttr === "false" || pageContextAttr === "0") {
1841
+ pageContext = false;
1842
+ } else if (pageContextAttr === "declared") {
1843
+ pageContext = "declared";
1844
+ } else if (pageContextAttr === "full" || pageContextAttr === "on" || pageContextAttr === "true" || pageContextAttr === "1") {
1845
+ pageContext = true;
1846
+ }
1774
1847
  return {
1775
1848
  publishableKey,
1776
1849
  origin: el?.dataset.origin || DEFAULT_ORIGIN,
1777
1850
  theme: el?.dataset.theme || "auto",
1778
1851
  surface: el?.dataset.surface || "",
1779
1852
  container,
1780
- pageContext: pageContextAttr !== "off" && pageContextAttr !== "false" && pageContextAttr !== "0"
1853
+ pageContext
1781
1854
  // No `data-dev` — see the `dev` field's doc comment: the URL trigger is the
1782
1855
  // point for the script-tag path, so there is deliberately no script-tag knob here.
1783
1856
  // No `data-actions` either: the action policy lives entirely in the lazy chunk,
@@ -2047,6 +2120,11 @@ var EmbedHost = class {
2047
2120
  case "widget.requestContext":
2048
2121
  void this.loadContext().then((m) => m.provideContext());
2049
2122
  break;
2123
+ case "widget.pageContextMax":
2124
+ void this.loadContext().then((m) => {
2125
+ m.setPageContextMax(msg.mode);
2126
+ });
2127
+ break;
2050
2128
  case "widget.requestSnapshot":
2051
2129
  void this.loadContext().then((m) => m.sendSnapshot(this.send));
2052
2130
  break;
@@ -2151,6 +2229,13 @@ var EmbedHost = class {
2151
2229
  case "widget.chat":
2152
2230
  this.emit({ type: "chat", phase: msg.phase, chatId: msg.chatId });
2153
2231
  break;
2232
+ case "widget.artifactParams":
2233
+ this.emit({
2234
+ type: "artifactParams",
2235
+ params: msg.params,
2236
+ source: msg.source
2237
+ });
2238
+ break;
2154
2239
  }
2155
2240
  }
2156
2241
  /**
@@ -2258,14 +2343,22 @@ var EmbedHost = class {
2258
2343
  */
2259
2344
  async provideAuth() {
2260
2345
  const provider = this.config.authTokenProvider ?? globalThis.matterfact?.getEmbedAuthToken;
2261
- if (!provider) return;
2262
- if (++this.ac > 5) return;
2346
+ if (!provider) {
2347
+ alog("no provider");
2348
+ return;
2349
+ }
2350
+ if (++this.ac > 5) {
2351
+ alog("storm cap");
2352
+ return;
2353
+ }
2263
2354
  try {
2264
2355
  const token = await provider();
2265
2356
  if (token) this.send({ type: "host.auth", token, expiresAt: 0 });
2266
2357
  this.emit({ type: "auth", phase: token ? "granted" : "failed" });
2267
- } catch {
2358
+ alog(token ? "token" : "empty");
2359
+ } catch (e) {
2268
2360
  this.emit({ type: "auth", phase: "failed" });
2361
+ alog("threw", e);
2269
2362
  }
2270
2363
  }
2271
2364
  loadContext() {
@@ -2288,6 +2381,23 @@ var EmbedHost = class {
2288
2381
  this.queue = [];
2289
2382
  for (const m of pending) this.send(m);
2290
2383
  }
2384
+ /**
2385
+ * Push artifact param values into the widget — a PARTIAL patch; only the named
2386
+ * params change. Buffered like every other host→widget message until the widget
2387
+ * says it's listening.
2388
+ *
2389
+ * Explicit and deliberate, so it applies even to a param whose page-context binding
2390
+ * the viewer has overridden (spec §7.3).
2391
+ */
2392
+ setArtifactParams(params) {
2393
+ if (params == null || typeof params !== "object" || Array.isArray(params)) {
2394
+ console.error(
2395
+ "[matterfact] setArtifactParams expects an object of param values"
2396
+ );
2397
+ return;
2398
+ }
2399
+ this.send({ type: "host.artifactParams", params });
2400
+ }
2291
2401
  /** Tear down: stop listening and remove the host element. For the React wrapper's
2292
2402
  * unmount — the vanilla `<script>` loader lives for the page's lifetime and never
2293
2403
  * calls this. */
@@ -2305,6 +2415,10 @@ var EmbedHost = class {
2305
2415
  function mount(config) {
2306
2416
  const host = new EmbedHost(config);
2307
2417
  host.mount();
2418
+ window.matterfact = {
2419
+ ...window.matterfact ?? {},
2420
+ setArtifactParams: (params) => host.setArtifactParams(params)
2421
+ };
2308
2422
  return host;
2309
2423
  }
2310
2424