@oharness/server 0.9.3 → 0.11.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/dist/analytics.d.ts +32 -29
- package/dist/analytics.js +225 -278
- package/dist/analytics.js.map +1 -1
- package/dist/i18n.js +500 -64
- package/dist/i18n.js.map +1 -1
- package/dist/icons.js +1 -0
- package/dist/icons.js.map +1 -1
- package/dist/main.js +4 -0
- package/dist/main.js.map +1 -1
- package/dist/model-order.js +1 -0
- package/dist/model-order.js.map +1 -1
- package/dist/page/client/account.js +570 -27
- package/dist/page/client/account.js.map +1 -1
- package/dist/page/client/base.js +55 -12
- package/dist/page/client/base.js.map +1 -1
- package/dist/page/client/browser.js +1 -1
- package/dist/page/client/composer.js +802 -59
- package/dist/page/client/composer.js.map +1 -1
- package/dist/page/client/computer.d.ts +27 -0
- package/dist/page/client/computer.js +358 -0
- package/dist/page/client/computer.js.map +1 -0
- package/dist/page/client/dialogs.js +168 -6
- package/dist/page/client/dialogs.js.map +1 -1
- package/dist/page/client/events.js +119 -9
- package/dist/page/client/events.js.map +1 -1
- package/dist/page/client/files.js +86 -1
- package/dist/page/client/files.js.map +1 -1
- package/dist/page/client/index.js +8 -0
- package/dist/page/client/index.js.map +1 -1
- package/dist/page/client/panels.js +14 -10
- package/dist/page/client/panels.js.map +1 -1
- package/dist/page/client/ripple.d.ts +13 -0
- package/dist/page/client/ripple.js +247 -0
- package/dist/page/client/ripple.js.map +1 -0
- package/dist/page/client/routines.js +432 -45
- package/dist/page/client/routines.js.map +1 -1
- package/dist/page/client/shell.js +386 -104
- package/dist/page/client/shell.js.map +1 -1
- package/dist/page/client/state.js +278 -19
- package/dist/page/client/state.js.map +1 -1
- package/dist/page/client/tasks.d.ts +8 -0
- package/dist/page/client/tasks.js +293 -0
- package/dist/page/client/tasks.js.map +1 -0
- package/dist/page/client/terminal.js +34 -7
- package/dist/page/client/terminal.js.map +1 -1
- package/dist/page/client/theme.d.ts +17 -0
- package/dist/page/client/theme.js +365 -0
- package/dist/page/client/theme.js.map +1 -0
- package/dist/page/client/transcript.js +196 -8
- package/dist/page/client/transcript.js.map +1 -1
- package/dist/page/client/usage.js +1 -11
- package/dist/page/client/usage.js.map +1 -1
- package/dist/page/style.js +745 -76
- package/dist/page/style.js.map +1 -1
- package/dist/page.js +94 -16
- package/dist/page.js.map +1 -1
- package/dist/prefs.d.ts +31 -0
- package/dist/prefs.js +13 -1
- package/dist/prefs.js.map +1 -1
- package/dist/remote-crypto.d.ts +78 -0
- package/dist/remote-crypto.js +216 -0
- package/dist/remote-crypto.js.map +1 -0
- package/dist/remote.d.ts +103 -0
- package/dist/remote.js +498 -0
- package/dist/remote.js.map +1 -0
- package/dist/server.d.ts +70 -0
- package/dist/server.js +1738 -73
- package/dist/server.js.map +1 -1
- package/dist/starters.d.ts +92 -0
- package/dist/starters.js +213 -0
- package/dist/starters.js.map +1 -0
- package/dist/suggest.d.ts +42 -0
- package/dist/suggest.js +215 -0
- package/dist/suggest.js.map +1 -0
- package/dist/terminal.d.ts +28 -2
- package/dist/terminal.js +45 -3
- package/dist/terminal.js.map +1 -1
- package/dist/theme-assets.d.ts +39 -0
- package/dist/theme-assets.js +109 -0
- package/dist/theme-assets.js.map +1 -0
- package/dist/theme-store.d.ts +50 -0
- package/dist/theme-store.js +124 -0
- package/dist/theme-store.js.map +1 -0
- package/dist/theme-tools.d.ts +25 -0
- package/dist/theme-tools.js +243 -0
- package/dist/theme-tools.js.map +1 -0
- package/dist/turn-shots.d.ts +43 -0
- package/dist/turn-shots.js +80 -0
- package/dist/turn-shots.js.map +1 -0
- package/dist/user-theme.d.ts +143 -0
- package/dist/user-theme.js +510 -0
- package/dist/user-theme.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The field behind a new session: a grid of dots a click sends a shockwave
|
|
3
|
+
* through.
|
|
4
|
+
*
|
|
5
|
+
* Only while the empty state is on screen. The canvas sits under everything
|
|
6
|
+
* in #main and takes no pointer, so it can never eat a click meant for the
|
|
7
|
+
* composer or a starter; it only hears them go by. Nothing animates at rest —
|
|
8
|
+
* a frame is drawn when a wave is travelling or the pointer moves, and the
|
|
9
|
+
* loop stops once the last wave has died out, so an idle new tab costs nothing.
|
|
10
|
+
*
|
|
11
|
+
* One part of the client script; see ./index.ts for how the parts join.
|
|
12
|
+
*/
|
|
13
|
+
export const RIPPLE = String.raw `
|
|
14
|
+
// --- ripple field ------------------------------------------------------------
|
|
15
|
+
|
|
16
|
+
(function rippleField() {
|
|
17
|
+
const host = document.getElementById("main");
|
|
18
|
+
if (!host || !emptyEl) return;
|
|
19
|
+
const still = window.matchMedia("(prefers-reduced-motion: reduce)");
|
|
20
|
+
|
|
21
|
+
const canvas = document.createElement("canvas");
|
|
22
|
+
canvas.id = "ripple";
|
|
23
|
+
canvas.setAttribute("aria-hidden", "true");
|
|
24
|
+
host.prepend(canvas);
|
|
25
|
+
const ctx = canvas.getContext("2d");
|
|
26
|
+
|
|
27
|
+
const GAP = 24; // grid pitch, css px
|
|
28
|
+
const SPEED = 0.38; // wavefront, px per ms
|
|
29
|
+
const LIFE = 3200; // ms a wave travels before it is gone
|
|
30
|
+
const BAND = 90; // width of the wavefront's swell
|
|
31
|
+
const LENS = 120; // radius the pointer magnifies
|
|
32
|
+
const TINT = "fg"; // what a wave lights up in: "fg" plain, "brand" lime
|
|
33
|
+
|
|
34
|
+
let w = 0, h = 0, dpr = 1;
|
|
35
|
+
let waves = [];
|
|
36
|
+
let pointer = null; // {x, y} inside the canvas, or null
|
|
37
|
+
let raf = 0;
|
|
38
|
+
let shown = false;
|
|
39
|
+
let ink = { fg: "#111", brand: "#5e8c00" };
|
|
40
|
+
|
|
41
|
+
function readInk() {
|
|
42
|
+
const css = getComputedStyle(document.documentElement);
|
|
43
|
+
ink = {
|
|
44
|
+
fg: css.getPropertyValue("--fg").trim() || ink.fg,
|
|
45
|
+
brand: css.getPropertyValue("--brand").trim() || ink.brand,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function fit() {
|
|
50
|
+
const box = host.getBoundingClientRect();
|
|
51
|
+
dpr = Math.min(window.devicePixelRatio || 1, 2);
|
|
52
|
+
w = Math.max(1, Math.round(box.width));
|
|
53
|
+
h = Math.max(1, Math.round(box.height));
|
|
54
|
+
canvas.width = w * dpr;
|
|
55
|
+
canvas.height = h * dpr;
|
|
56
|
+
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Is the invitation actually in front of someone? */
|
|
60
|
+
const visible = () => emptyEl.isConnected && emptyEl.offsetParent !== null;
|
|
61
|
+
|
|
62
|
+
function kick() {
|
|
63
|
+
if (!raf) raf = requestAnimationFrame(frame);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function frame(now) {
|
|
67
|
+
raf = 0;
|
|
68
|
+
waves = waves.filter((wave) => now - wave.t < LIFE);
|
|
69
|
+
draw(now);
|
|
70
|
+
if (waves.length) kick();
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function draw(at) {
|
|
74
|
+
ctx.clearRect(0, 0, w, h);
|
|
75
|
+
if (!shown) return;
|
|
76
|
+
// rAF's stamp is when the frame began, which can trail a wave pushed by a
|
|
77
|
+
// timer since; time is read here so no wave is ever younger than zero.
|
|
78
|
+
const now = Math.max(at, performance.now());
|
|
79
|
+
|
|
80
|
+
// The dot field: each dot pushed outward by every wave passing it, and lit
|
|
81
|
+
// by how hard it is being pushed.
|
|
82
|
+
const ox = (w % GAP) / 2 + GAP / 2;
|
|
83
|
+
const oy = (h % GAP) / 2 + GAP / 2;
|
|
84
|
+
ctx.fillStyle = ink.fg;
|
|
85
|
+
const lit = [];
|
|
86
|
+
for (let gy = oy; gy < h; gy += GAP) {
|
|
87
|
+
for (let gx = ox; gx < w; gx += GAP) {
|
|
88
|
+
let dx = 0, dy = 0, energy = 0;
|
|
89
|
+
for (const wave of waves) {
|
|
90
|
+
const age = now - wave.t;
|
|
91
|
+
const front = age * SPEED;
|
|
92
|
+
const vx = gx - wave.x, vy = gy - wave.y;
|
|
93
|
+
const dist = Math.hypot(vx, vy) || 1;
|
|
94
|
+
const off = dist - front;
|
|
95
|
+
if (off > BAND || off < -BAND * 3) continue;
|
|
96
|
+
const fade = 1 - age / LIFE;
|
|
97
|
+
// One long, low swell at the front — a breath through the grid, not a shake.
|
|
98
|
+
const env = Math.exp(-(off * off) / (BAND * BAND * 0.35)) * fade * fade;
|
|
99
|
+
const push = Math.sin(off / 26) * 4.5 * env * wave.power;
|
|
100
|
+
dx += (vx / dist) * push;
|
|
101
|
+
dy += (vy / dist) * push;
|
|
102
|
+
energy += env * wave.power;
|
|
103
|
+
}
|
|
104
|
+
let r = 1.05, alpha = 0.13;
|
|
105
|
+
if (pointer) {
|
|
106
|
+
const near = Math.hypot(gx - pointer.x, gy - pointer.y);
|
|
107
|
+
if (near < LENS) {
|
|
108
|
+
const k = 1 - near / LENS;
|
|
109
|
+
r += k * k * 1.3;
|
|
110
|
+
alpha += k * k * 0.22;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (energy > 0.06) {
|
|
114
|
+
lit.push(gx + dx, gy + dy, Math.min(1, energy), r);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
ctx.globalAlpha = alpha;
|
|
118
|
+
ctx.beginPath();
|
|
119
|
+
ctx.arc(gx + dx, gy + dy, r, 0, 6.2832);
|
|
120
|
+
ctx.fill();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// Dots on a wavefront take the brand's lime and swell with it.
|
|
124
|
+
ctx.fillStyle = ink[TINT];
|
|
125
|
+
for (let i = 0; i < lit.length; i += 4) {
|
|
126
|
+
const e = lit[i + 2];
|
|
127
|
+
ctx.globalAlpha = 0.18 + e * 0.5;
|
|
128
|
+
ctx.beginPath();
|
|
129
|
+
ctx.arc(lit[i], lit[i + 1], lit[i + 3] + e * 0.9, 0, 6.2832);
|
|
130
|
+
ctx.fill();
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// The fronts themselves: a hairline lime ring with a faint dotted ghost of
|
|
134
|
+
// the ink riding just outside it, and a soft bloom where the click landed.
|
|
135
|
+
ctx.lineCap = "round";
|
|
136
|
+
for (const wave of waves) {
|
|
137
|
+
const age = now - wave.t;
|
|
138
|
+
const fade = 1 - age / LIFE;
|
|
139
|
+
const front = age * SPEED;
|
|
140
|
+
ctx.globalAlpha = 0.32 * fade * fade * fade * wave.power;
|
|
141
|
+
ctx.strokeStyle = ink[TINT];
|
|
142
|
+
ctx.lineWidth = 1;
|
|
143
|
+
ctx.beginPath();
|
|
144
|
+
ctx.arc(wave.x, wave.y, front, 0, 6.2832);
|
|
145
|
+
ctx.stroke();
|
|
146
|
+
ctx.globalAlpha = 0.08 * fade * fade * fade * wave.power;
|
|
147
|
+
ctx.strokeStyle = ink.fg;
|
|
148
|
+
ctx.lineWidth = 1;
|
|
149
|
+
ctx.setLineDash([2, 9]);
|
|
150
|
+
ctx.lineDashOffset = -age / 40;
|
|
151
|
+
ctx.beginPath();
|
|
152
|
+
ctx.arc(wave.x, wave.y, front * 1.03 + 4, 0, 6.2832);
|
|
153
|
+
ctx.stroke();
|
|
154
|
+
ctx.setLineDash([]);
|
|
155
|
+
if (age < 420) {
|
|
156
|
+
const k = 1 - age / 420;
|
|
157
|
+
const glow = ctx.createRadialGradient(wave.x, wave.y, 0, wave.x, wave.y, 46);
|
|
158
|
+
glow.addColorStop(0, ink[TINT]);
|
|
159
|
+
glow.addColorStop(1, "transparent");
|
|
160
|
+
ctx.globalAlpha = 0.22 * k * k * wave.power;
|
|
161
|
+
ctx.fillStyle = glow;
|
|
162
|
+
ctx.beginPath();
|
|
163
|
+
ctx.arc(wave.x, wave.y, 46, 0, 6.2832);
|
|
164
|
+
ctx.fill();
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
ctx.globalAlpha = 1;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function wave(x, y, power) {
|
|
171
|
+
if (still.matches) return;
|
|
172
|
+
waves.push({ x, y, t: performance.now(), power });
|
|
173
|
+
if (waves.length > 28) waves.shift();
|
|
174
|
+
kick();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function local(event) {
|
|
178
|
+
const box = canvas.getBoundingClientRect();
|
|
179
|
+
return { x: event.clientX - box.left, y: event.clientY - box.top };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Held down and dragged, the pointer leaves a wake: a small wave every
|
|
183
|
+
// TRAIL px it travels, so a stroke draws a line of rings behind it.
|
|
184
|
+
const TRAIL = 40;
|
|
185
|
+
let drag = null; // where the last wake wave went out, while held
|
|
186
|
+
|
|
187
|
+
host.addEventListener("pointerdown", (event) => {
|
|
188
|
+
if (!shown || event.button > 0) return;
|
|
189
|
+
const at = local(event);
|
|
190
|
+
drag = at;
|
|
191
|
+
// A stroke begun on bare background is drawing, not selecting: without
|
|
192
|
+
// this it would sweep the headline and starters into a text selection.
|
|
193
|
+
const bare = event.target === host || event.target.id === "log" || event.target === emptyEl;
|
|
194
|
+
if (bare) event.preventDefault();
|
|
195
|
+
wave(at.x, at.y, 1);
|
|
196
|
+
});
|
|
197
|
+
host.addEventListener("pointermove", (event) => {
|
|
198
|
+
if (!shown) return;
|
|
199
|
+
const at = local(event);
|
|
200
|
+
if (event.pointerType === "mouse") pointer = at;
|
|
201
|
+
if (drag && event.buttons & 1) {
|
|
202
|
+
const gone = Math.hypot(at.x - drag.x, at.y - drag.y);
|
|
203
|
+
if (gone >= TRAIL) {
|
|
204
|
+
drag = at;
|
|
205
|
+
wave(at.x, at.y, Math.min(0.45, 0.2 + gone / 240));
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
kick();
|
|
209
|
+
});
|
|
210
|
+
const release = () => { drag = null; };
|
|
211
|
+
window.addEventListener("pointerup", release);
|
|
212
|
+
window.addEventListener("pointercancel", release);
|
|
213
|
+
host.addEventListener("pointerleave", () => {
|
|
214
|
+
pointer = null;
|
|
215
|
+
if (shown) kick();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
// The empty state comes and goes with tab switches and first messages, and
|
|
219
|
+
// nothing announces it, so look twice a second; on arrival, the brand mark
|
|
220
|
+
// sends out one wave of its own.
|
|
221
|
+
function check() {
|
|
222
|
+
const now = visible();
|
|
223
|
+
if (now === shown) return;
|
|
224
|
+
shown = now;
|
|
225
|
+
canvas.classList.toggle("on", shown);
|
|
226
|
+
if (!shown) { waves = []; pointer = null; kick(); return; }
|
|
227
|
+
readInk();
|
|
228
|
+
fit();
|
|
229
|
+
const mark = emptyEl.querySelector(".brand-mark");
|
|
230
|
+
if (mark) {
|
|
231
|
+
const at = mark.getBoundingClientRect();
|
|
232
|
+
const box = canvas.getBoundingClientRect();
|
|
233
|
+
wave(at.left + at.width / 2 - box.left, at.top + at.height / 2 - box.top, 0.7);
|
|
234
|
+
}
|
|
235
|
+
kick();
|
|
236
|
+
}
|
|
237
|
+
setInterval(check, 500);
|
|
238
|
+
check();
|
|
239
|
+
|
|
240
|
+
new ResizeObserver(() => { if (shown) { fit(); kick(); } }).observe(host);
|
|
241
|
+
// Theme flips (system or the in-app toggle) change the ink.
|
|
242
|
+
window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { readInk(); kick(); });
|
|
243
|
+
new MutationObserver(() => { readInk(); kick(); })
|
|
244
|
+
.observe(document.documentElement, { attributes: true, attributeFilter: ["data-theme"] });
|
|
245
|
+
})();
|
|
246
|
+
`;
|
|
247
|
+
//# sourceMappingURL=ripple.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ripple.js","sourceRoot":"","sources":["../../../src/page/client/ripple.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyO/B,CAAC"}
|