@lotics/cli 0.246.0 → 0.247.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.
@@ -421,6 +421,15 @@ var __loticsProbe = (() => {
421
421
  };
422
422
  }
423
423
  var SINGLE_LINE_FIELDS = /* @__PURE__ */ new Set(["text", "search", "tel", "url", "email", "number"]);
424
+ function clippingAncestor(el, stop) {
425
+ for (let at = el.parentElement; at !== null; at = at.parentElement) {
426
+ const style = getComputedStyle(at);
427
+ const x = style.overflowX || style.overflow;
428
+ if (x === "hidden" || x === "clip") return at;
429
+ if (at === stop) return null;
430
+ }
431
+ return null;
432
+ }
424
433
  function truncatedLeaves(root = document.body) {
425
434
  const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
426
435
  const clippedAway2 = /^inset\(\s*50%/;
@@ -438,7 +447,16 @@ var __loticsProbe = (() => {
438
447
  if (box.width <= 1 && box.height <= 1) continue;
439
448
  if (clippedAway2.test(style.getPropertyValue("clip-path"))) continue;
440
449
  total += 1;
441
- if (style.overflow === "visible") continue;
450
+ if (style.overflow === "visible") {
451
+ const within2 = el.closest('.lotics-fact-groups, [role="listbox"]');
452
+ const clip = within2 === null ? null : clippingAncestor(el, within2);
453
+ if (clip !== null) {
454
+ const edge = clip.getBoundingClientRect();
455
+ const shown = Math.min(box.right, edge.right) - Math.max(box.left, edge.left);
456
+ if (box.right > edge.right + 1 || box.left < edge.left - 1) cut.push({ text: text.slice(0, 40), axis: "width", has: Math.max(0, Math.round(shown)), needs: Math.round(box.width) });
457
+ }
458
+ continue;
459
+ }
442
460
  const states = (property) => {
443
461
  const value = style.getPropertyValue(property);
444
462
  return value !== "" && value !== "none";
@@ -459,7 +477,7 @@ var __loticsProbe = (() => {
459
477
  const walk = (node) => {
460
478
  if (node.nodeType === Node.TEXT_NODE) {
461
479
  const parent = node.parentElement;
462
- if (!parent || skipTag.test(parent.tagName) || parent.closest("[data-content]") !== null) return;
480
+ if (!parent || skipTag.test(parent.tagName) || parent.tagName === "TEXTAREA" || parent.closest('[data-content], [contenteditable="true"]') !== null) return;
463
481
  const text = node.textContent || "";
464
482
  if (!text.trim()) return;
465
483
  total += 1;
@@ -694,8 +712,9 @@ var __loticsProbe = (() => {
694
712
  }
695
713
 
696
714
  // src/probe_collectors/anatomy.ts
697
- var BODY = ".lotics-record-page__body";
698
- var BRIEF = ".lotics-record-page__brief";
715
+ var FRAME = ".lotics-record-frame";
716
+ var HEADER = ".lotics-record-header";
717
+ var BLOCK = ".lotics-record-frame__block";
699
718
  var PRIMARY = '.lotics-button[data-variant="primary"]';
700
719
  var FIRST_VIEWPORTS = [
701
720
  { width: 1280, height: 800 },
@@ -705,11 +724,10 @@ var __loticsProbe = (() => {
705
724
  return FIRST_VIEWPORTS.reduce((near, one) => Math.abs(one.width - width) < Math.abs(near.width - width) ? one : near);
706
725
  }
707
726
  function surfaces(root) {
708
- return within(root, BODY).filter(onScreen).flatMap((body) => {
709
- const head = body.closest(".lotics-drawer__popup, .lotics-record-page");
710
- if (head === null) return [];
711
- const title = within(head, "h1, h2").find((one) => !body.contains(one) && onScreen(one));
712
- return [{ body, root: head, name: words2(title) || nameOf(head) || "(unnamed record)" }];
727
+ return within(root, FRAME).filter(onScreen).map((frame) => {
728
+ const head = frame.closest(".lotics-drawer__popup") ?? frame;
729
+ const title = within(head, `${HEADER}__title`).find(onScreen);
730
+ return { frame, root: head, name: words2(title) || nameOf(head) || "(unnamed record)" };
713
731
  });
714
732
  }
715
733
  function words2(el) {
@@ -720,77 +738,51 @@ var __loticsProbe = (() => {
720
738
  function headingPrimaries(root = document.body) {
721
739
  const drawn = surfaces(root);
722
740
  const loud = drawn.flatMap(
723
- (surface) => within(surface.body, PRIMARY).filter((act) => onScreen(act) && act.closest(".lotics-section-heading") !== null).map((act) => ({ record: surface.name, act: nameOf(act) || words2(act) }))
741
+ (surface) => within(surface.frame, PRIMARY).filter(
742
+ (act) => onScreen(act) && act.closest(BLOCK) !== null && (act.closest(`${FRAME}__action`) !== null || act.closest(".lotics-section-heading") !== null)
743
+ ).map((act) => ({ record: surface.name, act: nameOf(act) || words2(act) }))
724
744
  );
725
745
  return { total: drawn.length, loud };
726
746
  }
727
- function briefsBelowFold(root = document.body, width = window.innerWidth) {
747
+ function actsBelowFold(root = document.body, width = window.innerWidth) {
728
748
  const fold = firstViewport(width).height;
729
- const briefs = surfaces(root).flatMap((surface) => {
730
- const brief = within(surface.body, BRIEF).find(onScreen);
731
- return brief === void 0 ? [] : [{ record: surface.name, bottom: Math.round(brief.getBoundingClientRect().bottom) }];
732
- });
733
- return { total: briefs.length, fold, below: briefs.filter((one) => one.bottom > fold) };
734
- }
735
- function openPanel(body) {
736
- return within(body, '[role="tabpanel"]').find(onScreen);
737
- }
738
- function headings(el) {
739
- return within(el, ".lotics-section-heading").filter(onScreen).flatMap((heading) => {
740
- const title = within(heading, "h1, h2, h3, h4, h5, h6")[0];
741
- return title === void 0 ? [] : [words2(title)];
749
+ const acts = surfaces(root).flatMap((surface) => {
750
+ const seat = within(surface.frame, `${FRAME}__act`).find(onScreen);
751
+ const button = seat === void 0 ? void 0 : within(seat, ".lotics-button").find(onScreen);
752
+ return button === void 0 ? [] : [{ record: surface.name, act: nameOf(button) || words2(button), bottom: Math.round(button.getBoundingClientRect().bottom) }];
742
753
  });
754
+ return { total: acts.length, fold, below: acts.filter((one) => one.bottom > fold) };
743
755
  }
744
- function briefRepeats(root = document.body) {
756
+ function factRepeats(root = document.body) {
745
757
  let total = 0;
746
758
  const repeated = surfaces(root).flatMap((surface) => {
747
- const brief = within(surface.body, BRIEF).find(onScreen);
748
- if (brief === void 0) return [];
759
+ const header = within(surface.root, HEADER).find(onScreen);
760
+ if (header === void 0) return [];
749
761
  const head = new Set(
750
- within(surface.root, "*").filter((el) => el.childElementCount === 0 && !surface.body.contains(el) && el.closest('button, [role="button"], [role="tab"]') === null && onScreen(el)).map((el) => said(words2(el))).filter((text) => text.length > 1)
762
+ within(header, "*").filter((el) => el.childElementCount === 0 && el.closest('button, [role="button"], [role="menu"]') === null && onScreen(el)).map((el) => said(words2(el))).filter((text) => text.length > 1)
751
763
  );
752
- const values = within(brief, ".lotics-record-facts__value").filter(onScreen);
753
- const parts = headings(brief);
754
- total += values.length + parts.length;
755
- const panel = openPanel(surface.body);
756
- const open = new Set(panel === void 0 ? [] : headings(panel).map(said));
757
- return [
758
- ...values.flatMap((value) => {
759
- const text = said(words2(value));
760
- return text !== "" && head.has(text) ? [{ record: surface.name, value: words2(value), where: "head" }] : [];
761
- }),
762
- ...parts.flatMap((part) => open.has(said(part)) ? [{ record: surface.name, value: part, where: "tab" }] : [])
763
- ];
764
- });
765
- return { total, repeated };
766
- }
767
- function actRepeats(root = document.body) {
768
- let total = 0;
769
- const repeated = surfaces(root).flatMap((surface) => {
770
- const panel = openPanel(surface.body);
771
- if (panel === void 0) return [];
772
- const verbs = new Set(
773
- within(surface.root, PRIMARY).filter((act) => !surface.body.contains(act) && onScreen(act)).map((act) => said(words2(act))).filter((text) => text !== "")
774
- );
775
- const waiting = within(panel, ".lotics-act-card").filter((card) => card.querySelector(".lotics-act-card__fold") === null && onScreen(card));
776
- const firsts = waiting.filter((card, at) => waiting.findIndex((other) => other.parentElement === card.parentElement) === at);
777
- total += firsts.length;
778
- return firsts.flatMap((card) => {
779
- const title = words2(card.querySelector(".lotics-act-card__title"));
780
- return title !== "" && verbs.has(said(title)) ? [{ record: surface.name, act: title }] : [];
764
+ const fields = within(surface.frame, ".lotics-fact-groups__field").filter(onScreen);
765
+ total += fields.length;
766
+ return fields.flatMap((field) => {
767
+ const label = within(field, ".lotics-form-field__label")[0];
768
+ const value = within(field, "*").filter((el) => el.childElementCount === 0 && (label === void 0 || !label.contains(el)) && onScreen(el)).map((el) => words2(el)).join(" ").trim();
769
+ return value !== "" && head.has(said(value)) ? [{ record: surface.name, field: words2(label), value }] : [];
781
770
  });
782
771
  });
783
772
  return { total, repeated };
784
773
  }
785
- function emptyTabs(root = document.body) {
774
+ function emptyBlocks(root = document.body) {
786
775
  let total = 0;
787
776
  const empty = surfaces(root).flatMap((surface) => {
788
- const tabs = within(surface.body, '[role="tab"]').filter(onScreen);
789
- total += tabs.length;
790
- const open = tabs.find((tab) => tab.getAttribute("aria-selected") === "true");
791
- const panel = openPanel(surface.body);
792
- const bare = open !== void 0 && panel !== void 0 && within(panel, ".lotics-section").filter(onScreen).length === 0;
793
- return bare ? [{ record: surface.name, tab: words2(open).replace(/\d+$/, "").trim() }] : [];
777
+ const blocks = within(surface.frame, BLOCK).filter(onScreen);
778
+ total += blocks.length;
779
+ return blocks.flatMap((block) => {
780
+ const body = Array.from(block.children).filter(
781
+ (child) => !child.matches(`.lotics-section-heading, ${FRAME}__strip`) && onScreen(child)
782
+ );
783
+ const drawn = body.some((child) => words2(child) !== "" || child.querySelector("img, svg, input, textarea, button") !== null);
784
+ return drawn ? [] : [{ record: surface.name, block: block.getAttribute("aria-label") ?? "" }];
785
+ });
794
786
  });
795
787
  return { total, empty };
796
788
  }
@@ -801,8 +793,8 @@ var __loticsProbe = (() => {
801
793
  function clutterCensus(root = document.body) {
802
794
  const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
803
795
  const chrome = 'nav, [role="banner"]';
804
- const recordSurface = '.lotics-record-page, [role="dialog"]';
805
- const headings2 = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
796
+ const recordSurface = '.lotics-record-frame, [role="dialog"]:not([data-side])';
797
+ const headings = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
806
798
  const noteFloor = 3;
807
799
  const formFloor = 3;
808
800
  const hueStep = 30;
@@ -811,7 +803,7 @@ var __loticsProbe = (() => {
811
803
  const sameEdge = 8;
812
804
  const words5 = (el) => el === null ? "" : (el.textContent || "").trim().slice(0, 40);
813
805
  const surfaces2 = within(root, recordSurface).filter(onScreen);
814
- const called2 = (el) => el.matches(headings2) ? words5(el) || "the section" : el.matches(recordSurface) ? words5(el.querySelector(".lotics-record-page__heading, h1, h2")) || "the record" : "the screen";
806
+ const called2 = (el) => el.matches(headings) ? words5(el) || "the section" : el.matches(recordSurface) ? words5(el.querySelector(".lotics-record-header__title, h1, h2")) || "the record" : "the screen";
815
807
  const rules = [];
816
808
  const primary = '[data-variant="primary"]';
817
809
  const acts = within(root, primary).filter(
@@ -824,7 +816,7 @@ var __loticsProbe = (() => {
824
816
  const surface = act.closest(recordSurface) ?? page;
825
817
  let titles = titled.get(surface);
826
818
  if (titles === void 0) {
827
- titles = within(surface, headings2).filter(onScreen);
819
+ titles = within(surface, headings).filter(onScreen);
828
820
  titled.set(surface, titles);
829
821
  }
830
822
  let owner = surface;
@@ -917,34 +909,6 @@ var __loticsProbe = (() => {
917
909
  count: twice,
918
910
  offenders: repeated
919
911
  });
920
- const absent = /^[—–-]$/;
921
- let facts = 0;
922
- let standing = 0;
923
- const open = [];
924
- for (const surface of surfaces2) {
925
- for (const block of within(surface, ".lotics-record-facts")) {
926
- const fold = block.querySelector(".lotics-record-facts__more");
927
- if (fold === null) continue;
928
- for (const band of Array.from(block.querySelectorAll(".lotics-record-facts__group"))) {
929
- if ((fold.compareDocumentPosition(band) & Node.DOCUMENT_POSITION_FOLLOWING) === 0) continue;
930
- for (const cell of Array.from(band.querySelectorAll(".lotics-record-facts__fact"))) {
931
- const value = cell.querySelector(".lotics-record-facts__value");
932
- if (value === null || !onScreen(cell)) continue;
933
- facts += 1;
934
- if (!absent.test((value.textContent || "").trim())) continue;
935
- standing += 1;
936
- if (open.length < 3) open.push(`"${words5(cell.querySelector(".lotics-record-facts__label")) || words5(cell)}" is unstated and drawn`);
937
- }
938
- }
939
- }
940
- }
941
- rules.push({
942
- law: "hierarchy.md",
943
- rule: "How a record derives them",
944
- total: facts,
945
- count: standing,
946
- offenders: open
947
- });
948
912
  let boxes = 0;
949
913
  let overReserved = 0;
950
914
  const reserved = [];
@@ -1177,11 +1141,11 @@ var __loticsProbe = (() => {
1177
1141
  const tag = /^H([1-6])$/.exec(el.tagName);
1178
1142
  return tag === null ? 2 : Number(tag[1]);
1179
1143
  };
1180
- const headings2 = within(root, HEADING2).filter(
1144
+ const headings = within(root, HEADING2).filter(
1181
1145
  (el) => onScreen(el) && el.closest(NAVIGATION) === null && words3(el) !== ""
1182
1146
  );
1183
- const shallowest = headings2.reduce((top, el) => Math.min(top, rank(el)), Infinity);
1184
- const pageTitle = headings2.find((el) => rank(el) === shallowest);
1147
+ const shallowest = headings.reduce((top, el) => Math.min(top, rank(el)), Infinity);
1148
+ const pageTitle = headings.find((el) => rank(el) === shallowest);
1185
1149
  const headerRow = (heading) => {
1186
1150
  const own = words3(heading);
1187
1151
  let row = heading;
@@ -1195,7 +1159,7 @@ var __loticsProbe = (() => {
1195
1159
  let sections = 0;
1196
1160
  let badged = 0;
1197
1161
  const badges = [];
1198
- for (const heading of headings2) {
1162
+ for (const heading of headings) {
1199
1163
  if (heading === pageTitle) continue;
1200
1164
  sections += 1;
1201
1165
  const mark = graphicIn(headerRow(heading));
@@ -1399,18 +1363,18 @@ var __loticsProbe = (() => {
1399
1363
  const row = ".lotics-table__row";
1400
1364
  return within(root, table).filter((register) => onScreen(register) && !register.hasAttribute("data-stacked")).map((register) => {
1401
1365
  const rows = Array.from(register.querySelectorAll(row)).filter((one) => inside(one, register, table) && onScreen(one) && stored(one));
1402
- const headings2 = Array.from(register.querySelectorAll(".lotics-table__column")).filter((heading) => inside(heading, register, table));
1366
+ const headings = Array.from(register.querySelectorAll(".lotics-table__column")).filter((heading) => inside(heading, register, table));
1403
1367
  const cellsOf = (one) => {
1404
1368
  const box = Array.from(one.querySelectorAll(".lotics-table__cells")).find((cells) => inside(cells, one, row));
1405
1369
  return box === void 0 ? [] : Array.from(box.children).filter((cell) => cell.classList.contains("lotics-table__cell"));
1406
1370
  };
1407
1371
  const gutterOf = (one) => Array.from(one.querySelectorAll(".lotics-table__trailing")).find((gutter) => inside(gutter, one, row));
1408
1372
  const grid = rows.map(cellsOf);
1409
- const columns = Math.max(headings2.length, ...grid.map((cells) => cells.length), 0);
1373
+ const columns = Math.max(headings.length, ...grid.map((cells) => cells.length), 0);
1410
1374
  const slots = Array.from({ length: columns }, (_, index) => ({
1411
- name: words4(headings2[index]) || `column ${index + 1}`,
1375
+ name: words4(headings[index]) || `column ${index + 1}`,
1412
1376
  kind: "column",
1413
- heading: headings2[index],
1377
+ heading: headings[index],
1414
1378
  cells: grid.map((cells) => cells[index])
1415
1379
  }));
1416
1380
  const gutters = rows.map(gutterOf);
@@ -1421,19 +1385,19 @@ var __loticsProbe = (() => {
1421
1385
  function ariaRegisters(root) {
1422
1386
  const table = '[role="table"], [role="grid"]';
1423
1387
  return within(root, table).filter(onScreen).map((register) => {
1424
- const headings2 = Array.from(register.querySelectorAll('[role="columnheader"]')).filter((heading) => inside(heading, register, table)).map(words4);
1388
+ const headings = Array.from(register.querySelectorAll('[role="columnheader"]')).filter((heading) => inside(heading, register, table)).map(words4);
1425
1389
  const grid = Array.from(register.querySelectorAll('[role="row"]')).filter((one) => inside(one, register, table) && onScreen(one) && stored(one)).map((one) => ({
1426
1390
  row: one,
1427
1391
  cells: Array.from(one.querySelectorAll('[role="cell"], [role="gridcell"]')).filter(
1428
1392
  (cell) => inside(cell, one, '[role="row"]')
1429
1393
  )
1430
1394
  })).filter((one) => one.cells.length > 0);
1431
- const columns = Math.max(headings2.length, ...grid.map((one) => one.cells.length), 0);
1395
+ const columns = Math.max(headings.length, ...grid.map((one) => one.cells.length), 0);
1432
1396
  return {
1433
1397
  root: register,
1434
1398
  rows: grid.map((one) => one.row),
1435
1399
  slots: Array.from({ length: columns }, (_, index) => ({
1436
- name: headings2[index] || `column ${index + 1}`,
1400
+ name: headings[index] || `column ${index + 1}`,
1437
1401
  kind: "column",
1438
1402
  cells: grid.map((one) => one.cells[index])
1439
1403
  }))
@@ -1612,39 +1576,10 @@ var __loticsProbe = (() => {
1612
1576
 
1613
1577
  // src/probe_collectors/right_form.ts
1614
1578
  var DAY_RUN_PART = ".lotics-timeline__dayhead";
1615
- var SIDE = 'aside, [role="complementary"], nav, [role="navigation"]';
1616
- var IN_PAGE = 'button, summary, [role="button"], [role="link"], [role="tab"], [role="menuitem"], a[href^="#"]';
1617
- var HEADING3 = 'h1, h2, h3, h4, h5, h6, [role="heading"]';
1618
- var RECORD_ROWS = 'table, [role="table"], [role="grid"], [role="treegrid"], [role="row"], [role="list"], ul, ol';
1619
- var PARKED = [
1620
- {
1621
- is: 'input, textarea, select, [contenteditable=""], [contenteditable="true"], [role="textbox"], [role="searchbox"], [role="combobox"], [role="spinbutton"], [role="slider"], [role="checkbox"], [role="radio"], [role="switch"], form, [role="form"]',
1622
- called: "a field"
1623
- },
1624
- { is: 'table, [role="table"], [role="grid"], [role="treegrid"], [role="row"], [role="listbox"]', called: "a list of rows" },
1625
- { is: 'article, [role="article"], [role="feed"], [role="log"]', called: "a conversation" },
1626
- { is: 'img, svg, picture, canvas, [role="img"], [role="figure"]', called: "a picture" },
1627
- { is: "a[href]", called: "a link out of the page" }
1628
- ];
1629
1579
  function rightFormCensus(root = document.body) {
1630
1580
  const skipTag = /^(SCRIPT|STYLE|TITLE|META|LINK|NOSCRIPT|TEMPLATE)$/;
1631
- const recordSurface = '.lotics-record-page, [role="dialog"]';
1632
1581
  const columnFloor = 3;
1633
- const twoTrackWidth = 1016;
1634
- const trackHeight = 240;
1635
1582
  const words5 = (el) => el === null ? "" : (el.textContent || "").trim().slice(0, 40);
1636
- const surfaces2 = within(root, recordSurface).filter(onScreen);
1637
- const called2 = (el) => words5(el.querySelector(".lotics-record-page__heading, h1, h2")) || "the record";
1638
- const readingInside = (side) => {
1639
- for (const kind of PARKED) {
1640
- for (const el of Array.from(side.querySelectorAll(kind.is))) {
1641
- if (!onScreen(el) || el.closest(IN_PAGE) !== null) continue;
1642
- const named = nameOf(el).slice(0, 40) || words5(el);
1643
- return named === "" ? kind.called : `${kind.called} ("${named}")`;
1644
- }
1645
- }
1646
- return null;
1647
- };
1648
1583
  const rules = [];
1649
1584
  const choosers = within(root, 'select, [role="listbox"]').filter(onScreen);
1650
1585
  let twoOption = 0;
@@ -1663,7 +1598,7 @@ var __loticsProbe = (() => {
1663
1598
  }
1664
1599
  rules.push({
1665
1600
  law: "data_entry.md",
1666
- rule: "Choosing a CHOICE control \u2014 `choiceControl`, never taste",
1601
+ rule: "Choosing a CHOICE control \u2014 by the set, never taste",
1667
1602
  total: choosers.length,
1668
1603
  count: twoOption,
1669
1604
  offenders: pairs
@@ -1707,7 +1642,7 @@ var __loticsProbe = (() => {
1707
1642
  walk(root);
1708
1643
  }
1709
1644
  const dates = leaves.filter((leaf) => leaf.date);
1710
- const headings2 = leaves.filter((leaf) => !leaf.date);
1645
+ const headings = leaves.filter((leaf) => !leaf.date);
1711
1646
  const leads = (leaf) => !leaves.some(
1712
1647
  (other) => other.left < leaf.left && other.top < leaf.bottom && other.bottom > leaf.top
1713
1648
  );
@@ -1733,112 +1668,19 @@ var __loticsProbe = (() => {
1733
1668
  grouped += 1;
1734
1669
  if (runs.length < 3) {
1735
1670
  const top = Math.min(...column.items.map((leaf) => leaf.top));
1736
- const over = headings2.filter((label) => label.top < top && top - label.top <= 360 && label.left < column.right && label.right > column.left).sort((a, b) => b.top - a.top)[0];
1671
+ const over = headings.filter((label) => label.top < top && top - label.top <= 360 && label.left < column.right && label.right > column.left).sort((a, b) => b.top - a.top)[0];
1737
1672
  runs.push(
1738
1673
  `${over === void 0 ? "(unnamed)" : over.text}: ${column.items.length} rows over ${days.size} days, the date repeated per row`
1739
1674
  );
1740
1675
  }
1741
1676
  }
1742
1677
  rules.push({
1743
- law: "templates.md",
1678
+ law: "screen.md",
1744
1679
  rule: "Which device answers the question \u2192 Read what happened, or what is planned, in time",
1745
1680
  total: columns.length,
1746
1681
  count: grouped,
1747
1682
  offenders: runs
1748
1683
  });
1749
- const namesItself = (region) => {
1750
- const heading = Array.from(region.querySelectorAll(HEADING3)).find((el) => el.closest("summary") === null);
1751
- return heading !== void 0 || region.querySelector(RECORD_ROWS) !== null;
1752
- };
1753
- let folds = 0;
1754
- let sections = 0;
1755
- const hidden = [];
1756
- for (const surface of surfaces2) {
1757
- for (const fold of Array.from(surface.querySelectorAll(".lotics-accordion__trigger, details, [aria-expanded]"))) {
1758
- if (!onScreen(fold) || fold.closest('[role="complementary"]') !== null) continue;
1759
- if (fold.hasAttribute("aria-haspopup") || fold.matches(HOLDS_A_VALUE)) continue;
1760
- folds += 1;
1761
- const opened = fold.tagName === "DETAILS" ? [fold] : pointedAt(fold, "aria-controls");
1762
- if (!fold.matches(".lotics-accordion__trigger") && !opened.some(namesItself)) continue;
1763
- sections += 1;
1764
- if (hidden.length < 3) {
1765
- const named = nameOf(fold).slice(0, 40);
1766
- hidden.push(`${called2(surface)}: "${named === "" ? "(unnamed)" : named}" is a section behind a fold`);
1767
- }
1768
- }
1769
- }
1770
- rules.push({
1771
- law: "templates.md",
1772
- rule: "Which device answers the question \u2192 See what NAMES this record",
1773
- total: folds,
1774
- count: sections,
1775
- offenders: hidden
1776
- });
1777
- let read = 0;
1778
- let split = 0;
1779
- const tracks = [];
1780
- if (window.innerWidth >= twoTrackWidth) {
1781
- for (const surface of surfaces2) {
1782
- read += 1;
1783
- const box = surface.getBoundingClientRect();
1784
- const owners = [surface, ...Array.from(surface.querySelectorAll("*"))];
1785
- let found = null;
1786
- for (const owner of owners) {
1787
- const columnsHere = Array.from(owner.children).filter((child) => {
1788
- if (!onScreen(child)) return false;
1789
- if (child.matches(SIDE) || child.querySelector(SIDE) !== null) return false;
1790
- const rect = child.getBoundingClientRect();
1791
- return rect.height >= trackHeight && rect.height >= box.height * 0.5 && rect.width >= box.width * 0.2;
1792
- });
1793
- for (let a = 0; a < columnsHere.length && found === null; a++) {
1794
- for (let b = a + 1; b < columnsHere.length && found === null; b++) {
1795
- const left = columnsHere[a].getBoundingClientRect();
1796
- const right = columnsHere[b].getBoundingClientRect();
1797
- const apart = left.right <= right.left + 8 || right.right <= left.left + 8;
1798
- const overlap = Math.min(left.bottom, right.bottom) - Math.max(left.top, right.top);
1799
- const together = Math.max(left.right, right.right) - Math.min(left.left, right.left);
1800
- if (apart && overlap >= Math.min(left.height, right.height) * 0.5 && together >= box.width * 0.7) {
1801
- found = [columnsHere[a], columnsHere[b]];
1802
- }
1803
- }
1804
- }
1805
- if (found !== null) break;
1806
- }
1807
- if (found !== null) {
1808
- split += 1;
1809
- if (tracks.length < 3) {
1810
- const left = Math.round(found[0].getBoundingClientRect().width);
1811
- const right = Math.round(found[1].getBoundingClientRect().width);
1812
- tracks.push(`${called2(surface)}: two columns side by side, ${left}px and ${right}px`);
1813
- }
1814
- continue;
1815
- }
1816
- for (const side of Array.from(surface.querySelectorAll(SIDE))) {
1817
- if (!onScreen(side)) continue;
1818
- const rect = side.getBoundingClientRect();
1819
- if (rect.width > box.width * 0.6) continue;
1820
- const column = Array.from(surface.querySelectorAll("*")).some((other) => {
1821
- if (other === side || side.contains(other) || other.contains(side) || !onScreen(other)) return false;
1822
- const beside = other.getBoundingClientRect();
1823
- const apart = beside.left >= rect.right - 8 || beside.right <= rect.left + 8;
1824
- const overlap = Math.min(beside.bottom, rect.bottom) - Math.max(beside.top, rect.top);
1825
- return apart && beside.width >= box.width * 0.4 && overlap >= rect.height * 0.5;
1826
- });
1827
- if (!column) continue;
1828
- const parked = readingInside(side);
1829
- if (parked === null) continue;
1830
- split += 1;
1831
- if (tracks.length < 3) tracks.push(`${called2(surface)}: ${parked} beside the reading column`);
1832
- }
1833
- }
1834
- }
1835
- rules.push({
1836
- law: "hierarchy.md",
1837
- rule: "Depth \u2014 four surfaces, four jobs",
1838
- total: read,
1839
- count: split,
1840
- offenders: tracks
1841
- });
1842
1684
  return rules;
1843
1685
  }
1844
1686
 
@@ -1850,12 +1692,12 @@ var __loticsProbe = (() => {
1850
1692
  const navFirst = nav === null ? void 0 : Array.from(nav.querySelectorAll('a[href], [role="link"]')).filter(
1851
1693
  (item) => onScreen(item) && (item.textContent || "").trim() !== ""
1852
1694
  )[0];
1853
- const headings2 = within(root, "h1, h2, h3, h4, h5, h6").filter(
1695
+ const headings = within(root, "h1, h2, h3, h4, h5, h6").filter(
1854
1696
  (el) => onScreen(el) && el.closest('nav, [role="banner"]') === null && (el.textContent || "").trim() !== ""
1855
1697
  );
1856
- const title = headings2[0];
1698
+ const title = headings[0];
1857
1699
  const rank = (el) => Number(el.tagName.slice(1));
1858
- const heading = title === void 0 ? void 0 : headings2.slice(1).find((el) => rank(el) > rank(title));
1700
+ const heading = title === void 0 ? void 0 : headings.slice(1).find((el) => rank(el) > rank(title));
1859
1701
  const primaries = within(root, '[data-variant="primary"]').filter(
1860
1702
  (el) => onScreen(el) && el.closest('nav, [role="banner"]') === null
1861
1703
  );
@@ -2016,27 +1858,22 @@ var __loticsProbe = (() => {
2016
1858
  record_primary: {
2017
1859
  law: "hierarchy.md",
2018
1860
  section: "One primary act per surface",
2019
- fix: "Draw a section's add secondary (`SectionHeadingAdd`): the record's one primary act is its head's \u2014 the next queued act, else the step its stage leads with."
2020
- },
2021
- brief_fold: {
2022
- law: "hierarchy.md",
2023
- section: "Depth \u2014 four surfaces, four jobs",
2024
- fix: "Keep in the brief only what the head's act needs \u2014 the newest entry, the words to say, the one band placed first \u2014 and move the rest to a tab, until its foot is in the first viewport unscrolled."
1861
+ fix: "Draw a block's add secondary: the record's one primary act is the act open now, in its `ActPanel`."
2025
1862
  },
2026
- brief_repeats: {
1863
+ act_fold: {
2027
1864
  law: "hierarchy.md",
2028
- section: "One fact, one place",
2029
- fix: "Take it out of the brief: the head already states the value, or the tab open at rest already draws the part, so the brief says it a second time."
1865
+ section: "Budgets a reviewer can count",
1866
+ fix: "Seat the open act where the frame pins it \u2014 `RecordFrame`'s `act`, never a button of the page's own \u2014 so it stays in view while the facts run on."
2030
1867
  },
2031
- act_repeats: {
1868
+ fact_repeats: {
2032
1869
  law: "hierarchy.md",
2033
1870
  section: "One fact, one place",
2034
- fix: "Draw the head's act in its queue's card by its words and outcomes alone (`ActCard` `carried`): its verb, due day and refusal are the head's."
1871
+ fix: "Take the field out of the facts: the header already states it \u2014 its title, its status, the line under it or its figure."
2035
1872
  },
2036
- empty_tab: {
1873
+ empty_block: {
2037
1874
  law: "hierarchy.md",
2038
- section: "Depth \u2014 four surfaces, four jobs",
2039
- fix: "Draw no tab over nothing: a tab is drawn only over a part with a body, so a part with no rows passes none."
1875
+ section: "Whitespace is structure",
1876
+ fix: "Draw the block's body, or its empty line: a block under a heading that draws nothing is a press that leads nowhere."
2040
1877
  },
2041
1878
  render: {
2042
1879
  law: "reviewing.md",
@@ -2062,7 +1899,7 @@ var __loticsProbe = (() => {
2062
1899
  fix: "Move a fact down a tier, fold it, or take it off the screen \u2014 a budget failing is a tiering question, never a permission to shrink something."
2063
1900
  },
2064
1901
  right_form: {
2065
- law: "templates.md",
1902
+ law: "screen.md",
2066
1903
  section: "Which device answers the question",
2067
1904
  fix: "Draw what the JOB names: the device index gives the entry and the wrong device beside it, and a shape is picked by what the reader must do rather than by what the data is."
2068
1905
  },
@@ -2268,49 +2105,37 @@ var __loticsProbe = (() => {
2268
2105
  ...signature("record_primary"),
2269
2106
  total: primaries.total,
2270
2107
  count: primaries.loud.length,
2271
- detail: primaries.loud.slice(0, 5).map((one) => `"${one.record}": "${one.act}" is primary in a section's heading row, beside the head's act`)
2108
+ detail: primaries.loud.slice(0, 5).map((one) => `"${one.record}": "${one.act}" is primary in a block's heading row, beside the record's open act`)
2272
2109
  });
2273
2110
  }
2274
- const folded = briefsBelowFold(surface);
2111
+ const folded = actsBelowFold(surface);
2275
2112
  if (folded.below.length > 0) {
2276
2113
  findings.push({
2277
- probe: "brief_fold",
2278
- ...signature("brief_fold"),
2114
+ probe: "act_fold",
2115
+ ...signature("act_fold"),
2279
2116
  total: folded.total,
2280
2117
  count: folded.below.length,
2281
- detail: folded.below.slice(0, 5).map((one) => `"${one.record}": the brief ends at ${one.bottom}px, past the ${folded.fold}px first viewport`)
2118
+ detail: folded.below.slice(0, 5).map((one) => `"${one.record}": "${one.act}" ends at ${one.bottom}px, past the ${folded.fold}px first viewport`)
2282
2119
  });
2283
2120
  }
2284
- const repeats = briefRepeats(surface);
2121
+ const repeats = factRepeats(surface);
2285
2122
  if (repeats.repeated.length > 0) {
2286
2123
  findings.push({
2287
- probe: "brief_repeats",
2288
- ...signature("brief_repeats"),
2124
+ probe: "fact_repeats",
2125
+ ...signature("fact_repeats"),
2289
2126
  total: repeats.total,
2290
2127
  count: repeats.repeated.length,
2291
- detail: repeats.repeated.slice(0, 5).map(
2292
- (one) => one.where === "head" ? `"${one.record}": the brief states "${one.value}", which the head already says` : `"${one.record}": the brief stands "${one.value}", which the tab open at rest draws too`
2293
- )
2128
+ detail: repeats.repeated.slice(0, 5).map((one) => `"${one.record}": the fact "${one.field}" reads "${one.value}", which the header already says`)
2294
2129
  });
2295
2130
  }
2296
- const acts = actRepeats(surface);
2297
- if (acts.repeated.length > 0) {
2131
+ const blocks = emptyBlocks(surface);
2132
+ if (blocks.empty.length > 0) {
2298
2133
  findings.push({
2299
- probe: "act_repeats",
2300
- ...signature("act_repeats"),
2301
- total: acts.total,
2302
- count: acts.repeated.length,
2303
- detail: acts.repeated.slice(0, 5).map((one) => `"${one.record}": the head's act "${one.act}" heads a card in the tab open at rest too`)
2304
- });
2305
- }
2306
- const tabs = emptyTabs(surface);
2307
- if (tabs.empty.length > 0) {
2308
- findings.push({
2309
- probe: "empty_tab",
2310
- ...signature("empty_tab"),
2311
- total: tabs.total,
2312
- count: tabs.empty.length,
2313
- detail: tabs.empty.slice(0, 5).map((one) => `"${one.record}": the tab "${one.tab}" holds nothing`)
2134
+ probe: "empty_block",
2135
+ ...signature("empty_block"),
2136
+ total: blocks.total,
2137
+ count: blocks.empty.length,
2138
+ detail: blocks.empty.slice(0, 5).map((one) => `"${one.record}": the block "${one.block}" draws nothing`)
2314
2139
  });
2315
2140
  }
2316
2141
  findings.push(...censusFindings("clutter", clutterCensus(surface)));
@@ -2395,8 +2220,8 @@ var __loticsProbe = (() => {
2395
2220
  function recordLinks() {
2396
2221
  const here = location.pathname + location.search;
2397
2222
  const doors2 = Array.from(
2398
- document.querySelectorAll('a[href], [data-opens="page"], button, [role="button"], [role="link"]')
2399
- ).filter((door) => door.matches('a[href], [data-opens="page"]') || isEmptyDoor(door));
2223
+ document.querySelectorAll('a[href], [data-opens="page"], [data-opens="event"], button, [role="button"], [role="link"]')
2224
+ ).filter((door) => door.matches('a[href], [data-opens="page"], [data-opens="event"]') || isEmptyDoor(door));
2400
2225
  return doors2.filter((door) => {
2401
2226
  if (door.closest('nav, [role="banner"], [role="tablist"]') !== null) return false;
2402
2227
  if (doors2.some((other) => other !== door && other.contains(door))) return false;
@@ -2496,6 +2321,9 @@ var __loticsProbe = (() => {
2496
2321
  const folds = Array.from(document.querySelectorAll('[aria-expanded="false"]')).filter(
2497
2322
  (el) => reachable(el) && el.getAttribute("aria-haspopup") === null
2498
2323
  );
2324
+ const tabs = Array.from(document.querySelectorAll('[role="tab"][aria-selected="false"]')).filter(
2325
+ (el) => onScreen(el) && el.closest('nav, [role="banner"]') === null && !open.some((overlay) => overlay.contains(el))
2326
+ );
2499
2327
  const acts = Array.from(
2500
2328
  document.querySelectorAll('[data-variant="primary"], [data-variant="secondary"]')
2501
2329
  ).filter((el) => {
@@ -2518,7 +2346,8 @@ var __loticsProbe = (() => {
2518
2346
  ...menus.slice(0, 1).map((el) => ({ kind: "menu", el })),
2519
2347
  ...folds.slice(0, 1).map((el) => ({ kind: "fold", el })),
2520
2348
  ...acts.map((el) => ({ kind: "dialog", el })),
2521
- ...parties.map((el) => ({ kind: "party", el }))
2349
+ ...parties.map((el) => ({ kind: "party", el })),
2350
+ ...tabs.map((el) => ({ kind: "tab", el }))
2522
2351
  ];
2523
2352
  }
2524
2353
  function doors() {