@enigmax/primitives 0.16.0 → 0.18.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/button-CaXaqG_K.d.ts +63 -0
- package/dist/chunk-2QFTRNAZ.js +88 -0
- package/dist/chunk-6BGBYUSZ.js +114 -0
- package/dist/chunk-AU3H5WIY.js +107 -0
- package/dist/chunk-F25CGNQC.js +19 -0
- package/dist/chunk-HC2ME5PU.js +168 -0
- package/dist/chunk-HS3X3XCW.js +43 -0
- package/dist/chunk-IIT6U7LM.js +249 -0
- package/dist/chunk-IXVMRVD4.js +96 -0
- package/dist/chunk-KJINGUQN.js +188 -0
- package/dist/chunk-LKA2UG4P.js +542 -0
- package/dist/chunk-MMQPZGSU.js +161 -0
- package/dist/chunk-OCMI7R6H.js +79 -0
- package/dist/chunk-QYMUIW5I.js +28 -0
- package/dist/chunk-S653GLSF.js +17 -0
- package/dist/chunk-SNYUBXWQ.js +149 -0
- package/dist/chunk-U3V4EHOB.js +41 -0
- package/dist/chunk-UOSSNUSC.js +309 -0
- package/dist/chunk-WSB655JB.js +47 -0
- package/dist/chunk-XNNQRA35.js +31 -0
- package/dist/chunk-XQHCZAPJ.js +102 -0
- package/dist/chunk-ZCUFYBPB.js +154 -0
- package/dist/chunk-ZWR2EXHQ.js +55 -0
- package/dist/flags-BBJc9unY.d.ts +133 -0
- package/dist/index-D-ARvWpQ.d.ts +306 -0
- package/dist/index-DNXPtY9X.d.ts +144 -0
- package/dist/index.d.ts +16 -496
- package/dist/index.js +9 -1
- package/dist/input-BwXjFenq.d.ts +77 -0
- package/dist/marquee-CJ3Uwy3E.d.ts +81 -0
- package/dist/network-D2LsBG_k.d.ts +39 -0
- package/dist/next/index.d.ts +20 -3
- package/dist/next/index.js +23 -3
- package/dist/notifications-BpVV6sel.d.ts +70 -0
- package/dist/password-3DRQYAYQ.js +2 -0
- package/dist/password-C8lG4Zm9.d.ts +71 -0
- package/dist/password-FB2CUEKJ.js +1 -0
- package/dist/react/button.d.ts +75 -0
- package/dist/react/button.js +4 -0
- package/dist/react/flag.d.ts +37 -0
- package/dist/react/flag.js +3 -0
- package/dist/react/index.d.ts +28 -286
- package/dist/react/index.js +22 -2
- package/dist/react/input.d.ts +4 -0
- package/dist/react/input.js +3 -0
- package/dist/react/marquee.d.ts +44 -0
- package/dist/react/marquee.js +3 -0
- package/dist/react/network.d.ts +20 -0
- package/dist/react/network.js +3 -0
- package/dist/react/notifications.d.ts +17 -0
- package/dist/react/notifications.js +3 -0
- package/dist/react/palette.d.ts +3 -0
- package/dist/react/palette.js +4 -0
- package/dist/react/relative-time.d.ts +21 -0
- package/dist/react/relative-time.js +3 -0
- package/dist/react/search.d.ts +30 -0
- package/dist/react/search.js +3 -0
- package/dist/react/slot.d.ts +47 -0
- package/dist/react/slot.js +2 -0
- package/dist/react/toast.d.ts +40 -0
- package/dist/react/toast.js +4 -0
- package/dist/react-router/index.d.ts +20 -3
- package/dist/react-router/index.js +23 -3
- package/dist/relative-time-YpRTG7YH.d.ts +106 -0
- package/dist/search-P4OHCRXD.js +50 -0
- package/package.json +53 -1
- package/registry.json +235 -16
- package/src/core/flags.ts +266 -0
- package/src/core/input-icons.ts +34 -0
- package/src/core/input.ts +5 -21
- package/src/core/palette.ts +0 -0
- package/src/index.ts +18 -0
- package/src/react/button.tsx +70 -4
- package/src/react/flag.tsx +70 -0
- package/src/react/index.ts +52 -5
- package/src/react/input/icon.tsx +17 -0
- package/src/react/input/index.tsx +307 -0
- package/src/react/input/password.tsx +174 -0
- package/src/react/input/search.tsx +82 -0
- package/src/react/input/types.ts +146 -0
- package/src/react/input/write-value.ts +18 -0
- package/src/react/palette/context.ts +60 -0
- package/src/react/palette/index.tsx +66 -0
- package/src/react/palette/root.tsx +621 -0
- package/src/react/slot.tsx +91 -0
- package/src/react/use-button.ts +3 -1
- package/dist/chunk-53DQY6LP.js +0 -1164
- package/dist/chunk-U2KYYMBJ.js +0 -724
- package/src/react/input.tsx +0 -429
package/dist/chunk-53DQY6LP.js
DELETED
|
@@ -1,1164 +0,0 @@
|
|
|
1
|
-
// src/core/marquee.ts
|
|
2
|
-
var DEFAULT_DECAY = 0.12;
|
|
3
|
-
var DRAG_THRESHOLD = 6;
|
|
4
|
-
var STILL_MS = 90;
|
|
5
|
-
var MAX_DT = 0.05;
|
|
6
|
-
var VELOCITY_SMOOTHING = 0.6;
|
|
7
|
-
var TOUCH_HOVER_SUPPRESS_MS = 1e3;
|
|
8
|
-
var IDLE_EPSILON = 0.01;
|
|
9
|
-
var DEFAULTS = {
|
|
10
|
-
speed: 60,
|
|
11
|
-
reverse: false,
|
|
12
|
-
vertical: false,
|
|
13
|
-
draggable: true,
|
|
14
|
-
hover: "off",
|
|
15
|
-
decay: DEFAULT_DECAY,
|
|
16
|
-
manageStyles: true,
|
|
17
|
-
copies: "clone"
|
|
18
|
-
};
|
|
19
|
-
var MAX_COPIES = 24;
|
|
20
|
-
function createMarquee(lane, track, options = {}) {
|
|
21
|
-
let opts = { ...DEFAULTS, ...options };
|
|
22
|
-
let period = 0;
|
|
23
|
-
let copyCount = Math.max(2, track.children.length);
|
|
24
|
-
let offset = 0;
|
|
25
|
-
let velocity = 0;
|
|
26
|
-
let running = false;
|
|
27
|
-
let paused = false;
|
|
28
|
-
let destroyed = false;
|
|
29
|
-
let frame = 0;
|
|
30
|
-
let lastTime = 0;
|
|
31
|
-
let dragging = false;
|
|
32
|
-
let pointerId = null;
|
|
33
|
-
let lastPointer = 0;
|
|
34
|
-
let startPointer = 0;
|
|
35
|
-
let lastMoveTime = 0;
|
|
36
|
-
let maxTravel = 0;
|
|
37
|
-
let hovering = false;
|
|
38
|
-
let lastTouchAt = 0;
|
|
39
|
-
const motionQuery = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-reduced-motion: reduce)") : null;
|
|
40
|
-
let reducedMotion = motionQuery?.matches ?? false;
|
|
41
|
-
function hoverTarget(cruise) {
|
|
42
|
-
const hover = opts.hover ?? "off";
|
|
43
|
-
if (hover === "off") return cruise;
|
|
44
|
-
if (hover === "pause") return 0;
|
|
45
|
-
if (typeof hover === "number") return cruise * hover;
|
|
46
|
-
return (cruise < 0 ? -1 : 1) * Math.abs(hover.speed);
|
|
47
|
-
}
|
|
48
|
-
function target() {
|
|
49
|
-
if (reducedMotion || paused) return 0;
|
|
50
|
-
const cruise = opts.reverse ? -opts.speed : opts.speed;
|
|
51
|
-
return hovering ? hoverTarget(cruise) : cruise;
|
|
52
|
-
}
|
|
53
|
-
function axisSize(element) {
|
|
54
|
-
return opts.vertical ? element.offsetHeight : element.offsetWidth;
|
|
55
|
-
}
|
|
56
|
-
function axisStart(element) {
|
|
57
|
-
return opts.vertical ? element.offsetTop : element.offsetLeft;
|
|
58
|
-
}
|
|
59
|
-
function applyStyles() {
|
|
60
|
-
if (!opts.manageStyles) return;
|
|
61
|
-
lane.style.overflow = "hidden";
|
|
62
|
-
lane.style.touchAction = opts.vertical ? "pan-x" : "pan-y";
|
|
63
|
-
lane.style.userSelect = "none";
|
|
64
|
-
lane.style.webkitUserSelect = "none";
|
|
65
|
-
track.style.willChange = "transform";
|
|
66
|
-
}
|
|
67
|
-
function render() {
|
|
68
|
-
const x = opts.vertical ? 0 : -offset;
|
|
69
|
-
const y = opts.vertical ? -offset : 0;
|
|
70
|
-
track.style.transform = `translate3d(${x}px, ${y}px, 0)`;
|
|
71
|
-
}
|
|
72
|
-
function readPeriod() {
|
|
73
|
-
const first = track.children[0];
|
|
74
|
-
const second = track.children[1];
|
|
75
|
-
if (!first) return 0;
|
|
76
|
-
if (second) {
|
|
77
|
-
const step = axisStart(second) - axisStart(first);
|
|
78
|
-
if (step > 0) return step;
|
|
79
|
-
}
|
|
80
|
-
return axisSize(first);
|
|
81
|
-
}
|
|
82
|
-
function requiredCopies(measured) {
|
|
83
|
-
if (measured <= 0) return 2;
|
|
84
|
-
return Math.min(MAX_COPIES, Math.max(2, Math.ceil(axisSize(lane) / measured) + 1));
|
|
85
|
-
}
|
|
86
|
-
function syncClones(count) {
|
|
87
|
-
const source = track.children[0];
|
|
88
|
-
if (!source) return;
|
|
89
|
-
while (track.children.length > count) track.removeChild(track.lastElementChild);
|
|
90
|
-
while (track.children.length < count) {
|
|
91
|
-
const clone = source.cloneNode(true);
|
|
92
|
-
clone.setAttribute("aria-hidden", "true");
|
|
93
|
-
clone.dataset.enigmaMarqueeCopy = String(track.children.length);
|
|
94
|
-
track.appendChild(clone);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
function measure() {
|
|
98
|
-
if (destroyed) return;
|
|
99
|
-
const cloning = opts.copies === "clone";
|
|
100
|
-
if (cloning && track.children.length < 2) syncClones(2);
|
|
101
|
-
let measured = readPeriod();
|
|
102
|
-
let needed = requiredCopies(measured);
|
|
103
|
-
if (cloning && track.children.length !== needed) {
|
|
104
|
-
syncClones(needed);
|
|
105
|
-
measured = readPeriod();
|
|
106
|
-
needed = requiredCopies(measured);
|
|
107
|
-
}
|
|
108
|
-
const changed = measured !== period || needed !== copyCount;
|
|
109
|
-
period = measured;
|
|
110
|
-
if (needed !== copyCount) {
|
|
111
|
-
copyCount = needed;
|
|
112
|
-
opts.onCopyCountChange?.(needed);
|
|
113
|
-
}
|
|
114
|
-
if (period > 0) offset = wrap(offset);
|
|
115
|
-
render();
|
|
116
|
-
if (changed) opts.onMeasure?.(period, copyCount);
|
|
117
|
-
start();
|
|
118
|
-
}
|
|
119
|
-
function wrap(value) {
|
|
120
|
-
if (period <= 0) return value;
|
|
121
|
-
return (value % period + period) % period;
|
|
122
|
-
}
|
|
123
|
-
function tick(now) {
|
|
124
|
-
frame = 0;
|
|
125
|
-
if (destroyed) return;
|
|
126
|
-
const dt = Math.min((now - lastTime) / 1e3, MAX_DT);
|
|
127
|
-
lastTime = now;
|
|
128
|
-
if (!dragging) {
|
|
129
|
-
velocity += (target() - velocity) * (1 - Math.pow(opts.decay, dt));
|
|
130
|
-
offset = wrap(offset + velocity * dt);
|
|
131
|
-
}
|
|
132
|
-
render();
|
|
133
|
-
const idle = !dragging && Math.abs(velocity) < IDLE_EPSILON && Math.abs(target()) < IDLE_EPSILON;
|
|
134
|
-
if (idle) {
|
|
135
|
-
velocity = 0;
|
|
136
|
-
running = false;
|
|
137
|
-
return;
|
|
138
|
-
}
|
|
139
|
-
frame = requestAnimationFrame(tick);
|
|
140
|
-
}
|
|
141
|
-
function start() {
|
|
142
|
-
if (destroyed || running) return;
|
|
143
|
-
running = true;
|
|
144
|
-
lastTime = performance.now();
|
|
145
|
-
frame = requestAnimationFrame(tick);
|
|
146
|
-
}
|
|
147
|
-
function pointerDelta(event) {
|
|
148
|
-
return opts.vertical ? event.clientY : event.clientX;
|
|
149
|
-
}
|
|
150
|
-
function onPointerDown(event) {
|
|
151
|
-
if (event.pointerType !== "mouse") noteTouch();
|
|
152
|
-
if (!opts.draggable || event.button > 0 || pointerId !== null) return;
|
|
153
|
-
pointerId = event.pointerId;
|
|
154
|
-
dragging = true;
|
|
155
|
-
lastPointer = pointerDelta(event);
|
|
156
|
-
startPointer = lastPointer;
|
|
157
|
-
lastMoveTime = event.timeStamp;
|
|
158
|
-
maxTravel = 0;
|
|
159
|
-
lane.dataset.dragging = "true";
|
|
160
|
-
window.addEventListener("pointermove", onPointerMove);
|
|
161
|
-
window.addEventListener("pointerup", onPointerUp);
|
|
162
|
-
window.addEventListener("pointercancel", onPointerUp);
|
|
163
|
-
start();
|
|
164
|
-
}
|
|
165
|
-
function onPointerMove(event) {
|
|
166
|
-
if (event.pointerId !== pointerId) return;
|
|
167
|
-
const position = pointerDelta(event);
|
|
168
|
-
const delta = position - lastPointer;
|
|
169
|
-
lastPointer = position;
|
|
170
|
-
const dt = Math.max((event.timeStamp - lastMoveTime) / 1e3, 1e-3);
|
|
171
|
-
lastMoveTime = event.timeStamp;
|
|
172
|
-
maxTravel = Math.max(maxTravel, Math.abs(position - startPointer));
|
|
173
|
-
offset = wrap(offset - delta);
|
|
174
|
-
const instant = -delta / dt;
|
|
175
|
-
velocity = velocity * VELOCITY_SMOOTHING + instant * (1 - VELOCITY_SMOOTHING);
|
|
176
|
-
}
|
|
177
|
-
function onPointerUp(event) {
|
|
178
|
-
if (event.pointerId !== pointerId) return;
|
|
179
|
-
pointerId = null;
|
|
180
|
-
dragging = false;
|
|
181
|
-
delete lane.dataset.dragging;
|
|
182
|
-
window.removeEventListener("pointermove", onPointerMove);
|
|
183
|
-
window.removeEventListener("pointerup", onPointerUp);
|
|
184
|
-
window.removeEventListener("pointercancel", onPointerUp);
|
|
185
|
-
if (event.timeStamp - lastMoveTime > STILL_MS) velocity = 0;
|
|
186
|
-
start();
|
|
187
|
-
}
|
|
188
|
-
function onClickCapture(event) {
|
|
189
|
-
if (maxTravel <= DRAG_THRESHOLD) return;
|
|
190
|
-
event.preventDefault();
|
|
191
|
-
event.stopPropagation();
|
|
192
|
-
}
|
|
193
|
-
function onClick(event) {
|
|
194
|
-
if (maxTravel > DRAG_THRESHOLD) return;
|
|
195
|
-
opts.onItemClick?.(event);
|
|
196
|
-
}
|
|
197
|
-
function onPointerEnter(event) {
|
|
198
|
-
if (event.pointerType !== "mouse") {
|
|
199
|
-
noteTouch();
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
202
|
-
if (Date.now() - lastTouchAt < TOUCH_HOVER_SUPPRESS_MS) return;
|
|
203
|
-
hovering = true;
|
|
204
|
-
lane.dataset.hovering = "true";
|
|
205
|
-
start();
|
|
206
|
-
}
|
|
207
|
-
function onPointerLeave() {
|
|
208
|
-
hovering = false;
|
|
209
|
-
delete lane.dataset.hovering;
|
|
210
|
-
start();
|
|
211
|
-
}
|
|
212
|
-
function noteTouch() {
|
|
213
|
-
lastTouchAt = Date.now();
|
|
214
|
-
if (!hovering) return;
|
|
215
|
-
hovering = false;
|
|
216
|
-
delete lane.dataset.hovering;
|
|
217
|
-
start();
|
|
218
|
-
}
|
|
219
|
-
function onMotionChange(event) {
|
|
220
|
-
reducedMotion = event.matches;
|
|
221
|
-
lane.dataset.reducedMotion = String(reducedMotion);
|
|
222
|
-
start();
|
|
223
|
-
}
|
|
224
|
-
const resizeObserver = typeof ResizeObserver === "function" ? new ResizeObserver(() => measure()) : null;
|
|
225
|
-
function init() {
|
|
226
|
-
applyStyles();
|
|
227
|
-
lane.dataset.enigmaMarquee = "";
|
|
228
|
-
lane.dataset.reducedMotion = String(reducedMotion);
|
|
229
|
-
track.dataset.enigmaMarqueeTrack = "";
|
|
230
|
-
for (const child of Array.from(track.children)) {
|
|
231
|
-
child.dataset.enigmaMarqueeCopy ??= "0";
|
|
232
|
-
}
|
|
233
|
-
lane.addEventListener("pointerdown", onPointerDown);
|
|
234
|
-
lane.addEventListener("pointerenter", onPointerEnter);
|
|
235
|
-
lane.addEventListener("pointerleave", onPointerLeave);
|
|
236
|
-
lane.addEventListener("touchstart", noteTouch, { passive: true });
|
|
237
|
-
lane.addEventListener("click", onClickCapture, true);
|
|
238
|
-
lane.addEventListener("click", onClick);
|
|
239
|
-
motionQuery?.addEventListener("change", onMotionChange);
|
|
240
|
-
resizeObserver?.observe(lane);
|
|
241
|
-
const first = track.children[0];
|
|
242
|
-
if (first) resizeObserver?.observe(first);
|
|
243
|
-
measure();
|
|
244
|
-
void document.fonts?.ready.then(() => measure());
|
|
245
|
-
remeasureOnImageLoad();
|
|
246
|
-
}
|
|
247
|
-
function remeasureOnImageLoad() {
|
|
248
|
-
for (const image of track.querySelectorAll("img")) {
|
|
249
|
-
if (image.complete) continue;
|
|
250
|
-
const done = () => {
|
|
251
|
-
image.removeEventListener("load", done);
|
|
252
|
-
image.removeEventListener("error", done);
|
|
253
|
-
if (!destroyed) measure();
|
|
254
|
-
};
|
|
255
|
-
image.addEventListener("load", done);
|
|
256
|
-
image.addEventListener("error", done);
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
init();
|
|
260
|
-
return {
|
|
261
|
-
get offset() {
|
|
262
|
-
return offset;
|
|
263
|
-
},
|
|
264
|
-
get period() {
|
|
265
|
-
return period;
|
|
266
|
-
},
|
|
267
|
-
get copyCount() {
|
|
268
|
-
return copyCount;
|
|
269
|
-
},
|
|
270
|
-
get dragging() {
|
|
271
|
-
return dragging;
|
|
272
|
-
},
|
|
273
|
-
get reducedMotion() {
|
|
274
|
-
return reducedMotion;
|
|
275
|
-
},
|
|
276
|
-
update(next) {
|
|
277
|
-
const styleKeys = next.vertical !== void 0 || next.manageStyles !== void 0;
|
|
278
|
-
opts = { ...opts, ...next };
|
|
279
|
-
if (styleKeys) applyStyles();
|
|
280
|
-
measure();
|
|
281
|
-
},
|
|
282
|
-
measure,
|
|
283
|
-
pause() {
|
|
284
|
-
paused = true;
|
|
285
|
-
start();
|
|
286
|
-
},
|
|
287
|
-
resume() {
|
|
288
|
-
paused = false;
|
|
289
|
-
start();
|
|
290
|
-
},
|
|
291
|
-
destroy() {
|
|
292
|
-
destroyed = true;
|
|
293
|
-
if (frame) cancelAnimationFrame(frame);
|
|
294
|
-
resizeObserver?.disconnect();
|
|
295
|
-
motionQuery?.removeEventListener("change", onMotionChange);
|
|
296
|
-
lane.removeEventListener("pointerdown", onPointerDown);
|
|
297
|
-
lane.removeEventListener("pointerenter", onPointerEnter);
|
|
298
|
-
lane.removeEventListener("pointerleave", onPointerLeave);
|
|
299
|
-
lane.removeEventListener("touchstart", noteTouch);
|
|
300
|
-
lane.removeEventListener("click", onClickCapture, true);
|
|
301
|
-
lane.removeEventListener("click", onClick);
|
|
302
|
-
window.removeEventListener("pointermove", onPointerMove);
|
|
303
|
-
window.removeEventListener("pointerup", onPointerUp);
|
|
304
|
-
window.removeEventListener("pointercancel", onPointerUp);
|
|
305
|
-
}
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
// src/core/input.ts
|
|
310
|
-
var INPUT_ICON_PATHS = {
|
|
311
|
-
eye: ["M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7-10-7-10-7Z", "M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z"],
|
|
312
|
-
eyeOff: [
|
|
313
|
-
"M10.6 5.2A9.8 9.8 0 0 1 12 5c6.5 0 10 7 10 7a17.6 17.6 0 0 1-3.2 4.2M6.2 6.2A17.7 17.7 0 0 0 2 12s3.5 7 10 7a9.6 9.6 0 0 0 4.2-.9",
|
|
314
|
-
"m2 2 20 20",
|
|
315
|
-
"M9.9 9.9a3 3 0 0 0 4.2 4.2"
|
|
316
|
-
],
|
|
317
|
-
generate: ["m12 3 1.9 4.6L18.5 9.5 13.9 11.4 12 16l-1.9-4.6L5.5 9.5l4.6-1.9Z", "M19 15l.8 2.2 2.2.8-2.2.8-.8 2.2-.8-2.2-2.2-.8 2.2-.8Z"]
|
|
318
|
-
};
|
|
319
|
-
function iconMarkup(paths) {
|
|
320
|
-
const body = paths.map((path) => `<path d="${path}"/>`).join("");
|
|
321
|
-
return `<svg viewBox="0 0 24 24" width="1em" height="1em" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">${body}</svg>`;
|
|
322
|
-
}
|
|
323
|
-
var EYE = iconMarkup(INPUT_ICON_PATHS.eye);
|
|
324
|
-
var EYE_OFF = iconMarkup(INPUT_ICON_PATHS.eyeOff);
|
|
325
|
-
function resolve(value, state) {
|
|
326
|
-
return typeof value === "function" ? value(state) : value;
|
|
327
|
-
}
|
|
328
|
-
function paint(target, icon) {
|
|
329
|
-
if (typeof icon === "string") target.innerHTML = icon;
|
|
330
|
-
else {
|
|
331
|
-
target.replaceChildren(icon);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
function createInput(input, options = {}) {
|
|
335
|
-
let opts = { ...options };
|
|
336
|
-
let revealed = false;
|
|
337
|
-
let destroyed = false;
|
|
338
|
-
let restoreTimer = null;
|
|
339
|
-
const owned = !opts.container;
|
|
340
|
-
const container = opts.container ?? document.createElement("span");
|
|
341
|
-
const buttons = /* @__PURE__ */ new Map();
|
|
342
|
-
function state() {
|
|
343
|
-
return { input, revealed, locked: input.disabled || input.readOnly };
|
|
344
|
-
}
|
|
345
|
-
function revealConfig() {
|
|
346
|
-
return typeof opts.reveal === "object" ? opts.reveal : {};
|
|
347
|
-
}
|
|
348
|
-
function revealAction() {
|
|
349
|
-
const config = revealConfig();
|
|
350
|
-
return {
|
|
351
|
-
name: "reveal",
|
|
352
|
-
label: (current) => current.revealed ? config.hideLabel ?? "Hide password" : config.showLabel ?? "Show password",
|
|
353
|
-
icon: (current) => current.revealed ? config.hideIcon ?? EYE_OFF : config.showIcon ?? EYE,
|
|
354
|
-
pressed: (current) => current.revealed,
|
|
355
|
-
onSelect: () => setRevealed(!revealed)
|
|
356
|
-
};
|
|
357
|
-
}
|
|
358
|
-
function isPasswordField() {
|
|
359
|
-
return input.type === "password" || revealed && input.dataset.enigmaInputReveal === "on";
|
|
360
|
-
}
|
|
361
|
-
function activeActions() {
|
|
362
|
-
const custom = opts.actions ?? [];
|
|
363
|
-
const wantsReveal = opts.reveal !== false && isPasswordField();
|
|
364
|
-
const builtIn = wantsReveal && !custom.some((action) => action.name === "reveal") ? [revealAction()] : [];
|
|
365
|
-
return [...builtIn, ...custom];
|
|
366
|
-
}
|
|
367
|
-
function setRevealed(next) {
|
|
368
|
-
if (!isPasswordField() && next) return;
|
|
369
|
-
const selection = readSelection();
|
|
370
|
-
revealed = next;
|
|
371
|
-
input.type = next ? "text" : "password";
|
|
372
|
-
input.dataset.enigmaInputReveal = next ? "on" : "off";
|
|
373
|
-
render();
|
|
374
|
-
if (selection) {
|
|
375
|
-
applySelection(selection);
|
|
376
|
-
if (restoreTimer !== null) clearTimeout(restoreTimer);
|
|
377
|
-
restoreTimer = setTimeout(() => {
|
|
378
|
-
restoreTimer = null;
|
|
379
|
-
if (!destroyed && document.activeElement === input) applySelection(selection);
|
|
380
|
-
}, 0);
|
|
381
|
-
}
|
|
382
|
-
opts.onRevealChange?.(next);
|
|
383
|
-
}
|
|
384
|
-
function readSelection() {
|
|
385
|
-
if (document.activeElement !== input) return null;
|
|
386
|
-
try {
|
|
387
|
-
const { selectionStart, selectionEnd } = input;
|
|
388
|
-
return selectionStart === null || selectionEnd === null ? null : [selectionStart, selectionEnd];
|
|
389
|
-
} catch {
|
|
390
|
-
return null;
|
|
391
|
-
}
|
|
392
|
-
}
|
|
393
|
-
function applySelection(selection) {
|
|
394
|
-
try {
|
|
395
|
-
input.setSelectionRange(selection[0], selection[1]);
|
|
396
|
-
} catch {
|
|
397
|
-
}
|
|
398
|
-
}
|
|
399
|
-
function render() {
|
|
400
|
-
if (destroyed) return;
|
|
401
|
-
const selection = readSelection();
|
|
402
|
-
const current = state();
|
|
403
|
-
const actions = activeActions();
|
|
404
|
-
const seen = /* @__PURE__ */ new Set();
|
|
405
|
-
for (const action of actions) {
|
|
406
|
-
seen.add(action.name);
|
|
407
|
-
const visible = action.visible ? action.visible(current) : true;
|
|
408
|
-
let button = buttons.get(action.name);
|
|
409
|
-
if (!button) {
|
|
410
|
-
button = document.createElement("button");
|
|
411
|
-
button.type = "button";
|
|
412
|
-
button.dataset.enigmaInputAction = action.name;
|
|
413
|
-
buttons.set(action.name, button);
|
|
414
|
-
container.append(button);
|
|
415
|
-
}
|
|
416
|
-
if (!visible) {
|
|
417
|
-
button.remove();
|
|
418
|
-
buttons.delete(action.name);
|
|
419
|
-
continue;
|
|
420
|
-
}
|
|
421
|
-
if (!button.isConnected) container.append(button);
|
|
422
|
-
const label = resolve(action.label, current);
|
|
423
|
-
button.setAttribute("aria-label", label);
|
|
424
|
-
button.title = label;
|
|
425
|
-
button.disabled = current.locked;
|
|
426
|
-
if (action.pressed) button.setAttribute("aria-pressed", String(action.pressed(current)));
|
|
427
|
-
else button.removeAttribute("aria-pressed");
|
|
428
|
-
paint(button, resolve(action.icon, current));
|
|
429
|
-
}
|
|
430
|
-
for (const [name, button] of [...buttons]) {
|
|
431
|
-
if (seen.has(name)) continue;
|
|
432
|
-
button.remove();
|
|
433
|
-
buttons.delete(name);
|
|
434
|
-
}
|
|
435
|
-
container.hidden = buttons.size === 0;
|
|
436
|
-
if (selection) {
|
|
437
|
-
try {
|
|
438
|
-
input.setSelectionRange(selection[0], selection[1]);
|
|
439
|
-
} catch {
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
function onMouseDown(event) {
|
|
444
|
-
if (!event.target?.closest("[data-enigma-input-action]")) return;
|
|
445
|
-
event.preventDefault();
|
|
446
|
-
}
|
|
447
|
-
function onClick(event) {
|
|
448
|
-
const target = event.target?.closest("[data-enigma-input-action]");
|
|
449
|
-
if (!target) return;
|
|
450
|
-
const action = activeActions().find((candidate) => candidate.name === target.dataset.enigmaInputAction);
|
|
451
|
-
if (!action) return;
|
|
452
|
-
event.preventDefault();
|
|
453
|
-
action.onSelect(state());
|
|
454
|
-
}
|
|
455
|
-
const observer = typeof MutationObserver === "function" ? new MutationObserver(() => {
|
|
456
|
-
if (!isPasswordField() && revealed) revealed = false;
|
|
457
|
-
render();
|
|
458
|
-
}) : null;
|
|
459
|
-
function mount() {
|
|
460
|
-
input.dataset.enigmaInput = "";
|
|
461
|
-
container.dataset.enigmaInputActions = "";
|
|
462
|
-
container.dataset.position = opts.position ?? "end";
|
|
463
|
-
if (owned && !container.isConnected) {
|
|
464
|
-
if ((opts.position ?? "end") === "start") input.before(container);
|
|
465
|
-
else input.after(container);
|
|
466
|
-
}
|
|
467
|
-
container.addEventListener("mousedown", onMouseDown);
|
|
468
|
-
container.addEventListener("click", onClick);
|
|
469
|
-
observer?.observe(input, { attributes: true, attributeFilter: ["type", "disabled", "readonly"] });
|
|
470
|
-
render();
|
|
471
|
-
}
|
|
472
|
-
mount();
|
|
473
|
-
return {
|
|
474
|
-
get revealed() {
|
|
475
|
-
return revealed;
|
|
476
|
-
},
|
|
477
|
-
reveal(next) {
|
|
478
|
-
setRevealed(next ?? !revealed);
|
|
479
|
-
},
|
|
480
|
-
refresh: render,
|
|
481
|
-
update(next) {
|
|
482
|
-
const moved = next.position !== void 0 && next.position !== opts.position;
|
|
483
|
-
opts = { ...opts, ...next };
|
|
484
|
-
if (moved && owned) {
|
|
485
|
-
container.dataset.position = opts.position ?? "end";
|
|
486
|
-
if (opts.position === "start") input.before(container);
|
|
487
|
-
else input.after(container);
|
|
488
|
-
}
|
|
489
|
-
render();
|
|
490
|
-
},
|
|
491
|
-
destroy() {
|
|
492
|
-
destroyed = true;
|
|
493
|
-
if (restoreTimer !== null) clearTimeout(restoreTimer);
|
|
494
|
-
observer?.disconnect();
|
|
495
|
-
container.removeEventListener("mousedown", onMouseDown);
|
|
496
|
-
container.removeEventListener("click", onClick);
|
|
497
|
-
for (const button of buttons.values()) button.remove();
|
|
498
|
-
buttons.clear();
|
|
499
|
-
if (owned) container.remove();
|
|
500
|
-
delete input.dataset.enigmaInput;
|
|
501
|
-
delete input.dataset.enigmaInputReveal;
|
|
502
|
-
if (revealed) input.type = "password";
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
}
|
|
506
|
-
|
|
507
|
-
// src/core/button.ts
|
|
508
|
-
var TICK_MS = 100;
|
|
509
|
-
function store(cooldown) {
|
|
510
|
-
if (!cooldown.key || typeof window === "undefined") return null;
|
|
511
|
-
try {
|
|
512
|
-
const target = cooldown.storage === "local" ? window.localStorage : window.sessionStorage;
|
|
513
|
-
const probe = "__enigma_probe__";
|
|
514
|
-
target.setItem(probe, "1");
|
|
515
|
-
target.removeItem(probe);
|
|
516
|
-
return target;
|
|
517
|
-
} catch {
|
|
518
|
-
return null;
|
|
519
|
-
}
|
|
520
|
-
}
|
|
521
|
-
function normalize(cooldown) {
|
|
522
|
-
if (cooldown == null) return null;
|
|
523
|
-
return typeof cooldown === "number" ? { ms: cooldown } : cooldown;
|
|
524
|
-
}
|
|
525
|
-
function isTyping(target) {
|
|
526
|
-
const element = target;
|
|
527
|
-
if (!element) return false;
|
|
528
|
-
const tag = element.tagName;
|
|
529
|
-
return tag === "INPUT" || tag === "TEXTAREA" || tag === "SELECT" || element.isContentEditable === true;
|
|
530
|
-
}
|
|
531
|
-
function createButton(options = {}) {
|
|
532
|
-
let opts = { ...options };
|
|
533
|
-
let loading = Boolean(opts.loading);
|
|
534
|
-
let readyAt = 0;
|
|
535
|
-
let timer = null;
|
|
536
|
-
let destroyed = false;
|
|
537
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
538
|
-
const storageKey = () => {
|
|
539
|
-
const cooldown = normalize(opts.cooldown);
|
|
540
|
-
return cooldown?.key ? `enigma:cooldown:${cooldown.key}` : null;
|
|
541
|
-
};
|
|
542
|
-
function restore() {
|
|
543
|
-
const cooldown = normalize(opts.cooldown);
|
|
544
|
-
if (!cooldown) return;
|
|
545
|
-
const target = store(cooldown);
|
|
546
|
-
const key = storageKey();
|
|
547
|
-
if (!target || !key) return;
|
|
548
|
-
const saved = Number(target.getItem(key));
|
|
549
|
-
if (Number.isFinite(saved) && saved > Date.now()) readyAt = saved;
|
|
550
|
-
}
|
|
551
|
-
function remaining() {
|
|
552
|
-
return Math.max(0, readyAt - Date.now());
|
|
553
|
-
}
|
|
554
|
-
function snapshot() {
|
|
555
|
-
const disabled = Boolean(opts.disabled);
|
|
556
|
-
const cooldown = remaining();
|
|
557
|
-
return {
|
|
558
|
-
element: opts.href ? "a" : "button",
|
|
559
|
-
available: !disabled && !loading && cooldown === 0,
|
|
560
|
-
loading,
|
|
561
|
-
disabled,
|
|
562
|
-
cooldown,
|
|
563
|
-
shortcut: opts.shortcut ?? null
|
|
564
|
-
};
|
|
565
|
-
}
|
|
566
|
-
function emit() {
|
|
567
|
-
const state = snapshot();
|
|
568
|
-
opts.onChange?.(state);
|
|
569
|
-
for (const listener of listeners) listener(state);
|
|
570
|
-
}
|
|
571
|
-
function stopTicking() {
|
|
572
|
-
if (timer === null) return;
|
|
573
|
-
clearInterval(timer);
|
|
574
|
-
timer = null;
|
|
575
|
-
}
|
|
576
|
-
function startTicking() {
|
|
577
|
-
if (timer !== null || remaining() === 0) return;
|
|
578
|
-
timer = setInterval(() => {
|
|
579
|
-
if (remaining() > 0) {
|
|
580
|
-
emit();
|
|
581
|
-
return;
|
|
582
|
-
}
|
|
583
|
-
stopTicking();
|
|
584
|
-
emit();
|
|
585
|
-
}, TICK_MS);
|
|
586
|
-
}
|
|
587
|
-
function beginCooldown() {
|
|
588
|
-
const cooldown = normalize(opts.cooldown);
|
|
589
|
-
if (!cooldown || cooldown.ms <= 0) return;
|
|
590
|
-
readyAt = Date.now() + cooldown.ms;
|
|
591
|
-
const target = store(cooldown);
|
|
592
|
-
const key = storageKey();
|
|
593
|
-
if (target && key) {
|
|
594
|
-
try {
|
|
595
|
-
target.setItem(key, String(readyAt));
|
|
596
|
-
} catch {
|
|
597
|
-
}
|
|
598
|
-
}
|
|
599
|
-
startTicking();
|
|
600
|
-
}
|
|
601
|
-
async function press(event) {
|
|
602
|
-
if (destroyed || !snapshot().available) return;
|
|
603
|
-
const result = opts.onPress?.(event);
|
|
604
|
-
if (result instanceof Promise) {
|
|
605
|
-
loading = true;
|
|
606
|
-
emit();
|
|
607
|
-
try {
|
|
608
|
-
await result;
|
|
609
|
-
} finally {
|
|
610
|
-
loading = false;
|
|
611
|
-
beginCooldown();
|
|
612
|
-
emit();
|
|
613
|
-
}
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
beginCooldown();
|
|
617
|
-
emit();
|
|
618
|
-
}
|
|
619
|
-
function onKeyDown(event) {
|
|
620
|
-
if (!opts.shortcut || isTyping(event.target)) return;
|
|
621
|
-
if (event.ctrlKey || event.metaKey || event.altKey || event.shiftKey) return;
|
|
622
|
-
if (event.key.toLowerCase() !== opts.shortcut.toLowerCase()) return;
|
|
623
|
-
if (!snapshot().available) return;
|
|
624
|
-
event.preventDefault();
|
|
625
|
-
void press(event);
|
|
626
|
-
}
|
|
627
|
-
if (typeof window !== "undefined") {
|
|
628
|
-
restore();
|
|
629
|
-
startTicking();
|
|
630
|
-
window.addEventListener("keydown", onKeyDown);
|
|
631
|
-
}
|
|
632
|
-
return {
|
|
633
|
-
get state() {
|
|
634
|
-
return snapshot();
|
|
635
|
-
},
|
|
636
|
-
press,
|
|
637
|
-
update(next) {
|
|
638
|
-
const hadCooldown = JSON.stringify(normalize(opts.cooldown));
|
|
639
|
-
opts = { ...opts, ...next };
|
|
640
|
-
if (next.loading !== void 0) loading = Boolean(next.loading);
|
|
641
|
-
if (JSON.stringify(normalize(opts.cooldown)) !== hadCooldown) restore();
|
|
642
|
-
emit();
|
|
643
|
-
},
|
|
644
|
-
reset() {
|
|
645
|
-
readyAt = 0;
|
|
646
|
-
stopTicking();
|
|
647
|
-
const cooldown = normalize(opts.cooldown);
|
|
648
|
-
const key = storageKey();
|
|
649
|
-
if (cooldown && key) store(cooldown)?.removeItem(key);
|
|
650
|
-
emit();
|
|
651
|
-
},
|
|
652
|
-
subscribe(listener) {
|
|
653
|
-
listeners.add(listener);
|
|
654
|
-
return () => {
|
|
655
|
-
listeners.delete(listener);
|
|
656
|
-
};
|
|
657
|
-
},
|
|
658
|
-
destroy() {
|
|
659
|
-
destroyed = true;
|
|
660
|
-
stopTicking();
|
|
661
|
-
listeners.clear();
|
|
662
|
-
if (typeof window !== "undefined") window.removeEventListener("keydown", onKeyDown);
|
|
663
|
-
}
|
|
664
|
-
};
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
// src/core/password.ts
|
|
668
|
-
var LOWERCASE = "abcdefghijklmnopqrstuvwxyz";
|
|
669
|
-
var UPPERCASE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
670
|
-
var DIGITS = "0123456789";
|
|
671
|
-
var SYMBOLS = "!@#$%^&*()-_=+[]{};:,.?";
|
|
672
|
-
var AMBIGUOUS = "Il1O0";
|
|
673
|
-
function randomIndex(bound) {
|
|
674
|
-
const random = globalThis.crypto?.getRandomValues?.bind(globalThis.crypto);
|
|
675
|
-
if (!random) {
|
|
676
|
-
throw new Error("Generating a password needs crypto.getRandomValues, which browsers only expose over HTTPS (or on localhost).");
|
|
677
|
-
}
|
|
678
|
-
const limit = Math.floor(2 ** 32 / bound) * bound;
|
|
679
|
-
const buffer = new Uint32Array(1);
|
|
680
|
-
let value;
|
|
681
|
-
do {
|
|
682
|
-
random(buffer);
|
|
683
|
-
value = buffer[0];
|
|
684
|
-
} while (value >= limit);
|
|
685
|
-
return value % bound;
|
|
686
|
-
}
|
|
687
|
-
function pick(alphabet) {
|
|
688
|
-
return alphabet[randomIndex(alphabet.length)];
|
|
689
|
-
}
|
|
690
|
-
function classes(options) {
|
|
691
|
-
const { lowercase = true, uppercase = true, digits = true, symbols = true, excludeAmbiguous = false, exclude = "" } = options;
|
|
692
|
-
const banned = /* @__PURE__ */ new Set([...excludeAmbiguous ? AMBIGUOUS : "", ...exclude]);
|
|
693
|
-
const clean = (source) => [...source].filter((character) => !banned.has(character)).join("");
|
|
694
|
-
return [
|
|
695
|
-
lowercase ? clean(LOWERCASE) : "",
|
|
696
|
-
uppercase ? clean(UPPERCASE) : "",
|
|
697
|
-
digits ? clean(DIGITS) : "",
|
|
698
|
-
symbols ? clean(SYMBOLS) : ""
|
|
699
|
-
].filter(Boolean);
|
|
700
|
-
}
|
|
701
|
-
function shuffle(characters) {
|
|
702
|
-
for (let index = characters.length - 1; index > 0; index--) {
|
|
703
|
-
const swap = randomIndex(index + 1);
|
|
704
|
-
[characters[index], characters[swap]] = [characters[swap], characters[index]];
|
|
705
|
-
}
|
|
706
|
-
return characters;
|
|
707
|
-
}
|
|
708
|
-
function generatePassword(options = {}) {
|
|
709
|
-
const { length = 20, requireEachClass = true } = options;
|
|
710
|
-
const pools = classes(options);
|
|
711
|
-
if (!pools.length) throw new Error("generatePassword: every character class was excluded.");
|
|
712
|
-
if (length < 1) throw new Error("generatePassword: length must be at least 1.");
|
|
713
|
-
if (requireEachClass && length < pools.length) {
|
|
714
|
-
throw new Error(`generatePassword: length ${length} cannot hold one character from each of the ${pools.length} classes requested.`);
|
|
715
|
-
}
|
|
716
|
-
const everything = pools.join("");
|
|
717
|
-
const required = requireEachClass ? pools.map(pick) : [];
|
|
718
|
-
const rest = Array.from({ length: length - required.length }, () => pick(everything));
|
|
719
|
-
return shuffle([...required, ...rest]).join("");
|
|
720
|
-
}
|
|
721
|
-
var COMMON = /* @__PURE__ */ new Set([
|
|
722
|
-
"password",
|
|
723
|
-
"passwd",
|
|
724
|
-
"123456",
|
|
725
|
-
"12345678",
|
|
726
|
-
"123456789",
|
|
727
|
-
"1234567890",
|
|
728
|
-
"qwerty",
|
|
729
|
-
"qwertyuiop",
|
|
730
|
-
"abc123",
|
|
731
|
-
"111111",
|
|
732
|
-
"123123",
|
|
733
|
-
"admin",
|
|
734
|
-
"letmein",
|
|
735
|
-
"welcome",
|
|
736
|
-
"monkey",
|
|
737
|
-
"dragon",
|
|
738
|
-
"sunshine",
|
|
739
|
-
"iloveyou",
|
|
740
|
-
"princess",
|
|
741
|
-
"football",
|
|
742
|
-
"baseball",
|
|
743
|
-
"master",
|
|
744
|
-
"shadow",
|
|
745
|
-
"superman",
|
|
746
|
-
"batman",
|
|
747
|
-
"trustno1",
|
|
748
|
-
"hello",
|
|
749
|
-
"freedom",
|
|
750
|
-
"whatever",
|
|
751
|
-
"starwars",
|
|
752
|
-
"changeme",
|
|
753
|
-
"secret",
|
|
754
|
-
"login",
|
|
755
|
-
"root",
|
|
756
|
-
"toor",
|
|
757
|
-
"test",
|
|
758
|
-
"guest",
|
|
759
|
-
"azerty",
|
|
760
|
-
"1q2w3e4r",
|
|
761
|
-
"zaq12wsx"
|
|
762
|
-
]);
|
|
763
|
-
var SEQUENCES = ["abcdefghijklmnopqrstuvwxyz", "0123456789", "qwertyuiop", "asdfghjkl", "zxcvbnm"];
|
|
764
|
-
function poolSize(password) {
|
|
765
|
-
let size = 0;
|
|
766
|
-
if (/[a-z]/.test(password)) size += 26;
|
|
767
|
-
if (/[A-Z]/.test(password)) size += 26;
|
|
768
|
-
if (/\d/.test(password)) size += 10;
|
|
769
|
-
if (/[^\w\s]|_/.test(password)) size += SYMBOLS.length;
|
|
770
|
-
if (/\s/.test(password)) size += 1;
|
|
771
|
-
return size || 1;
|
|
772
|
-
}
|
|
773
|
-
function longestRun(password) {
|
|
774
|
-
let longest = 1, run = 1;
|
|
775
|
-
for (let index = 1; index < password.length; index++) {
|
|
776
|
-
run = password[index] === password[index - 1] ? run + 1 : 1;
|
|
777
|
-
longest = Math.max(longest, run);
|
|
778
|
-
}
|
|
779
|
-
return longest;
|
|
780
|
-
}
|
|
781
|
-
function longestSequence(password) {
|
|
782
|
-
const lower = password.toLowerCase();
|
|
783
|
-
let longest = 0;
|
|
784
|
-
for (const line of SEQUENCES) {
|
|
785
|
-
const reversed = [...line].reverse().join("");
|
|
786
|
-
for (const source of [line, reversed]) {
|
|
787
|
-
for (let start = 0; start < source.length; start++) {
|
|
788
|
-
for (let end = source.length; end > start + longest; end--) {
|
|
789
|
-
if (lower.includes(source.slice(start, end))) {
|
|
790
|
-
longest = Math.max(longest, end - start);
|
|
791
|
-
break;
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
return longest;
|
|
798
|
-
}
|
|
799
|
-
function core(password) {
|
|
800
|
-
return password.toLowerCase().replace(/^[^a-z]+/, "").replace(/[^a-z]+$/, "").replace(/[0!@$]/g, (character) => ({ "0": "o", "!": "i", "@": "a", "$": "s" })[character] ?? character);
|
|
801
|
-
}
|
|
802
|
-
function estimatePasswordStrength(password, options = {}) {
|
|
803
|
-
if (!password) return { score: 0, bits: 0, warnings: [], empty: true };
|
|
804
|
-
const warnings = [];
|
|
805
|
-
let bits = password.length * Math.log2(poolSize(password));
|
|
806
|
-
const stripped = core(password);
|
|
807
|
-
if (COMMON.has(password.toLowerCase()) || COMMON.has(stripped)) {
|
|
808
|
-
bits = Math.min(bits, 8);
|
|
809
|
-
warnings.push("This is one of the most common passwords there is.");
|
|
810
|
-
}
|
|
811
|
-
for (const input of options.userInputs ?? []) {
|
|
812
|
-
const needle = input.trim().toLowerCase();
|
|
813
|
-
if (needle.length < 4 || !password.toLowerCase().includes(needle)) continue;
|
|
814
|
-
bits = Math.min(bits, 16);
|
|
815
|
-
warnings.push("It contains something you already typed on this form.");
|
|
816
|
-
break;
|
|
817
|
-
}
|
|
818
|
-
const run = longestRun(password);
|
|
819
|
-
if (run >= 3) {
|
|
820
|
-
bits -= (run - 2) * Math.log2(poolSize(password));
|
|
821
|
-
warnings.push("A character repeats several times in a row.");
|
|
822
|
-
}
|
|
823
|
-
const sequence = longestSequence(password);
|
|
824
|
-
if (sequence >= 4) {
|
|
825
|
-
bits -= sequence * Math.log2(poolSize(password)) * 0.75;
|
|
826
|
-
warnings.push("Part of it runs straight along the keyboard or the alphabet.");
|
|
827
|
-
}
|
|
828
|
-
if (password.length < 8) warnings.push("Short passwords fall to a brute force whatever they contain.");
|
|
829
|
-
bits = Math.max(0, Math.round(bits));
|
|
830
|
-
const score = bits < 28 ? 0 : bits < 40 ? 1 : bits < 60 ? 2 : bits < 80 ? 3 : 4;
|
|
831
|
-
return { score, bits, warnings, empty: false };
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
// src/core/notifications.ts
|
|
835
|
-
var DEFAULT_MAX = 4;
|
|
836
|
-
var DEFAULT_DURATION = 5e3;
|
|
837
|
-
function createNotifications(options = {}) {
|
|
838
|
-
const view = typeof document === "undefined" ? null : document;
|
|
839
|
-
const max = options.max ?? DEFAULT_MAX;
|
|
840
|
-
const defaultDuration = options.duration ?? DEFAULT_DURATION;
|
|
841
|
-
const sticky = /* @__PURE__ */ new Set([...options.stickyTones ?? ["error"], "loading"]);
|
|
842
|
-
let items = [];
|
|
843
|
-
let paused = false;
|
|
844
|
-
let sequence = 0;
|
|
845
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
846
|
-
const timers = /* @__PURE__ */ new Map();
|
|
847
|
-
function emit() {
|
|
848
|
-
const snapshot = Object.freeze([...items]);
|
|
849
|
-
for (const listener of listeners) listener(snapshot);
|
|
850
|
-
}
|
|
851
|
-
function clearTimer(id) {
|
|
852
|
-
const timer = timers.get(id);
|
|
853
|
-
if (timer?.handle) clearTimeout(timer.handle);
|
|
854
|
-
timers.delete(id);
|
|
855
|
-
}
|
|
856
|
-
function startTimer(id, remaining) {
|
|
857
|
-
if (!Number.isFinite(remaining) || remaining <= 0) return;
|
|
858
|
-
const handle = paused ? null : setTimeout(() => dismiss(id), remaining);
|
|
859
|
-
timers.set(id, { handle, remaining, startedAt: Date.now() });
|
|
860
|
-
}
|
|
861
|
-
function dismiss(id) {
|
|
862
|
-
const next = items.filter((item) => item.id !== id);
|
|
863
|
-
if (next.length === items.length) return;
|
|
864
|
-
items = next;
|
|
865
|
-
clearTimer(id);
|
|
866
|
-
emit();
|
|
867
|
-
}
|
|
868
|
-
function notify(input) {
|
|
869
|
-
const tone = input.tone ?? "info";
|
|
870
|
-
const duration = input.duration ?? (sticky.has(tone) ? Infinity : defaultDuration);
|
|
871
|
-
const existing = input.key ? items.find((item) => item.key === input.key) : void 0;
|
|
872
|
-
const id = existing?.id ?? `n${++sequence}`;
|
|
873
|
-
const notification = {
|
|
874
|
-
id,
|
|
875
|
-
key: input.key,
|
|
876
|
-
title: input.title,
|
|
877
|
-
body: input.body,
|
|
878
|
-
tone,
|
|
879
|
-
duration,
|
|
880
|
-
action: input.action,
|
|
881
|
-
data: input.data,
|
|
882
|
-
createdAt: Date.now()
|
|
883
|
-
};
|
|
884
|
-
items = existing ? items.map((item) => item.id === id ? notification : item) : [...items, notification];
|
|
885
|
-
if (items.length > max) {
|
|
886
|
-
const evictable = items.find((item) => Number.isFinite(item.duration) && item.id !== id);
|
|
887
|
-
if (evictable) {
|
|
888
|
-
items = items.filter((item) => item.id !== evictable.id);
|
|
889
|
-
clearTimer(evictable.id);
|
|
890
|
-
} else {
|
|
891
|
-
items = items.slice(items.length - max);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
clearTimer(id);
|
|
895
|
-
startTimer(id, duration);
|
|
896
|
-
emit();
|
|
897
|
-
return id;
|
|
898
|
-
}
|
|
899
|
-
const onVisibility = () => {
|
|
900
|
-
if (!view) return;
|
|
901
|
-
if (view.hidden) instance.pause();
|
|
902
|
-
else instance.resume();
|
|
903
|
-
};
|
|
904
|
-
function update(id, patch) {
|
|
905
|
-
const current = items.find((item) => item.id === id);
|
|
906
|
-
if (!current) return;
|
|
907
|
-
const tone = patch.tone ?? current.tone;
|
|
908
|
-
const duration = patch.duration ?? (patch.tone ? sticky.has(tone) ? Infinity : defaultDuration : current.duration);
|
|
909
|
-
items = items.map((item) => item.id === id ? { ...current, ...patch, tone, duration } : item);
|
|
910
|
-
clearTimer(id);
|
|
911
|
-
startTimer(id, duration);
|
|
912
|
-
emit();
|
|
913
|
-
}
|
|
914
|
-
function resolveMessage(message, value) {
|
|
915
|
-
const resolved = typeof message === "function" ? message(value) : message;
|
|
916
|
-
return typeof resolved === "string" ? { title: resolved } : resolved;
|
|
917
|
-
}
|
|
918
|
-
async function promise(work, messages) {
|
|
919
|
-
const start = typeof messages.loading === "string" ? { title: messages.loading } : messages.loading;
|
|
920
|
-
const key = start.key ?? `p${++sequence}`;
|
|
921
|
-
notify({ ...start, key, tone: "loading" });
|
|
922
|
-
try {
|
|
923
|
-
const value = await work;
|
|
924
|
-
notify({ ...resolveMessage(messages.success, value), key, tone: "success" });
|
|
925
|
-
return value;
|
|
926
|
-
} catch (error) {
|
|
927
|
-
notify({ ...resolveMessage(messages.error, error), key, tone: "error" });
|
|
928
|
-
throw error;
|
|
929
|
-
}
|
|
930
|
-
}
|
|
931
|
-
const instance = {
|
|
932
|
-
get items() {
|
|
933
|
-
return Object.freeze([...items]);
|
|
934
|
-
},
|
|
935
|
-
notify,
|
|
936
|
-
update,
|
|
937
|
-
promise,
|
|
938
|
-
dismiss,
|
|
939
|
-
dismissAll() {
|
|
940
|
-
for (const item of items) clearTimer(item.id);
|
|
941
|
-
items = [];
|
|
942
|
-
emit();
|
|
943
|
-
},
|
|
944
|
-
pause() {
|
|
945
|
-
if (paused) return;
|
|
946
|
-
paused = true;
|
|
947
|
-
for (const [id, timer] of timers) {
|
|
948
|
-
if (!timer.handle) continue;
|
|
949
|
-
clearTimeout(timer.handle);
|
|
950
|
-
timers.set(id, {
|
|
951
|
-
handle: null,
|
|
952
|
-
remaining: Math.max(timer.remaining - (Date.now() - timer.startedAt), 0),
|
|
953
|
-
startedAt: Date.now()
|
|
954
|
-
});
|
|
955
|
-
}
|
|
956
|
-
},
|
|
957
|
-
resume() {
|
|
958
|
-
if (!paused) return;
|
|
959
|
-
paused = false;
|
|
960
|
-
for (const [id, timer] of [...timers]) {
|
|
961
|
-
clearTimer(id);
|
|
962
|
-
startTimer(id, timer.remaining);
|
|
963
|
-
}
|
|
964
|
-
},
|
|
965
|
-
subscribe(listener) {
|
|
966
|
-
listeners.add(listener);
|
|
967
|
-
return () => {
|
|
968
|
-
listeners.delete(listener);
|
|
969
|
-
};
|
|
970
|
-
},
|
|
971
|
-
destroy() {
|
|
972
|
-
for (const item of items) clearTimer(item.id);
|
|
973
|
-
items = [];
|
|
974
|
-
listeners.clear();
|
|
975
|
-
view?.removeEventListener("visibilitychange", onVisibility);
|
|
976
|
-
}
|
|
977
|
-
};
|
|
978
|
-
view?.addEventListener("visibilitychange", onVisibility);
|
|
979
|
-
return instance;
|
|
980
|
-
}
|
|
981
|
-
|
|
982
|
-
// src/core/network.ts
|
|
983
|
-
var SERVER_NETWORK_STATE = {
|
|
984
|
-
online: true,
|
|
985
|
-
downlink: null,
|
|
986
|
-
downlinkMax: null,
|
|
987
|
-
effectiveType: null,
|
|
988
|
-
rtt: null,
|
|
989
|
-
saveData: null,
|
|
990
|
-
type: null,
|
|
991
|
-
recovered: false,
|
|
992
|
-
slow: false
|
|
993
|
-
};
|
|
994
|
-
function connection() {
|
|
995
|
-
if (typeof navigator === "undefined") return null;
|
|
996
|
-
const nav = navigator;
|
|
997
|
-
return nav.connection ?? nav.mozConnection ?? nav.webkitConnection ?? null;
|
|
998
|
-
}
|
|
999
|
-
function shallowEqual(left, right) {
|
|
1000
|
-
for (const key of Object.keys(left)) {
|
|
1001
|
-
if (left[key] !== right[key]) return false;
|
|
1002
|
-
}
|
|
1003
|
-
return true;
|
|
1004
|
-
}
|
|
1005
|
-
function createNetworkMonitor() {
|
|
1006
|
-
if (typeof window === "undefined") {
|
|
1007
|
-
return {
|
|
1008
|
-
get state() {
|
|
1009
|
-
return SERVER_NETWORK_STATE;
|
|
1010
|
-
},
|
|
1011
|
-
subscribe() {
|
|
1012
|
-
return () => {
|
|
1013
|
-
};
|
|
1014
|
-
},
|
|
1015
|
-
destroy() {
|
|
1016
|
-
}
|
|
1017
|
-
};
|
|
1018
|
-
}
|
|
1019
|
-
const listeners = /* @__PURE__ */ new Set();
|
|
1020
|
-
let hasBeenOffline = false;
|
|
1021
|
-
let state = read();
|
|
1022
|
-
function read() {
|
|
1023
|
-
const link2 = connection();
|
|
1024
|
-
const online = navigator.onLine;
|
|
1025
|
-
const effectiveType = link2?.effectiveType ?? null;
|
|
1026
|
-
const saveData = link2?.saveData ?? null;
|
|
1027
|
-
return {
|
|
1028
|
-
online,
|
|
1029
|
-
downlink: link2?.downlink ?? null,
|
|
1030
|
-
downlinkMax: link2?.downlinkMax ?? null,
|
|
1031
|
-
effectiveType,
|
|
1032
|
-
rtt: link2?.rtt ?? null,
|
|
1033
|
-
saveData,
|
|
1034
|
-
type: link2?.type ?? null,
|
|
1035
|
-
recovered: online && hasBeenOffline,
|
|
1036
|
-
slow: saveData === true || effectiveType === "2g" || effectiveType === "slow-2g"
|
|
1037
|
-
};
|
|
1038
|
-
}
|
|
1039
|
-
function update() {
|
|
1040
|
-
if (!navigator.onLine) hasBeenOffline = true;
|
|
1041
|
-
const next = read();
|
|
1042
|
-
if (shallowEqual(state, next)) return;
|
|
1043
|
-
state = next;
|
|
1044
|
-
for (const listener of listeners) listener(state);
|
|
1045
|
-
}
|
|
1046
|
-
const link = connection();
|
|
1047
|
-
window.addEventListener("online", update, { passive: true });
|
|
1048
|
-
window.addEventListener("offline", update, { passive: true });
|
|
1049
|
-
link?.addEventListener("change", update, { passive: true });
|
|
1050
|
-
return {
|
|
1051
|
-
get state() {
|
|
1052
|
-
return state;
|
|
1053
|
-
},
|
|
1054
|
-
subscribe(listener) {
|
|
1055
|
-
listeners.add(listener);
|
|
1056
|
-
return () => {
|
|
1057
|
-
listeners.delete(listener);
|
|
1058
|
-
};
|
|
1059
|
-
},
|
|
1060
|
-
destroy() {
|
|
1061
|
-
listeners.clear();
|
|
1062
|
-
window.removeEventListener("online", update);
|
|
1063
|
-
window.removeEventListener("offline", update);
|
|
1064
|
-
link?.removeEventListener("change", update);
|
|
1065
|
-
}
|
|
1066
|
-
};
|
|
1067
|
-
}
|
|
1068
|
-
|
|
1069
|
-
// src/core/relative-time.ts
|
|
1070
|
-
var ZONED = /([zZ]|[+-]\d{2}:?\d{2})$/;
|
|
1071
|
-
var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
|
|
1072
|
-
var HAS_TIME = /\d{2}:\d{2}/;
|
|
1073
|
-
var DURATION = /^([+-])?P(?:(\d+(?:\.\d+)?)Y)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)W)?(?:(\d+(?:\.\d+)?)D)?(?:T(?:(\d+(?:\.\d+)?)H)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)S)?)?$/;
|
|
1074
|
-
var SECOND = 1e3;
|
|
1075
|
-
var MINUTE = 60 * SECOND;
|
|
1076
|
-
var HOUR = 60 * MINUTE;
|
|
1077
|
-
var DAY = 24 * HOUR;
|
|
1078
|
-
var WEEK = 7 * DAY;
|
|
1079
|
-
var MONTH = 30 * DAY;
|
|
1080
|
-
var YEAR = 365 * DAY;
|
|
1081
|
-
function ensureZone(text) {
|
|
1082
|
-
const trimmed = text.trim();
|
|
1083
|
-
if (!trimmed || DATE_ONLY.test(trimmed) || ZONED.test(trimmed)) return trimmed;
|
|
1084
|
-
const iso = trimmed.replace(" ", "T");
|
|
1085
|
-
return HAS_TIME.test(iso) ? `${iso}Z` : iso;
|
|
1086
|
-
}
|
|
1087
|
-
function normalizeDate(value) {
|
|
1088
|
-
if (value == null) return null;
|
|
1089
|
-
if (value instanceof Date) return Number.isNaN(value.getTime()) ? null : value;
|
|
1090
|
-
if (typeof value === "number") return Number.isFinite(value) ? new Date(value) : null;
|
|
1091
|
-
const date = new Date(ensureZone(value));
|
|
1092
|
-
return Number.isNaN(date.getTime()) ? null : date;
|
|
1093
|
-
}
|
|
1094
|
-
function parseDuration(value) {
|
|
1095
|
-
const match = DURATION.exec(value.trim());
|
|
1096
|
-
if (!match) return 0;
|
|
1097
|
-
const [, sign, years, months, weeks, days, hours, minutes, seconds] = match;
|
|
1098
|
-
const n = (part) => part ? Number(part) : 0;
|
|
1099
|
-
const total = n(years) * YEAR + n(months) * MONTH + n(weeks) * WEEK + n(days) * DAY + n(hours) * HOUR + n(minutes) * MINUTE + n(seconds) * SECOND;
|
|
1100
|
-
return sign === "-" ? -total : total;
|
|
1101
|
-
}
|
|
1102
|
-
function dateTimeOptions(options) {
|
|
1103
|
-
const { second, minute, hour, weekday, day, month, year, timeZone, timeZoneName } = options;
|
|
1104
|
-
const parts = { timeZone, timeZoneName, second, minute, hour, weekday, day, month, year };
|
|
1105
|
-
for (const key of Object.keys(parts)) {
|
|
1106
|
-
if (parts[key] === void 0) delete parts[key];
|
|
1107
|
-
}
|
|
1108
|
-
return parts;
|
|
1109
|
-
}
|
|
1110
|
-
function format(date, locale, options) {
|
|
1111
|
-
try {
|
|
1112
|
-
return new Intl.DateTimeFormat(locale, options).format(date);
|
|
1113
|
-
} catch {
|
|
1114
|
-
return date.toISOString();
|
|
1115
|
-
}
|
|
1116
|
-
}
|
|
1117
|
-
function relativeTimeView(value, options = {}, now = /* @__PURE__ */ new Date()) {
|
|
1118
|
-
const date = normalizeDate(value);
|
|
1119
|
-
if (!date) return { date: null, iso: "", label: "", exact: "", beyondThreshold: false, absoluteOnly: false };
|
|
1120
|
-
const { locale, threshold = "P30D", prefix = "on", numericBeyondThreshold = false } = options;
|
|
1121
|
-
const age = Math.abs(date.getTime() - now.getTime());
|
|
1122
|
-
const beyondThreshold = age > parseDuration(threshold);
|
|
1123
|
-
const absoluteOnly = numericBeyondThreshold && beyondThreshold;
|
|
1124
|
-
const parts = absoluteOnly ? { day: "numeric", month: "numeric", year: "numeric", timeZone: options.timeZone } : { day: "numeric", month: "short", year: "numeric", ...dateTimeOptions(options) };
|
|
1125
|
-
const label = format(date, locale, parts);
|
|
1126
|
-
return {
|
|
1127
|
-
date,
|
|
1128
|
-
iso: date.toISOString(),
|
|
1129
|
-
label: !absoluteOnly && prefix ? `${prefix} ${label}` : label,
|
|
1130
|
-
exact: format(date, locale, { dateStyle: "full", timeStyle: "long", timeZone: options.timeZone }),
|
|
1131
|
-
beyondThreshold,
|
|
1132
|
-
absoluteOnly
|
|
1133
|
-
};
|
|
1134
|
-
}
|
|
1135
|
-
function relativeTimeAttributes(view, options = {}) {
|
|
1136
|
-
const attributes = {
|
|
1137
|
-
datetime: view.iso,
|
|
1138
|
-
format: options.format,
|
|
1139
|
-
tense: options.tense,
|
|
1140
|
-
precision: options.precision,
|
|
1141
|
-
threshold: options.threshold,
|
|
1142
|
-
prefix: options.prefix,
|
|
1143
|
-
"format-style": options.formatStyle,
|
|
1144
|
-
second: options.second,
|
|
1145
|
-
minute: options.minute,
|
|
1146
|
-
hour: options.hour,
|
|
1147
|
-
weekday: options.weekday,
|
|
1148
|
-
day: options.day,
|
|
1149
|
-
month: options.month,
|
|
1150
|
-
year: options.year,
|
|
1151
|
-
lang: options.locale,
|
|
1152
|
-
"time-zone": options.timeZone,
|
|
1153
|
-
"time-zone-name": options.timeZoneName,
|
|
1154
|
-
// Boolean attributes are read by presence: "false" would still be true.
|
|
1155
|
-
"no-title": options.noTitle ? "" : void 0
|
|
1156
|
-
};
|
|
1157
|
-
const out = {};
|
|
1158
|
-
for (const [key, value] of Object.entries(attributes)) {
|
|
1159
|
-
if (value !== void 0) out[key] = value;
|
|
1160
|
-
}
|
|
1161
|
-
return out;
|
|
1162
|
-
}
|
|
1163
|
-
|
|
1164
|
-
export { INPUT_ICON_PATHS, SERVER_NETWORK_STATE, createButton, createInput, createMarquee, createNetworkMonitor, createNotifications, ensureZone, estimatePasswordStrength, generatePassword, iconMarkup, normalizeDate, parseDuration, relativeTimeAttributes, relativeTimeView };
|