@weavetab/mcp 2.5.0-beta.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/AGENTS.md +48 -0
- package/ARCHITECTURE.md +590 -0
- package/LICENSE +631 -0
- package/README.md +909 -0
- package/SECURITY.md +196 -0
- package/TROUBLESHOOTING.md +365 -0
- package/dist/anticaptcha/detector.d.ts +49 -0
- package/dist/anticaptcha/detector.js +465 -0
- package/dist/anticaptcha/integration.d.ts +36 -0
- package/dist/anticaptcha/integration.js +123 -0
- package/dist/anticaptcha/solver.d.ts +27 -0
- package/dist/anticaptcha/solver.js +552 -0
- package/dist/audit/logger.d.ts +13 -0
- package/dist/audit/logger.js +177 -0
- package/dist/audit/telemetry.d.ts +36 -0
- package/dist/audit/telemetry.js +162 -0
- package/dist/cdp/bridge.d.ts +16 -0
- package/dist/cdp/bridge.js +826 -0
- package/dist/cdp/confirm.d.ts +26 -0
- package/dist/cdp/confirm.js +79 -0
- package/dist/cdp/connector.d.ts +37 -0
- package/dist/cdp/connector.js +661 -0
- package/dist/cdp/extension.d.ts +36 -0
- package/dist/cdp/extension.js +118 -0
- package/dist/cdp/finder.d.ts +1 -0
- package/dist/cdp/finder.js +2 -0
- package/dist/cdp/helpers.d.ts +11 -0
- package/dist/cdp/helpers.js +195 -0
- package/dist/cdp/label_cache.d.ts +12 -0
- package/dist/cdp/label_cache.js +51 -0
- package/dist/cdp/lock.d.ts +33 -0
- package/dist/cdp/lock.js +206 -0
- package/dist/cdp/omni_broker.d.ts +17 -0
- package/dist/cdp/omni_broker.js +119 -0
- package/dist/cdp/profiles.d.ts +1 -0
- package/dist/cdp/profiles.js +87 -0
- package/dist/cdp/pruner.d.ts +41 -0
- package/dist/cdp/pruner.js +263 -0
- package/dist/cdp/ref_cache.d.ts +101 -0
- package/dist/cdp/ref_cache.js +234 -0
- package/dist/cdp/retry.d.ts +5 -0
- package/dist/cdp/retry.js +39 -0
- package/dist/cdp/retry_enhanced.d.ts +44 -0
- package/dist/cdp/retry_enhanced.js +102 -0
- package/dist/cdp/security.d.ts +50 -0
- package/dist/cdp/security.js +237 -0
- package/dist/cdp/verify.d.ts +39 -0
- package/dist/cdp/verify.js +143 -0
- package/dist/cdp/walker.d.ts +91 -0
- package/dist/cdp/walker.js +896 -0
- package/dist/cdp/walker.test.d.ts +2 -0
- package/dist/cdp/walker.test.js +172 -0
- package/dist/cdp/websocket_transport.d.ts +15 -0
- package/dist/cdp/websocket_transport.js +118 -0
- package/dist/cli/plugin.d.ts +9 -0
- package/dist/cli/plugin.js +1134 -0
- package/dist/cli/updater.d.ts +1 -0
- package/dist/cli/updater.js +72 -0
- package/dist/config/loader.d.ts +120 -0
- package/dist/config/loader.js +704 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1248 -0
- package/dist/intelligence/hints.d.ts +11 -0
- package/dist/intelligence/hints.js +39 -0
- package/dist/intelligence/intent.d.ts +33 -0
- package/dist/intelligence/intent.js +171 -0
- package/dist/intelligence/sessionMemory.d.ts +66 -0
- package/dist/intelligence/sessionMemory.js +284 -0
- package/dist/intelligence/sessionMemory.test.d.ts +2 -0
- package/dist/intelligence/sessionMemory.test.js +62 -0
- package/dist/intelligence/trail.d.ts +64 -0
- package/dist/intelligence/trail.js +233 -0
- package/dist/intelligence/trail.test.d.ts +2 -0
- package/dist/intelligence/trail.test.js +110 -0
- package/dist/intelligence/vision.d.ts +24 -0
- package/dist/intelligence/vision.js +151 -0
- package/dist/lifecycle/mission_manager.d.ts +16 -0
- package/dist/lifecycle/mission_manager.js +44 -0
- package/dist/overlay/bubble.d.ts +16 -0
- package/dist/overlay/bubble.js +37 -0
- package/dist/overlay/ghost.d.ts +35 -0
- package/dist/overlay/ghost.js +508 -0
- package/dist/overlay/injector.d.ts +22 -0
- package/dist/overlay/injector.js +198 -0
- package/dist/plugin/interface.d.ts +134 -0
- package/dist/plugin/interface.js +1 -0
- package/dist/plugin/loader.d.ts +18 -0
- package/dist/plugin/loader.js +155 -0
- package/dist/plugin/registry.d.ts +50 -0
- package/dist/plugin/registry.js +234 -0
- package/dist/security/blacklist.d.ts +24 -0
- package/dist/security/blacklist.js +80 -0
- package/dist/security/filter.d.ts +2 -0
- package/dist/security/filter.js +20 -0
- package/dist/security/guard.d.ts +8 -0
- package/dist/security/guard.js +35 -0
- package/dist/security/ratelimit.d.ts +11 -0
- package/dist/security/ratelimit.js +57 -0
- package/dist/security/secrets.d.ts +75 -0
- package/dist/security/secrets.js +365 -0
- package/dist/sensors/actionability.d.ts +81 -0
- package/dist/sensors/actionability.js +303 -0
- package/dist/sensors/guard.d.ts +25 -0
- package/dist/sensors/guard.js +168 -0
- package/dist/sensors/mutations.d.ts +23 -0
- package/dist/sensors/mutations.js +93 -0
- package/dist/sensors/network.d.ts +67 -0
- package/dist/sensors/network.js +193 -0
- package/dist/sensors/operation_monitor.d.ts +80 -0
- package/dist/sensors/operation_monitor.js +203 -0
- package/dist/sensors/pacing.d.ts +44 -0
- package/dist/sensors/pacing.js +111 -0
- package/dist/sensors/thoughts.d.ts +2 -0
- package/dist/sensors/thoughts.js +59 -0
- package/dist/sensors/wait.d.ts +12 -0
- package/dist/sensors/wait.js +44 -0
- package/dist/server.d.ts +7 -0
- package/dist/server.js +1687 -0
- package/dist/state/agent.d.ts +36 -0
- package/dist/state/agent.js +101 -0
- package/dist/state/cache.d.ts +16 -0
- package/dist/state/cache.js +92 -0
- package/dist/state/cache.test.d.ts +2 -0
- package/dist/state/cache.test.js +76 -0
- package/dist/state/delta.d.ts +71 -0
- package/dist/state/delta.js +234 -0
- package/dist/state/delta.test.d.ts +2 -0
- package/dist/state/delta.test.js +279 -0
- package/dist/state/groups.d.ts +15 -0
- package/dist/state/groups.js +80 -0
- package/dist/state/session.d.ts +30 -0
- package/dist/state/session.js +155 -0
- package/dist/tools/accessibility.d.ts +14 -0
- package/dist/tools/accessibility.js +32 -0
- package/dist/tools/ask.d.ts +14 -0
- package/dist/tools/ask.js +49 -0
- package/dist/tools/automation.d.ts +36 -0
- package/dist/tools/automation.js +218 -0
- package/dist/tools/browser_detect.d.ts +13 -0
- package/dist/tools/browser_detect.js +73 -0
- package/dist/tools/burst.d.ts +35 -0
- package/dist/tools/burst.js +344 -0
- package/dist/tools/canvas.d.ts +25 -0
- package/dist/tools/canvas.js +132 -0
- package/dist/tools/captcha.d.ts +38 -0
- package/dist/tools/captcha.js +209 -0
- package/dist/tools/click.d.ts +36 -0
- package/dist/tools/click.js +561 -0
- package/dist/tools/click_and_wait.d.ts +33 -0
- package/dist/tools/click_and_wait.js +149 -0
- package/dist/tools/clipboard.d.ts +29 -0
- package/dist/tools/clipboard.js +279 -0
- package/dist/tools/console.d.ts +21 -0
- package/dist/tools/console.js +107 -0
- package/dist/tools/cookies.d.ts +67 -0
- package/dist/tools/cookies.js +121 -0
- package/dist/tools/design_extract.d.ts +109 -0
- package/dist/tools/design_extract.js +641 -0
- package/dist/tools/dialog.d.ts +24 -0
- package/dist/tools/dialog.js +109 -0
- package/dist/tools/drag.d.ts +15 -0
- package/dist/tools/drag.js +29 -0
- package/dist/tools/emulation.d.ts +33 -0
- package/dist/tools/emulation.js +69 -0
- package/dist/tools/eval.d.ts +20 -0
- package/dist/tools/eval.js +92 -0
- package/dist/tools/fill.d.ts +41 -0
- package/dist/tools/fill.js +667 -0
- package/dist/tools/find.d.ts +30 -0
- package/dist/tools/find.js +60 -0
- package/dist/tools/geo.d.ts +19 -0
- package/dist/tools/geo.js +29 -0
- package/dist/tools/github/analyze.d.ts +4 -0
- package/dist/tools/github/analyze.js +113 -0
- package/dist/tools/github/cache.d.ts +21 -0
- package/dist/tools/github/cache.js +28 -0
- package/dist/tools/github/index.d.ts +4 -0
- package/dist/tools/github/index.js +4 -0
- package/dist/tools/github/issues.d.ts +9 -0
- package/dist/tools/github/issues.js +88 -0
- package/dist/tools/github/pr.d.ts +5 -0
- package/dist/tools/github/pr.js +88 -0
- package/dist/tools/github/rateLimiter.d.ts +4 -0
- package/dist/tools/github/rateLimiter.js +46 -0
- package/dist/tools/github/read.d.ts +34 -0
- package/dist/tools/github/read.js +74 -0
- package/dist/tools/github/repoContext.d.ts +20 -0
- package/dist/tools/github/repoContext.js +129 -0
- package/dist/tools/highlight.d.ts +20 -0
- package/dist/tools/highlight.js +140 -0
- package/dist/tools/hover.d.ts +19 -0
- package/dist/tools/hover.js +57 -0
- package/dist/tools/inspect.d.ts +17 -0
- package/dist/tools/inspect.js +125 -0
- package/dist/tools/integration.test.d.ts +2 -0
- package/dist/tools/integration.test.js +96 -0
- package/dist/tools/key.d.ts +25 -0
- package/dist/tools/key.js +173 -0
- package/dist/tools/macro_compiler.d.ts +7 -0
- package/dist/tools/macro_compiler.js +82 -0
- package/dist/tools/mouse.d.ts +21 -0
- package/dist/tools/mouse.js +62 -0
- package/dist/tools/navigate.d.ts +16 -0
- package/dist/tools/navigate.js +188 -0
- package/dist/tools/network_intercept.d.ts +17 -0
- package/dist/tools/network_intercept.js +151 -0
- package/dist/tools/office/index.d.ts +9 -0
- package/dist/tools/office/index.js +7 -0
- package/dist/tools/office/list.d.ts +15 -0
- package/dist/tools/office/list.js +41 -0
- package/dist/tools/office/parse.d.ts +20 -0
- package/dist/tools/office/parse.js +172 -0
- package/dist/tools/pdf.d.ts +22 -0
- package/dist/tools/pdf.js +42 -0
- package/dist/tools/performance.d.ts +12 -0
- package/dist/tools/performance.js +47 -0
- package/dist/tools/performance_test.d.ts +70 -0
- package/dist/tools/performance_test.js +128 -0
- package/dist/tools/plan.d.ts +39 -0
- package/dist/tools/plan.js +161 -0
- package/dist/tools/pointer.d.ts +35 -0
- package/dist/tools/pointer.js +522 -0
- package/dist/tools/read.d.ts +30 -0
- package/dist/tools/read.js +488 -0
- package/dist/tools/recording.d.ts +45 -0
- package/dist/tools/recording.js +124 -0
- package/dist/tools/response.d.ts +129 -0
- package/dist/tools/response.js +5 -0
- package/dist/tools/scrape.d.ts +24 -0
- package/dist/tools/scrape.js +140 -0
- package/dist/tools/screenshot.d.ts +40 -0
- package/dist/tools/screenshot.js +79 -0
- package/dist/tools/scroll.d.ts +23 -0
- package/dist/tools/scroll.js +196 -0
- package/dist/tools/select.d.ts +13 -0
- package/dist/tools/select.js +217 -0
- package/dist/tools/snapshot.d.ts +27 -0
- package/dist/tools/snapshot.js +99 -0
- package/dist/tools/storage.d.ts +18 -0
- package/dist/tools/storage.js +29 -0
- package/dist/tools/tabs.d.ts +77 -0
- package/dist/tools/tabs.js +342 -0
- package/dist/tools/thought.d.ts +16 -0
- package/dist/tools/thought.js +79 -0
- package/dist/tools/type.d.ts +65 -0
- package/dist/tools/type.js +1061 -0
- package/dist/tools/type_secret.d.ts +23 -0
- package/dist/tools/type_secret.js +229 -0
- package/dist/tools/upload.d.ts +19 -0
- package/dist/tools/upload.js +103 -0
- package/dist/tools/video_compiler.d.ts +24 -0
- package/dist/tools/video_compiler.js +199 -0
- package/dist/tools/viewport.d.ts +44 -0
- package/dist/tools/viewport.js +230 -0
- package/dist/tools/wait.d.ts +19 -0
- package/dist/tools/wait.js +202 -0
- package/dist/tools/wait_for.d.ts +23 -0
- package/dist/tools/wait_for.js +30 -0
- package/dist/tools/windows.d.ts +22 -0
- package/dist/tools/windows.js +57 -0
- package/dist/ui/index.d.ts +1 -0
- package/dist/ui/index.js +12 -0
- package/docs/DEV_MODE.md +68 -0
- package/docs/DOCUMENTATION.md +532 -0
- package/docs/PLUGINS.md +538 -0
- package/docs/TOOLS.md +104 -0
- package/docs/dev_plugin.md +310 -0
- package/ext-dist/background.js +467 -0
- package/ext-dist/content.js +3089 -0
- package/ext-dist/dashboard.html +583 -0
- package/ext-dist/hud/hud.js +73 -0
- package/ext-dist/hud/index.html +20 -0
- package/ext-dist/manifest.json +77 -0
- package/ext-dist/sidepanel/sidepanel.js +25 -0
- package/ext-dist/sidepanel.html +547 -0
- package/ext-dist/styles.css +218 -0
- package/icon.png +0 -0
- package/mcp.json +796 -0
- package/package.json +113 -0
- package/server.json +23 -0
- package/smithery.yaml +11 -0
|
@@ -0,0 +1,641 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getOutputDir } from "../config/loader.js";
|
|
4
|
+
import { hideWeaveTabOverlay, restoreWeaveTabOverlay } from "../cdp/helpers.js";
|
|
5
|
+
/**
|
|
6
|
+
* Pretty-print and format raw HTML with clean 2-space indentation and semantic readability.
|
|
7
|
+
*/
|
|
8
|
+
export function beautifyHtml(rawHtml) {
|
|
9
|
+
if (!rawHtml || !rawHtml.trim())
|
|
10
|
+
return "";
|
|
11
|
+
// Normalize newlines and collapse excessive spaces
|
|
12
|
+
let formatted = "";
|
|
13
|
+
let indentLevel = 0;
|
|
14
|
+
const indent = () => " ".repeat(indentLevel);
|
|
15
|
+
const voidElements = new Set([
|
|
16
|
+
"area", "base", "br", "col", "embed", "hr", "img", "input",
|
|
17
|
+
"link", "meta", "param", "source", "track", "wbr"
|
|
18
|
+
]);
|
|
19
|
+
const inlineElements = new Set([
|
|
20
|
+
"span", "a", "strong", "b", "em", "i", "code", "small", "kbd", "badge"
|
|
21
|
+
]);
|
|
22
|
+
// Tokenize tags and text content
|
|
23
|
+
const tokens = [];
|
|
24
|
+
const tagRegex = /<!--[\s\S]*?-->|<(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">])+>/g;
|
|
25
|
+
let lastIndex = 0;
|
|
26
|
+
let match;
|
|
27
|
+
while ((match = tagRegex.exec(rawHtml)) !== null) {
|
|
28
|
+
if (match.index > lastIndex) {
|
|
29
|
+
const text = rawHtml.substring(lastIndex, match.index).trim();
|
|
30
|
+
if (text) {
|
|
31
|
+
tokens.push({ type: "text", content: text });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
const tagStr = match[0];
|
|
35
|
+
if (tagStr.startsWith("<!--")) {
|
|
36
|
+
tokens.push({ type: "comment", content: tagStr });
|
|
37
|
+
}
|
|
38
|
+
else if (tagStr.startsWith("</")) {
|
|
39
|
+
const tagNameMatch = tagStr.match(/<\/([a-zA-Z0-9:-]+)/);
|
|
40
|
+
const tag = tagNameMatch ? tagNameMatch[1].toLowerCase() : "";
|
|
41
|
+
tokens.push({ type: "close", tag, content: tagStr });
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
const tagNameMatch = tagStr.match(/<([a-zA-Z0-9:-]+)/);
|
|
45
|
+
const tag = tagNameMatch ? tagNameMatch[1].toLowerCase() : "";
|
|
46
|
+
const isSelfClosing = tagStr.endsWith("/>") || voidElements.has(tag);
|
|
47
|
+
tokens.push({
|
|
48
|
+
type: isSelfClosing ? "selfClose" : "open",
|
|
49
|
+
tag,
|
|
50
|
+
content: tagStr
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
lastIndex = tagRegex.lastIndex;
|
|
54
|
+
}
|
|
55
|
+
if (lastIndex < rawHtml.length) {
|
|
56
|
+
const text = rawHtml.substring(lastIndex).trim();
|
|
57
|
+
if (text) {
|
|
58
|
+
tokens.push({ type: "text", content: text });
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
62
|
+
const token = tokens[i];
|
|
63
|
+
if (token.type === "open") {
|
|
64
|
+
// Check for compact single-line elements (e.g. <h1>Title</h1> or <a href="...">Link</a>)
|
|
65
|
+
const next1 = tokens[i + 1];
|
|
66
|
+
const next2 = tokens[i + 2];
|
|
67
|
+
const isCompact = next1 && next1.type === "text" && next2 && next2.type === "close" && next2.tag === token.tag && next1.content.length < 80;
|
|
68
|
+
if (isCompact) {
|
|
69
|
+
formatted += `${indent()}${token.content}${next1.content}${next2.content}\n`;
|
|
70
|
+
i += 2; // Skip text and close tag
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
formatted += `${indent()}${token.content}\n`;
|
|
74
|
+
indentLevel++;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
else if (token.type === "close") {
|
|
78
|
+
indentLevel = Math.max(0, indentLevel - 1);
|
|
79
|
+
formatted += `${indent()}${token.content}\n`;
|
|
80
|
+
}
|
|
81
|
+
else if (token.type === "selfClose" || token.type === "comment") {
|
|
82
|
+
formatted += `${indent()}${token.content}\n`;
|
|
83
|
+
}
|
|
84
|
+
else if (token.type === "text") {
|
|
85
|
+
formatted += `${indent()}${token.content}\n`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return formatted.trim() + "\n";
|
|
89
|
+
}
|
|
90
|
+
export async function browserExtractDesign(session, options = {}, config) {
|
|
91
|
+
const { selector = "main", format = "tailwind", outputDir: customOutputDir, name: customName, includeAssets = true, hideOverlay = true, } = options;
|
|
92
|
+
if (hideOverlay) {
|
|
93
|
+
await hideWeaveTabOverlay(session).catch(() => { });
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
const evalRes = await session.Runtime.evaluate({
|
|
97
|
+
expression: `(function(targetSelector, includeAssetsFlag) {
|
|
98
|
+
// Find target container
|
|
99
|
+
let root = document.querySelector(targetSelector);
|
|
100
|
+
if (!root && targetSelector !== 'body') {
|
|
101
|
+
root = document.querySelector('body') || document.documentElement;
|
|
102
|
+
}
|
|
103
|
+
if (!root) root = document.documentElement;
|
|
104
|
+
|
|
105
|
+
// ── 1. Meta & Environment ──
|
|
106
|
+
const isDark = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
107
|
+
|| document.documentElement.classList.contains('dark')
|
|
108
|
+
|| document.body?.classList.contains('dark')
|
|
109
|
+
|| (window.getComputedStyle(document.body).backgroundColor === 'rgb(0, 0, 0)' || window.getComputedStyle(document.body).backgroundColor.startsWith('rgb(1') || window.getComputedStyle(document.body).backgroundColor.startsWith('rgb(2'));
|
|
110
|
+
|
|
111
|
+
const meta = {
|
|
112
|
+
url: window.location.href,
|
|
113
|
+
title: document.title || '',
|
|
114
|
+
extractedAt: new Date().toISOString(),
|
|
115
|
+
selector: targetSelector,
|
|
116
|
+
viewport: {
|
|
117
|
+
width: window.innerWidth,
|
|
118
|
+
height: window.innerHeight
|
|
119
|
+
},
|
|
120
|
+
isDarkMode: !!isDark
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// ── 2. CSS Custom Properties (:root / html / body) ──
|
|
124
|
+
const cssVars = {};
|
|
125
|
+
const rootStyles = window.getComputedStyle(document.documentElement);
|
|
126
|
+
const bodyStyles = document.body ? window.getComputedStyle(document.body) : null;
|
|
127
|
+
|
|
128
|
+
// Extract declared variables from styleSheets
|
|
129
|
+
Array.from(document.styleSheets).forEach(sheet => {
|
|
130
|
+
try {
|
|
131
|
+
Array.from(sheet.cssRules || []).forEach(rule => {
|
|
132
|
+
if (rule.style && (rule.selectorText === ':root' || rule.selectorText === 'html' || rule.selectorText === 'body' || rule.selectorText === '.dark' || rule.selectorText === '[data-theme="dark"]')) {
|
|
133
|
+
for (let i = 0; i < rule.style.length; i++) {
|
|
134
|
+
const propName = rule.style[i];
|
|
135
|
+
if (propName.startsWith('--')) {
|
|
136
|
+
const val = rootStyles.getPropertyValue(propName).trim() || (bodyStyles ? bodyStyles.getPropertyValue(propName).trim() : '');
|
|
137
|
+
if (val) cssVars[propName] = val;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
});
|
|
142
|
+
} catch (_) {
|
|
143
|
+
// CORS stylesheet security restriction — ignore
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// ── Helper: Color Parsing & Normalization ──
|
|
148
|
+
function normalizeColor(c) {
|
|
149
|
+
if (!c || c === 'rgba(0, 0, 0, 0)' || c === 'transparent') return null;
|
|
150
|
+
return c.trim();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ── 3. Token Harvesters ──
|
|
154
|
+
const bgColors = new Set();
|
|
155
|
+
const textColors = new Set();
|
|
156
|
+
const borderColors = new Set();
|
|
157
|
+
const fontFamilies = new Set();
|
|
158
|
+
const shadows = new Set();
|
|
159
|
+
const radii = new Set();
|
|
160
|
+
const blurs = new Set();
|
|
161
|
+
const displayTypes = new Set();
|
|
162
|
+
const gaps = new Set();
|
|
163
|
+
const paddings = new Set();
|
|
164
|
+
|
|
165
|
+
const headings = {};
|
|
166
|
+
let bodyTypography = {
|
|
167
|
+
fontFamily: '',
|
|
168
|
+
fontSize: '',
|
|
169
|
+
fontWeight: '',
|
|
170
|
+
lineHeight: '',
|
|
171
|
+
color: ''
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
if (document.body) {
|
|
175
|
+
const bs = window.getComputedStyle(document.body);
|
|
176
|
+
bodyTypography = {
|
|
177
|
+
fontFamily: bs.fontFamily.split(',')[0].replace(/['"]/g, '').trim(),
|
|
178
|
+
fontSize: bs.fontSize,
|
|
179
|
+
fontWeight: bs.fontWeight,
|
|
180
|
+
lineHeight: bs.lineHeight,
|
|
181
|
+
color: bs.color
|
|
182
|
+
};
|
|
183
|
+
if (bs.fontFamily) fontFamilies.add(bodyTypography.fontFamily);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Collect heading styles
|
|
187
|
+
['h1', 'h2', 'h3', 'h4', 'h5', 'h6'].forEach(tag => {
|
|
188
|
+
const el = root.querySelector(tag);
|
|
189
|
+
if (el) {
|
|
190
|
+
const hs = window.getComputedStyle(el);
|
|
191
|
+
headings[tag] = {
|
|
192
|
+
fontFamily: hs.fontFamily.split(',')[0].replace(/['"]/g, '').trim(),
|
|
193
|
+
fontSize: hs.fontSize,
|
|
194
|
+
fontWeight: hs.fontWeight,
|
|
195
|
+
lineHeight: hs.lineHeight,
|
|
196
|
+
letterSpacing: hs.letterSpacing,
|
|
197
|
+
color: hs.color
|
|
198
|
+
};
|
|
199
|
+
if (hs.fontFamily) fontFamilies.add(headings[tag].fontFamily);
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
let totalNodes = 0;
|
|
204
|
+
let svgCount = 0;
|
|
205
|
+
|
|
206
|
+
function walkTokens(node) {
|
|
207
|
+
if (!node || node.nodeType !== 1) return;
|
|
208
|
+
if (node.id === 'wt-shadow-host' || node.hasAttribute('data-wt-ignore')) return;
|
|
209
|
+
|
|
210
|
+
totalNodes++;
|
|
211
|
+
const tag = node.tagName.toLowerCase();
|
|
212
|
+
if (['script', 'style', 'noscript', 'iframe'].includes(tag)) return;
|
|
213
|
+
if (tag === 'svg') svgCount++;
|
|
214
|
+
|
|
215
|
+
const s = window.getComputedStyle(node);
|
|
216
|
+
if (s.display === 'none' || s.visibility === 'hidden') return;
|
|
217
|
+
|
|
218
|
+
const bg = normalizeColor(s.backgroundColor);
|
|
219
|
+
if (bg) bgColors.add(bg);
|
|
220
|
+
|
|
221
|
+
const col = normalizeColor(s.color);
|
|
222
|
+
if (col) textColors.add(col);
|
|
223
|
+
|
|
224
|
+
const bc = normalizeColor(s.borderColor);
|
|
225
|
+
if (bc && s.borderWidth !== '0px') borderColors.add(bc);
|
|
226
|
+
|
|
227
|
+
if (s.fontFamily) {
|
|
228
|
+
const f = s.fontFamily.split(',')[0].replace(/['"]/g, '').trim();
|
|
229
|
+
if (f && f !== 'inherit') fontFamilies.add(f);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (s.boxShadow && s.boxShadow !== 'none') shadows.add(s.boxShadow);
|
|
233
|
+
if (s.borderRadius && s.borderRadius !== '0px') radii.add(s.borderRadius);
|
|
234
|
+
if (s.backdropFilter && s.backdropFilter !== 'none') blurs.add(s.backdropFilter);
|
|
235
|
+
|
|
236
|
+
if (s.display) displayTypes.add(s.display);
|
|
237
|
+
if (s.gap && s.gap !== 'normal' && s.gap !== '0px') gaps.add(s.gap);
|
|
238
|
+
if (s.padding && s.padding !== '0px') paddings.add(s.padding);
|
|
239
|
+
|
|
240
|
+
for (let i = 0; i < node.children.length; i++) {
|
|
241
|
+
walkTokens(node.children[i]);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
walkTokens(root);
|
|
246
|
+
|
|
247
|
+
// ── 4. Intelligent DOM Normalizer & Beautifier ──
|
|
248
|
+
const clone = root.cloneNode(true);
|
|
249
|
+
|
|
250
|
+
// A. Remove useless elements
|
|
251
|
+
clone.querySelectorAll('script, style, noscript, iframe, link, #wt-shadow-host, [data-wt-ignore]').forEach(el => el.remove());
|
|
252
|
+
|
|
253
|
+
// B. Handle Framer Motion / SplitText Spans & Hidden Duplicates
|
|
254
|
+
function normalizeDomTree(el) {
|
|
255
|
+
if (!el || el.nodeType !== 1) return;
|
|
256
|
+
|
|
257
|
+
const tag = el.tagName.toLowerCase();
|
|
258
|
+
|
|
259
|
+
// 1. Check for visually hidden / aria-hidden text duplication (Linear / Framer Motion pattern)
|
|
260
|
+
const srOnlyEl = el.querySelector('.visuallyHidden, [class*="visuallyHidden"], .sr-only, [class*="sr-only"]');
|
|
261
|
+
const ariaHiddenEl = el.querySelector('[aria-hidden="true"]');
|
|
262
|
+
if (srOnlyEl && (ariaHiddenEl || el.querySelectorAll('span').length > 2)) {
|
|
263
|
+
const canonicalText = srOnlyEl.textContent.trim();
|
|
264
|
+
if (canonicalText) {
|
|
265
|
+
el.innerHTML = canonicalText;
|
|
266
|
+
}
|
|
267
|
+
} else if (['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'button', 'a'].includes(tag)) {
|
|
268
|
+
// If all children are spans with fragmented text or animation styles, unsplit them
|
|
269
|
+
const spans = Array.from(el.children).filter(c => c.tagName.toLowerCase() === 'span');
|
|
270
|
+
const hasOtherContent = Array.from(el.children).some(c => !['span', 'br', 'wbr'].includes(c.tagName.toLowerCase()));
|
|
271
|
+
|
|
272
|
+
if (spans.length >= 2 && !hasOtherContent) {
|
|
273
|
+
// Check if spans have duplicate text due to responsive breakpoints (e.g. show-mobile vs hide-mobile)
|
|
274
|
+
const mobileSpans = spans.filter(s => s.className && s.className.includes('mobile'));
|
|
275
|
+
const hideMobileSpans = spans.filter(s => s.className && s.className.includes('hide-mobile'));
|
|
276
|
+
|
|
277
|
+
if (hideMobileSpans.length > 0) {
|
|
278
|
+
// Desktop active: keep hide-mobile / remove show-mobile
|
|
279
|
+
mobileSpans.filter(s => s.className.includes('show-mobile')).forEach(s => s.remove());
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// If remaining spans are just split words, join them cleanly
|
|
283
|
+
const remainingSpans = Array.from(el.children).filter(c => c.tagName.toLowerCase() === 'span');
|
|
284
|
+
const textContent = remainingSpans.map(s => s.textContent).join(' ').replace(/\\s+/g, ' ').trim();
|
|
285
|
+
if (textContent) {
|
|
286
|
+
el.innerHTML = textContent;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// 2. Clean inline styles (remove animation / matrix / opacity: 1 fluff)
|
|
292
|
+
if (el.hasAttribute('style')) {
|
|
293
|
+
const rawStyle = el.getAttribute('style') || '';
|
|
294
|
+
const meaningfulStyles = [];
|
|
295
|
+
|
|
296
|
+
rawStyle.split(';').forEach(rule => {
|
|
297
|
+
const trimmed = rule.trim();
|
|
298
|
+
if (!trimmed) return;
|
|
299
|
+
const [prop, val] = trimmed.split(':').map(s => s.trim());
|
|
300
|
+
if (!prop || !val) return;
|
|
301
|
+
|
|
302
|
+
// Skip animation artifacts
|
|
303
|
+
if (
|
|
304
|
+
(prop === 'opacity' && val === '1') ||
|
|
305
|
+
(prop === 'filter' && (val === 'blur(0px)' || val === 'none')) ||
|
|
306
|
+
(prop === 'transform' && (val.includes('translateY(0%)') || val.includes('translate3d(0, 0, 0)') || val === 'none' || val.includes('matrix(1, 0, 0, 1, 0, 0)'))) ||
|
|
307
|
+
(prop === 'will-change') ||
|
|
308
|
+
(prop === 'perspective') ||
|
|
309
|
+
(prop === 'backface-visibility')
|
|
310
|
+
) {
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Keep intentional layout styles / CSS variables / spacer heights
|
|
315
|
+
if (prop.startsWith('--height') || prop === 'height') {
|
|
316
|
+
meaningfulStyles.push('height: ' + val);
|
|
317
|
+
} else if (prop.startsWith('--gap') || prop === 'gap') {
|
|
318
|
+
meaningfulStyles.push('gap: ' + val);
|
|
319
|
+
} else if (!prop.startsWith('--')) {
|
|
320
|
+
meaningfulStyles.push(prop + ': ' + val);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
if (meaningfulStyles.length > 0) {
|
|
325
|
+
el.setAttribute('style', meaningfulStyles.join('; '));
|
|
326
|
+
} else {
|
|
327
|
+
el.removeAttribute('style');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// 3. Clean and normalize class names (strip random hashes, keep semantic names)
|
|
332
|
+
if (el.hasAttribute('class')) {
|
|
333
|
+
const rawClasses = (el.getAttribute('class') || '').split(/\\s+/);
|
|
334
|
+
const cleanClasses = [];
|
|
335
|
+
|
|
336
|
+
rawClasses.forEach(cls => {
|
|
337
|
+
const c = cls.trim();
|
|
338
|
+
if (!c) return;
|
|
339
|
+
// Skip internal / ephemeral classes
|
|
340
|
+
if (c.startsWith('sc-') || c === 'show-mobile' || c.startsWith('wt-')) return;
|
|
341
|
+
|
|
342
|
+
// De-hash CSS module names: e.g. "ImRUSq_justify-space-between" -> "justify-between"
|
|
343
|
+
if (c.includes('_')) {
|
|
344
|
+
const parts = c.split('_');
|
|
345
|
+
const semanticPart = parts[parts.length - 1];
|
|
346
|
+
if (semanticPart && semanticPart.length > 2 && !semanticPart.match(/^[a-f0-9]{4,}$/i)) {
|
|
347
|
+
let normalized = semanticPart
|
|
348
|
+
.replace('justify-space-between', 'justify-between')
|
|
349
|
+
.replace('align-center', 'items-center');
|
|
350
|
+
if (normalized === 'root') normalized = 'spacer';
|
|
351
|
+
cleanClasses.push(normalized);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// Keep standard semantic / utility class
|
|
357
|
+
cleanClasses.push(c);
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
const uniqueClasses = Array.from(new Set(cleanClasses));
|
|
361
|
+
if (uniqueClasses.length > 0) {
|
|
362
|
+
el.setAttribute('class', uniqueClasses.join(' '));
|
|
363
|
+
} else {
|
|
364
|
+
el.removeAttribute('class');
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 4. Remove internal / framework attributes
|
|
369
|
+
['data-wt-id', 'data-wt-secret', 'jsaction', 'jscontroller', 'jsname', 'jsmodel', 'data-sentry-component', 'data-sentry-source-file'].forEach(attr => {
|
|
370
|
+
el.removeAttribute(attr);
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
// 5. Clean SVGs
|
|
374
|
+
if (el.tagName.toLowerCase() === 'svg' && !includeAssetsFlag) {
|
|
375
|
+
el.innerHTML = '<!-- svg icon -->';
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
// Recurse children
|
|
379
|
+
const children = Array.from(el.children);
|
|
380
|
+
for (let i = 0; i < children.length; i++) {
|
|
381
|
+
normalizeDomTree(children[i]);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// 6. Remove empty spacer divs with no styles or classes
|
|
385
|
+
if (tag === 'div' && el.children.length === 0 && !el.textContent.trim() && !el.hasAttribute('style') && !el.hasAttribute('class')) {
|
|
386
|
+
el.remove();
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
normalizeDomTree(clone);
|
|
391
|
+
|
|
392
|
+
// ── 5. Full Real CSS Extraction ──
|
|
393
|
+
const collectedStyleBlocks = [];
|
|
394
|
+
const keyframeMap = new Map();
|
|
395
|
+
const mediaQueryMap = new Map();
|
|
396
|
+
const fontFaceList = [];
|
|
397
|
+
const classRules = [];
|
|
398
|
+
|
|
399
|
+
// A. Extract all <style> tags (includes Tailwind, Emotion, styled-components, Next.js CSS)
|
|
400
|
+
document.querySelectorAll('style').forEach(st => {
|
|
401
|
+
const text = (st.textContent || '').trim();
|
|
402
|
+
if (text) {
|
|
403
|
+
collectedStyleBlocks.push(text);
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
// B. Extract accessible CSS rules from document.styleSheets
|
|
408
|
+
Array.from(document.styleSheets).forEach(sheet => {
|
|
409
|
+
try {
|
|
410
|
+
Array.from(sheet.cssRules || []).forEach(rule => {
|
|
411
|
+
const ruleText = rule.cssText;
|
|
412
|
+
if (!ruleText) return;
|
|
413
|
+
|
|
414
|
+
if (rule.type === 7 || rule.constructor.name === 'CSSKeyframesRule') {
|
|
415
|
+
keyframeMap.set(rule.name || ruleText.slice(0, 30), ruleText);
|
|
416
|
+
} else if (rule.type === 5 || rule.constructor.name === 'CSSFontFaceRule') {
|
|
417
|
+
fontFaceList.push(ruleText);
|
|
418
|
+
} else if (rule.type === 4 || rule.constructor.name === 'CSSMediaRule') {
|
|
419
|
+
mediaQueryMap.set(rule.conditionText || ruleText.slice(0, 30), ruleText);
|
|
420
|
+
} else if (rule.style) {
|
|
421
|
+
const sel = rule.selectorText || '';
|
|
422
|
+
if (sel && !sel.includes('wt-') && !sel.includes('chrome-extension')) {
|
|
423
|
+
classRules.push(ruleText);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
} catch (_) {
|
|
428
|
+
// Cross-origin stylesheet security restriction — ignored
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// C. Build formatted comprehensive styles.css
|
|
433
|
+
let fullCss = '/* ==========================================================================\\n';
|
|
434
|
+
fullCss += ' Design Blueprint Stylesheet: ' + (document.title || window.location.hostname) + '\\n';
|
|
435
|
+
fullCss += ' Extracted via Weavetab browser_extract_design\\n';
|
|
436
|
+
fullCss += ' ========================================================================== */\\n\\n';
|
|
437
|
+
|
|
438
|
+
// 1. CSS Custom Properties
|
|
439
|
+
fullCss += '/* ── 1. CSS Custom Properties (:root / themes) ── */\\n:root {\\n';
|
|
440
|
+
Object.entries(cssVars).forEach(([k, v]) => {
|
|
441
|
+
fullCss += ' ' + k + ': ' + v + ';\\n';
|
|
442
|
+
});
|
|
443
|
+
fullCss += '}\\n\\n';
|
|
444
|
+
|
|
445
|
+
// 2. Font Faces
|
|
446
|
+
if (fontFaceList.length > 0) {
|
|
447
|
+
fullCss += '/* ── 2. Font Faces ── */\\n';
|
|
448
|
+
Array.from(new Set(fontFaceList)).slice(0, 10).forEach(ff => {
|
|
449
|
+
fullCss += ff + '\\n\\n';
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
// 3. Keyframe Animations
|
|
454
|
+
if (keyframeMap.size > 0) {
|
|
455
|
+
fullCss += '/* ── 3. Keyframe Animations ── */\\n';
|
|
456
|
+
Array.from(keyframeMap.values()).forEach(kf => {
|
|
457
|
+
fullCss += kf + '\\n\\n';
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// 4. Stylesheet Rules / Style Blocks
|
|
462
|
+
fullCss += '/* ── 4. Layout & Component Styles ── */\\n';
|
|
463
|
+
if (collectedStyleBlocks.length > 0) {
|
|
464
|
+
collectedStyleBlocks.forEach(sb => {
|
|
465
|
+
fullCss += sb + '\\n\\n';
|
|
466
|
+
});
|
|
467
|
+
} else if (classRules.length > 0) {
|
|
468
|
+
Array.from(new Set(classRules)).forEach(cr => {
|
|
469
|
+
fullCss += cr + '\\n';
|
|
470
|
+
});
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// 5. Responsive Media Queries
|
|
474
|
+
if (mediaQueryMap.size > 0 && collectedStyleBlocks.length === 0) {
|
|
475
|
+
fullCss += '\\n/* ── 5. Responsive Media Queries ── */\\n';
|
|
476
|
+
Array.from(mediaQueryMap.values()).forEach(mq => {
|
|
477
|
+
fullCss += mq + '\\n\\n';
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return {
|
|
482
|
+
tokens: {
|
|
483
|
+
meta,
|
|
484
|
+
cssVariables: cssVars,
|
|
485
|
+
palette: {
|
|
486
|
+
backgrounds: Array.from(bgColors).slice(0, 25),
|
|
487
|
+
text: Array.from(textColors).slice(0, 20),
|
|
488
|
+
borders: Array.from(borderColors).slice(0, 15),
|
|
489
|
+
accents: Array.from(bgColors).filter(c => !c.includes('255, 255, 255') && !c.includes('0, 0, 0') && !c.includes('rgba(0, 0, 0')).slice(0, 10)
|
|
490
|
+
},
|
|
491
|
+
typography: {
|
|
492
|
+
fontFamilies: Array.from(fontFamilies),
|
|
493
|
+
headings,
|
|
494
|
+
body: bodyTypography
|
|
495
|
+
},
|
|
496
|
+
effects: {
|
|
497
|
+
shadows: Array.from(shadows).slice(0, 10),
|
|
498
|
+
radii: Array.from(radii).slice(0, 10),
|
|
499
|
+
backdropBlurs: Array.from(blurs).slice(0, 5)
|
|
500
|
+
},
|
|
501
|
+
layout: {
|
|
502
|
+
displayTypes: Array.from(displayTypes),
|
|
503
|
+
gapValues: Array.from(gaps).slice(0, 10),
|
|
504
|
+
paddingValues: Array.from(paddings).slice(0, 10)
|
|
505
|
+
}
|
|
506
|
+
},
|
|
507
|
+
rawHtml: clone.outerHTML,
|
|
508
|
+
css: fullCss,
|
|
509
|
+
metrics: {
|
|
510
|
+
nodeCount: totalNodes,
|
|
511
|
+
svgCount,
|
|
512
|
+
htmlBytes: clone.outerHTML.length
|
|
513
|
+
}
|
|
514
|
+
};
|
|
515
|
+
})(${JSON.stringify(selector)}, ${JSON.stringify(includeAssets)})`,
|
|
516
|
+
returnByValue: true
|
|
517
|
+
});
|
|
518
|
+
const result = evalRes.result?.value;
|
|
519
|
+
if (!result || !result.tokens) {
|
|
520
|
+
throw new Error(`Failed to extract design from selector '${selector}'`);
|
|
521
|
+
}
|
|
522
|
+
const { tokens, rawHtml, css, metrics } = result;
|
|
523
|
+
// ── Prettify and Format HTML ──
|
|
524
|
+
const beautifulHtml = beautifyHtml(rawHtml);
|
|
525
|
+
// ── Output Directory Resolution ──
|
|
526
|
+
let targetDir;
|
|
527
|
+
if (customOutputDir && customOutputDir.trim()) {
|
|
528
|
+
targetDir = path.isAbsolute(customOutputDir.trim())
|
|
529
|
+
? customOutputDir.trim()
|
|
530
|
+
: path.resolve(process.cwd(), customOutputDir.trim());
|
|
531
|
+
}
|
|
532
|
+
else {
|
|
533
|
+
const baseDesignsDir = getOutputDir("designs", config);
|
|
534
|
+
let folderName = customName ? customName.replace(/[^a-zA-Z0-9_-]/g, "_") : "";
|
|
535
|
+
if (!folderName) {
|
|
536
|
+
let domain = "page";
|
|
537
|
+
try {
|
|
538
|
+
const parsed = new URL(tokens.meta.url);
|
|
539
|
+
domain = parsed.hostname.replace(/[^a-zA-Z0-9.-]/g, "_");
|
|
540
|
+
}
|
|
541
|
+
catch (_) { }
|
|
542
|
+
folderName = `${domain}-${Date.now()}`;
|
|
543
|
+
}
|
|
544
|
+
targetDir = path.join(baseDesignsDir, folderName);
|
|
545
|
+
}
|
|
546
|
+
await fs.mkdir(targetDir, { recursive: true });
|
|
547
|
+
// ── Generate Files ──
|
|
548
|
+
const tokensPath = path.join(targetDir, "tokens.json");
|
|
549
|
+
await fs.writeFile(tokensPath, JSON.stringify(tokens, null, 2), "utf-8");
|
|
550
|
+
const htmlPath = path.join(targetDir, "template.html");
|
|
551
|
+
await fs.writeFile(htmlPath, beautifulHtml, "utf-8");
|
|
552
|
+
const cssPath = path.join(targetDir, "styles.css");
|
|
553
|
+
await fs.writeFile(cssPath, css, "utf-8");
|
|
554
|
+
// Generate Tailwind Theme Extension if requested
|
|
555
|
+
let tailwindPath;
|
|
556
|
+
if (format === "tailwind") {
|
|
557
|
+
const tailwindTheme = {
|
|
558
|
+
theme: {
|
|
559
|
+
extend: {
|
|
560
|
+
colors: {
|
|
561
|
+
...tokens.palette.backgrounds.reduce((acc, c, i) => ({ ...acc, [`extracted-bg-${i + 1}`]: c }), {}),
|
|
562
|
+
...tokens.palette.accents.reduce((acc, c, i) => ({ ...acc, [`extracted-accent-${i + 1}`]: c }), {}),
|
|
563
|
+
},
|
|
564
|
+
fontFamily: {
|
|
565
|
+
...tokens.typography.fontFamilies.reduce((acc, f, i) => {
|
|
566
|
+
const key = f.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
567
|
+
return { ...acc, [key || `font-${i + 1}`]: [f, "sans-serif"] };
|
|
568
|
+
}, {})
|
|
569
|
+
},
|
|
570
|
+
borderRadius: {
|
|
571
|
+
...tokens.effects.radii.reduce((acc, r, i) => ({ ...acc, [`extracted-r-${i + 1}`]: r }), {})
|
|
572
|
+
},
|
|
573
|
+
boxShadow: {
|
|
574
|
+
...tokens.effects.shadows.reduce((acc, s, i) => ({ ...acc, [`extracted-shadow-${i + 1}`]: s }), {})
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
};
|
|
579
|
+
tailwindPath = path.join(targetDir, "tailwind.theme.json");
|
|
580
|
+
await fs.writeFile(tailwindPath, JSON.stringify(tailwindTheme, null, 2), "utf-8");
|
|
581
|
+
}
|
|
582
|
+
// Generate Markdown README Guide for AI Agent
|
|
583
|
+
const readmePath = path.join(targetDir, "README.md");
|
|
584
|
+
const readmeContent = `# Design Blueprint: ${tokens.meta.title || tokens.meta.url}
|
|
585
|
+
|
|
586
|
+
- **Extracted At**: ${tokens.meta.extractedAt}
|
|
587
|
+
- **Source URL**: ${tokens.meta.url}
|
|
588
|
+
- **Theme**: ${tokens.meta.isDarkMode ? "Dark Mode" : "Light Mode"}
|
|
589
|
+
- **Selector**: \`${tokens.meta.selector}\`
|
|
590
|
+
|
|
591
|
+
## Design System Tokens
|
|
592
|
+
- **Font Families**: ${tokens.typography.fontFamilies.join(", ") || "System Default"}
|
|
593
|
+
- **Primary Background Colors**: \`${tokens.palette.backgrounds.slice(0, 5).join("`, `")}\`
|
|
594
|
+
- **Text Colors**: \`${tokens.palette.text.slice(0, 5).join("`, `")}\`
|
|
595
|
+
- **Border Radii**: \`${tokens.effects.radii.slice(0, 5).join("`, `")}\`
|
|
596
|
+
- **Shadows**: ${tokens.effects.shadows.length} extracted shadow styles
|
|
597
|
+
|
|
598
|
+
## Included Files
|
|
599
|
+
1. \`tokens.json\` — Full machine-readable design tokens, CSS variables, typography hierarchy, and colors.
|
|
600
|
+
2. \`template.html\` — Clean, semantic, beautified HTML skeleton (${metrics.nodeCount} elements, ${metrics.svgCount} SVGs).
|
|
601
|
+
3. \`styles.css\` — Extracted CSS variables and scoped styles.
|
|
602
|
+
${tailwindPath ? "4. `tailwind.theme.json` — Tailwind CSS `theme.extend` configuration.\n" : ""}
|
|
603
|
+
## Agent Implementation Instructions
|
|
604
|
+
- Open \`tokens.json\` to copy exact hex colors, typography weights, and border radius tokens.
|
|
605
|
+
- Open \`template.html\` to review semantic structure, SVGs, and component layout hierarchy.
|
|
606
|
+
- Recreate the target component in your project using these exact tokens.
|
|
607
|
+
`;
|
|
608
|
+
await fs.writeFile(readmePath, readmeContent, "utf-8");
|
|
609
|
+
return {
|
|
610
|
+
success: true,
|
|
611
|
+
data: {
|
|
612
|
+
outputDirectory: targetDir,
|
|
613
|
+
files: {
|
|
614
|
+
tokens: tokensPath,
|
|
615
|
+
html: htmlPath,
|
|
616
|
+
css: cssPath,
|
|
617
|
+
tailwind: tailwindPath,
|
|
618
|
+
readme: readmePath,
|
|
619
|
+
},
|
|
620
|
+
tokensSummary: {
|
|
621
|
+
colors: tokens.palette,
|
|
622
|
+
fonts: tokens.typography.fontFamilies,
|
|
623
|
+
radii: tokens.effects.radii,
|
|
624
|
+
shadows: tokens.effects.shadows,
|
|
625
|
+
cssVariablesCount: Object.keys(tokens.cssVariables).length
|
|
626
|
+
},
|
|
627
|
+
htmlMetrics: {
|
|
628
|
+
nodeCount: metrics.nodeCount,
|
|
629
|
+
svgCount: metrics.svgCount,
|
|
630
|
+
htmlBytes: beautifulHtml.length
|
|
631
|
+
}
|
|
632
|
+
},
|
|
633
|
+
message: `Design extracted successfully to ${targetDir}. Saved tokens.json, template.html, styles.css, and README.md.`
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
finally {
|
|
637
|
+
if (hideOverlay) {
|
|
638
|
+
await restoreWeaveTabOverlay(session).catch(() => { });
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Weavetab — tools/dialog.ts
|
|
3
|
+
* Auto-handles JavaScript alert/confirm/prompt dialogs via CDP Page API.
|
|
4
|
+
* Weavetab by fy2ne
|
|
5
|
+
*/
|
|
6
|
+
import CDP from "chrome-remote-interface";
|
|
7
|
+
export interface DialogResult {
|
|
8
|
+
success: true;
|
|
9
|
+
dialog_type: string;
|
|
10
|
+
message: string;
|
|
11
|
+
accepted: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare function hasOpenDialog(session: CDP.Client): boolean;
|
|
14
|
+
/** Register the auto-dismiss handler for the given CDP session. */
|
|
15
|
+
export declare function initDialogHandler(session: CDP.Client): void;
|
|
16
|
+
/**
|
|
17
|
+
* Manually trigger dialog handling — call this if the agent suspects
|
|
18
|
+
* a dialog is pending that blocked previous tool calls.
|
|
19
|
+
*/
|
|
20
|
+
export interface DialogOptions {
|
|
21
|
+
action: "accept" | "dismiss";
|
|
22
|
+
promptText?: string;
|
|
23
|
+
}
|
|
24
|
+
export declare function browserDialog(session: CDP.Client, options: DialogOptions): Promise<DialogResult>;
|