@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.
Files changed (281) hide show
  1. package/AGENTS.md +48 -0
  2. package/ARCHITECTURE.md +590 -0
  3. package/LICENSE +631 -0
  4. package/README.md +909 -0
  5. package/SECURITY.md +196 -0
  6. package/TROUBLESHOOTING.md +365 -0
  7. package/dist/anticaptcha/detector.d.ts +49 -0
  8. package/dist/anticaptcha/detector.js +465 -0
  9. package/dist/anticaptcha/integration.d.ts +36 -0
  10. package/dist/anticaptcha/integration.js +123 -0
  11. package/dist/anticaptcha/solver.d.ts +27 -0
  12. package/dist/anticaptcha/solver.js +552 -0
  13. package/dist/audit/logger.d.ts +13 -0
  14. package/dist/audit/logger.js +177 -0
  15. package/dist/audit/telemetry.d.ts +36 -0
  16. package/dist/audit/telemetry.js +162 -0
  17. package/dist/cdp/bridge.d.ts +16 -0
  18. package/dist/cdp/bridge.js +826 -0
  19. package/dist/cdp/confirm.d.ts +26 -0
  20. package/dist/cdp/confirm.js +79 -0
  21. package/dist/cdp/connector.d.ts +37 -0
  22. package/dist/cdp/connector.js +661 -0
  23. package/dist/cdp/extension.d.ts +36 -0
  24. package/dist/cdp/extension.js +118 -0
  25. package/dist/cdp/finder.d.ts +1 -0
  26. package/dist/cdp/finder.js +2 -0
  27. package/dist/cdp/helpers.d.ts +11 -0
  28. package/dist/cdp/helpers.js +195 -0
  29. package/dist/cdp/label_cache.d.ts +12 -0
  30. package/dist/cdp/label_cache.js +51 -0
  31. package/dist/cdp/lock.d.ts +33 -0
  32. package/dist/cdp/lock.js +206 -0
  33. package/dist/cdp/omni_broker.d.ts +17 -0
  34. package/dist/cdp/omni_broker.js +119 -0
  35. package/dist/cdp/profiles.d.ts +1 -0
  36. package/dist/cdp/profiles.js +87 -0
  37. package/dist/cdp/pruner.d.ts +41 -0
  38. package/dist/cdp/pruner.js +263 -0
  39. package/dist/cdp/ref_cache.d.ts +101 -0
  40. package/dist/cdp/ref_cache.js +234 -0
  41. package/dist/cdp/retry.d.ts +5 -0
  42. package/dist/cdp/retry.js +39 -0
  43. package/dist/cdp/retry_enhanced.d.ts +44 -0
  44. package/dist/cdp/retry_enhanced.js +102 -0
  45. package/dist/cdp/security.d.ts +50 -0
  46. package/dist/cdp/security.js +237 -0
  47. package/dist/cdp/verify.d.ts +39 -0
  48. package/dist/cdp/verify.js +143 -0
  49. package/dist/cdp/walker.d.ts +91 -0
  50. package/dist/cdp/walker.js +896 -0
  51. package/dist/cdp/walker.test.d.ts +2 -0
  52. package/dist/cdp/walker.test.js +172 -0
  53. package/dist/cdp/websocket_transport.d.ts +15 -0
  54. package/dist/cdp/websocket_transport.js +118 -0
  55. package/dist/cli/plugin.d.ts +9 -0
  56. package/dist/cli/plugin.js +1134 -0
  57. package/dist/cli/updater.d.ts +1 -0
  58. package/dist/cli/updater.js +72 -0
  59. package/dist/config/loader.d.ts +120 -0
  60. package/dist/config/loader.js +704 -0
  61. package/dist/index.d.ts +2 -0
  62. package/dist/index.js +1248 -0
  63. package/dist/intelligence/hints.d.ts +11 -0
  64. package/dist/intelligence/hints.js +39 -0
  65. package/dist/intelligence/intent.d.ts +33 -0
  66. package/dist/intelligence/intent.js +171 -0
  67. package/dist/intelligence/sessionMemory.d.ts +66 -0
  68. package/dist/intelligence/sessionMemory.js +284 -0
  69. package/dist/intelligence/sessionMemory.test.d.ts +2 -0
  70. package/dist/intelligence/sessionMemory.test.js +62 -0
  71. package/dist/intelligence/trail.d.ts +64 -0
  72. package/dist/intelligence/trail.js +233 -0
  73. package/dist/intelligence/trail.test.d.ts +2 -0
  74. package/dist/intelligence/trail.test.js +110 -0
  75. package/dist/intelligence/vision.d.ts +24 -0
  76. package/dist/intelligence/vision.js +151 -0
  77. package/dist/lifecycle/mission_manager.d.ts +16 -0
  78. package/dist/lifecycle/mission_manager.js +44 -0
  79. package/dist/overlay/bubble.d.ts +16 -0
  80. package/dist/overlay/bubble.js +37 -0
  81. package/dist/overlay/ghost.d.ts +35 -0
  82. package/dist/overlay/ghost.js +508 -0
  83. package/dist/overlay/injector.d.ts +22 -0
  84. package/dist/overlay/injector.js +198 -0
  85. package/dist/plugin/interface.d.ts +134 -0
  86. package/dist/plugin/interface.js +1 -0
  87. package/dist/plugin/loader.d.ts +18 -0
  88. package/dist/plugin/loader.js +155 -0
  89. package/dist/plugin/registry.d.ts +50 -0
  90. package/dist/plugin/registry.js +234 -0
  91. package/dist/security/blacklist.d.ts +24 -0
  92. package/dist/security/blacklist.js +80 -0
  93. package/dist/security/filter.d.ts +2 -0
  94. package/dist/security/filter.js +20 -0
  95. package/dist/security/guard.d.ts +8 -0
  96. package/dist/security/guard.js +35 -0
  97. package/dist/security/ratelimit.d.ts +11 -0
  98. package/dist/security/ratelimit.js +57 -0
  99. package/dist/security/secrets.d.ts +75 -0
  100. package/dist/security/secrets.js +365 -0
  101. package/dist/sensors/actionability.d.ts +81 -0
  102. package/dist/sensors/actionability.js +303 -0
  103. package/dist/sensors/guard.d.ts +25 -0
  104. package/dist/sensors/guard.js +168 -0
  105. package/dist/sensors/mutations.d.ts +23 -0
  106. package/dist/sensors/mutations.js +93 -0
  107. package/dist/sensors/network.d.ts +67 -0
  108. package/dist/sensors/network.js +193 -0
  109. package/dist/sensors/operation_monitor.d.ts +80 -0
  110. package/dist/sensors/operation_monitor.js +203 -0
  111. package/dist/sensors/pacing.d.ts +44 -0
  112. package/dist/sensors/pacing.js +111 -0
  113. package/dist/sensors/thoughts.d.ts +2 -0
  114. package/dist/sensors/thoughts.js +59 -0
  115. package/dist/sensors/wait.d.ts +12 -0
  116. package/dist/sensors/wait.js +44 -0
  117. package/dist/server.d.ts +7 -0
  118. package/dist/server.js +1687 -0
  119. package/dist/state/agent.d.ts +36 -0
  120. package/dist/state/agent.js +101 -0
  121. package/dist/state/cache.d.ts +16 -0
  122. package/dist/state/cache.js +92 -0
  123. package/dist/state/cache.test.d.ts +2 -0
  124. package/dist/state/cache.test.js +76 -0
  125. package/dist/state/delta.d.ts +71 -0
  126. package/dist/state/delta.js +234 -0
  127. package/dist/state/delta.test.d.ts +2 -0
  128. package/dist/state/delta.test.js +279 -0
  129. package/dist/state/groups.d.ts +15 -0
  130. package/dist/state/groups.js +80 -0
  131. package/dist/state/session.d.ts +30 -0
  132. package/dist/state/session.js +155 -0
  133. package/dist/tools/accessibility.d.ts +14 -0
  134. package/dist/tools/accessibility.js +32 -0
  135. package/dist/tools/ask.d.ts +14 -0
  136. package/dist/tools/ask.js +49 -0
  137. package/dist/tools/automation.d.ts +36 -0
  138. package/dist/tools/automation.js +218 -0
  139. package/dist/tools/browser_detect.d.ts +13 -0
  140. package/dist/tools/browser_detect.js +73 -0
  141. package/dist/tools/burst.d.ts +35 -0
  142. package/dist/tools/burst.js +344 -0
  143. package/dist/tools/canvas.d.ts +25 -0
  144. package/dist/tools/canvas.js +132 -0
  145. package/dist/tools/captcha.d.ts +38 -0
  146. package/dist/tools/captcha.js +209 -0
  147. package/dist/tools/click.d.ts +36 -0
  148. package/dist/tools/click.js +561 -0
  149. package/dist/tools/click_and_wait.d.ts +33 -0
  150. package/dist/tools/click_and_wait.js +149 -0
  151. package/dist/tools/clipboard.d.ts +29 -0
  152. package/dist/tools/clipboard.js +279 -0
  153. package/dist/tools/console.d.ts +21 -0
  154. package/dist/tools/console.js +107 -0
  155. package/dist/tools/cookies.d.ts +67 -0
  156. package/dist/tools/cookies.js +121 -0
  157. package/dist/tools/design_extract.d.ts +109 -0
  158. package/dist/tools/design_extract.js +641 -0
  159. package/dist/tools/dialog.d.ts +24 -0
  160. package/dist/tools/dialog.js +109 -0
  161. package/dist/tools/drag.d.ts +15 -0
  162. package/dist/tools/drag.js +29 -0
  163. package/dist/tools/emulation.d.ts +33 -0
  164. package/dist/tools/emulation.js +69 -0
  165. package/dist/tools/eval.d.ts +20 -0
  166. package/dist/tools/eval.js +92 -0
  167. package/dist/tools/fill.d.ts +41 -0
  168. package/dist/tools/fill.js +667 -0
  169. package/dist/tools/find.d.ts +30 -0
  170. package/dist/tools/find.js +60 -0
  171. package/dist/tools/geo.d.ts +19 -0
  172. package/dist/tools/geo.js +29 -0
  173. package/dist/tools/github/analyze.d.ts +4 -0
  174. package/dist/tools/github/analyze.js +113 -0
  175. package/dist/tools/github/cache.d.ts +21 -0
  176. package/dist/tools/github/cache.js +28 -0
  177. package/dist/tools/github/index.d.ts +4 -0
  178. package/dist/tools/github/index.js +4 -0
  179. package/dist/tools/github/issues.d.ts +9 -0
  180. package/dist/tools/github/issues.js +88 -0
  181. package/dist/tools/github/pr.d.ts +5 -0
  182. package/dist/tools/github/pr.js +88 -0
  183. package/dist/tools/github/rateLimiter.d.ts +4 -0
  184. package/dist/tools/github/rateLimiter.js +46 -0
  185. package/dist/tools/github/read.d.ts +34 -0
  186. package/dist/tools/github/read.js +74 -0
  187. package/dist/tools/github/repoContext.d.ts +20 -0
  188. package/dist/tools/github/repoContext.js +129 -0
  189. package/dist/tools/highlight.d.ts +20 -0
  190. package/dist/tools/highlight.js +140 -0
  191. package/dist/tools/hover.d.ts +19 -0
  192. package/dist/tools/hover.js +57 -0
  193. package/dist/tools/inspect.d.ts +17 -0
  194. package/dist/tools/inspect.js +125 -0
  195. package/dist/tools/integration.test.d.ts +2 -0
  196. package/dist/tools/integration.test.js +96 -0
  197. package/dist/tools/key.d.ts +25 -0
  198. package/dist/tools/key.js +173 -0
  199. package/dist/tools/macro_compiler.d.ts +7 -0
  200. package/dist/tools/macro_compiler.js +82 -0
  201. package/dist/tools/mouse.d.ts +21 -0
  202. package/dist/tools/mouse.js +62 -0
  203. package/dist/tools/navigate.d.ts +16 -0
  204. package/dist/tools/navigate.js +188 -0
  205. package/dist/tools/network_intercept.d.ts +17 -0
  206. package/dist/tools/network_intercept.js +151 -0
  207. package/dist/tools/office/index.d.ts +9 -0
  208. package/dist/tools/office/index.js +7 -0
  209. package/dist/tools/office/list.d.ts +15 -0
  210. package/dist/tools/office/list.js +41 -0
  211. package/dist/tools/office/parse.d.ts +20 -0
  212. package/dist/tools/office/parse.js +172 -0
  213. package/dist/tools/pdf.d.ts +22 -0
  214. package/dist/tools/pdf.js +42 -0
  215. package/dist/tools/performance.d.ts +12 -0
  216. package/dist/tools/performance.js +47 -0
  217. package/dist/tools/performance_test.d.ts +70 -0
  218. package/dist/tools/performance_test.js +128 -0
  219. package/dist/tools/plan.d.ts +39 -0
  220. package/dist/tools/plan.js +161 -0
  221. package/dist/tools/pointer.d.ts +35 -0
  222. package/dist/tools/pointer.js +522 -0
  223. package/dist/tools/read.d.ts +30 -0
  224. package/dist/tools/read.js +488 -0
  225. package/dist/tools/recording.d.ts +45 -0
  226. package/dist/tools/recording.js +124 -0
  227. package/dist/tools/response.d.ts +129 -0
  228. package/dist/tools/response.js +5 -0
  229. package/dist/tools/scrape.d.ts +24 -0
  230. package/dist/tools/scrape.js +140 -0
  231. package/dist/tools/screenshot.d.ts +40 -0
  232. package/dist/tools/screenshot.js +79 -0
  233. package/dist/tools/scroll.d.ts +23 -0
  234. package/dist/tools/scroll.js +196 -0
  235. package/dist/tools/select.d.ts +13 -0
  236. package/dist/tools/select.js +217 -0
  237. package/dist/tools/snapshot.d.ts +27 -0
  238. package/dist/tools/snapshot.js +99 -0
  239. package/dist/tools/storage.d.ts +18 -0
  240. package/dist/tools/storage.js +29 -0
  241. package/dist/tools/tabs.d.ts +77 -0
  242. package/dist/tools/tabs.js +342 -0
  243. package/dist/tools/thought.d.ts +16 -0
  244. package/dist/tools/thought.js +79 -0
  245. package/dist/tools/type.d.ts +65 -0
  246. package/dist/tools/type.js +1061 -0
  247. package/dist/tools/type_secret.d.ts +23 -0
  248. package/dist/tools/type_secret.js +229 -0
  249. package/dist/tools/upload.d.ts +19 -0
  250. package/dist/tools/upload.js +103 -0
  251. package/dist/tools/video_compiler.d.ts +24 -0
  252. package/dist/tools/video_compiler.js +199 -0
  253. package/dist/tools/viewport.d.ts +44 -0
  254. package/dist/tools/viewport.js +230 -0
  255. package/dist/tools/wait.d.ts +19 -0
  256. package/dist/tools/wait.js +202 -0
  257. package/dist/tools/wait_for.d.ts +23 -0
  258. package/dist/tools/wait_for.js +30 -0
  259. package/dist/tools/windows.d.ts +22 -0
  260. package/dist/tools/windows.js +57 -0
  261. package/dist/ui/index.d.ts +1 -0
  262. package/dist/ui/index.js +12 -0
  263. package/docs/DEV_MODE.md +68 -0
  264. package/docs/DOCUMENTATION.md +532 -0
  265. package/docs/PLUGINS.md +538 -0
  266. package/docs/TOOLS.md +104 -0
  267. package/docs/dev_plugin.md +310 -0
  268. package/ext-dist/background.js +467 -0
  269. package/ext-dist/content.js +3089 -0
  270. package/ext-dist/dashboard.html +583 -0
  271. package/ext-dist/hud/hud.js +73 -0
  272. package/ext-dist/hud/index.html +20 -0
  273. package/ext-dist/manifest.json +77 -0
  274. package/ext-dist/sidepanel/sidepanel.js +25 -0
  275. package/ext-dist/sidepanel.html +547 -0
  276. package/ext-dist/styles.css +218 -0
  277. package/icon.png +0 -0
  278. package/mcp.json +796 -0
  279. package/package.json +113 -0
  280. package/server.json +23 -0
  281. 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>;