diff --git a/package.json b/package.json
index bab53b8..74193c6 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@kne/example-driver",
- "version": "0.1.17",
+ "version": "0.1.18",
"description": "用于在线展示和编辑React组件",
"syntax": {
"esmodules": true
@@ -16,11 +16,10 @@
"init-example": "modules-dev-libs-init",
"build:md": "npx @kne/md-doc",
"start:md": "npx @kne/md-doc --watch",
- "build:types": "cp src/index.d.ts dist/",
- "build:package-manifest": "cp src/package-manifest.json dist/",
- "build:lib-main": "microbundle --no-compress --format modern,cjs --jsx React.createElement --jsxFragment React.Fragment",
- "build:lib": "run-s build:lib-main build:types build:package-manifest",
- "start:lib": "microbundle watch --no-compress --format modern,cjs --jsx React.createElement --jsxFragment React.Fragment",
+ "build:locale": "microbundle src/locale/*.js -o dist/locale --no-compress --format modern,cjs ",
+ "build:lib-main": "microbundle --no-compress --format modern,cjs --jsxImportSource react --jsx React.createElement --jsxFragment React.Fragment",
+ "build:lib": "run-s build:locale build:lib-main",
+ "start:lib": "microbundle watch --no-compress --format modern,cjs --jsxImportSource react --jsx React.createElement --jsxFragment React.Fragment",
"build:example": "cd example && npm run build",
"start:example": "cd example && npm run start",
"test:build": "run-s build",
@@ -72,14 +71,15 @@
"husky": "^9.0.11",
"npm-run-all": "^4.1.5",
"prettier": "^2.5.1",
- "react-scripts": "^5.0.0",
- "sass": "^1.97.3",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-scripts": "^5.0.0",
+ "sass": "^1.97.3"
},
"dependencies": {
"@babel/standalone": "^7.24.0",
"@kne/react-error-boundary": "^0.1.1",
+ "@kne/react-intl": "^0.1.12",
"@monaco-editor/react": "^4.6.0",
"babel-standalone": "^6.26.0",
"classnames": "^2.3.1",
diff --git a/prompts/prompts-frontend-libs/README.md b/prompts/prompts-frontend-libs/README.md
new file mode 100644
index 0000000..33fe5be
--- /dev/null
+++ b/prompts/prompts-frontend-libs/README.md
@@ -0,0 +1,132 @@
+# Prompts Frontend Libs 文档索引
+
+本项目包含多个前端开发辅助提示词文档,用于指导 AI 完成常见的前端组件库开发任务。
+
+## 文档列表
+
+### 1. 组件国际化
+
+**功能**: 指导将 React 组件完成国际化,支持多语言切换
+
+**适用场景**:
+- 组件需要支持多语言显示
+- 项目需要面向国际用户
+- 现有组件需要添加国际化支持
+
+**核心内容**:
+- useIntl Hook 使用方法
+- withLocale HOC 包裹模式
+- 语言包配置规范
+- 不同组件类型的国际化处理方式
+
+**使用方式**: 阅读 [国际化](prompts/国际化.md) 了解详情
+
+---
+
+### 2. 添加 TypeScript 类型声明
+
+**功能**: 为现有的 React 组件库添加完整的 TypeScript 类型定义
+
+**适用场景**:
+- JavaScript 组件库需要添加 TypeScript 支持
+- 提升代码可维护性和 IDE 智能提示
+- 便于团队协作和代码审查
+
+**核心内容**:
+- Props 接口定义规范
+- 类型声明文件结构
+- 复杂场景处理(嵌套对象、函数属性、联合类型)
+- 组件导出声明模板
+
+**使用方式**: 阅读 [添加ts类型声明](prompts/添加ts类型声明.md) 了解详情
+
+---
+
+### 3. 完善 package.json 描述和关键词
+
+**功能**: 为项目完善 package.json 的 description 和 keywords 字段
+
+**适用场景**:
+- 新建项目需要完善 package.json 信息
+- 项目功能更新后需要调整描述
+- 提升 npm 包的搜索可发现性
+
+**核心内容**:
+- 描述撰写原则和模板
+- 关键词分类和提取来源
+- 信息收集和分析流程
+- 输出格式和检查清单
+
+**使用方式**: 阅读 [完善package.json描述和关键词](prompts/完善package.json描述和关键词.md) 了解详情
+
+---
+
+### 4. 生成包功能描述文件
+
+**功能**: 为 JavaScript/TypeScript 包生成 package-manifest.json 描述文件
+
+**适用场景**:
+- 自动化 API 文档生成
+- IDE 智能提示和自动补全
+- 包管理和分发平台的组件展示
+- 第三方工具的集成和分析
+
+**核心内容**:
+- 项目结构分析流程
+- 导出模块识别方法
+- JSON 结构构建规范
+- 类型定义标准和描述编写原则
+
+**使用方式**: 阅读 [生成包功能描述文件](prompts/生成包功能描述文件.md) 了解详情
+
+---
+
+### 5. 生成文档
+
+**功能**: 为项目生成标准化的 API 文档和概述文档
+
+**适用场景**:
+- 需要生成项目概述文档 (summary.md)
+- 需要生成 API 文档 (api.md)
+- 规范化项目文档结构
+
+**核心内容**:
+- 文档结构规范
+- 标题级别使用规范
+- 表格格式要求
+- 内容基于实际代码分析的原则
+
+**使用方式**: 阅读 [生成文档](prompts/生成文档.md) 了解详情
+
+---
+
+### 6. 组件示例编写
+
+**功能**: 编写遵循特定目录结构规范的 React 组件示例
+
+**适用场景**:
+- 为组件创建可运行的示例代码
+- 编写组件演示和文档
+- 构建组件库的示例展示系统
+
+**核心内容**:
+- 项目目录布局规范
+- example.json 配置结构
+- 示例代码编写模板
+- scope 依赖声明规则
+- 数据模拟方法
+
+**使用方式**: 阅读 [组件示例编写](prompts/组件示例编写.md) 了解详情
+
+---
+
+## 如何选择
+
+| 需求 | 使用文档 |
+|------|----------|
+| 组件需要支持多语言 | 组件国际化 |
+| 为 JS 组件库添加 TS 类型支持 | 添加 TypeScript 类型声明 |
+| 完善 package.json 描述和关键词 | 完善 package.json 描述和关键词 |
+| 生成包的 API 描述文件 | 生成包功能描述文件 |
+| 生成项目概述和 API 文档 | 生成文档 |
+| 编写组件演示示例代码 | 组件示例编写 |
diff --git "a/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md" "b/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md"
new file mode 100644
index 0000000..9ec750e
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\345\233\275\351\231\205\345\214\226.md"
@@ -0,0 +1,217 @@
+# 组件国际化指南
+
+## 概述
+
+本指南用于将组件完成国际化。
+
+## 一、创建的文件
+
+1. **`src/withLocale.js`** - 统一的国际化 HOC,所有组件共用
+2. **`src/locale/zh-CN.js`** - 中文语言包,所有组件共用
+3. **`src/locale/en-US.js`** - 英文语言包,所有组件共用
+
+## 二、需要修改的文件类型
+
+### 主组件文件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+### FormInner 表单组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+- 表单 label 国际化
+
+### getColumns 等工具函数
+- 通过参数接收 `formatMessage`
+- 移除内部的 `useIntl` 和 `withLocale` 引入
+
+### Action 操作组件
+- 添加 `useIntl` Hook
+- 用 `withLocale` 包裹导出
+
+## 三、国际化的关键模式
+
+### 1. useIntl Hook 使用
+```javascript
+import { useIntl } from '@kne/react-intl';
+
+const Component = () => {
+ const { formatMessage } = useIntl();
+ return
{formatMessage({ id: 'ComponentName.Key' })}
;
+};
+```
+
+### 2. withLocale 包裹普通组件
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return {formatMessage({ id: 'ComponentName.Key' })}
;
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+### 3. createWithRemoteLoader 组件(推荐格式)
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+**注意:** 对于 `createWithRemoteLoader` 创建的组件,可以先用 `ComponentInner` 存储,再用 `withLocale` 包裹。
+
+### 4. getColumns 等工具函数(formatMessage 从父组件传入)
+```javascript
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'name',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+
+// 父组件中使用
+const columns = getColumns({formatMessage});
+```
+
+### 5. 带参数的翻译
+```javascript
+formatMessage({ id: 'ComponentName.KeyWithParam' }, { name: value })
+```
+
+## 四、注意事项
+
+1. **所有使用 `useIntl` 的组件必须用 `withLocale` 包裹**
+2. **`getColumns` 等工具函数通过参数接收 `formatMessage`,不使用 `useIntl`**
+3. **语言包中避免重复的 key**,命名规则:`组件名 + 功能名`,如 `PersonalCard.status.online`
+4. **`createWithRemoteLoader` 创建的组件内部使用 useIntl 时,外层需要重命名并用 withLocale 包裹**
+5. 注意检查 `withLocale`文件的引用地址,根目录组件使用 `../withLocale`
+
+---
+
+# 组件国际化操作步骤
+
+## 步骤
+
+### 1. 更新语言包文件
+在 `src/locale/zh-CN.js` 和 `src/locale/en-US.js` 中添加对应的翻译文本:
+```javascript
+// zh-CN.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: '在线',
+ offline: '离线'
+ },
+ summary: '简介'
+ }
+};
+
+// en-US.js
+const locale = {
+ ComponentName: {
+ status: {
+ online: 'Online',
+ offline: 'Offline'
+ },
+ summary: 'Summary'
+ }
+};
+```
+
+### 2. 修改组件文件
+
+#### 主组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ComponentInner = ({ ...props }) => {
+ const { formatMessage } = useIntl();
+ // 将所有中文替换为 formatMessage({ id: 'ComponentName.Key' })
+ return (
+ // ...
+ );
+};
+
+const Component = withLocale(ComponentInner);
+
+export default Component;
+```
+
+#### FormInner 修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const FormInnerInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // label={formatMessage({ id: 'ComponentName.Key' })}
+ // ...
+});
+
+const FormInner = withLocale(FormInnerInner);
+
+export default FormInner;
+```
+
+#### Action 操作组件修改模式:
+```javascript
+import withLocale from '../withLocale';
+import { useIntl } from '@kne/react-intl';
+
+const ActionInner = createWithRemoteLoader({...})(({ remoteModules, ...props }) => {
+ const { formatMessage } = useIntl();
+ // ...
+});
+
+const ActionComponent = withLocale(ActionInner);
+
+export default ActionComponent;
+```
+
+#### getColumns 修改模式:
+```javascript
+// 移除 useIntl 和 withLocale 引入
+const getColumns = ({formatMessage}) => {
+ return [
+ {
+ name: 'xxx',
+ title: formatMessage({ id: 'ComponentName.Key' })
+ }
+ ];
+};
+```
+
+父组件中调用:`getColumns({formatMessage})`
+
+### 3. 语言包 key 命名规范
+- 避免重复,使用 `组件名+功能名` 格式,如 `PersonalCard.status.online`、`UserName`、`UserRole`、`SettingType`
+- 中文和英文语言包保持完全一致的 key 结构
+
+### 4. 检查要点
+- [ ] 所有使用 `useIntl` 的组件都用 `withLocale` 包裹
+- [ ] `getColumns` 等工具函数通过参数接收 `formatMessage`
+- [ ] 语言包中无重复 key
+- [ ] `withLocale` 引用路径正确(组件在子目录使用 `../withLocale`)
+
+### 5. 最后检查
+运行命令找到所有使用 useIntl 的文件,确保都已正确包裹:
+```bash
+grep -r "useIntl" src/ --include="*.js" -l
+```
diff --git "a/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md" "b/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md"
new file mode 100644
index 0000000..b6438cc
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\345\256\214\345\226\204package.json\346\217\217\350\277\260\345\222\214\345\205\263\351\224\256\350\257\215.md"
@@ -0,0 +1,142 @@
+# 完善 package.json 描述和关键词
+
+## 任务描述
+
+为 JavaScript/TypeScript 包项目的 `package.json` 文件完善 `description` 和 `keywords` 字段,使其准确反映项目的功能定位和核心特性。
+
+## 实施步骤
+
+### 1. 收集信息
+
+#### 1.1 分析文档目录
+- 阅读 `doc/api.md` 了解组件 API 和功能列表
+- 阅读 `README.md` 了解项目概述和主要特性
+- 查看 `doc/example.json` 了解示例配置
+
+#### 1.2 分析源码结构
+- 查看 `src/` 目录结构,识别导出的组件和模块
+- 分析主入口文件 `src/index.js` 的导出内容
+- 检查组件的 props 和功能特性
+
+#### 1.3 分析依赖关系
+- 查看 `package.json` 中的 `peerDependencies` 了解技术栈
+- 查看 `dependencies` 了解核心依赖库
+
+### 2. 撰写 description
+
+#### 2.1 描述结构
+```
+[核心定位] + [主要功能] + [适用场景]
+```
+
+#### 2.2 撰写原则
+- **简洁明了**:控制在 1-2 句话,不超过 100 个字符
+- **突出特色**:强调项目的独特价值
+- **用户视角**:从使用者角度描述功能
+- **避免技术细节**:不包含实现细节
+
+#### 2.3 描述模板
+
+**React 组件库模板**:
+```
+一个用于 [场景] 的 React 组件库,提供 [核心组件/功能],支持 [特性列表]。
+```
+
+**工具库模板**:
+```
+用于 [场景] 的 [类型] 工具库,提供 [核心功能],支持 [特性列表]。
+```
+
+### 3. 生成 keywords
+
+#### 3.1 关键词分类
+
+| 分类 | 示例 | 说明 |
+|------|------|------|
+| 核心功能 | `select`, `picker`, `dropdown` | 描述主要功能 |
+| 技术栈 | `react`, `antd`, `typescript` | 依赖的技术框架 |
+| 组件类型 | `component`, `ui`, `form` | 组件分类 |
+| 数据结构 | `tree`, `table`, `list` | 支持的数据展示形式 |
+| 特性 | `multiselect`, `searchable`, `i18n` | 核心特性标签 |
+| 场景 | `enterprise`, `admin`, `dashboard` | 适用场景 |
+
+#### 3.2 关键词数量
+- 建议数量:5-15 个
+- 按重要性排序
+- 包含英文和常见缩写
+
+#### 3.3 关键词提取来源
+
+1. **组件名称**:从导出的组件名称提取核心词汇
+2. **功能特性**:从支持的功能特性提取标签
+3. **技术依赖**:从 peerDependencies 和 dependencies 提取技术栈
+4. **使用场景**:从项目定位提取适用场景
+
+### 4. 输出格式
+
+#### 4.1 JSON 格式
+```json
+{
+ "description": "完善的描述内容",
+ "keywords": [
+ "keyword1",
+ "keyword2",
+ "keyword3"
+ ]
+}
+```
+
+#### 4.2 直接修改 package.json
+将生成的内容更新到 `package.json` 的对应字段。
+
+## 示例
+
+### 示例分析流程
+
+**步骤一:源码分析**
+- 识别 `src/index.js` 导出的所有模块和组件
+- 分析组件的 props、功能特性
+- 确定技术栈(如 React、Vue、TypeScript 等)
+
+**步骤二:文档分析**
+- 阅读 `README.md` 获取项目概述
+- 分析 `doc/api.md` 了解 API 功能列表
+- 查看示例代码理解使用场景
+
+**步骤三:生成结果**
+- 根据 分析结果撰写 description
+- 从组件功能、技术栈、特性等维度提取 keywords
+- 输出 JSON 格式的 description 和 keywords
+
+**示例输出格式**:
+```json
+{
+ "description": "一个用于 [场景] 的 React 组件库,提供 [核心功能],支持 [特性列表]",
+ "keywords": [
+ "react",
+ "component",
+ "功能关键词",
+ "技术栈关键词",
+ "特性关键词"
+ ]
+}
+```
+
+## 检查清单
+
+- [ ] description 长度适中(不超过 100 字符)
+- [ ] description 包含核心功能和特色
+- [ ] keywords 包含技术栈标签
+- [ ] keywords 包含功能标签
+- [ ] keywords 按重要性排序
+- [ ] 没有重复或冗余的关键词
+- [ ] 关键词使用小写字母
+- [ ] 关键词使用连字符分隔单词(如 `tree-select`)
+
+## 注意事项
+
+1. **避免过度宣传**:描述应客观准确,不使用夸张词汇
+2. **保持一致性**:关键词应与项目实际功能一致
+3. **考虑搜索优化**:使用 npm 搜索中常见的关键词
+4. **参考同类项目**:可参考 npm 上类似项目的关键词设置
+5. **定期更新**:当项目功能变化时,同步更新描述和关键词
diff --git "a/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md" "b/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
new file mode 100644
index 0000000..f6117f6
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\346\267\273\345\212\240ts\347\261\273\345\236\213\345\243\260\346\230\216.md"
@@ -0,0 +1,103 @@
+# 为React组件库添加TypeScript类型定义的通用提示词
+
+## 任务描述
+
+为一个已有的React组件库添加完整的TypeScript类型定义,包括所有组件的Props接口、类型声明和导出。
+
+## 实施步骤
+
+### 1. 创建类型定义文件
+
+在源代码目录创建`index.d.ts`文件,位置与主入口文件`index.js`同级。
+
+### 2. 分析组件结构
+
+- 查看所有组件文件的Props参数
+- 确定每个组件的输入属性类型
+- 识别回调函数的参数和返回值类型
+
+### 3. 定义基础类型
+
+```typescript
+import { ReactNode, ComponentType, FC } from 'react';
+```
+
+### 4. 为每个组件创建Props接口
+
+- 组件名 + Props 作为接口名(如:FormInfoProps)
+- 所有可选属性使用`?`标记
+- ReactNode类型用于支持字符串和JSX元素
+- 函数类型明确定义参数和返回值
+- 使用`[key: string]: any`支持动态属性
+
+### 5. 处理复杂场景
+
+- **嵌套对象属性**:定义内联对象类型
+- **函数属性**:明确参数类型和返回值类型
+- **联合类型**:使用`|`支持多种类型
+- **泛型组件**:使用ComponentType
+
+### 6. 导出组件类型声明
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 类型定义模板
+
+### 基础组件Props模板
+
+```typescript
+export interface ComponentNameProps {
+ className?: string;
+ children?: ReactNode;
+
+ // 其他属性...
+ [key: string]: any; // 支持动态属性
+}
+```
+
+### 函数回调模板
+
+```typescript
+onEvent ? : (data: any, context: any, ...args: any[]) => Promise | any;
+```
+
+### 组件声明模板
+
+```typescript
+export declare const ComponentName: FC;
+```
+
+## 最佳实践
+
+1. **类型完整性**:覆盖所有组件的公开API
+2. **向后兼容**:保持现有JavaScript代码的正常运行
+3. **可选属性**:合理使用可选属性,提供良好的开发体验
+4. **文档注释**:为复杂类型添加JSDoc注释
+5. **版本控制**:在package.json中指定types字段指向类型文件
+
+## 使用场景
+
+- 为现有组件库添加TypeScript支持
+- 提升代码可维护性和开发体验
+- 支持IDE智能提示和类型检查
+- 便于团队协作和代码审查
+
+## 示例项目结构
+
+```
+src/
+├── index.js # 主入口文件
+├── index.d.ts # 类型定义文件(新增)
+├── ComponentA.js # 组件A
+├── ComponentB.js # 组件B
+└── ...
+```
+
+## 注意事项
+
+- 类型定义文件应与实际组件实现保持同步
+- 考虑使用更具体的类型替代`any`类型
+- 定期检查和更新类型定义以匹配组件API的变化
+- 对于复杂的组件,考虑拆分类型定义到多个文件中
\ No newline at end of file
diff --git "a/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md" "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
new file mode 100644
index 0000000..39dfcd3
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\345\214\205\345\212\237\350\203\275\346\217\217\350\277\260\346\226\207\344\273\266.md"
@@ -0,0 +1,179 @@
+# 生成包功能描述文件(package-manifest.json)的通用提示词
+
+## 任务描述
+
+为一个JavaScript/TypeScript包项目生成`package-manifest.json`文件,该文件用于描述包的主要功能、导出模块、组件API等详细信息。
+
+## 实施步骤
+
+### 1. 分析项目结构
+
+- 查看`package.json`了解包的基本信息
+- 分析主入口文件(`index.js`/`index.ts`)的导出内容
+- 检查源代码目录结构,识别所有可导出的模块/组件
+
+### 2. 识别导出模块
+
+- 默认导出:通常标记为`default`
+- 命名导出:所有具名导出的组件、函数、类等
+
+### 3. 分析每个模块的功能
+
+对于每个导出的模块,需要收集以下信息:
+
+#### React组件
+- **description**: 组件的主要功能描述
+- **type**: 通常为`ReactNode`
+- **props**: 组件的所有属性,包括:
+ - 属性名
+ - 属性描述
+ - 属性类型(支持联合类型,如`string|number`,不能使用下面`类型定义规范`定义之外的类型)
+
+#### 函数/类
+- **description**: 功能描述
+- **type**: 返回值类型
+- **parameters**: 参数列表(如果有)
+- **properties**: 类属性(如果是类)
+
+#### 常量/对象
+- **description**: 用途描述
+- **type**: 数据类型
+- **properties**: 对象属性结构
+
+### 4. 构建JSON结构
+
+```json
+{
+ "description": "包的主要导出模块入口",
+ "modules": {
+ "模块名": {
+ "description": "模块功能描述",
+ "type": "类型声明",
+ // 根据模块类型可能包含以下字段
+ "props": {}, // React组件属性
+ "parameters": [], // 函数参数
+ "properties": {}, // 对象/类属性
+ "returns": "返回值类型" // 函数返回值
+ }
+ }
+}
+```
+
+## 类型定义规范
+
+### 基础类型
+- `string`: 字符串
+- `number`: 数字
+- `boolean`: 布尔值
+- `object`: 对象
+- `array`: 数组
+- `function`: 函数
+- `Date`: 日期对象
+- `Promise`: Promise对象
+- `RegExp`: 正则表达式
+- `Error`: 错误对象
+- `undefined`: 未定义
+- `null`: 空值
+- `any`: 任意类型
+- `void`: 无返回值
+- `unknown`: 未知类型
+- `ReactNode`: React节点(组件专用)
+
+### 联合类型
+使用`|`连接多种类型:
+- `string|ReactNode`: 字符串或React节点
+- `number|object`: 数字或对象
+- `function|null`: 函数或null
+
+### 复杂类型
+- `object[]`: 对象数组
+- `ReactNode[]`: React节点数组
+- `string[]`: 字符串数组
+
+## 优先级排序
+
+1. **主要组件**: 包中最核心、最常用的组件
+2. **辅助组件**: 支持性组件
+3. **工具函数**: 纯函数、工具方法
+4. **类型定义**: TypeScript类型、接口
+5. **常量配置**: 配置对象、常量
+
+## 属性命名约定
+
+### 通用属性
+- `className`: CSS类名
+- `children`: 子元素
+- `style`: 样式对象
+- `title`: 标题
+- `description`: 描述
+
+## 描述编写原则
+
+1. **简洁明确**: 一句话说明核心功能
+2. **用户视角**: 从使用者的角度描述
+3. **突出价值**: 说明解决了什么问题
+4. **避免实现细节**: 不描述内部实现逻辑
+
+## 示例格式
+
+### React组件示例
+```json
+{
+ "FormModal": {
+ "description": "模态框表单组件,在弹窗中展示表单",
+ "type": "ReactNode",
+ "props": {
+ "open": {
+ "description": "是否打开模态框",
+ "type": "boolean"
+ },
+ "title": {
+ "description": "模态框标题",
+ "type": "ReactNode"
+ },
+ "onSubmit": {
+ "description": "提交回调函数",
+ "type": "function"
+ }
+ }
+ }
+}
+```
+
+### 工具函数示例
+```json
+{
+ "formatDate": {
+ "description": "格式化日期字符串",
+ "type": "string",
+ "parameters": [
+ {
+ "name": "format",
+ "description": "格式化模板",
+ "type": "string"
+ }
+ ]
+ }
+}
+```
+
+## 文件位置
+
+将生成的`package-manifest.json`文件放置在:
+- `src/package-manifest.json`
+
+## 使用场景
+
+- 自动化API文档生成
+- IDE智能提示和自动补全
+- 包管理和分发平台的组件展示
+- 团队协作时的API参考
+- 第三方工具的集成和分析
+
+## 注意事项
+
+- 避免包含内部实现细节
+- 不要包含私有方法和属性
+- 确保向后兼容性
+- 定期更新以匹配代码变更
+- 考虑国际化需求(如需要)
\ No newline at end of file
diff --git "a/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md" "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md"
new file mode 100644
index 0000000..3476ca7
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\224\237\346\210\220\346\226\207\346\241\243.md"
@@ -0,0 +1,20 @@
+### 文档生成说明
+
+本次文档生成遵循以下规范:
+
+#### 文档结构
+
+1. **doc/summary.md** - 项目概述文档
+ - 使用 h3 及以下标题级别
+ - 包含:项目概述、主要特性、使用场景
+
+2. **doc/api.md** - API 文档
+ - 使用 h3 及以下标题级别
+ - 使用表格格式展示命令和参数
+ - 包含:命令行接口、程序化 API、输出结构说明
+
+#### 文档要求
+
+- 标题级别:仅使用 h3(###)及以下
+- 表格格式:属性名、类型/参数、默认值/返回值、说明
+- 内容基于实际代码分析,确保准确性
diff --git "a/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md" "b/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
new file mode 100644
index 0000000..df906c5
--- /dev/null
+++ "b/prompts/prompts-frontend-libs/\347\273\204\344\273\266\347\244\272\344\276\213\347\274\226\345\206\231.md"
@@ -0,0 +1,316 @@
+# 组件示例编写提示词
+
+本提示词用于编写遵循特定目录结构规范的 React 项目组件示例。
+
+## 1. 目录结构规范
+
+### 项目目录布局
+
+```
+{PROJECT_ROOT}/
+├── src/ # 源代码目录
+│ └── components/ # 组件源代码
+│ ├── ComponentA/ # 单个组件目录
+│ ├── ComponentB/ # 带子组件的目录
+│ │ ├── Sub1/
+│ │ ├── Sub2/
+│ │ └── index.js
+│ └── ...
+├── doc/ # 文档及示例目录(所有组件共用)
+│ ├── example.json # 示例配置文件(核心)
+│ ├── api.md # 组件API文档
+│ ├── summary.md # 组件简要描述
+│ ├── base.js # 基础示例代码
+│ ├── component-a.js # ComponentA示例
+│ ├── component-b.js # ComponentB主组件示例
+│ ├── component-b-sub1.js # ComponentB子组件Sub1示例
+│ ├── component-b-sub2.js # ComponentB子组件Sub2示例
+│ └── style.scss # 示例样式(可选)
+└── package.json # 项目配置文件
+```
+
+### 示例文件规则
+
+**重要**:所有组件的示例文件都统一放在项目根目录的 `doc/` 中,而不是在每个组件目录下创建单独的 `doc/` 目录。
+
+- 单个组件:使用组件名的小写短横线形式命名示例文件,如 `component-a.js`
+- 带子组件的组件:主组件示例使用组件名,子组件示例使用组件名加子组件名,如 `component-b.js`、`component-b-sub1.js`
+
+### 项目配置变量
+
+在编写示例时需要替换以下变量:
+
+| 变量名 | 说明 | 示例值 |
+|--------|------|--------|
+| `{PACKAGE_NAME}` | 从 package.json 的 name 字段获取 | `@kne/react-file` |
+| `{PROJECT_NAME}` | 项目名称,用于 getPublicPath | `react-file` |
+| `{LIB_NAME}` | 组件库名称,通常是驼峰形式 | `ReactFile` |
+
+## 2. example.json 配置结构
+
+所有组件的示例配置统一在 `doc/example.json` 中管理:
+
+```json
+{
+ "isFull": true,
+ "list": [
+ {
+ "title": "ComponentA",
+ "description": "组件A的描述",
+ "code": "./component-a.js",
+ "scope": [
+ {
+ "name": "_{LIB_NAME}",
+ "packageName": "{PACKAGE_NAME}",
+ "importStatement": "import * as _{LIB_NAME} from \"{PACKAGE_NAME}\""
+ },
+ {
+ "packageName": "{PACKAGE_NAME}/dist/index.css"
+ },
+ {
+ "name": "antd",
+ "packageName": "antd"
+ },
+ {
+ "name": "remoteLoader",
+ "packageName": "@kne/remote-loader"
+ }
+ ]
+ },
+ {
+ "title": "ComponentB",
+ "description": "组件B主组件",
+ "code": "./component-b.js",
+ "scope": []
+ },
+ {
+ "title": "ComponentB-Sub1",
+ "description": "组件B子组件Sub1",
+ "code": "./component-b-sub1.js",
+ "scope": []
+ }
+ ]
+}
+```
+
+**重要说明**:
+- `packageName` 可以使用当前组件包名的特殊格式:`@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/react-file` 项目可以使用 `@kne/current-lib_react-file` 来引用当前正在开发的组件包
+- 这种特殊写法用于文档示例系统自动解析当前包名
+
+## 3. 示例代码规范
+
+### 示例编写原则
+
+**重要原则**:
+
+1. **充分展示API功能**:示例代码要尽可能完整地展示组件的所有API功能和配置选项,包括:
+ - 组件的所有核心属性(props)及其不同取值
+ - 事件回调函数的完整实现
+ - 不同配置模式下的使用场景
+ - 组件间的组合使用方式
+ - 各种边界情况和特殊场景
+
+2. **贴近业务场景的示例数据**:示例数据应尽量模拟真实的业务场景,避免过于简化的假数据:
+ - 使用真实的字段名称和数据结构
+ - 包含合理的业务上下文信息
+ - 体现实际应用中的复杂度和多样性
+ - 模拟真实接口返回的数据格式
+
+### 导入方式
+
+- 使用 `scope` 中声明的变量名:`const { FilePreview } = _{LIB_NAME};`
+- 工具包引用:`const { createWithRemoteLoader, getPublicPath } = remoteLoader;`
+
+### 标准示例模板(需要 PureGlobal 模拟数据)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+const BaseExample = createWithRemoteLoader({
+ modules: ['components-core:Global@PureGlobal', 'components-core:InfoPage']
+})(({ remoteModules }) => {
+ const [PureGlobal, InfoPage] = remoteModules;
+ return (
+ {
+ return { data: { code: 0, data: api.loader() } };
+ },
+ apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 模拟数据
+ return 'mock-url';
+ }
+ }
+ }
+ }
+ }}>
+
+
+
+
+
+
+ );
+});
+
+render();
+```
+
+### 简化示例模板(无需远程加载)
+
+```javascript
+const { YourComponent } = _{LIB_NAME};
+const { Flex, Switch } = antd;
+const { useState } = React;
+
+const BaseExample = () => {
+ const [state, setState] = useState(false);
+ return (
+
+
+
+ );
+};
+
+render();
+```
+
+## 4. scope 依赖声明规则
+
+| 场景 | name | packageName | importStatement(可选) |
+|------|------|-------------|------------------------|
+| 项目组件 | `_{LIB_NAME}` | `{PACKAGE_NAME}` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}` | `import * as _{LIB_NAME} from "{PACKAGE_NAME}"` |
+| 样式文件 | - | `{PACKAGE_NAME}/dist/index.css` 或 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}/dist/index.css` | - |
+| 远程加载器 | `remoteLoader` | `@kne/remote-loader` | - |
+| Antd组件 | `antd` | `antd` | - |
+| React原生 | `React` | - | -(无需声明) |
+
+**当前包引用规则**:
+- 在 `example.json` 的 `scope` 中引用当前正在开发的组件包时,使用 `@kne/current-lib_{PACKAGE_NAME_SUFFIX}`
+- 例如:`@kne/info-page` 项目应使用 `@kne/current-lib_info-page`
+- 这样可以让文档系统自动识别并加载当前开发中的组件版本
+
+## 5. API文档编写
+
+`doc/api.md` 中包含所有组件的API文档,使用表格格式:
+
+```markdown
+### 组件名称
+
+组件描述
+
+#### 属性
+
+| 属性 | 类型 | 默认值 | 描述 |
+|------|------|-------|------|
+| propName | string | - | 属性描述 |
+```
+
+## 6. 数据模拟
+
+在示例中使用 PureGlobal 的 preset 模拟数据:
+
+```javascript
+apis: {
+ file: {
+ staticUrl: getPublicPath('{PROJECT_NAME}') || window.PUBLIC_URL,
+ getUrl: {
+ loader: async ({ params }) => {
+ // 根据 params 返回对应的 mock 数据
+ const urlMap = {
+ 1: '/mock/example.png',
+ 2: '/mock/example.pdf'
+ };
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(urlMap[params.id]);
+ }, 500);
+ });
+ }
+ }
+ }
+}
+```
+
+## 7. 组件导出规范
+
+在组件的主入口 `index.js` 中,需要导出所有子组件:
+
+```javascript
+export { default } from './MainComponent';
+export { default as SubComponent1 } from './SubComponent1';
+export { default as SubComponent2 } from './SubComponent2';
+```
+
+在示例中通过解构引用:
+
+```javascript
+const { MainComponent, SubComponent1, SubComponent2 } = _{LIB_NAME};
+```
+
+## 8. 使用说明
+
+### 创建新组件示例步骤
+
+1. 在 `src/components/` 下创建或编辑组件
+2. 在 `doc/example.json` 中添加示例配置
+3. 在 `doc/` 目录下创建对应的 `.js` 示例文件
+4. 在 `doc/api.md` 中添加组件API文档(如果是新组件)
+5. 在组件的 `index.js` 中确保正确导出
+
+### 变量替换示例
+
+假设项目 `package.json` 中配置为:
+
+```json
+{
+ "name": "@your-org/your-project"
+}
+```
+
+则变量值为:
+
+- `{PACKAGE_NAME}` → `@your-org/your-project`
+- `{PROJECT_NAME}` → `your-project`
+- `{LIB_NAME}` → `YourProject`
+- `{PACKAGE_NAME_SUFFIX}` → `your-project`(用于当前包引用)
+
+实际使用时需要替换为:
+
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@your-org/your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+**引用当前开发包时的特殊写法**:
+```json
+{
+ "name": "_YourProject",
+ "packageName": "@kne/current-lib_your-project",
+ "importStatement": "import * as _YourProject from \"@your-org/your-project\""
+}
+```
+
+```javascript
+const { YourComponent } = _YourProject;
+const { createWithRemoteLoader, getPublicPath } = remoteLoader;
+
+// ...
+staticUrl: getPublicPath('your-project') || window.PUBLIC_URL,
+```
+
+## 9. 项目参考
+
+本项目的示例文件位于 `doc/` 目录:
+
+- `doc/example.json` - 所有组件的示例配置
+- `doc/api.md` - 所有组件的API文档
+- `doc/summary.md` - 项目概述和核心特性说明
\ No newline at end of file
diff --git a/prompts/prompts.json b/prompts/prompts.json
new file mode 100644
index 0000000..3d07349
--- /dev/null
+++ b/prompts/prompts.json
@@ -0,0 +1,3 @@
+{
+ "@kne/prompts-frontend-libs": "1.0.1"
+}
diff --git "a/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md" "b/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md"
new file mode 100644
index 0000000..b7f99c7
--- /dev/null
+++ "b/prompts/\347\224\237\346\210\220README\347\264\242\345\274\225.md"
@@ -0,0 +1,65 @@
+### 文档索引生成指南
+
+#### 概述
+
+本文档描述如何从包含多个子目录的文档目录生成结构清晰、便于查阅的索引文档。
+
+#### 生成流程
+
+##### 第一步:扫描源目录
+
+识别当前文件所在目录中的所有子目录和文档文件中的所有README.md文件。
+
+##### 第二步:分析子目录结构
+
+对每个README.md进行内容分析:
+
+- 提取文档集合的核心用途和主要特性
+- 记录已安装的版本信息(如存在 prompts.json)
+
+##### 第三步:设计索引结构
+
+采用统一的模板组织信息:
+
+```markdown
+### 序号. 文档名称
+
+**功能**: 简要描述
+
+**适用场景**:
+- 场景一
+- 场景二
+
+**核心内容**:
+- 内容点一
+- 内容点二
+```
+
+##### 第四步:生成索引文档
+
+按以下结构组织最终文档:
+
+1. **项目标题与简介**:说明文档集合的用途
+2. **文档集合列表**:按子目录分组展示各文档摘要
+3. **快速选择指南**:帮助用户根据需求快速定位
+
+##### 第五步:添加导航辅助
+
+在文末添加决策表格,帮助用户根据需求快速定位:
+
+| 需求 | 推荐文档 | 所属集合 |
+|------|----------|----------|
+| 具体需求描述 | 对应文档名称 | 所属子目录 |
+
+#### 设计原则
+
+1. **信息层次清晰**:使用标题层级区分结构,使用加粗、列表等格式突出重点
+2. **内容适度抽象**:不直接复制原文内容,提炼核心要点
+3. **便于快速检索**:提供功能定位、明确适用场景、添加决策辅助表格
+4. **保持可维护性**:结构化模板便于更新,格式统一减少维护成本
+
+#### 扩展建议
+
+- **版本管理**:可在文档列表中添加版本号和更新日期
+- **标签系统**:为文档添加标签便于分类检索
+- **依赖关系**:说明文档之间的关联或前置依赖
diff --git a/src/components/CodePanel.js b/src/components/CodePanel.js
new file mode 100644
index 0000000..d5edbd1
--- /dev/null
+++ b/src/components/CodePanel.js
@@ -0,0 +1,25 @@
+import React from 'react';
+import {useIntl} from '@kne/react-intl';
+import HighlightCode from './HighlightCode';
+import ErrorComponent from './ErrorComponent';
+import CodeEditor from '@monaco-editor/react';
+
+const generateImportCode = (scope) => {
+ return scope.map(({name, packageName, importStatement}) =>
+ importStatement ? importStatement : (name ? `import * as ${name} from '${packageName}';` : `import '${packageName}';`)
+ ).join('\n');
+};
+
+const CodePanelInner = ({code, scope, error, editable, onChange}) => {
+ const {formatMessage} = useIntl();
+ return ();
+};
+
+export default CodePanelInner;
diff --git a/src/components/DescriptionBar.js b/src/components/DescriptionBar.js
new file mode 100644
index 0000000..8e84110
--- /dev/null
+++ b/src/components/DescriptionBar.js
@@ -0,0 +1,13 @@
+import React from 'react';
+
+const DescriptionBar = ({title, description, codeOpen, onToggle}) => {
+ return (
+
{title}
+
+
+ {codeOpen ? '>' : '< >'}
+
+
);
+};
+
+export default DescriptionBar;
diff --git a/src/components/DriverItem.js b/src/components/DriverItem.js
new file mode 100644
index 0000000..b7ed723
--- /dev/null
+++ b/src/components/DriverItem.js
@@ -0,0 +1,18 @@
+import React from 'react';
+import classnames from 'classnames';
+import uniqueId from 'lodash/uniqueId';
+import LiveCode from './LiveCode';
+import MiniCode from './MiniCode';
+
+const DriverItem = ({isFull, contextComponent, list}) => {
+ return
+ {list.map((props) => (
+
+ {props.hasOwnProperty('qrcodeUrl') ? :
+ }
+
+ ))}
+
;
+};
+
+export default DriverItem;
diff --git a/src/components/ErrorComponent.js b/src/components/ErrorComponent.js
new file mode 100644
index 0000000..054b5c5
--- /dev/null
+++ b/src/components/ErrorComponent.js
@@ -0,0 +1,9 @@
+import React, {memo} from 'react';
+
+const ErrorComponent = memo(({error}) => {
+ return (
+ {error &&
{typeof error === 'string' ? error : error?.message}}
+
);
+});
+
+export default ErrorComponent;
diff --git a/src/components/ExampleDriver.js b/src/components/ExampleDriver.js
new file mode 100644
index 0000000..7f4d45d
--- /dev/null
+++ b/src/components/ExampleDriver.js
@@ -0,0 +1,13 @@
+import React from 'react';
+import classnames from 'classnames';
+import DriverItem from './DriverItem';
+
+const ExampleDriver = ({list, isFull, contextComponent, className, ...props}) => {
+ const groupList = isFull === true ? [list] : [list.filter((_, index) => index % 2 === 0), list.filter((_, index) => index % 2 !== 0)];
+ return (
+ {groupList.map((item, index) => )}
+
);
+};
+
+export default ExampleDriver;
diff --git a/src/components/HighlightCode.js b/src/components/HighlightCode.js
new file mode 100644
index 0000000..3a87fc5
--- /dev/null
+++ b/src/components/HighlightCode.js
@@ -0,0 +1,19 @@
+import React from 'react';
+import Highlight, {Prism} from "prism-react-renderer";
+import theme from '../theme';
+
+const HighlightCode = ({code}) => {
+ return
+ {({tokens, getLineProps, getTokenProps}) => (<>
+ {tokens.map((line, i) => (
+ {line.map((token, key) => ())}
+
))}
+ >)}
+
+};
+
+export default HighlightCode;
diff --git a/src/components/LiveCode.js b/src/components/LiveCode.js
new file mode 100644
index 0000000..5833943
--- /dev/null
+++ b/src/components/LiveCode.js
@@ -0,0 +1,49 @@
+import React, {useEffect, useRef, useState, useMemo} from 'react';
+import ErrorBoundary from '@kne/react-error-boundary';
+import withLocale from '../withLocale';
+import {useInView, useLazyCompile, useReactRoot} from '../hooks';
+import DescriptionBar from './DescriptionBar';
+import CodePanel from './CodePanel';
+import ErrorComponent from './ErrorComponent';
+
+const LiveCodeInner = ({code, scope, title, description, contextComponent}) => {
+ const [_code, setCode] = useState(code);
+ const [codeOpen, setCodeOpen] = useState(false);
+ const containerRef = useRef(null);
+
+ const {shouldRender, heightRef} = useInView(containerRef);
+ const {compiledCode, error} = useLazyCompile(_code, shouldRender);
+
+ const currentScope = useMemo(() => scope.filter(({component, name}) => !!component && !!name), [scope]);
+
+ const [renderJsx, setRenderJsx] = useState(null);
+
+ useEffect(() => {
+ if (!compiledCode || !shouldRender) return;
+ try {
+ // eslint-disable-next-line no-new-func
+ const runnerFunction = new Function('React', 'render', ...currentScope.map(({name}) => name), compiledCode);
+ const Component = contextComponent || (({children}) => children);
+ runnerFunction(React, jsx => setRenderJsx(
+
+ {jsx}
+
+ ), ...currentScope.map(({component}) => component));
+ } catch (e) {
+ setRenderJsx(null);
+ }
+ }, [compiledCode, currentScope, contextComponent, shouldRender]);
+
+ useReactRoot(containerRef, shouldRender, renderJsx, heightRef);
+
+ return <>
+
+
setCodeOpen(!codeOpen)}/>
+ {codeOpen && }
+ >;
+};
+
+const LiveCode = withLocale(LiveCodeInner);
+
+export default LiveCode;
diff --git a/src/components/MiniCode.js b/src/components/MiniCode.js
new file mode 100644
index 0000000..3f28fc3
--- /dev/null
+++ b/src/components/MiniCode.js
@@ -0,0 +1,25 @@
+import React, {useState} from 'react';
+import {useIntl} from '@kne/react-intl';
+import withLocale from '../withLocale';
+import DescriptionBar from './DescriptionBar';
+import CodePanel from './CodePanel';
+
+const MiniCodeInner = ({code, qrcodeUrl, scope, title, description}) => {
+ const {formatMessage} = useIntl();
+ const [codeOpen, setCodeOpen] = useState(false);
+ return <>
+
+
+
+
{formatMessage({id: 'MiniCode.scanQrcode'})}
+
+
+ setCodeOpen(!codeOpen)}/>
+ {codeOpen && }
+ >;
+};
+
+const MiniCode = withLocale(MiniCodeInner);
+
+export default MiniCode;
diff --git a/src/components/index.js b/src/components/index.js
new file mode 100644
index 0000000..861263c
--- /dev/null
+++ b/src/components/index.js
@@ -0,0 +1,8 @@
+export {default as ExampleDriver} from './ExampleDriver';
+export {default as DriverItem} from './DriverItem';
+export {default as LiveCode} from './LiveCode';
+export {default as MiniCode} from './MiniCode';
+export {default as CodePanel} from './CodePanel';
+export {default as DescriptionBar} from './DescriptionBar';
+export {default as HighlightCode} from './HighlightCode';
+export {default as ErrorComponent} from './ErrorComponent';
diff --git a/src/hooks/index.js b/src/hooks/index.js
new file mode 100644
index 0000000..6bb310a
--- /dev/null
+++ b/src/hooks/index.js
@@ -0,0 +1,3 @@
+export {default as useLazyCompile} from './useLazyCompile';
+export {default as useInView} from './useInView';
+export {default as useReactRoot} from './useReactRoot';
diff --git a/src/hooks/useInView.js b/src/hooks/useInView.js
new file mode 100644
index 0000000..cefeac7
--- /dev/null
+++ b/src/hooks/useInView.js
@@ -0,0 +1,33 @@
+import {useState, useEffect, useRef} from 'react';
+
+const useInView = (ref) => {
+ const [shouldRender, setShouldRender] = useState(false);
+ const heightRef = useRef(0);
+
+ useEffect(() => {
+ const container = ref.current;
+ if (!container) return;
+
+ const observer = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ setShouldRender(true);
+ }
+ if (!entry.isIntersecting && entry.intersectionRatio === 0) {
+ const h = container.getBoundingClientRect().height;
+ if (h > 0) {
+ heightRef.current = h;
+ }
+ setShouldRender(false);
+ }
+ });
+ }, {threshold: [0]});
+
+ observer.observe(container);
+ return () => observer.disconnect();
+ }, [ref]);
+
+ return {shouldRender, heightRef};
+};
+
+export default useInView;
diff --git a/src/hooks/useLazyCompile.js b/src/hooks/useLazyCompile.js
new file mode 100644
index 0000000..dac99e0
--- /dev/null
+++ b/src/hooks/useLazyCompile.js
@@ -0,0 +1,33 @@
+import {useState, useEffect} from 'react';
+import {transform as _transform} from '@babel/standalone';
+import {useDebouncedCallback} from 'use-debounce';
+
+const useLazyCompile = (code, shouldCompile) => {
+ const [compiledCode, setCompiledCode] = useState(null);
+ const [error, setError] = useState(null);
+
+ const compileCode = useDebouncedCallback((codeToCompile) => {
+ if (!codeToCompile) {
+ setCompiledCode(null);
+ return;
+ }
+ try {
+ setError(null);
+ const transformCode = _transform(codeToCompile, {presets: ['es2015', 'react']}).code;
+ setCompiledCode(transformCode);
+ } catch (e) {
+ setError(e);
+ setCompiledCode(null);
+ }
+ }, 500);
+
+ useEffect(() => {
+ if (shouldCompile) {
+ compileCode(code);
+ }
+ }, [code, shouldCompile, compileCode]);
+
+ return {compiledCode, error};
+};
+
+export default useLazyCompile;
diff --git a/src/hooks/useReactRoot.js b/src/hooks/useReactRoot.js
new file mode 100644
index 0000000..b1bcf34
--- /dev/null
+++ b/src/hooks/useReactRoot.js
@@ -0,0 +1,50 @@
+import {useEffect, useRef} from 'react';
+import {createRoot} from 'react-dom/client';
+
+const useReactRoot = (containerRef, shouldRender, renderJsx, heightRef) => {
+ const reactRootRef = useRef(null);
+
+ useEffect(() => {
+ const container = containerRef.current;
+ if (!container) return;
+
+ if (!shouldRender) {
+ if (reactRootRef.current) {
+ const root = reactRootRef.current;
+ const savedHeight = heightRef.current;
+ reactRootRef.current = null;
+
+ setTimeout(() => {
+ root.unmount();
+ container.innerHTML = '';
+ const placeholder = document.createElement('div');
+ placeholder.className = 'example-driver-placeholder';
+ placeholder.style.height = savedHeight + 'px';
+ container.appendChild(placeholder);
+ }, 0);
+ }
+ return;
+ }
+
+ const wasPlaceholder = container.querySelector('.example-driver-placeholder');
+ if (wasPlaceholder) {
+ wasPlaceholder.remove();
+ }
+
+ const root = document.createElement('div');
+ root.className = 'example-driver-runner';
+ container.appendChild(root);
+ const reactRoot = createRoot(root);
+ reactRootRef.current = reactRoot;
+ reactRoot.render(renderJsx);
+
+ requestAnimationFrame(() => {
+ const h = container.getBoundingClientRect().height;
+ if (h > 0) {
+ heightRef.current = h;
+ }
+ });
+ }, [shouldRender, renderJsx, containerRef, heightRef]);
+};
+
+export default useReactRoot;
diff --git a/src/index.js b/src/index.js
index 144956a..93cc7be 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,235 +1,6 @@
-import React, {useEffect, useRef, useState, useMemo, memo} from 'react';
-import {createRoot} from 'react-dom/client';
-import classnames from 'classnames';
-import theme from './theme';
-import Highlight, {Prism} from "prism-react-renderer";
-import ErrorBoundary from '@kne/react-error-boundary';
-import uniqueId from 'lodash/uniqueId';
-import {transform as _transform} from '@babel/standalone';
-import CodeEditor from '@monaco-editor/react';
+import ExampleDriver from './components/ExampleDriver';
import monacoLoader from '@monaco-editor/loader';
-import {useDebouncedCallback} from 'use-debounce';
-import './style.scss'
-
-const HighlightCode = ({code}) => {
- return
- {({tokens, getLineProps, getTokenProps}) => (<>
- {tokens.map((line, i) => (
- {line.map((token, key) => ())}
-
))}
- >)}
-
-};
-
-const ErrorComponent = memo(({error}) => {
- return (
- {error &&
{typeof error === 'string' ? error : error?.message}}
-
);
-});
-
-const LiveCode = ({code, scope, title, description, contextComponent}) => {
- const [_code, setCode] = useState(code), [error, setError] = useState(null), [codeOpen, setCodeOpen] = useState(false), [minHeight, setMinHeight] = useState(0),
- containerRef = useRef(null);
- const reactRootRef = useRef(null);
- const [renderJsx, setRenderJsx] = useState(null);
- const [compiledCode, setCompiledCode] = useState(null);
- const [shouldRender, setShouldRender] = useState(false);
- const currentScope = useMemo(() => scope.filter(({component, name}) => !!component && !!name), [scope]);
-
- // 防抖编译函数,避免频繁编译
- const compileCode = useDebouncedCallback((codeToCompile) => {
- if (!codeToCompile) {
- setCompiledCode(null);
- return;
- }
-
- try {
- setError(null);
- const transformCode = _transform(codeToCompile, {presets: ['es2015', 'react']}).code;
- setCompiledCode(transformCode);
- } catch (e) {
- setError(e);
- setCompiledCode(null);
- }
- }, 500);
-
- useEffect(() => {
- compileCode(_code);
- }, [_code, compileCode]);
-
- // 视口检测
- useEffect(() => {
- const container = containerRef.current;
- if (!container) return;
-
- const observer = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- // 部分进入视口时开始渲染
- if (entry.isIntersecting) {
- setShouldRender(true);
- }
- // 完全离开视口时先清空 renderJsx 让 React unmount
- if (!entry.isIntersecting && entry.intersectionRatio === 0) {
- setRenderJsx(null);
- setShouldRender(false);
- }
- });
- }, {threshold: [0, 1]});
-
- observer.observe(container);
-
- return () => {
- observer.disconnect();
- };
- }, []);
-
- useEffect(() => {
- if (!compiledCode || !shouldRender) return;
-
- try {
- setError(null);
- // eslint-disable-next-line no-new-func
- const runnerFunction = new Function('React', 'render', ...currentScope.map(({name}) => name), compiledCode)
- const Component = contextComponent || (({children}) => {
- return children;
- });
- runnerFunction(React, jsx => setRenderJsx({jsx}), ...currentScope.map(({component}) => component));
- } catch (e) {
- setError(e);
- }
- }, [compiledCode, currentScope, contextComponent, shouldRender]);
-
- // 渲染逻辑 - 统一处理渲染和卸载
- useEffect(() => {
- const container = containerRef.current;
- if (!container) return;
-
- // 需要卸载时(shouldRender 为 false 或 renderJsx 为 null)
- if (!shouldRender || !renderJsx) {
- if (reactRootRef.current) {
- const root = reactRootRef.current;
- reactRootRef.current = null;
- // 使用微任务延迟卸载,等待 React 完成当前渲染周期
- queueMicrotask(() => {
- root.unmount();
- if (container) {
- container.innerHTML = '';
- }
- });
- }
- return;
- }
-
- // 渲染新内容
- if (reactRootRef.current) {
- reactRootRef.current.unmount();
- }
- container.innerHTML = '';
-
- const root = document.createElement('div');
- root.className = 'example-driver-runner';
- container.appendChild(root);
- const reactRoot = createRoot(root);
- reactRootRef.current = reactRoot;
- reactRoot.render(renderJsx);
- setMinHeight((height) => {
- return Math.max(height, root.getBoundingClientRect().height);
- });
- }, [shouldRender, renderJsx]);
-
- return <>
-
- {/* React 组件通过 createRoot 动态渲染到这里 */}
-
-
-
{title}
-
-
setCodeOpen(!codeOpen)}>
- {codeOpen ? '>' : '< >'}
-
-
- {codeOpen ? (<>
-
-
- importStatement ? importStatement : (name ? `import * as ${name} from '${packageName}';` : `import '${packageName}';`))
- .join('\n')}
-`}/>
-
-
-
- {error && }
- >) : null}
- >;
-};
-
-const MiniCode = ({code, qrcodeUrl, scope, title, description}) => {
- const [codeOpen, setCodeOpen] = useState(false);
- return <>
-
-
-
{title}
-
-
setCodeOpen(!codeOpen)}>
- {codeOpen ? '>' : '< >'}
-
-
- {codeOpen ? (<>
-
-
- importStatement ? importStatement : (name ? `import * as ${name} from '${packageName}';` : `import '${packageName}';`))
- .join('\n')}
-`}/>
-
-
-
- >) : null}
- >
-};
-
-const DriverItem = ({isFull, contextComponent, list}) => {
- return
- {list.map((props) => {
- return
- {props.hasOwnProperty('qrcodeUrl') ? :
- }
-
- })}
-
-};
-
-const ExampleDriver = ({list, isFull, contextComponent, className, ...props}) => {
- const groupList = isFull === true ? [list] : [list.filter((item, index) => index % 2 === 0), list.filter((item, index) => index % 2 !== 0)];
- return (
- {groupList.map((item, index) => )}
-
);
-};
+import './style.scss';
export default ExampleDriver;
export const config = monacoLoader.config;
diff --git a/src/locale/en-US.js b/src/locale/en-US.js
new file mode 100644
index 0000000..10cccc6
--- /dev/null
+++ b/src/locale/en-US.js
@@ -0,0 +1,7 @@
+const locale = {
+ 'CodePanel.loading': 'Loading code editor...',
+ 'MiniCode.example': 'Example',
+ 'MiniCode.scanQrcode': 'Please scan the QR code to view the example'
+};
+
+export default locale;
diff --git a/src/locale/zh-CN.js b/src/locale/zh-CN.js
new file mode 100644
index 0000000..d9e0e0e
--- /dev/null
+++ b/src/locale/zh-CN.js
@@ -0,0 +1,7 @@
+const locale = {
+ 'CodePanel.loading': '正在加载代码编辑器...',
+ 'MiniCode.example': '示例',
+ 'MiniCode.scanQrcode': '请扫描二维码查看示例程序'
+};
+
+export default locale;
diff --git a/src/withLocale.js b/src/withLocale.js
new file mode 100644
index 0000000..bfb5cb6
--- /dev/null
+++ b/src/withLocale.js
@@ -0,0 +1,14 @@
+import {createWithIntlProvider} from '@kne/react-intl';
+import zhCN from './locale/zh-CN';
+import enUS from './locale/en-US';
+
+const withLocale = createWithIntlProvider({
+ defaultLocale: 'zh-CN',
+ defaultMessage: zhCN,
+ messages: {
+ 'zh-CN': zhCN,
+ 'en-US': enUS
+ }
+});
+
+export default withLocale;