Skip to main content
Glama

Better Playwright MCP

by livoras
smart-outline-simple.ts5.77 kB
/** * 简单的智能大纲生成器 - 专注于列表检测 */ import { ListDetector, type ListPattern } from './list-detector.js'; import { UselessWrapperRemover } from './remove-useless-wrappers.js'; import type { ElementNode } from '../types/outline.js'; export class SmartOutlineSimple { private listDetector = new ListDetector(); private wrapperRemover = new UselessWrapperRemover(); private readonly TEXT_TRUNCATE_LENGTH = 50; private readonly MAX_REFS_IN_SUMMARY = 5; /** * 生成大纲 */ generate(snapshot: string): string { const lines = snapshot.split('\n'); let rootNodes = this.buildTree(lines); // 先删除无意义的generic嵌套 rootNodes = this.wrapperRemover.removeWrappers(rootNodes); // 再进行列表检测和折叠 const outlineLines = this.processWithListDetection(rootNodes); return `Page Outline (${outlineLines.length}/${lines.length} lines):\n` + outlineLines.join('\n'); } /** * 解析单行 */ private parseLine(line: string, lineNumber: number): ElementNode | null { const indent = line.length - line.trimStart().length; const elementMatch = line.match(/^\s*-\s*([a-z]+)(.*)$/); if (!elementMatch) return null; const type = elementMatch[1]; const rest = elementMatch[2]; const refMatch = rest.match(/\[ref=([^\]]+)\]/); const ref = refMatch ? refMatch[1] : ''; let content = rest; if (refMatch) { content = rest.substring(0, refMatch.index).trim(); } const hasInteraction = line.includes('[cursor=pointer]'); return { indent, type, ref, content, line, children: [], priority: 5, isRepetitive: false, lineNumber, hasInteraction }; } /** * 构建树结构 */ private buildTree(lines: string[]): ElementNode[] { const rootNodes: ElementNode[] = []; const stack: ElementNode[] = []; for (let i = 0; i < lines.length; i++) { const line = lines[i]; if (!line.trim()) continue; const node = this.parseLine(line, i); if (!node) continue; while (stack.length > 0 && stack[stack.length - 1].indent >= node.indent) { stack.pop(); } if (stack.length > 0) { const parent = stack[stack.length - 1]; parent.children.push(node); node.parent = parent; } else { rootNodes.push(node); } stack.push(node); } return rootNodes; } /** * 递归处理节点,检测列表 */ private processWithListDetection(nodes: ElementNode[]): string[] { const output: string[] = []; // 首先检查这些同级节点是否构成列表 if (nodes.length >= 3) { const lists = this.listDetector.detectLists(nodes); if (lists.length > 0) { // 处理检测到的列表 for (const list of lists) { const listLines = this.formatList(list); output.push(...listLines); // 递归处理样本元素的子节点 if (list.sample.children.length > 0) { const childLines = this.processWithListDetection( list.sample.children ); output.push(...childLines); } } // 处理不在列表中的节点 const listItems = new Set(lists.flatMap(l => l.items)); const nonListNodes = nodes.filter(n => !listItems.has(n)); for (const node of nonListNodes) { output.push(this.formatNode(node)); if (node.children.length > 0) { const childLines = this.processWithListDetection( node.children ); output.push(...childLines); } } return output; } } // 没有检测到列表,正常处理每个节点 for (const node of nodes) { output.push(this.formatNode(node)); // 递归处理子节点 if (node.children.length > 0) { const childLines = this.processWithListDetection( node.children ); output.push(...childLines); } } return output; } /** * 格式化列表 */ private formatList(list: ListPattern): string[] { const output: string[] = []; const indent = ' '.repeat(list.sample.indent); // 输出样本元素 output.push(this.formatNode(list.sample)); // 输出折叠行 if (list.count > 1) { const remaining = list.count - 1; const refs = list.refs.slice(1, Math.min(this.MAX_REFS_IN_SUMMARY + 1, list.refs.length)); let foldLine = `${indent}- ${list.sample.type}`; foldLine += ` (... and ${remaining} more similar)`; if (refs.length > 0) { foldLine += ` [refs: ${refs.join(', ')}`; if (list.refs.length > this.MAX_REFS_IN_SUMMARY + 1) { foldLine += ', ...'; } foldLine += ']'; } output.push(foldLine); } return output; } /** * 格式化节点 */ private formatNode(node: ElementNode): string { const indent = ' '.repeat(node.indent); let result = `${indent}- ${node.type}`; // 截断长内容 if (node.content) { let content = node.content; if (content.length > this.TEXT_TRUNCATE_LENGTH) { content = content.substring(0, this.TEXT_TRUNCATE_LENGTH) + '...'; } result += ` ${content}`; } // 添加ref if (node.ref) { result += ` [ref=${node.ref}]`; } // 添加交互标记 if (node.hasInteraction) { result += ' [cursor=pointer]'; } return result; } }

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/livoras/better-playwright-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server