@pinecall/dot 0.0.0-stage → 0.11.22
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/LICENSE +202 -0
- package/README.md +316 -2
- package/dist/agent/index.d.ts +4 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/tutor.d.ts +67 -0
- package/dist/agent/tutor.d.ts.map +1 -0
- package/dist/agent/tutor.js +351 -0
- package/dist/agent/tutor.js.map +1 -0
- package/dist/face/draw.d.ts +43 -0
- package/dist/face/draw.d.ts.map +1 -0
- package/dist/face/draw.js +74 -0
- package/dist/face/draw.js.map +1 -0
- package/dist/face/ear.d.ts +23 -0
- package/dist/face/ear.d.ts.map +1 -0
- package/dist/face/ear.js +111 -0
- package/dist/face/ear.js.map +1 -0
- package/dist/face/index.d.ts +23 -0
- package/dist/face/index.d.ts.map +1 -0
- package/dist/face/index.js +138 -0
- package/dist/face/index.js.map +1 -0
- package/dist/face/motion.d.ts +25 -0
- package/dist/face/motion.d.ts.map +1 -0
- package/dist/face/motion.js +58 -0
- package/dist/face/motion.js.map +1 -0
- package/dist/face/mouth.d.ts +45 -0
- package/dist/face/mouth.d.ts.map +1 -0
- package/dist/face/mouth.js +61 -0
- package/dist/face/mouth.js.map +1 -0
- package/dist/face/shape.d.ts +15 -0
- package/dist/face/shape.d.ts.map +1 -0
- package/dist/face/shape.js +50 -0
- package/dist/face/shape.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/page/page.d.ts +34 -0
- package/dist/page/page.d.ts.map +1 -0
- package/dist/page/page.js +218 -0
- package/dist/page/page.js.map +1 -0
- package/dist/page/roles.d.ts +10 -0
- package/dist/page/roles.d.ts.map +1 -0
- package/dist/page/roles.js +87 -0
- package/dist/page/roles.js.map +1 -0
- package/dist/pointer/card.d.ts +13 -0
- package/dist/pointer/card.d.ts.map +1 -0
- package/dist/pointer/card.js +75 -0
- package/dist/pointer/card.js.map +1 -0
- package/dist/pointer/code.d.ts +21 -0
- package/dist/pointer/code.d.ts.map +1 -0
- package/dist/pointer/code.js +147 -0
- package/dist/pointer/code.js.map +1 -0
- package/dist/pointer/flight.d.ts +30 -0
- package/dist/pointer/flight.d.ts.map +1 -0
- package/dist/pointer/flight.js +92 -0
- package/dist/pointer/flight.js.map +1 -0
- package/dist/pointer/hover.d.ts +20 -0
- package/dist/pointer/hover.d.ts.map +1 -0
- package/dist/pointer/hover.js +96 -0
- package/dist/pointer/hover.js.map +1 -0
- package/dist/pointer/menu.d.ts +48 -0
- package/dist/pointer/menu.d.ts.map +1 -0
- package/dist/pointer/menu.js +252 -0
- package/dist/pointer/menu.js.map +1 -0
- package/dist/pointer/person.d.ts +91 -0
- package/dist/pointer/person.d.ts.map +1 -0
- package/dist/pointer/person.js +345 -0
- package/dist/pointer/person.js.map +1 -0
- package/dist/pointer/sections.d.ts +24 -0
- package/dist/pointer/sections.d.ts.map +1 -0
- package/dist/pointer/sections.js +64 -0
- package/dist/pointer/sections.js.map +1 -0
- package/dist/pointer/syntax.d.ts +10 -0
- package/dist/pointer/syntax.d.ts.map +1 -0
- package/dist/pointer/syntax.js +61 -0
- package/dist/pointer/syntax.js.map +1 -0
- package/dist/pointer/typing.d.ts +6 -0
- package/dist/pointer/typing.d.ts.map +1 -0
- package/dist/pointer/typing.js +62 -0
- package/dist/pointer/typing.js.map +1 -0
- package/dist/tutor/captions.d.ts +22 -0
- package/dist/tutor/captions.d.ts.map +1 -0
- package/dist/tutor/captions.js +62 -0
- package/dist/tutor/captions.js.map +1 -0
- package/dist/tutor/macro.d.ts +19 -0
- package/dist/tutor/macro.d.ts.map +1 -0
- package/dist/tutor/macro.js +67 -0
- package/dist/tutor/macro.js.map +1 -0
- package/dist/tutor/options.d.ts +42 -0
- package/dist/tutor/options.d.ts.map +1 -0
- package/dist/tutor/options.js +3 -0
- package/dist/tutor/options.js.map +1 -0
- package/dist/tutor/tutor.d.ts +14 -0
- package/dist/tutor/tutor.d.ts.map +1 -0
- package/dist/tutor/tutor.js +389 -0
- package/dist/tutor/tutor.js.map +1 -0
- package/dist/tutor/views.d.ts +27 -0
- package/dist/tutor/views.d.ts.map +1 -0
- package/dist/tutor/views.js +63 -0
- package/dist/tutor/views.js.map +1 -0
- package/dist/tutor/walking.d.ts +44 -0
- package/dist/tutor/walking.d.ts.map +1 -0
- package/dist/tutor/walking.js +142 -0
- package/dist/tutor/walking.js.map +1 -0
- package/dist/tutor/walks.d.ts +46 -0
- package/dist/tutor/walks.d.ts.map +1 -0
- package/dist/tutor/walks.js +112 -0
- package/dist/tutor/walks.js.map +1 -0
- package/dist/wire/index.d.ts +133 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +204 -0
- package/dist/wire/index.js.map +1 -0
- package/package.json +53 -3
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/** A window of code the tutor opens beside the page while it explains it: written in line by line, coloured, the lines it talks about lit. */
|
|
2
|
+
//
|
|
3
|
+
// It stands on the person's layer, out of what the agent reads, at the page's left, centred in the
|
|
4
|
+
// height, so the pointer and the page stay in sight. As it opens its lines come in one after another,
|
|
5
|
+
// as if written; the lines lit are the ones the voice is on, the rest dim, and `show` answers the
|
|
6
|
+
// first lit line, for the pointer to go to. While it is open it has the focus: a border and a halo
|
|
7
|
+
// in the face's colour, and the page dimmed behind it (the person stands above, undimmed). Colours:
|
|
8
|
+
// --pc-face, and the window's own.
|
|
9
|
+
import { coloured } from "./syntax.js";
|
|
10
|
+
/** How long one line takes to come in as the window opens, in ms. */
|
|
11
|
+
const LINE_MS = 55;
|
|
12
|
+
const STYLE = `
|
|
13
|
+
.pc-code { position: fixed; z-index: 1; left: 32px; top: 50%; width: min(640px, calc(100vw - 64px)); max-height: min(80vh, 640px);
|
|
14
|
+
display: flex; flex-direction: column; transform: translateY(-50%); border-radius: 16px; overflow: hidden; pointer-events: auto;
|
|
15
|
+
background: #11121b; color: #e4e4f0; border: 1px solid color-mix(in srgb, var(--pc-face, #b7a6ff) 65%, transparent);
|
|
16
|
+
box-shadow: 0 0 0 4px color-mix(in srgb, var(--pc-face, #b7a6ff) 18%, transparent), 0 30px 80px rgb(0 0 0 / .5), 0 0 0 100vmax rgb(6 7 12 / .45);
|
|
17
|
+
transition: opacity .3s, transform .3s; font: 14px/1.7 ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace; }
|
|
18
|
+
.pc-code[hidden] { display: flex !important; opacity: 0; transform: translateY(-47%) scale(.98); pointer-events: none; }
|
|
19
|
+
.pc-code-bar { display: flex; align-items: center; gap: 8px; padding: 12px 16px; background: #181927; font-size: 12.5px; color: #9b9bb4;
|
|
20
|
+
border-bottom: 1px solid rgb(255 255 255 / .05); }
|
|
21
|
+
.pc-code-bar i { width: 11px; height: 11px; border-radius: 50%; background: #34354a; }
|
|
22
|
+
.pc-code-bar span { margin-left: 8px; color: #d8d8ea; }
|
|
23
|
+
.pc-code-bar span::before { content: ""; display: inline-block; width: 7px; height: 7px; margin-right: 9px; border-radius: 50%;
|
|
24
|
+
vertical-align: 1px; background: var(--pc-face, #b7a6ff); box-shadow: 0 0 8px var(--pc-face, #b7a6ff); }
|
|
25
|
+
.pc-code-lines { margin: 0; padding: 14px 0; overflow: auto; }
|
|
26
|
+
.pc-code-line { display: flex; padding: 0 20px 0 0; white-space: pre; transition: opacity .25s, background-color .25s, box-shadow .25s; }
|
|
27
|
+
.pc-code-line b { flex: none; width: 3.4em; padding-right: 1.2em; text-align: right; font-weight: 400; color: #4f5068; user-select: none; }
|
|
28
|
+
.pc-code-line.pc-coming { opacity: 0; transform: translateX(-4px); }
|
|
29
|
+
.pc-code-line:not(.pc-coming) { transition: opacity .25s, transform .25s, background-color .25s, box-shadow .25s; }
|
|
30
|
+
.pc-code.pc-lit .pc-code-line:not(.pc-coming) { opacity: .38; }
|
|
31
|
+
.pc-code.pc-lit .pc-code-line.pc-on { opacity: 1; background: color-mix(in srgb, var(--pc-face, #b7a6ff) 20%, transparent);
|
|
32
|
+
box-shadow: inset 3px 0 var(--pc-face, #b7a6ff); }
|
|
33
|
+
.pc-k-comment { color: #7b7f9e; font-style: italic; } .pc-k-keyword { color: #c792ea; } .pc-k-string { color: #c3e88d; }
|
|
34
|
+
.pc-k-decorator { color: #ffcb6b; } .pc-k-type { color: #82aaff; } .pc-k-tag { color: #f07178; } .pc-k-number { color: #f78c6c; }
|
|
35
|
+
.pc-k-self { color: #ff9cac; }
|
|
36
|
+
@media (max-width: 720px) { .pc-code { left: 12px; width: calc(100vw - 24px); top: auto; bottom: 120px; transform: none; max-height: 48vh; font-size: 12.5px; }
|
|
37
|
+
.pc-code[hidden] { transform: translateY(8px); } }
|
|
38
|
+
@media (prefers-reduced-motion: reduce) { .pc-code, .pc-code-line { transition: none; } }
|
|
39
|
+
`;
|
|
40
|
+
export class CodeWindow {
|
|
41
|
+
#document;
|
|
42
|
+
#style;
|
|
43
|
+
#window;
|
|
44
|
+
#title;
|
|
45
|
+
#lines;
|
|
46
|
+
#still;
|
|
47
|
+
#shown = null;
|
|
48
|
+
#writing = [];
|
|
49
|
+
/** The window, hidden, put on a layer by `host`; `still` writes its lines in at once (reduced motion). */
|
|
50
|
+
constructor(document, host, still = false) {
|
|
51
|
+
this.#document = document;
|
|
52
|
+
this.#still = still;
|
|
53
|
+
this.#style = document.createElement("style");
|
|
54
|
+
this.#style.textContent = STYLE;
|
|
55
|
+
document.head.append(this.#style);
|
|
56
|
+
this.#window = document.createElement("div");
|
|
57
|
+
this.#window.className = "pc-code";
|
|
58
|
+
this.#window.hidden = true;
|
|
59
|
+
this.#window.setAttribute("role", "figure");
|
|
60
|
+
this.#window.innerHTML = `<div class="pc-code-bar"><i></i><i></i><i></i><span></span></div><pre class="pc-code-lines"></pre>`;
|
|
61
|
+
this.#title = this.#window.querySelector("span") ?? this.#window;
|
|
62
|
+
this.#lines = this.#window.querySelector("pre") ?? this.#window;
|
|
63
|
+
host(this.#window);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* What a step shows: a window opened (or another in its place) for `code`, closed for `null`, kept as
|
|
67
|
+
* it is for none; and `lines` lit. Answers the first lit line, for the pointer to go to.
|
|
68
|
+
*/
|
|
69
|
+
show(code, lines) {
|
|
70
|
+
if (code === null) {
|
|
71
|
+
this.hide();
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (code !== undefined && code !== this.#shown)
|
|
75
|
+
this.#open(code);
|
|
76
|
+
return lines === undefined ? null : this.light(lines);
|
|
77
|
+
}
|
|
78
|
+
/** Light lines `from` to `to` (from 1), dim the rest, bring the lit ones into the window; answers the first one's code, past its number. */
|
|
79
|
+
light([from, to]) {
|
|
80
|
+
if (this.#shown === null)
|
|
81
|
+
return null;
|
|
82
|
+
this.#window.classList.add("pc-lit");
|
|
83
|
+
const rows = [...this.#lines.children];
|
|
84
|
+
rows.forEach((row, at) => row.classList.toggle("pc-on", at + 1 >= from && at + 1 <= to));
|
|
85
|
+
const first = rows[from - 1];
|
|
86
|
+
first?.scrollIntoView?.({ block: "nearest", behavior: "smooth" });
|
|
87
|
+
const code = [...(first?.querySelectorAll("span") ?? [])].find((span) => (span.textContent ?? "").trim() !== "");
|
|
88
|
+
return code ?? (first instanceof HTMLElement ? first : null);
|
|
89
|
+
}
|
|
90
|
+
/** The box the lit lines fill on the screen, for the person to stand clear of; null with none lit. */
|
|
91
|
+
litBox() {
|
|
92
|
+
const lit = [...this.#lines.querySelectorAll(".pc-on")].map((row) => row.getBoundingClientRect());
|
|
93
|
+
if (this.#shown === null || lit.length === 0)
|
|
94
|
+
return null;
|
|
95
|
+
const top = Math.min(...lit.map((box) => box.top));
|
|
96
|
+
const bottom = Math.max(...lit.map((box) => box.bottom));
|
|
97
|
+
const left = Math.min(...lit.map((box) => box.left));
|
|
98
|
+
const right = Math.max(...lit.map((box) => box.right));
|
|
99
|
+
return new DOMRect(left, top, right - left, bottom - top);
|
|
100
|
+
}
|
|
101
|
+
/** Whether a window is open. */
|
|
102
|
+
get open() {
|
|
103
|
+
return this.#shown !== null;
|
|
104
|
+
}
|
|
105
|
+
hide() {
|
|
106
|
+
this.#stopWriting();
|
|
107
|
+
this.#shown = null;
|
|
108
|
+
this.#window.hidden = true;
|
|
109
|
+
}
|
|
110
|
+
close() {
|
|
111
|
+
this.#stopWriting();
|
|
112
|
+
this.#window.remove();
|
|
113
|
+
this.#style.remove();
|
|
114
|
+
}
|
|
115
|
+
#open(code) {
|
|
116
|
+
this.#stopWriting();
|
|
117
|
+
this.#shown = code;
|
|
118
|
+
this.#title.textContent = code.title;
|
|
119
|
+
this.#window.classList.remove("pc-lit");
|
|
120
|
+
const rows = coloured(code.text).map((pieces, at) => {
|
|
121
|
+
const row = this.#document.createElement("div");
|
|
122
|
+
row.className = this.#still ? "pc-code-line" : "pc-code-line pc-coming";
|
|
123
|
+
const number = this.#document.createElement("b");
|
|
124
|
+
number.textContent = String(at + 1);
|
|
125
|
+
row.append(number);
|
|
126
|
+
for (const piece of pieces) {
|
|
127
|
+
const span = this.#document.createElement("span");
|
|
128
|
+
if (piece.kind !== "plain")
|
|
129
|
+
span.className = `pc-k-${piece.kind}`;
|
|
130
|
+
span.textContent = piece.text;
|
|
131
|
+
row.append(span);
|
|
132
|
+
}
|
|
133
|
+
if (pieces.length === 0)
|
|
134
|
+
row.append(this.#document.createTextNode(" "));
|
|
135
|
+
return row;
|
|
136
|
+
});
|
|
137
|
+
this.#lines.replaceChildren(...rows);
|
|
138
|
+
this.#window.hidden = false;
|
|
139
|
+
this.#writing = rows.map((row, at) => setTimeout(() => row.classList.remove("pc-coming"), this.#still ? 0 : (at + 1) * LINE_MS));
|
|
140
|
+
}
|
|
141
|
+
#stopWriting() {
|
|
142
|
+
for (const timer of this.#writing)
|
|
143
|
+
clearTimeout(timer);
|
|
144
|
+
this.#writing = [];
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
//# sourceMappingURL=code.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code.js","sourceRoot":"","sources":["../../src/pointer/code.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** How the pointer moves: where it aims on an element, the path it flies, and the page scrolled like a hand scrolls it. */
|
|
2
|
+
export interface Point {
|
|
3
|
+
x: number;
|
|
4
|
+
y: number;
|
|
5
|
+
}
|
|
6
|
+
/** Minimum jerk: 0 to 1, starting and ending still. */
|
|
7
|
+
export declare function minJerk(u: number): number;
|
|
8
|
+
/** How long a flight of `distance` px takes on a screen `width` px wide, in ms. */
|
|
9
|
+
export declare function flightMs(distance: number, width: number): number;
|
|
10
|
+
/** The point at `u` (0 to 1) of the flight from `from` to `to`, bent a little off the line. */
|
|
11
|
+
export declare function along(from: Point, to: Point, u: number): Point;
|
|
12
|
+
/**
|
|
13
|
+
* Where a person aims on an element: just under its first line of words, at their middle (at most
|
|
14
|
+
* 60 px in), so the pointer shows the words rather than covering them; else the middle of its box.
|
|
15
|
+
*/
|
|
16
|
+
export declare function aimOf(element: Element): Point;
|
|
17
|
+
/** Run `step(u)` from 0 to 1 over `ms`, once a frame; resolves at 1. */
|
|
18
|
+
export declare function animate(ms: number, step: (u: number) => void): Promise<void>;
|
|
19
|
+
/**
|
|
20
|
+
* The scroll at which `box` (as the screen shows it at `scrollY`) sits at `where` of a screen `height`
|
|
21
|
+
* tall, within a page `pageHeight` tall: its middle there, or, for one taller than most of the screen
|
|
22
|
+
* (a section on a phone), its start near the top, so what it begins with is what shows.
|
|
23
|
+
*/
|
|
24
|
+
export declare function scrollGoal(box: {
|
|
25
|
+
top: number;
|
|
26
|
+
height: number;
|
|
27
|
+
}, scrollY: number, height: number, pageHeight: number, where: number): number;
|
|
28
|
+
/** Scroll the page, eased, until the element sits at `where` of the screen's height. Nothing when it is there already. */
|
|
29
|
+
export declare function scrollOnto(element: Element, where: number, still: boolean): Promise<void>;
|
|
30
|
+
//# sourceMappingURL=flight.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flight.d.ts","sourceRoot":"","sources":["../../src/pointer/flight.ts"],"names":[],"mappings":"AAAA,2HAA2H;AAK3H,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,uDAAuD;AACvD,wBAAgB,OAAO,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED,+FAA+F;AAC/F,wBAAgB,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,GAAG,KAAK,CAS9D;AAKD;;;GAGG;AACH,wBAAgB,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,KAAK,CAgB7C;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAW5E;AAOD;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,MAAM,CAG3I;AAED,0HAA0H;AAC1H,wBAAsB,UAAU,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAU/F"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/** How the pointer moves: where it aims on an element, the path it flies, and the page scrolled like a hand scrolls it. */
|
|
2
|
+
//
|
|
3
|
+
// The path is the screen recorder's: minimum jerk, timed by the distance, with a slight arc off the
|
|
4
|
+
// straight line, the way a hand moves a mouse.
|
|
5
|
+
/** Minimum jerk: 0 to 1, starting and ending still. */
|
|
6
|
+
export function minJerk(u) {
|
|
7
|
+
return u * u * u * (10 + u * (-15 + 6 * u));
|
|
8
|
+
}
|
|
9
|
+
/** How long a flight of `distance` px takes on a screen `width` px wide, in ms. */
|
|
10
|
+
export function flightMs(distance, width) {
|
|
11
|
+
return Math.round(480 + 560 * Math.min(1, distance / Math.max(1, width)));
|
|
12
|
+
}
|
|
13
|
+
/** The point at `u` (0 to 1) of the flight from `from` to `to`, bent a little off the line. */
|
|
14
|
+
export function along(from, to, u) {
|
|
15
|
+
const dx = to.x - from.x;
|
|
16
|
+
const dy = to.y - from.y;
|
|
17
|
+
const distance = Math.hypot(dx, dy);
|
|
18
|
+
if (u >= 1)
|
|
19
|
+
return { ...to };
|
|
20
|
+
const e = minJerk(u);
|
|
21
|
+
if (distance < 1)
|
|
22
|
+
return { x: from.x + dx * e, y: from.y + dy * e };
|
|
23
|
+
const lift = Math.sin(Math.PI * e) * Math.min(60, distance * 0.12);
|
|
24
|
+
return { x: from.x + dx * e - (dy / distance) * lift, y: from.y + dy * e + (dx / distance) * lift };
|
|
25
|
+
}
|
|
26
|
+
/** How far under the words the tip stands, in px. */
|
|
27
|
+
const UNDER = 3;
|
|
28
|
+
/**
|
|
29
|
+
* Where a person aims on an element: just under its first line of words, at their middle (at most
|
|
30
|
+
* 60 px in), so the pointer shows the words rather than covering them; else the middle of its box.
|
|
31
|
+
*/
|
|
32
|
+
export function aimOf(element) {
|
|
33
|
+
if (!element.matches("article, section, form, figure, img")) {
|
|
34
|
+
const document = element.ownerDocument;
|
|
35
|
+
const walker = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, {
|
|
36
|
+
acceptNode: (node) => ((node.textContent ?? "").trim() ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP),
|
|
37
|
+
});
|
|
38
|
+
const text = walker.nextNode();
|
|
39
|
+
if (text !== null) {
|
|
40
|
+
const range = document.createRange();
|
|
41
|
+
range.selectNodeContents(text);
|
|
42
|
+
const words = range.getClientRects()[0] ?? range.getBoundingClientRect();
|
|
43
|
+
if (words.width > 0)
|
|
44
|
+
return { x: words.left + Math.min(words.width / 2, 60), y: words.bottom + UNDER };
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const box = element.getBoundingClientRect();
|
|
48
|
+
return { x: box.left + box.width / 2, y: box.top + Math.min(box.height / 2, 60) };
|
|
49
|
+
}
|
|
50
|
+
/** Run `step(u)` from 0 to 1 over `ms`, once a frame; resolves at 1. */
|
|
51
|
+
export function animate(ms, step) {
|
|
52
|
+
const start = performance.now();
|
|
53
|
+
return new Promise((done) => {
|
|
54
|
+
const frame = (now) => {
|
|
55
|
+
const u = ms <= 0 ? 1 : Math.min(1, (now - start) / ms);
|
|
56
|
+
step(u);
|
|
57
|
+
if (u < 1)
|
|
58
|
+
requestAnimationFrame(frame);
|
|
59
|
+
else
|
|
60
|
+
done();
|
|
61
|
+
};
|
|
62
|
+
requestAnimationFrame(frame);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** How much of the screen's height an element may take and still be centred; a taller one shows its start. */
|
|
66
|
+
const TALL = 0.8;
|
|
67
|
+
/** Where a tall element's start stands, as a share of the screen's height. */
|
|
68
|
+
const START = 0.1;
|
|
69
|
+
/**
|
|
70
|
+
* The scroll at which `box` (as the screen shows it at `scrollY`) sits at `where` of a screen `height`
|
|
71
|
+
* tall, within a page `pageHeight` tall: its middle there, or, for one taller than most of the screen
|
|
72
|
+
* (a section on a phone), its start near the top, so what it begins with is what shows.
|
|
73
|
+
*/
|
|
74
|
+
export function scrollGoal(box, scrollY, height, pageHeight, where) {
|
|
75
|
+
const at = box.height > height * TALL ? box.top - height * START : box.top + box.height / 2 - height * where;
|
|
76
|
+
return Math.max(0, Math.min(pageHeight - height, scrollY + at));
|
|
77
|
+
}
|
|
78
|
+
/** Scroll the page, eased, until the element sits at `where` of the screen's height. Nothing when it is there already. */
|
|
79
|
+
export async function scrollOnto(element, where, still) {
|
|
80
|
+
const view = element.ownerDocument.defaultView;
|
|
81
|
+
if (view === null)
|
|
82
|
+
return;
|
|
83
|
+
const box = element.getBoundingClientRect();
|
|
84
|
+
const goal = scrollGoal(box, view.scrollY, view.innerHeight, element.ownerDocument.documentElement.scrollHeight, where);
|
|
85
|
+
const from = view.scrollY;
|
|
86
|
+
const dy = goal - from;
|
|
87
|
+
if (Math.abs(dy) < 8)
|
|
88
|
+
return;
|
|
89
|
+
const ms = still ? 0 : Math.round(520 + 680 * Math.min(1, Math.abs(dy) / (view.innerHeight * 2)));
|
|
90
|
+
await animate(ms, (u) => view.scrollTo(0, from + dy * minJerk(u)));
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=flight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"flight.js","sourceRoot":"","sources":["../../src/pointer/flight.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Hover without a mouse: the site's `:hover` rules mirrored onto an attribute, set where the pointer is. */
|
|
2
|
+
/** The pointer's look over an element: a hand on a link, an I-beam in a field, the arrow elsewhere. */
|
|
3
|
+
export type Kind = "arrow" | "hand" | "ibeam";
|
|
4
|
+
export declare class Hover {
|
|
5
|
+
#private;
|
|
6
|
+
constructor(document: Document);
|
|
7
|
+
/** Hover what is at (x, y) in the viewport; the kind of pointer it asks for. */
|
|
8
|
+
at(x: number, y: number): Kind;
|
|
9
|
+
/**
|
|
10
|
+
* Hover `under` and every ancestor, as a mouse resting on it does; the kind of pointer it asks for.
|
|
11
|
+
* The tip stands under the words it points at, so what it points at is hovered, not what lies beneath.
|
|
12
|
+
*/
|
|
13
|
+
on(under: Element | null): Kind;
|
|
14
|
+
/** Hover nothing. */
|
|
15
|
+
clear(): void;
|
|
16
|
+
close(): void;
|
|
17
|
+
}
|
|
18
|
+
/** Every `:hover` rule the page's sheets can be read for, rewritten. A sheet of another origin cannot be read, and is skipped. */
|
|
19
|
+
export declare function mirrored(document: Document): string;
|
|
20
|
+
//# sourceMappingURL=hover.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.d.ts","sourceRoot":"","sources":["../../src/pointer/hover.ts"],"names":[],"mappings":"AAAA,6GAA6G;AAU7G,uGAAuG;AACvG,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,MAAM,GAAG,OAAO,CAAC;AAE9C,qBAAa,KAAK;;IAKhB,YAAY,QAAQ,EAAE,QAAQ,EAM7B;IAED,gFAAgF;IAChF,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,IAAI,CAE7B;IAED;;;OAGG;IACH,EAAE,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,GAAG,IAAI,CAO9B;IAED,qBAAqB;IACrB,KAAK,IAAI,IAAI,CAGZ;IAED,KAAK,IAAI,IAAI,CAGZ;CACF;AAsBD,kIAAkI;AAClI,wBAAgB,QAAQ,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAoBnD"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** Hover without a mouse: the site's `:hover` rules mirrored onto an attribute, set where the pointer is. */
|
|
2
|
+
//
|
|
3
|
+
// A script cannot move the real mouse, so `:hover` never matches under a drawn pointer. Every rule
|
|
4
|
+
// of the page's own style sheets that says `:hover` is copied with `[data-pc-hover]` in its place,
|
|
5
|
+
// and the attribute goes on the element under the tip and every ancestor, as a real hover does.
|
|
6
|
+
// `pointerenter` and `mouseenter` are sent too, for a site whose code listens for them.
|
|
7
|
+
/** The attribute a mirrored hover matches. */
|
|
8
|
+
const HOVERED = "data-pc-hover";
|
|
9
|
+
export class Hover {
|
|
10
|
+
#document;
|
|
11
|
+
#mirror;
|
|
12
|
+
#on = [];
|
|
13
|
+
constructor(document) {
|
|
14
|
+
this.#document = document;
|
|
15
|
+
this.#mirror = document.createElement("style");
|
|
16
|
+
this.#mirror.dataset["pinecallSkip"] = "";
|
|
17
|
+
this.#mirror.textContent = mirrored(document);
|
|
18
|
+
document.head.append(this.#mirror);
|
|
19
|
+
}
|
|
20
|
+
/** Hover what is at (x, y) in the viewport; the kind of pointer it asks for. */
|
|
21
|
+
at(x, y) {
|
|
22
|
+
return this.on(this.#document.elementFromPoint(x, y));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Hover `under` and every ancestor, as a mouse resting on it does; the kind of pointer it asks for.
|
|
26
|
+
* The tip stands under the words it points at, so what it points at is hovered, not what lies beneath.
|
|
27
|
+
*/
|
|
28
|
+
on(under) {
|
|
29
|
+
const chain = [];
|
|
30
|
+
for (let node = under; node !== null && node !== this.#document.body; node = node.parentElement)
|
|
31
|
+
chain.push(node);
|
|
32
|
+
for (const node of this.#on)
|
|
33
|
+
if (!chain.includes(node))
|
|
34
|
+
left(node);
|
|
35
|
+
for (const node of chain)
|
|
36
|
+
if (!this.#on.includes(node))
|
|
37
|
+
entered(node);
|
|
38
|
+
this.#on = chain;
|
|
39
|
+
return under === null ? "arrow" : kindOf(under);
|
|
40
|
+
}
|
|
41
|
+
/** Hover nothing. */
|
|
42
|
+
clear() {
|
|
43
|
+
for (const node of this.#on)
|
|
44
|
+
left(node);
|
|
45
|
+
this.#on = [];
|
|
46
|
+
}
|
|
47
|
+
close() {
|
|
48
|
+
this.clear();
|
|
49
|
+
this.#mirror.remove();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function entered(node) {
|
|
53
|
+
node.setAttribute(HOVERED, "");
|
|
54
|
+
node.dispatchEvent(new PointerEvent("pointerenter"));
|
|
55
|
+
node.dispatchEvent(new MouseEvent("mouseenter"));
|
|
56
|
+
}
|
|
57
|
+
function left(node) {
|
|
58
|
+
node.removeAttribute(HOVERED);
|
|
59
|
+
node.dispatchEvent(new PointerEvent("pointerleave"));
|
|
60
|
+
node.dispatchEvent(new MouseEvent("mouseleave"));
|
|
61
|
+
}
|
|
62
|
+
function kindOf(element) {
|
|
63
|
+
const asked = element.ownerDocument.defaultView?.getComputedStyle(element).cursor ?? "auto";
|
|
64
|
+
if (asked === "pointer")
|
|
65
|
+
return "hand";
|
|
66
|
+
if (asked === "text")
|
|
67
|
+
return "ibeam";
|
|
68
|
+
if (asked === "auto" && element.matches("input:not([type=checkbox]):not([type=radio]):not([type=range]), textarea, [contenteditable=true]"))
|
|
69
|
+
return "ibeam";
|
|
70
|
+
return "arrow";
|
|
71
|
+
}
|
|
72
|
+
/** Every `:hover` rule the page's sheets can be read for, rewritten. A sheet of another origin cannot be read, and is skipped. */
|
|
73
|
+
export function mirrored(document) {
|
|
74
|
+
const out = [];
|
|
75
|
+
const visit = (rules, wrap) => {
|
|
76
|
+
for (const rule of rules) {
|
|
77
|
+
if (rule instanceof CSSMediaRule)
|
|
78
|
+
visit(rule.cssRules, (body) => wrap(`@media ${rule.media.mediaText} { ${body} }`));
|
|
79
|
+
else if (rule instanceof CSSStyleRule && rule.selectorText.includes(":hover")) {
|
|
80
|
+
out.push(wrap(`${rule.selectorText.replaceAll(":hover", `[${HOVERED}]`)} { ${rule.style.cssText} }`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
for (const sheet of document.styleSheets) {
|
|
85
|
+
let rules;
|
|
86
|
+
try {
|
|
87
|
+
rules = sheet.cssRules;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
visit(rules, (body) => body);
|
|
93
|
+
}
|
|
94
|
+
return out.join("\n");
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=hover.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hover.js","sourceRoot":"","sources":["../../src/pointer/hover.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/** The walks a visitor may play with no call: a small button beside the resting face, its pills fanned in an arc round it. */
|
|
2
|
+
import type { Point } from "./flight.js";
|
|
3
|
+
/** One walk as its pill shows it. */
|
|
4
|
+
export interface MenuItem {
|
|
5
|
+
title: string;
|
|
6
|
+
ms: number;
|
|
7
|
+
}
|
|
8
|
+
export interface MenuOptions {
|
|
9
|
+
dock: "right" | "left";
|
|
10
|
+
/** What the button is called for a screen reader: "Walks". */
|
|
11
|
+
label: string;
|
|
12
|
+
/** Where the resting face's middle is, and its radius, now. */
|
|
13
|
+
face: () => {
|
|
14
|
+
at: Point;
|
|
15
|
+
radius: number;
|
|
16
|
+
};
|
|
17
|
+
/** A walk picked, by its place in the list. */
|
|
18
|
+
onPick: (index: number) => void;
|
|
19
|
+
/** What the button that shows the next pills is called for a screen reader: "More walks". */
|
|
20
|
+
more: string;
|
|
21
|
+
}
|
|
22
|
+
/** How long a walk offered alone stays beside the face, in ms. */
|
|
23
|
+
export declare const OFFERED_MS = 5000;
|
|
24
|
+
/** How many pills show at once; the rest come a page at a time, from a small button under them. */
|
|
25
|
+
export declare const PAGE = 4;
|
|
26
|
+
/** How far apart two pills stand, in px: a pill's height and a gap of 6, so no two ever overlap. */
|
|
27
|
+
export declare const STEP: number;
|
|
28
|
+
/**
|
|
29
|
+
* Where each of `count` pills sits beside a face at `at` of `radius`, the end nearest it: rising a
|
|
30
|
+
* step at a time from beside the face, each a slant nearer it than the one below, the highest a gap
|
|
31
|
+
* from its edge. On an arc wide enough for the highest, the lowest stood far out from the face.
|
|
32
|
+
*/
|
|
33
|
+
export declare function fanned(count: number, at: Point, radius: number, dock: "right" | "left"): Point[];
|
|
34
|
+
export declare class Menu {
|
|
35
|
+
#private;
|
|
36
|
+
constructor(document: Document, host: (element: Element) => void, options: MenuOptions);
|
|
37
|
+
/** The walks to offer; none hides the button. */
|
|
38
|
+
offer(items: MenuItem[]): void;
|
|
39
|
+
/** Whether the button may show now: not during a call, nor while a walk plays. */
|
|
40
|
+
available(on: boolean): void;
|
|
41
|
+
/** Offer one walk alone, beside the face, for `ms`, while the button shows and the pills are folded. */
|
|
42
|
+
suggest(index: number, ms?: number): void;
|
|
43
|
+
/** Fan the pills out, or fold them back. */
|
|
44
|
+
toggle(open: boolean): void;
|
|
45
|
+
get open(): boolean;
|
|
46
|
+
close(): void;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../src/pointer/menu.ts"],"names":[],"mappings":"AAAA,8HAA8H;AAS9H,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,aAAa,CAAC;AA6BzC,qCAAqC;AACrC,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,8DAA8D;IAC9D,KAAK,EAAE,MAAM,CAAC;IACd,+DAA+D;IAC/D,IAAI,EAAE,MAAM;QAAE,EAAE,EAAE,KAAK,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1C,+CAA+C;IAC/C,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,6FAA6F;IAC7F,IAAI,EAAE,MAAM,CAAC;CACd;AAED,kEAAkE;AAClE,eAAO,MAAM,UAAU,OAAO,CAAC;AAE/B,mGAAmG;AACnG,eAAO,MAAM,IAAI,IAAI,CAAC;AAEtB,oGAAoG;AACpG,eAAO,MAAM,IAAI,QAAW,CAAC;AAM7B;;;;GAIG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,KAAK,EAAE,CAOhG;AAED,qBAAa,IAAI;;IAwBf,YAAY,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,OAAO,EAAE,WAAW,EAiCrF;IAED,iDAAiD;IACjD,KAAK,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,IAAI,CAsB7B;IAED,kFAAkF;IAClF,SAAS,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAI3B;IAED,wGAAwG;IACxG,OAAO,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,SAAa,GAAG,IAAI,CAQ5C;IAED,4CAA4C;IAC5C,MAAM,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAO1B;IAwBD,IAAI,IAAI,IAAI,OAAO,CAElB;IA8CD,KAAK,IAAI,IAAI,CASZ;CACF"}
|