feat: 收录 inkreach-teable-plugin skill;install.sh 安装全部技能

skills/ 现包含 inkreach-teable + inkreach-teable-plugin,
npx skills add 一并安装两个;install.sh 改为遍历安装 skills/*。
This commit is contained in:
yeuimu
2026-09-23 06:23:46 +00:00
parent d81d14bd52
commit e09ed1bc0f
8 changed files with 1261 additions and 6 deletions
@@ -0,0 +1,106 @@
# Common pitfalls and how to avoid them
These are real issues discovered during plugin development. Read before writing
plugin logic — each one will save you debugging time.
## Pitfall 1: Importing `@teable/*` packages instead of using host globals
```typescript
// WRONG — webpack externals map these away at build time, so at runtime
// the import produces `undefined` or throws:
import { useFields } from '@teable/sdk'; // → undefined at runtime
import { Button } from '@teable/ui-lib'; // → undefined at runtime
import { definePlugin } from '@teable/plugin-sdk'; // fine (it re-exports from host)
// CORRECT — always read from the host global:
const host = window.__TEABLE_HOST__;
const { useFields } = host.SDK;
const { Button } = host.UILib;
const { definePlugin } = host.PluginSDK;
const React = host.React;
```
The webpack `externals` config in the scaffold template maps `@teable/sdk` →
`window.__TEABLE_HOST__.SDK` at build time. This means the bundled code replaces
the import with `window.__TEABLE_HOST__.SDK`, but destructuring from the import
statement doesn't survive the webpack transformation — use direct `host.*`
access instead. The only exception is `@teable/plugin-sdk` which re-exports
from `window.__TEABLE_HOST__.PluginSDK` at the level webpack expects.
**Symptoms**: `useFields is not a function`, `Cannot read properties of undefined`,
or components silently rendering nothing.
## Pitfall 2: `window.__TEABLE_HOST__` is undefined
The host object is injected by `HostInitializer` which mounts inside
`<AppProvider>`. This happens after the initial React tree render. If you try
to access `window.__TEABLE_HOST__` in a script that executes before the host
tree mounts, it will be `undefined`.
**Fix**: The `PluginLoader` component ensures the host is ready before loading
plugin scripts, so plugins loaded through the normal flow don't hit this. But
if you're testing a plugin directly (e.g. loading main.js in a browser console),
you'll see `__TEABLE_HOST__` is null until the app renders.
## Pitfall 3: Upload returns 403 — not a developer
Only users in the `plugin_developer` DB table can upload bundles. The
`DeveloperGuard` checks this. If you get 403 on `upload-bundle`, your user is
not whitelisted.
**Fix**: Add the user to `plugin_developer`:
```sql
INSERT INTO plugin_developer (id, user_id, email, created_by)
VALUES (gen_random_uuid(), '<userId>', '<email>', '<adminUserId>');
```
## Pitfall 4: Enable (plugin-config) must be called before install
Installing a plugin to a position (contextMenu / panel / view / dashboard)
requires the plugin to be enabled first. If you skip enable and call install
directly, you'll get 403.
**The correct sequence is always**:
1. Upload bundle → get pluginId
2. Enable via `PUT /base/:baseId/plugin-config/:pluginId`
3. Install to position
## Pitfall 5: `getSelectionRecords` returns empty array, not throw
The old iframe/ZIP `bridge.getSelectionRecords()` threw when nothing was
selected. The dynamic system's `PluginSDK.getSelectionRecords()` returns an
empty array instead. You still need to handle the empty case in your UI,
but you don't need a try/catch wrapper.
```typescript
const { getSelectionRecords } = host.PluginSDK;
const res = await getSelectionRecords(tableId, viewId, selection);
if (res.records.length === 0) {
// Show "no records selected" message — no try/catch needed
}
```
## Pitfall 6: Plugin component renders nothing / context menu empty
If your plugin uploads, enables, and installs successfully but the component
never appears:
1. **Check `plugin.json` `positions`** — it must include the position you're
installing to (e.g. `"positions": ["contextMenu"]`)
2. **Check `onLoad` registration** — `ctx.addContextMenuAction('main', Component)`
must be called inside `onLoad`
3. **Check Authority Matrix** — the user's role must have the plugin enabled
in AM. For the test account, the owner bypasses AM, but for other users,
an admin must enable the plugin per role in the RoleEditor UI.
4. **Check browser console** — errors in `onLoad` are silently caught; add a
`console.log` at the top of `onLoad` to verify it runs.
## Pitfall 7: Component uses JSX but webpack doesn't transpile it
The scaffold template uses `ts-loader` with `jsx: "react-jsx"` in tsconfig.
If you're setting up the webpack config manually, make sure:
- `tsconfig.json` has `"jsx": "react-jsx"` under `compilerOptions`
- The plugin does NOT need `import React from 'react'` at the top when using
`react-jsx` transform — JSX is auto-transpiled. But you still need
`const React = host.React` for any explicit `React.createElement` calls.