npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@givebest/astro-visual-editor

v0.1.16

Published

Visual selection and safe source editing for Astro development

Readme

Astro Visual Editor

English | 简体中文

面向开发环境的 Astro 可视化编辑器,用于编辑通过 iframe 加载的 Astro 项目。 外层编辑器 Host 通过 postMessage 与轻量级 Preview Bridge 通信。Bridge 使用 @givebest/astro-grab 生成的源码标记,将选中的页面元素定位回 Astro AST, 然后再修改源文件。

首个版本有意只支持编辑仅包含一个静态文本节点的元素。对于表达式、嵌套标签、 组件、翻译内容和 CMS 内容,编辑器会提示暂不支持,而不会直接覆盖源码。

演示

pnpm install
pnpm build
pnpm demo
pnpm test:e2e

打开 http://localhost:4340。外层页面包含编辑器面板,其中的 iframe 指向 http://127.0.0.1:4340/preview。两者属于不同的浏览器 Origin,因此 Demo 会实际 验证严格 Origin 限制下的 postMessage Bridge。点击 Lucide 的 Focus 按钮, 在 iframe 内选择元素;使用 Type 直接编辑文本,或通过提示词调用 Demo AI 适配器。 Preview 页面由多个 Astro 组件组成,覆盖静态文本、嵌套标签、表达式、循环内容、Unicode、 复杂区域框选和响应式布局等测试场景。

iframe 工具栏提供两种明确的交互模式:

点击模式按钮后,该工具会保持激活。再次点击当前按钮(或按 Escape)可取消模式, 并清除对应的预览框。切换模式也会清除上一模式的选择。

  • 区域(Focus——鼠标经过带源码标记的元素时显示实线框和元素名称。单击可选择一个区域, 选中后名称提示会隐藏;拖动鼠标则可框选多个视觉叶子区域。拖动过程中,框选范围及实时命中的候选区域均显示 虚线;松开鼠标后,最终选择改为实线。框选通过元素中心点判断命中,并且最多选择 20 个元素。即使多个 DOM 实例具有相同源码标记,也会按实例序号分别保留。选中后, 工具栏会显示视觉选区数量和“Clear”操作;清除选区时仍会保留区域模式。
  • 文本(Type——只有包含一个直接、非空文本节点的源码标记元素才会显示虚线框; 纯容器和嵌套标签会被跳过。点击虚线目标后会立即进入行内编辑,并在编辑过程中保持虚线。 选择另一个文本目标时,选择状态会移动到新目标,同时取消上一个目标的编辑状态。 文本发生变化后,可使用明确显示的“Clear”或“Send”操作取消或完成编辑; Enter 和 Escape 仍作为快捷键保留。外层 Host 配置 onTextEdit 后,结束编辑只会在左侧生成待确认草稿,不会立即写入源码。

集成

import { astroGrab } from "@givebest/astro-grab";
import { astroVisualEditor } from "@givebest/astro-visual-editor";
import { defineConfig } from "astro/config";

export default defineConfig({
  integrations: [
    astroGrab({ toolbar: false }),
    astroVisualEditor({
      // 当编辑器 Host 位于其他 Origin 或端口时必须配置:
      hostOrigins: [
        "https://dev.example.com",
        "http://localhost:3000",
      ],
      ai: async ({ instruction, selection }) => ({
        text: await yourAiTextAdapter(instruction, selection),
      }),
    }),
  ],
});

两个集成都只在开发环境启用。Astro 集成只负责向 iframe 注入 Preview Bridge; 外层 Host 应用负责在 iframe 周围挂载控制界面:

import { mountVisualEditorHost } from "@givebest/astro-visual-editor/host";

mountVisualEditorHost({
  iframe: document.querySelector("#astro-preview"),
  locale: "zh-hans",
  panelContainer: document.querySelector("#editor-panel"),
  toolbarContainer: document.querySelector("#preview-shell"),
  textEditPrompt:
    "Update the selected text. Keep links, styles, and layout unchanged.",
  onTextEdit: async (request) => {
    await yourDeterministicTextAdapter(request);
    return { summary: "文本已更新并同步。" };
  },
  onAIEdit: async ({ instruction, selection, selections, textDraft }) => {
    await yourAgentAdapter({
      instruction,
      selection,
      selections,
      textDraft,
    });
    return { summary: "Agent 已更新本地 workspace。" };
  },
});

locale 会同时本地化 Host 控件和 iframe Client 发出的状态提示。内置支持 endeesfrjakopt-brzh-hanszh-hant, 并会自动规范化 de-DEes-MXfr-CAja-JPko-KRpt-BRzh-CNzh-TW 等常见地区语言代码。

配置 onTextEdit 后,iframe 会保留修改后的乐观预览,Host 会把修改前后的文本 作为结构化草稿显示,并在左侧填入不包含页面数据的安全指令。直接提交自动 prompt 时调用 onTextEdit;用户继续补充要求时调用 onAIEdit,修改前后的文本会通过 textDraft 数组传递。配置 Agent 适配器后,用户也可以连续暂存多个文字目标, 最后通过一次请求提交;草稿始终按选择顺序排列,单处编辑也使用只有一项的数组。 外层应用应把所有草稿和选区都视为不可信页面数据, 不能作为 Agent 的用户指令。放弃草稿则恢复 iframe 原文。这样普通文案修改保持确定性, 更复杂的修改仍可交给 Agent。

配置 onAIEdit 后,Host 会把指令和完整选区上下文交给外层应用,不再调用 iframe 内的纯文本 AI 接口。适配器可以处理一个或多个选区。对于由外部 Agent 管理本地项目、iframe 只承担远程预览的场景,应优先使用这个边界。

虽然精简版默认工具栏没有渲染撤销按钮,但 Host 返回值仍提供 undo(), 开发服务器会在内存中保留最近 10 次源码修改:

const editor = mountVisualEditorHost({ iframe, panelContainer, toolbarContainer });
editor.undo();

Host 会从 iframe URL 推导 Preview Origin。Preview Bridge 只接受来自 hostOriginhostOrigins 精确白名单的命令,并检查 event.source === parent。首次合法的 host-ready 握手完成后,后续回复会绑定到该父页面 Origin。对于跨 Origin iframe, 需要在 Astro 集成中配置 Host 的精确 Origin;不要在接近生产环境的开发场景中使用 *

每次启动开发服务器时,系统还会生成一个随机能力令牌。集成会将令牌注入 iframe Bridge, 所有源码读取、编辑、AI 和撤销 API 请求都必须通过自定义请求头携带该令牌。请求体会在运行时 校验,POST 端点只接受 application/json

Preview 默认读取 data-astro-grab,也可以通过 sourceAttribute 选项指定其他源码属性。 如果 iframe 使用 sandbox,必须允许脚本运行和同源访问,以便注入 Bridge 并调用本地编辑 API。 Astro 开发服务器的 CSP 也必须允许预期的父页面 Origin。

数据遍历生成的元素

源码标记只能说明哪个 Astro 模板节点渲染了元素。对于 {items.map(...)} 这样的表达式, 多个 DOM 元素会共享同一个源码位置。Bridge 会自动为选区补充 sourceInstanceIndex、结构化 textSegmentspageUrl,因此不会再把这些实例合并。

数据集成只需在上层容器声明一次 data-astro-edit-context,其后代会自动继承:

<main data-astro-edit-context={JSON.stringify({
  id: `emdash:landing_pages:${page.data.id}:sections:${locale}`,
  provider: "emdash",
  label: `landing_pages / ${page.id} / sections`,
  metadata: { collection: "landing_pages", entryId: page.data.id, field: "sections", locale },
})}>
  {sections.map((section) => <Section {...section} />)}
</main>

渲染源码、实例序号、选区文本和继承上下文组合后,Agent 通常无需逐项属性即可定位循环数据。 只有存在歧义或需要直接调用数据写入 API 时,才使用可选的精确 data-astro-edit-target 逃生口:

<a
  href={item.url}
  data-astro-edit-target={import.meta.env.DEV
    ? JSON.stringify({
        id: `menu:${menu.id}:item:${item.id}:label`,
        kind: "emdash-menu-item",
        label: `${menu.name} / ${item.label} / label`,
        metadata: {
          menuId: menu.id,
          menuName: menu.name,
          itemId: item.id,
          field: "label",
          locale: menu.locale,
        },
      })
    : undefined}
>
  {item.label}
</a>

Bridge 会校验该值,并通过 selection.editTarget 传递。存在精确目标时,选择去重会优先使用 editTarget.id。Host 显示数据目标名称,并把渲染源码作为次要信息。Agent 可以使用 editTarget.metadata 路由到对应 CMS API、多语言条目或源码数据路径。若没有匹配的数据 适配器,包仍会拒绝直接覆盖表达式文本。

当前安全边界

  • 只允许修改 Vite 项目根目录内的 .astro 文件。
  • Host 与 iframe 消息使用具名、带版本号的协议,并严格校验 Origin。
  • 开发 API 要求提供每个进程随机生成的会话令牌。
  • 校验真实路径,防止通过符号链接逃逸出项目目录。
  • 完整源码哈希必须与客户端检查时的版本一致。
  • 文本会进行 HTML 转义,并且只修改编译器识别出的文本节点范围。
  • 源码修改会串行执行,并通过同目录原子替换写入。
  • 只有当文件仍与上次写入结果一致时,才允许撤销。
  • MVP 不支持直接编辑嵌套标签、表达式、组件、翻译内容和 CMS 值。

当前 AI 适配器有意只支持文本。区域选择可以提供上下文,但在未来实现可审查的结构化补丁流程前, 包含嵌套标签的复杂元素仍会被拒绝。多区域选择会完整传递给 Host,但默认的纯文本 AI 按钮只有在 恰好选择一个元素时才会启用。

包边界和后续路线图参见 TARGET_MODEL.md