@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.
- package/AGENTS.md +2 -3
- package/README.md +12 -13
- package/dist/probe_page.js +111 -282
- package/dist/src/cli.js +26836 -38103
- package/dist/src/client.d.ts +9 -63
- package/dist/src/client.js +2 -4
- package/docs/building_an_app.md +55 -57
- package/docs/cli_reference.md +13 -14
- package/docs/migration.md +49 -58
- package/package.json +1 -1
- package/docs/clauses.md +0 -135
package/dist/probe_page.js
CHANGED
|
@@ -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")
|
|
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(
|
|
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
|
|
698
|
-
var
|
|
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,
|
|
709
|
-
const head =
|
|
710
|
-
|
|
711
|
-
|
|
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.
|
|
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
|
|
747
|
+
function actsBelowFold(root = document.body, width = window.innerWidth) {
|
|
728
748
|
const fold = firstViewport(width).height;
|
|
729
|
-
const
|
|
730
|
-
const
|
|
731
|
-
|
|
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
|
|
756
|
+
function factRepeats(root = document.body) {
|
|
745
757
|
let total = 0;
|
|
746
758
|
const repeated = surfaces(root).flatMap((surface) => {
|
|
747
|
-
const
|
|
748
|
-
if (
|
|
759
|
+
const header = within(surface.root, HEADER).find(onScreen);
|
|
760
|
+
if (header === void 0) return [];
|
|
749
761
|
const head = new Set(
|
|
750
|
-
within(
|
|
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
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
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
|
|
774
|
+
function emptyBlocks(root = document.body) {
|
|
786
775
|
let total = 0;
|
|
787
776
|
const empty = surfaces(root).flatMap((surface) => {
|
|
788
|
-
const
|
|
789
|
-
total +=
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
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-
|
|
805
|
-
const
|
|
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(
|
|
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,
|
|
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
|
|
1144
|
+
const headings = within(root, HEADING2).filter(
|
|
1181
1145
|
(el) => onScreen(el) && el.closest(NAVIGATION) === null && words3(el) !== ""
|
|
1182
1146
|
);
|
|
1183
|
-
const shallowest =
|
|
1184
|
-
const pageTitle =
|
|
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
|
|
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
|
|
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(
|
|
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(
|
|
1375
|
+
name: words4(headings[index]) || `column ${index + 1}`,
|
|
1412
1376
|
kind: "column",
|
|
1413
|
-
heading:
|
|
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
|
|
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(
|
|
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:
|
|
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
|
|
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
|
|
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 =
|
|
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: "
|
|
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
|
|
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 =
|
|
1698
|
+
const title = headings[0];
|
|
1857
1699
|
const rank = (el) => Number(el.tagName.slice(1));
|
|
1858
|
-
const heading = title === void 0 ? void 0 :
|
|
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
|
|
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
|
-
|
|
1863
|
+
act_fold: {
|
|
2027
1864
|
law: "hierarchy.md",
|
|
2028
|
-
section: "
|
|
2029
|
-
fix: "
|
|
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
|
-
|
|
1868
|
+
fact_repeats: {
|
|
2032
1869
|
law: "hierarchy.md",
|
|
2033
1870
|
section: "One fact, one place",
|
|
2034
|
-
fix: "
|
|
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
|
-
|
|
1873
|
+
empty_block: {
|
|
2037
1874
|
law: "hierarchy.md",
|
|
2038
|
-
section: "
|
|
2039
|
-
fix: "Draw
|
|
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: "
|
|
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
|
|
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 =
|
|
2111
|
+
const folded = actsBelowFold(surface);
|
|
2275
2112
|
if (folded.below.length > 0) {
|
|
2276
2113
|
findings.push({
|
|
2277
|
-
probe: "
|
|
2278
|
-
...signature("
|
|
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}":
|
|
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 =
|
|
2121
|
+
const repeats = factRepeats(surface);
|
|
2285
2122
|
if (repeats.repeated.length > 0) {
|
|
2286
2123
|
findings.push({
|
|
2287
|
-
probe: "
|
|
2288
|
-
...signature("
|
|
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
|
|
2297
|
-
if (
|
|
2131
|
+
const blocks = emptyBlocks(surface);
|
|
2132
|
+
if (blocks.empty.length > 0) {
|
|
2298
2133
|
findings.push({
|
|
2299
|
-
probe: "
|
|
2300
|
-
...signature("
|
|
2301
|
-
total:
|
|
2302
|
-
count:
|
|
2303
|
-
detail:
|
|
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() {
|