Token导航 LogoToken导航

Agent Skill · 研究检索

electrobun-native-ui(Electrobun 原生 ui)

用于辅助界面设计、视觉规范、排版、配色、布局和交互体验优化。它适合让 Agent 根据产品场景整理页面结构、生成 UI 方案、检查视觉一致性或改进组件层级。使用时需要结合现有品牌、设计系统和用户任务,不应只堆装饰元素;涉及真实页面改动时,应通过截图或浏览器预览检查文本溢出、对齐和响应式表现。

electrobun-native-ui 是研究检索类 Agent Skill,可用宿主为 Codex、Claude、Cursor、Gemini CLI,通过GitHub 仓库获取,权限为操作浏览器;许可证信息还不明确,第三方扫描已通过,按安装量在研究检索类的 73,675 个 Skill 里排第 39,506。

总安装 1,402周安装 59GitHub Stars 6下载 491核验 2026-05-01

安装说明

本站只整理中文说明,不托管安装包,也不代用户安装。
GitHub
许可证
unknown

安装方式

通过对话安装

复制提示词发给支持本地命令或 Skills 的 AI 助手,先确认命令和权限,再让它执行。

请帮我安装这个 Agent Skill:electrobun-native-ui(Electrobun 原生 ui)
来源仓库:https://github.com/rajavijayach/electrobun-skills
仓库路径:skills/electrobun-native-ui
安装命令:
npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-native-ui
安装前请先检查当前环境是否支持对应 CLI,并向我确认将要执行的命令、安装目录、联网范围和文件读写权限;确认后再执行。

命令行安装

复制命令到本机终端执行。该命令会通过 npx skills 从第三方来源获取 Skill;本站只展示命令,不托管安装包,也不自动执行。

skills.shnpx skills
npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-native-ui

这个 Skill 是做什么的

研究检索类 Skill 主要解决「找资料、核对事实、把来源整理清楚」的问题,通常会让助手先检索再总结。

electrobun-native-ui 用于辅助界面设计、视觉规范和交互体验优化,适合生成 UI 方案和检查视觉一致性。适用于 Electrobun 应用程序的原生菜单和应用界面集成场景。提供应用菜单结构、快捷键配置和原生 UI 组件管理等核心能力。

安装命令:npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-native-ui 使用时需要结合现有品牌和用户任务,不应只堆装饰元素。

来源仓库内路径:skills/electrobun-native-ui

上手步骤

4 步
  1. 1

    确认自己的宿主工具

    公开资料标注支持 Codex、Claude、Cursor、Gemini CLI;先确认自己用的是其中之一,再按对应工具的 Skills 目录放置。

  2. 2

    拿到安装命令

    可以直接复制的命令是 npx skills add https://github.com/rajavijayach/electrobun-skills --skill electro…;命令里的占位符换成自己的路径或密钥再执行。

  3. 3

    安装后重启宿主

    命令执行完要完全退出宿主工具再打开,让新装的 Skill 被重新扫描到,否则列表里可能看不到。

  4. 4

    按最小权限试用

    它需要操作浏览器,先在测试目录或测试账号里跑,确认没问题再放到正式项目。

常见问题

6 条

electrobun-native-ui 怎么安装?

在终端执行 npx skills add https://github.com/rajavijayach/electrobun-skills --skill electrobun-native…。装完完全退出宿主工具再打开,让它重新扫描本地 Skill。

electrobun-native-ui 支持哪些 AI 工具?

公开资料标注支持 Codex、Claude、Cursor、Gemini CLI。其他工具只要能读取本地 Skill 目录,一般也能手动放进去,但要自己确认触发方式是否一致。

electrobun-native-ui 需要什么权限?

权限级别是操作浏览器。该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

electrobun-native-ui 是免费的吗?

公开资料没有标注明确许可证,收费和商用限制都无法确认。正式使用前请到来源仓库核对授权说明。

electrobun-native-ui 用的人多吗?

公开数据是累计安装 1,402 次,近一周安装 59 次,下载 491 次,GitHub 6 星。这些数字来自第三方目录统计,只能当作热度参考,不代表质量或安全性。

electrobun-native-ui 安全吗?

第三方扫描结果为通过,但这只覆盖已知的静态检查项,不能替代自己读一遍源码和安装命令。

值得选的地方和要先想清楚的地方

值得选的地方

  • 同时适配 4 种工具,换客户端不用重新找替代品。
  • 第三方扫描已通过,常见的高风险行为没有命中。

先想清楚的地方

  • 许可证没有明确标注,能不能商用、能不能再分发都不确定。
  • 第三方目录整理,版本更新和行为细节以来源仓库为准。

适不适合你

更适合

  • 已经在用 Codex,想直接扩展它能力的人。
  • 需要处理研究检索类任务、又不想每次都手写提示词的人。

不太适合

  • 需要明确授权才能用到公司项目里的场景。
  • 需要厂商级技术支持和 SLA 的生产系统。

研究检索类 Skill 对比

6 个同类
Skill总安装GitHub 星权限许可证扫描
find-skills

寻找技能 · Codex、Claude

31,199,81316,486操作浏览器MIT有提醒
self-improving-agent

自我改进 Agent · OpenClaw

16,464,0023,396接触敏感数据MIT-0通过
soultrace

灵魂追踪 · Codex、Claude

7,588,94412需要联网未明确有提醒
self-improving

自我完善 · OpenClaw

6,829,2841,046需要联网MIT-0通过
microsoft-foundry

微软代工厂 · Codex、Claude

6,614,144764调用外部服务未明确有提醒
electrobun-native-ui

Electrobun 原生 ui · 本页

1,4026操作浏览器未明确通过

按安装量取同分类前几名;数字来自第三方目录统计,只作热度参考,不代表质量结论。

SKILL.md 原文

Electrobun Native UI

Comprehensive guide to native UI integration in Electrobun applications.

Application Menu

Basic Menu Structure

import { ApplicationMenu } from "electrobun/bun";

ApplicationMenu.setMenu([
  {
    label: "File",
    submenu: [
      {
        label: "New",
        accelerator: "CmdOrCtrl+N",
        action: () => createNewDocument()
      },
      {
        label: "Open...",
        accelerator: "CmdOrCtrl+O",
        action: () => openFile()
      },
      { type: "separator" },
      {
        label: "Save",
        accelerator: "CmdOrCtrl+S",
        action: () => saveFile()
      },
      {
        label: "Save As...",
        accelerator: "CmdOrCtrl+Shift+S",
        action: () => saveFileAs()
      },
      { type: "separator" },
      {
        label: "Quit",
        accelerator: "CmdOrCtrl+Q",
        action: () => app.quit()
      }
    ]
  },
  {
    label: "Edit",
    submenu: [
      { role: "undo" },
      { role: "redo" },
      { type: "separator" },
      { role: "cut" },
      { role: "copy" },
      { role: "paste" },
      { type: "separator" },
      { role: "selectAll" }
    ]
  },
  {
    label: "View",
    submenu: [
      {
        label: "Reload",
        accelerator: "CmdOrCtrl+R",
        action: () => currentWindow.reload()
      },
      {
        label: "Toggle DevTools",
        accelerator: "CmdOrCtrl+Shift+I",
        action: () => currentWindow.toggleDevTools()
      },
      { type: "separator" },
      {
        label: "Zoom In",
        accelerator: "CmdOrCtrl+Plus",
        action: () => adjustZoom(0.1)
      },
      {
        label: "Zoom Out",
        accelerator: "CmdOrCtrl+-",
        action: () => adjustZoom(-0.1)
      },
      {
        label: "Reset Zoom",
        accelerator: "CmdOrCtrl+0",
        action: () => resetZoom()
      }
    ]
  },
  {
    label: "Help",
    submenu: [
      {
        label: "Documentation",
        action: () => shell.openExternal("https://docs.myapp.com")
      },
      { type: "separator" },
      {
        label: "About",
        action: () => showAboutDialog()
      }
    ]
  }
]);

Built-in Roles

// Standard edit roles
{ role: "undo" }        // Undo last action
{ role: "redo" }        // Redo last action
{ role: "cut" }         // Cut selection
{ role: "copy" }        // Copy selection
{ role: "paste" }       // Paste from clipboard
{ role: "selectAll" }   // Select all content

// Window roles
{ role: "minimize" }    // Minimize window
{ role: "close" }       // Close window
{ role: "quit" }        // Quit application
{ role: "reload" }      // Reload current page
{ role: "forceReload" } // Force reload (clear cache)
{ role: "toggleDevTools" } // Toggle developer tools
{ role: "toggleFullScreen" } // Toggle fullscreen

// Zoom roles
{ role: "zoomIn" }      // Zoom in
{ role: "zoomOut" }     // Zoom out
{ role: "resetZoom" }   // Reset zoom to 100%

Dynamic Menus

class MenuManager {
  private currentMenu: any[] = [];

  updateRecentFiles(files: string[]) {
    const recentMenu = files.map(file => ({
      label: path.basename(file),
      action: () => openFile(file)
    }));

    // Find File menu and update Recent submenu
    const fileMenu = this.currentMenu.find(m => m.label === "File");
    const recentIndex = fileMenu.submenu.findIndex(
      (item: any) => item.label === "Recent"
    );

    if (recentIndex >= 0) {
      fileMenu.submenu[recentIndex] = {
        label: "Recent",
        submenu: recentMenu.length > 0 ? recentMenu : [
          { label: "No recent files", enabled: false }
        ]
      };
    }

    ApplicationMenu.setMenu(this.currentMenu);
  }

  setCheckState(menuPath: string[], checked: boolean) {
    // Navigate menu structure and update check state
    let current: any = this.currentMenu;

    for (let i = 0; i < menuPath.length - 1; i++) {
      current = current.find((m: any) => m.label === menuPath[i]);
      if (!current) return;
      current = current.submenu;
    }

    const item = current.find((m: any) => m.label === menuPath[menuPath.length - 1]);
    if (item) {
      item.checked = checked;
      ApplicationMenu.setMenu(this.currentMenu);
    }
  }

  enableMenuItem(menuPath: string[], enabled: boolean) {
    // Similar navigation to setCheckState
    // Set item.enabled = enabled
  }
}

// Usage
const menuManager = new MenuManager();
menuManager.updateRecentFiles(["/path/to/file1.txt", "/path/to/file2.txt"]);
menuManager.setCheckState(["View", "Show Sidebar"], true);

Platform-Specific Menus

function createMenu() {
  const isMac = process.platform === "darwin";

  const menu = [];

  // macOS app menu
  if (isMac) {
    menu.push({
      label: app.name,
      submenu: [
        { role: "about" },
        { type: "separator" },
        {
          label: "Preferences...",
          accelerator: "Cmd+,",
          action: () => showPreferences()
        },
        { type: "separator" },
        { role: "services" },
        { type: "separator" },
        { role: "hide" },
        { role: "hideOthers" },
        { role: "unhide" },
        { type: "separator" },
        { role: "quit" }
      ]
    });
  }

  // File menu
  menu.push({
    label: "File",
    submenu: [
      { label: "New", accelerator: "CmdOrCtrl+N", action: createNew },
      { label: "Open", accelerator: "CmdOrCtrl+O", action: openFile },
      // On Windows/Linux, add Quit here
      ...(!isMac ? [
        { type: "separator" },
        { label: "Exit", action: () => app.quit() }
      ] : [])
    ]
  });

  return menu;
}

Context Menu

Basic Context Menu

import { ContextMenu, BrowserWindow } from "electrobun/bun";

const win = new BrowserWindow({ /* ... */ });

win.on("context-menu", (event) => {
  ContextMenu.show([
    {
      label: "Copy",
      accelerator: "CmdOrCtrl+C",
      action: () => {
        // Copy selected text
      }
    },
    {
      label: "Paste",
      accelerator: "CmdOrCtrl+V",
      action: () => {
        // Paste from clipboard
      }
    },
    { type: "separator" },
    {
      label: "Inspect Element",
      action: () => {
        win.inspectElement(event.x, event.y);
      }
    }
  ]);
});

Dynamic Context Menu

// Webview sends context info
// In webview (index.ts):
document.addEventListener("contextmenu", (e) => {
  e.preventDefault();

  const target = e.target as HTMLElement;
  const context = {
    x: e.clientX,
    y: e.clientY,
    hasSelection: window.getSelection()?.toString().length > 0,
    isLink: target.tagName === "A",
    linkUrl: target.tagName === "A" ? (target as HTMLAnchorElement).href : null,
    isImage: target.tagName === "IMG",
    imageUrl: target.tagName === "IMG" ? (target as HTMLImageElement).src : null,
  };

  electroview.rpc.showContextMenu(context);
});

// Main process handles it
win.defineRpc({
  handlers: {
    async showContextMenu(context: any) {
      const menu = [];

      if (context.hasSelection) {
        menu.push(
          { label: "Copy", action: () => win.rpc.copy() },
          { type: "separator" }
        );
      }

      if (context.isLink) {
        menu.push(
          {
            label: "Open Link",
            action: () => shell.openExternal(context.linkUrl)
          },
          {
            label: "Copy Link",
            action: () => clipboard.write(context.linkUrl)
          },
          { type: "separator" }
        );
      }

      if (context.isImage) {
        menu.push(
          {
            label: "Save Image...",
            action: async () => {
              const result = await dialog.showSaveDialog({
                defaultPath: "image.png"
              });
              if (!result.canceled) {
                await downloadImage(context.imageUrl, result.filePath);
              }
            }
          },
          {
            label: "Copy Image",
            action: () => copyImage(context.imageUrl)
          },
          { type: "separator" }
        );
      }

      menu.push({
        label: "Inspect",
        action: () => win.inspectElement(context.x, context.y)
      });

      ContextMenu.show(menu);
    }
  }
});

System Tray

Basic Tray Icon

import { Tray } from "electrobun/bun";

const tray = new Tray({
  icon: "assets://tray-icon.png",
  tooltip: "My Application",
  menu: [
    {
      label: "Show Window",
      action: () => mainWindow.show()
    },
    {
      label: "Hide Window",
      action: () => mainWindow.hide()
    },
    { type: "separator" },
    {
      label: "Quit",
      action: () => app.quit()
    }
  ]
});

Dynamic Tray Updates

class TrayManager {
  private tray: Tray;
  private status: "idle" | "working" | "error" = "idle";

  constructor() {
    this.tray = new Tray({
      icon: this.getIconForStatus("idle"),
      tooltip: "My App - Idle"
    });
    this.updateMenu();
  }

  setStatus(status: "idle" | "working" | "error") {
    this.status = status;
    this.tray.setIcon(this.getIconForStatus(status));
    this.tray.setTooltip(`My App - ${status}`);
    this.updateMenu();
  }

  private getIconForStatus(status: string) {
    return `assets://tray-${status}.png`;
  }

  private updateMenu() {
    const menu = [
      {
        label: `Status: ${this.status}`,
        enabled: false
      },
      { type: "separator" },
      {
        label: "Show Window",
        action: () => mainWindow.show()
      }
    ];

    if (this.status === "working") {
      menu.push({
        label: "Cancel",
        action: () => cancelWork()
      });
    }

    menu.push(
      { type: "separator" },
      {
        label: "Quit",
        action: () => app.quit()
      }
    );

    this.tray.setMenu(menu);
  }

  showNotification(title: string, message: string) {
    this.tray.showBalloon({
      title,
      content: message,
      icon: "assets://notification-icon.png"
    });
  }
}

const trayManager = new TrayManager();
trayManager.setStatus("working");
trayManager.showNotification("Task Complete", "Your task has finished");

Native Dialogs

File Dialogs

import { dialog } from "electrobun/bun";

// Open single file
async function openFile() {
  const result = await dialog.showOpenDialog({
    title: "Open File",
    defaultPath: paths.home,
    filters: [
      { name: "Text Files", extensions: ["txt", "md"] },
      { name: "All Files", extensions: ["*"] }
    ],
    properties: ["openFile"]
  });

  if (!result.canceled && result.filePaths.length > 0) {
    const content = await Bun.file(result.filePaths[0]).text();
    return { path: result.filePaths[0], content };
  }

  return null;
}

// Open multiple files
async function openMultipleFiles() {
  const result = await dialog.showOpenDialog({
    properties: ["openFile", "multiSelections"]
  });

  if (!result.canceled) {
    return result.filePaths;
  }

  return [];
}

// Open directory
async function openDirectory() {
  const result = await dialog.showOpenDialog({
    title: "Select Folder",
    properties: ["openDirectory"]
  });

  if (!result.canceled && result.filePaths.length > 0) {
    return result.filePaths[0];
  }

  return null;
}

// Save file
async function saveFile(defaultName = "untitled.txt") {
  const result = await dialog.showSaveDialog({
    title: "Save File",
    defaultPath: path.join(paths.home, defaultName),
    filters: [
      { name: "Text Files", extensions: ["txt"] },
      { name: "All Files", extensions: ["*"] }
    ]
  });

  if (!result.canceled) {
    return result.filePath;
  }

  return null;
}

Message Boxes

// Confirmation dialog
async function confirmQuit() {
  const result = await dialog.showMessageBox({
    type: "question",
    title: "Confirm Quit",
    message: "Are you sure you want to quit?",
    detail: "Unsaved changes will be lost.",
    buttons: ["Quit", "Cancel"],
    defaultId: 1,
    cancelId: 1
  });

  return result.response === 0; // Returns true if "Quit" clicked
}

// Error dialog
async function showError(message: string, detail?: string) {
  await dialog.showMessageBox({
    type: "error",
    title: "Error",
    message,
    detail,
    buttons: ["OK"]
  });
}

// Warning with options
async function showWarning() {
  const result = await dialog.showMessageBox({
    type: "warning",
    title: "Warning",
    message: "This action cannot be undone",
    detail: "Are you sure you want to continue?",
    buttons: ["Continue", "Cancel", "Learn More"],
    defaultId: 1,
    cancelId: 1
  });

  if (result.response === 0) {
    // Continue
  } else if (result.response === 2) {
    shell.openExternal("https://docs.myapp.com/warning");
  }
}

// Info dialog
async function showInfo(message: string) {
  await dialog.showMessageBox({
    type: "info",
    title: "Information",
    message,
    buttons: ["OK"]
  });
}

Keyboard Shortcuts

Global Shortcuts

import { globalShortcut } from "electrobun/bun";

// Register global shortcut
globalShortcut.register("CommandOrControl+Shift+Space", () => {
  mainWindow.show();
  mainWindow.focus();
});

// Register multiple shortcuts
const shortcuts = [
  { key: "CommandOrControl+1", action: () => switchToTab(0) },
  { key: "CommandOrControl+2", action: () => switchToTab(1) },
  { key: "CommandOrControl+3", action: () => switchToTab(2) },
];

shortcuts.forEach(({ key, action }) => {
  globalShortcut.register(key, action);
});

// Unregister on quit
app.on("will-quit", () => {
  globalShortcut.unregisterAll();
});

In-Window Shortcuts

// Define in menu (automatically registered)
{
  label: "New Window",
  accelerator: "CmdOrCtrl+N",
  action: () => createWindow()
}

// Custom accelerators in webview
document.addEventListener("keydown", (e) => {
  // Cmd/Ctrl + K
  if ((e.metaKey || e.ctrlKey) && e.key === "k") {
    e.preventDefault();
    openCommandPalette();
  }

  // Cmd/Ctrl + P
  if ((e.metaKey || e.ctrlKey) && e.key === "p") {
    e.preventDefault();
    openFileFinder();
  }
});

Notifications

System Notifications

import { Notification } from "electrobun/bun";

function showNotification(title: string, body: string) {
  new Notification({
    title,
    body,
    icon: "assets://notification-icon.png",
    sound: true
  }).show();
}

// With actions (on supported platforms)
const notification = new Notification({
  title: "New Message",
  body: "You have a new message from Alice",
  actions: [
    { type: "button", text: "Reply" },
    { type: "button", text: "Dismiss" }
  ]
});

notification.on("action", (index) => {
  if (index === 0) {
    openReplyWindow();
  }
});

notification.show();

Resources

For more on Electrobun:

  • Core skill: electrobun - Basic UI setup
  • Window management: electrobun-window-management - Window coordination
  • RPC patterns: electrobun-rpc-patterns - UI state sync

平台分布

Codex

36.15%
按下载量换算177

Claude

25.76%
按下载量换算126

Cursor

19.83%
按下载量换算97

Gemini CLI

9.64%
按下载量换算47

安全审计

Gen Agent Trust Hub

通过

Socket

通过

Snyk

通过

权限和风险

操作浏览器

该 Skill 可能涉及浏览器控制能力,使用时可能读取或操作网页内容,需要在受控环境中确认权限边界。

安装前确认

本站仅展示第三方公开信息,不托管安装包,不提供自动安装或运行环境。安装前应自行审查源码、依赖和命令行为。目前只有一个公开来源,建议同时核对源仓库。

继续浏览同类 Skills