---
name: hypercard-component-authoring
description: Use when writing or editing HyperCard / @hy-bricks low-code component source (Vue Options-style strings) — for component-to-component events (broadcast/receive), data injection (dataInput / setDataInput), the reserved hc* props, and layout-component authoring. Triggers on tasks like 组件联动 / 广播事件 / 接收事件 / 取数灌入 / 写个布局组件.
---

# HyperCard 组件编写(@hy-bricks SDK)

组件源码是 **Vue Options 风格的字符串**(`export default { data, methods, template, custom }`),由 SDK 编译挂载。运行时全局是 `window.__HYPERCARD__`,组件代码里直接可用,无需 import。完整文档见站内 `指南 / 组件间事件`。

## 保留 prop(SDK 注入,别用这些名字声明自己的 prop)

- `this.hcInstanceId` — 自己的实例 ID(订自己信箱 / 被定向发都用它)
- `this.hcCanvasId` — 所在画布 ID(限定本画布群投用)
- `this.hcComponentId` — 组件源 ID(同款多实例共享)
- `this.hcCustomValues` — 属性面板填的 override 值

## 事件模型:私有信箱,不是全局总线

每个实例有一个私有信箱:

- `runtime.on(X, event, fn)` — 订实例 X 的信箱
- `runtime.emit(X, event, payload)` — 投一封信到 X(只有 X 收到)
- `canvas.broadcastInCanvas(event, payload)` — 往本画布每个实例都投一封

**唯一要记的规则**:接收方永远订自己 `on(this.hcInstanceId, …)`;广播方用 `broadcastInCanvas` 群投。广播方知道发给谁时才用 `emit(目标id, …)` 定向。

### 广播(一对多联动)

```js
window.__HYPERCARD__
  ?.canvases.get(this.hcCanvasId)
  ?.broadcastInCanvas('cascade:change', { dim: this.dim, grain: this.grain })
```

### 接收(务必 beforeUnmount 退订)

```js
data() { return { _off: null } },
mounted() {
  this._off = window.__HYPERCARD__
    ?.runtime.on(this.hcInstanceId, 'cascade:change', (p) => { this.reload(p) })
},
beforeUnmount() { this._off && this._off() },
```

### 三层范围,选最小的

- 单实例定向:`runtime.emit(id, e, p)`;多画布带 scope:`canvases.emitToCanvas(cid, id, e, p)`
- 本画布:`canvases.get(cid).broadcastInCanvas(e, p)`
- 全部画布:`canvases.broadcast(e, p)`

## 取数灌入(dataInput)

组件声明接收字段,模板读 `custom.<key>.value`:

```js
custom: { rows: { value: [], dataInput: true } },
```

收到广播后把数据写进去走 `setDataInput`(等价 `this.custom.rows.value = rows`,但会被绑定/诊断记录):

```js
const h = window.__HYPERCARD__?.runtime.getInstance(this.hcInstanceId)
h?.setDataInput('rows', rows)
```

字段必须显式 `dataInput: true`,否则 `setDataInput` 拒写并 warn。

## 冒泡事件给宿主

组件往自己信箱投,宿主订该实例信箱:

```js
// 组件源码
window.__HYPERCARD__?.runtime.emit(this.hcInstanceId, 'rowClick', row)
// 宿主页面(非组件源码)
window.__HYPERCARD__?.runtime.on('table_orders', 'rowClick', (row) => { /* ... */ })
```

## 关键纪律

- 事件名写**字面量**(`emit('rowClick')`),后端 `parseComponentSource` 才能扫进契约的 `emitsDecl`,devtools / 属性面板才列得出。动态名(`emit(this.evt)`)扫不到。
- `on(...)` 返回的 off 一定存起来,`beforeUnmount` 调用;漏了在换源 / 卸载 / HMR 时泄漏 handler。
- 0.4.0 起 register 在 `created`(早于 mounted)完成,所以 mounted 里订阅无需 `nextTick`。
- 但订生命周期信号 `instance:ready` 时,0.4.0 起回调触发时子组件 DOM 还没渲染,`vm.$el` 是 null,要访问 DOM 包 `nextTick`。
- 设计器 `mode="design"` 里组件也真实编译挂载,信箱机制照常,所以预览与发布行为一致;设计期联动的取数副作用会真发出去。

## 排查

- 接收方没反应 → 订错 id(不是 `this.hcInstanceId`)/ 广播方该用 `broadcastInCanvas` 却用了 `emit(单id)`。
- 偶发不触发 + `instance not found` warn → 广播早于接收方 mounted(时序)/ 目标 id 写错。
- 换了组件代码旧逻辑还跑 → 漏 `off()`。
- 多画布串台 → 全站 `broadcast` 误用,应 `broadcastInCanvas`;定向没带 `canvasId`。
- devtools / 属性面板不列事件 → 事件名不是字面量。

参考:站内「指南 / 组件间事件」「API / core」,全文索引 llms-full.txt。
