@panyam/tsappkit 0.6.1 → 0.6.2

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/index.mjs CHANGED
@@ -1429,16 +1429,20 @@ function lazy(load) {
1429
1429
  }
1430
1430
  function mountIslands(spec, registry, findSlot, context, bus, log, options = {}) {
1431
1431
  const out = [];
1432
+ const skip = (island, reason, message2) => {
1433
+ log(message2);
1434
+ options.onSkip?.(island, reason);
1435
+ };
1432
1436
  let ctx;
1433
1437
  for (const island of spec.islands) {
1434
1438
  const entry = Object.prototype.hasOwnProperty.call(registry, island.name) ? registry[island.name] : void 0;
1435
1439
  if (!entry) {
1436
- log(`page spec: no island called "${island.name}" in this page's registry`);
1440
+ skip(island, "not in the registry", `page spec: no island called "${island.name}" in this page's registry`);
1437
1441
  continue;
1438
1442
  }
1439
1443
  const el = findSlot(island.slot);
1440
1444
  if (el === null) {
1441
- log(`page spec: island "${island.name}" wants slot "${island.slot}", which isn't on the page`);
1445
+ skip(island, "no slot", `page spec: island "${island.name}" wants slot "${island.slot}", which isn't on the page`);
1442
1446
  continue;
1443
1447
  }
1444
1448
  const build = (factory) => {
@@ -1446,14 +1450,16 @@ function mountIslands(spec, registry, findSlot, context, bus, log, options = {})
1446
1450
  ctx ?? (ctx = context());
1447
1451
  return factory(el, island, ctx, bus);
1448
1452
  } catch (err) {
1449
- log(`page spec: island "${island.name}" failed to mount: ${message(err)}`);
1453
+ skip(island, "factory threw", `page spec: island "${island.name}" failed to mount: ${message(err)}`);
1450
1454
  return void 0;
1451
1455
  }
1452
1456
  };
1453
1457
  const mountLate = () => {
1454
1458
  const late = (factory) => {
1455
1459
  const c2 = build(factory);
1456
- if (c2 !== void 0) options.onLateMount?.(c2, island);
1460
+ if (c2 === void 0) return;
1461
+ options.onMount?.(c2, island, el);
1462
+ options.onLateMount?.(c2, island);
1457
1463
  };
1458
1464
  if (typeof entry === "function") {
1459
1465
  late(entry);
@@ -1463,9 +1469,9 @@ function mountIslands(spec, registry, findSlot, context, bus, log, options = {})
1463
1469
  (m) => {
1464
1470
  const factory = typeof m === "function" ? m : m?.default;
1465
1471
  if (typeof factory === "function") late(factory);
1466
- else log(`page spec: island "${island.name}" loaded, but its module has no factory (a default export or the function itself)`);
1472
+ else skip(island, "no factory", `page spec: island "${island.name}" loaded, but its module has no factory (a default export or the function itself)`);
1467
1473
  },
1468
- (err) => log(`page spec: island "${island.name}" failed to load: ${message(err)}`)
1474
+ (err) => skip(island, "failed to load", `page spec: island "${island.name}" failed to load: ${message(err)}`)
1469
1475
  );
1470
1476
  };
1471
1477
  const strategy = parseLoad(island.load);
@@ -1481,7 +1487,10 @@ function mountIslands(spec, registry, findSlot, context, bus, log, options = {})
1481
1487
  continue;
1482
1488
  }
1483
1489
  const c = build(entry);
1484
- if (c !== void 0) out.push(c);
1490
+ if (c !== void 0) {
1491
+ out.push(c);
1492
+ options.onMount?.(c, island, el);
1493
+ }
1485
1494
  }
1486
1495
  return out;
1487
1496
  }
@@ -1489,6 +1498,48 @@ function message(err) {
1489
1498
  return err instanceof Error ? err.message : String(err);
1490
1499
  }
1491
1500
 
1501
+ // src/page/overlay.ts
1502
+ var STYLE_ID = "island-overlay-style";
1503
+ var CSS = `
1504
+ [data-island-debug] { position: relative; outline: 2px dashed #1565c0; outline-offset: -2px; }
1505
+ [data-island-debug]::after {
1506
+ content: attr(data-island-debug); position: absolute; top: 0; right: 0; z-index: 2147483647;
1507
+ font: 11px/1.4 ui-monospace, monospace; padding: 1px 6px; background: #1565c0; color: #fff; pointer-events: none;
1508
+ }
1509
+ [data-island-state="mounted"] { outline-color: #2e7d32; }
1510
+ [data-island-state="mounted"]::after { background: #2e7d32; }
1511
+ [data-island-state="failed"] { outline-color: #c62828; }
1512
+ [data-island-state="failed"]::after { background: #c62828; }
1513
+ `;
1514
+ var IslandOverlay = class {
1515
+ constructor(doc = document, now = () => performance.now()) {
1516
+ this.doc = doc;
1517
+ this.now = now;
1518
+ if (!doc.getElementById(STYLE_ID)) {
1519
+ const style = doc.createElement("style");
1520
+ style.id = STYLE_ID;
1521
+ style.textContent = CSS;
1522
+ doc.head.appendChild(style);
1523
+ }
1524
+ }
1525
+ /** The island has a slot and hasn't mounted yet. */
1526
+ waiting(island, el) {
1527
+ this.label(island, el, "waiting", "waiting");
1528
+ }
1529
+ /** The island has just mounted. */
1530
+ mounted(island, el) {
1531
+ this.label(island, el, "mounted", `mounted ${Math.round(this.now())} ms`);
1532
+ }
1533
+ /** The island won't mount, for `reason`. */
1534
+ failed(island, el, reason) {
1535
+ this.label(island, el, "failed", reason);
1536
+ }
1537
+ label(island, el, state, text) {
1538
+ el.dataset.islandState = state;
1539
+ el.dataset.islandDebug = [island.name, island.slot, island.load || "eager", text].join(" \xB7 ");
1540
+ }
1541
+ };
1542
+
1492
1543
  // src/page/spec.ts
1493
1544
  var SPEC_ELEMENT_ID = "page-spec";
1494
1545
  var SLOT = /^[a-z][a-z0-9-]*$/;
@@ -1541,10 +1592,18 @@ var IslandPage = class extends BasePage {
1541
1592
  console.warn(`page spec: no readable #${SPEC_ELEMENT_ID} on this page, so nothing is mounted`);
1542
1593
  return [];
1543
1594
  }
1595
+ const findSlot = (slot) => document.querySelector(`[data-slot="${slot}"]`);
1596
+ const overlay = this.showIslandOverlay() ? new IslandOverlay() : void 0;
1597
+ if (overlay) {
1598
+ for (const island of spec.islands) {
1599
+ const el = findSlot(island.slot);
1600
+ if (el) overlay.waiting(island, el);
1601
+ }
1602
+ }
1544
1603
  return mountIslands(
1545
1604
  spec,
1546
1605
  this.registry(),
1547
- (slot) => document.querySelector(`[data-slot="${slot}"]`),
1606
+ findSlot,
1548
1607
  () => this.makeContext(spec),
1549
1608
  this.eventBus,
1550
1609
  (message2) => console.warn(message2),
@@ -1552,10 +1611,23 @@ var IslandPage = class extends BasePage {
1552
1611
  defer: (island, el, mount) => this.scheduleLoad(parseLoad(island.load) ?? { kind: "eager" }, el, mount),
1553
1612
  onLateMount: (component, island) => {
1554
1613
  new LifecycleController(this.eventBus, LifecycleController.DefaultConfig).initializeFromRoot(component).catch((err) => console.warn(`page spec: island "${island.name}" failed to start: ${err instanceof Error ? err.message : String(err)}`));
1555
- }
1614
+ },
1615
+ onMount: overlay && ((_c, island, el) => overlay.mounted(island, el)),
1616
+ onSkip: overlay && ((island, reason) => {
1617
+ const el = findSlot(island.slot);
1618
+ if (el) overlay.failed(island, el, reason);
1619
+ })
1556
1620
  }
1557
1621
  );
1558
1622
  }
1623
+ /**
1624
+ * Whether to draw the island overlay. The default is true when the URL has
1625
+ * an `islands` query parameter (`/game?islands`); a subclass can tie it to
1626
+ * its own debug setting instead.
1627
+ */
1628
+ showIslandOverlay() {
1629
+ return new URLSearchParams(window.location.search).has("islands");
1630
+ }
1559
1631
  /**
1560
1632
  * Waits for `strategy` and then calls `mount`. Defaults to scheduleMount
1561
1633
  * against the window; a subclass or test can replace how the waiting is done.
@@ -1907,6 +1979,6 @@ var KeyboardShortcutManager = class {
1907
1979
  }
1908
1980
  };
1909
1981
 
1910
- export { BaseComponent, BasePage, ComponentEventTypes, EventBus, IslandPage, KeyboardShortcutManager, KeyboardState, LifecycleController, LifecycleEventTypes, MobileBottomDrawer, Modal, SPEC_ELEMENT_ID, SplashScreen, TemplateLoader, ThemeManager, ToastManager, hasModifierKeys, isInInputContext, lazy, mountIslands, parseLoad, readSpec, scheduleMount, shouldIgnoreShortcut };
1982
+ export { BaseComponent, BasePage, ComponentEventTypes, EventBus, IslandOverlay, IslandPage, KeyboardShortcutManager, KeyboardState, LifecycleController, LifecycleEventTypes, MobileBottomDrawer, Modal, SPEC_ELEMENT_ID, SplashScreen, TemplateLoader, ThemeManager, ToastManager, hasModifierKeys, isInInputContext, lazy, mountIslands, parseLoad, readSpec, scheduleMount, shouldIgnoreShortcut };
1911
1983
  //# sourceMappingURL=index.mjs.map
1912
1984
  //# sourceMappingURL=index.mjs.map