@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,345 @@
|
|
|
1
|
+
/** The tutor as one more person on the page: a pointer of its own, its face beside the tip, what it says beside the face. */
|
|
2
|
+
//
|
|
3
|
+
// One fixed layer over the page that takes no clicks but on the face. Resting, the face sits in a
|
|
4
|
+
// corner like the widget's button; touring, it shrinks beside the tip of an arrow in its colour,
|
|
5
|
+
// the way a multiplayer cursor carries a name tag. The spotlight and the "here" ring live there too,
|
|
6
|
+
// and, in the corner it rests in, what the visitor is saying and the buttons: captions, with hang-up
|
|
7
|
+
// in a call and stop in a walk. They stand beside the resting face, and slide into its corner while
|
|
8
|
+
// it tours, back aside as it comes home. Every colour is a custom property: --pc-face
|
|
9
|
+
// (the dot and the arrow), --pc-face-ring, --pc-bubble, --pc-bubble-ink, --pc-heard, --pc-heard-ink,
|
|
10
|
+
// --pc-hang.
|
|
11
|
+
const STYLE = `
|
|
12
|
+
.pc-layer { position: fixed; inset: 0; pointer-events: none; z-index: 2147483000; }
|
|
13
|
+
.pc-person { position: fixed; left: 0; top: 0; z-index: 2; will-change: transform; }
|
|
14
|
+
.pc-arrow { position: absolute; left: -3px; top: -2px; width: 26px; height: 26px; opacity: 0; transition: opacity .25s; filter: drop-shadow(0 2px 3px rgb(0 0 0 / .35)); }
|
|
15
|
+
.pc-person.pc-touring .pc-arrow { opacity: 1; }
|
|
16
|
+
.pc-arrow path { display: none; }
|
|
17
|
+
.pc-arrow[data-kind="arrow"] .pc-kind-arrow, .pc-arrow[data-kind="hand"] .pc-kind-hand, .pc-arrow[data-kind="ibeam"] .pc-kind-ibeam { display: inline; }
|
|
18
|
+
.pc-face { position: absolute; width: var(--pc-dot-size, 64px); height: var(--pc-dot-size, 64px); left: calc(var(--pc-dot-size, 64px) / -2); top: calc(var(--pc-dot-size, 64px) / -2);
|
|
19
|
+
transition: width .45s cubic-bezier(.2,.8,.2,1), height .45s cubic-bezier(.2,.8,.2,1), left .45s cubic-bezier(.2,.8,.2,1), top .45s cubic-bezier(.2,.8,.2,1);
|
|
20
|
+
pointer-events: auto; cursor: pointer; border-radius: 50%; }
|
|
21
|
+
.pc-face:focus-visible { outline: 2px solid var(--pc-face-ring, #44a785); outline-offset: 3px; }
|
|
22
|
+
.pc-person.pc-touring .pc-face { width: 40px; height: 40px; left: 14px; top: 16px; }
|
|
23
|
+
.pc-progress { position: absolute; width: calc(var(--pc-dot-size, 64px) + 12px); height: calc(var(--pc-dot-size, 64px) + 12px);
|
|
24
|
+
left: calc(var(--pc-dot-size, 64px) / -2 - 6px); top: calc(var(--pc-dot-size, 64px) / -2 - 6px); transform: rotate(-90deg); pointer-events: none;
|
|
25
|
+
transition: width .45s cubic-bezier(.2,.8,.2,1), height .45s cubic-bezier(.2,.8,.2,1), left .45s cubic-bezier(.2,.8,.2,1), top .45s cubic-bezier(.2,.8,.2,1); }
|
|
26
|
+
.pc-progress[hidden] { display: none; }
|
|
27
|
+
.pc-progress circle { fill: none; stroke: var(--pc-face, #1f6b55); stroke-width: 2.5; stroke-linecap: round; }
|
|
28
|
+
.pc-person.pc-touring .pc-progress { width: 52px; height: 52px; left: 8px; top: 10px; }
|
|
29
|
+
.pc-bubble { position: absolute; width: max-content; max-width: min(270px, 70vw); right: -30px; bottom: 44px; padding: 9px 13px;
|
|
30
|
+
background: var(--pc-bubble, #fff); color: var(--pc-bubble-ink, #17201c); border: 1px solid rgb(0 0 0 / .08); border-radius: 16px 16px 4px 16px;
|
|
31
|
+
font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; box-shadow: 0 10px 28px rgb(0 0 0 / .14); transition: opacity .2s, transform .2s; transform-origin: bottom right; }
|
|
32
|
+
.pc-bubble[hidden] { display: block !important; opacity: 0; transform: scale(.94); }
|
|
33
|
+
.pc-bubble .pc-coming { opacity: .38; }
|
|
34
|
+
.pc-person.pc-touring .pc-bubble { right: auto; bottom: auto; left: 62px; top: 14px; border-radius: 4px 16px 16px 16px; transform-origin: top left; }
|
|
35
|
+
.pc-person.pc-touring.pc-west .pc-face { left: -54px; }
|
|
36
|
+
.pc-person.pc-touring.pc-west .pc-progress { left: -60px; }
|
|
37
|
+
.pc-person.pc-touring.pc-west .pc-bubble { left: auto; right: 62px; border-radius: 16px 4px 16px 16px; transform-origin: top right; }
|
|
38
|
+
.pc-person.pc-touring.pc-up .pc-face { top: -56px; }
|
|
39
|
+
.pc-person.pc-touring.pc-up .pc-progress { top: -62px; }
|
|
40
|
+
.pc-person.pc-touring.pc-up .pc-bubble { top: auto; bottom: 14px; border-radius: 16px 16px 16px 4px; transform-origin: bottom left; }
|
|
41
|
+
.pc-person.pc-touring.pc-west.pc-up .pc-bubble { border-radius: 16px 16px 4px 16px; transform-origin: bottom right; }
|
|
42
|
+
.pc-person.pc-left:not(.pc-touring) .pc-bubble { right: auto; left: -30px; border-radius: 16px 16px 16px 4px; transform-origin: bottom left; }
|
|
43
|
+
.pc-heard { position: fixed; right: 22px; bottom: 96px; width: max-content; max-width: min(270px, 70vw); padding: 9px 13px;
|
|
44
|
+
background: var(--pc-heard, #e7f3ee); color: var(--pc-heard-ink, #17201c); border-radius: 16px 16px 4px 16px;
|
|
45
|
+
font: 14px/1.45 system-ui, -apple-system, "Segoe UI", sans-serif; box-shadow: 0 10px 28px rgb(0 0 0 / .12); transition: opacity .2s; }
|
|
46
|
+
.pc-heard[hidden] { display: block !important; opacity: 0; }
|
|
47
|
+
.pc-calling { position: fixed; right: 92px; bottom: 35px; display: flex; gap: 8px; pointer-events: auto; transition: right .35s cubic-bezier(.2,.8,.2,1), left .35s cubic-bezier(.2,.8,.2,1); }
|
|
48
|
+
.pc-layer.pc-away .pc-calling { right: 22px; }
|
|
49
|
+
.pc-calling[hidden] { display: none; }
|
|
50
|
+
.pc-calling button { all: unset; box-sizing: border-box; height: 34px; min-width: 34px; padding: 0 10px; border-radius: 999px; cursor: pointer;
|
|
51
|
+
font: 600 12px/34px system-ui, -apple-system, "Segoe UI", sans-serif; text-align: center; box-shadow: 0 4px 12px rgb(0 0 0 / .18); }
|
|
52
|
+
.pc-calling button:focus-visible { outline: 2px solid var(--pc-face-ring, #44a785); outline-offset: 2px; }
|
|
53
|
+
.pc-calling .pc-cc, .pc-calling .pc-pause, .pc-calling .pc-stop { display: grid; place-items: center; padding: 0; background: var(--pc-bubble, #fff); color: var(--pc-bubble-ink, #17201c); }
|
|
54
|
+
.pc-calling svg { display: block; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
55
|
+
.pc-calling .pc-stop svg { fill: currentColor; stroke: none; }
|
|
56
|
+
.pc-pause .pc-go { display: none; fill: currentColor; stroke: none; }
|
|
57
|
+
.pc-pause[aria-pressed="true"] .pc-go { display: inline; }
|
|
58
|
+
.pc-pause[aria-pressed="true"] .pc-hold { display: none; }
|
|
59
|
+
.pc-cc .pc-off { display: none; }
|
|
60
|
+
.pc-cc[aria-pressed="false"] { opacity: .6; }
|
|
61
|
+
.pc-cc[aria-pressed="false"] .pc-off { display: inline; }
|
|
62
|
+
.pc-calling button[hidden] { display: none; }
|
|
63
|
+
.pc-calling .pc-hang { background: var(--pc-hang, #d6453d); color: #fff; font-size: 16px; }
|
|
64
|
+
.pc-play { position: fixed; width: 26px; height: 26px; border-radius: 50%; pointer-events: auto; cursor: pointer; border: 0; padding: 0;
|
|
65
|
+
background: var(--pc-face, #1f6b55); color: #fff; font: 11px/26px system-ui, sans-serif; text-align: center; box-shadow: 0 3px 10px rgb(0 0 0 / .25); }
|
|
66
|
+
.pc-play[hidden] { display: none; }
|
|
67
|
+
.pc-play:focus-visible { outline: 2px solid var(--pc-face-ring, #44a785); outline-offset: 2px; }
|
|
68
|
+
.pc-layer.pc-left .pc-heard { right: auto; left: 22px; border-radius: 16px 16px 16px 4px; }
|
|
69
|
+
.pc-layer.pc-left .pc-calling { right: auto; left: 92px; flex-direction: row-reverse; }
|
|
70
|
+
.pc-layer.pc-left.pc-away .pc-calling { left: 22px; }
|
|
71
|
+
.pc-spot { position: fixed; border-radius: 16px; box-shadow: 0 0 0 9999px rgb(10 18 16 / .34), 0 0 0 3px var(--pc-face-ring, #44a785); opacity: 0; transition: opacity .3s; }
|
|
72
|
+
.pc-spot.pc-on { opacity: 1; }
|
|
73
|
+
.pc-ring { position: fixed; width: 18px; height: 18px; margin: -9px 0 0 -9px; border-radius: 50%; border: 2px solid var(--pc-face-ring, #44a785); opacity: 0; }
|
|
74
|
+
.pc-ring.pc-go { animation: pc-ring 1.1s ease-out 2; }
|
|
75
|
+
@keyframes pc-ring { 0% { transform: scale(.6); opacity: .9; } 100% { transform: scale(3.2); opacity: 0; } }
|
|
76
|
+
@media (prefers-reduced-motion: reduce) { .pc-face, .pc-bubble, .pc-heard, .pc-spot { transition: none; } .pc-ring.pc-go { animation: none; opacity: .9; } }
|
|
77
|
+
`;
|
|
78
|
+
// Captions: lines of words in a frame, struck through while off. Pause: two bars, a triangle while held. Stop: a square.
|
|
79
|
+
const CAPTIONS = (size) => `<svg viewBox="0 0 24 24" width="${size}" height="${size}" aria-hidden="true"><rect x="3" y="5.5" width="18" height="13" rx="3"/><path d="M7 10.5h4M13.5 10.5h3.5M7 14h6.5M16 14h1"/><path class="pc-off" d="M4 4l16 16"/></svg>`;
|
|
80
|
+
const PAUSE = `<svg viewBox="0 0 24 24" width="12" height="12" aria-hidden="true"><path class="pc-hold" d="M8.5 6v12M15.5 6v12" stroke-width="3"/><path class="pc-go" d="M8 5.5v13l10-6.5Z"/></svg>`;
|
|
81
|
+
const STOP = `<svg viewBox="0 0 24 24" width="10" height="10" aria-hidden="true"><rect x="5" y="5" width="14" height="14" rx="2.5"/></svg>`;
|
|
82
|
+
// The arrow is the screen recorder's (~/ss/screen-demo/build.mjs), in the face's colour; the hand and the I-beam are drawn to match.
|
|
83
|
+
const ARROW = `<svg class="pc-arrow" viewBox="0 0 26 26" data-kind="arrow" aria-hidden="true">
|
|
84
|
+
<path class="pc-kind-arrow" d="M3 2 L3 21 L8 16.5 L11.4 24 L14.6 22.6 L11.3 15.3 L18 15.3 Z" fill="var(--pc-face, #1f6b55)" stroke="#fff" stroke-width="1.6" stroke-linejoin="round"/>
|
|
85
|
+
<path class="pc-kind-hand" d="M8 3.5c0-1.1 1.8-1.1 1.8 0V11h.6V9.2c0-1.1 1.8-1.1 1.8 0V11h.6V9.8c0-1.1 1.8-1.1 1.8 0V11.4h.6v-.8c0-1.1 1.8-1.1 1.8 0V16c0 3.6-2.2 6.5-5.6 6.5-2.5 0-3.8-1.2-5-3.2l-2.6-4.4c-.6-1 .7-2 1.6-1.2L8 15.4Z" fill="var(--pc-face, #1f6b55)" stroke="#fff" stroke-width="1.4" stroke-linejoin="round"/>
|
|
86
|
+
<path class="pc-kind-ibeam" d="M9 3h5M9 23h5M11.5 3v20" fill="none" stroke="var(--pc-face, #1f6b55)" stroke-width="2.4" stroke-linecap="round"/>
|
|
87
|
+
</svg>`;
|
|
88
|
+
/** How far the face and its words reach from the tip, across and down, in px; above, they end this far over it. */
|
|
89
|
+
const REACH = { across: 340, down: 96, over: 14 };
|
|
90
|
+
/** How much of the box being explained the face may touch: the tip stands in its first line. */
|
|
91
|
+
const TOUCH = 6;
|
|
92
|
+
const SIDES = [
|
|
93
|
+
{ west: false, up: false },
|
|
94
|
+
{ west: true, up: false },
|
|
95
|
+
{ west: false, up: true },
|
|
96
|
+
{ west: true, up: true },
|
|
97
|
+
];
|
|
98
|
+
/** The first side whose reach fits the screen and covers nothing of `avoid`; else the first that fits; else below and right. */
|
|
99
|
+
export function sideOf(tip, avoid, width, height) {
|
|
100
|
+
const reach = (side) => ({
|
|
101
|
+
left: side.west ? tip.x - REACH.across : tip.x + 8,
|
|
102
|
+
right: side.west ? tip.x - 8 : tip.x + REACH.across,
|
|
103
|
+
top: side.up ? tip.y - REACH.down : tip.y + 8,
|
|
104
|
+
bottom: side.up ? tip.y - REACH.over : tip.y + REACH.down,
|
|
105
|
+
});
|
|
106
|
+
const fits = (side) => {
|
|
107
|
+
const box = reach(side);
|
|
108
|
+
return box.left >= 0 && box.right <= width && box.top >= 0 && box.bottom <= height;
|
|
109
|
+
};
|
|
110
|
+
const clear = (side) => {
|
|
111
|
+
const box = reach(side);
|
|
112
|
+
if (avoid === null)
|
|
113
|
+
return true;
|
|
114
|
+
return box.right <= avoid.left + TOUCH || box.left >= avoid.right - TOUCH || box.bottom <= avoid.top + TOUCH || box.top >= avoid.bottom - TOUCH;
|
|
115
|
+
};
|
|
116
|
+
return SIDES.find((side) => fits(side) && clear(side)) ?? SIDES.find(fits) ?? { west: false, up: false };
|
|
117
|
+
}
|
|
118
|
+
export class Person {
|
|
119
|
+
/** The element the face is drawn into. */
|
|
120
|
+
face;
|
|
121
|
+
#document;
|
|
122
|
+
#style;
|
|
123
|
+
#layer;
|
|
124
|
+
#person;
|
|
125
|
+
#arrow;
|
|
126
|
+
#bubble;
|
|
127
|
+
#heard;
|
|
128
|
+
#calling;
|
|
129
|
+
#cc;
|
|
130
|
+
#stop;
|
|
131
|
+
#pause;
|
|
132
|
+
#hang;
|
|
133
|
+
#play;
|
|
134
|
+
#offered = null;
|
|
135
|
+
#spot;
|
|
136
|
+
#ring;
|
|
137
|
+
#dock;
|
|
138
|
+
#at;
|
|
139
|
+
#shown = "";
|
|
140
|
+
#heardShown = "";
|
|
141
|
+
constructor(document, options) {
|
|
142
|
+
this.#document = document;
|
|
143
|
+
this.#dock = options.dock;
|
|
144
|
+
this.#style = document.createElement("style");
|
|
145
|
+
this.#style.textContent = STYLE;
|
|
146
|
+
this.#layer = document.createElement("div");
|
|
147
|
+
this.#layer.className = "pc-layer";
|
|
148
|
+
this.#layer.dataset["pinecallSkip"] = "";
|
|
149
|
+
if (options.color !== undefined)
|
|
150
|
+
this.#layer.style.setProperty("--pc-face", options.color);
|
|
151
|
+
this.#layer.innerHTML = `<div class="pc-spot"></div><div class="pc-ring"></div><div class="pc-person">${ARROW}<svg class="pc-progress" viewBox="0 0 100 100" hidden aria-hidden="true"><circle cx="50" cy="50" r="47" pathLength="100" stroke-dasharray="0 100"></circle></svg><div class="pc-face" role="button" tabindex="0"></div><div class="pc-bubble" role="status" hidden></div></div><div class="pc-heard" role="status" hidden></div><div class="pc-calling" hidden><button type="button" class="pc-cc" aria-pressed="true">${CAPTIONS(17)}</button><button type="button" class="pc-pause" aria-pressed="false" hidden>${PAUSE}</button><button type="button" class="pc-stop" hidden>${STOP}</button><button type="button" class="pc-hang">✕</button></div><button type="button" class="pc-play" hidden>▶</button>`;
|
|
152
|
+
this.#spot = this.#part(".pc-spot");
|
|
153
|
+
this.#ring = this.#part(".pc-ring");
|
|
154
|
+
this.#person = this.#part(".pc-person");
|
|
155
|
+
this.#arrow = this.#part(".pc-arrow");
|
|
156
|
+
this.face = this.#part(".pc-face");
|
|
157
|
+
this.#bubble = this.#part(".pc-bubble");
|
|
158
|
+
this.#heard = this.#part(".pc-heard");
|
|
159
|
+
this.#calling = this.#part(".pc-calling");
|
|
160
|
+
this.#cc = this.#part(".pc-cc");
|
|
161
|
+
this.#stop = this.#part(".pc-stop");
|
|
162
|
+
this.#stop.setAttribute("aria-label", options.stop.label);
|
|
163
|
+
this.#stop.title = options.stop.label;
|
|
164
|
+
this.#stop.addEventListener("click", options.stop.onClick);
|
|
165
|
+
this.#pause = this.#part(".pc-pause");
|
|
166
|
+
this.#pause.setAttribute("aria-label", options.pause.label);
|
|
167
|
+
this.#pause.title = options.pause.label;
|
|
168
|
+
this.#pause.addEventListener("click", options.pause.onClick);
|
|
169
|
+
this.#play = this.#part(".pc-play");
|
|
170
|
+
this.#play.setAttribute("aria-label", options.explain.label);
|
|
171
|
+
this.#play.title = options.explain.label;
|
|
172
|
+
this.#play.addEventListener("click", () => {
|
|
173
|
+
if (this.#offered !== null)
|
|
174
|
+
options.explain.onClick(this.#offered);
|
|
175
|
+
});
|
|
176
|
+
this.#person.classList.toggle("pc-left", options.dock === "left");
|
|
177
|
+
this.#layer.classList.toggle("pc-left", options.dock === "left");
|
|
178
|
+
this.#hang = this.#part(".pc-hang");
|
|
179
|
+
this.#hang.setAttribute("aria-label", options.hangUp.label);
|
|
180
|
+
this.#hang.title = options.hangUp.label;
|
|
181
|
+
this.#hang.addEventListener("click", options.hangUp.onClick);
|
|
182
|
+
this.#cc.hidden = options.captions === null;
|
|
183
|
+
if (options.captions !== null) {
|
|
184
|
+
this.#cc.setAttribute("aria-label", options.captions.label);
|
|
185
|
+
this.#cc.title = options.captions.label;
|
|
186
|
+
this.#cc.addEventListener("click", options.captions.onClick);
|
|
187
|
+
}
|
|
188
|
+
this.face.setAttribute("aria-label", options.label);
|
|
189
|
+
this.face.title = options.label;
|
|
190
|
+
this.face.addEventListener("click", options.onClick);
|
|
191
|
+
this.face.addEventListener("keydown", (event) => {
|
|
192
|
+
if (event.key !== "Enter" && event.key !== " ")
|
|
193
|
+
return;
|
|
194
|
+
event.preventDefault();
|
|
195
|
+
options.onClick();
|
|
196
|
+
});
|
|
197
|
+
document.head.append(this.#style);
|
|
198
|
+
document.body.append(this.#layer);
|
|
199
|
+
this.#at = this.dock();
|
|
200
|
+
this.place(this.#at);
|
|
201
|
+
}
|
|
202
|
+
/** Where the face rests: its middle, 52 px in from the corner. */
|
|
203
|
+
dock() {
|
|
204
|
+
const view = this.#document.defaultView;
|
|
205
|
+
const width = view?.innerWidth ?? 0;
|
|
206
|
+
const height = view?.innerHeight ?? 0;
|
|
207
|
+
return { x: this.#dock === "right" ? width - 52 : 52, y: height - 52 };
|
|
208
|
+
}
|
|
209
|
+
/** The tip, now. */
|
|
210
|
+
get at() {
|
|
211
|
+
return this.#at;
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Put the tip at a point of the viewport, the face and its words on `side`, or on the side of it that
|
|
215
|
+
* fits the screen and covers nothing of `avoid`, the box being explained: right or left, below or above.
|
|
216
|
+
*/
|
|
217
|
+
place(point, avoid = null, side) {
|
|
218
|
+
this.#at = point;
|
|
219
|
+
this.#person.style.transform = `translate(${point.x.toFixed(1)}px, ${point.y.toFixed(1)}px)`;
|
|
220
|
+
const view = this.#document.defaultView;
|
|
221
|
+
side ??= sideOf(point, avoid, view?.innerWidth ?? 0, view?.innerHeight ?? 0);
|
|
222
|
+
this.#person.classList.toggle("pc-west", side.west);
|
|
223
|
+
this.#person.classList.toggle("pc-up", side.up);
|
|
224
|
+
}
|
|
225
|
+
/** Out of the corner with the arrow showing, the buttons sliding into it; or back in it, and they aside. */
|
|
226
|
+
tour(on) {
|
|
227
|
+
this.#person.classList.toggle("pc-touring", on);
|
|
228
|
+
this.#layer.classList.toggle("pc-away", on);
|
|
229
|
+
}
|
|
230
|
+
get touring() {
|
|
231
|
+
return this.#person.classList.contains("pc-touring");
|
|
232
|
+
}
|
|
233
|
+
kind(kind) {
|
|
234
|
+
this.#arrow.dataset["kind"] = kind;
|
|
235
|
+
}
|
|
236
|
+
/** What it is saying: the words said, and the ones still to come; null hides the bubble. */
|
|
237
|
+
say(lit, coming = "") {
|
|
238
|
+
const shown = lit === null ? "" : `${lit}\u0000${coming}`;
|
|
239
|
+
if (shown === this.#shown)
|
|
240
|
+
return;
|
|
241
|
+
this.#shown = shown;
|
|
242
|
+
this.#bubble.hidden = lit === null;
|
|
243
|
+
if (lit === null)
|
|
244
|
+
return;
|
|
245
|
+
const later = this.#document.createElement("span");
|
|
246
|
+
later.className = "pc-coming";
|
|
247
|
+
later.textContent = coming;
|
|
248
|
+
this.#bubble.replaceChildren(this.#document.createTextNode(lit), later);
|
|
249
|
+
}
|
|
250
|
+
/** What the visitor is saying, in the corner; null hides it. */
|
|
251
|
+
heard(text) {
|
|
252
|
+
if ((text ?? "") === this.#heardShown)
|
|
253
|
+
return;
|
|
254
|
+
this.#heardShown = text ?? "";
|
|
255
|
+
this.#heard.hidden = text === null;
|
|
256
|
+
if (text !== null)
|
|
257
|
+
this.#heard.textContent = text;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* The corner's buttons: captions and hang-up while a call is on, captions, pause and stop while a
|
|
261
|
+
* walk plays; captions pressed while they show, pause while the walk is held. Called every frame, so
|
|
262
|
+
* it writes only what changed.
|
|
263
|
+
*/
|
|
264
|
+
buttons(call, walk, captioned, paused = false) {
|
|
265
|
+
const show = (element, on) => {
|
|
266
|
+
if (element.hidden === on)
|
|
267
|
+
element.hidden = !on;
|
|
268
|
+
};
|
|
269
|
+
show(this.#calling, call || walk);
|
|
270
|
+
show(this.#hang, call);
|
|
271
|
+
show(this.#stop, walk && !call);
|
|
272
|
+
show(this.#pause, walk && !call);
|
|
273
|
+
const press = (element, on) => {
|
|
274
|
+
if (element.getAttribute("aria-pressed") !== String(on))
|
|
275
|
+
element.setAttribute("aria-pressed", String(on));
|
|
276
|
+
};
|
|
277
|
+
press(this.#cc, captioned);
|
|
278
|
+
press(this.#pause, paused);
|
|
279
|
+
}
|
|
280
|
+
/** The ▶ at the top right of `element`, or none. Called every frame while shown, so it follows the element. */
|
|
281
|
+
offer(element) {
|
|
282
|
+
this.#offered = element;
|
|
283
|
+
this.#play.hidden = element === null;
|
|
284
|
+
if (element === null)
|
|
285
|
+
return;
|
|
286
|
+
const box = element.getBoundingClientRect();
|
|
287
|
+
const width = this.#document.defaultView?.innerWidth ?? 0;
|
|
288
|
+
this.#play.style.left = `${Math.min(box.right - 34, width - 40).toFixed(0)}px`;
|
|
289
|
+
this.#play.style.top = `${Math.max(box.top + 8, 8).toFixed(0)}px`;
|
|
290
|
+
}
|
|
291
|
+
/** The spotlight on an element's box, or off. Called every frame while on, so it follows the element. */
|
|
292
|
+
spot(element) {
|
|
293
|
+
this.#spot.classList.toggle("pc-on", element !== null);
|
|
294
|
+
if (element === null)
|
|
295
|
+
return;
|
|
296
|
+
const box = element.getBoundingClientRect();
|
|
297
|
+
const pad = 8;
|
|
298
|
+
const radius = Number.parseFloat(this.#document.defaultView?.getComputedStyle(element).borderRadius ?? "") || 10;
|
|
299
|
+
Object.assign(this.#spot.style, {
|
|
300
|
+
left: `${box.left - pad}px`,
|
|
301
|
+
top: `${box.top - pad}px`,
|
|
302
|
+
width: `${box.width + pad * 2}px`,
|
|
303
|
+
height: `${box.height + pad * 2}px`,
|
|
304
|
+
borderRadius: `${radius + pad}px`,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
/** "Here": a ring where the tip is, twice. */
|
|
308
|
+
ring() {
|
|
309
|
+
this.#ring.style.left = `${this.#at.x}px`;
|
|
310
|
+
this.#ring.style.top = `${this.#at.y}px`;
|
|
311
|
+
this.#ring.classList.remove("pc-go");
|
|
312
|
+
void this.#ring.offsetWidth;
|
|
313
|
+
this.#ring.classList.add("pc-go");
|
|
314
|
+
}
|
|
315
|
+
/** Put another part on the person's layer, out of what the agent reads: the walks' button and pills. */
|
|
316
|
+
add(element) {
|
|
317
|
+
this.#layer.append(element);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Half the resting face's width, in px, touring or not: read off a face still small from a walk, it
|
|
321
|
+
* set the walks' button where the face, grown back, covered it.
|
|
322
|
+
*/
|
|
323
|
+
get radius() {
|
|
324
|
+
const size = Number.parseFloat(this.#document.defaultView?.getComputedStyle(this.face).getPropertyValue("--pc-dot-size") ?? "");
|
|
325
|
+
return (Number.isFinite(size) && size > 0 ? size : 64) / 2;
|
|
326
|
+
}
|
|
327
|
+
/** How far a walk has played, 0 to 1, as a ring round the face; null takes the ring away. */
|
|
328
|
+
progress(fraction) {
|
|
329
|
+
const ring = this.#part(".pc-progress");
|
|
330
|
+
ring.toggleAttribute("hidden", fraction === null);
|
|
331
|
+
const done = Math.round(Math.min(1, Math.max(0, fraction ?? 0)) * 1000) / 10;
|
|
332
|
+
ring.querySelector("circle")?.setAttribute("stroke-dasharray", `${done} ${100 - done}`);
|
|
333
|
+
}
|
|
334
|
+
close() {
|
|
335
|
+
this.#layer.remove();
|
|
336
|
+
this.#style.remove();
|
|
337
|
+
}
|
|
338
|
+
#part(selector) {
|
|
339
|
+
const found = this.#layer.querySelector(selector);
|
|
340
|
+
if (found === null)
|
|
341
|
+
throw new Error(`the person has no ${selector}`);
|
|
342
|
+
return found;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
//# sourceMappingURL=person.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"person.js","sourceRoot":"","sources":["../../src/pointer/person.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** The page's ways into a walk with no call: the section scrolled to offers its own, and the site's own button plays one. */
|
|
2
|
+
/** The attribute a section names its walk with. */
|
|
3
|
+
export declare const WALK_ATTRIBUTE = "data-pinecall-walk";
|
|
4
|
+
/** The attribute the site's own button names its walk with. */
|
|
5
|
+
export declare const PLAY_ATTRIBUTE = "data-pinecall-play";
|
|
6
|
+
export interface SectionsOptions {
|
|
7
|
+
/** The section scrolled to names a published walk, by its name. */
|
|
8
|
+
onReach: (name: string) => void;
|
|
9
|
+
/** The site's own button for a walk clicked, by the walk's name. */
|
|
10
|
+
onPlay: (name: string) => void;
|
|
11
|
+
}
|
|
12
|
+
export declare class Sections {
|
|
13
|
+
#private;
|
|
14
|
+
constructor(document: Document, options: SectionsOptions);
|
|
15
|
+
/** The names of the walks the agent published. */
|
|
16
|
+
offer(names: string[]): void;
|
|
17
|
+
/**
|
|
18
|
+
* Note the section scrolled to, and say so when it is a new one naming a published walk and `on`;
|
|
19
|
+
* show the site's own buttons while `on`, for a published walk. Called every frame.
|
|
20
|
+
*/
|
|
21
|
+
place(on: boolean): void;
|
|
22
|
+
close(): void;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=sections.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sections.d.ts","sourceRoot":"","sources":["../../src/pointer/sections.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAO7H,mDAAmD;AACnD,eAAO,MAAM,cAAc,uBAAuB,CAAC;AACnD,+DAA+D;AAC/D,eAAO,MAAM,cAAc,uBAAuB,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,mEAAmE;IACnE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChC,oEAAoE;IACpE,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAChC;AAED,qBAAa,QAAQ;;IAenB,YAAY,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,eAAe,EAIvD;IAED,kDAAkD;IAClD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,CAE3B;IAED;;;OAGG;IACH,KAAK,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAgBvB;IAED,KAAK,IAAI,IAAI,CAEZ;CACF"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/** The page's ways into a walk with no call: the section scrolled to offers its own, and the site's own button plays one. */
|
|
2
|
+
//
|
|
3
|
+
// The site marks a section with `data-pinecall-walk="<walk>"`: as the page is scrolled to it (its box
|
|
4
|
+
// across the screen's middle), the tutor offers that walk for a few seconds beside the face. A button
|
|
5
|
+
// the site draws and marks `data-pinecall-play="<walk>"` is shown (its `hidden` taken off) while that
|
|
6
|
+
// walk can play, and a click on it plays it. Both only for the walks the agent published.
|
|
7
|
+
/** The attribute a section names its walk with. */
|
|
8
|
+
export const WALK_ATTRIBUTE = "data-pinecall-walk";
|
|
9
|
+
/** The attribute the site's own button names its walk with. */
|
|
10
|
+
export const PLAY_ATTRIBUTE = "data-pinecall-play";
|
|
11
|
+
/** The line across the screen a section must cross to be the one scrolled to, as a share of the screen's height. */
|
|
12
|
+
const MIDDLE = 0.5;
|
|
13
|
+
export class Sections {
|
|
14
|
+
#document;
|
|
15
|
+
#options;
|
|
16
|
+
#names = [];
|
|
17
|
+
#on = false;
|
|
18
|
+
/** The section across the screen's middle at the last frame. */
|
|
19
|
+
#at = null;
|
|
20
|
+
#clicked = (event) => {
|
|
21
|
+
const button = event.target instanceof Element ? event.target.closest(`[${PLAY_ATTRIBUTE}]`) : null;
|
|
22
|
+
const name = button?.getAttribute(PLAY_ATTRIBUTE) ?? null;
|
|
23
|
+
if (!this.#on || name === null || !this.#names.includes(name))
|
|
24
|
+
return;
|
|
25
|
+
event.preventDefault();
|
|
26
|
+
this.#options.onPlay(name);
|
|
27
|
+
};
|
|
28
|
+
constructor(document, options) {
|
|
29
|
+
this.#document = document;
|
|
30
|
+
this.#options = options;
|
|
31
|
+
document.addEventListener("click", this.#clicked);
|
|
32
|
+
}
|
|
33
|
+
/** The names of the walks the agent published. */
|
|
34
|
+
offer(names) {
|
|
35
|
+
this.#names = names;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Note the section scrolled to, and say so when it is a new one naming a published walk and `on`;
|
|
39
|
+
* show the site's own buttons while `on`, for a published walk. Called every frame.
|
|
40
|
+
*/
|
|
41
|
+
place(on) {
|
|
42
|
+
this.#on = on;
|
|
43
|
+
for (const button of this.#document.querySelectorAll(`[${PLAY_ATTRIBUTE}]`)) {
|
|
44
|
+
const hidden = !on || !this.#names.includes(button.getAttribute(PLAY_ATTRIBUTE) ?? "");
|
|
45
|
+
if (button.hidden !== hidden)
|
|
46
|
+
button.hidden = hidden;
|
|
47
|
+
}
|
|
48
|
+
const middle = (this.#document.defaultView?.innerHeight ?? 0) * MIDDLE;
|
|
49
|
+
const at = [...this.#document.querySelectorAll(`[${WALK_ATTRIBUTE}]`)].find((section) => {
|
|
50
|
+
const box = section.getBoundingClientRect();
|
|
51
|
+
return box.height > 0 && box.top <= middle && box.bottom > middle;
|
|
52
|
+
}) ?? null;
|
|
53
|
+
if (at === this.#at)
|
|
54
|
+
return;
|
|
55
|
+
this.#at = at;
|
|
56
|
+
const name = at?.getAttribute(WALK_ATTRIBUTE) ?? null;
|
|
57
|
+
if (on && name !== null && this.#names.includes(name))
|
|
58
|
+
this.#options.onReach(name);
|
|
59
|
+
}
|
|
60
|
+
close() {
|
|
61
|
+
this.#document.removeEventListener("click", this.#clicked);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sections.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sections.js","sourceRoot":"","sources":["../../src/pointer/sections.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** TypeScript and TSX coloured as an editor colours them: each line its pieces, each piece what it is. Pure. */
|
|
2
|
+
/** What a piece of a line is. */
|
|
3
|
+
export type Kind = "plain" | "comment" | "keyword" | "string" | "decorator" | "type" | "tag" | "number" | "self";
|
|
4
|
+
export interface Piece {
|
|
5
|
+
text: string;
|
|
6
|
+
kind: Kind;
|
|
7
|
+
}
|
|
8
|
+
/** Each line of `text` as its pieces, in order; joined, a line's pieces are the line. */
|
|
9
|
+
export declare function coloured(text: string): Piece[][];
|
|
10
|
+
//# sourceMappingURL=syntax.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntax.d.ts","sourceRoot":"","sources":["../../src/pointer/syntax.ts"],"names":[],"mappings":"AAAA,gHAAgH;AAKhH,iCAAiC;AACjC,MAAM,MAAM,IAAI,GAAG,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,QAAQ,GAAG,WAAW,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEjH,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,IAAI,CAAC;CACZ;AAWD,yFAAyF;AACzF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,KAAK,EAAE,EAAE,CAmChD"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** TypeScript and TSX coloured as an editor colours them: each line its pieces, each piece what it is. Pure. */
|
|
2
|
+
//
|
|
3
|
+
// A small tokenizer, not a parser: enough for the code a walk explains (a class, its fields, a tool,
|
|
4
|
+
// its view). A block comment runs across lines, so the state is carried from one line to the next.
|
|
5
|
+
const KEYWORDS = new Set([
|
|
6
|
+
"export", "import", "from", "default", "class", "extends", "implements", "interface", "type", "enum", "const", "let", "var",
|
|
7
|
+
"function", "return", "if", "else", "for", "of", "in", "while", "async", "await", "new", "static", "override", "private",
|
|
8
|
+
"public", "readonly", "true", "false", "null", "undefined", "void", "as", "throw", "try", "catch", "finally",
|
|
9
|
+
]);
|
|
10
|
+
// One alternative per kind, tried in order at each position.
|
|
11
|
+
const PIECES = /(\/\/.*$)|(\/\*.*?(?:\*\/|$))|("(?:[^"\\]|\\.)*"?|'(?:[^'\\]|\\.)*'?|`(?:[^`\\]|\\.)*`?)|(@[A-Za-z_]\w*)|(<\/?[a-z][\w.-]*|\/?>)|(\b\d+(?:\.\d+)?\b)|(\b[A-Za-z_$][\w$]*\b)/g;
|
|
12
|
+
/** Each line of `text` as its pieces, in order; joined, a line's pieces are the line. */
|
|
13
|
+
export function coloured(text) {
|
|
14
|
+
let inComment = false;
|
|
15
|
+
return text.split("\n").map((line) => {
|
|
16
|
+
const pieces = [];
|
|
17
|
+
let at = 0;
|
|
18
|
+
if (inComment) {
|
|
19
|
+
const end = line.indexOf("*/");
|
|
20
|
+
const upto = end === -1 ? line.length : end + 2;
|
|
21
|
+
pieces.push({ text: line.slice(0, upto), kind: "comment" });
|
|
22
|
+
at = upto;
|
|
23
|
+
inComment = end === -1;
|
|
24
|
+
}
|
|
25
|
+
PIECES.lastIndex = at;
|
|
26
|
+
for (let found = PIECES.exec(line); found !== null; found = PIECES.exec(line)) {
|
|
27
|
+
if (found.index > at)
|
|
28
|
+
pieces.push({ text: line.slice(at, found.index), kind: "plain" });
|
|
29
|
+
const [whole, lineComment, blockComment, string, decorator, tag, number] = found;
|
|
30
|
+
let kind = "plain";
|
|
31
|
+
if (lineComment !== undefined)
|
|
32
|
+
kind = "comment";
|
|
33
|
+
else if (blockComment !== undefined) {
|
|
34
|
+
kind = "comment";
|
|
35
|
+
inComment = !blockComment.endsWith("*/") || blockComment.length < 4;
|
|
36
|
+
}
|
|
37
|
+
else if (string !== undefined)
|
|
38
|
+
kind = "string";
|
|
39
|
+
else if (decorator !== undefined)
|
|
40
|
+
kind = "decorator";
|
|
41
|
+
else if (tag !== undefined)
|
|
42
|
+
kind = "tag";
|
|
43
|
+
else if (number !== undefined)
|
|
44
|
+
kind = "number";
|
|
45
|
+
else if (whole === "this" || whole === "super")
|
|
46
|
+
kind = "self";
|
|
47
|
+
else if (KEYWORDS.has(whole))
|
|
48
|
+
kind = "keyword";
|
|
49
|
+
else if (/^[A-Z]/.test(whole))
|
|
50
|
+
kind = "type";
|
|
51
|
+
pieces.push({ text: whole, kind });
|
|
52
|
+
at = found.index + whole.length;
|
|
53
|
+
if (whole.length === 0)
|
|
54
|
+
PIECES.lastIndex += 1;
|
|
55
|
+
}
|
|
56
|
+
if (at < line.length)
|
|
57
|
+
pieces.push({ text: line.slice(at), kind: "plain" });
|
|
58
|
+
return pieces;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=syntax.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syntax.js","sourceRoot":"","sources":["../../src/pointer/syntax.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** The pointer filling a field the way a person types: letter by letter, with the events the site's own code listens for. */
|
|
2
|
+
/** Why `element` is not a field to fill, or null when it is one. */
|
|
3
|
+
export declare function notFillable(element: Element): string | null;
|
|
4
|
+
/** Fill `field` with `text`: typed letter by letter, or, for a list, the option that says it. Resolves with why it could not, or null. */
|
|
5
|
+
export declare function fill(field: Element, text: string, still: boolean): Promise<string | null>;
|
|
6
|
+
//# sourceMappingURL=typing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"typing.d.ts","sourceRoot":"","sources":["../../src/pointer/typing.ts"],"names":[],"mappings":"AAAA,6HAA6H;AAY7H,oEAAoE;AACpE,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAM3D;AAED,0IAA0I;AAC1I,wBAAsB,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAiB/F"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/** The pointer filling a field the way a person types: letter by letter, with the events the site's own code listens for. */
|
|
2
|
+
//
|
|
3
|
+
// The value is set through the element's own setter and announced with `input` and `change`, so a
|
|
4
|
+
// framework that watches the field (React's, Vue's) sees it typed. A list takes the option whose
|
|
5
|
+
// words hold what was said. It never submits, and never touches a password, a card, a file or a
|
|
6
|
+
// hidden field.
|
|
7
|
+
/** How long one letter takes, in ms. */
|
|
8
|
+
const LETTER_MS = 45;
|
|
9
|
+
/** The inputs a person writes words in. */
|
|
10
|
+
const WRITTEN = new Set(["text", "tel", "email", "search", "url", "number", "date", "time", "datetime-local", "month", "week"]);
|
|
11
|
+
/** Why `element` is not a field to fill, or null when it is one. */
|
|
12
|
+
export function notFillable(element) {
|
|
13
|
+
if (element instanceof HTMLSelectElement || element instanceof HTMLTextAreaElement)
|
|
14
|
+
return element.disabled ? "it is disabled" : null;
|
|
15
|
+
if (!(element instanceof HTMLInputElement))
|
|
16
|
+
return "it is not a field";
|
|
17
|
+
if (element.type === "password" || element.autocomplete.startsWith("cc-"))
|
|
18
|
+
return "a password or a card is the visitor's to type";
|
|
19
|
+
if (!WRITTEN.has(element.type))
|
|
20
|
+
return `a ${element.type} field is not written in`;
|
|
21
|
+
return element.disabled || element.readOnly ? "it is disabled" : null;
|
|
22
|
+
}
|
|
23
|
+
/** Fill `field` with `text`: typed letter by letter, or, for a list, the option that says it. Resolves with why it could not, or null. */
|
|
24
|
+
export async function fill(field, text, still) {
|
|
25
|
+
const refused = notFillable(field);
|
|
26
|
+
if (refused !== null)
|
|
27
|
+
return refused;
|
|
28
|
+
if (field instanceof HTMLSelectElement)
|
|
29
|
+
return choose(field, text);
|
|
30
|
+
const written = field;
|
|
31
|
+
written.focus({ preventScroll: true });
|
|
32
|
+
if (still) {
|
|
33
|
+
set(written, text);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
set(written, "");
|
|
37
|
+
for (let at = 1; at <= text.length; at++) {
|
|
38
|
+
set(written, text.slice(0, at));
|
|
39
|
+
await new Promise((done) => setTimeout(done, LETTER_MS));
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
written.dispatchEvent(new Event("change", { bubbles: true }));
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
function choose(list, text) {
|
|
46
|
+
const wanted = text.trim().toLowerCase();
|
|
47
|
+
const options = [...list.options];
|
|
48
|
+
const option = options.find((one) => one.text.trim().toLowerCase() === wanted) ?? options.find((one) => one.text.toLowerCase().includes(wanted));
|
|
49
|
+
if (option === undefined)
|
|
50
|
+
return `no option says "${text}": the options are ${options.map((one) => `"${one.text}"`).join(", ")}`;
|
|
51
|
+
list.value = option.value;
|
|
52
|
+
list.dispatchEvent(new Event("input", { bubbles: true }));
|
|
53
|
+
list.dispatchEvent(new Event("change", { bubbles: true }));
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
// A framework keeps its own copy of the value and reads it back from the prototype's setter.
|
|
57
|
+
function set(field, value) {
|
|
58
|
+
const prototype = field instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
59
|
+
Object.getOwnPropertyDescriptor(prototype, "value")?.set?.call(field, value);
|
|
60
|
+
field.dispatchEvent(new Event("input", { bubbles: true }));
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=typing.js.map
|