🚀 Socket Launch Week Day 5:Introducing Repository Access Permissions and Custom Roles.Learn more
Sign In

@hy-bricks/editor

Package Overview
Dependencies
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@hy-bricks/editor

HyperCard 嵌入式编辑器 — Monaco 三栏 + 拖 tab 分栏 + 实时预览 + shadcn-vue 风格 UI

latest
npmnpm
Version
0.6.6
Version published
Maintainers
1
Created
Source

@hy-bricks/editor

嵌入式低代码组件编辑器:Monaco 三栏(html/js/css)+ 拖 tab 多分栏 + 实时预览 + 草稿持久化 + Monaco 类型注入。

npm license vue monaco tailwind

📦 当前发布版 0.6.3 ── 与 @hy-bricks/core 同步(fixed 组同版本号)。逐版变更(含历史 BREAKING)以 CHANGELOG 为准;注意 0.6.0 跟随 core 收口公开面(移除 test-only / 内部导出)。

┌─────────────────────────────────────────────────────────────────────┐
│  demo-component  v3 · 已发布          ☐ 自动预览  ⚙  保存草稿  推版本 │
├──────────────────────────────────┬──────────────────────────────────┤
│  html │ javascript │ + css       │  预览                  ⧉           │
│ ─────────────────────────────── │ ─────────────────────────────── │
│  <div class="root">              │                                  │
│    <h2>{{ greeting }}</h2>       │   Hello World !                  │
│    <el-input v-model="msg" />    │   ┌───────────────┐              │
│    <el-button @click="add">      │   │ 试试输入       │              │
│      点我 +1                      │   └───────────────┘              │
│    </el-button>                  │   你输入了:                      │
│  </div>                          │                                  │
│                                  │   [ 点我 +1 ]  点了 0 次          │
└──────────────────────────────────┴──────────────────────────────────┘
                          (拖 tab 头 → 多分栏)

<HyperCardEditor> 是一个受控组件:你给一份 {html, js, css} 源码,它给你一个 IDE 风界面 + 一份能跑的实时预览。所有副作用(保存、推版本、开窗口、设置面板)都通过事件抛回宿主,SDK 不调任何 API不依赖 vue-router不强制宿主目录结构

Quickstart(5 分钟,从空白 Vite + Vue 3 项目接进来)

1. 安装 SDK + 全部 peerDeps

reka-ui / cva / clsx / tailwind-merge / lucide-vue-next 在 SDK 构建时被 vite external,必须由宿主装(详见下面的 peerDependencies)。

pnpm add @hy-bricks/editor @hy-bricks/core \
  vue monaco-editor \
  reka-ui class-variance-authority clsx tailwind-merge lucide-vue-next \
  tailwindcss tailwindcss-animate autoprefixer postcss

2. tailwind.config.js —— 不要用 SDK 的 preset

包内 .vue 用了大量 Tailwind utility,但 Tailwind v3 默认 ignore node_modules,即便 content 写上 node_modules/@hy-bricks/** 也大概率扫不全(symlink、pnpm hoist 路径变化)。

正确做法:本包发布时已经 self-compile 出 dist/style.css,把全部需要的 utility 都打进去了。宿主只要 import 这一个 css 就行,不需要让自己的 Tailwind 扫包内源码。

/** tailwind.config.js — 宿主只配自己的内容,不扫 SDK */
export default {
  content: [
    './index.html',
    './src/**/*.{vue,ts,tsx,js,jsx}',
  ],
  theme: { extend: {} },
  plugins: [],
}

3. src/style.css

@tailwind base;
@tailwind components;
@tailwind utilities;

不要在这个 css 里 @import '@hy-bricks/editor/style.css'。PostCSS 的 @import 走它自己的解析器,不识别 npm scope alias(@hy-bricks/...),通常会 silent 失败: 编译没报错,主题变量却丢失。唯一推荐:在 main.ts 里用 JS import(下一步), 走 bundler 的 ESM 解析,稳定可靠。

4. src/main.ts

import { createApp } from 'vue'
import { createHyperCard } from '@hy-bricks/core'

// 通过 vite ESM resolve 直接拿包内 self-compiled 样式 — 这是引入主题 CSS 的
// **唯一推荐方式**(不要在 style.css 里 @import,见上一步说明)
import '@hy-bricks/editor/style.css'

import App from './App.vue'
import './style.css'

const app = createApp(App)

app.use(
  createHyperCard({
    libs: {
      // 你提供给组件作者用的运行时库,组件代码里通过 __HYPERCARD__.libs.* 拿
      http: /* axios 或自己封装的 fetch */ undefined,
      ui: /* ElementPlus / antdv / 你自己的 UI 库 */ undefined,
    },
  }),
)

app.mount('#app')

5. App.vue

<script setup lang="ts">
import { ref } from 'vue'
import { HyperCardEditor } from '@hy-bricks/editor'

const source = ref({ html: '', js: '', css: '' })
</script>

<template>
  <div class="h-screen">
    <HyperCardEditor v-model="source" draft-key="my-component" />
  </div>
</template>

pnpm dev 就能看到完整 IDE。三栏 Monaco 都能编辑、改了立刻预览、Cmd+S 存草稿、刷新页面草稿还在。

peerDependencies(必装清单)

版本必装说明
vue^3.5Composition API + <script setup>
monaco-editor^0.55三栏代码编辑器内核
tailwindcss^3.4 || ^4宿主自己的 Tailwind(只扫自己,不扫 SDK)
@hy-bricks/core同 SDK 版本运行时(预览盒子、createHyperCard plugin)
reka-ui^1.0.0-alpha.8shadcn-vue 的 headless 底座(Button 等用了)
class-variance-authority^0.7buttonVariants 生成器
clsx^2.1className 合并
tailwind-merge^2.5dedupe Tailwind 冲突 class
lucide-vue-next^0.469顶栏图标(Settings / RefreshCw 等)
tailwindcss-animate^1.0推荐shadcn-vue 默认动画(若你启用宿主侧 shadcn 组件)

SDK package.json 里 reka-ui / cva / clsx / tailwind-merge / lucide-vue-next 已经移到 peerDependencies(语义就是"宿主项目需要装"):宿主必须显式安装;SDK build 时把它们 external,避免重复打包。

API

<HyperCardEditor> props

prop类型默认说明
modelValue{ html: string; js: string; css: string }必填v-model 双向绑定;形状写死,宿主必须适配
draftKeystringundefined启用 localStorage 草稿;实际 key = hc:draft:<draftKey>。留空 = 不启用草稿
componentIdstring回退 draftKey,再回退实例级唯一 id(__hc_editor_<uid>__,用 getCurrentInstance().uid)给运行时实例的 id。默认回退已自动每实例唯一,不再撞;多实例仍建议显式传,方便 runtime / cssScope / 调试定位
showBackButtonbooleanfalse顶栏左侧返回按钮。点击触发 go-back 事件,SDK 自己不路由
titlestringundefined顶栏组件标题
statusLabelstringundefined顶栏右侧的版本号 / 状态文字(如 'v3 · 已发布')
autoPreviewbooleantrue改源码后自动 debounce 渲染预览;false 时显示「重新渲染」按钮,需手点
previewRationumber0.62编辑器与预览的初始宽度比 0–1
extraLibsExtraLib[]undefined给 Monaco 注入的额外类型 lib(自动补全 __HYPERCARD__.libs.*),详见 Monaco 类型注入
interface ExtraLib {
  filePath: string  // 必须形如 'file:///foo.d.ts'
  content: string   // ambient .d.ts 字符串(不能含 export {})
}

emit 事件

事件payload触发时机宿主该怎么处理
update:modelValue{ html, js, css }任意一栏 Monaco 内容变化(throttle)v-model 自动接,无需手写
dirtyboolean用户改了源码 / 调用 applyDraft 后 → true;v-model 重置或保存草稿后 → false顶栏 dot、disable「推版本」按钮等
save-draft用户点顶栏「保存草稿」或 Cmd+S(草稿已写入 localStorage)toast「已保存」即可,无需自己再写
push-version用户点顶栏「推版本」弹自家 dialog 让填备注 → 调后端 API
open-preview-window用户点预览面板右上的「⧉ 独立窗口」window.open(routerResolve('/preview/...'), ...);开了之后 SDK 自动用 BroadcastChannel 同步源码
open-settings用户点顶栏齿轮图标弹自家组件元数据 drawer(name / desc / tags 等)
go-backshowBackButton=true 时点击返回按钮router.back() 之类
error{ message, stack? }编译 / 运行时报错写 console、上报 Sentry、Toast 等

expose(ref 拿到的 instance)

const editor = useTemplateRef<{
  isDirty: Ref<boolean>                      // ← 注意是 Ref,不是裸 boolean
  confirmLeave(): boolean
  applyDraft(draft: ComponentDraft): void
  manualPreview(): void
  applyPreview(): void
}>('editor')
方法 / 属性签名说明
isDirtyRef<boolean>是 ref 不是裸值,读用 editor.value.isDirty.value、模板里直接 editor?.isDirty
confirmLeave()() => boolean同步弹 confirm,dirty 时提示「未保存,确定离开?」;返回 true 表示放行,false 表示用户取消
applyDraft(draft)(d: ComponentDraft) => void把外部草稿灌进编辑器(用于「恢复历史版本」),会把 dirty 设为 true
manualPreview()() => voidautoPreview=false 时手动触发预览
applyPreview()() => voidv-model 灌进新 source 后,如果想立刻刷预览(watch(modelValue) 已经会调,基本用不到)

vue-router 离开守卫

import { onBeforeRouteLeave } from 'vue-router'

const editor = useTemplateRef<{ confirmLeave(): boolean }>('editor')

onBeforeRouteLeave(() => editor.value?.confirmLeave() ?? true)

beforeunload(关浏览器、刷新)SDK 自动接,无需宿主写。

Monaco 类型注入

让组件作者写 __HYPERCARD__.libs.http. 时 Monaco 自动补全到具体方法。SDK 默认已经注册 __HYPERCARD__ / runtime / assets 三个 ambient,libs 的具体类型必须由宿主补(因为只有宿主知道你提供了 axios 还是 ky 还是 ElementPlus)。

import { HyperCardEditor, type ExtraLib } from '@hy-bricks/editor'

const extraLibs: ExtraLib[] = [
  {
    // 必须 file:/// 协议,Monaco 用这个判断模块身份
    filePath: 'file:///host-libs.d.ts',
    content: `
// 关键:用 declaration merging 跟 SDK 自带的 HyperCardLibs 接口合并,
// 不要写 export {},一旦文件里有 import/export,
// 它就变成 module 而不是 ambient,声明就泄漏不到全局了
interface HyperCardLibs {
  http: {
    get<T = any>(url: string, config?: any): Promise<{ data: T; status: number }>
    post<T = any>(url: string, data?: any, config?: any): Promise<{ data: T; status: number }>
  }
  ui: { version: string; install: (...args: any[]) => void }
}
`,
  },
]
<HyperCardEditor v-model="source" :extra-libs="extraLibs" />

致命陷阱:content 字符串里不能出现 export {} / import ...,否则 .d.ts 自动从 ambient 转成 module,顶层 declare const __HYPERCARD__ 就不再是 global,组件作者那边补全直接灰掉。

主题定制

SDK 用 shadcn-vue 风的 HSL CSS variables。不需要重 build,在宿主自己的 :root 覆盖任意一条即可。

变量默认 (light)用途
--background / --foreground0 0% 100% / 222.2 84% 4.9%页面底色 / 主文字
--primary / --primary-foreground222.2 47.4% 11.2% / 210 40% 98%「推版本」主按钮
--secondary / --secondary-foreground210 40% 96.1% / 222.2 47.4% 11.2%次按钮
--muted / --muted-foreground210 40% 96.1% / 215.4 16.3% 46.9%弱化区域、placeholder
--accent / --accent-foreground210 40% 96.1% / 222.2 47.4% 11.2%hover 反馈
--destructive / --destructive-foreground0 84.2% 60.2% / 210 40% 98%删除 / 警告
--border / --input / --ring214.3 31.8% 91.4% / 同左 / 222.2 84% 4.9%边框 / input / focus ring
--radius0.5rem基础圆角(Button、面板)
/* 改主色 + dark mode 一起改 */
:root {
  --primary: 220 80% 50%;
  --radius: 0.25rem;
}

.dark {
  --primary: 220 70% 60%;
  --background: 222.2 84% 4.9%;
}

值要写纯 HSL 三元(无 hsl() 包裹、无逗号),包内 hsl(var(--primary) / <alpha>) 会自己拼。

草稿持久化 + 离开守卫

场景SDK 做了什么宿主做什么
设置 draft-key="abc"启用 localStorage,key = hc:draft:abc传 prop
顶栏「保存草稿」/ Cmd+S写 localStorage,清除 dirty,触发 @save-draft接事件 toast 反馈
进入页面检测到旧草稿顶栏渲染琥珀色 pill「本地草稿 · 5 分钟前 · [恢复] [丢弃]」
关浏览器 / 刷新 (beforeunload)dirty 时弹浏览器原生 confirm
切路由 (onBeforeRouteLeave)暴露 confirmLeave()在 router guard 中调:return editor.value?.confirmLeave()
推版本成功宿主自己 clearDraft(key)(从 SDK 导出)
import { clearDraft, loadDraft, saveDraft } from '@hy-bricks/editor'

clearDraft('my-component')                        // 推版本成功后清
const d = loadDraft('my-component')               // SSR / 列表预读
saveDraft('my-component', { html, javascript, css })   // 自己触发存(很少用)

子组件单独导出(高级用户)

不想要默认的「IDE 顶栏 + 三栏 + 预览」布局?可以拿乐高块自己拼:

import {
  // 布局
  EditorLayout, EditorGroup, MonacoEditor, Splitter,
  // multi-instance scope(每个独立编辑器一份,model 不串)
  createEditorScope, useEditorScope, EDITOR_SCOPE_KEY,
  // 布局算法(纯函数,immutable)
  defaultLayout, activateTab, closeTab, splitWithTab, moveTab, setSplitRatio,
  // 草稿
  loadDraft, saveDraft, clearDraft,
  // 预览跨窗口总线
  createPreviewBus,
  // shadcn-vue 风 Button(包内 vendor)
  Button, buttonVariants,
  // Monaco 类型钩子
  setupHyperCardMonacoTypes, addMonacoExtraLib,
} from '@hy-bricks/editor'
  • EditorLayout / EditorGroup / MonacoEditor / Splitter —— 自拼 IDE 界面
  • createEditorScope / useEditorScope —— multi-instance 的 monaco model 隔离
  • Button / buttonVariants —— vendor 自 shadcn-vue,跟 SDK 主题变量一致

Troubleshooting

❌ 装了 SDK,顶栏 Button 没样式 / 圆角错 / 颜色全黑

原因:Tailwind v3 出于性能默认 ignore node_modules,所以即便宿主 tailwind.config.jscontent 写了 node_modules/@hy-bricks/**,也基本扫不到包内 .vue 里的 utility class。

解法:不要让宿主 Tailwind 扫这个包。本包发布时已经 self-compile 出 dist/style.css,把全部需要的 utility 都打进去。宿主只要:

import '@hy-bricks/editor/style.css'

就拿到完整样式 + 主题变量。Tailwind preset 共享只是给「我也想跟本包用同一套色板」的宿主用,跟样式生效无关

Cannot read file ... ?worker 报错

原因:Vite 默认会把 node_modules 里的 ESM 包预 bundle(esbuild),但 esbuild 不认识 Vite 的 ?worker query —— monaco-editor 的 worker import 就这样炸。

解法:宿主 vite.config.ts 把 SDK 排除:

export default defineConfig({
  optimizeDeps: {
    exclude: ['@hy-bricks/core', '@hy-bricks/editor'],
  },
})

❌ PostCSS Cannot find module 'postcss' / CJS 加载失败

原因:Vite 把 SDK 当 ESM 加载时,如果宿主和 SDK 用了不同 PostCSS 版本,Node 模块解析会撞到 CJS/ESM 边界。

解法:SDK 已经把 postcss 处理打进自己的 dist 里了。如果宿主仍然另行 import postcss,在 vite.config.ts 里加 dedupe:

export default defineConfig({
  resolve: {
    dedupe: ['postcss', 'monaco-editor', 'vue'],
  },
})

editor.value.isDirty 永远 truthy

原因:isDirty 暴露的是 Ref<boolean> 不是裸 boolean,而 Ref 对象本身永远 truthy。

解法:读 .value,或在模板里依赖自动 unwrap。TS 类型也要写对:

const editor = useTemplateRef<{ isDirty: Ref<boolean> }>('editor')

if (editor.value?.isDirty.value) { /* ... */ }

或干脆听 @dirty 事件,最推荐:

<HyperCardEditor v-model="source" @dirty="d => isDirty = d" />

❌ Monaco 补全 __HYPERCARD__.libs. 是空的

原因:你的 extraLibs[].content 里出现了 export {} / import x from ...,文件被 TS 识别成 module,声明不再 global。

解法:见 Monaco 类型注入。content 必须是纯 ambient,只有 declare / interface / type,没有任何 import / export

⚠ 多个 <HyperCardEditor> 同页,样式 / 实例 id 区分

默认行为:不传 componentId 时,自动回退成实例级唯一 id(__hc_editor_<uid>__,基于 getCurrentInstance().uid),每个 <HyperCardEditor> 实例自动拿不同 id,不会撞

仍建议:多实例并存时显式传 componentId(或不同 draftKey,会被自动回退用),让 runtime / cssScope / 调试堆栈里看得清是哪个实例:

<HyperCardEditor v-model="a" component-id="card-a" />
<HyperCardEditor v-model="b" component-id="card-b" />

配合使用 / Ecosystem

License

MIT © hy_top

Keywords

hypercard

FAQs

Package last updated on 27 Jun 2026

Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts