@jackwener/opencli 1.1.1 → 1.2.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/CONTRIBUTING.md +39 -1
- package/README.md +9 -10
- package/README.zh-CN.md +39 -17
- package/SKILL.md +10 -5
- package/dist/browser/cdp.d.ts +4 -4
- package/dist/browser/cdp.js +39 -16
- package/dist/browser/daemon-client.d.ts +2 -1
- package/dist/browser/dom-helpers.js +38 -7
- package/dist/browser/dom-snapshot.d.ts +86 -0
- package/dist/browser/dom-snapshot.js +729 -0
- package/dist/browser/dom-snapshot.test.d.ts +11 -0
- package/dist/browser/dom-snapshot.test.js +212 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- package/dist/browser/page.d.ts +14 -24
- package/dist/browser/page.js +37 -4
- package/dist/build-manifest.d.ts +11 -4
- package/dist/build-manifest.js +59 -21
- package/dist/build-manifest.test.js +58 -2
- package/dist/cli-manifest.json +3856 -1509
- package/dist/cli.js +66 -0
- package/dist/clis/barchart/greeks.js +1 -1
- package/dist/clis/barchart/options.js +1 -1
- package/dist/clis/barchart/quote.js +1 -1
- package/dist/clis/bilibili/download.js +1 -1
- package/dist/clis/bilibili/following.js +1 -1
- package/dist/clis/bilibili/subtitle.js +1 -1
- package/dist/clis/bilibili/user-videos.js +1 -1
- package/dist/clis/boss/batchgreet.js +10 -97
- package/dist/clis/boss/chatlist.js +8 -25
- package/dist/clis/boss/chatmsg.js +11 -42
- package/dist/clis/boss/common.d.ts +92 -0
- package/dist/clis/boss/common.js +223 -0
- package/dist/clis/boss/detail.js +7 -49
- package/dist/clis/boss/exchange.js +13 -79
- package/dist/clis/boss/greet.js +18 -145
- package/dist/clis/boss/invite.js +26 -121
- package/dist/clis/boss/joblist.js +6 -31
- package/dist/clis/boss/mark.js +12 -85
- package/dist/clis/boss/recommend.js +10 -49
- package/dist/clis/boss/resume.js +18 -118
- package/dist/clis/boss/search.js +12 -60
- package/dist/clis/boss/send.js +17 -151
- package/dist/clis/boss/stats.js +18 -69
- package/dist/clis/coupang/add-to-cart.js +1 -1
- package/dist/clis/devto/tag.yaml +34 -0
- package/dist/clis/devto/top.yaml +29 -0
- package/dist/clis/devto/user.yaml +33 -0
- package/dist/clis/douban/book-hot.d.ts +1 -0
- package/dist/clis/douban/book-hot.js +14 -0
- package/dist/clis/douban/marks.d.ts +1 -0
- package/dist/clis/douban/marks.js +115 -0
- package/dist/clis/douban/movie-hot.d.ts +1 -0
- package/dist/clis/douban/movie-hot.js +14 -0
- package/dist/clis/douban/reviews.d.ts +1 -0
- package/dist/clis/douban/reviews.js +106 -0
- package/dist/clis/douban/search.d.ts +1 -0
- package/dist/clis/douban/search.js +16 -0
- package/dist/clis/douban/shared.d.ts +4 -0
- package/dist/clis/douban/shared.js +155 -0
- package/dist/clis/douban/subject.yaml +76 -0
- package/dist/clis/douban/top250.yaml +70 -0
- package/dist/clis/douban/utils.d.ts +35 -0
- package/dist/clis/douban/utils.js +48 -0
- package/dist/clis/facebook/add-friend.yaml +43 -0
- package/dist/clis/facebook/events.yaml +44 -0
- package/dist/clis/facebook/feed.yaml +63 -0
- package/dist/clis/facebook/friends.yaml +42 -0
- package/dist/clis/facebook/groups.yaml +50 -0
- package/dist/clis/facebook/join-group.yaml +44 -0
- package/dist/clis/facebook/memories.yaml +39 -0
- package/dist/clis/facebook/notifications.yaml +40 -0
- package/dist/clis/facebook/profile.yaml +37 -0
- package/dist/clis/facebook/search.yaml +46 -0
- package/dist/clis/google/news.d.ts +5 -0
- package/dist/clis/google/news.js +58 -0
- package/dist/clis/google/search.d.ts +10 -0
- package/dist/clis/google/search.js +127 -0
- package/dist/clis/google/suggest.d.ts +5 -0
- package/dist/clis/google/suggest.js +34 -0
- package/dist/clis/google/trends.d.ts +5 -0
- package/dist/clis/google/trends.js +38 -0
- package/dist/clis/google/utils.d.ts +9 -0
- package/dist/clis/google/utils.js +23 -0
- package/dist/clis/google/utils.test.d.ts +1 -0
- package/dist/clis/google/utils.test.js +75 -0
- package/dist/clis/grok/ask.d.ts +14 -0
- package/dist/clis/grok/ask.js +257 -65
- package/dist/clis/grok/ask.test.d.ts +1 -0
- package/dist/clis/grok/ask.test.js +36 -0
- package/dist/clis/instagram/comment.yaml +52 -0
- package/dist/clis/instagram/explore.yaml +43 -0
- package/dist/clis/instagram/follow.yaml +41 -0
- package/dist/clis/instagram/followers.yaml +51 -0
- package/dist/clis/instagram/following.yaml +51 -0
- package/dist/clis/instagram/like.yaml +46 -0
- package/dist/clis/instagram/profile.yaml +42 -0
- package/dist/clis/instagram/save.yaml +46 -0
- package/dist/clis/instagram/saved.yaml +40 -0
- package/dist/clis/instagram/search.yaml +43 -0
- package/dist/clis/instagram/unfollow.yaml +38 -0
- package/dist/clis/instagram/unlike.yaml +46 -0
- package/dist/clis/instagram/unsave.yaml +46 -0
- package/dist/clis/instagram/user.yaml +54 -0
- package/dist/clis/jike/repost.js +1 -1
- package/dist/clis/jimeng/generate.yaml +1 -0
- package/dist/clis/linux-do/category.yaml +1 -0
- package/dist/clis/lobsters/active.yaml +29 -0
- package/dist/clis/lobsters/hot.yaml +29 -0
- package/dist/clis/lobsters/newest.yaml +29 -0
- package/dist/clis/lobsters/tag.yaml +34 -0
- package/dist/clis/medium/feed.d.ts +1 -0
- package/dist/clis/medium/feed.js +15 -0
- package/dist/clis/medium/search.d.ts +1 -0
- package/dist/clis/medium/search.js +15 -0
- package/dist/clis/medium/shared.d.ts +5 -0
- package/dist/clis/medium/shared.js +78 -0
- package/dist/clis/medium/user.d.ts +1 -0
- package/dist/clis/medium/user.js +15 -0
- package/dist/clis/reddit/comment.js +1 -1
- package/dist/clis/reddit/read.js +1 -1
- package/dist/clis/reddit/save.js +1 -1
- package/dist/clis/reddit/subreddit.yaml +1 -0
- package/dist/clis/reddit/subscribe.js +1 -1
- package/dist/clis/reddit/upvote.js +1 -1
- package/dist/clis/sinablog/article.d.ts +1 -0
- package/dist/clis/sinablog/article.js +14 -0
- package/dist/clis/sinablog/hot.d.ts +1 -0
- package/dist/clis/sinablog/hot.js +14 -0
- package/dist/clis/sinablog/search.d.ts +1 -0
- package/dist/clis/sinablog/search.js +51 -0
- package/dist/clis/sinablog/shared.d.ts +7 -0
- package/dist/clis/sinablog/shared.js +187 -0
- package/dist/clis/sinablog/user.d.ts +1 -0
- package/dist/clis/sinablog/user.js +15 -0
- package/dist/clis/substack/feed.d.ts +1 -0
- package/dist/clis/substack/feed.js +15 -0
- package/dist/clis/substack/publication.d.ts +1 -0
- package/dist/clis/substack/publication.js +15 -0
- package/dist/clis/substack/search.d.ts +1 -0
- package/dist/clis/substack/search.js +77 -0
- package/dist/clis/substack/shared.d.ts +4 -0
- package/dist/clis/substack/shared.js +129 -0
- package/dist/clis/tiktok/comment.yaml +66 -0
- package/dist/clis/tiktok/explore.yaml +39 -0
- package/dist/clis/tiktok/follow.yaml +39 -0
- package/dist/clis/tiktok/following.yaml +46 -0
- package/dist/clis/tiktok/friends.yaml +47 -0
- package/dist/clis/tiktok/like.yaml +38 -0
- package/dist/clis/tiktok/live.yaml +51 -0
- package/dist/clis/tiktok/notifications.yaml +52 -0
- package/dist/clis/tiktok/profile.yaml +45 -0
- package/dist/clis/tiktok/save.yaml +34 -0
- package/dist/clis/tiktok/search.yaml +46 -0
- package/dist/clis/tiktok/unfollow.yaml +44 -0
- package/dist/clis/tiktok/unlike.yaml +38 -0
- package/dist/clis/tiktok/unsave.yaml +36 -0
- package/dist/clis/tiktok/user.yaml +44 -0
- package/dist/clis/twitter/download.d.ts +1 -1
- package/dist/clis/twitter/download.js +3 -3
- package/dist/clis/twitter/followers.js +1 -1
- package/dist/clis/twitter/following.js +1 -1
- package/dist/clis/twitter/thread.js +1 -1
- package/dist/clis/twitter/timeline.d.ts +23 -0
- package/dist/clis/twitter/timeline.js +42 -14
- package/dist/clis/twitter/timeline.test.d.ts +1 -0
- package/dist/clis/twitter/timeline.test.js +102 -0
- package/dist/clis/wikipedia/random.d.ts +1 -0
- package/dist/clis/wikipedia/random.js +19 -0
- package/dist/clis/wikipedia/search.js +3 -3
- package/dist/clis/wikipedia/summary.js +4 -9
- package/dist/clis/wikipedia/trending.d.ts +1 -0
- package/dist/clis/wikipedia/trending.js +35 -0
- package/dist/clis/wikipedia/utils.d.ts +28 -0
- package/dist/clis/wikipedia/utils.js +13 -0
- package/dist/clis/xiaohongshu/creator-note-detail.js +1 -1
- package/dist/clis/xiaohongshu/creator-note-detail.test.js +2 -0
- package/dist/clis/xiaohongshu/creator-notes.test.js +2 -0
- package/dist/clis/xiaohongshu/download.js +1 -1
- package/dist/clis/xueqiu/earnings-date.yaml +69 -0
- package/dist/clis/xueqiu/search.yaml +2 -1
- package/dist/clis/xueqiu/stock.yaml +2 -0
- package/dist/clis/yahoo-finance/quote.js +1 -1
- package/dist/commanderAdapter.js +13 -7
- package/dist/discovery.d.ts +8 -0
- package/dist/discovery.js +105 -19
- package/dist/doctor.js +3 -1
- package/dist/doctor.test.js +46 -2
- package/dist/engine.test.d.ts +0 -3
- package/dist/engine.test.js +74 -6
- package/dist/execution.d.ts +4 -2
- package/dist/execution.js +31 -7
- package/dist/explore.d.ts +76 -3
- package/dist/explore.js +11 -4
- package/dist/generate.d.ts +41 -2
- package/dist/generate.js +5 -4
- package/dist/main.js +2 -1
- package/dist/pipeline/executor.d.ts +2 -2
- package/dist/pipeline/executor.js +2 -2
- package/dist/pipeline/executor.test.js +33 -6
- package/dist/pipeline/registry.d.ts +1 -1
- package/dist/pipeline/steps/browser.d.ts +7 -7
- package/dist/pipeline/steps/browser.js +15 -7
- package/dist/pipeline/steps/fetch.d.ts +1 -1
- package/dist/pipeline/steps/fetch.js +11 -7
- package/dist/pipeline/steps/transform.d.ts +6 -5
- package/dist/pipeline/steps/transform.js +30 -9
- package/dist/pipeline/template.d.ts +6 -6
- package/dist/pipeline/template.js +43 -5
- package/dist/pipeline/template.test.js +18 -0
- package/dist/pipeline/transform.test.js +11 -0
- package/dist/plugin.d.ts +31 -0
- package/dist/plugin.js +216 -0
- package/dist/plugin.test.d.ts +4 -0
- package/dist/plugin.test.js +76 -0
- package/dist/registry-api.d.ts +11 -0
- package/dist/registry-api.js +9 -0
- package/dist/registry.d.ts +11 -0
- package/dist/registry.js +6 -1
- package/dist/synthesize.d.ts +94 -4
- package/dist/synthesize.js +5 -4
- package/dist/types.d.ts +39 -26
- package/dist/validate.js +8 -2
- package/docs/.vitepress/config.mts +6 -4
- package/docs/adapters/browser/barchart.md +6 -5
- package/docs/adapters/browser/bilibili.md +9 -0
- package/docs/adapters/browser/devto.md +35 -0
- package/docs/adapters/browser/douban.md +38 -0
- package/docs/adapters/browser/facebook.md +36 -0
- package/docs/adapters/browser/google.md +62 -0
- package/docs/adapters/browser/grok.md +26 -8
- package/docs/adapters/browser/instagram.md +46 -0
- package/docs/adapters/browser/lobsters.md +32 -0
- package/docs/adapters/browser/medium.md +32 -0
- package/docs/adapters/browser/reddit.md +9 -0
- package/docs/adapters/browser/sinablog.md +36 -0
- package/docs/adapters/browser/substack.md +38 -0
- package/docs/adapters/browser/tiktok.md +68 -0
- package/docs/adapters/browser/wikipedia.md +11 -2
- package/docs/adapters/browser/xueqiu.md +10 -0
- package/docs/adapters/browser/yahoo-finance.md +6 -5
- package/docs/adapters/desktop/antigravity.md +6 -0
- package/docs/adapters/desktop/chatgpt.md +2 -1
- package/docs/adapters/desktop/codex.md +5 -1
- package/docs/adapters/desktop/cursor.md +4 -0
- package/docs/adapters/desktop/discord.md +7 -7
- package/docs/adapters/index.md +1 -4
- package/docs/guide/getting-started.md +1 -0
- package/docs/guide/plugins.md +153 -0
- package/docs/zh/guide/plugins.md +107 -0
- package/extension/src/background.ts +18 -11
- package/package.json +10 -5
- package/scripts/clean-dist.cjs +13 -0
- package/src/browser/cdp.ts +71 -31
- package/src/browser/daemon-client.ts +2 -1
- package/src/browser/dom-helpers.ts +38 -7
- package/src/browser/dom-snapshot.test.ts +249 -0
- package/src/browser/dom-snapshot.ts +770 -0
- package/src/browser/index.ts +2 -0
- package/src/browser/page.ts +50 -19
- package/src/build-manifest.test.ts +70 -2
- package/src/build-manifest.ts +94 -26
- package/src/cli.ts +71 -2
- package/src/clis/barchart/greeks.ts +1 -1
- package/src/clis/barchart/options.ts +1 -1
- package/src/clis/barchart/quote.ts +1 -1
- package/src/clis/bilibili/download.ts +1 -1
- package/src/clis/bilibili/following.ts +1 -1
- package/src/clis/bilibili/subtitle.ts +1 -1
- package/src/clis/bilibili/user-videos.ts +1 -1
- package/src/clis/boss/batchgreet.ts +14 -106
- package/src/clis/boss/chatlist.ts +12 -26
- package/src/clis/boss/chatmsg.ts +16 -40
- package/src/clis/boss/common.ts +287 -0
- package/src/clis/boss/detail.ts +8 -54
- package/src/clis/boss/exchange.ts +15 -89
- package/src/clis/boss/greet.ts +23 -160
- package/src/clis/boss/invite.ts +36 -133
- package/src/clis/boss/joblist.ts +7 -36
- package/src/clis/boss/mark.ts +13 -94
- package/src/clis/boss/recommend.ts +12 -57
- package/src/clis/boss/resume.ts +19 -124
- package/src/clis/boss/search.ts +13 -66
- package/src/clis/boss/send.ts +21 -161
- package/src/clis/boss/stats.ts +19 -74
- package/src/clis/coupang/add-to-cart.ts +1 -1
- package/src/clis/devto/tag.yaml +34 -0
- package/src/clis/devto/top.yaml +29 -0
- package/src/clis/devto/user.yaml +33 -0
- package/src/clis/douban/book-hot.ts +15 -0
- package/src/clis/douban/marks.ts +135 -0
- package/src/clis/douban/movie-hot.ts +15 -0
- package/src/clis/douban/reviews.ts +127 -0
- package/src/clis/douban/search.ts +17 -0
- package/src/clis/douban/shared.ts +165 -0
- package/src/clis/douban/subject.yaml +76 -0
- package/src/clis/douban/top250.yaml +70 -0
- package/src/clis/douban/utils.ts +81 -0
- package/src/clis/facebook/add-friend.yaml +43 -0
- package/src/clis/facebook/events.yaml +44 -0
- package/src/clis/facebook/feed.yaml +63 -0
- package/src/clis/facebook/friends.yaml +42 -0
- package/src/clis/facebook/groups.yaml +50 -0
- package/src/clis/facebook/join-group.yaml +44 -0
- package/src/clis/facebook/memories.yaml +39 -0
- package/src/clis/facebook/notifications.yaml +40 -0
- package/src/clis/facebook/profile.yaml +37 -0
- package/src/clis/facebook/search.yaml +46 -0
- package/src/clis/google/news.ts +66 -0
- package/src/clis/google/search.ts +133 -0
- package/src/clis/google/suggest.ts +40 -0
- package/src/clis/google/trends.ts +44 -0
- package/src/clis/google/utils.test.ts +82 -0
- package/src/clis/google/utils.ts +24 -0
- package/src/clis/grok/ask.test.ts +53 -0
- package/src/clis/grok/ask.ts +300 -69
- package/src/clis/instagram/comment.yaml +52 -0
- package/src/clis/instagram/explore.yaml +43 -0
- package/src/clis/instagram/follow.yaml +41 -0
- package/src/clis/instagram/followers.yaml +51 -0
- package/src/clis/instagram/following.yaml +51 -0
- package/src/clis/instagram/like.yaml +46 -0
- package/src/clis/instagram/profile.yaml +42 -0
- package/src/clis/instagram/save.yaml +46 -0
- package/src/clis/instagram/saved.yaml +40 -0
- package/src/clis/instagram/search.yaml +43 -0
- package/src/clis/instagram/unfollow.yaml +38 -0
- package/src/clis/instagram/unlike.yaml +46 -0
- package/src/clis/instagram/unsave.yaml +46 -0
- package/src/clis/instagram/user.yaml +54 -0
- package/src/clis/jike/repost.ts +1 -1
- package/src/clis/jimeng/generate.yaml +1 -0
- package/src/clis/linux-do/category.yaml +1 -0
- package/src/clis/lobsters/active.yaml +29 -0
- package/src/clis/lobsters/hot.yaml +29 -0
- package/src/clis/lobsters/newest.yaml +29 -0
- package/src/clis/lobsters/tag.yaml +34 -0
- package/src/clis/medium/feed.ts +16 -0
- package/src/clis/medium/search.ts +16 -0
- package/src/clis/medium/shared.ts +83 -0
- package/src/clis/medium/user.ts +16 -0
- package/src/clis/reddit/comment.ts +1 -1
- package/src/clis/reddit/read.ts +1 -1
- package/src/clis/reddit/save.ts +1 -1
- package/src/clis/reddit/subreddit.yaml +1 -0
- package/src/clis/reddit/subscribe.ts +1 -1
- package/src/clis/reddit/upvote.ts +1 -1
- package/src/clis/sinablog/article.ts +15 -0
- package/src/clis/sinablog/hot.ts +15 -0
- package/src/clis/sinablog/search.ts +56 -0
- package/src/clis/sinablog/shared.ts +198 -0
- package/src/clis/sinablog/user.ts +16 -0
- package/src/clis/substack/feed.ts +16 -0
- package/src/clis/substack/publication.ts +16 -0
- package/src/clis/substack/search.ts +91 -0
- package/src/clis/substack/shared.ts +132 -0
- package/src/clis/tiktok/comment.yaml +66 -0
- package/src/clis/tiktok/explore.yaml +39 -0
- package/src/clis/tiktok/follow.yaml +39 -0
- package/src/clis/tiktok/following.yaml +46 -0
- package/src/clis/tiktok/friends.yaml +47 -0
- package/src/clis/tiktok/like.yaml +38 -0
- package/src/clis/tiktok/live.yaml +51 -0
- package/src/clis/tiktok/notifications.yaml +52 -0
- package/src/clis/tiktok/profile.yaml +45 -0
- package/src/clis/tiktok/save.yaml +34 -0
- package/src/clis/tiktok/search.yaml +46 -0
- package/src/clis/tiktok/unfollow.yaml +44 -0
- package/src/clis/tiktok/unlike.yaml +38 -0
- package/src/clis/tiktok/unsave.yaml +36 -0
- package/src/clis/tiktok/user.yaml +44 -0
- package/src/clis/twitter/download.ts +3 -3
- package/src/clis/twitter/followers.ts +1 -1
- package/src/clis/twitter/following.ts +1 -1
- package/src/clis/twitter/thread.ts +1 -1
- package/src/clis/twitter/timeline.test.ts +109 -0
- package/src/clis/twitter/timeline.ts +59 -19
- package/src/clis/wikipedia/random.ts +19 -0
- package/src/clis/wikipedia/search.ts +10 -4
- package/src/clis/wikipedia/summary.ts +4 -9
- package/src/clis/wikipedia/trending.ts +41 -0
- package/src/clis/wikipedia/utils.ts +31 -0
- package/src/clis/xiaohongshu/creator-note-detail.test.ts +2 -0
- package/src/clis/xiaohongshu/creator-note-detail.ts +1 -1
- package/src/clis/xiaohongshu/creator-notes.test.ts +2 -0
- package/src/clis/xiaohongshu/download.ts +1 -1
- package/src/clis/xueqiu/earnings-date.yaml +69 -0
- package/src/clis/xueqiu/search.yaml +2 -1
- package/src/clis/xueqiu/stock.yaml +2 -0
- package/src/clis/yahoo-finance/quote.ts +1 -1
- package/src/commanderAdapter.ts +17 -10
- package/src/discovery.ts +134 -24
- package/src/doctor.test.ts +59 -2
- package/src/doctor.ts +4 -2
- package/src/engine.test.ts +79 -6
- package/src/execution.ts +42 -16
- package/src/explore.ts +77 -9
- package/src/generate.ts +58 -9
- package/src/main.ts +2 -1
- package/src/pipeline/executor.test.ts +35 -6
- package/src/pipeline/executor.ts +11 -7
- package/src/pipeline/registry.ts +3 -3
- package/src/pipeline/steps/browser.ts +24 -15
- package/src/pipeline/steps/fetch.ts +18 -13
- package/src/pipeline/steps/transform.ts +40 -15
- package/src/pipeline/template.test.ts +18 -0
- package/src/pipeline/template.ts +86 -13
- package/src/pipeline/transform.test.ts +15 -2
- package/src/plugin.test.ts +86 -0
- package/src/plugin.ts +254 -0
- package/src/registry-api.ts +12 -0
- package/src/registry.ts +19 -1
- package/src/synthesize.ts +102 -21
- package/src/types.ts +44 -12
- package/src/validate.ts +19 -4
- package/tests/e2e/browser-public.test.ts +11 -0
- package/tests/e2e/public-commands.test.ts +64 -0
- package/dist/clis/feishu/new.d.ts +0 -1
- package/dist/clis/feishu/new.js +0 -27
- package/dist/clis/feishu/read.d.ts +0 -1
- package/dist/clis/feishu/read.js +0 -40
- package/dist/clis/feishu/search.d.ts +0 -1
- package/dist/clis/feishu/search.js +0 -30
- package/dist/clis/feishu/send.d.ts +0 -1
- package/dist/clis/feishu/send.js +0 -39
- package/dist/clis/feishu/status.d.ts +0 -1
- package/dist/clis/feishu/status.js +0 -28
- package/dist/clis/neteasemusic/like.d.ts +0 -1
- package/dist/clis/neteasemusic/like.js +0 -25
- package/dist/clis/neteasemusic/lyrics.d.ts +0 -1
- package/dist/clis/neteasemusic/lyrics.js +0 -47
- package/dist/clis/neteasemusic/next.d.ts +0 -1
- package/dist/clis/neteasemusic/next.js +0 -26
- package/dist/clis/neteasemusic/play.d.ts +0 -1
- package/dist/clis/neteasemusic/play.js +0 -26
- package/dist/clis/neteasemusic/playing.d.ts +0 -1
- package/dist/clis/neteasemusic/playing.js +0 -59
- package/dist/clis/neteasemusic/playlist.d.ts +0 -1
- package/dist/clis/neteasemusic/playlist.js +0 -46
- package/dist/clis/neteasemusic/prev.d.ts +0 -1
- package/dist/clis/neteasemusic/prev.js +0 -25
- package/dist/clis/neteasemusic/search.d.ts +0 -1
- package/dist/clis/neteasemusic/search.js +0 -52
- package/dist/clis/neteasemusic/status.d.ts +0 -1
- package/dist/clis/neteasemusic/status.js +0 -16
- package/dist/clis/neteasemusic/volume.d.ts +0 -1
- package/dist/clis/neteasemusic/volume.js +0 -54
- package/dist/clis/wechat/chats.d.ts +0 -1
- package/dist/clis/wechat/chats.js +0 -28
- package/dist/clis/wechat/contacts.d.ts +0 -1
- package/dist/clis/wechat/contacts.js +0 -28
- package/dist/clis/wechat/read.d.ts +0 -1
- package/dist/clis/wechat/read.js +0 -58
- package/dist/clis/wechat/search.d.ts +0 -1
- package/dist/clis/wechat/search.js +0 -31
- package/dist/clis/wechat/send.d.ts +0 -1
- package/dist/clis/wechat/send.js +0 -42
- package/dist/clis/wechat/status.d.ts +0 -1
- package/dist/clis/wechat/status.js +0 -29
- package/dist/pipeline.d.ts +0 -7
- package/dist/pipeline.js +0 -7
- package/docs/adapters/browser/github.md +0 -26
- package/docs/adapters/desktop/feishu.md +0 -20
- package/docs/adapters/desktop/neteasemusic.md +0 -31
- package/docs/adapters/desktop/wechat.md +0 -28
- package/src/clis/antigravity/README.md +0 -5
- package/src/clis/antigravity/README.zh-CN.md +0 -51
- package/src/clis/chaoxing/README.md +0 -14
- package/src/clis/chaoxing/README.zh-CN.md +0 -35
- package/src/clis/chatgpt/README.md +0 -5
- package/src/clis/chatgpt/README.zh-CN.md +0 -44
- package/src/clis/chatwise/README.md +0 -5
- package/src/clis/chatwise/README.zh-CN.md +0 -38
- package/src/clis/codex/README.md +0 -5
- package/src/clis/codex/README.zh-CN.md +0 -33
- package/src/clis/cursor/README.md +0 -5
- package/src/clis/cursor/README.zh-CN.md +0 -33
- package/src/clis/discord-app/README.md +0 -5
- package/src/clis/discord-app/README.zh-CN.md +0 -28
- package/src/clis/feishu/README.md +0 -5
- package/src/clis/feishu/README.zh-CN.md +0 -20
- package/src/clis/feishu/new.ts +0 -32
- package/src/clis/feishu/read.ts +0 -48
- package/src/clis/feishu/search.ts +0 -35
- package/src/clis/feishu/send.ts +0 -46
- package/src/clis/feishu/status.ts +0 -34
- package/src/clis/neteasemusic/README.md +0 -5
- package/src/clis/neteasemusic/README.zh-CN.md +0 -31
- package/src/clis/neteasemusic/like.ts +0 -28
- package/src/clis/neteasemusic/lyrics.ts +0 -53
- package/src/clis/neteasemusic/next.ts +0 -30
- package/src/clis/neteasemusic/play.ts +0 -30
- package/src/clis/neteasemusic/playing.ts +0 -62
- package/src/clis/neteasemusic/playlist.ts +0 -51
- package/src/clis/neteasemusic/prev.ts +0 -29
- package/src/clis/neteasemusic/search.ts +0 -58
- package/src/clis/neteasemusic/status.ts +0 -18
- package/src/clis/neteasemusic/volume.ts +0 -61
- package/src/clis/notion/README.md +0 -5
- package/src/clis/notion/README.zh-CN.md +0 -29
- package/src/clis/wechat/README.md +0 -5
- package/src/clis/wechat/README.zh-CN.md +0 -28
- package/src/clis/wechat/chats.ts +0 -33
- package/src/clis/wechat/contacts.ts +0 -33
- package/src/clis/wechat/read.ts +0 -72
- package/src/clis/wechat/search.ts +0 -36
- package/src/clis/wechat/send.ts +0 -49
- package/src/clis/wechat/status.ts +0 -35
- package/src/pipeline.ts +0 -8
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DOM Snapshot Engine — Advanced DOM pruning for LLM consumption.
|
|
3
|
+
*
|
|
4
|
+
* Inspired by browser-use's multi-layer pruning pipeline, adapted for opencli's
|
|
5
|
+
* Chrome Extension + CDP architecture. Runs entirely in-page via Runtime.evaluate.
|
|
6
|
+
*
|
|
7
|
+
* Pipeline:
|
|
8
|
+
* 1. Walk DOM tree, collect visibility + layout + interactivity signals
|
|
9
|
+
* 2. Prune invisible, zero-area, non-content elements
|
|
10
|
+
* 3. SVG & decoration collapse
|
|
11
|
+
* 4. Shadow DOM traversal
|
|
12
|
+
* 5. Same-origin iframe content extraction
|
|
13
|
+
* 6. Bounding-box parent-child dedup (link/button wrapping children)
|
|
14
|
+
* 7. Paint-order occlusion detection (overlay/modal coverage)
|
|
15
|
+
* 8. Attribute whitelist filtering
|
|
16
|
+
* 9. Table-aware serialization (markdown tables)
|
|
17
|
+
* 10. Token-efficient serialization with interactive indices
|
|
18
|
+
* 11. data-ref annotation for click/type targeting
|
|
19
|
+
* 12. Hidden interactive element hints (scroll-to-reveal)
|
|
20
|
+
* 13. Incremental diff (mark new elements with *)
|
|
21
|
+
*
|
|
22
|
+
* Additional tools:
|
|
23
|
+
* - scrollToRefJs(ref) — scroll to a data-opencli-ref element
|
|
24
|
+
* - getFormStateJs() — extract all form fields as structured JSON
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
// ─── Types ───────────────────────────────────────────────────────────
|
|
28
|
+
|
|
29
|
+
export interface SnapshotOptions {
|
|
30
|
+
/** Extra pixels beyond viewport to include (default 800) */
|
|
31
|
+
viewportExpand?: number;
|
|
32
|
+
/** Maximum DOM depth to traverse (default 50) */
|
|
33
|
+
maxDepth?: number;
|
|
34
|
+
/** Only emit interactive elements and their landmark ancestors */
|
|
35
|
+
interactiveOnly?: boolean;
|
|
36
|
+
/** Maximum text content length per node (default 120) */
|
|
37
|
+
maxTextLength?: number;
|
|
38
|
+
/** Include scroll position info on scrollable containers (default true) */
|
|
39
|
+
includeScrollInfo?: boolean;
|
|
40
|
+
/** Enable bounding-box parent-child dedup (default true) */
|
|
41
|
+
bboxDedup?: boolean;
|
|
42
|
+
/** Traverse Shadow DOM roots (default true) */
|
|
43
|
+
includeShadowDom?: boolean;
|
|
44
|
+
/** Extract same-origin iframe content (default true) */
|
|
45
|
+
includeIframes?: boolean;
|
|
46
|
+
/** Maximum number of iframes to process (default 5) */
|
|
47
|
+
maxIframes?: number;
|
|
48
|
+
/** Enable paint-order occlusion detection (default true) */
|
|
49
|
+
paintOrderCheck?: boolean;
|
|
50
|
+
/** Annotate interactive elements with data-opencli-ref (default true) */
|
|
51
|
+
annotateRefs?: boolean;
|
|
52
|
+
/** Report hidden interactive elements outside viewport (default true) */
|
|
53
|
+
reportHidden?: boolean;
|
|
54
|
+
/** Filter ad/noise elements (default true) */
|
|
55
|
+
filterAds?: boolean;
|
|
56
|
+
/** Serialize tables as markdown (default true) */
|
|
57
|
+
markdownTables?: boolean;
|
|
58
|
+
/** Previous snapshot hash set (JSON array of hashes) for diff marking (default null) */
|
|
59
|
+
previousHashes?: string | null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// ─── Utility JS Generators ───────────────────────────────────────────
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Generate JS to scroll to an element identified by data-opencli-ref.
|
|
66
|
+
* Completes the snapshot→action loop: snapshot identifies `[3]<button>`,
|
|
67
|
+
* caller can then `scrollToRef('3')` to bring it into view.
|
|
68
|
+
*/
|
|
69
|
+
export function scrollToRefJs(ref: string): string {
|
|
70
|
+
const safeRef = JSON.stringify(ref);
|
|
71
|
+
return `
|
|
72
|
+
(() => {
|
|
73
|
+
const ref = ${safeRef};
|
|
74
|
+
const el = document.querySelector('[data-opencli-ref="' + ref + '"]')
|
|
75
|
+
|| document.querySelector('[data-ref="' + ref + '"]');
|
|
76
|
+
if (!el) throw new Error('Element not found: ref=' + ref);
|
|
77
|
+
el.scrollIntoView({ behavior: 'smooth', block: 'center', inline: 'nearest' });
|
|
78
|
+
return { scrolled: true, tag: el.tagName.toLowerCase(), text: (el.textContent || '').trim().slice(0, 80) };
|
|
79
|
+
})()
|
|
80
|
+
`.trim();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generate JS to extract all form field values from the page.
|
|
85
|
+
* Returns structured JSON: { forms: [{ id, action, fields: [{ tag, type, name, value, ... }] }] }
|
|
86
|
+
*/
|
|
87
|
+
export function getFormStateJs(): string {
|
|
88
|
+
return `
|
|
89
|
+
(() => {
|
|
90
|
+
const result = { forms: [], orphanFields: [] };
|
|
91
|
+
|
|
92
|
+
// Collect all forms
|
|
93
|
+
for (const form of document.forms) {
|
|
94
|
+
const formData = {
|
|
95
|
+
id: form.id || null,
|
|
96
|
+
name: form.name || null,
|
|
97
|
+
action: form.action || null,
|
|
98
|
+
method: (form.method || 'get').toUpperCase(),
|
|
99
|
+
fields: [],
|
|
100
|
+
};
|
|
101
|
+
for (const el of form.elements) {
|
|
102
|
+
const field = extractField(el);
|
|
103
|
+
if (field) formData.fields.push(field);
|
|
104
|
+
}
|
|
105
|
+
if (formData.fields.length > 0) result.forms.push(formData);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Collect orphan fields (not inside a form)
|
|
109
|
+
const allInputs = document.querySelectorAll('input, textarea, select, [contenteditable="true"]');
|
|
110
|
+
for (const el of allInputs) {
|
|
111
|
+
if (el.form) continue; // already in a form
|
|
112
|
+
const field = extractField(el);
|
|
113
|
+
if (field) result.orphanFields.push(field);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function extractField(el) {
|
|
117
|
+
const tag = el.tagName.toLowerCase();
|
|
118
|
+
const type = (el.getAttribute('type') || (tag === 'textarea' ? 'textarea' : tag === 'select' ? 'select' : 'text')).toLowerCase();
|
|
119
|
+
if (type === 'hidden' || type === 'submit' || type === 'button' || type === 'reset') return null;
|
|
120
|
+
const name = el.name || el.id || null;
|
|
121
|
+
const ref = el.getAttribute('data-opencli-ref') || null;
|
|
122
|
+
const label = findLabel(el);
|
|
123
|
+
let value;
|
|
124
|
+
if (tag === 'select') {
|
|
125
|
+
const opt = el.options?.[el.selectedIndex];
|
|
126
|
+
value = opt ? opt.textContent.trim() : '';
|
|
127
|
+
} else if (type === 'checkbox' || type === 'radio') {
|
|
128
|
+
value = el.checked;
|
|
129
|
+
} else if (type === 'password') {
|
|
130
|
+
value = el.value ? '••••' : '';
|
|
131
|
+
} else if (el.isContentEditable) {
|
|
132
|
+
value = (el.textContent || '').trim().slice(0, 200);
|
|
133
|
+
} else {
|
|
134
|
+
value = (el.value || '').slice(0, 200);
|
|
135
|
+
}
|
|
136
|
+
return { tag, type, name, ref, label, value, required: el.required || false, disabled: el.disabled || false };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function findLabel(el) {
|
|
140
|
+
// 1. aria-label
|
|
141
|
+
if (el.getAttribute('aria-label')) return el.getAttribute('aria-label');
|
|
142
|
+
// 2. associated <label>
|
|
143
|
+
if (el.id) {
|
|
144
|
+
const label = document.querySelector('label[for="' + el.id + '"]');
|
|
145
|
+
if (label) return label.textContent.trim().slice(0, 80);
|
|
146
|
+
}
|
|
147
|
+
// 3. parent label
|
|
148
|
+
const parentLabel = el.closest('label');
|
|
149
|
+
if (parentLabel) return parentLabel.textContent.trim().slice(0, 80);
|
|
150
|
+
// 4. placeholder
|
|
151
|
+
return el.placeholder || null;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
})()
|
|
156
|
+
`.trim();
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ─── Main Snapshot JS Generator ──────────────────────────────────────
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Generate JavaScript code that, when evaluated in a page context via CDP
|
|
163
|
+
* Runtime.evaluate, returns a pruned DOM snapshot string optimised for LLMs.
|
|
164
|
+
*
|
|
165
|
+
* The snapshot output format:
|
|
166
|
+
* [42]<button type=submit>Search</button>
|
|
167
|
+
* |scroll|<div> (0.5↑ 3.2↓)
|
|
168
|
+
* *[58]<a href=/r/1>Result 1</a>
|
|
169
|
+
* [59]<a href=/r/2>Result 2</a>
|
|
170
|
+
*
|
|
171
|
+
* - `[id]` — interactive element with backend index for targeting
|
|
172
|
+
* - `*` prefix — newly appeared element (incremental diff)
|
|
173
|
+
* - `|scroll|` — scrollable container with page counts
|
|
174
|
+
* - `|shadow|` — Shadow DOM boundary
|
|
175
|
+
* - `|iframe|` — iframe content
|
|
176
|
+
* - `|table|` — markdown table rendering
|
|
177
|
+
*/
|
|
178
|
+
export function generateSnapshotJs(opts: SnapshotOptions = {}): string {
|
|
179
|
+
const viewportExpand = opts.viewportExpand ?? 800;
|
|
180
|
+
const maxDepth = Math.max(1, Math.min(opts.maxDepth ?? 50, 200));
|
|
181
|
+
const interactiveOnly = opts.interactiveOnly ?? false;
|
|
182
|
+
const maxTextLength = opts.maxTextLength ?? 120;
|
|
183
|
+
const includeScrollInfo = opts.includeScrollInfo ?? true;
|
|
184
|
+
const bboxDedup = opts.bboxDedup ?? true;
|
|
185
|
+
const includeShadowDom = opts.includeShadowDom ?? true;
|
|
186
|
+
const includeIframes = opts.includeIframes ?? true;
|
|
187
|
+
const maxIframes = opts.maxIframes ?? 5;
|
|
188
|
+
const paintOrderCheck = opts.paintOrderCheck ?? true;
|
|
189
|
+
const annotateRefs = opts.annotateRefs ?? true;
|
|
190
|
+
const reportHidden = opts.reportHidden ?? true;
|
|
191
|
+
const filterAds = opts.filterAds ?? true;
|
|
192
|
+
const markdownTables = opts.markdownTables ?? true;
|
|
193
|
+
const previousHashes = opts.previousHashes ?? null;
|
|
194
|
+
|
|
195
|
+
return `
|
|
196
|
+
(() => {
|
|
197
|
+
'use strict';
|
|
198
|
+
|
|
199
|
+
// ── Config ─────────────────────────────────────────────────────────
|
|
200
|
+
const VIEWPORT_EXPAND = ${viewportExpand};
|
|
201
|
+
const MAX_DEPTH = ${maxDepth};
|
|
202
|
+
const INTERACTIVE_ONLY = ${interactiveOnly};
|
|
203
|
+
const MAX_TEXT_LEN = ${maxTextLength};
|
|
204
|
+
const INCLUDE_SCROLL_INFO = ${includeScrollInfo};
|
|
205
|
+
const BBOX_DEDUP = ${bboxDedup};
|
|
206
|
+
const INCLUDE_SHADOW_DOM = ${includeShadowDom};
|
|
207
|
+
const INCLUDE_IFRAMES = ${includeIframes};
|
|
208
|
+
const MAX_IFRAMES = ${maxIframes};
|
|
209
|
+
const PAINT_ORDER_CHECK = ${paintOrderCheck};
|
|
210
|
+
const ANNOTATE_REFS = ${annotateRefs};
|
|
211
|
+
const REPORT_HIDDEN = ${reportHidden};
|
|
212
|
+
const FILTER_ADS = ${filterAds};
|
|
213
|
+
const MARKDOWN_TABLES = ${markdownTables};
|
|
214
|
+
const PREV_HASHES = ${previousHashes ? `new Set(${previousHashes})` : 'null'};
|
|
215
|
+
|
|
216
|
+
// ── Constants ──────────────────────────────────────────────────────
|
|
217
|
+
|
|
218
|
+
const SKIP_TAGS = new Set([
|
|
219
|
+
'script', 'style', 'noscript', 'link', 'meta', 'head',
|
|
220
|
+
'template', 'br', 'wbr', 'col', 'colgroup',
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
const SVG_CHILDREN = new Set([
|
|
224
|
+
'path', 'rect', 'g', 'circle', 'ellipse', 'line', 'polyline',
|
|
225
|
+
'polygon', 'use', 'defs', 'clippath', 'mask', 'pattern',
|
|
226
|
+
'text', 'tspan', 'lineargradient', 'radialgradient', 'stop',
|
|
227
|
+
'filter', 'fegaussianblur', 'fecolormatrix', 'feblend',
|
|
228
|
+
'symbol', 'marker', 'foreignobject', 'desc', 'title',
|
|
229
|
+
]);
|
|
230
|
+
|
|
231
|
+
const INTERACTIVE_TAGS = new Set([
|
|
232
|
+
'a', 'button', 'input', 'select', 'textarea', 'details',
|
|
233
|
+
'summary', 'option', 'optgroup',
|
|
234
|
+
]);
|
|
235
|
+
|
|
236
|
+
const INTERACTIVE_ROLES = new Set([
|
|
237
|
+
'button', 'link', 'menuitem', 'option', 'radio', 'checkbox',
|
|
238
|
+
'tab', 'textbox', 'combobox', 'slider', 'spinbutton',
|
|
239
|
+
'searchbox', 'switch', 'menuitemcheckbox', 'menuitemradio',
|
|
240
|
+
'treeitem', 'gridcell', 'row',
|
|
241
|
+
]);
|
|
242
|
+
|
|
243
|
+
const LANDMARK_ROLES = new Set([
|
|
244
|
+
'main', 'navigation', 'banner', 'search', 'region',
|
|
245
|
+
'complementary', 'contentinfo', 'form', 'dialog',
|
|
246
|
+
]);
|
|
247
|
+
|
|
248
|
+
const LANDMARK_TAGS = new Set([
|
|
249
|
+
'nav', 'main', 'header', 'footer', 'aside', 'form',
|
|
250
|
+
'search', 'dialog', 'section', 'article',
|
|
251
|
+
]);
|
|
252
|
+
|
|
253
|
+
const ATTR_WHITELIST = new Set([
|
|
254
|
+
'id', 'name', 'type', 'value', 'placeholder', 'title', 'alt',
|
|
255
|
+
'role', 'aria-label', 'aria-expanded', 'aria-checked', 'aria-selected',
|
|
256
|
+
'aria-disabled', 'aria-valuemin', 'aria-valuemax', 'aria-valuenow',
|
|
257
|
+
'aria-haspopup', 'aria-live', 'aria-required',
|
|
258
|
+
'href', 'src', 'action', 'method', 'for', 'checked', 'selected',
|
|
259
|
+
'disabled', 'required', 'multiple', 'accept', 'min', 'max',
|
|
260
|
+
'pattern', 'maxlength', 'minlength', 'data-testid', 'data-test',
|
|
261
|
+
'contenteditable', 'tabindex', 'autocomplete',
|
|
262
|
+
]);
|
|
263
|
+
|
|
264
|
+
const PROPAGATING_TAGS = new Set(['a', 'button']);
|
|
265
|
+
|
|
266
|
+
const AD_PATTERNS = [
|
|
267
|
+
'googleadservices.com', 'doubleclick.net', 'googlesyndication.com',
|
|
268
|
+
'facebook.com/tr', 'analytics.google.com', 'connect.facebook.net',
|
|
269
|
+
'ad.doubleclick', 'pagead', 'adsense',
|
|
270
|
+
];
|
|
271
|
+
|
|
272
|
+
const AD_SELECTOR_RE = /\\b(ad[_-]?(?:banner|container|wrapper|slot|unit|block|frame|leaderboard|sidebar)|google[_-]?ad|sponsored|adsbygoogle|banner[_-]?ad)\\b/i;
|
|
273
|
+
|
|
274
|
+
// ── Viewport & Layout Helpers ──────────────────────────────────────
|
|
275
|
+
|
|
276
|
+
const vw = window.innerWidth;
|
|
277
|
+
const vh = window.innerHeight;
|
|
278
|
+
|
|
279
|
+
function isInExpandedViewport(rect) {
|
|
280
|
+
if (!rect || (rect.width === 0 && rect.height === 0)) return false;
|
|
281
|
+
return rect.bottom > -VIEWPORT_EXPAND && rect.top < vh + VIEWPORT_EXPAND &&
|
|
282
|
+
rect.right > -VIEWPORT_EXPAND && rect.left < vw + VIEWPORT_EXPAND;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function isVisibleByCSS(el) {
|
|
286
|
+
const style = el.style;
|
|
287
|
+
if (style.display === 'none') return false;
|
|
288
|
+
if (style.visibility === 'hidden' || style.visibility === 'collapse') return false;
|
|
289
|
+
if (style.opacity === '0') return false;
|
|
290
|
+
try {
|
|
291
|
+
const cs = window.getComputedStyle(el);
|
|
292
|
+
if (cs.display === 'none') return false;
|
|
293
|
+
if (cs.visibility === 'hidden') return false;
|
|
294
|
+
if (parseFloat(cs.opacity) <= 0) return false;
|
|
295
|
+
if (cs.clip === 'rect(0px, 0px, 0px, 0px)' && cs.position === 'absolute') return false;
|
|
296
|
+
if (cs.overflow === 'hidden' && el.offsetWidth === 0 && el.offsetHeight === 0) return false;
|
|
297
|
+
} catch {}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ── Paint Order Occlusion ──────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
function isOccludedByOverlay(el) {
|
|
304
|
+
if (!PAINT_ORDER_CHECK) return false;
|
|
305
|
+
try {
|
|
306
|
+
const rect = el.getBoundingClientRect();
|
|
307
|
+
if (rect.width === 0 || rect.height === 0) return false;
|
|
308
|
+
const cx = rect.left + rect.width / 2;
|
|
309
|
+
const cy = rect.top + rect.height / 2;
|
|
310
|
+
if (cx < 0 || cy < 0 || cx > vw || cy > vh) return false;
|
|
311
|
+
const topEl = document.elementFromPoint(cx, cy);
|
|
312
|
+
if (!topEl || topEl === el || el.contains(topEl) || topEl.contains(el)) return false;
|
|
313
|
+
const cs = window.getComputedStyle(topEl);
|
|
314
|
+
if (parseFloat(cs.opacity) < 0.5) return false;
|
|
315
|
+
const bg = cs.backgroundColor;
|
|
316
|
+
if (bg === 'rgba(0, 0, 0, 0)' || bg === 'transparent') return false;
|
|
317
|
+
return true;
|
|
318
|
+
} catch { return false; }
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
// ── Ad/Noise Detection ─────────────────────────────────────────────
|
|
322
|
+
|
|
323
|
+
function isAdElement(el) {
|
|
324
|
+
if (!FILTER_ADS) return false;
|
|
325
|
+
try {
|
|
326
|
+
const id = el.id || '';
|
|
327
|
+
const cls = el.className || '';
|
|
328
|
+
const testStr = id + ' ' + (typeof cls === 'string' ? cls : '');
|
|
329
|
+
if (AD_SELECTOR_RE.test(testStr)) return true;
|
|
330
|
+
if (el.tagName === 'IFRAME') {
|
|
331
|
+
const src = el.src || '';
|
|
332
|
+
for (const p of AD_PATTERNS) { if (src.includes(p)) return true; }
|
|
333
|
+
}
|
|
334
|
+
if (el.hasAttribute('data-ad') || el.hasAttribute('data-ad-slot') ||
|
|
335
|
+
el.hasAttribute('data-adunit') || el.hasAttribute('data-google-query-id')) return true;
|
|
336
|
+
} catch {}
|
|
337
|
+
return false;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ── Interactivity Detection ────────────────────────────────────────
|
|
341
|
+
|
|
342
|
+
function isInteractive(el) {
|
|
343
|
+
const tag = el.tagName.toLowerCase();
|
|
344
|
+
if (INTERACTIVE_TAGS.has(tag)) {
|
|
345
|
+
if (tag === 'label' && el.hasAttribute('for')) return false;
|
|
346
|
+
if (el.disabled && (tag === 'button' || tag === 'input')) return false;
|
|
347
|
+
return true;
|
|
348
|
+
}
|
|
349
|
+
const role = el.getAttribute('role');
|
|
350
|
+
if (role && INTERACTIVE_ROLES.has(role)) return true;
|
|
351
|
+
if (el.hasAttribute('onclick') || el.hasAttribute('onmousedown') || el.hasAttribute('ontouchstart')) return true;
|
|
352
|
+
if (el.hasAttribute('tabindex') && el.getAttribute('tabindex') !== '-1') return true;
|
|
353
|
+
try { if (window.getComputedStyle(el).cursor === 'pointer') return true; } catch {}
|
|
354
|
+
if (el.isContentEditable && el.getAttribute('contenteditable') !== 'false') return true;
|
|
355
|
+
return false;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
function isLandmark(el) {
|
|
359
|
+
const role = el.getAttribute('role');
|
|
360
|
+
if (role && LANDMARK_ROLES.has(role)) return true;
|
|
361
|
+
return LANDMARK_TAGS.has(el.tagName.toLowerCase());
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// ── Scrollability Detection ────────────────────────────────────────
|
|
365
|
+
|
|
366
|
+
function getScrollInfo(el) {
|
|
367
|
+
if (!INCLUDE_SCROLL_INFO) return null;
|
|
368
|
+
const sh = el.scrollHeight, ch = el.clientHeight;
|
|
369
|
+
const sw = el.scrollWidth, cw = el.clientWidth;
|
|
370
|
+
const isV = sh > ch + 5, isH = sw > cw + 5;
|
|
371
|
+
if (!isV && !isH) return null;
|
|
372
|
+
try {
|
|
373
|
+
const cs = window.getComputedStyle(el);
|
|
374
|
+
const scrollable = ['auto', 'scroll', 'overlay'];
|
|
375
|
+
const tag = el.tagName.toLowerCase();
|
|
376
|
+
const isBody = tag === 'body' || tag === 'html';
|
|
377
|
+
if (isV && !isBody && !scrollable.includes(cs.overflowY)) return null;
|
|
378
|
+
const info = {};
|
|
379
|
+
if (isV) {
|
|
380
|
+
const above = ch > 0 ? +(el.scrollTop / ch).toFixed(1) : 0;
|
|
381
|
+
const below = ch > 0 ? +((sh - ch - el.scrollTop) / ch).toFixed(1) : 0;
|
|
382
|
+
if (above > 0 || below > 0) info.v = { above, below };
|
|
383
|
+
}
|
|
384
|
+
if (isH && scrollable.includes(cs.overflowX)) {
|
|
385
|
+
info.h = { pct: cw > 0 ? Math.round(el.scrollLeft / (sw - cw) * 100) : 0 };
|
|
386
|
+
}
|
|
387
|
+
return Object.keys(info).length > 0 ? info : null;
|
|
388
|
+
} catch { return null; }
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// ── BBox Containment Check ─────────────────────────────────────────
|
|
392
|
+
|
|
393
|
+
function isContainedBy(childRect, parentRect, threshold) {
|
|
394
|
+
if (!childRect || !parentRect) return false;
|
|
395
|
+
const cArea = childRect.width * childRect.height;
|
|
396
|
+
if (cArea === 0) return false;
|
|
397
|
+
const xO = Math.max(0, Math.min(childRect.right, parentRect.right) - Math.max(childRect.left, parentRect.left));
|
|
398
|
+
const yO = Math.max(0, Math.min(childRect.bottom, parentRect.bottom) - Math.max(childRect.top, parentRect.top));
|
|
399
|
+
return (xO * yO) / cArea >= threshold;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ── Text Helpers ───────────────────────────────────────────────────
|
|
403
|
+
|
|
404
|
+
function getDirectText(el) {
|
|
405
|
+
let text = '';
|
|
406
|
+
for (const child of el.childNodes) {
|
|
407
|
+
if (child.nodeType === 3) {
|
|
408
|
+
const t = child.textContent.trim();
|
|
409
|
+
if (t) text += (text ? ' ' : '') + t;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return text;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function capText(s) {
|
|
416
|
+
if (!s) return '';
|
|
417
|
+
const t = s.replace(/\\s+/g, ' ').trim();
|
|
418
|
+
return t.length > MAX_TEXT_LEN ? t.slice(0, MAX_TEXT_LEN) + '…' : t;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// ── Element Hashing (for incremental diff) ─────────────────────────
|
|
422
|
+
|
|
423
|
+
function hashElement(el) {
|
|
424
|
+
// Simple hash: tag + id + className + textContent prefix
|
|
425
|
+
const tag = el.tagName || '';
|
|
426
|
+
const id = el.id || '';
|
|
427
|
+
const cls = (typeof el.className === 'string' ? el.className : '').slice(0, 50);
|
|
428
|
+
const text = (el.textContent || '').trim().slice(0, 40);
|
|
429
|
+
const s = tag + '|' + id + '|' + cls + '|' + text;
|
|
430
|
+
let h = 0;
|
|
431
|
+
for (let i = 0; i < s.length; i++) {
|
|
432
|
+
h = ((h << 5) - h + s.charCodeAt(i)) | 0;
|
|
433
|
+
}
|
|
434
|
+
return '' + (h >>> 0); // unsigned
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
// ── Attribute Serialization ────────────────────────────────────────
|
|
438
|
+
|
|
439
|
+
function serializeAttrs(el) {
|
|
440
|
+
const parts = [];
|
|
441
|
+
for (const attr of el.attributes) {
|
|
442
|
+
if (!ATTR_WHITELIST.has(attr.name)) continue;
|
|
443
|
+
let val = attr.value.trim();
|
|
444
|
+
if (!val) continue;
|
|
445
|
+
if (val.length > 120) val = val.slice(0, 100) + '…';
|
|
446
|
+
if (attr.name === 'type' && val.toLowerCase() === el.tagName.toLowerCase()) continue;
|
|
447
|
+
if (attr.name === 'value' && el.getAttribute('type') === 'password') { parts.push('value=••••'); continue; }
|
|
448
|
+
if (attr.name === 'href') {
|
|
449
|
+
if (val.startsWith('javascript:')) continue;
|
|
450
|
+
try {
|
|
451
|
+
const u = new URL(val, location.origin);
|
|
452
|
+
if (u.origin === location.origin) val = u.pathname + u.search + u.hash;
|
|
453
|
+
} catch {}
|
|
454
|
+
}
|
|
455
|
+
parts.push(attr.name + '=' + val);
|
|
456
|
+
}
|
|
457
|
+
// Synthetic attributes
|
|
458
|
+
const tag = el.tagName;
|
|
459
|
+
if (tag === 'INPUT') {
|
|
460
|
+
const type = (el.getAttribute('type') || 'text').toLowerCase();
|
|
461
|
+
const fmts = { 'date':'YYYY-MM-DD', 'time':'HH:MM', 'datetime-local':'YYYY-MM-DDTHH:MM', 'month':'YYYY-MM', 'week':'YYYY-W##' };
|
|
462
|
+
if (fmts[type]) parts.push('format=' + fmts[type]);
|
|
463
|
+
if (['text','email','tel','url','search','number','date','time','datetime-local','month','week'].includes(type)) {
|
|
464
|
+
if (el.value && !parts.some(p => p.startsWith('value='))) parts.push('value=' + capText(el.value));
|
|
465
|
+
}
|
|
466
|
+
if (type === 'password' && el.value && !parts.some(p => p.startsWith('value='))) parts.push('value=••••');
|
|
467
|
+
if ((type === 'checkbox' || type === 'radio') && el.checked && !parts.some(p => p.startsWith('checked'))) parts.push('checked');
|
|
468
|
+
if (type === 'file' && el.files && el.files.length > 0) parts.push('files=' + Array.from(el.files).map(f => f.name).join(','));
|
|
469
|
+
}
|
|
470
|
+
if (tag === 'TEXTAREA' && el.value && !parts.some(p => p.startsWith('value='))) parts.push('value=' + capText(el.value));
|
|
471
|
+
if (tag === 'SELECT') {
|
|
472
|
+
const sel = el.options?.[el.selectedIndex];
|
|
473
|
+
if (sel && !parts.some(p => p.startsWith('value='))) parts.push('value=' + capText(sel.textContent));
|
|
474
|
+
const optEls = Array.from(el.options || []).slice(0, 6);
|
|
475
|
+
if (optEls.length > 0) {
|
|
476
|
+
const ot = optEls.map(o => capText(o.textContent).slice(0, 30));
|
|
477
|
+
if (el.options.length > 6) ot.push('…' + (el.options.length - 6) + ' more');
|
|
478
|
+
parts.push('options=[' + ot.join('|') + ']');
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return parts.join(' ');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
// ── Table → Markdown Serialization ─────────────────────────────────
|
|
485
|
+
|
|
486
|
+
function serializeTable(table, depth) {
|
|
487
|
+
if (!MARKDOWN_TABLES) return false;
|
|
488
|
+
try {
|
|
489
|
+
const rows = table.querySelectorAll('tr');
|
|
490
|
+
if (rows.length === 0 || rows.length > 50) return false; // skip huge tables
|
|
491
|
+
const grid = [];
|
|
492
|
+
let maxCols = 0;
|
|
493
|
+
for (const row of rows) {
|
|
494
|
+
const cells = [];
|
|
495
|
+
for (const cell of row.querySelectorAll('th, td')) {
|
|
496
|
+
let text = capText(cell.textContent || '');
|
|
497
|
+
// Include interactive elements in cells
|
|
498
|
+
const links = cell.querySelectorAll('a[href]');
|
|
499
|
+
if (links.length === 1 && text) {
|
|
500
|
+
const href = links[0].getAttribute('href');
|
|
501
|
+
if (href && !href.startsWith('javascript:')) {
|
|
502
|
+
try {
|
|
503
|
+
const u = new URL(href, location.origin);
|
|
504
|
+
text = '[' + text + '](' + (u.origin === location.origin ? u.pathname + u.search : href) + ')';
|
|
505
|
+
} catch { text = '[' + text + '](' + href + ')'; }
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
cells.push(text || '');
|
|
509
|
+
}
|
|
510
|
+
if (cells.length > 0) {
|
|
511
|
+
grid.push(cells);
|
|
512
|
+
if (cells.length > maxCols) maxCols = cells.length;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
if (grid.length < 2 || maxCols === 0) return false; // need at least header + 1 row
|
|
516
|
+
// Pad rows to maxCols
|
|
517
|
+
for (const row of grid) { while (row.length < maxCols) row.push(''); }
|
|
518
|
+
// Compute column widths
|
|
519
|
+
const widths = [];
|
|
520
|
+
for (let c = 0; c < maxCols; c++) {
|
|
521
|
+
let w = 3;
|
|
522
|
+
for (const row of grid) { if (row[c].length > w) w = Math.min(row[c].length, 40); }
|
|
523
|
+
widths.push(w);
|
|
524
|
+
}
|
|
525
|
+
const indent = ' '.repeat(depth);
|
|
526
|
+
const tableLines = [];
|
|
527
|
+
// Header
|
|
528
|
+
tableLines.push(indent + '| ' + grid[0].map((c, i) => c.padEnd(widths[i])).join(' | ') + ' |');
|
|
529
|
+
tableLines.push(indent + '| ' + widths.map(w => '-'.repeat(w)).join(' | ') + ' |');
|
|
530
|
+
// Body
|
|
531
|
+
for (let r = 1; r < grid.length; r++) {
|
|
532
|
+
tableLines.push(indent + '| ' + grid[r].map((c, i) => c.padEnd(widths[i])).join(' | ') + ' |');
|
|
533
|
+
}
|
|
534
|
+
return tableLines;
|
|
535
|
+
} catch { return false; }
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// ── Main Tree Walk ─────────────────────────────────────────────────
|
|
539
|
+
|
|
540
|
+
let interactiveIndex = 0;
|
|
541
|
+
const lines = [];
|
|
542
|
+
const hiddenInteractives = [];
|
|
543
|
+
const currentHashes = [];
|
|
544
|
+
let iframeCount = 0;
|
|
545
|
+
|
|
546
|
+
function walk(el, depth, parentPropagatingRect) {
|
|
547
|
+
if (depth > MAX_DEPTH) return false;
|
|
548
|
+
if (el.nodeType !== 1) return false;
|
|
549
|
+
|
|
550
|
+
const tag = el.tagName.toLowerCase();
|
|
551
|
+
if (SKIP_TAGS.has(tag)) return false;
|
|
552
|
+
if (isAdElement(el)) return false;
|
|
553
|
+
|
|
554
|
+
// SVG: emit tag, collapse children
|
|
555
|
+
if (tag === 'svg') {
|
|
556
|
+
const attrs = serializeAttrs(el);
|
|
557
|
+
const interactive = isInteractive(el);
|
|
558
|
+
let prefix = '';
|
|
559
|
+
if (interactive) {
|
|
560
|
+
interactiveIndex++;
|
|
561
|
+
if (ANNOTATE_REFS) el.setAttribute('data-opencli-ref', '' + interactiveIndex);
|
|
562
|
+
prefix = '[' + interactiveIndex + ']';
|
|
563
|
+
}
|
|
564
|
+
lines.push(' '.repeat(depth) + prefix + '<svg' + (attrs ? ' ' + attrs : '') + ' />');
|
|
565
|
+
return interactive;
|
|
566
|
+
}
|
|
567
|
+
if (SVG_CHILDREN.has(tag)) return false;
|
|
568
|
+
|
|
569
|
+
// Table: try markdown serialization before generic walk
|
|
570
|
+
if (tag === 'table' && MARKDOWN_TABLES) {
|
|
571
|
+
const tableLines = serializeTable(el, depth);
|
|
572
|
+
if (tableLines) {
|
|
573
|
+
const indent = ' '.repeat(depth);
|
|
574
|
+
lines.push(indent + '|table|');
|
|
575
|
+
for (const tl of tableLines) lines.push(tl);
|
|
576
|
+
return false; // tables usually non-interactive
|
|
577
|
+
}
|
|
578
|
+
// Fall through to generic walk if markdown failed
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// iframe handling
|
|
582
|
+
if (tag === 'iframe' && INCLUDE_IFRAMES && iframeCount < MAX_IFRAMES) {
|
|
583
|
+
return walkIframe(el, depth);
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Visibility check
|
|
587
|
+
let rect;
|
|
588
|
+
try { rect = el.getBoundingClientRect(); } catch { return false; }
|
|
589
|
+
const hasArea = rect.width > 0 && rect.height > 0;
|
|
590
|
+
if (hasArea && !isVisibleByCSS(el)) {
|
|
591
|
+
if (!(tag === 'input' && el.type === 'file')) return false;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const interactive = isInteractive(el);
|
|
595
|
+
|
|
596
|
+
// Viewport threshold pruning
|
|
597
|
+
if (hasArea && !isInExpandedViewport(rect)) {
|
|
598
|
+
if (interactive && REPORT_HIDDEN) {
|
|
599
|
+
const scrollDist = rect.top > vh ? rect.top - vh : -rect.bottom;
|
|
600
|
+
const pagesAway = Math.abs(scrollDist / vh).toFixed(1);
|
|
601
|
+
const direction = rect.top > vh ? 'below' : 'above';
|
|
602
|
+
const text = capText(getDirectText(el) || el.getAttribute('aria-label') || el.getAttribute('title') || '');
|
|
603
|
+
hiddenInteractives.push({ tag, text, direction, pagesAway });
|
|
604
|
+
}
|
|
605
|
+
return false;
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
// Paint order occlusion
|
|
609
|
+
if (interactive && hasArea && isOccludedByOverlay(el)) return false;
|
|
610
|
+
|
|
611
|
+
const landmark = isLandmark(el);
|
|
612
|
+
const scrollInfo = getScrollInfo(el);
|
|
613
|
+
const isScrollable = scrollInfo !== null;
|
|
614
|
+
|
|
615
|
+
// BBox dedup
|
|
616
|
+
let excludedByParent = false;
|
|
617
|
+
if (BBOX_DEDUP && parentPropagatingRect && !interactive) {
|
|
618
|
+
if (hasArea && isContainedBy(rect, parentPropagatingRect, 0.95)) {
|
|
619
|
+
const hasSemantic = el.hasAttribute('aria-label') ||
|
|
620
|
+
(el.getAttribute('role') && INTERACTIVE_ROLES.has(el.getAttribute('role')));
|
|
621
|
+
if (!hasSemantic && !['input','select','textarea','label'].includes(tag)) {
|
|
622
|
+
excludedByParent = true;
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
let propagateRect = parentPropagatingRect;
|
|
628
|
+
if (BBOX_DEDUP && PROPAGATING_TAGS.has(tag) && hasArea) propagateRect = rect;
|
|
629
|
+
|
|
630
|
+
// Process children
|
|
631
|
+
const origLen = lines.length;
|
|
632
|
+
let hasInteractiveDescendant = false;
|
|
633
|
+
|
|
634
|
+
for (const child of el.children) {
|
|
635
|
+
const r = walk(child, depth + 1, propagateRect);
|
|
636
|
+
if (r) hasInteractiveDescendant = true;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
// Shadow DOM
|
|
640
|
+
if (INCLUDE_SHADOW_DOM && el.shadowRoot) {
|
|
641
|
+
const shadowOrigLen = lines.length;
|
|
642
|
+
for (const child of el.shadowRoot.children) {
|
|
643
|
+
const r = walk(child, depth + 1, propagateRect);
|
|
644
|
+
if (r) hasInteractiveDescendant = true;
|
|
645
|
+
}
|
|
646
|
+
if (lines.length > shadowOrigLen) {
|
|
647
|
+
lines.splice(shadowOrigLen, 0, ' '.repeat(depth + 1) + '|shadow|');
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const childLinesCount = lines.length - origLen;
|
|
652
|
+
const text = capText(getDirectText(el));
|
|
653
|
+
|
|
654
|
+
// Decide whether to emit
|
|
655
|
+
if (INTERACTIVE_ONLY && !interactive && !landmark && !hasInteractiveDescendant && !text) {
|
|
656
|
+
lines.length = origLen;
|
|
657
|
+
return false;
|
|
658
|
+
}
|
|
659
|
+
if (excludedByParent && !interactive && !isScrollable) return hasInteractiveDescendant;
|
|
660
|
+
if (!interactive && !isScrollable && !text && childLinesCount === 0 && !landmark) return false;
|
|
661
|
+
|
|
662
|
+
// ── Emit node ────────────────────────────────────────────────────
|
|
663
|
+
const indent = ' '.repeat(depth);
|
|
664
|
+
let line = indent;
|
|
665
|
+
|
|
666
|
+
// Incremental diff: mark new elements with *
|
|
667
|
+
if (PREV_HASHES) {
|
|
668
|
+
const h = hashElement(el);
|
|
669
|
+
currentHashes.push(h);
|
|
670
|
+
if (!PREV_HASHES.has(h)) line += '*';
|
|
671
|
+
} else {
|
|
672
|
+
currentHashes.push(hashElement(el));
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
// Scroll marker
|
|
676
|
+
if (isScrollable && !interactive) line += '|scroll|';
|
|
677
|
+
|
|
678
|
+
// Interactive index + data-ref
|
|
679
|
+
if (interactive) {
|
|
680
|
+
interactiveIndex++;
|
|
681
|
+
if (ANNOTATE_REFS) el.setAttribute('data-opencli-ref', '' + interactiveIndex);
|
|
682
|
+
line += isScrollable ? '|scroll[' + interactiveIndex + ']|' : '[' + interactiveIndex + ']';
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// Tag + attributes
|
|
686
|
+
const attrs = serializeAttrs(el);
|
|
687
|
+
line += '<' + tag;
|
|
688
|
+
if (attrs) line += ' ' + attrs;
|
|
689
|
+
|
|
690
|
+
// Scroll info suffix, inline text, or self-close
|
|
691
|
+
if (isScrollable && scrollInfo) {
|
|
692
|
+
const parts = [];
|
|
693
|
+
if (scrollInfo.v) parts.push(scrollInfo.v.above + '↑ ' + scrollInfo.v.below + '↓');
|
|
694
|
+
if (scrollInfo.h) parts.push('h:' + scrollInfo.h.pct + '%');
|
|
695
|
+
line += ' /> (' + parts.join(', ') + ')';
|
|
696
|
+
} else if (text && childLinesCount === 0) {
|
|
697
|
+
line += '>' + text + '</' + tag + '>';
|
|
698
|
+
} else {
|
|
699
|
+
line += ' />';
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
lines.splice(origLen, 0, line);
|
|
703
|
+
if (text && childLinesCount > 0) lines.splice(origLen + 1, 0, indent + ' ' + text);
|
|
704
|
+
|
|
705
|
+
return interactive || hasInteractiveDescendant;
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
// ── iframe Processing ──────────────────────────────────────────────
|
|
709
|
+
|
|
710
|
+
function walkIframe(el, depth) {
|
|
711
|
+
const indent = ' '.repeat(depth);
|
|
712
|
+
try {
|
|
713
|
+
const doc = el.contentDocument;
|
|
714
|
+
if (!doc || !doc.body) {
|
|
715
|
+
const attrs = serializeAttrs(el);
|
|
716
|
+
lines.push(indent + '|iframe|<iframe' + (attrs ? ' ' + attrs : '') + ' /> (cross-origin)');
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
iframeCount++;
|
|
720
|
+
const attrs = serializeAttrs(el);
|
|
721
|
+
lines.push(indent + '|iframe|<iframe' + (attrs ? ' ' + attrs : '') + ' />');
|
|
722
|
+
let has = false;
|
|
723
|
+
for (const child of doc.body.children) {
|
|
724
|
+
if (walk(child, depth + 1, null)) has = true;
|
|
725
|
+
}
|
|
726
|
+
return has;
|
|
727
|
+
} catch {
|
|
728
|
+
const attrs = serializeAttrs(el);
|
|
729
|
+
lines.push(indent + '|iframe|<iframe' + (attrs ? ' ' + attrs : '') + ' /> (blocked)');
|
|
730
|
+
return false;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
// ── Entry Point ────────────────────────────────────────────────────
|
|
735
|
+
|
|
736
|
+
lines.push('url: ' + location.href);
|
|
737
|
+
lines.push('title: ' + document.title);
|
|
738
|
+
lines.push('viewport: ' + vw + 'x' + vh);
|
|
739
|
+
const pageScrollInfo = getScrollInfo(document.documentElement) || getScrollInfo(document.body);
|
|
740
|
+
if (pageScrollInfo && pageScrollInfo.v) {
|
|
741
|
+
lines.push('page_scroll: ' + pageScrollInfo.v.above + '↑ ' + pageScrollInfo.v.below + '↓');
|
|
742
|
+
}
|
|
743
|
+
lines.push('---');
|
|
744
|
+
|
|
745
|
+
const root = document.body || document.documentElement;
|
|
746
|
+
if (root) walk(root, 0, null);
|
|
747
|
+
|
|
748
|
+
// Hidden interactive elements hint
|
|
749
|
+
if (REPORT_HIDDEN && hiddenInteractives.length > 0) {
|
|
750
|
+
lines.push('---');
|
|
751
|
+
lines.push('hidden_interactive (' + hiddenInteractives.length + '):');
|
|
752
|
+
const shown = hiddenInteractives.slice(0, 10);
|
|
753
|
+
for (const h of shown) {
|
|
754
|
+
const label = h.text ? ' "' + h.text + '"' : '';
|
|
755
|
+
lines.push(' <' + h.tag + '>' + label + ' ~' + h.pagesAway + ' pages ' + h.direction);
|
|
756
|
+
}
|
|
757
|
+
if (hiddenInteractives.length > 10) lines.push(' …' + (hiddenInteractives.length - 10) + ' more');
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// Footer
|
|
761
|
+
lines.push('---');
|
|
762
|
+
lines.push('interactive: ' + interactiveIndex + ' | iframes: ' + iframeCount);
|
|
763
|
+
|
|
764
|
+
// Store hashes on window for next diff snapshot
|
|
765
|
+
try { window.__opencli_prev_hashes = JSON.stringify(currentHashes); } catch {}
|
|
766
|
+
|
|
767
|
+
return lines.join('\\n');
|
|
768
|
+
})()
|
|
769
|
+
`.trim();
|
|
770
|
+
}
|