{"_id":"@axhub/genie-editor-client","name":"@axhub/genie-editor-client","dist-tags":{"latest":"0.0.1"},"versions":{"0.0.1":{"name":"@axhub/genie-editor-client","version":"0.0.1","main":"dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"}},"scripts":{"build":"tsup src/index.ts --format cjs,esm --dts --clean","dev":"tsup src/index.ts --format cjs,esm --dts --watch"},"peerDependencies":{"react":"^18.2.0"},"devDependencies":{"@types/react":"^18.0.0","tsup":"^8.4.0","typescript":"^5.0.0"},"_id":"@axhub/genie-editor-client@0.0.1","gitHead":"d0cf5099c320788f62723b8ee5bdb28ccd24b0c4","description":"`@axhub/genie-editor-client` 是给 Genie Editor 宿主侧使用的一层轻量客户端集成工具包。","_nodeVersion":"22.21.1","_npmVersion":"10.9.4","dist":{"integrity":"sha512-D2flhjPZkgltdx/8MATnAHRDC32nZKLyZwr+CaHg3AzBsfl3sf01GUFwYc0UKf8ELFQSP5sDFMHyi9yIJ4U7Vg==","shasum":"11ea973f8dab6ff2e7b1e11f64d18188a596b1ca","tarball":"https://registry.npmjs.org/@axhub/genie-editor-client/-/genie-editor-client-0.0.1.tgz","fileCount":6,"unpackedSize":33380,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBs3aqSmwGLxrsugZ1MmWZaC2Wa+90CmdFowUMmnKiwVAiEA+7/90RBKUbXqKb3r27mmoaBA3imM6l90s78k/bVtl18="}]},"_npmUser":{"name":"lintendo","email":"geekljz@gmail.com"},"directories":{},"maintainers":[{"name":"lintendo","email":"geekljz@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/genie-editor-client_0.0.1_1776614795066_0.03901198670024408"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-19T16:06:34.957Z","0.0.1":"2026-04-19T16:06:35.204Z","modified":"2026-04-19T16:06:35.452Z"},"maintainers":[{"name":"lintendo","email":"geekljz@gmail.com"}],"description":"`@axhub/genie-editor-client` 是给 Genie Editor 宿主侧使用的一层轻量客户端集成工具包。","readme":"# @axhub/genie-editor-client\n\n`@axhub/genie-editor-client` 是给 Genie Editor 宿主侧使用的一层轻量客户端集成工具包。\n\n当前首批能力聚焦在 React tweak helper，但它的定位不是只服务 React hook，而是承接页面宿主与 `axhub-genie-editor` 对接时需要的客户端侧工具。\n\n## 对外使用口径\n\n如果你是这个 npm 包的使用方，对外只需要关注：\n\n- 安装 `@axhub/genie-editor-client`\n- 所有 import 都从 `@axhub/genie-editor-client` 读取\n- 把 schema / values / adapter 注册到全局 tweak protocol\n\n`axhub-genie-editor` 是当前仓库里的 editor 运行时消费方，用来读取你注册到页面全局的 tweak protocol。它不是这个 README 面向的安装入口，也不是你在宿主项目里需要直接依赖的包。\n\n它不定义业务字段，也不替宿主决定哪些属性可编辑，只做三件事：\n\n- 提供一个 `useSyncExternalStore` 风格的 tweak store\n- 把 React 侧状态包装成 Genie Editor 可识别的 tweak adapter\n- 帮宿主把元素级 tweak 能力注册到全局协议中\n\n协议本身仍然是框架无关的，未来 Vue / jQuery / 原生 DOM 宿主也可以直接实现同一套协议。\n\n## 安装\n\n```ts\nimport {\n  createGenieEditorReactTweakStore,\n  createGenieEditorReactTweakAdapter,\n  useGenieEditorReactTweakStore,\n  useRegisterGenieEditorTweak,\n} from '@axhub/genie-editor-client';\n```\n\n如果你的宿主需要直接访问协议类型，也可以直接从同一个包导入：\n\n```ts\nimport type {\n  GenieEditorTweakSchema,\n  GenieEditorTweakValues,\n} from '@axhub/genie-editor-client';\n```\n\n如果你的宿主需要在“字段结构变了但没有重建 adapter”时手动刷新 editor，也可以导入：\n\n```ts\nimport { notifyGlobalGenieEditorTweakProtocol } from '@axhub/genie-editor-client';\n```\n\n## 何时使用\n\n适合以下场景：\n\n- 宿主本身是 React 页面\n- 元素的 tweak 值已经存在于 React state / props / store 中\n- 希望让 Genie Editor 的 `\"调整\"` tab 直接消费这些值\n\n不适合以下场景：\n\n- 想把页面级全局配置直接映射为 tweak\n- 想让 editor 内置一套固定可编辑字段\n- 想把 tweak 设计成只读静态 schema 且没有 element 绑定关系\n\n## 核心概念\n\n### 1. store\n\n`createGenieEditorReactTweakStore(initialValues)` 创建一个最小外部 store：\n\n```ts\nconst tweakStore = createGenieEditorReactTweakStore({\n  title: '旧标题',\n  visible: true,\n});\n```\n\n它提供：\n\n- `getSnapshot()`\n- `subscribe(listener)`\n- `set(nextValues)`\n- `update(patch)`\n\n如果组件里想直接消费这个 store，可以用：\n\n```ts\nconst values = useGenieEditorReactTweakStore(tweakStore);\n```\n\n### 2. schema\n\nschema 完全由宿主决定。编辑器只识别通用字段描述，不提供业务字段白名单。\n\n```ts\nconst tweakSchema = {\n  title: '卡片配置',\n  description: '这些配置绑定到当前卡片实例。',\n  fields: [\n    {\n      key: 'title',\n      label: '标题',\n      type: 'text',\n    },\n    {\n      key: 'visible',\n      label: '显示',\n      type: 'switch',\n    },\n  ],\n} satisfies GenieEditorTweakSchema;\n```\n\n当前支持的 `type`：\n\n- `text`\n- `number`\n- `slider`\n- `select`\n- `card`\n- `checkbox`\n- `switch`\n- `color`\n\n其中 `card` 适合表达纵向单选卡片。每个选项使用：\n\n- `label` 作为卡片标题\n- `description` 作为卡片描述\n- `value` 作为实际回写值\n\n### 3. 注册到全局 tweak protocol\n\n最简单的 React 用法是直接使用 `useRegisterGenieEditorTweak(...)`：\n\n```tsx\nimport React from 'react';\nimport {\n  createGenieEditorReactTweakStore,\n  useRegisterGenieEditorTweak,\n} from '@axhub/genie-editor-client';\nimport type { GenieEditorTweakSchema } from '@axhub/genie-editor-client';\n\nconst tweakSchema: GenieEditorTweakSchema = {\n  title: '卡片配置',\n  fields: [\n    { key: 'title', label: '标题', type: 'text' },\n    { key: 'visible', label: '显示', type: 'switch' },\n  ],\n};\n\nexport function SalesCard() {\n  const rootRef = React.useRef<HTMLDivElement | null>(null);\n  const tweakStore = React.useMemo(\n    () =>\n      createGenieEditorReactTweakStore({\n        title: '本周销售额',\n        visible: true,\n      }),\n    [],\n  );\n\n  useRegisterGenieEditorTweak({\n    elementRef: rootRef,\n    schema: tweakSchema,\n    store: tweakStore,\n    onUpdate: async (patch) => {\n      tweakStore.update(patch);\n    },\n  });\n\n  return <div ref={rootRef}>...</div>;\n}\n```\n\n行为说明：\n\n- 只要 `elementRef.current` 和 `schema` 都可用，hook 就会注册 adapter\n- 组件卸载时会自动注销\n- 注册和注销都会自动通知 Genie Editor 刷新全局 tweak 列表\n- store 通过 `subscribe` 推送值变化时，Genie Editor 会自动回读最新 values\n- editor 选中该元素后，如果 `schema.fields.length > 0`，会显示 `\"调整\"` tab\n- `\"调整\"` tab 会排在 `\"样式\"` 前面\n\n这意味着大多数 React 场景不需要你手动调用刷新函数：\n\n- 组件挂载 / 卸载\n- HMR 导致 hook 重新注册\n- `store.set(...)` / `store.update(...)`\n\n这些都会自动反映到 Genie Editor。\n\n## 手动创建 adapter\n\n如果你不想用 hook，也可以手动创建 adapter：\n\n```ts\nimport { ensureGlobalGenieEditorTweakProtocol } from '@axhub/genie-editor-client';\nimport {\n  createGenieEditorReactTweakAdapter,\n  createGenieEditorReactTweakStore,\n} from '@axhub/genie-editor-client';\n\nconst element = document.querySelector('#sales-card') as Element;\nconst store = createGenieEditorReactTweakStore({ title: '销售额' });\n\nconst unregister = ensureGlobalGenieEditorTweakProtocol().register(\n  createGenieEditorReactTweakAdapter({\n    element,\n    schema: {\n      fields: [{ key: 'title', label: '标题', type: 'text' }],\n    },\n    store,\n    onUpdate: async (patch) => {\n      store.update(patch);\n    },\n  }),\n);\n```\n\n## 什么时候需要显式刷新\n\n如果你是“原地修改” schema，而不是让 hook / adapter 重新注册，Genie Editor 不一定知道字段结构已经变了。这类场景可以显式通知一次：\n\n```ts\nimport { notifyGlobalGenieEditorTweakProtocol } from '@axhub/genie-editor-client';\n\nmutableSchema.fields = [\n  { key: 'title', label: '标题', type: 'text' },\n  { key: 'theme', label: '主题色', type: 'color' },\n];\n\nnotifyGlobalGenieEditorTweakProtocol();\n```\n\n更适合显式刷新的场景：\n\n- 你在原对象上 push / splice `schema.fields`\n- 你改变了 adapter 的匹配逻辑，但没有重新注册\n- 你把多个外部数据源合并成一个 schema，变化发生在 React 之外\n\n如果你已经让 `useRegisterGenieEditorTweak(...)` 因为依赖变化而重新注册 adapter，通常就不需要再手动刷新。\n\n## 与 editor 的关系\n\n客户端包只负责注册和协议桥接。对外使用时，你只需要依赖 `@axhub/genie-editor-client`；如果页面里存在 Genie Editor 运行时，它会去消费这些全局注册的数据。\n\n当前仓库里的 editor 消费方式是：\n\n- editor 通过 `window.__AXHUB_GENIE_EDITOR_TWEAK_PROTOCOL__` 读取 adapter\n- editor 在 property panel 中读取 `getSchema(element)` / `getValues(element)`\n- editor 在用户修改时调用 `update(element, patch)`\n- editor 在 adapter 注册、注销、订阅更新、显式 `notify()` 后刷新 tweak 视图\n- editor 会把 tweak 改动写入变更摘要，并在 prompt 中排到样式前面\n\n## 约束\n\n- tweak 是元素级绑定，不是页面级全局配置\n- schema / values / update 必须由宿主控制\n- editor 不保证通用“恢复默认值”语义，当前回退仍然依赖宿主 adapter 接受回写 patch\n- 客户端包里的 React helper 只是便捷层，不会改变底层协议\n","readmeFilename":"README.md","_rev":"1-fefd5153e75931485e9c80417156164c"}