autokap 1.0.7 → 1.0.8
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/assets/cursors/macos.svg +4 -0
- package/assets/cursors/windows.svg +15 -0
- package/assets/skill/OPCODE-REFERENCE.md +607 -0
- package/assets/skill/README.md +39 -0
- package/assets/skill/SKILL.md +453 -468
- package/assets/skill/STUDIO-SKILL.md +476 -0
- package/assets/skill/references/examples.md +104 -0
- package/assets/skill/references/interactive-demo.md +225 -0
- package/assets/skill/references/mock-data.md +178 -0
- package/dist/action-verifier.d.ts +29 -0
- package/dist/action-verifier.js +133 -0
- package/dist/agent-action-recovery.d.ts +45 -0
- package/dist/agent-action-recovery.js +370 -0
- package/dist/agent-message-utils.d.ts +21 -0
- package/dist/agent-message-utils.js +77 -0
- package/dist/agent-url-utils.d.ts +30 -0
- package/dist/agent-url-utils.js +138 -0
- package/dist/agent.d.ts +92 -8
- package/dist/agent.js +2936 -781
- package/dist/ak-tree.d.ts +39 -0
- package/dist/ak-tree.js +368 -0
- package/dist/alt-text.d.ts +26 -0
- package/dist/alt-text.js +55 -0
- package/dist/auth-capture.d.ts +17 -0
- package/dist/auth-capture.js +164 -0
- package/dist/benchmark.d.ts +59 -0
- package/dist/benchmark.js +135 -0
- package/dist/browser-bar.d.ts +14 -6
- package/dist/browser-bar.js +145 -8
- package/dist/browser-pool.d.ts +7 -0
- package/dist/browser-pool.js +15 -5
- package/dist/browser-utils.d.ts +31 -0
- package/dist/browser-utils.js +97 -0
- package/dist/browser.d.ts +51 -1
- package/dist/browser.js +1481 -31
- package/dist/capture-alt-text.js +2 -1
- package/dist/capture-language-preflight.js +14 -0
- package/dist/capture-llm-page-identity.js +22 -10
- package/dist/capture-page-identity.d.ts +5 -7
- package/dist/capture-page-identity.js +211 -78
- package/dist/capture-preset-credentials.d.ts +50 -0
- package/dist/capture-preset-credentials.js +127 -0
- package/dist/capture-request-plan.d.ts +2 -2
- package/dist/capture-request-plan.js +64 -16
- package/dist/capture-run-optimizer.js +48 -33
- package/dist/capture-selector-memory.d.ts +5 -0
- package/dist/capture-selector-memory.js +18 -0
- package/dist/capture-strategy.d.ts +36 -0
- package/dist/capture-strategy.js +95 -0
- package/dist/capture-studio-sync.d.ts +1 -0
- package/dist/capture-studio-sync.js +9 -3
- package/dist/capture-surface-contract.d.ts +36 -0
- package/dist/capture-surface-contract.js +299 -0
- package/dist/capture-transition-engine.d.ts +28 -0
- package/dist/capture-transition-engine.js +292 -0
- package/dist/capture-variant-state.d.ts +2 -0
- package/dist/capture-variant-state.js +26 -0
- package/dist/capture-verification.d.ts +35 -0
- package/dist/capture-verification.js +95 -0
- package/dist/capture-viewport-lock.d.ts +48 -0
- package/dist/capture-viewport-lock.js +74 -0
- package/dist/circuit-breaker.d.ts +42 -0
- package/dist/circuit-breaker.js +119 -0
- package/dist/cli-config.d.ts +8 -1
- package/dist/cli-config.js +62 -6
- package/dist/cli-contract.d.ts +15 -0
- package/dist/cli-contract.js +167 -0
- package/dist/cli-runner-local.d.ts +12 -0
- package/dist/cli-runner-local.js +102 -0
- package/dist/cli-runner.d.ts +34 -0
- package/dist/cli-runner.js +433 -0
- package/dist/cli-utils.d.ts +0 -1
- package/dist/cli-utils.js +2 -5
- package/dist/cli.js +1005 -267
- package/dist/clip-orchestrator.js +9 -2
- package/dist/clip-postprocess.js +25 -16
- package/dist/cookie-dismiss.d.ts +2 -0
- package/dist/cookie-dismiss.js +48 -13
- package/dist/cost-logging.d.ts +8 -0
- package/dist/cost-logging.js +160 -46
- package/dist/cost-resolution-monitor.d.ts +16 -0
- package/dist/cost-resolution-monitor.js +34 -0
- package/dist/credential-templates.js +2 -2
- package/dist/cursor-overlay-script.d.ts +6 -0
- package/dist/cursor-overlay-script.js +169 -0
- package/dist/dom-css-purger.d.ts +65 -0
- package/dist/dom-css-purger.js +333 -0
- package/dist/dom-font-inliner.d.ts +45 -0
- package/dist/dom-font-inliner.js +148 -0
- package/dist/dom-patch-resolver.d.ts +52 -0
- package/dist/dom-patch-resolver.js +242 -0
- package/dist/dom-serializer.d.ts +82 -0
- package/dist/dom-serializer.js +378 -0
- package/dist/element-capture.d.ts +1 -41
- package/dist/element-capture.js +202 -446
- package/dist/env-validation.d.ts +5 -0
- package/dist/env-validation.js +29 -0
- package/dist/execution-schema.d.ts +4423 -0
- package/dist/execution-schema.js +507 -0
- package/dist/execution-types.d.ts +886 -0
- package/dist/execution-types.js +65 -0
- package/dist/fonts-loader.d.ts +14 -0
- package/dist/fonts-loader.js +55 -0
- package/dist/hybrid-navigator.js +12 -12
- package/dist/index.d.ts +9 -6
- package/dist/index.js +10 -4
- package/dist/legacy/agent-action-recovery.d.ts +45 -0
- package/dist/legacy/agent-action-recovery.js +370 -0
- package/dist/legacy/agent-message-utils.d.ts +21 -0
- package/dist/legacy/agent-message-utils.js +77 -0
- package/dist/legacy/agent-url-utils.d.ts +30 -0
- package/dist/legacy/agent-url-utils.js +138 -0
- package/dist/legacy/agent.d.ts +226 -0
- package/dist/legacy/agent.js +6666 -0
- package/dist/legacy/clip-orchestrator.d.ts +148 -0
- package/dist/legacy/clip-orchestrator.js +957 -0
- package/dist/legacy/credential-templates.d.ts +5 -0
- package/dist/legacy/credential-templates.js +60 -0
- package/dist/legacy/hybrid-navigator.d.ts +138 -0
- package/dist/legacy/hybrid-navigator.js +468 -0
- package/dist/legacy/llm-usage.d.ts +17 -0
- package/dist/legacy/llm-usage.js +45 -0
- package/dist/legacy/prompt-cache.d.ts +10 -0
- package/dist/legacy/prompt-cache.js +24 -0
- package/dist/legacy/prompts.d.ts +175 -0
- package/dist/legacy/prompts.js +1038 -0
- package/dist/legacy/tools.d.ts +4 -0
- package/dist/legacy/tools.js +216 -0
- package/dist/legacy/video-agent.d.ts +143 -0
- package/dist/legacy/video-agent.js +4788 -0
- package/dist/legacy/video-observation.d.ts +36 -0
- package/dist/legacy/video-observation.js +192 -0
- package/dist/legacy/video-planner.d.ts +12 -0
- package/dist/legacy/video-planner.js +501 -0
- package/dist/legacy/video-prompts.d.ts +37 -0
- package/dist/legacy/video-prompts.js +569 -0
- package/dist/legacy/video-tools.d.ts +3 -0
- package/dist/legacy/video-tools.js +59 -0
- package/dist/legacy/video-variant-state.d.ts +29 -0
- package/dist/legacy/video-variant-state.js +80 -0
- package/dist/legacy/vision-model.d.ts +17 -0
- package/dist/legacy/vision-model.js +74 -0
- package/dist/llm-healer.d.ts +63 -0
- package/dist/llm-healer.js +166 -0
- package/dist/llm-provider.d.ts +29 -0
- package/dist/llm-provider.js +80 -0
- package/dist/logger.d.ts +6 -2
- package/dist/logger.js +15 -1
- package/dist/mockup-html.js +35 -25
- package/dist/mockup.d.ts +95 -2
- package/dist/mockup.js +427 -166
- package/dist/mouse-animation.d.ts +2 -2
- package/dist/mouse-animation.js +34 -20
- package/dist/opcode-actions.d.ts +42 -0
- package/dist/opcode-actions.js +511 -0
- package/dist/opcode-runner.d.ts +51 -0
- package/dist/opcode-runner.js +770 -0
- package/dist/openrouter-client.d.ts +40 -0
- package/dist/openrouter-client.js +16 -0
- package/dist/overlay-engine.d.ts +24 -0
- package/dist/overlay-engine.js +176 -0
- package/dist/postcondition.d.ts +16 -0
- package/dist/postcondition.js +269 -0
- package/dist/program-patcher.d.ts +25 -0
- package/dist/program-patcher.js +44 -0
- package/dist/prompts.d.ts +13 -5
- package/dist/prompts.js +224 -351
- package/dist/provider-config.d.ts +12 -0
- package/dist/provider-config.js +15 -0
- package/dist/recovery-chain.d.ts +37 -0
- package/dist/recovery-chain.js +350 -0
- package/dist/remote-browser.d.ts +28 -4
- package/dist/remote-browser.js +60 -5
- package/dist/safari-browser-bar.d.ts +15 -0
- package/dist/safari-browser-bar.js +95 -0
- package/dist/safari-toolbar-asset.d.ts +15 -0
- package/dist/safari-toolbar-asset.js +12 -0
- package/dist/security.d.ts +2 -1
- package/dist/security.js +49 -10
- package/dist/selector-resolver.d.ts +34 -0
- package/dist/selector-resolver.js +181 -0
- package/dist/semantic-resolver.d.ts +35 -0
- package/dist/semantic-resolver.js +161 -0
- package/dist/server-capture-runtime.d.ts +5 -3
- package/dist/server-capture-runtime.js +42 -95
- package/dist/server-credit-usage.d.ts +2 -2
- package/dist/server-project-webhooks.d.ts +15 -1
- package/dist/server-project-webhooks.js +34 -8
- package/dist/server-screenshot-watermark.js +27 -5
- package/dist/session-profile.js +164 -1
- package/dist/sf-pro-symbols.d.ts +1 -0
- package/dist/sf-pro-symbols.js +55 -0
- package/dist/skill-packaging.d.ts +28 -0
- package/dist/skill-packaging.js +169 -0
- package/dist/smart-wait.d.ts +27 -0
- package/dist/smart-wait.js +81 -0
- package/dist/status-bar-render.d.ts +20 -0
- package/dist/status-bar-render.js +410 -0
- package/dist/status-bar.d.ts +9 -0
- package/dist/status-bar.js +298 -14
- package/dist/svg-browser-bar.d.ts +33 -0
- package/dist/svg-browser-bar.js +206 -0
- package/dist/svg-status-bar.d.ts +36 -0
- package/dist/svg-status-bar.js +597 -0
- package/dist/svg-text.d.ts +61 -0
- package/dist/svg-text.js +118 -0
- package/dist/tools.js +89 -451
- package/dist/types.d.ts +240 -5
- package/dist/types.js +23 -1
- package/dist/v2/action-verifier.d.ts +29 -0
- package/dist/v2/action-verifier.js +133 -0
- package/dist/v2/alt-text.d.ts +26 -0
- package/dist/v2/alt-text.js +55 -0
- package/dist/v2/benchmark.d.ts +59 -0
- package/dist/v2/benchmark.js +135 -0
- package/dist/v2/capture-strategy.d.ts +30 -0
- package/dist/v2/capture-strategy.js +67 -0
- package/dist/v2/capture-verification.d.ts +35 -0
- package/dist/v2/capture-verification.js +95 -0
- package/dist/v2/circuit-breaker.d.ts +42 -0
- package/dist/v2/circuit-breaker.js +119 -0
- package/dist/v2/cli-runner-local.d.ts +11 -0
- package/dist/v2/cli-runner-local.js +91 -0
- package/dist/v2/cli-runner.d.ts +34 -0
- package/dist/v2/cli-runner.js +300 -0
- package/dist/v2/compiler-prompts.d.ts +27 -0
- package/dist/v2/compiler-prompts.js +123 -0
- package/dist/v2/compiler.d.ts +37 -0
- package/dist/v2/compiler.js +147 -0
- package/dist/v2/explorer.d.ts +41 -0
- package/dist/v2/explorer.js +56 -0
- package/dist/v2/index.d.ts +37 -0
- package/dist/v2/index.js +31 -0
- package/dist/v2/llm-healer.d.ts +62 -0
- package/dist/v2/llm-healer.js +166 -0
- package/dist/v2/llm-provider.d.ts +29 -0
- package/dist/v2/llm-provider.js +80 -0
- package/dist/v2/opcode-runner.d.ts +47 -0
- package/dist/v2/opcode-runner.js +634 -0
- package/dist/v2/overlay-engine.d.ts +24 -0
- package/dist/v2/overlay-engine.js +150 -0
- package/dist/v2/postcondition.d.ts +16 -0
- package/dist/v2/postcondition.js +249 -0
- package/dist/v2/program-patcher.d.ts +25 -0
- package/dist/v2/program-patcher.js +44 -0
- package/dist/v2/recovery-chain.d.ts +30 -0
- package/dist/v2/recovery-chain.js +368 -0
- package/dist/v2/schema.d.ts +2580 -0
- package/dist/v2/schema.js +295 -0
- package/dist/v2/selector-resolver.d.ts +34 -0
- package/dist/v2/selector-resolver.js +181 -0
- package/dist/v2/semantic-resolver.d.ts +35 -0
- package/dist/v2/semantic-resolver.js +161 -0
- package/dist/v2/smart-wait.d.ts +27 -0
- package/dist/v2/smart-wait.js +81 -0
- package/dist/v2/types.d.ts +444 -0
- package/dist/v2/types.js +19 -0
- package/dist/v2/web-playwright-local.d.ts +69 -0
- package/dist/v2/web-playwright-local.js +392 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +5 -0
- package/dist/video-agent.js +18 -13
- package/dist/video-planner.js +2 -1
- package/dist/video-prompts.js +3 -3
- package/dist/web-playwright-local.d.ts +126 -0
- package/dist/web-playwright-local.js +819 -0
- package/dist/ws-auth.js +4 -1
- package/dist/ws-broadcast.d.ts +34 -0
- package/dist/ws-broadcast.js +85 -0
- package/dist/ws-connection-limits.d.ts +12 -0
- package/dist/ws-connection-limits.js +44 -0
- package/dist/ws-handler-utils.d.ts +32 -0
- package/dist/ws-handler-utils.js +139 -0
- package/dist/ws-handler.js +294 -164
- package/dist/ws-metrics-server.d.ts +9 -0
- package/dist/ws-metrics-server.js +31 -0
- package/dist/ws-server.js +41 -1
- package/package.json +51 -34
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
// SVG files are the single source of truth — edit web/public/cursors/*.svg directly.
|
|
5
|
+
function loadCursorSvg(name) {
|
|
6
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
+
const candidatePaths = [
|
|
8
|
+
// From dist/ → ../web/public/cursors/
|
|
9
|
+
path.resolve(currentDir, '../web/public/cursors', `${name}.svg`),
|
|
10
|
+
// From package/dist/ → ../../web/public/cursors/
|
|
11
|
+
path.resolve(currentDir, '../../web/public/cursors', `${name}.svg`),
|
|
12
|
+
// Bundled alongside dist in package/assets/cursors/
|
|
13
|
+
path.resolve(currentDir, '../assets/cursors', `${name}.svg`),
|
|
14
|
+
// Fallback: same directory
|
|
15
|
+
path.resolve(currentDir, './web/public/cursors', `${name}.svg`),
|
|
16
|
+
];
|
|
17
|
+
for (const candidatePath of candidatePaths) {
|
|
18
|
+
if (fs.existsSync(candidatePath)) {
|
|
19
|
+
return fs.readFileSync(candidatePath, 'utf-8').trim();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
throw new Error(`Cursor asset "${name}.svg" not found. Looked in: ${candidatePaths.join(', ')}`);
|
|
23
|
+
}
|
|
24
|
+
const CURSOR_THEME_SVGS = {};
|
|
25
|
+
function getCursorThemeSvg(theme) {
|
|
26
|
+
if (!CURSOR_THEME_SVGS[theme]) {
|
|
27
|
+
CURSOR_THEME_SVGS[theme] = loadCursorSvg(theme);
|
|
28
|
+
}
|
|
29
|
+
return CURSOR_THEME_SVGS[theme];
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* JavaScript injected via `context.addInitScript()` to show a visible animated
|
|
33
|
+
* cursor in Playwright video recordings (the native OS cursor is invisible).
|
|
34
|
+
*/
|
|
35
|
+
export function buildCursorOverlayScript(theme = 'minimal') {
|
|
36
|
+
const cursorSvg = theme === 'minimal' ? null : getCursorThemeSvg(theme);
|
|
37
|
+
return `
|
|
38
|
+
(function() {
|
|
39
|
+
function injectCursor() {
|
|
40
|
+
if (document.getElementById('__ak_cursor__')) return;
|
|
41
|
+
const cursorTheme = ${JSON.stringify(theme)};
|
|
42
|
+
const cursorSvg = ${JSON.stringify(cursorSvg)};
|
|
43
|
+
|
|
44
|
+
const style = document.createElement('style');
|
|
45
|
+
style.textContent = [
|
|
46
|
+
'*, *::before, *::after { cursor: none !important; }',
|
|
47
|
+
'#__ak_cursor__ { mix-blend-mode: normal; }',
|
|
48
|
+
'#__ak_cursor__ svg { width: 100%; height: 100%; display: block; overflow: visible; }',
|
|
49
|
+
'#__ak_cursor__[data-theme="macos"], #__ak_cursor__[data-theme="windows"] { width: 28px !important; height: 28px !important; background: transparent !important; border: 0 !important; border-radius: 0 !important; box-shadow: none !important; }',
|
|
50
|
+
'#__ak_cursor__.__ak_pressed { transform: translate(-50%, -50%) scale(0.72) !important; box-shadow: 0 0 0 8px rgba(37, 99, 235, 0.18), 0 4px 18px rgba(0,0,0,0.28) !important; background: rgba(255,255,255,0.98) !important; border-color: rgba(30,41,59,0.92) !important; }',
|
|
51
|
+
'#__ak_cursor__[data-theme="macos"].__ak_pressed, #__ak_cursor__[data-theme="windows"].__ak_pressed { background: transparent !important; border-color: transparent !important; box-shadow: none !important; filter: drop-shadow(0 0 0 rgba(37, 99, 235, 0)) drop-shadow(0 0 0 rgba(37, 99, 235, 0)) !important; }',
|
|
52
|
+
'#__ak_cursor__[data-theme="macos"].__ak_pressed svg, #__ak_cursor__[data-theme="windows"].__ak_pressed svg { filter: drop-shadow(0 0 0.5px rgba(255,255,255,0.9)) drop-shadow(0 0 10px rgba(37, 99, 235, 0.45)); }',
|
|
53
|
+
'#__ak_cursor_click_pulse__ { position: fixed; width: 20px; height: 20px; border-radius: 9999px; pointer-events: none; z-index: 2147483646; border: 3px solid rgba(37, 99, 235, 0.75); box-shadow: 0 0 0 1px rgba(255,255,255,0.45) inset; transform: translate(-50%, -50%) scale(0.55); opacity: 0; }',
|
|
54
|
+
'#__ak_cursor_click_pulse__.__ak_active { animation: __ak_cursor_pulse__ 340ms cubic-bezier(0.16, 1, 0.3, 1) forwards; }',
|
|
55
|
+
'@keyframes __ak_cursor_pulse__ { 0% { opacity: 0.95; transform: translate(-50%, -50%) scale(0.55); } 100% { opacity: 0; transform: translate(-50%, -50%) scale(2.6); } }',
|
|
56
|
+
].join('\\n');
|
|
57
|
+
document.head.appendChild(style);
|
|
58
|
+
|
|
59
|
+
const cursor = document.createElement('div');
|
|
60
|
+
cursor.id = '__ak_cursor__';
|
|
61
|
+
cursor.dataset.theme = cursorTheme;
|
|
62
|
+
cursor.style.cssText = [
|
|
63
|
+
'position: fixed',
|
|
64
|
+
'top: -100px',
|
|
65
|
+
'left: -100px',
|
|
66
|
+
'width: 20px',
|
|
67
|
+
'height: 20px',
|
|
68
|
+
'background: rgba(255, 255, 255, 0.95)',
|
|
69
|
+
'border: 2.5px solid rgba(0, 0, 0, 0.8)',
|
|
70
|
+
'border-radius: 50%',
|
|
71
|
+
'pointer-events: none',
|
|
72
|
+
'z-index: 2147483647',
|
|
73
|
+
'transform: translate(-50%, -50%)',
|
|
74
|
+
'box-shadow: 0 2px 8px rgba(0,0,0,0.4)',
|
|
75
|
+
'transition: transform 0.09s ease, box-shadow 0.12s ease, background 0.12s ease, border-color 0.12s ease',
|
|
76
|
+
'will-change: left, top',
|
|
77
|
+
].join(';');
|
|
78
|
+
if (cursorSvg) cursor.innerHTML = cursorSvg;
|
|
79
|
+
|
|
80
|
+
const pulse = document.createElement('div');
|
|
81
|
+
pulse.id = '__ak_cursor_click_pulse__';
|
|
82
|
+
|
|
83
|
+
document.body.appendChild(cursor);
|
|
84
|
+
document.body.appendChild(pulse);
|
|
85
|
+
|
|
86
|
+
let pulseResetTimer = null;
|
|
87
|
+
|
|
88
|
+
function setCursorPosition(x, y) {
|
|
89
|
+
cursor.style.left = x + 'px';
|
|
90
|
+
cursor.style.top = y + 'px';
|
|
91
|
+
pulse.style.left = x + 'px';
|
|
92
|
+
pulse.style.top = y + 'px';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function triggerPulse() {
|
|
96
|
+
pulse.classList.remove('__ak_active');
|
|
97
|
+
void pulse.offsetWidth;
|
|
98
|
+
pulse.classList.add('__ak_active');
|
|
99
|
+
if (pulseResetTimer) clearTimeout(pulseResetTimer);
|
|
100
|
+
pulseResetTimer = setTimeout(function() {
|
|
101
|
+
pulse.classList.remove('__ak_active');
|
|
102
|
+
}, 380);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// CDP mouse events (page.mouse.move) don't fire DOM mousemove events in
|
|
106
|
+
// Playwright video recording. Expose global helpers so the runner can
|
|
107
|
+
// position the cursor directly via page.evaluate().
|
|
108
|
+
window.__akMoveCursor = function(x, y) { setCursorPosition(x, y); };
|
|
109
|
+
window.__akPressCursor = function() { cursor.classList.add('__ak_pressed'); };
|
|
110
|
+
window.__akReleaseCursor = function() { cursor.classList.remove('__ak_pressed'); };
|
|
111
|
+
window.__akClickPulse = function(x, y) {
|
|
112
|
+
setCursorPosition(x, y);
|
|
113
|
+
triggerPulse();
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// Keep DOM event listeners as fallback for real mouse events (headed mode)
|
|
117
|
+
document.addEventListener('mousemove', function(e) {
|
|
118
|
+
setCursorPosition(e.clientX, e.clientY);
|
|
119
|
+
}, { passive: true });
|
|
120
|
+
|
|
121
|
+
window.addEventListener('mousedown', function(e) {
|
|
122
|
+
setCursorPosition(e.clientX, e.clientY);
|
|
123
|
+
cursor.classList.add('__ak_pressed');
|
|
124
|
+
triggerPulse();
|
|
125
|
+
}, true);
|
|
126
|
+
window.addEventListener('mouseup', function(e) {
|
|
127
|
+
setCursorPosition(e.clientX, e.clientY);
|
|
128
|
+
cursor.classList.remove('__ak_pressed');
|
|
129
|
+
}, true);
|
|
130
|
+
window.addEventListener('click', function(e) {
|
|
131
|
+
setCursorPosition(e.clientX, e.clientY);
|
|
132
|
+
triggerPulse();
|
|
133
|
+
}, true);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Inject immediately and re-inject if a framework (React, Next.js, etc.)
|
|
137
|
+
// replaces the body during hydration and removes our cursor element.
|
|
138
|
+
function ensureCursor() {
|
|
139
|
+
if (!document.getElementById('__ak_cursor__')) {
|
|
140
|
+
if (document.body) injectCursor();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (document.readyState === 'loading') {
|
|
145
|
+
document.addEventListener('DOMContentLoaded', ensureCursor);
|
|
146
|
+
} else {
|
|
147
|
+
ensureCursor();
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// Re-inject when frameworks replace body content during hydration.
|
|
151
|
+
var observer = new MutationObserver(function() {
|
|
152
|
+
if (document.body && !document.getElementById('__ak_cursor__')) {
|
|
153
|
+
injectCursor();
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
function startObserving() {
|
|
157
|
+
if (document.body) {
|
|
158
|
+
observer.observe(document.body, { childList: true });
|
|
159
|
+
} else {
|
|
160
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
161
|
+
observer.observe(document.body, { childList: true });
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
startObserving();
|
|
166
|
+
})();
|
|
167
|
+
`;
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=cursor-overlay-script.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM CSS Purger — AUT-121 Interactive Demos, Phase 3
|
|
3
|
+
*
|
|
4
|
+
* Walks captured HTML, collects every tag/class/id present in the DOM, then
|
|
5
|
+
* walks each inline `<style>` block and drops CSS rules whose selectors
|
|
6
|
+
* never match any element. Designed to shrink captured DOMs by 60-80% per
|
|
7
|
+
* the Linear estimate.
|
|
8
|
+
*
|
|
9
|
+
* Conservative on purpose — better to keep a few unused bytes than break a
|
|
10
|
+
* captured layout. Specifically, the purger PRESERVES:
|
|
11
|
+
* - All at-rules: @media, @supports, @keyframes, @font-face, @import,
|
|
12
|
+
* @charset, @namespace, @page (passed through unchanged for the rules
|
|
13
|
+
* inside @media/@supports, recursively pruned)
|
|
14
|
+
* - Selectors that only contain pseudo-classes (`:hover`, `:focus`, etc.)
|
|
15
|
+
* since they may match elements only in interactive states
|
|
16
|
+
* - Universal selector `*`
|
|
17
|
+
* - Attribute selectors (parsed but not validated against the DOM —
|
|
18
|
+
* `[data-state]` would require a DOM walk to verify, kept for safety)
|
|
19
|
+
*
|
|
20
|
+
* For Phase 3 minimum viable, only inline `<style>` blocks are purged.
|
|
21
|
+
* External `<link rel="stylesheet">` files would require server-side
|
|
22
|
+
* fetching of the asset bytes — that's left to a follow-up.
|
|
23
|
+
*
|
|
24
|
+
* No regex CSS parser is perfect, but the trade-off is:
|
|
25
|
+
* - dependency-free (we already pay for parse5 in dom-serializer)
|
|
26
|
+
* - small footprint (~150 LOC)
|
|
27
|
+
* - good enough for the 95% of CSS that captures produce
|
|
28
|
+
*
|
|
29
|
+
* Worst case: a malformed CSS block is left untouched.
|
|
30
|
+
*/
|
|
31
|
+
export interface PurgeStats {
|
|
32
|
+
/** Number of `<style>` blocks processed */
|
|
33
|
+
blocks: number;
|
|
34
|
+
/** Bytes of CSS before purging */
|
|
35
|
+
preBytes: number;
|
|
36
|
+
/** Bytes of CSS after purging */
|
|
37
|
+
postBytes: number;
|
|
38
|
+
/** Number of rules removed */
|
|
39
|
+
rulesRemoved: number;
|
|
40
|
+
}
|
|
41
|
+
export interface PurgeResult {
|
|
42
|
+
html: string;
|
|
43
|
+
stats: PurgeStats;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Walk an HTML document, find every `<style>` block, and purge each one
|
|
47
|
+
* against the set of tags/classes/ids actually present in the document.
|
|
48
|
+
*/
|
|
49
|
+
export declare function purgeInlineStyleBlocks(html: string): PurgeResult;
|
|
50
|
+
interface DomPresence {
|
|
51
|
+
tags: Set<string>;
|
|
52
|
+
classes: Set<string>;
|
|
53
|
+
ids: Set<string>;
|
|
54
|
+
}
|
|
55
|
+
interface PurgedCss {
|
|
56
|
+
css: string;
|
|
57
|
+
removed: number;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Tokenize CSS into a flat sequence of `at-rule` / `rule` blocks and walk
|
|
61
|
+
* each rule to decide whether to keep it. At-rules are preserved verbatim
|
|
62
|
+
* — `@media`/`@supports` bodies are recursively purged.
|
|
63
|
+
*/
|
|
64
|
+
export declare function purgeCss(input: string, presence: DomPresence): PurgedCss;
|
|
65
|
+
export {};
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM CSS Purger — AUT-121 Interactive Demos, Phase 3
|
|
3
|
+
*
|
|
4
|
+
* Walks captured HTML, collects every tag/class/id present in the DOM, then
|
|
5
|
+
* walks each inline `<style>` block and drops CSS rules whose selectors
|
|
6
|
+
* never match any element. Designed to shrink captured DOMs by 60-80% per
|
|
7
|
+
* the Linear estimate.
|
|
8
|
+
*
|
|
9
|
+
* Conservative on purpose — better to keep a few unused bytes than break a
|
|
10
|
+
* captured layout. Specifically, the purger PRESERVES:
|
|
11
|
+
* - All at-rules: @media, @supports, @keyframes, @font-face, @import,
|
|
12
|
+
* @charset, @namespace, @page (passed through unchanged for the rules
|
|
13
|
+
* inside @media/@supports, recursively pruned)
|
|
14
|
+
* - Selectors that only contain pseudo-classes (`:hover`, `:focus`, etc.)
|
|
15
|
+
* since they may match elements only in interactive states
|
|
16
|
+
* - Universal selector `*`
|
|
17
|
+
* - Attribute selectors (parsed but not validated against the DOM —
|
|
18
|
+
* `[data-state]` would require a DOM walk to verify, kept for safety)
|
|
19
|
+
*
|
|
20
|
+
* For Phase 3 minimum viable, only inline `<style>` blocks are purged.
|
|
21
|
+
* External `<link rel="stylesheet">` files would require server-side
|
|
22
|
+
* fetching of the asset bytes — that's left to a follow-up.
|
|
23
|
+
*
|
|
24
|
+
* No regex CSS parser is perfect, but the trade-off is:
|
|
25
|
+
* - dependency-free (we already pay for parse5 in dom-serializer)
|
|
26
|
+
* - small footprint (~150 LOC)
|
|
27
|
+
* - good enough for the 95% of CSS that captures produce
|
|
28
|
+
*
|
|
29
|
+
* Worst case: a malformed CSS block is left untouched.
|
|
30
|
+
*/
|
|
31
|
+
import { parse, serialize, defaultTreeAdapter } from 'parse5';
|
|
32
|
+
/**
|
|
33
|
+
* Walk an HTML document, find every `<style>` block, and purge each one
|
|
34
|
+
* against the set of tags/classes/ids actually present in the document.
|
|
35
|
+
*/
|
|
36
|
+
export function purgeInlineStyleBlocks(html) {
|
|
37
|
+
const document = parse(html);
|
|
38
|
+
const presence = collectPresence(document);
|
|
39
|
+
const stats = {
|
|
40
|
+
blocks: 0,
|
|
41
|
+
preBytes: 0,
|
|
42
|
+
postBytes: 0,
|
|
43
|
+
rulesRemoved: 0,
|
|
44
|
+
};
|
|
45
|
+
walk(document, (node) => {
|
|
46
|
+
if (!isElement(node) || node.tagName !== 'style')
|
|
47
|
+
return;
|
|
48
|
+
const text = readText(node);
|
|
49
|
+
if (!text)
|
|
50
|
+
return;
|
|
51
|
+
stats.blocks += 1;
|
|
52
|
+
stats.preBytes += Buffer.byteLength(text, 'utf8');
|
|
53
|
+
const purged = purgeCss(text, presence);
|
|
54
|
+
stats.postBytes += Buffer.byteLength(purged.css, 'utf8');
|
|
55
|
+
stats.rulesRemoved += purged.removed;
|
|
56
|
+
setText(node, purged.css);
|
|
57
|
+
});
|
|
58
|
+
return {
|
|
59
|
+
html: serialize(document),
|
|
60
|
+
stats,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function collectPresence(root) {
|
|
64
|
+
const presence = {
|
|
65
|
+
tags: new Set(),
|
|
66
|
+
classes: new Set(),
|
|
67
|
+
ids: new Set(),
|
|
68
|
+
};
|
|
69
|
+
walk(root, (node) => {
|
|
70
|
+
if (!isElement(node))
|
|
71
|
+
return;
|
|
72
|
+
presence.tags.add(node.tagName);
|
|
73
|
+
for (const attr of node.attrs) {
|
|
74
|
+
if (attr.name === 'class') {
|
|
75
|
+
for (const cls of attr.value.split(/\s+/)) {
|
|
76
|
+
if (cls)
|
|
77
|
+
presence.classes.add(cls);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
else if (attr.name === 'id' && attr.value) {
|
|
81
|
+
presence.ids.add(attr.value);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
return presence;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Tokenize CSS into a flat sequence of `at-rule` / `rule` blocks and walk
|
|
89
|
+
* each rule to decide whether to keep it. At-rules are preserved verbatim
|
|
90
|
+
* — `@media`/`@supports` bodies are recursively purged.
|
|
91
|
+
*/
|
|
92
|
+
export function purgeCss(input, presence) {
|
|
93
|
+
const cleaned = stripComments(input);
|
|
94
|
+
const tokens = tokenize(cleaned);
|
|
95
|
+
let removed = 0;
|
|
96
|
+
const out = [];
|
|
97
|
+
for (const token of tokens) {
|
|
98
|
+
if (token.kind === 'at-rule') {
|
|
99
|
+
const lowerName = token.name.toLowerCase();
|
|
100
|
+
if (lowerName === 'keyframes' || lowerName === '-webkit-keyframes' || lowerName === 'font-face' ||
|
|
101
|
+
lowerName === 'page' || lowerName === 'charset' || lowerName === 'import' || lowerName === 'namespace') {
|
|
102
|
+
// Preserved as-is — keyframes/font-face/page selectors aren't DOM-bound.
|
|
103
|
+
out.push(token.raw);
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
if (lowerName === 'media' || lowerName === 'supports' || lowerName === 'container') {
|
|
107
|
+
// Recursively purge the body of conditional groups.
|
|
108
|
+
const innerPurged = purgeCss(token.body ?? '', presence);
|
|
109
|
+
removed += innerPurged.removed;
|
|
110
|
+
out.push(`@${token.name} ${token.prelude}{${innerPurged.css}}`);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
// Any unknown at-rule: pass through.
|
|
114
|
+
out.push(token.raw);
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
// Regular rule — split selectors on commas (respecting parentheses)
|
|
118
|
+
const selectorList = splitSelectorList(token.selectors);
|
|
119
|
+
const kept = selectorList.filter((sel) => selectorMatches(sel, presence));
|
|
120
|
+
if (kept.length === 0) {
|
|
121
|
+
removed += 1;
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
out.push(`${kept.join(',')}{${token.body}}`);
|
|
125
|
+
}
|
|
126
|
+
return { css: out.join(''), removed };
|
|
127
|
+
}
|
|
128
|
+
function tokenize(input) {
|
|
129
|
+
const tokens = [];
|
|
130
|
+
let i = 0;
|
|
131
|
+
const len = input.length;
|
|
132
|
+
while (i < len) {
|
|
133
|
+
while (i < len && /\s/.test(input[i]))
|
|
134
|
+
i += 1;
|
|
135
|
+
if (i >= len)
|
|
136
|
+
break;
|
|
137
|
+
if (input[i] === '@') {
|
|
138
|
+
const start = i;
|
|
139
|
+
i += 1;
|
|
140
|
+
let nameStart = i;
|
|
141
|
+
while (i < len && /[\w-]/.test(input[i]))
|
|
142
|
+
i += 1;
|
|
143
|
+
const name = input.slice(nameStart, i);
|
|
144
|
+
let prelude = '';
|
|
145
|
+
while (i < len && input[i] !== '{' && input[i] !== ';') {
|
|
146
|
+
prelude += input[i];
|
|
147
|
+
i += 1;
|
|
148
|
+
}
|
|
149
|
+
if (i < len && input[i] === ';') {
|
|
150
|
+
const raw = input.slice(start, i + 1);
|
|
151
|
+
tokens.push({ kind: 'at-rule', name, prelude: prelude.trim(), raw });
|
|
152
|
+
i += 1;
|
|
153
|
+
continue;
|
|
154
|
+
}
|
|
155
|
+
// body block
|
|
156
|
+
if (i < len && input[i] === '{') {
|
|
157
|
+
i += 1;
|
|
158
|
+
const bodyStart = i;
|
|
159
|
+
const bodyEnd = findMatchingBrace(input, i);
|
|
160
|
+
const body = input.slice(bodyStart, bodyEnd);
|
|
161
|
+
i = bodyEnd + 1;
|
|
162
|
+
const raw = input.slice(start, i);
|
|
163
|
+
tokens.push({ kind: 'at-rule', name, prelude: prelude.trim(), body, raw });
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
// EOF without delimiter
|
|
167
|
+
tokens.push({ kind: 'at-rule', name, prelude: prelude.trim(), raw: input.slice(start, i) });
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
// Regular rule: read selectors until '{'
|
|
171
|
+
let selectors = '';
|
|
172
|
+
while (i < len && input[i] !== '{') {
|
|
173
|
+
selectors += input[i];
|
|
174
|
+
i += 1;
|
|
175
|
+
}
|
|
176
|
+
if (i >= len)
|
|
177
|
+
break;
|
|
178
|
+
i += 1; // skip {
|
|
179
|
+
const bodyStart = i;
|
|
180
|
+
const bodyEnd = findMatchingBrace(input, i);
|
|
181
|
+
const body = input.slice(bodyStart, bodyEnd);
|
|
182
|
+
i = bodyEnd + 1;
|
|
183
|
+
tokens.push({ kind: 'rule', selectors: selectors.trim(), body });
|
|
184
|
+
}
|
|
185
|
+
return tokens;
|
|
186
|
+
}
|
|
187
|
+
function findMatchingBrace(input, openIdx) {
|
|
188
|
+
let depth = 1;
|
|
189
|
+
let i = openIdx;
|
|
190
|
+
while (i < input.length && depth > 0) {
|
|
191
|
+
if (input[i] === '{')
|
|
192
|
+
depth += 1;
|
|
193
|
+
else if (input[i] === '}')
|
|
194
|
+
depth -= 1;
|
|
195
|
+
if (depth === 0)
|
|
196
|
+
return i;
|
|
197
|
+
i += 1;
|
|
198
|
+
}
|
|
199
|
+
return input.length;
|
|
200
|
+
}
|
|
201
|
+
function stripComments(input) {
|
|
202
|
+
return input.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
203
|
+
}
|
|
204
|
+
function splitSelectorList(input) {
|
|
205
|
+
// Split top-level commas only (respect parentheses for `:is(...)`, `:not(...)`, etc.)
|
|
206
|
+
const result = [];
|
|
207
|
+
let depth = 0;
|
|
208
|
+
let current = '';
|
|
209
|
+
for (const char of input) {
|
|
210
|
+
if (char === '(' || char === '[')
|
|
211
|
+
depth += 1;
|
|
212
|
+
else if (char === ')' || char === ']')
|
|
213
|
+
depth -= 1;
|
|
214
|
+
if (char === ',' && depth === 0) {
|
|
215
|
+
const trimmed = current.trim();
|
|
216
|
+
if (trimmed)
|
|
217
|
+
result.push(trimmed);
|
|
218
|
+
current = '';
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
current += char;
|
|
222
|
+
}
|
|
223
|
+
const trimmed = current.trim();
|
|
224
|
+
if (trimmed)
|
|
225
|
+
result.push(trimmed);
|
|
226
|
+
return result;
|
|
227
|
+
}
|
|
228
|
+
// ── Selector matching ──────────────────────────────────────────────
|
|
229
|
+
function selectorMatches(selector, presence) {
|
|
230
|
+
// Strip pseudo-elements (`::before`) and pseudo-classes (`:hover`) — keep
|
|
231
|
+
// their associated structural part for matching.
|
|
232
|
+
// Splits on the *last* compound selector since CSS targets that element.
|
|
233
|
+
const compounds = splitCompounds(selector);
|
|
234
|
+
const last = compounds[compounds.length - 1];
|
|
235
|
+
// If the last compound is empty after stripping pseudo-stuff, the selector
|
|
236
|
+
// is "container :hover"-style — keep it (conservative).
|
|
237
|
+
const structural = stripPseudo(last);
|
|
238
|
+
if (!structural)
|
|
239
|
+
return true;
|
|
240
|
+
// Universal selector — always present
|
|
241
|
+
if (structural === '*')
|
|
242
|
+
return true;
|
|
243
|
+
// Attribute selectors only — can't validate cheaply, keep
|
|
244
|
+
if (/^\[[^\]]+\]/.test(structural))
|
|
245
|
+
return true;
|
|
246
|
+
// Try matching ID > class > tag in this compound
|
|
247
|
+
const idMatch = structural.match(/#([\w-]+)/);
|
|
248
|
+
if (idMatch && presence.ids.has(idMatch[1]))
|
|
249
|
+
return true;
|
|
250
|
+
// If selector has an ID that's NOT present, drop it.
|
|
251
|
+
if (idMatch && !presence.ids.has(idMatch[1]))
|
|
252
|
+
return false;
|
|
253
|
+
const classMatches = Array.from(structural.matchAll(/\.([\w-]+)/g)).map((m) => m[1]);
|
|
254
|
+
if (classMatches.length > 0) {
|
|
255
|
+
return classMatches.every((cls) => presence.classes.has(cls));
|
|
256
|
+
}
|
|
257
|
+
const tagMatch = structural.match(/^([a-zA-Z][\w-]*)/);
|
|
258
|
+
if (tagMatch) {
|
|
259
|
+
return presence.tags.has(tagMatch[1].toLowerCase());
|
|
260
|
+
}
|
|
261
|
+
// Could not interpret — keep it (conservative)
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
function splitCompounds(selector) {
|
|
265
|
+
// Split on whitespace and combinators (`>`, `+`, `~`), keeping each
|
|
266
|
+
// compound (sequence of simple selectors).
|
|
267
|
+
const parts = [];
|
|
268
|
+
let current = '';
|
|
269
|
+
let depth = 0;
|
|
270
|
+
for (let i = 0; i < selector.length; i += 1) {
|
|
271
|
+
const c = selector[i];
|
|
272
|
+
if (c === '(' || c === '[')
|
|
273
|
+
depth += 1;
|
|
274
|
+
else if (c === ')' || c === ']')
|
|
275
|
+
depth -= 1;
|
|
276
|
+
if (depth === 0 && /[\s>+~]/.test(c)) {
|
|
277
|
+
if (current)
|
|
278
|
+
parts.push(current);
|
|
279
|
+
current = '';
|
|
280
|
+
continue;
|
|
281
|
+
}
|
|
282
|
+
current += c;
|
|
283
|
+
}
|
|
284
|
+
if (current)
|
|
285
|
+
parts.push(current);
|
|
286
|
+
return parts.length > 0 ? parts : [selector];
|
|
287
|
+
}
|
|
288
|
+
function stripPseudo(compound) {
|
|
289
|
+
// Remove pseudo-classes that take a parenthesised arg first.
|
|
290
|
+
let out = compound.replace(/:(?:is|where|not|has|nth-child|nth-of-type|lang|dir)\([^)]*\)/g, '');
|
|
291
|
+
// Then bare pseudo-classes / pseudo-elements.
|
|
292
|
+
out = out.replace(/::?[\w-]+/g, '');
|
|
293
|
+
return out.trim();
|
|
294
|
+
}
|
|
295
|
+
// ── parse5 helpers (mirrors dom-serializer.ts) ─────────────────────
|
|
296
|
+
function isElement(node) {
|
|
297
|
+
return node.tagName !== undefined && node.attrs !== undefined;
|
|
298
|
+
}
|
|
299
|
+
function isParentNode(node) {
|
|
300
|
+
return Array.isArray(node.childNodes);
|
|
301
|
+
}
|
|
302
|
+
function walk(root, visit) {
|
|
303
|
+
const stack = [{ node: root, parent: null }];
|
|
304
|
+
while (stack.length > 0) {
|
|
305
|
+
const { node, parent } = stack.pop();
|
|
306
|
+
visit(node, parent);
|
|
307
|
+
if (!isParentNode(node))
|
|
308
|
+
continue;
|
|
309
|
+
const children = [...node.childNodes];
|
|
310
|
+
for (let i = children.length - 1; i >= 0; i -= 1) {
|
|
311
|
+
stack.push({ node: children[i], parent: node });
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
function readText(element) {
|
|
316
|
+
let text = '';
|
|
317
|
+
for (const child of element.childNodes) {
|
|
318
|
+
const node = child;
|
|
319
|
+
if (node.nodeName === '#text' && typeof node.value === 'string') {
|
|
320
|
+
text += node.value;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return text;
|
|
324
|
+
}
|
|
325
|
+
function setText(element, value) {
|
|
326
|
+
// Replace all child nodes with a single text node carrying `value`.
|
|
327
|
+
// Use defaultTreeAdapter to keep parent/child wiring sane.
|
|
328
|
+
for (let i = element.childNodes.length - 1; i >= 0; i -= 1) {
|
|
329
|
+
defaultTreeAdapter.detachNode(element.childNodes[i]);
|
|
330
|
+
}
|
|
331
|
+
defaultTreeAdapter.insertText(element, value);
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=dom-css-purger.js.map
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Font Inliner — AUT-121 Interactive Demos, Phase 3.4
|
|
3
|
+
*
|
|
4
|
+
* Walks captured HTML, finds `@font-face { src: url(...) }` references inside
|
|
5
|
+
* inline `<style>` blocks, fetches each font, and base64-inlines fonts whose
|
|
6
|
+
* size is below a configurable threshold (default 100kb). Larger fonts are
|
|
7
|
+
* left untouched — the Linear issue mentions "Base64 OU stockage dédié" as
|
|
8
|
+
* alternatives, so the threshold lets the caller pick whichever fits.
|
|
9
|
+
*
|
|
10
|
+
* Pure module: the actual fetching is delegated via a `fetcher` callback so
|
|
11
|
+
* this file stays dependency-free and unit-testable. Server-side wiring
|
|
12
|
+
* passes a Supabase-aware fetcher.
|
|
13
|
+
*/
|
|
14
|
+
/** Maximum bytes a font can have to be inlined as base64. */
|
|
15
|
+
export declare const DEFAULT_FONT_INLINE_THRESHOLD: number;
|
|
16
|
+
export interface FontFetchResult {
|
|
17
|
+
buffer: Buffer;
|
|
18
|
+
mimeType: string;
|
|
19
|
+
}
|
|
20
|
+
export interface FontInlineOptions {
|
|
21
|
+
fetcher: (url: string) => Promise<FontFetchResult | null>;
|
|
22
|
+
baseUrl?: string;
|
|
23
|
+
thresholdBytes?: number;
|
|
24
|
+
}
|
|
25
|
+
export interface FontInlineStats {
|
|
26
|
+
scanned: number;
|
|
27
|
+
inlined: number;
|
|
28
|
+
skipped: number;
|
|
29
|
+
inlinedBytes: number;
|
|
30
|
+
}
|
|
31
|
+
export interface FontInlineResult {
|
|
32
|
+
html: string;
|
|
33
|
+
stats: FontInlineStats;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Walk an HTML document, scan every inline `<style>` block for `@font-face`
|
|
37
|
+
* `url(...)` references, fetch and base64-inline each one whose payload size
|
|
38
|
+
* is below the threshold.
|
|
39
|
+
*/
|
|
40
|
+
export declare function inlineFontFaceUrls(html: string, options: FontInlineOptions): Promise<FontInlineResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Extract every `url(...)` token that appears inside `@font-face { src: ... }`
|
|
43
|
+
* declarations. Returns the URLs in the order they appear.
|
|
44
|
+
*/
|
|
45
|
+
export declare function extractFontFaceUrls(css: string): string[];
|