npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@cqsjjb/scene-engine

v0.0.1-alpha.5

Published

场景引擎组件,通过 iframe 嵌入场景引擎功能

Readme

@cqsjjb/scene-engine

场景引擎组件库,通过 iframe 嵌入场景引擎功能,支持富文本编辑和内容渲染。

📦 安装

npm install @cqsjjb/scene-engine
# 或
yarn add @cqsjjb/scene-engine
# 或
pnpm add @cqsjjb/scene-engine

🔧 依赖要求

该组件库需要以下 peer dependencies:

  • react: ^16.8.0 || ^17.0.0 || ^18.0.0
  • react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0
  • react-quill: ^1.3.0 || ^2.0.0

🚀 快速开始

SceneEngine - 场景引擎

场景引擎组件通过 iframe 嵌入场景引擎功能,支持新增和编辑场景。

import React, { useState } from 'react';
import { SceneEngine } from '@cqsjjb/scene-engine';

const App = () => {
  const [visible, setVisible] = useState(false);
  const [sceneId, setSceneId] = useState<string>();

  const handleOpenEditor = (id?: string) => {
    setSceneId(id);
    setVisible(true);
  };

  const handleClose = (data: {
    id: string;
    contentTitle: string;
    content: string;
    settingConfig: {
      [key: string]: any;
    };
  }) => {
    console.log('场景数据:', data);
    setVisible(false);
    // 处理保存逻辑
  };

  return (
    <div>
      <button onClick={() => handleOpenEditor()}>新增场景</button>
      <button onClick={() => handleOpenEditor('scene-123')}>编辑场景</button>
      
      <SceneEngine
        visible={visible}
        id={sceneId}
        host="http://localhost:5173"
        onClose={handleClose}
        style={{ zIndex: 9999 }}
      />
    </div>
  );
};

ContentRenderer - 内容渲染器

内容渲染器组件用于渲染富文本内容,适用于预览场景内容。

import React from 'react';
import { ContentRenderer } from '@cqsjjb/scene-engine';

const Preview = () => {
  const htmlContent = '<p>这是富文本内容</p>';

  return (
    <div>
      <h2>场景预览</h2>
      <ContentRenderer content={htmlContent} />
    </div>
  );
};

📚 API 文档

SceneEngine

场景引擎组件,通过 iframe 嵌入场景引擎功能。

Props

| 属性名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | style | React.CSSProperties | 否 | - | 自定义样式,默认组件使用固定定位全屏显示(z-index: 9999) | | host | string | 否 | window.location.origin | 主机地址(协议 + 域名 + 端口),用于构建 iframe 的源地址 | | src | string | 否 | host + '/article-layout' | iframe 的源地址,如果提供则优先级高于 host | | id | string | 否 | - | 场景 ID,未传则认为是新增场景,传入则认为是编辑场景 | | visible | boolean | 否 | true | 是否显示组件 | | onClose | (data: CloseData) => void | 否 | - | 关闭事件回调 |

CloseData 类型

interface CloseData {
  id: string; // 场景 ID
  contentTitle: string; // 场景的标题
  content: string; // 场景的内容(富文本 HTML)
  settingConfig: {
    [key: string]: any; // 场景的设置配置
  };
}

事件通信

组件通过 postMessage 与 iframe 内的编辑器进行通信:

  • 打开编辑器: 组件会在 iframe 加载完成后发送 EVENT_OPEN_SCENE_EDITOR 消息,消息格式:

    {
      type: 'EVENT_OPEN_SCENE_EDITOR',
      id: string // 场景 ID,新增时为空字符串
    }
  • 关闭编辑器: iframe 内编辑器发送 EVENT_CLOSE_SCENE_EDITOR 消息时触发 onClose 回调,消息格式:

    {
      type: 'EVENT_CLOSE_SCENE_EDITOR',
      id: string,
      contentTitle: string,
      content: string,
      settingConfig: Record<string, any>
    }

组件会验证消息来源,只处理来自 iframe 源地址的消息,确保通信安全。

ContentRenderer

内容渲染器组件,用于渲染富文本内容。组件会自动清洗 HTML 内容,确保内容以只读模式展示:

  • 移除所有包含 ql-material-image--selected 类的元素(清理图片选中状态)
  • 移除所有 contenteditable 属性(确保内容不可编辑)

Props

| 属性名 | 类型 | 必填 | 默认值 | 说明 | |--------|------|------|--------|------| | content | string | 否 | '' | 要渲染的 HTML 内容(富文本) |

💡 使用示例

完整示例

import React, { useState } from 'react';
import { SceneEngine, ContentRenderer } from '@cqsjjb/scene-engine';

interface SceneData {
  id: string;
  title: string;
  content: string;
  settingConfig: Record<string, any>;
}

const SceneManager = () => {
  const [editorVisible, setEditorVisible] = useState(false);
  const [editingSceneId, setEditingSceneId] = useState<string>();
  const [scenes, setScenes] = useState<SceneData[]>([]);
  const [previewContent, setPreviewContent] = useState<string>('');

  // 打开编辑器
  const handleOpenEditor = (sceneId?: string) => {
    setEditingSceneId(sceneId);
    setEditorVisible(true);
  };

  // 关闭编辑器并保存数据
  const handleCloseEditor = (data: {
    id: string;
    contentTitle: string;
    content: string;
    settingConfig: Record<string, any>;
  }) => {
    const sceneData: SceneData = {
      id: data.id,
      title: data.contentTitle,
      content: data.content,
      settingConfig: data.settingConfig,
    };

    // 更新场景列表
    const existingIndex = scenes.findIndex((s) => s.id === sceneData.id);
    if (existingIndex >= 0) {
      const updatedScenes = [...scenes];
      updatedScenes[existingIndex] = sceneData;
      setScenes(updatedScenes);
    } else {
      setScenes([...scenes, sceneData]);
    }

    setEditorVisible(false);
    setEditingSceneId(undefined);
  };

  // 预览场景
  const handlePreview = (scene: SceneData) => {
    setPreviewContent(scene.content);
  };

  return (
    <div>
      <div>
        <h2>场景列表</h2>
        <button onClick={() => handleOpenEditor()}>新增场景</button>
        {scenes.map((scene) => (
          <div key={scene.id}>
            <h3>{scene.title}</h3>
            <button onClick={() => handleOpenEditor(scene.id)}>编辑</button>
            <button onClick={() => handlePreview(scene)}>预览</button>
          </div>
        ))}
      </div>

      {previewContent && (
        <div>
          <h2>预览</h2>
          <ContentRenderer content={previewContent} />
        </div>
      )}

      <SceneEngine
        visible={editorVisible}
        id={editingSceneId}
        host="http://localhost:5173"
        onClose={handleCloseEditor}
        style={{ zIndex: 9999 }}
      />
    </div>
  );
};

export default SceneManager;

自定义 iframe 地址

<SceneEngine
  visible={true}
  src="https://example.com/custom-editor-path"
  onClose={handleClose}
/>

自定义样式

<SceneEngine
  visible={true}
  style={{
    zIndex: 10000,
    backgroundColor: '#f5f5f5',
  }}
  onClose={handleClose}
/>

🏗️ 构建

该组件库使用 Vite 构建,支持 ES Module 和 CommonJS 两种格式。

开发构建

npm run build

构建产物将输出到 publish 目录,包括:

  • index.esm.js - ES Module 格式
  • index.cjs.js - CommonJS 格式
  • index.d.ts - TypeScript 类型定义
  • index.css - 样式文件

发布

npm publish

发布前会自动执行 prepublishOnly 脚本进行构建。

📝 注意事项

  1. iframe 通信: 组件通过 postMessage 与 iframe 内的编辑器通信,确保 iframe 的源地址与 hostsrc 配置一致。组件会验证消息来源,只处理来自 iframe 源的消息。

  2. 样式隔离: 组件使用 createPortal 将编辑器渲染到 document.body,确保样式不受父组件影响。组件默认使用固定定位全屏显示(z-index: 9999)。

  3. 富文本内容: ContentRenderer 组件使用 dangerouslySetInnerHTML 渲染 HTML 内容,请确保内容来源可信,避免 XSS 攻击。组件会自动清洗 HTML 内容:

    • 移除所有包含 ql-material-image--selected 类的元素(清理图片选中状态)
    • 移除所有 contenteditable 属性(确保内容不可编辑)
  4. react-quill 样式: 组件已引入 react-quill/dist/quill.snow.css 和自定义样式,无需额外引入。

  5. 浏览器兼容性: 组件依赖现代浏览器 API(如 postMessagecreatePortal),建议在支持 ES2020 的浏览器中使用。

  6. 默认路径: 如果未提供 src 属性,组件会使用 host + '/scene-engine' 作为 iframe 的默认路径。

🔒 安全提示

  • 使用 ContentRenderer 渲染用户输入内容时,请确保对 HTML 内容进行安全过滤和转义
  • 在生产环境中,建议使用 CSP (Content Security Policy) 限制 iframe 的源地址