FloatyJS 现在是一个基于原生 JavaScript 的主从式浮窗工具。
floaty-main.js用在宿主页面,负责悬浮按钮、iframe 浮窗、页面上下文采集、截图、与 iframe 通信floaty-slave.js用在 iframe 页面,负责向宿主页面请求上下文,并暴露易于调用的全局对象
这套结构更适合和 AI 工具配合使用,因为 iframe 侧可以主动获取:
- 当前页面纯文本
- 当前焦点元素及其祖先链
- 当前鼠标位置对应元素及其祖先链
- 当前页面截图
- 面向 AI 的组合快照
floaty-main.js宿主页面入口,暴露window.FloatyMainfloaty-slave.jsiframe 页面入口,暴露window.FloatySlavefloaty.js兼容入口,会转发到floaty-main.jsindex.html完整宿主页演示台,同时保留README.md的在线渲染iframe.html完整从页演示台,用于演示FloatySlave的各种能力serve.py本地静态服务器脚本,启动后可直接打开 demo
<script
type="module"
src="./floaty-main.js"
data-theme-color="#111827"
data-button-text="AI"
data-tooltip-text="点击打开 AI 浮窗"
data-window-width="1000px"
data-window-height="800px"
data-window-title="FloatyJS Main"
data-target-link="./iframe.html"
data-position="bottom-right"
data-dark="false"
data-close-on-outside-click="true"
data-page-info="这是一个电商商品详情页,当前用户已登录,默认展示标准版套餐。"
defer
></script><script type="module" src="./floaty-slave.js"></script>接入后,iframe 页面里会得到一个全局对象:
window.FloatySlavedata-theme-color悬浮按钮颜色,默认#111827data-button-text按钮文本,默认💬data-tooltip-text按钮提示文本data-window-width浮窗宽度data-window-height浮窗高度data-window-title浮窗标题data-window-theme-color浮窗背景色data-target-linkiframe 目标地址data-position支持bottom-right、bottom-left、top-right、top-leftdata-dark是否启用暗色浮窗data-close-on-outside-click点击浮窗外部时是否关闭,默认truedata-page-info预先提供给从页的页面补充信息或描述,会在getPageText()/page:text中返回data-screenshot-library-url截图库地址,默认使用html2canvas
宿主页面加载后会暴露:
window.FloatyMain
window.floaty可用方法:
window.FloatyMain.open();
window.FloatyMain.close();
window.FloatyMain.toggle();
window.FloatyMain.setPageInfo("这是一个订单确认页,价格单位为人民币。");
window.FloatyMain.getPageInfo();
window.FloatyMain.syncPageText();
window.FloatyMain.sendEvent("custom:event", { hello: "world" });
window.FloatyMain.getFocusContext({ depth: 5 });
window.FloatyMain.getPointerContext({ depth: 5 });
window.FloatyMain.getInteractionContext({ depth: 5 });
window.FloatyMain.captureScreenshot({ includePointer: true, fullPage: false });
window.FloatyMain.getAISnapshot({ depth: 5, includeScreenshot: true });
window.FloatyMain.getState();
window.FloatyMain.destroy();iframe 页面加载后会暴露:
window.FloatySlave可用方法:
await window.FloatySlave.getPageText();
await window.FloatySlave.getFocusContext({ depth: 5 });
await window.FloatySlave.getPointerContext({ depth: 5 });
await window.FloatySlave.getInteractionContext({ depth: 5 });
await window.FloatySlave.captureScreenshot({
includePointer: true,
fullPage: false,
});
await window.FloatySlave.getAISnapshot({
depth: 5,
includeScreenshot: true,
screenshot: { includePointer: true },
});getPageText() 返回示例:
{
"text": "页面纯文本...",
"page": {
"title": "Demo",
"url": "http://localhost:8000/index.html"
},
"pageInfo": "这是一个商品详情页,用户当前正在比较不同套餐。"
}从页也支持监听宿主发来的事件:
const off = window.FloatySlave.on("page:text", (payload) => {
console.log(payload.text);
console.log(payload.pageInfo);
});默认会向上采集 5 层 DOM 祖先链,可通过 depth 修改:
const focus = await window.FloatySlave.getFocusContext({ depth: 8 });
const pointer = await window.FloatySlave.getPointerContext({ depth: 8 });返回结构示例:
{
"page": {
"title": "Demo",
"url": "http://localhost:8000/index.html",
"origin": "http://localhost:8000",
"lang": "zh",
"viewport": {
"width": 1280,
"height": 720,
"scrollX": 0,
"scrollY": 0
}
},
"focus": {
"depth": 5,
"chain": [
{
"level": 0,
"tagName": "span",
"selector": "span.demo",
"id": null,
"className": "demo",
"text": "当前元素文本",
"attributes": {},
"rect": {
"top": 120,
"left": 240,
"width": 80,
"height": 20
},
"html": "<span class=\"demo\">当前元素文本</span>"
}
]
}
}从页可直接请求宿主页面截图:
const screenshot = await window.FloatySlave.captureScreenshot({
includePointer: true,
fullPage: false,
format: "image/png",
scale: 1
});返回结构示例:
{
"format": "image/png",
"fullPage": false,
"includePointer": true,
"cursorIncluded": true,
"note": "Pointer is rendered as a simulated overlay based on the latest tracked mouse position.",
"width": 1280,
"height": 720,
"dataUrl": "data:image/png;base64,...",
"page": {},
"pointer": {
"clientX": 320,
"clientY": 180,
"pageX": 320,
"pageY": 180
}
}说明:
- 浏览器没有稳定的原生 API 直接把系统鼠标指针一起截进 DOM 截图
includePointer: true时,FloatyJS 会根据最近一次鼠标位置,在截图上叠加一个模拟指针
如果你希望给 AI 一次性提供页面文本、焦点元素、鼠标元素,甚至截图,可以直接使用:
const snapshot = await window.FloatySlave.getAISnapshot({
depth: 5,
includeScreenshot: true,
screenshot: {
includePointer: true,
fullPage: false
}
});这通常是最适合 AI 调用的一种方式。若主页面提前设置了 pageInfo,它也会一并出现在返回结果里。
FloatyJS 主从之间通过 postMessage 传输统一 envelope:
{
"namespace": "floatyjs",
"version": "2.0.0",
"source": "floaty-main",
"target": "floaty-slave",
"type": "request",
"action": "page:getFocusContext",
"requestId": "1711111111111-abcd1234",
"success": true,
"payload": {
"depth": 5
},
"error": null,
"sentAt": "2026-03-22T12:34:56.000Z"
}字段说明:
namespace固定为floatyjsversion当前协议版本sourcefloaty-main或floaty-slavetargetfloaty-main或floaty-slavetypeevent、request、responseaction具体动作名,如ready、page:getScreenshotrequestId请求响应关联 IDpayload业务数据success仅响应时有意义error错误信息
运行:
python serve.py然后打开:
http://127.0.0.1:8000/index.html
默认的 demo 结构:
index.html展示宿主页控制台、交互沙箱、截图预览和 README 文档iframe.html展示从页控制台、事件日志、截图预览和 AI Snapshot 演示
- 现在已经不再依赖 Lit
- 现在推荐直接使用
floaty-main.js/floaty-slave.js floaty.js只是兼容入口