# 更新日志
## v0.9.211
### 修复
+ 修复JS兼容Tips 为空报错问题
## v0.9.210
### 优化
+ 优化AI代码块拆分逻辑
+ 优化回放自定义数据帧播放逻辑
### 修复
+ 修复切换环境可能存在文件写入失败问题
## v0.9.209
### 优化
+ 优化LSP开启关闭状态逻辑
## v0.9.208
### 优化
+ LSP 语法提示，添加Tab键进行选择选项
## v0.9.207
### 
+ 优化turnOnLSPServer 接口，广播对应事件给其他用户
## v0.9.206
### 优化
+ 优化在切题模式下， 开发者工具显示问题
## v0.9.203
### 优化
+ 浏览器组件打开新的Tab页Icon添加Tips
+ 开发者工具按钮只有在运行时才显示
+ 优化开发者工具按钮连续快速点击开发者工具没显示出来问题
+ 优化浏览器组件Iframe内容不展示问题
## v0.9.202
### 新增
+ 新增开发者工具显示与隐藏状态广播事件`devtoolStatusChange`,业务方可以根据该事件进行相应的UI调整等处理
```ts
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      switch (name) {
        case Messages.DevtoolStatusChange:
          messageBox.info(JSON.stringify(payload));
          break;
```
### 优化
+ 优化AI代码块拆分逻辑
+ 优化只有当Iframe 注入开发者工具成功后，才显示开发者工具按钮
+ 优化文件树操作按钮挂在到Body上
+ 优化AI改用户代码，取得权限，写入代码后的确认框的位置异常——按钮区域被遮挡了问题
+ 优化AI代码块标识高亮区域兼容不同的Editor字体模式
## v0.9.201
### 新增
+ 新增AI代码块标识区域，点击标识的高亮线任意位置，点击触发AI代码块菜单行为
### 优化
+ 优化AI代码块标识区域，去掉ICON，并且将代码块的标识范围移动到序号右侧位置
+ 优化AI代码块标识区域为一直显示（之前是hover才显示）
+ 优化开发者工具只有在小屏显示Icon的时候才显示Tooltips
## v0.9.200
### 新增
+ 新增关闭与打开LSP功能的接口`turnOnLSPServer`
```ts
daoPaasObj.turnOnLSPServer(true/false);
```
+ 新增AI修改AI代码块增加确认机制
+ 新增支持AI修改跨多个代码块修改，并进行代码块的拆分
+ 新增浏览器组件的响应式处理， `开发工具`在小屏的时候，显示ICON，并且新增Tips提示，并能跟随浏览器组件的大小变化进行切换
### 优化
+ 优化Tips在Windows下存在文字换行的问题
+ 优化AI代码块的相关交互动画流畅行为
 
## v0.9.199
### 新增
+ 新增`Editor` 空白页面可进行内容配置属性`editorBlankContent`
```ts
  const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      ...
      editorBlankContent: `
      <div class="blank_container"
      <div class="item">
        <div class="item-title">Show All Commands</div>
        <div class="item-shortCut">
          <div class="item-shortCut-item">⇧1</div>
          <div class="item-shortCut-item">⌘</div>
          <div class="item-shortCut-item">P</div>
        </div>
      </div>
    </div>
    ...
```
+ 新增`stopInputCode(isAutoConfirm);` 方法添加参数：true/false 用来配置AI代码块是否自动确认
### 优化
+ 优化代码块相关ICON样式问题
### 修复
+ 修复 AI代码块，偶现没有确认按钮，但是`getAllUnConfirmAIBlock`能获取到待确认打代码块信息
+ 修复 AI代码块，确认按钮偶现点击报错
+ 修复 回放代码高亮，引起的回放存在报错问题

## v0.9.198
### 新增
+ 新增AI虚拟用户下线接口`offlineVirtualUser`
  ```ts
  daoPaasObj.offlineVirtualUser();
  ```
### 优化
+ 优化AI代码块和答题区样式冲突问题
+ 优化AI代码显示的样式问题
+ 优化调用`stopInputCode` 后，自动跳转到确认和按钮区域
+ 优化Diff 代码块滚动到非确认按钮区域
+ 优化AI代码块操作按钮菜单， 如果没有配置的不显示弹框
## v0.9.197
### 新增
+ 新增回放功能， 高亮显示用户修改区域，复制粘贴代码区域，文件树出现**M**标识
+ 回放初始化SDK新增配置参数`reportHighlightData` 和 `replayFileChangeLogs`
```ts

    const dao = new DaoPaaS({
      paasDomain: process.env.PAAS_MANAGER_API_ORIGIN,
      tenantId: '3',
      ticket: dataRes?.ticket,
      ....
      reportHighlightData: {
        originDockerId: '13456',
        list: [
          {
            path: 'java.java',
            baseContent: '',
            arr: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13],
          },
          {
            path: 'ShowMeBug.java',
            baseContent: '/ 可以引⼊的库和版本相关请参考 ',
            arr: [12, 13, 14, 15, 16, 17, 18, 19], // 变更行数
          },
        ],
      },
      replayFileChangeLogs:  {
        [dockerId]:  ['filePath1', 'filePath2']
      }
    });
```
+ 新增`Browser` Iframe 显示`Devtool`(开发工具), 目前支持显示:`console`, `sources`,`network` 三个面板
## v0.9.196
### 重构
+ 重构AI代码块实现方案(DB存储)
### 优化
+ 优化IDEServer 文件找不到错误
+ 优化AI代码块确认和取消按钮可能点击不行问题
+ 优化创建新文件后，没有选中新建文件
+ 优化打开文件后， Editor没有自动聚焦， 不能直接输入内容问题
## v0.9.195
### 新增
+ 支持动态配置是否显示Editor Tooltips配置
```ts
daoPaasObj?.updateGlobalBaseConfig({
  hideEditorToolTip: true/false,
});
```
+ 支持动态配置是否显示Editor 右键菜单配置
```ts
daoPaasObj?.updateGlobalBaseConfig({
  hideEditorContextMenu: true/false,
});
```
+ 支持配置特殊语言不支持远程LSP，但是只用CodeMirror内置LSP，默认支持是`['html', 'css', 'less', 'sass']`,业务方也可以进行配置
```ts
 const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      ticket: dataRes?.ticket,
      defaultLspLang: ['html', 'css', 'less', 'sass'], // 会直接覆盖默认配置
```
+ 支持Editor fileHeader 通过配置，显示Tooltips, 显示时间3S
```ts
  fileHeader[0].showTips = true; // 对应时机设置对应的showTips=true,
  dao?.updateFileHeader([...fileHeader]);
```
+ 支持Editor fileHeader 通过配置，显示Tooltips配置消失时常参数`tipsDuration`
```ts
  {
      icon: {
        className: 'dao42__icon--layout2',
        tips: {
          [I18nLanguageType.ZH]: '预览Markdown',
          [I18nLanguageType.EN]: 'Preview Markdown',
        },
        action: () => {
          messageBox.success('预览Markdown');
        },
      },
      tipsDuration: 1000 * 4, // 不配置默认为： 100ms
    },
```
### 优化
+ 优化AI标识的内容，能够复制，并且复制的内容不包括AI标识
## v0.9.194
+ 支持自定义编辑器右键菜单配置`aiCodeMenu`
> 1. 新增内置菜单行为`cut`, ` copy`, `paste`,`removeAllFlags`（删除所有AI标识）,`removeFlag`（删除AI标识）,`remove`(删除代码块)
> 2. 支持内置菜单行为，自定义菜单行为
> 3. 支持菜单自定义样式
> 4. `aiCodeMenu`配置是一个**二维数组**，可以针对菜单进行分组显示
```ts
 const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      ...,
      aiCodeMenu: [
        [
          {
            text: {
              [I18nLanguageType.ZH]: '清空内容',
              [I18nLanguageType.EN]: 'Cut',
            },
            actionName: 'remove',
            shortcutClassName: '',
            withCodeFlag: true,
          },
          {
            text: {
              [I18nLanguageType.ZH]: '复制代码',
              [I18nLanguageType.EN]: 'Copy',
            },
            actionName: 'copy',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '粘贴代码',
              [I18nLanguageType.EN]: 'Paste',
            },
            actionName: 'paste',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '取消标识',
              [I18nLanguageType.EN]: 'Paste',
            },
            actionName: 'removeFlag',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '取消所有标识',
              [I18nLanguageType.EN]: 'Paste',
            },
            actionName: 'removeAllFlags',
          },
        ],
        [
          {
            text: {
              [I18nLanguageType.ZH]: '解释代码',
              [I18nLanguageType.EN]: 'Cut(Custom)',
            },
            shortcutKey: 'Ctrl/Cmd+X',
            actionFun: (view, commands) => {
              const selections = dao?.daoEditor.getSelection();
              console.log(selections);
              const tr = view.state.replaceSelection('replaceCode');
              view?.dispatch({
                ...tr,
              });
            },
          },
          {
            text: {
              [I18nLanguageType.ZH]: '生成代码',
              [I18nLanguageType.EN]: 'Cut(Custom)',
            },
            shortcutKey: 'Ctrl/Cmd+X',
            actionFun: (view, commands) => {
              // const tr = view.state.replaceSelection('');
              // view?.dispatch({
              //   ...tr,
              // });
            },
          },
          {
            text: {
              [I18nLanguageType.ZH]: '交换代码',
              [I18nLanguageType.EN]: 'Cut(Custom)',
            },
            shortcutKey: 'Ctrl/Cmd+X',
            actionFun: (view, commands) => {
              // const tr = view.state.replaceSelection('');
              // view?.dispatch({
              //   ...tr,
              // });
            },
          },
        ],
      ]
    });
```
+ 新增`getAllUnConfirmAIBlock`,用于获取待确认的代码块信息
```ts
   const res = await daoPaasObj?.daoEditor.getAllUnConfirmAIBlock(
                aiBlockPath,
              );
              messageBox.info(JSON.stringify(res));
            }}
```
+ 新增`removeAllAiFlags`,用于移除指定文件的所有AI标识
```ts
    const res = await daoPaasObj?.daoEditor.removeAllAiFlags(
                aiBlockPath,
              );
              messageBox.info(JSON.stringify(res));
            }}
```
### 优化
+ 优化`isEnableEditCode` 方法， 会返回对应代码块的`boundingClientRect` 等位置信息，方便业务方定位弹框位置
+ 优化AI代码块文件，全选能够删除AI代码块标识
+ 优化空白文件， AI编程，不需要确认弹框
+ 优化点击AI代码菜单按钮， 选中代码区域
+ 优化AI代码块高亮区域， 只有Hover/光标移至对应区域才显示
  
## v0.9.193
### 新增
+ 新增支持**AI**编程代码块功能（AI编程用`replayCodeByRange` 方法进行代码的操作）
+ 新增初始化配置参数`isSplitCode`,用于控制是否进行代码块的拆分
```ts
    const dao = new DaoPaaS({
      ...
      isSplitCode: true, // 用于控制是否判断进行代码块的拆分： 1024 配置true, 其他可以不用配置
      ...
    });
```
+ 新增`isEnableEditCode` 方法用于**AI**编程判断是否可以修改对应的代码块
```ts
   const res = await daoPaasObj?.daoEditor.isEnableEditCode({
        path: path, // 修改的文件路径
        selection: { start, end }, // 需要修改的范围
  });
    if (res.status) {
        modifyCode();
      } else {
        confirm({
          title:
            '当前需要修改的代码是【用户】创建的，是否运行【AI导师】执行修改任务如下所示?',
          icon: <ExclamationCircleFilled />,
          content: <div>任务进度1/3: 子弹的移动</div>,
          okText: '允许修改',
          cancelText: '停止修改',
          onOk() {
            modifyCode();
          },
          onCancel() {},
        });
      }
            
```
+ 新增`stopInputCode` 方法用于**AI**模式编程，主动告知AI结束编程
```ts
daoPaasObj.daoEditor.stopInputCode();
```
### 优化
+ 优化**AI**模式下编程， 不能打开其他文件， 不能创建文件，关闭文件等行为
+ 

## v0.9.192
### 优化
+ 文件树按住 shift 键时阻止一些默认事件
+ MarkDownPreview 组件预览排版样式(代码关键字高亮，分割线等)

### 新增
+ 消息 Messages.CloseRunLoading: 运行 Web 环境时关闭 loading
+ 方法 isShowPreviewMDIframe(value: boolean): 设置 MarkDownPreview 预览窗口是否显示

## v0.9.191
### 新增
+ 新增配置参数`customImagePreview` 参数， 业务方可控制MD图片预览的操作行为
```ts
  const dao = new DaoPaaS({
      paasDomain: process.env.PAAS_MANAGER_API_ORIGIN,
      tenantId: '3',
      ticket: dataRes?.ticket,
      customImagePreview: true,
      userInfo: {
        username: DemoUserInfo.name,
      },
    });
```
+ 新增监听`previewImage` 事件， 用于业务方自定义监听处理MD图片点击事件
```ts
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case Messages.PreviewImage: {
          messageBox.info('PreviewImage:' + JSON.stringify(payload));
          break;
        }
        ...
```
### 优化
+ 优化`Editor`协同操作逻辑
### 修复
+ 修复文件树存在文件打开失败问题
+ 修复`Codemirror` JS 报错问题
## v0.9.190
### 修复
+ 修复回放时打开文件可能存在文件不存在的问题
## v0.9.189
### 优化
+ 优化答题区域删除内容，必须保留一个空白行
+ 优化文件树，隐藏、测试用例等行为，在答题时的交互行为为无选中状态，并无hover 状态
+ 优化`Markdown`图片预览的操作按钮区域添加`d42-image-preivew-actions`类名，防止样式重写冲突问题
### 修复
+ 修复回放报错问题
## v0.9.188
### 新增
+ 新增`Markdown`预览页面，图片点击放大旋转等效果
### 优化
+ 优化`Editor`的`placeholder`多行时，光标过长问题
## v0.9.187
### 优化
+ 实现多人操作时，同步双方对文本的选中高亮效果
+ 修复 MarkDownPreview 组件的实时预览失效问题
+ 修复在无痕模式下使用 iframe 嵌入网页时 localStorage 不可访问的报错问题
## v0.9.186
### 新增
+ 新增`Editor`三连击，全选功能
### 优化
+ 关键词搜索控件按钮增加 tooltip 提示
+ 文件树多文件移动时自动过滤重名文件
+ 文件树中支持 Shfit 键对文件的选中操作
## v0.9.185
### 新增
+ 新增消息 Messages.BehindTooMuch
+ 新增方法 keyword_search_btn_replace_all
```ts
const dao = new DaoPaaS({});
// codemirror 本地版本落后太多时可监听到此消息
dao.onMessage((message: Message) => {
    case Messages.BehindTooMuch: 
    dao.syncServeDocForce(); // 强制同步服务端的文档及版本信息
    break;
});
```
### 优化
+ 优化多人编辑代码不同步的问题
+ 优化多人编辑掉线重连后代码不同步的问题 
## v0.9.184
### 新增
+ `replayCodeByRange` 支持虚拟用户输入显示光标，并跟随视角
+ 新增对`.ui`, `.svelte`,`.pro`后缀文件语法高亮
### 优化
+ 紧急修复编辑器代码不同步问题
## v0.9.182
### 新增
+ 新增AI虚拟用户自动跟随效果
+ 新增AI虚拟用户输入光标显示效果
### 优化
+ 优化OT的版本校验问题
## v0.9.181
### 新增
+ 新增文件树支持多个文件拖拽
+ 新增文件数支持在指定目录下上传文件 / 文件夹
+ 新增注册虚拟用户接口返回虚拟用户信息
+ 新增支持注册虚拟账户自定义头像
```ts
       const res = await daoPaasObj.registerVirtualUser({
                  name: '小AI ',
                  avatar: // 头像地址
                    'https://img1.baidu.com/it/u=4264793750,8370810&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=500',
                });
```
+ 新增清空`Markdown` 预览组件内容方法`resetMarkdownPreview`(业务方可以根据时机清除内容)
```ts
daoPaasObj?.daoEditor.resetMarkdownPreview();
```
### 优化
+ 编辑器默认字体调整为`16px`
+ 优化虚拟账号信息和实体账户信息统一(`username`)

## v0.9.180
### 新增
+ 新增当前用户添加虚拟用户接口`registerVirtualUser`
```ts
 const res = await daoPaasObj.registerVirtualUser(
        {
          name: '小AI ', // 定义虚拟用户名称
        }
      );
```
+ 新增当前用户和虚拟用户切换接口`switchVirtualUser`
```ts
  const res = await daoPaasObj?.switchVirtualUser(true/false);
```
+ 新增打开关键词全局搜索控件的方法`openKeywordSearch`
```ts
 daoPaasObj.openKeywordSearch(true); // 打开控件

```
+ 新增获取关键词全局搜索控件数据的方法`fetchKeywordSearchInfo`
```ts
  const data = daoPaasObj.fetchKeywordSearchInfo(); // 获取数据
```
### 优化
+ 优化`RunStatusChanged` 事件，返回更多运行结果信息
```ts
internalRunInfo: {
    "autoImport": {
        "output": "",
        "err": null,
        "duration": 0
    },
    "compile": {
        "output": "",
        "err": null,
        "duration": 6126
    },
    "run": {
        "output": "Hello World",
        "err": null,
        "duration": 2209
    }
}
```
+ 回滚文件系统到`Btrf`
  
## v0.9.179
### 新增
+ 新增添加文件或者文件夹方法`createFile`
```ts
    const isDir = true
     daoPaasObj.createFile(filePath, isDir).then(
        (v) => {
            messageBox.success('创建成功.');
        },
        (err) => {
            messageBox.error('创建失败.' + err);
          },
      );
```
+ 新增展开指定路径文件夹方法`expandByPath`
```ts
  daoPaasObj.expandByPath(expendFilePath).then(
                  (v) => {
                    return messageBox.success('展开成功.');
                  },
                  (err) => {
                    messageBox.error('展开失败.' + err);
                  },
                );
```
+ 新增获取编辑器实例方法，业务方可以根据Editor 实例开展灵活业务操作
```ts
 const editorIns = daoPaasObj?.daoEditor.getEditorInstance();
              if (!editorIns) {
                messageBox.error('No Editor Instance!');
                return;
              }
              messageBox.success(`Length: ${editorIns.state.doc.length}`);
              messageBox.success(`Doc: ${editorIns.state.doc.toJSON()}`);
```
+ 新增`replayCodeByRange` 方法，进行指定位置代码替换， 或者全文档清空内容，或者全文档内容替换
```ts
const res = await daoPaasObj?.daoEditor.replayCodeByRange({
                  path: replacePath, // 文件路径
                  content: encodeURI(replaceContent),// 替换的内容， 如果要清空文件内容， content 为空字符串，下面的selection 为空
                  selection: { start: 102, end: 158 } ,//  要替换的光标的位置，或者为空字符串， 则整个文档内容替换
                });
                messageBox.success(JSON.stringify(res));
```
+ 新增全项目文件搜索功能， 需要业务方配置对应的参数进行接入，默认不开启
```ts
    const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      // 全局配置
      globalConfig: {
        // 全局配置-关键词搜索
        searchKeywordConfig: {
          useSearch: true, // 是否开启搜索
          isShowHiddenFile: false, //搜索内容是否能包含隐藏内容
        }, 
      },
    });
```
### 优化
+ 优化`Ctrl+P`搜索文件关键词匹配权重问题
## v0.9.178
### 新增
+ 新增`Markdown` 预览对`a`标签的打开模式， 支持新的Tab页打开，也支持组件内部打开
```tsx
globalConfig: {
    markdownPreview: {
        iframeWidth: '100%', // 默认100%
        iframeHeight: '400', // 默认400
        isBlank: false, // 默认false
    }
}
```
+ `IDEServer` 文件系统的重构
### 优化
+ 优化回放倍速业务方自定义具体值
+ 优化浏览器组件`Ctrl+S`文案

## v0.9.177
### 优化
+ 优化跟随模式下， 不同屏幕大小导致跟随没有换行的情况
+ 优化`Console`存在输出没显示问题
+ 优化`Debug`模式下,`Browser`组件Iframe 没有自动刷新问题
## v0.9.176
### 新增
+ 新增支持业务方定义`Editor`插件功能,配置属性`editorPlugins`
> 1. 可以参考[插件Demo](https://showmbug-filestore-1258723534.cos.ap-guangzhou.myqcloud.com/plugins.ts) 示范
> 2. `CodeMirror`[插件开发文档](https://codemirror.net/examples/decoration/)
```ts
    const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      ...
      editorPlugins: [ // 自定义插件,数组形式
        hilightKeyWord(/\[\[(\w+)\]\]/g), // 具体插件
        hilightKeyWord(/(Dear)/g),
        hilightKeyWord(/(\d{2,})/g),
        underlineKeymap,
      ],
    });
```
### 优化
+ 优化告警群中相关错误信息
### 修复
+ 修复`Tabsize` 配置设置为`auto`不生效问题

## v0.9.174
### 修复
+ 修复编辑器出现非法字符`0`
## v0.9.173
### 新增
+ 支持自定义编辑器右键菜单配置`contextMenu`
> 1. 新增内置菜单行为`cut`, ` copy`, `paste`
> 2. 支持内置菜单行为，自定义菜单行为
> 3. 支持快捷键行为： 优先级： 业务方配置 > SDK 定制 > CM 内置的默认的 
> 4. 支持菜单自定义样式
> 5. `contextMenu`配置是一个**二维数组**，可以针对菜单进行分组显示
```ts
 const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      ...,
      contextMenu: [
        [
          {
            text: { // 显示的文案
              [I18nLanguageType.ZH]: '剪切',
              [I18nLanguageType.EN]: 'Cut',
            },
            shortcutKey: 'Ctrl/Cmd+X', // 快捷键的描叙
            actionName: 'cut', // 内置的菜单行为：cut， copy，paste
            className: 'contextmenu-cut', // 自定义菜单样式
            textClassName: 'd42 dao42__icon--create', //自定义文案样式
            shortcutClassName: '', // 自定义快捷键样式
          },
          {
            text: {
              [I18nLanguageType.ZH]: '剪切(自定义)',
              [I18nLanguageType.EN]: 'Cut(Custom)',
            },
            shortcutKey: 'Ctrl/Cmd+X',
            actionFun: (view, commands) => { // 自定义菜单行为
              const tr = view.state.replaceSelection('');
              view?.dispatch({
                ...tr,
              });
            },
          },
          {
            text: {
              [I18nLanguageType.ZH]: '复制',
              [I18nLanguageType.EN]: 'Copy',
            },
            shortcutKey: 'Ctrl/Cmd+C',
            actionName: 'copy',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '粘贴',
              [I18nLanguageType.EN]: 'Paste',
            },
            shortcutKey: 'Ctrl/Cmd+V',
            actionName: 'paste',
          },
        ],
        [
          {
            text: {
              [I18nLanguageType.ZH]: '撤销',
              [I18nLanguageType.EN]: 'Undo',
            },
            shortcutKey: 'Ctrl/Cmd+Z',
            actionName: 'undo',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '取消注释',
              [I18nLanguageType.EN]: 'UnComment Block',
            },
            shortcutKey: 'Mod-/',
            // actionName: 'toggleBlockComment',
            actionShortcutKey: 'Mod-/',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '注释代码',
              [I18nLanguageType.EN]: 'Comment Block',
            },
            shortcutKey: 'Mod-/',
            // actionName: 'blockComment',
            actionShortcutKey: 'Mod-/',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '全选',
              [I18nLanguageType.EN]: 'Select All',
            },
            shortcutKey: 'Ctrl/Cmd+A',
            actionName: 'selectAll',
          },
          {
            text: {
              [I18nLanguageType.ZH]: '选中整行',
              [I18nLanguageType.EN]: 'Select Line',
            },
            shortcutKey: 'Ctrl/Cmd+R',
            actionName: 'selectLine', //codemirror 内置行为: https://codemirror.net/docs/ref/#commands
          },
          {
            text: {
              [I18nLanguageType.ZH]: '格式化代码',
              [I18nLanguageType.EN]: 'Format Document',
            },
            shortcutKey: 'Mod-Alt-f',
            actionShortcutKey: 'Mod-Alt-f',, // 运行指定快捷键行为：   // 优先级： 业务方配置 > SDK 定制 > CM 内置的默认的 （内置的快捷键： https://codemirror.net/docs/ref/#commands）
          },
          {
            text: {
              [I18nLanguageType.ZH]: '插入代码(自定义快捷键)',
              [I18nLanguageType.EN]: 'Insert Code',
            },
            shortcutKey: 'Mod-j',
            actionShortcutKey: 'Mod-j', // 运行指定快捷键行为：   // 优先级： 业务方配置 > SDK 定制 > CM 内置的默认的 （内置的快捷键： https://codemirror.net/docs/ref/#commands）
          },
        ],
      ],
    });
```

+ 新增`fileTree`文件树配置`fileRootActions` 支持自定义样式名称`className`
```tsx
{
        container: '.tree-section',
        item: 'Tree',
        props: {
          hideFeature: defaultValue,
          ...,
          disableFileAction: {
            filesPath: [],
            isDisable: true,
          },
          fileRootActions: [
            {
              label: '下载项目',
              className: 'download-project', // 自定义ITEM样式
              onClick: () => {
                ...
              },
            },
          ],
        },
      })
```
+ `fileTree` 上传文件/文件夹等行为，添加自定义样式，以便业务方进行针对性业务能力扩展
+ 
### 优化
+ 优化`addHotKeys` 自定义快捷键,回调方法`run`添加  `view` 编辑器实例对象，以便业务方能针对编辑器做更多的业务定制行为
+ 优化链式跟随模式下， 组件和头像高亮样式
+ 优化`fileTree`层级限制从`10`变更为`20`
+ 优化如`C#`等语言编译时生成的临时文件，也会在回放中显示
### 修复
+ 修复非SDK创建的大文件，报错问题
+ 修复`Ruby`格式化代码，注释内容自动换行问题
+ 修复`focusEditorPosition` 传递`TOP`, 没有触发`FileOpenDone`事件问题
## v0.9.172
### 优化
+ 优化`Python`语法高亮
+ 优化切题时运行状态问题
## v0.9.171
### 优化
+ 优化链式跟随模式，切题时， 隐藏文件对无权限操作用户不可见
+ 优化`javascript`,`typescript`等语言的高亮展示
+ 优化切题场景下， `Layer`数据消费时机
## v0.9.170
### 新增
+ 新增支持修改过的文件显示`M`标记,
```ts
  const dao = new DaoPaaS({
      ...
      showModifyIcon: true, // 默认false, 是否展示文件修改的Icon
      ...
```
+ 新增`M`标记的文件树`Item` 样式名称`is-modified-file-item`,业务方可以参考如下覆盖自定义样式
```css
.is-modified-file-item {
  background-color: #a4a2a2;
}

.is-modified-file-item .d42-item-title {
  color: #fff;
}

.is-modified-file-item .rct-tree-item-arrow-path {
  fill: #fff;
}

.is-modified-file-item .d42-action-item {
  color: #fff;
}

.is-modified-file-item .d42-action-item path {
  fill: #fff;
}
```
+ 新增支持`LSP`如果选择的是`Method`,自动补全括号
### 优化
+ 优化`FileTree`拖动文件立即展开文件夹的行为
+ 优化后台自动生成文件，不进入回放操作
+ 优化回放文件树和实际展示内容不一致问题

## v0.9.169
### 新增
+ 新增打开文件默认聚焦`Editor`, 默认聚焦文件底部`BOTTOM`,并提供配置参数`focusEditorPosition`
```ts
    const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      ticket: dataRes?.ticket,
      openLspDiagnostic: true, // 默认是true,
      focusEditorPosition: 'BOTTOM', // TOP | BOTTOM | number
      ....
```
+ 新增文件打开完成事件`fileOpenDone`,用于处理文件打开完成后要处理的事情，如跳转到上次打开位置
```ts
   dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case Messages.FileOpenDone: {
          messageBox.info('FileOpenDone:' + JSON.stringify(payload));
          const editorInfo = JSON.parse(
            localStorage.getItem('UnFocusEditor') || '{}',
          );
          const prevOpenInfo = editorInfo[payload.openPath];
          if (prevOpenInfo) {
            // 定位光标
            dao.focuseEditorByCursorPosition(
              prevOpenInfo.openFile,
              prevOpenInfo.cursorPosition,
            );
          }
          break;
        }
```
+ 新增`Editor`失去焦点事件,业务方可以在此事件记录对应文件上次编辑的位置
```ts
  case Messages.UnFocusEditor: {
          messageBox.info('UnFocusEditor:' + JSON.stringify(payload));
          const editorInfo = JSON.parse(
            localStorage.getItem('UnFocusEditor') || '{}',
          );
          editorInfo[payload.openPath] = payload;
          localStorage.setItem('UnFocusEditor', JSON.stringify(editorInfo));
          break;
        }
```
+ 新增跳转到指定文件的指定位置方法`focuseEditorByCursorPosition`,可以配合`FileOpenDone` 事件使用
```ts
  dao.focuseEditorByCursorPosition(
      prevOpenInfo.openFile,
      prevOpenInfo.cursorPosition,
  );
```
+ 新增设置文件树指定路径文件样式`setFileStyle`
```ts
daoPaasObj?.setFileStyle({
  paths: ['a/b/c.js', 'a/c/c.js', 'c/index.js'],
  styles: {
    title: 'customizeTitle',
    icon: 'customizeIcon',
    arrowIcon: 'customizeArrowIcon',
    background: 'customizeBackground',
  },
});
```
### 修复
+ 修复`C++`格式化代码问题
+ 修复回放模式下显示跟随模式Icon问题
## v0.9.168
### 新增
+ 添加跟随模式下，如果被跟随者打卡一个隐藏文件， 跟随着没有权限， 会广播对应的事件：
```ts
   dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case 'openLimitFile': {
          messageBox.success('OpenLimitFile: ' + JSON.stringify(payload));
          break;
        }
        ...
          setAgentUsers(payload.agentUsers);
          if (payload.xtermKey) {
            setActiveKey(payload.xtermKey);
          }
          break;
      }
    });
```
### 优化
+ 优化`markdown`预览`postion`超出文档问题
+ 优化非`ideserver`更新文件,文件被覆盖问题(vim编辑)
+ 完善SDK注册事件日志上报
+ 优化浏览器回放， 切换到不支持模式的时候， 浏览器组件重置默认页面效果
+ 优化HTML模版不支持浏览器回放功能
+ 优化链式跟随模式， 跟随名称的显示优化（A跟随B， B跟随C， 实际上是A链式跟随C，显示A看到的跟随名称是B）
+ 优化跟随模式下，出题场景下， 切换角色时，面试官看不到隐藏文件问题
+ 优化链式跟随模式下，不同用户来回切换文件问题
+ 优化链式跟随模式下， `editor`滚动的跟随效果


## v0.9.167
### 新增
+ 新增`showRRwebController` 属性来控制RRWEB回放的播放控制器(用于业务方调试)
+ 新增聚焦行是空行显示`placeholder`
```ts
new DaoPaaS({
    defaultPlaceholder: { // 默认设置
        ...
        showOnEmptyLine: true,
    },
    placeholders: { // 指定文件设置
        'README.md': { 
        ... 
        showOnEmptyLine: false,
        }
    }
})
```
### 优化
+ 优化浏览器回放业务方多次调用`startPlayback` 方法问题
## v0.9.166
### 新增
+ 新增支持链式跟随模式
### 优化
+ 测试用例接口`runUnitTestByFilePath` 多文件运行，并且单文件配置独立参数
```ts
[
  {
    fileKey: 'sum.test.js',
    outputConsole: false,
    consoleText: '123',
    runMode: false,
    // runId: uuidv4(),
  },
  {
    fileKey: 'sum2.test.js',
    outputConsole: false,
    consoleText: '123',
    runMode: false,
    // runId: uuidv4(),
  },
  {
    fileKey: 'sum3.test.js',
    outputConsole: false,
    consoleText: '123',
    runMode: false,
    // runId: uuidv4(),
  },
]
```
+ 优化跟随模式， 对隐藏且没有权限文件的进行不可见，并给予相关提示处理
### 修复
+ 修复切题时指定文件语法高亮失效
## v0.9.165
### 新增
+ 新增自定义`xterm`字体样式  
```ts
new DaoPaaS({
	...
	xtermStyle:{
		...
		consoleStyle: {
        fontSize: 12,
        lineHeight: 1,
    	  fontFamily: 'Monaco, Menlo, monospace',
    },
	}
})
```
+ 新增支持指定文件名后缀进行语法高亮(完全匹配优先级高于后缀名匹配) 
```ts
new DaoPaaS({
 ...
specialFileHighlight: [
    {
      fileName: '.java.answer',
      languageType: 'java',
    },
    {
      fileName: 'paas_test.java.answer',
      languageType: 'yaml',
    },
]
})
```
 
### 优化
+ 优化浏览器组件系统和回放功能
+ 优化ctrl+p快捷键操作  
+ 移除谷歌字体链接 
## v0.9.164
### 新增
+ 新增实例化`DaoPaaS` 参数`customFileTreeAction`,控制文件树操作行为是否业务方控制
```ts
    const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      tenantId: '3',
      ticket: dataRes?.ticket,
      ....
      customFileTreeAction: customFileTreeAction,
    });
```
+ 新增文件树相关事件点击的订阅事件
```ts
  dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case 'fileTreeAction': {
          messageBox.success(JSON.stringify(payload));
          switch (payload.type) {
            case 'unittest': {
              setTimeout(() => {
                dao?.setFileToUnitTest({
                  path: payload.path,
                  unitTest: payload.targetValue,
                });
              }, 1000 * 2);
              break;
            }
            case 'hide': {
              setTimeout(() => {
                dao?.setFileVisible({
                  path: payload.path,
                  hide: payload.targetValue,
                });
              }, 1000 * 2);
              break;
            }
            case 'lock': {
              setTimeout(() => {
                dao?.setFileLockStatus({
                  path: payload.path,
                  lock: payload.targetValue,
                });
              }, 1000 * 2);
              break;
            }
            default: {
              break;
            }
          }
          break;
        }
```
+ 新增文件添加/删除`unittest`, `hide`, `lock` 行为接口
```ts
 dao?.setFileToUnitTest({
    path: payload.path,
    unitTest: payload.targetValue,
  });
  dao?.setFileVisible({
      path: payload.path,
      hide: payload.targetValue,
  });
 dao?.setFileLockStatus({
    path: payload.path,
    lock: payload.targetValue,
});
```
+ 新增跟随模式下， 对应跟随组件高亮区域显示跟随用户名称
### 优化
+ 优化跟随模式，存在回放两个Dom实例的情况（多次触发跟随接口）

## v0.9.162
### 新增
+ 测试用例接口移除`runId`参数
+ `runUnitTestByFilePath` 接口支持多文件运行，对应的`fileKey`参数可以为：`string | string[]`
```ts
daoPaasObj?.runUnitTestByFilePath({
  fileKey: ['sum.test.js'],
  outputConsole: false,
  consoleText: '123',
  runMode: true,
});
```
+ `stopUnitTest` 传递 `fileKey: string | string[]` 进行停止指定文件或者文件数组 测试用例
```ts
daoPaasObj?.stopUnitTest(fileKey: string | string[])
```
+ `runCmd` 接口调用方式：
```ts
const res: any = await daoPaasObj?.runCmd({
          cmd: 'test',
        });
```
+ `stopRunCmd` 接口调用方式：
```ts
daoPaasObj?.stopRunCmd(cmd: string)
```
+ 新增测试用例执行相关订阅事件
```ts
   dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        // 执行测试用例状态
        case Messages.RunUnitTestStatus:
          messageBox.success(JSON.stringify(payload));
          break;
        // 执行用例结果
        case Messages.RunUnitTestResult:
          messageBox.success(JSON.stringify(payload));
          break;
        // 停止执行用例结果
        case Messages.StopUnitTestResult:
          messageBox.success(JSON.stringify(payload));
          break;
        // 执行CMD结果
        case Messages.RumCmdResult:
          messageBox.success(JSON.stringify(payload));
          break;
        // 停止执行CMD结果
        case Messages.StopRunCmdResult:
          messageBox.success(JSON.stringify(payload));
          break;
        ...
```
### 优化
+ 优化浏览器协同回放样式问题
## v0.9.161
## 修复
+ 修复HTML文件报错

## v0.9.160
## 修复
+ 更新rrweb依赖移除worker_threads警告

## v0.9.159
## 新增
+ 新增获取当前的光标选中区域信息 [getSelection]
```ts
getSelection(): IDaoEditorSpace.ISelection | undefined;
```
+ 新增浏览器协同和回放功能
```ts
    const dao = new DaoPaaS({
      paasDomain:
        process.env.PAAS_MANAGER_API_ORIGIN + (mockSdkInitFail ? '1' : ''),
      ....
      recordBrowser: recordBrowser, //是否录制数据： 候选人： true, 面试官false
      persistenceWebData: persistenceWebData, // 浏览器数据是否持久化（保存到DB）， SMB： true, 1024: false
    });
```
+ 新增浏览器业务方控制是否录制数据的接口
```ts
  daoPaasObj?.startRecordBrowser(true/false);
```
## 优化
+ 重构`agentuserid`生成逻辑
+ 更新`@codemirror/autocomplete`包，修复补全问题
## v0.9.157
### 新增
+ 新增添加自定义快捷键方法[addHotKeys]
```ts
[{ // hotKeys
  ...
  key: 'Mod-j',
  run: (params) => {
    daoPaasObj?.daoEditor.insertCodeByCursorPosition({
      path: params.path,
      content: encodeURI(decodeURI(params.content) + 'INSERT TEST CODE'),
      cursor: params.cursor,
      offsetActions: 4,
    });
  },
}]

daoPaasObj?.daoEditor
  .addHotKeys(hotKeys)
  .catch((error: Error) => {
  	// error: 快捷键不合法
  });
```
+ 新增根据光标位置插入代码[insertCodeByCursorPosition]
```
daoPaasObj?.daoEditor.insertCodeByCursorPosition({
  path: 'main.go',
  content: encodeURI('TEST INSERT CODE'),
  cursor: 102,
  offsetActions: 4,
});
```
+ 新增根据range替换代码[replayCodeByRange]
```ts
daoPaasObj?.daoEditor.replayCodeByRange({
  path: 'main.go',
  content: encodeURI('TEST INSERT CODE'),
  selection: { start: 102, end: 158 },
});
```
+ 新增获取光标位置[getCursor]
```ts
const cursor = daoPaasObj?.daoEditor.getCursor();
```
### 优化
+ 更新codemirror相关依赖，解决一些codemirror已修复问题
+ 更新xterm相关依赖以及write前添加日志上报

## v0.9.156
### 新增
+ 暴露removeMessage和removeError移除事件监听

### 优化
+ 切环境重置fileOpened数据
+ debug模式下运行测试用例设置staring状态
+ 禁用markdownKeymap以及更新markdown依赖包(修复操作字符异常问题)

## v0.9.155
### 修复
+ agentuser的color不存在

## v0.9.154
### 新增
+ 新增支持初始化`SDK` `globalConfig` 全局配置
```ts
type GlobalConfig = {
  editorMode?: EditorMode;
  editorTabSize?: number | 'auto';
  editorWordWrap?: boolean;
  fontSize?: string;
  editorIndentMode?: EditorIndentMode;
  openDiagnostic?: boolean;
};
```
+ 新增回放模式下， LSP状态都为`notSupport` 值
### 修复
+ 修复更新协同插件agentuser的触发时机
## v0.9.152
### 新增
+ 新增`promisify` 接口`activePlaygroundSync`
```ts
     try {
            const res = await daoPaasObj?.activePlaygroundSync();
            messageBox.success(JSON.stringify(res));
          } catch (e) {
            messageBox.error(JSON.stringify(e));
          }
```
### 修复
+ 修复答题区tooltip点击无效问题
## v0.9.151
### 新增
+ 新增支持打开`csv`类型文件
+ 获取测试用例接口`getUnitTestFun`(必须是ACTIVE状态，不然接口直接抛错误)
```ts
  try {
        const res: any = await daoPaasObj?.getUnitTestFun();
        messageBox.success(res);
      } catch (e) {
        messageBox.error(JSON.stringify(e));
      }
```
+ 新增获取运行测试用例接口`runUnitTestByFilePath`(必须是ACTIVE状态，不然接口直接抛错误)
```ts
  try {
        const res: any = await daoPaasObj?.runUnitTestByFilePath({
                fileKey: 'sum.test.js',
                outputConsole: false,
                consoleText: '123',
                runMode: true,
                runId: uuidv4(),
              });
        messageBox.success(res);
      } catch (e) {
        messageBox.error(JSON.stringify(e));
      }
```
+ 新增停止运行测试用例接口`stopUnitTest`(必须是ACTIVE状态，不然接口直接抛错误)
```ts
  try {
        const res: any = await daoPaasObj?.stopUnitTest(runId); // runId是上面接口runUnitTestByFilePath 传递的runId
        messageBox.success(res);
      } catch (e) {
        messageBox.error(JSON.stringify(e));
      }
```
+ 新增运行`CMD`接口`runCmd`(必须是ACTIVE状态，不然接口直接抛错误)
```ts
  try {
        const res: any = await daoPaasObj?.runCmd({
          runId: uuidv4(),
          cmd: 'test',
        }); // runId是上面接口runUnitTestByFilePath 传递的runId
        messageBox.success(res);
      } catch (e) {
        messageBox.error(JSON.stringify(e));
      }
```
+ 新增停止运行`CMD`接口`stopRunCmd`(必须是ACTIVE状态，不然接口直接抛错误)
```ts
  try {
        const res: any = await daoPaasObj?.stopUnitTest(runId); // runCmd 传递的runId
        messageBox.success(res);
      } catch (e) {
        messageBox.error(JSON.stringify(e));
      }
```
### 优化
+ 优化答题区(点击标记行聚焦、编辑器失去焦点隐藏tooltip、派发更新Flags在事件订阅之前)
+ 优化`pushOT`的判断条件
### 修复
+ 修复`IDEServer`JS报错告警
## v0.9.150
### 修复
+ 修复回放暂停光标问题
## v0.9.149
### 新增
+ 新增业务方注入日志上报方法`customLog`
```ts
   const dao = new DaoPaaS({
    ....
      customLog: (level: string, args: any) => {
        const log = console[level];
        if (log) {
          log(`[customLog][${level}]`, ...args);
        }
      },
    });
```
+ 新增`promisify` 接口`appendCustomizeFrameDataSync`
```ts
     const res: any = await dao?.appendCustomizeFrameDataSync({
            action: 'startQuestion',
            value: activeId,
            uuid,
          });
```
+ 新增`promisify` 接口`preparePlaybackSync`
```ts
 const res = await daoPaasObj.preparePlaybackSync();
      messageBox.info('通过同步事件收到回放数据');
      setQuestionsData(res.questionsData);
      if (res.total > 0) {
            setPlayable(true);
            setPlaybackDuration(res.end - res.start);
        }
```
### 优化
+ 优化答题区域历史数据修改答题区域序号问题
+ 优化光标闪动问题

### 修复
+ 修复回放`getPlaybackSnapshot`报错
## v0.9.148
### 新增
+ 新增`SDK`初始化失败事件`initError`
```ts
   dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case Messages.InitError: {
          messageBox.error('初始化SDK失败，请刷新页面重试:' + payload);
          break;
        }
    ...
```
+ 新增针对切题场景`IDEServer`底层架构优化
### 优化
+ 优化文件IO写入节流操作(`200ms`)
+ 优化GUI项目的路径问题
+ 优化`placeholder` `defaultPlaceholder` 为非必传参数
+ 优化`placeholder` 显示光标问题
## v0.9.146
### 新增
+ 新增支持`Editor` `placeholder`功能,支持配置多语言，配置全局默认的， 配置指定文件路径的， 支持配置`Dom`元素
```ts
   const getDom = (lan: I18nLanguageType) => {
      const dom = document.createElement('div');
      const text =
        lan === I18nLanguageType.ZH
          ? '请在此输入你的代码'
          : 'Please input your answer at here';
      dom.textContent = text;
      dom.setAttribute('style', 'color:red;font-size:24px');
      return dom;
    };

 {
        container: '.editor-section',
        item: 'Editor',
        props: {
          ...
          defaultPlaceholder: {
            [I18nLanguageType.ZH]: '默认的Placeholder 内容',
            [I18nLanguageType.EN]: 'Default placeholder text',
          },
          placeholders: {
            'README.md': {
              [I18nLanguageType.ZH]: getDom(I18nLanguageType.ZH),
              [I18nLanguageType.EN]: getDom(I18nLanguageType.EN),
            },
            '1.c': {
              [I18nLanguageType.ZH]: '请在这里输入你的代码-1.c',
              [I18nLanguageType.EN]: 'Please input your code at here for 1.c',
            },
            'root/2.c': {
              [I18nLanguageType.ZH]: '请在这里输入你的代码-root/2.c',
              [I18nLanguageType.EN]:
                'Please input your code at here for roo/2.c',
            },
          },
          ....
```
+ 新增文件树支持`F2`快捷键重命名
### 优化
+ 用户离线重新上线更新用户数据的更新

## v0.9.145
### 新增
+ 新增选择代码右击菜单功能（目前只支持：【implement，update】两种菜单Action）：
```ts
{
        container: '.editor-section',
        item: 'Editor',
        props: {
          showHiddenCode, // 是否显示隐藏代码：面试官视角，应该是true, 候选人视角：false
          freezeCode, // 是否让设置的代码区域不能编辑， 面试官视角，应该是false,候选人视角：true
          contextMenu:  {
               title: 'ChatGPT',
                handleSelect: (e, callback) => {
                  messageBox.info(JSON.stringify(e));
                  const id = Math.floor(Math.random() * 1000);
                  callback?.({
                    extralAction: {
                     replaceSelection: ['业务方需要替换或者追加的代码']
                    }
                  });
                },
                items: [
                  {
                    action: 'implement',
                    text: '生成代码',
                    noActionCheck: true, // 答题区需要坚持隐藏，只读，答题区域内等情况不能设置
                    codeAction: 'append', // 代码追加的方式： append, replace,
                    icon: 'dao42__icon--create',
                  },
                  {
                    action: 'update',
                    text: '解析执行',
                    noActionCheck: true, // 答题区需要坚持隐藏，只读，答题区域内等情况不能设置
                    codeAction: 'replace', // 代码追加的方式： append, replace,
                    icon: 'dao42__icon--fix',
                  },
                ],
              }

        },
      },
```
### 优化
+ 优化答题区域历史数据修改答题区域序号问题
## v0.9.144
### 修复
+ 修复`safari`浏览器正则兼容性问题
## v0.9.143
### 新增

+ 新增批量修改答题区Flag属性接口(并支持修改历史数据)

```ts
daoPaasObj?.updateAreaFlags({
  path: 'README.md', // 指定文件ID
  ids: [ // 要修改的ID的集合
    {
      id: '1', // 答题区域的ID
      flag: '1', // 新的Flag值
    },
    {
      id: '2',
      flag: '3',
    },
  ],
});
```
+ 新增`IDEServer`配置日志输出级别
### 优化
+ `debug`变量`Hover`和面板数据懒加载
+ `xterm`设置最小`clos` (xterm宽度变换时~/app$换行问题)
+ 优化单击答题区后无法聚焦编辑器

## v0.9.142
### 优化
+ 优化`currentAgentId` 问题
+ 优化光标协同问题
+ 优化切题模式下事件处理机制
## v0.9.141
### 新增
+ `lsp`添加`Uninitialized` (未启动状态) 以及断网时`lspstatus`改为`notSupport`或者`uninitialized`状态
+ 新增记录哪些文件进行了修改， 对应的文件名称`.1024file`
+ 新增回放数据，标记对应的时间范围为空白时
```ts
  //
    const dao = new DaoPaaS({
      ...
      noFrameSpan: 2, // >=2S 则标记出来,单位为s
      ...
    });
    // PlaybackInfo 事件，会返回一个 whiteFrames数组
```
### 优化
+ 默认用户头像背景与光标颜色保持一致
+ 优化协同插件更新方式(减少相关样式重复载入、服务端版本大于客户端主动sync一次)
+ 优化光标选中区域协同(下线用户|切换文件|关闭文件|跟随模式|失去焦点时选中区域和光标tooltip缓存处理)
+ 优化agentUserId生成逻辑、由Ide server判断agentUserId是否可用(agentuser头像问题、多个tabs共用一个agentuserid)
+ 移除掉sentry包
+ IDEServer  Mongo 索引优化
## v0.9.140
### 优化
+  更新Roboto+Mono字体加载域名

## v0.9.139
### 优化
+  回放自动换行
+  用户光标颜色按顺序生成

## v0.9.138
### 修复
+  回放过程中打开文件死循环问题
## v0.9.137
### 修复
+  回放时stopPlayback状态也不显示光标的tooltip

## v0.9.136

### 优化

+ 回放暂停时，隐藏光标名称tooltip
+ 观测云日志上报异步改同步
+ appendEditorCRDT Mongo操作同步改异步
+ checkIfThePathExists方法异步改为同步
+ 优化LSP代码补全(列表关键字重复、from位置不正确、切题模式下lsp重连失败、超出重连次数派发Shutdown状态)

## v0.9.135

### 修复

+ 编辑器报错changes undefined，导致崩溃的问题
+ 回放模式下Markdown预览可点击

## v0.9.134

### 新增

+ 暴露debug状态信息

```ts
dao.onMessage((message: Message) => {
    switch (name) {
  case Messages.Debug:
   // payload.debugStatus状态信息
    }
}
```

+ debug面板添加启动中dot

### 修复

+ debug运行中离线重连后重置debug相关数据

### 优化

+ 全局消息调试最多可同时显示一条

## v0.9.133

### 新增

+ 设置答题区时返给外部代码缩进往左侧移动（最顶层不留indentUnit）

### 修复

+ 修复跟随模式changes为undefined和光标位置不正确问题
+ 重命名时，命名不规范恢复之前的名称
+ 修复编辑器选中内容的首行高亮问题

### 优化

+ 文件IO改成同步操作
+ FileTree_createFile异步方法优化
+ 观测云日志上报异步改同步
+ updateAgentUserData方法优化
+ 使用Promise.all优化getPlaybackInfo方法
+ 优化ctrl+p打开慢的问题
+ 去掉组件挂载时fallback的Loading组件
+ 优化打开文件慢的问题
+ 优化ctrl+p搜索文件的匹配方式（支持首字母，支持不输入/匹配）

## v0.9.132

### 优化

+ 控制台一些更新的标识更新不推送给客户端
+ debug组件下的类名补全d42前缀

## v0.9.131

### 优化

+ 优化debug(开始/停止icon颜色、点击堆栈项进行跳转、toolTip文案/背景修改、修复断点状态、优化获取当前文件断点数据方法)

### 新增

+ 新增realTimeRefresh参数文件有更新是否通知MQ(非运行状态时)
+ 新增insertCodeByCursor方法，根据光标位置插入数据

```ts
daoPaasObj?.insertCodeByCursor(insertCode);

  // 插入代码
const insertCode = (
    line: number, // line为当前光标聚焦行
    insercodeCallback: IInsertCodeInternalFn,
) => {
    // 计算pos
    insercodeCallback({
      line: line + offsetLine, // offsetLine为光标偏移量
      actions: 'current', // 代码插入的位置，当前行首(start) | 当前位置(current) | 当前行尾(end)
      text: encodeURI(insertCodeText),
    });
};
```

### 修复

+ 去掉图片地址的https前缀
+ 修复 tab 缩进时 IndentUnit 错误
+ 修复回放时，文件树内容与打开的文件不对应
+ 修复在冻结代码时, LSP自动补全无法导入包[答题区]
+ 修复回放时，视角不跟随光标移动的问题
+ 修复一个光标在其他端显示多光标的问题

## v0.9.130

### 优化

+ 优化debug组件(默认展开第一层、hover查看完整内容、修改tooltip内容、字体大小、边框、图标)
+ 优化浏览器组件实时刷新功能
+ 优化Docker 状态为END情况下不能打开问题

## v0.9.129

## 新增

+ 新增`XtermInfo`事件，`terminal`或者`console`有更新进行派发

```ts
dao.onMessage((message: Message) => {
 ...
 case Messages.XtermInfo: {
      console.log(payload); // 数据为IXtermInfo
      break;
    }
}
```

## 修复

+ 修复全局消息条样式问题
+ 修复markdown有序列表的padding样式问题

## v0.9.128

## 优化

+ 优化答题区`tooltip`显示时机,延时`600ms`出现
+ 优化禁止文件编辑`disableEditable`为true时，不可以打断点
+ 优化刷新文件缓存接口`forceRefreshFile`,支持刷新未打开文件缓存

## v0.9.127

## 新增

+ 新增强制刷新文件缓存的接口

```ts
daoPaas.forceRefreshFile(path?: string) // path 默认是当前Editor打开的文件
```

+ 新增触发聚焦答题区事件

```ts
dao.onMessage((message: Message) => {
...
case Messages.AnswerAreaInfo: {
  console.log(payload.foucusId); // 当焦点在答题区内foucusId为答题区id、不在则为空字符
  break;
}
})
```

+ 新增答题区新增`overrideItems`和`handleOverride`参数，光标选择区域包含答题区或在答题区内触发操作。如没有设置`overrideItems`右击菜单默认为items

```ts
contextMenu:{
    ...
    overrideItems: [
      {
        action: 'delete',
        text: '删除答题区',
      },
    ],
    handleOverride: (params: IOverrideParams) => {
      console.log('params: ', params); //执行对应删除操作
    },
}
```

## 修复

+ 修复答题区右击菜单和答题区tooltip同时存在，右击菜单出现时隐藏答题区tooltip
+ 修复跳转答题区没有展开文件树对应的目录和文件没有滚动到可视区域内
+ 修复中途禁用tooltip时tooltip不消失问题
+ 修复debug相关样式

## v0.9.126

## 新增

+ 新增断点hover功能
+ 新增IDEServer 单元测试

## 优化

+ 优化全局消息条，支持多条展示
+ 优化debug组件样式
+ 优化协同时，在编辑器失去焦点时，其他人只应该隐藏失去焦点人的光标，而不应该隐藏对方的选中区域
+ 优化检测文件是否存在采用fs.access方法，此方法是异步方法，且检测文件是否存在不需要打开文件
+ 优化调整默认头像的背景色和字体颜色

## 修复

+ 修复debug组件区域页面抖动问题
+ 修复答题区tooltip不支持多个icon类问题
+ 修复回放时debug相关订阅没有被销毁问题
+ 修复文件树在重命名和新增时，选中输入的文件名，移动鼠标时会隐藏输入框的问题
+ 修复当前playground status等于END时，构建文件树报错的问题（不需要构建文件树）
+

## v0.9.125

### 新增

+ 新增答题区选中Tooltip操作按钮

### 优化

+ 优化回放功能头像问题

### 修复

+ 修复Browser 组件 Open Icon 不能点击问题
+ 修复LSP排序问题
+ 修复点击文件树的action时item会变为聚焦状态

## v0.9.124

### 新增

+ 新增`Markdown`文件支持插入项目内图片路径
+ 新增`Editor` 头部支持注入插件功能

```ts
 {
        container: '.editor-section',
        item: 'Editor',
        props: {
          fileHeader: [
            {
              className: 'custom-className', // 非必需
              icon: { // 非必需
                className: 'dao42__icon--warning-circle',
                tips: {
                  [I18nLanguageType.ZH]: '代码智能检测和补全(LSP)：连接中',
                  [I18nLanguageType.EN]: '代码智能检测和补全(LSP)：连接中',
                },
                action: () => {
                  messageBox.success(' IconAction');
                  // 业务方自定义的业务行为，可以调用SDK提供的API
                },
              },
              labelAction: null as any, // 非必需
              label: {// 非必需
                [I18nLanguageType.ZH]: 'LSP 连接中',
                [I18nLanguageType.EN]: 'LSP Connecting',
              },
            },
             ...
          ],

        },
      },
```

+ 新增动态修改`Editor`头部插件接口

```ts
daoPaasObj?.updateFileHeader([...fileHeader]);
```

+ 新增答题区域业务方配置答题区Flag属性

```ts
 callback?.(
    {
      id,
      flag, // 业务方在返回ID的时候，同时返回flag 值
    },
    extralAction,
);
```

+ 新增批量修改答题区Flag属性接口

```ts
daoPaasObj?.updateAreaFlags({
  path: 'README.md', // 指定文件ID
  ids: [ // 要修改的ID的集合
    {
      id: '1', // 答题区域的ID
      flag: '1', // 新的Flag值
    },
    {
      id: '2',
      flag: '3',
    },
  ],
});
```

### 优化

+ 优化Debug Hover 变量组件
+ 优化浏览器实时刷新组件样式

### 修复

+ 修复因为自动缩进设置Auto引起的JS错误引起回放功能报错问题

## v0.9.123

### 修复

+ 修复选择多行，按`Tab`键，直接删除对应内容问题
+ 修复反复触发`retainedFiles`,可能造成其他其他feature 属性丢失情况
+ 修复`tooltip`快速切换子`icon`元素迅速将鼠标移出元素`onMouseLeave`不触发问题

### 优化

+ 优化SDK组件的单元测试流程（新增Editor单元测试入口）
+ 优化SDK 客户端日志上报（resourceMonitoring等频繁触发非重要事件不上报日志）
+ 优化`PaaS`回放业务方数据鉴权问题

## v0.9.122

### 新增

+ SDK新增组件单元测试
+ 新增支持图片预览功能
+ 新增支持业务方配置多个打开默认文件名称
+ 新增自定义执行方法，由业务方携带的配置信息来启动运行

```ts
// 自定义执行，通过业务方携带的配置信息来启动运行
// tips：而不是通过后端读取.1024配置文件信息
 customExecute(config: ICustomExcuteConfig): void;
```

### 修复

+ 修复光标选中内容，也会造成浏览器组件刷新问题

## v0.9.120

### 新增

+ 新增支持下载单文件
+ `editorTabSize`属性新增一个值为`auto`，自动识别文件缩进并且应用于编辑器

```ts
GlobalConfig {
  ...
  editorTabSize: 'auto'|number // 新增auto，自动识别文件缩进
}
```

+ 新增限制打开`db,db3,sqlite`相关文件，并给予提示

### 修复

+ 修复Tab模式缩进
+ 修复添加CDN资源后， Editor内容不刷新显示CDN资源内容

### 优化

+ 优化二进制文件检测方法

## v0.9.119

### 优化

+ 优化协同OT消费机制
+ 优化FileTree Feature Icon 样式
+ 优化LSP的模糊匹配（单词模糊匹配）
+ 优化限制大于16M的文件的上传，并给予提示

### 修复

+ 修复答题区域在冻结代码时，跳转到对应答题区光标聚焦
+ 修复答题区域删除最后一行后，无法再次进入对应答题区进行编辑
+ 修复偶发文件打开没成功一直Loading的问题

## v0.9.117

### 新增

+ 新增特定语言特定模版文件进行语法高亮，SDK内置了如下部分文件,同步支持业务方配置指定文件名称进行对应语言高亮

```ts
// SDK 默认已经支持的内置文件配置
 ConfigExtension {
  Gemfile = 'ruby',
  Rakefile = 'ruby',
  artisan = 'php',
  'Gemfile.lock' = 'ruby',
  '.npmrc' = 'javascript',
  '.eslintrc' = 'javascript',
}
// 业务方可以配置对应文件名称进行指定语法高亮
new DaoPaaS({
...
specialFileHighlight:[
  {
    fileName: 'Gemfile',
    languageType: 'ruby'
  }
] // 指定文件名称，使用特定的语法高亮
})
```

### 优化

+ 优化`Ctrl+p`搜索文件，支持大小写搜索

### 修复

+ 修复文件树拖拽上传的样式问题
+ 修复文件前缀相同情况下，Feature功能不生效问题
+ 修复`browser-container` 样式和业务方冲突问题

## v0.9.116

### 新增

+ 新增实时刷新功能配置
+ 新增PaaS Demo 回放功能，回放指定的回放数据（Mock 业务方的回放数据进行回放）
+ 新增IDEServer健康检测接口`/health`

### 修复

+ 修复断线重连期间打开文件，Editor一直Loading问题
+ 修复文件树外层目录收缩会导致内层同名目录也收缩
+ 修复答题区域的右侧边界样式问题

### 优化

+ 优化`Crtl+F` 这里搜索结果的选中的高亮色
+ 优化`Ctrl+p`搜索文件，将文件树打开的对应文件滚动到可视区内
+ 优化文件树的相关问题 (无法移除外层文件夹、拖拽样式交互、出现空白文件、重命名高亮、上传区域等)

## v0.9.115

### 优化

+ 优化回放模式下禁用`Ctrl+p` 打开文件
+ 优化电脑休眠重新上线LSP可用但是状态为running状态
+ 优化`FileTree`  `toolTip`组件
+ 优化回放数据，记录Docker URL数据
+ 优化Editor协同逻辑

### 修复

+ 修复`ide serve` 大文件写入`mongo`报错
+ 修复`Shell`进行文件新增删除操作，可能会出现文件不存在问题
+ 修复打开默认文件，如果默认文件的文件夹不存在，JS报错问题
+ 修复跟随模式，文件打开不跟随打开问题

## v0.9.114

### 新增

+ 新增回放过程中打开其他问题，派发暂停事件给业务方

```ts
 dao.onMessage((message: Message) => {
        const { name, payload } = message;
        ...
        switch (name) {
          case Messages.PausePlayback: {
            messageBox.success('PausePlayback');
            break;
          }
          ...
```

### 修复

+ 修改shell出现乱码的情况

## v0.9.113

### 新增

+ 新增支持回放模式打开文件树功能

### 修复

+ 修复运行代码代码不同步更新问题
+ 修复不允许重命名时， 回车依旧可以重命名

## v0.9.111

### 新增

+ 关闭实时刷新功能
+ 新增是否开启LSP诊断接口

```ts
const dao = new DaoPaaS({
  ...
  openLspDiagnostic: true, // 默认是true,
  ...
});
daoPaasObj?.switchLspDiagnostic(true/false);
```

### 修复

+ 修复sdk处理browser组件502状态码
+ 修复切换答题区自动定位光标以及删除多余的空行
+ 修复答题区复制代码缩进格式不对
+ 修复答题区JSX文件中 缩进不对问题
+ 修复组件不渲染问题

## v0.9.110

### 新增

+ 新增关闭LSP错误信息提示开关
+ 新增关闭实时预览Web项目开关
+ 新增监听Docker资源使用情况(5S 更新一次资源使用情况)

```ts
case Messages.ResourceMonitoring: {
      {
        cpuPercent: 0.33,
        memoryCurrent:144,
        memoryMax:1024,
      }
  }
```

## v0.9.106

## 修复

+ 修复`emacs`模式下的`ctrl +x` 快捷键不生效
+ 修复答题区位于文档开头或者结尾无法删除
+ 修复LSP语法告警情况下，存在最后一个字符无法选中情况
+ 修复回放模式，Ediotr 可以复制粘题内容
+ 修复回放模式，Shell 会出现重复内容

## 优化

+ 优化答题区域选中区域返回给业务方内容进行自动格式化缩进
+ 优化`debug`时，光标滚动定位到断点视图
+ 优化回放模式`shell`和`console`可以进行滚动内容
+ 优化回放模式，时长过长造成卡顿问题
+ 添加客户端日志上报，关键节点进行日志上报，便于问题排查

## v0.9.102

### 新增

+ 新增添加重连时机的事件广播（事件名称为：`reconnecting`）

### 修复

+ 修复回放功能文件树渲染问题
+ 修复删除文件，Editor会自动关闭有相同前缀开头的文件

### 优化

+ 优化`cmd+F` 搜索当前文件，相关选中区域样式问题
+ 优化`cmd + P` 体验细节优化（ESC关闭弹窗快捷键、修复文件搜索历史记录）
+ 优化记录文件位置，下一次打开该文件恢复位置
+ 优化添加回放功能跟随光标滚动滚动条

## v0.9.98

### 修复

+ 修复回放功能跳转题目最后一帧，Editor没打开文件问题

## v0.9.97

### 修复

+ 修复回放功能JS报错

### 优化

+ 优化`syncPlayground`失败后， 60S后能进行重新同步

## v0.9.96

### 修复

+ 修复`cmd + p`不应该显示文件树已经隐藏的文件
+ 修复 `Ctrl+p`搜索文件显示不存在的文件和相关提示语
+ 修复回放功能，文件树存在丢失问题
+ 修复回放功能`shell`内容重复显示问题

### 优化

+ 优化文件树排序
+ 优化`lsp`错误提示`tips`文案选中高亮样式

## v0.9.95

### 新增

+ 新增Debug支持变量Hover 显示变量值功能
+ 新增SDK配置支持打开默认文件功能

```ts
    const dao = new DaoPaaS({
      ....
      defaultOpenFiles: ['README.md', 'CHANGELOG.md'], // 默认打开文件列表(PaaS 默认只有README.md)，优先级'README.md' > 'CHANGELOG.md'
      specialOpenFile: openpath, // specialOpenFiles 优先级 高于defaultOpenFiles
    });
```

### 修复

+ 修复`console`和`shell`可能存在乱码的问题
+ 修复回放时光标样式不正确的问题
+ 修复跟随模式下，多次点击跟随，编辑器不协同的问题
+ 修复React JSX语法设置答题区域，显示对应标识内容问题

### 优化

+ IDEServer WS数据交互，不再明文传输，进行二进制加解密
+ 优化文件树，`Popover`组件,能自适应定位位置
+ 优化设置答题区后，答题区内容遵循代码缩进
+ 答题区内容，不能直接进行复制粘贴
+ 答题区在预览模式下，不可编辑区域进行操作，给予相应的提示

## v0.9.94

### 新增

+ 新增回放支持回放光标，选择区域，文件树头像功能

### 修复

+ 修复点击跳转答题区域，第一次只打开文件不跳转问题
+ 修复文件树收起，展开头像消失问题

### 优化

+ 优化`syncPlaygroundInfo`接口性能

## v0.9.93

## 优化

+ 支持IDE Server多节点部署时，根据负载选择节点

## v0.9.92

## 新增

+ 新增paas平台接入sentry监控 （需要接入方再打包自己项目时，自定义命名打包后的@dao42/d42paas-front文件，名称需要包含daopaas字符）

```ts
new DaoPaaS({
  ...
  enableSentry // 是否开启sentry监控
})
```

+ 新增测试用例展示demo
+ 新增支持Debug功能回放
+ 新增支持新题型多文件

```ts
  const res = await daoPaasObj?.switchLanguage(
    languageType,
    languageValue,
    languageFileName,
    createNewFile === '1', // 添加第四个参数，默认为false, 如果传递为true，则会创建新的文件
    );
```

## 修复

+ 修复浏览器输入框无效

## 优化

+ 将`syncBreakPoint`从`syncPlaygroundInfo`分离
+ 切换Tab后， WS断开进行自动重连，不直接立即抛出断开事件
+ 优化当所有用户都立刻后， 4分钟后， 进行清除Playground Info ，释放资源

## v0.9.90

## 修复

+ 修复Browser组件默认背景色为浅灰色问题

## v0.9.88

## 修复

+ 修复文件已存在的提示弹窗样式，单词不换行问题
+ 修复`emacs`模式下`ctrl-v`快捷键行为
+ 修复文件树Feature功能， 文件和子目录反复切换设置导致子文件设置不生效问题
+ 回退 Tab 键根据上下文进行缩进，采用Editor自带缩进功能
+ 修复Browser 组件初始化加载默认图片

## v0.9.87

## 修复

+ 修复回放模式反复切换题目，Editor快照生成问题

## v0.9.86

## 修复

+ 修复回放模式自定义数据帧跳转，生成功快照问题

## v0.9.85

## 新增

+ 新增Debugger Step In 和 Step Out 功能
+ 新增`Shift+Mod+z``redo`快捷键

## 优化

+ 控制答题区域范围内不能新增新的答题区
+ 优化在未打开指定文件的情况下，可以直接删除答题区域，并且同步打开文件(新增`filePath`参数)

```ts
 daoPaasObj?.removeAreaTextById(id, text, filePath);
```

+ 优化在未打开指定文件的情况下，可以直接跳转到指定答题区域，并且同步打开文件(新增`filePath`参数)

```ts
daoPaasObj?.jumpToAreaById(id, path);
```

## 修复

+ 修复回放功能数据帧丢失问题
+ 修复回放模式清空Consle 模块的相关标识

## v0.9.84

### 新增

+ 新增支持答题区域根据指定ID跳转到指定的答题区域接口

```ts
 daoPaasObj?.jumpToAreaById(id);
```

+ 新增支持Editor进行`redo` ,`undo` 如有答题区域信息变更，通知业务方接口

```ts
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      switch (name) {
        ...
        case Events.UpdateArea: {
          messageBox.info(JSON.stringify(payload));
          break;
        }
         ...
      }
    });
```

+ 新增回放模式，派发 Editor 人为滚动事件

```ts
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      switch (name) {
        ...
        case Messages.EditorScroll: {
            if (playing === PLAY_STATUS.INITIAL) {
              dao.pausePlayback();
              setPlaying(PLAY_STATUS.PAUSE);
            }
            break;
          }
         ...
      }
    });
```

+ 新增提供禁止编辑`fileTree` 和 `editor` 配置属性

```ts
 daoPaasObj?.updateConfig({
    disableEditable: true,
    disableFileTreeAction: true,
 });
```

+ 新增`console` 数据清除同步，并提供在跟随模式下 `console`, `shell` 切换事件派发进行跟随同步

```ts
// 上报当前terminal聚焦点，console | shell.（跟随模式）
syncXtermKey(xtermKey: IXtermKey)
// 接受其他人切换控制台tab栏（跟随模式）
dao.onMessage((message: Message) => {
 case Messages.UsersUpdated:
  ...
 if (payload.xtermKey) {
  setActiveKey(payload.xtermKey);
 }
 break;
}
```

### 优化

+ 优化Editor滚动支持回放
+ 优化`docker fail`重连
+ 优化回放模式Editor 可以滚动滚动条
+ 优化答题区域，选择区域默认选中首尾整行
+ 优化支持回放模式，可以点击答题区域相关按钮事件
+ 优化答题区域只能通过接口进行删除
+ 优化非Web项目，浏览器组件也会刷新问题
+ 优化当文件为不可编辑状态时，鼠标禁用以及文件不可编辑

### 修复

+ 修复设置答题区，`html`类型文件在浏览器中会显示相关标签文案
+ 修复Shell中识别错误的文件路径，然后打开文件问题

## v0.9.83

### 新增

+ 新增LSP Tab 键进行补全

### 修复

+ 修复Editor选中内容背景会出现重叠的问题
+ 修复浏览器页面切换后，重新回来， WS断连，给予断开链接提示
+ 修复终端非有效文件路径可以点击问题

## v0.9.82

### 新增

+ 新增删除指定答题区域接口

```ts
daoPaasObj?.removeAreaTextById(areaId, areaText);
```

+ 新增只读、隐藏文件不能设置答题区限制

### 修复

+ 修复WS重连且成功后， 但是Docker停止，抛出重启Docker的事件
+ 修复Docker重启后Terminal不换行问题
+ 修复LSP重连成功后， 影响Editor协同问题
+ 修复Web项目， Browser模块背景默认浅灰色问题
+ 修复1024PaaS Demo UI问题

## v0.9.81

### 新增

+ 新增文件树右键弹出菜单操作功能

### 修复

+ 修复切题模式，反复切换，不支持LSP的语言WS一直重连之前环境WS问题
+ 修复Editor已知光标问题
+ 修复答题区域样式和LSP样式冲突问题

### 优化

+ 优化LSP链接后Editor会有闪动问题
+ 优化DebugSupport状态机制

## v0.9.80

### 新增

+ 支持less、scss、sass 语法高亮
+ 新增重启LSP服务接口

```ts
daoPaasObj?.restartLSPServer();
```

+ 新增1024code官网底部添加`copyright`信息
+ 新增Editor 内置文案多语言配置

```ts
{
        container: '.editor-section',
        item: 'Editor',
        props: {
          translate: {
            next: { // next 为Editor显示的文案， 如果没有配置对应的语言， 就用对应的Key作为文案
              [I18nLanguageType.ZH]: '下一个',
              [I18nLanguageType.EN]: 'next',
            },
            previous: {
              [I18nLanguageType.ZH]: '上一个',
              [I18nLanguageType.EN]: 'previous',
            },
            all: {
              [I18nLanguageType.ZH]: '全部',
              [I18nLanguageType.EN]: 'all',
            },
          },
```

+ 新增Focus文件树，回车键触发重命名功能

### 修复

+ 修复文件树创建文件的时候，input可能会自动消失问题
+ 修复Debug配置文件问题
+ 修复Web项目，频繁刷新Iframe,导致白屏问题
+ 修复`replaceAll`语法兼容问题

### 优化

+ 优化在未显示隐藏文件的开关打开前，也可以创建隐藏文件，但是有相应的提示
+ 优化Windows 系统上， Editor字体显示问题
+ 优化LSP WS消息消费机制
+ 优化`useragent` 离线处理问题
+ 优化Editor回车，自动解析上下文对其缩进
+ 优化Editor多行选中时，Tab缩进功能
+ 优化Editor在光标不在起始位置，Tab缩进功能
+ 优化Editor `ctrl+x`进行剪切时，光标位置问题

## v0.9.79

### 修复

+ 修复文件树创建文件，文件名能只为空格问题
+ 修复`console` 输出换回问题
+ 修复切换语言/题目，之前的`LSP` WS还一直尝试重连之前的链接报错问题
+ 修复IDEServer WS在熄屏后，重新打开，链接失效，没有给业务方抛出`websocket broken` 消息

## v0.9.78

### 新增

+ 新增Debug组件

```ts
components.push({
 container: '.browser-section',
 item: 'Debug',
 props: {},
});
```

+ 新增初始化参数，是否开启debug模式isOpenDebugMode

```ts
new DaoPaaS({ isOpenDebugMode:true })
```

+ 新增debug启动事件

```ts
daoPaasObj?.debugRun()  // 启动事件
// debug结束事件与之前的结束事件相同
daoPaasObj?.stopPlayground()
```

+ 暴露`Debug`信息(目前只支持是否支持debug模式)

```ts
dao.onMessage((message: Message) => {
 ...
  switch (name) {
    case Messages.Debug:
    payload.debugSupport // 是否支持debug模式
})
```

### 修复

+ 修复切题模式或切换编程语言复制会出现多个内容问题

## v0.9.77

### 修复

+ 修复切题后，依然重连上次的LSP的问题

## v0.9.76

### 修复

+ 修复切题模式或切换编程语言复制会出现多个内容问题
+ 优化shell 文件链接问题

## v0.9.75

### 修复

+ 移除跟Markdown 解析相关无用的字体文件引用
+ 修复`Editor`标题可能存在为空，但是`Editor`有打开文件的问题

## v0.9.74

### 修复

+ 修复文件重命名，相同名称没有错误提示问题

## v0.9.73

### 优化

+ 优化CICD流程（Paas Demo 和 IDEServer 独立部署，并且添加CICD的流程）

### 修复

+ 修复回放功能脚本报错问题
+ 修复LSP的相关问题
+ 修复文件重命名导致输入框消失问题
+ 修复设置答题区域功能后，删除编辑器内容报错问题

## v0.9.72

### 新增

+ 新增插入指定代码块到指定区域功能

```ts
// 如果给指定问题区域插入代码：id则是指定问题ID，如果给指定区域插入代码， 则ID传入‘’空字符串， lineNum 支持：TOP，BOTTOM，和数字（数字为指定的行号）
 daoPaasObj?.insertTextById(id, lineText, lineNum);
```

+ 新增针对跟随模式对文件夹展开和关闭功能

### 修复

+ 修复WS重连后， Editor可能编辑内容不保存问题
+ 修复切题或者切换编程语言，复制文本重复问题

## v0.9.71

### 新增

+ 新增重启服务接口

```ts
  const restartServer = async () => {
    const res = await daoPaasObj?.restartServer();
    if (res?.status) {
      messageBox.success(res.message);
    } else {
      messageBox.error(res?.message);
    }
  };
```

+ 新增文档上下文单词智能提示(解决LSP服务启动慢，智能提示问题)

### 修复

+ 修复`shell`命令行不换行问题
+ 修复编辑器协同问题

## v0.9.70

### 新增

+ 支持对文件树指定文件调整文字颜色的功能。(文件路径支持模糊匹配)

```ts
daoPaasObj?.setFileStyle({
  paths: ['a', '*.md', '**/index.css', 'a/b/c.js', '*/c'],
  styles: {
    title: 'customizeTitle', // 对应文案的样式名称（业务方可以自定义名称）
    icon: 'customizeIcon', // 对应ICON的样式名称（业务方可以自定义名称）
    arrowIcon: 'customizeArrowIcon', // 对应Arrow ICON的样式名称（业务方可以自定义名称）
  },
});
```

### 修复

+ 修复点击停止运行后，浏览器还在工作
+ 修复文件树重命名，子文件会重复显示
+ LSP在WS启动缓慢的情况下，会存在文件未打开错误
+ 修复回放功能相关问题（系列新功能影响到回放）

### 优化

+ 优化`reloadI18nResources`方法，支持一次性设置多个语言内容

```ts
 // https://snyk.io/advisor/npm-package/i18next/functions/i18next.addResources 用法
 daoPaasObj?.reloadI18nResources(lngOrSource: Array<I18nResourcesType> | I18nLanguageType,resource?: any)
```

+ 优化高亮主题样式

## v0.9.69

### 新增

+ 答题区支持“修改代码”模式
+ 新增`PaaS`平台，文案可以自定义功能接口，接口使用如下：

```js
daoPaasObj?.reloadI18nResources(I18nLanguageType.ZH, {
      file_tree_create_new_file: '哈哈哈', // file_tree_create_new_file:要重命名的Key
});
```

### 修复

+ 修复大文件打开，导致浏览器崩溃问题
+ 修复在协同编辑文件，如重命名，删除操作，造成JS报错问题
+ 修复文件树重命名，点击空白区域无法自动重命名问题
+ 修复停止运行，但是浏览器还在运行工作问题

## v0.9.68

### 新增

+ 新增ctrl+p（mac是command+p）按文件名搜索文件快捷键功能
+ 新增一键收起文件树功能
+ 文件树头像位置优化
+ 新增修改xterm样式方式配置

```ts
new DaoPaaS({
  xtermStyle: XtermStyleType
  ...
})
```

+ 新增编辑器快捷键操作

### 修复

+ 优化lsp重连机制，限制最大重连次数，和IDEServer的socket重连次数保持一致
+ 优化支持markdown表格解析以及图片不换行
+ 兼容smb旧版本的markdown数学公式语法解析

```ts
new DaoPaaS({
  // true表示支持旧的`$ $`语法，默认false
  isLegacyMarkdownMath:true
  ...
})
```

+ 优化LSP服务在离开IDE Page 还在尝试重连问题
+ 修复GUI项目，在刷新页面后， 显示浏览器URL等组件问题
+ 修复WS重连后，但是Docker没有自动重启问题

## v0.9.67

### 修复

+ 修复编辑器协同问题

## v0.9.65

### 优化

+ 优化LSP服务在离开页面后，还在尝试重连问题

## v0.9.64

### 优化

+ 优化GUI项目UI显示（底部白色边框处理）

## v0.9.63

### 修复

+ 修复shell终端操作文件，不及时更新文件树的问题
+ 修复离线改为在线后，缓存数据不更新到后端的问题

## v0.9.62

### 修复

+ 修复字体文件本地引用问题

## v0.9.60

### 优化

+ 优化构建工具针对静态资源打包问题

## v0.9.59

### 优化

+ SDK ICON样式名称优化，以`BEM`方式进行命名，如： `dao42__icon--refresh`
+ 优化`Editor`大文件在新版本中滚动出现卡顿的现象

## v0.9.58

### 新增

+ 新增答题区域功能,右键菜单配置方式（目前只支持：【implement，update】两种菜单Action）：

```ts
 {
        container: '.editor-section',
        item: 'Editor',
        props: {
          showHiddenCode, // 是否显示隐藏代码：面试官视角，应该是true, 候选人视角：false
          freezeCode, // 是否让设置的代码区域不能编辑， 面试官视角，应该是false,候选人视角：true
          contextMenu: contextMenu
            ? {
                handleSelect: (e, callback) => {
                  messageBox.info(JSON.stringify(e));
                  const id = Math.floor(Math.random() * 1000);
                  callback?.({
                    id,
                  });
                },
                items: [
                  {
                    action: 'implement',
                    text: '实现功能',
                    icon: 'add-question',
                    iconLabel: '答题区',
                    commentStartLabel: '请在此实现代码功能',
                    // commentTips: '请在此实现代码功能',
                    hideCodeBlock: true,
                  },
                  {
                    action: 'update',
                    text: '修改代码',
                    icon: 'list-mode',
                    iconLabel: '编辑区',
                    commentStartLabel: '请在此修复代码',
                    // commentTips: '请在此修复代码',
                    hideCodeBlock: false,
                  },
                ],
              }
            : null,
        },
      },
```

+ 新增代码块ICON点击事件：

```ts
dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        ...
      case Messages.LineAction: {

          messageBox.info(JSON.stringify(payload));
          break;
        }
      }
```

+ 新增插入代码块功能（根据业务ID插入）

```ts
 //lineId:业务ID，lineText：要插入的字符
 daoPaasObj?.insertTextById(lineId, lineText);
```

+ 新增冻结非设置区域代码功能接口：

```ts
daoPaasObj?.switchFreezeCode();
```

+ 新增由业务方控制是否将操作数据写入CRDT，配置参数如下：

```ts
 const dao = new DaoPaaS({
      ...
      isInsertCrdt: false, //默认值为：true
      ...
    });
```

+ `onMessage`事件新增`ApplicationAvailable`事件类型，用于监听项目运行成功的事件

### 优化

+ 优化文件树构建，限制文件夹最多10层目录，每层文件夹限制最多30个文件

### 修复

+ 修复markdown语法解析错误、初次挂载offsetTop为undefined报错

## v0.9.57

### 修复

+ 修复icon样式问题

## v0.9.56

### 新增

+ 新增`markdown`预览模式功能，提供`MarkdownPreview`类型组件

```ts
// 组件schema配置
{
  container: '.browser-section',
  item: 'MarkdownPreview',
  props: {
    isRender: true,  // 初次是否挂载(true标识为初次挂载|false为不挂载)
  },
},

dao.onMessage((message: Message) =>{
//获取到选中的文件路径
  case Messages.FileInfo: {
     // payload.openedPath  文件路径(可用来获取文件后缀)
  }
...
})
// 控制预览组件的 挂载|销毁 - flag为true表示挂载
daoPaasObj?.toggleMarkDownPreview(flag);
```

+ 新增是否显示隐藏文件的功能
+ 新增超过16兆的文件不写入crdt，暂时不支持预览
+ 编辑器交互优化，新增待更新的提示，点击运行时调用的`runPlayground`方法，返回了promise，如果有还未完成的写入，会返回reject

### 修复

+ 优化编辑器和文件树的UI展示风格
+ 修复文件树锁住和控制文件是否隐藏的报错问题
+ 修复协同报错的bug
+ 修复IDEServer 日志全局截取500字符

## v0.9.55

## 优化

+ 优化MongoDB 表`HistoryCRDTSchema` 索引改为`hashed`类型

## 修复

+ 使用`\t`制表符作为`tab`缩进

## v0.9.54

### 新增

+ 优化浏览器组件UI功能
+ 新增IDEServer 捕获全局异常，Node服务不重启
+ 优化IDEServer
+ lsp新增断线重连机制，断线后的状态会从运行中变为连接中
+ 编辑器新增自动换行配置，默认会自动化换行，新增缩进方式的配置，默认以空格作为缩进，调用方式如下

```js
  daoPaas.updateGlobalConfig({
    //...
    editorWordWrap: true, // or false
    editorIndentMode: 'space' // or tab
    //...
  })
```

+ 新增`terminal`加载进度条UI效果
+ `terminal`和`console`支持通过点击文件路径打开文件树内的文件
+ 新增 `terminal` 语法高亮
+ 支持`Elixir` 代码高亮
+ 语言国际化功能，目前仅支持`ZH(中文)|EN(英文)`（默认中文）

```ts
new DaoPaaS ({
  ...
  i18nLanguage:I18nLanguageType.ZH // i18nLanguage 如果不传递，则默认为： ZH
  })
// 手动调用接口进行语言切换
daoPaasObj?.switchI18nLanguage(lang:I18nLanguageType);
// 国际化支持语言类型
export enum I18nLanguageType {
  /** 中文 */
  ZH = 'ZH',
  /** 英文 */
  EN = 'EN',
}
```

### 修复

+ 修复新题型切换语言接口文件不存在问题
+ 修复Webscoket 长链接不稳定问题
+ 解决编辑器协同问题（undo，redo，暂时屏蔽掉了，再下个版本再上
+ 修复Ide server中获取lock状态时`data.time`为`undefined`报错

## v0.9.52

## 修复

+ 修复代码无法同步和保存问题

## v0.9.51

### 新增

+ 新增统计数据获取接口`statisticsPlaybackData`,使用方式如下：

```js
  const res = await daoPaasObj?.statisticsPlaybackData(
                  eventName,
                  questionIds.split(',').map((item) => +item),
                );
```
>
> 1. `eventName` 统计的事件名称，如：`editor`, `fileTree`...
> 2. `questionIds` 表示要统计的问题ID集合，如`[1,2,3]`,表示汇总三个问题的数据
> 3. 返回的数据`data`是一个问题和统计结果的对象，问题ID作为对象ID,如：
>
```js
res = {
  status: 'success',
  data: {
    '1': 120,
    '2': 211,
    '3': 999
  }
}
```

### 修复

+ 修复切题模式，无法新建文件并且编辑文件不触发文件更新问题

## v0.9.50

### 新增

+ 新增切换编程语言接口`daoPaasObj?.switchLanguage`,返回`Promise`,ß使用方式如下：

```js
 const res = await daoPaasObj?.switchLanguage(
                  languageType,
                  languageValue,
                  languageFileName,
                );
                messageBox[res?.status ? 'info' : 'error'](res?.message);
```
>
> 1. languageType:要切换的文件类型：支持：【html, js, css】
> 2. languageValue: 要切换的目标语言，每种文件类型支持的目标语言映射表：
>
```js
{
  HTML: ['html', 'pug', 'hbs', 'md'],
  CSS: ['css', 'less', 'scss', 'sass', 'styl'],
  JS: ['js', 'ts', 'coffee'],
}
```
>
> 3. languageFileName: 修改对应的文件名称，如： `index.html`, `index.js`, `index.ts`

+ 新增向`index.html`文件中注入CDN资源，返回`Promsie`,使用方式如下：

```js
const res = await daoPaasObj?.addCDNSource(
                  cdnUrlType,
                  cdnUrlPath,
                  cdnUrlFileName,
                );
messageBox[res?.status ? 'info' : 'error'](res?.message);
```
>
> 1. cdnUrlType: 要注入CDN资源的类型，支持： 【js, css】
> 2. cdnUrlPath: 要注入的CDN的具体URL地址
> 3. cdnUrlFileName: 要注入CDN资源的文件名称，目前只支持`index.html`文件

+ 支持全局配置，目前可设置默认缩进的大小和编辑器的模式，调用方式如下

```javascript
// ...
dao.updateGlobalConfig({
   editorMode: 'default', // 支持的参数为 default | vim | emacs
   editorTabSize: 2,
})
// ...
```

**注意**: 以上配置的属性都是可选项，若不设置会采用默认的设置，默认编辑模式就是文本编辑模式，缩进默认是2，若之前设置过，则会使用之前的设置

+ 优化提示交互体验，在切题时，需要调用`reload`方法，该方法可以重置一些loading状态和初始数据，方法接收一个数组为参数，不传默认为`['Editor', 'Tree']`, 该方法一经调用，便会清空当前的文件树和编辑器，进入组件的loading状态，避免出现已经切题，上层已经loading，SDK依然显示旧数据的情况

```javascript
dao.reload()
```

+ 优化IDE server新增fileRootId，编辑器的OT相关操作和文件树操作的相关操作，会携带fileRootId参数到IDE Server，IDE Server会判断传进来的fileRootId是否和服务器匹配，不匹配就会放弃后面的操作
+ 添加处理IDE Server的错误，通过机器人发送到企业微信群进行错误告警
+ 优化编辑器组件通过fileRootId与当前文件路径作为key来缓存state数据，若能命中缓存，将不会再生成新的协同插件，而是采用之前的缓存，来避免偶发的多次syncOt操作

## v0.9.49

### 新增

+ 回放功能，事件`PlaybackInfo` 暴露所有自定义帧数据`customize`,并计算每一帧数据相对于第一帧数据的时间`costTime`

### 修复

+ 修复回放功能，拉动滚动条，再点击播放按钮问题
+ 修复回放功能，功能面板如Editor，不能拉动滚动条问题

## v0.9.48

### 新增

+ 回放功能，新增停止回放接口

```js
daoPaasObj.stopPlayback();
```

+ 回放功能，在回放过程中，新增广播自定义数据帧事件

```js
 dao.onMessage((message: Message) => {
      const { name, payload } = message;
      setSpinLoading(false);
      switch (name) {
        ....
        case 'customize': {
          // 可以根据：payload.data.action 来判断具体是哪种自定义数据帧
          messageBox.success('Customize Event:', payload.data.action);
          console.log(payload);
          break;
        }
      }
    });
```

+ 新增数据帧前后两帧数据间隔时长超过指定的阈值，广播事件，自定义事件名称跟上面一样：`customize`,其`action` 为： `timespanEvent`

+ 将上述具体时间间隔的数值通过初始化`DaoPassS`对象进行配置：

```js
    const dao = new DaoPaaS({
      paasDomain: process.env.PAAS_MANAGER_API_ORIGIN,
      tenantId: '3',
      ticket: dataRes?.ticket,
      triggerEventForMaxTimespan: 1000 * 2, // 配置该属性，如果不配置该属性值，则不会广播事件
      userInfo: {
        username: DemoUserInfo.name,
      },
    });
```

+ 将`websocket`断开重连的尝试次数，控制交由业务方进行控制，如果业务方不配置， 则默认为重连**5次**，则广播对应的事件

```js
   const dao = new DaoPaaS({
      paasDomain: process.env.PAAS_MANAGER_API_ORIGIN,
      tenantId: '3',
      ticket: dataRes?.ticket,
      reconnectMaxTimes: 6, // 配置改属性，如果不配置，则默认为最大重试5次
      userInfo: {
        username: DemoUserInfo.name,
      },
    });
```

+ 新增拖拽文件或文件夹到指定目录
+ 新增拖拽文件或文件夹并上传到指定目录
+ 新增下载项目文件

```js
dao.mapRender([
  ...
  {
    container: '.tree-section',
    item: 'Tree',
    props: {
      ...
      fileRootActions: [
        {
          label: '下载项目',
          onClick: () => {
            getProjectCode(playgroundId).then((res) => {
              const blob = new Blob([res.data]);
              const url = window.URL.createObjectURL(blob);
              const ele = document.createElement('a');
              ele.style.display = 'none';
              ele.href = url;
              ele.download = 'download.zip'; // 下载文件夹的名称业务方进行自定义， 后缀可以从res header 获取
              document.body.appendChild(ele);
              ele.click();
              ele.remove();
            });
          },
        },
      ],
      ...
    },
  },
]);
```

## 修复

+ 修复回放模式， 打开文件但是没有聚焦文件，导致在回放过程中文件为空

## v0.9.47

### 新增

+ 回放模式样式优化(鼠标状态为：`not-allowed`)
+ 回放模式，拉动播放滚动条，自动显示对应的帧的数据

### 修复

+ 修复代码编写时同一层级下出现两个相同的文件的问题
+ 修复偶现文件重命名失败问题

## v0.9.46

### 新增

+ 支持图形化界面展示功能
+ 支持Websocket 连接失败，重连指定次数（5次）后,仍连接失败， 广播`connectionBroken`事件给客户端进行处理

```js
 dao.onError((message: Message) => {
      const { name, error } = message;
      switch (name) {
        case Messages.ConnectionBroken:
          messageBox.error(error, 0);
          break}
  ...
 }
```

+ 新增回放功能，拖动播放滚动条后，再点击播放，在之前指定位置进行播放

### 修复

+ 修复编辑器`Position 98 is out of range for changeset of length 97`的报错
+ 修复协同模式，后进入用户不能同步问题
+ 修复fork项目，打开默认文件报错问题
+ 修复回放自定义帧，因客户端插入数据不准确进行容错处理

## v0.9.45

## 修复

+ 解决编辑器报`Applying change set to a document with the wrong length`的错误

## v0.9.44

### 新增

+ Terminal 进行文件操作（增删改等行为），也支持回放处理

## 修复

+ 修复在文件树快速切换文件（当文件还没在编辑器里打开时），造成编辑器闪动问题

## v0.9.43

## 修复

+ 修复切换环境或者切题后LSP不可用的问题
+ 修复C++语言LSP不可用的问题
+ 移除file watcher, 解决文件树 同步慢的问题。

## v0.9.42

### 新增

+ 回放功能支持多题目切题回放
+ 回放接口添加支持自定义数据帧插入：

```js
  // 插入自定义数据帧
 dao?.appendCustomizeFrameData({
    action: 'startQuestion', // startQuestion | endQuestion
    value: 1, // 题目ID
  });
```

```js
// 监听自定义数据帧插入是否成功事件
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        ...
        case 'appendCustomizeFrameData': {
          if (payload.status) {
            messageBox.success('插入数据帧成功');
          } else {
            messageBox.warn(`插入数据帧失败`);
          }
        }
      }
    });
```

+ 回放接口支持获取每个题目的数据帧情况（解析：questionsData内容 ）

```js
       dao.onMessage((message: Message) => {
        const { name, payload } = message;
        setSpinLoading(false);
        switch (name) {
          ...
          case Messages.PlaybackInfo: {
            // 解析： questionsData 数据内容
            setQuestionsData(payload.questionsData);
            if (payload.total > 0) {
              setPlayable(true);
              setPlaybackDuration(payload.end - payload.start);
            }
            break;
          }
        }
      });
```

### 修复

+ 历史Vue项目空间打开一直处于加载中的问题

## v0.9.41

### 修复

+ LSP未生效问题

## v0.9.40

### 新增

+ 回放功能播放和暂停重新播放接口重构
+ 重构Pass平台日志系统，便于问题排查

### 修复

+ Terminal 换行问题
+ 修复退出登录后，登录新用户显示旧用户的相关信息的问题
+ 修复切题模式或者切换语言时，打开指定文件，**需要在playgroundChanged时打开文件**

## v0.9.38

### 新增

+ 添加保留文件不能删除的功能,使用方式：

```js
    // 1. 设置保留文件属性的接口
    const res = await daoPaasObj?.retainingAllFiles();
    if (res?.status) {
      messageBox.success(res.message);
    } else {
      messageBox.error(res?.message);
    }

```

```js
  // 设置是否对保留文件进行处理
  dao.mapRender([
      {
        container: '.tree-section',
        item: 'Tree',
        props: {
          disableFileAction: { // 不是必填属性
            filesPath: [],
            isDisable: true,
          },
        },
      },
    ]);
```

+ 增加LSP真实可用的状态，真实运行中的状态由之前的running改为了lspReady

### 修复

+ 修复打开默认文件文件导航栏丢失的问题
+ 修复文件树Feature等属性，在对文件进行重命名属性丢失，删除文件后重新创建相同名称文件Feature属性仍然存在的问题
+ 修复压力测试发现的问题

## v0.9.36

### 重构

+ 重构打开指定文件接口，支持Promise形式调用,需要调用方自行捕获错误信息(reject错误)

### 新增

+ 添加支持回放功能

### 修复

+ 修复Python LSP 光标问题

## v0.9.35

### 修复

+ 修复编辑器client version too big 的问题
+ 修复文件树feature设置单一属性，影响其他feature属性的问题
+ 修复button 组件的样式问题

## v0.9.34

### 重构

+ 重构文件树更新逻辑，数据由全量更新修改为增量更新

### 新增

+ 新增打开指定文件的接口，`openFile(filePath:string) => void`,<font color=red>注意：该路径是相对路径</font>，调用示例方式如下：

```js
const daoPass = new DaoPass()
...
daoPass.openFile('path')
...
```

+ 切换文件再切回时，支持了`undo`,`redo`等操作

### 修复

+ 修复lsp服务器连接多次问题
+ 修复lsp连接失败问题
+ 修复编辑器协同模式, selection的问题

## v0.9.30

当前版本变更引用地址 ([294ae84289180e2c94d2589d0e435a9f54e530a2](https://github.com/dao42/d42paas_frontend/commit/294ae84289180e2c94d2589d0e435a9f54e530a2))

### 新增

+ Editor组件支持了打开默认文件，需要在mapRender时，给Editor的props设置defaultOpenFile, 值为文件路径，注意该路径是相对路径，而不是完整路径, 例如需要打开文件树根目录的index.js文件，只需要传入index.js即可，而不需要传入/xxx/xxx/index.js
+ 新增LSP语法提示和校验（但目前还需要继续完善，如LSP真实准备和连接完成的状态等），现在可以先测试。

### 修复

+ 切题时清空编辑器

### 重构+优化

+ 移除了引用的第三方库rsuite，自己封装了一些自定义组件，路径在packages/client/src/components/base下

## v0.9.29

+ **0.9.29:** 当前版本变更引用地址 ([8935c97f9c6c36cfa74a58f3eda12bf562184ebc](https://github.com/dao42/d42paas_frontend/commit/8935c97f9c6c36cfa74a58f3eda12bf562184ebc))

### 优化

+ 上传文件和上传文件夹限制了最大文件数量500
+ SDK新增了transitionCustomError方法，组件内可向上层通知错误信息（store.dao.channel().transitionCustomError({name: 'xxx', error: ''})
+ 文件树上传超过数量限制，会向上派发onError， 错误参数中的name字段为错误类型，值是upload，error为错误消息，上层可自由控制是否弹出错误提示和定制自己的样式

## v0.9.28

+ **0.9.28:** 当前版本变更引用地址 ([308f32e15f00c076b94dd679c2a22a09cb487215](https://github.com/dao42/d42paas_frontend/commit/308f32e15f00c076b94dd679c2a22a09cb487215))

### 优化

+ 拖拽上传时，移除拖拽目录的文件树监听，上传成功后恢复监听，避免IDE server多次推送更新到SDK
+ 删除文件夹时，移动当前文件夹的监听，删除后恢复

### 修复

+ IDEServer打印日志，排除Buffer
+ 删除文件时清空文件缓存
+ 删除文件夹时，清空以当前文件夹开头的缓存

### 样式

+ 修改文件树操作面板样式

# PaaS SDK 示例

欢迎来到PaaS SDK示例，本文是DaoPaaS的一个简单使用示例。

温馨提示
- SDK接口文档[api地址](https://www.apifox.cn/apidoc/shared-c0c0ebad-15b3-4605-896e-e39879fe6e47/doc-952073)
- 本案例的axios为封装后的axios, baseUrl默认为“www.1024paas.com”。
- 真实接口请求过程中，请按照接口文档的要求携带头信息。
- 本案例使用React，非React项目请参考逻辑。
- 请用户自行优化代码逻辑并做合适的错误处理。

## PART-ONE 请求获取ticket(票)流程

### 第一步：定义接口

```ts
/* 
 * 获取codeZone环境列表 
 * @returns { {id: int, versionList: {id: string, name: string}[]}[] }
*/
export async function getEnvironmentsApi() {
    const res = await axios.get('/api/v1/sdk/environments')
    return res.data // 环境列表(数组)
}
/* 
 * 创建codeZone, 获取codeZoneId
 * @param { string } environmentVerId 环境id
 * @returns { string }
*/
async function getCodeZoneIdApi(environmentVerId: string): string {
    const res = await axios.post('/api/v1/sdk/codeZones',{environmentVerId})
    return res.data.id
}
/* 
 * 通过 codeZoneId 获取 playgroundId
 * @param { string } codeZoneId 
 * @returns { string }
*/
async function getPlaygroundIdApi(codeZoneId: string): string {
    const res = await axios.get('/api/v1/sdk/codeZones/'+codeZoneId+'/playground')
    return res.data.id
}
/* 
 * 通过 playgroundId 获取 ticket
 * @param { string } playgroundId 
 * @returns { string }
*/
async function getTicketApi(playgroundId: string): string {
    const res = await axios.post('/api/v1/sdk/ticket', {
        playgroundId,
        tillTime: Date.now() + 12*60*60*1000, // 截止时间(时间戳)
        userInfo: {}, // 数据格式请参考接口文档
    })
    return res.data.ticket
}
```

### 第二步：封装逻辑

```ts
// 通过调用 getEnvironmentsApi() 我们获取到了codeZone环境列表, 并渲染到页面上
const environments = await getEnvironmentsApi()
// 假设用户点击了某一个环境，我们得到了一个具体的 environmentVerId
const environmentVerId = environments[0].versionList[0].id
// 封装获取ticket(票)逻辑
export async function handleGetTicket(environmentVerId: string) {
    const codeZoneId = await getCodeZoneIdApi(environmentVerId)
    const playgroundId = await getPlaygroundIdApi(codeZoneId)
    const ticket = await getTicketApi(playgroundId)
    return ticket
}
```

## PART-TWO 在组件中使用

### 第一步：引入SDK和样式
```ts
import { DaoPaaS, Messages } from 'DaoPaaS.cjs';
import 'style.css' 
```

### 第二步：初始化实例

1. 创建实例

```ts
const dao = new DaoPaaS({
    tenantId: '租户id',
    ticket: '动态获取的ticket', // ticket(票)
    userInfo: { username: '用户名'}, // 用户信息
});
```

2. 监听消息

```ts 
dao.onMessage((message: Message) => {
    const { name, payload } = message;
    let status: PlaygroundStatus;
    let dockerStatus: DockerStatus;
    let lspStatus: LspStatusEnum;
    switch (name) {
    case Messages.Ready:
        console.log('appStatus readyBasic');
        console.log('playgroundInfo状态', dao.playgroundInfo.status);
        console.log('语法补全状态', dao.playgroundInfo.lspStatus);
        console.log('docker运行状态', dao.playgroundInfo.runStatus);
        break;
    case Messages.Online:
        console.log('网络已恢复');
        setSpinLoading(false);
        break;
    case Messages.Offline:
        console.log('网络掉线');
        break;
    case Messages.StatusChanged:
        console.log('playground状态发生变化', payload.status);
        break;
    case Messages.PlaygroundChanged:
        console.log('playground环境变了，比如换题');
        break;
    case Messages.LspStatusChanged:
        console.log('Lsp状态发生变化', payload.status);
        break;
    case Messages.RunStatusChanged:
        console.log('docker运行状态变化', payload.status);
        if (dockerStatus != 'STOP') {
            return;
        } else if (payload.runResult === 0) {
            console.log(`运行结果成功`);
        } else {
            console.log(`运行结果失败: ${payload.runResult}`);
        }
        break;
    }
});
```

3. 监听错误消息

```ts
dao.onError((message: Message) => {
    const { name, error } = message;
    switch (name) {
    case Messages.ConnectionBroken:
        console.log('与服务器的连接中断，可以尝试连接到另一个服务器');
        break;
    case Messages.AuthorizationFailed:
        console.log('授权失败，所以您需要再次登录或获得一个新的ticket');
        break;
    case 'errorFromServer':
        console.log('与服务器的连接中断');
        break;
    }
});
```

4. 把实例的组件挂载到dom节点

```ts
// container: 项目中真实节点的选择器, item: 要挂载的dao内部组件的名称
// 这个方法运行后，会把item对应名称的dao内部组件挂载到container配置类名的dom节点上
dao.mapRender([
    { container: '.tree-section', item: 'Tree' },
    { container: '.editor-section', item: 'Editor' },
    { container: '.browser-section', item: 'Browser'},
    { container: '.terminal-section', item: 'Shell' },
    { container: '.console-section', item: 'Console' },
]);
```

5. 创建HTML标签

运行`dao.mapRender([...])`方法后，会把dao内部组件渲染到对应的dom节点

```html
<div>
    <div className="tree-section"></div>
    <div className="editor-section"></div>
    <div className="browser-section"></div>
    <div className="console-section"></div>
    <div className="terminal-section"></div>
</div>
```

### 第三步：销毁实例

在页面卸载的时候需要销毁dao实例和相关的缓存数据
```ts
useEffect(() => {
    // ...
    return () => {
        dao.dispose()
    }
}, [])
```

## PART-THREE SDK部分其他方法

```ts
// 配置是否在控制台开启DaoPaaS内部打印
dao.loggerEnable(true);
// 修改主题
dao.loadTheme('dark')
// 激活容器
dao.activePlayground()
// 运行容器
dao.runPlayground()
// 停止容器运行
dao.stopPlayground()
// ...
```


## PART-FOUR 基础案例

```tsx
import React, { useEffect, useState } from 'react';
import { DaoPaaS, Messages } from '~/DaoPaaS';

enum LspStatusEnum {
  NOT_SUPPORT = 'notSupport',
  LOADING = 'loading',
  RUNNING = 'running',
  SHUTDOWN = 'shutdown',
}

export default function SimpleExample({environmentVerId}) {
  const [daoPaasObj, setDaoPaasObj] = useState<DaoPaaS | null>(null);
  const [playgroundStatus, setPlaygroundStatus] =
    useState<PlaygroundStatus>('EMPTY');
  const [dockerStatus, setDockerStatus] = useState<DockerStatus>('STOP');
  const {setLspStatus, LspStatusFC} = useLspStatus();
  const [remindVisible, setRemindVisible] = useState(false);

  // SDK初始化
  const initSDK = async () => {
    console.log('开始连接运行环境...')
    // 获取ticket(票)
    const ticket = handleGetTicket(environmentVerId)
    // 生成实例
    const dao = new DaoPaaS({
      paasDomain: 'www.1024paas.com',
      tenantId: '请输入租户id',
      ticket: ticket,
      userInfo: { username: "***" },
    });
    // 保存实例
    setDaoPaasObj(dao);
    // 监听消息
    dao.onMessage((message: Message) => {
      const { name, payload } = message;
      let status: PlaygroundStatus;
      let dockerStatus: DockerStatus;
      let lspStatus: LspStatusEnum;
      switch (name) {
        case Messages.Ready:
          console.log('连接运行环境已经停止')
          setRemindVisible(dao.playgroundInfo.status === 'INACTIVE');
          setPlaygroundStatus(dao.playgroundInfo.status);
          setLspStatus(
            dao.playgroundInfo.lspStatus as LspStatusEnum,
          );
          setDockerStatus(
            dao.playgroundInfo.runStatus as DockerStatus,
          );
          break;
        case Messages.Online:
          console.log('网络已恢复');
          console.log('连接运行环境已经停止')
          break;
        case Messages.Offline:
          console.log('网络掉线');
          break;
        case Messages.StatusChanged:
          status = payload.status;
          setRemindVisible(status === 'INACTIVE');
          setPlaygroundStatus(payload.status);
          break;
        case Messages.PlaygroundChanged:
          setRemindVisible(false);
          break;
        case Messages.LspStatusChanged:
          lspStatus = payload.status;
          setLspStatus(lspStatus);
          break;
        case Messages.RunStatusChanged:
          dockerStatus = payload.status;
          setDockerStatus(dockerStatus);
          if (dockerStatus != 'STOP') {
            return;
          }
          if (payload.runResult === 0) {
            console.log(`运行结果成功`);
          } else {
            console.warn(`运行结果失败: ${payload.runResult}`);
          }
          break;
      }
    });
    // 监听报错消息
    dao.onError((message: Message) => {
      const { name, error } = message;
      switch (name) {
        case Messages.ConnectionBroken:
          console.error(error, 0);
          break;
        case Messages.AuthorizationFailed:
          console.error(error, 0);
          break;
        case 'errorFromServer':
          console.log('连接运行环境已经停止')
          console.error(error, 0);
          break;
      }
    });
    // 挂载实例组件
    dao.mapRender([
      { container: '.tree-section', item: 'Tree'},
      { container: '.editor-section', item: 'Editor'},
      { container: '.browser-section', item: 'Browser'},
      { container: '.terminal-section', item: 'Shell'},
      { container: '.console-section', item: 'Console'},
    ]);
  };
  // 语法补全连接状态
  function useLspStatus() {
    const [lspStatus, setLspStatus] = useState<LspStatusEnum>(
      LspStatusEnum.LOADING,
    );
    function LspStatusFC() {
      const textMap = {
        [LspStatusEnum.NOT_SUPPORT]: '不支持',
        [LspStatusEnum.LOADING]: '连接中',
        [LspStatusEnum.RUNNING]: '运行中',
        [LspStatusEnum.SHUTDOWN]: '已关闭',
      };
      return (
        <div> 代码补全·({textMap[lspStatus]})</div>
      );
    }
    return {
      setLspStatus,
      LspStatusFC,
    };
  }
  useEffect(() => {
    initSDK();
    return () => {
      // 在页面卸载的时候需要销毁实例和相关的缓存数据
      daoPaasObj && daoPaasObj.dispose();
      setDaoPaasObj(null);
    };
  }, []);
  return (
    <>
      {remindVisible && <button
        onClick={() => {
          daoPaasObj?.activePlayground();
          setRemindVisible(false);
        }}
      >
        容器状态为失活，点击确认激活容器
      </button>}
      <div>
        <LspStatusFC />
        <div>
          {playgroundStatus !== 'ACTIVE' ? (
            <button onClick={() =>  daoPaasObj?.activePlayground() }>激活</button>
          ) : (
            <div>已激活</div>
          )}
          {dockerStatus !== 'RUNNING' ? (
            <button
              disabled={playgroundStatus !== 'ACTIVE'}
              onClick={() => {
                daoPaasObj?.runPlayground();
              }}
            >
              运行
            </button>
          ) : (
            <button
              disabled={playgroundStatus !== 'ACTIVE'}
              onClick={() => daoPaasObj?.stopPlayground()}
            >
              停止
            </button>
          )}
        </div>
      </div>
      <div>
        <div className="tree-section"></div>
        <div className="editor-section"></div>
        <div className="browser-section"></div>
        <div className="console-section"></div>
        <div className="terminal-section"></div>
      </div>
    </>
  );
}
```