diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..b0385219db4a2d4803659968015dfe356a0f4719
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,19 @@
+trace_streamer/out
+.vscode
+.idea
+ide/bin
+ide/cert
+ide/dist
+ide/node_modules
+ide/package-lock.json
+ide/third-party
+trace_streamer/prebuilts/emsdk
+trace_streamer/prebuilts/linux
+trace_streamer/third_party
+trace_streamer/compile_commands.json
+trace_streamer/.cache
+tmp_*
+build-*
+*.pro.use*
+.DS_Store
+._.DS_Store
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..261eeb9e9f8b2b4b0d119366dda99c6fd7d35c64
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/OAT.xml b/OAT.xml
new file mode 100644
index 0000000000000000000000000000000000000000..961fd6b4150b9d37732c0ee204cc3592490b61dd
--- /dev/null
+++ b/OAT.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/README.md b/README.md
index 1e04572025d5a94157943bfe9f1f0b2263bd8907..3d93077a0aa0beaa8c81e502e3772e88a6ef7f50 100644
--- a/README.md
+++ b/README.md
@@ -1,39 +1,69 @@
-# developtools_smartperf_host
+# Smartperf-Host
+## 简介
+Smartperf-Host是一款深入挖掘数据、细粒度地展示数据的性能功耗调优工具,旨在为开发者提供一套性能调优平台,支持对CPU调度、频点、进程线程时间片、堆内存、帧率等数据进行采集和展示,展示方式为泳道图,支持GUI(图形用户界面)操作进行详细数据分析。
+## 架构图
+
+该组件整体分为设备端和PC端两部分,设备端和PC端基于gRPC(Remote Procedure Call)通信框架进行数据交互。
-#### 介绍
-{**以下是 Gitee 平台说明,您可以替换此简介**
-Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台
-无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)}
+设备端内部分为应用程序内嵌组件、命令行工具、性能调优服务、性能调优插件集合、部分系统工具及部分系统内核等模块。设备端提供了插件扩展能力,对外提供了插件接口,基于该扩展能力可以按需定义自己的能力,并集成到框架中来,目前基于插件能力已经完成了native内存插件、trace插件等,详细介绍见[性能调优组件](https://gitee.com/openharmony/developtools_profiler)。
-#### 软件架构
-软件架构说明
-
-
-#### 安装教程
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 使用说明
-
-1. xxxx
-2. xxxx
-3. xxxx
-
-#### 参与贡献
-
-1. Fork 本仓库
-2. 新建 Feat_xxx 分支
-3. 提交代码
-4. 新建 Pull Request
-
-
-#### 特技
-
-1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
-2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com)
-3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目
-4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目
-5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
-6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
+PC端以Smartperf-Host网站的形式进行发布,内部分为Trace Streamer数据解析、SQLite数据存储、hdc设备管理、数据导入、UI绘制、数据分析等模块。下文会重点对Smartperf-Host提供的各项能力进行介绍。
+## 项目目录
+```
+/developtools/smartperf_host
+├── figures # 图片资源
+├── ide # Smartperf-Host IDE 模块目录
+│ └── src # 主机测调优模块代码
+│ │ ├── base-ui # 基础组件目录
+│ │ └── Trace # 业务逻辑目录
+├── trace_streamer # 解析模块代码目录
+│ ├── base # 基础功能
+│ ├── cfg # 配置目录
+│ ├── filter # Filter 功能
+│ ├── include # Include 头文件
+│ ├── multi_platform # 平台适配
+│ ├── parser # 解析业务逻辑
+│ │ ├── bytrace_parser # byTrace 解析业务逻辑
+│ │ └── htrace_parser # hTrace 解析业务逻辑
+│ ├── table # 表结构
+│ ├── trace_data # trace 结构
+│ ├── trace_streamer # traceStreamer 结构
+│ └── kits # js/napi 接口存放目录
+```
+## 功能介绍
+### 网页加载trace
+使用Smartperf-Host加载保存在本地的trace文件(htrace、ftrace等)并显示数据到泳道图中,trace数据分析详见《[网页加载trace说明](./ide/src/doc/md/quickstart_systemtrace.md)》。
+### 网页抓取trace
+使用Smartperf-Host在线抓取trace,可以自定义抓取内容、抓取时长、trace保存路径,详见《[网页抓取trace说明](./ide/src/doc/md/quickstart_web_record.md)》。
+### 设备抓取trace
+在设备端抓取trace,可以自定义抓取内容、抓取时长、trace保存路径,详见《[设备端抓取trace说明](./ide/src/doc/md/quickstart_device_record.md)》。
+### Ability Monitor抓取
+使用Smartperf-Host抓取应用的CPU、内存、磁盘IO和网络的使用情况,详见《[Ability Monitor抓取和展示说明](./ide/src/doc/md/quickstart_ability_monitor.md)》。
+### Native Memory抓取
+使用Smartperf-Host抓取应用的Native Memory(C和C++部分)的分配和释放情况,详见《[Native Memory抓取和展示说明](./ide/src/doc/md/quickstart_native_memory.md)》。
+### Hiperf抓取
+使用Smartperf-Host抓取应用的cpu使用量、方法的调用栈等,详见《[HiPerf的抓取和展示说明](./ide/src/doc/md/quickstart_hiperf.md)》。
+### HiSystemEvent抓取
+使用Smartperf-Host抓取应用的各个子类别功耗占比(CPU、网络、定位等)、应用的资源申请使用记录(WorkScheduler、Runninglock、Alarm、Location Request)、应用功耗异常事件显示、功耗关联系统状态显示(电池电量、屏幕状态),详见《[HiSystemEvent的抓取和展示说明](./ide/src/doc/md/quickstart_hisystemevent.md)》。
+### FileSystem抓取
+使用Smartperf-Host抓取所有文件系统系统调用信息、读写调用次数等,详见《[FileSystem的抓取和展示说明](./ide/src/doc/md/quickstart_filesystem.md)》。
+### 页内存抓取
+使用Smartperf-Host抓取页内存相关事件的开始时间、持续时间、触发进程、触发线程、事件类型、内存地址、内存大小等,详见《[页内存的抓取和展示说明](./ide/src/doc/md/quickstart_page_fault.md)》。
+### Bio抓取
+使用Smartperf-Host抓取每次IO访问的起始时间、总延迟、进程、每4k数据的平均延迟、线程、操作(写数据、页面换入、Metadata)、访问量、路径等、Block number、优先级、Backtrace调用栈,详见《[Bio的抓取和展示说明](./ide/src/doc/md/quickstart_bio.md)》。
+### 进程Smaps抓取
+使用Smartperf-Host抓取单个进程的smaps数据(类别、Pss、Rss、Vss等),数据源为/proc/$pid/smaps,详见《[进程smaps的抓取和展示说明](./ide/src/doc/md/quickstart_smaps.md)》。
+### Sql分析和Metrics说明
+Smartperf-Host网站trace解析完成后在线数据库使用说明,详见《[Sql分析和Metrics说明](./ide/src/doc/md/quickstart_sql_metrics.md)》。
+## 编译指南
+项目编译主要包括两部分,Trace Streamer编译和Smartperf-Host编译部署。
+### 构建约束
+- C++ 11或以上
+- node 版本 >= 16.15.1
+- npm 版本 >= 8.13.2
+- TypeScript 版本 >= 4.2.3
+- golang 版本 >= 1.13.8
+### Trace Streamer编译
+搭建Smartperf-Host网站需要编译出trace_streamer的wasm版本供网页端进行原始trace数据解析工作,具体的编译过程参考《[如何独立编译Trace Streamer](./trace_streamer/doc/compile_trace_streamer.md)》。
+### Smartperf-Host编译部署
+具体的编译部署过程参考《[SmartPerf 编译部署指导](./ide/README_zh.md)》,部署成功后通过浏览器访问页面 https://[部署机器ip地址]:9000/application/ 即可使用Smartperf-Host的全部功能。
diff --git a/figures/smartperf_frame.png b/figures/smartperf_frame.png
new file mode 100755
index 0000000000000000000000000000000000000000..3ed5bb1d08cd1269078374e8ca2e962199f90e65
Binary files /dev/null and b/figures/smartperf_frame.png differ
diff --git a/ide/LICENSE b/ide/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..e454a52586f29b8ce8a6799163eac1f875e9ac01
--- /dev/null
+++ b/ide/LICENSE
@@ -0,0 +1,178 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/ide/README_zh.md b/ide/README_zh.md
new file mode 100644
index 0000000000000000000000000000000000000000..2c73b0ac620af594982288d8a28269e89cc6200c
--- /dev/null
+++ b/ide/README_zh.md
@@ -0,0 +1,121 @@
+# SmartPerf 编译部署指导文档
+
+
+## 编译环境搭建
+注意:在linux编译环境安装时以root或者其他 sudo 用户身份运行下面的命令。
+### node 环境安装
+##### 下载Node js安装包(windows推荐, linux跳过此步骤)
+从网站下载node js安装包 https://nodejs.org/en/download/current/。
+##### 安装nodejs
+- ubuntu 20.04 与Debian 11系统中,直接用apt-get安装,先切换到 root用户下,命令如下(node 版本 >= 16.15.1 npm 版本 >= 8.13.2)
+```
+ sudo su
+ apt-get update
+ apt-get install nodejs npm
+```
+
+
+
+- centos 系统中使用yum安装,先切换到root用户下,命令如下:
+```
+ sudo su
+ sudo yum -y install nodejs npm
+```
+
+
+- windows系统中, 用安装包一路next即可
+
+
+- 安装完成后运行检查是否安装成功
+```
+ node -v
+ npm -v
+```
+
+
+ 出现版本号就代表安装成功了。
+
+##### 更换npm源
+```
+ npm config set registry http://registry.npmmirror.com
+```
+
+##### 安装tsc typeScript 编译器
+直接使用npm 安装运行命令。
+```
+ npm install -g typescript
+ tsc -v
+```
+
+ 验证安装完成:
+
+
+### go 编译环境安装
+- ubuntu 环境下直接使用apt安装,以root用户执行(go 版本 >= 1.13.8 )
+```
+ apt-get install golang-go
+```
+
+
+- centos系统中使用yum安装,先切换到root用户下,命令如下:
+
+```
+ sudo su
+ sudo yum -y install go
+```
+
+
+- windows 系统下 从 https://golang.google.cn/dl/ 下载安装包, 一路next 完成 安装即可
+
+- 安装完成后 命令行运行验证是否安装成功
+
+```
+ go version
+```
+## 项目编译
+#### 先下载sql.js的二进制包
+从如下 https://github.com/sql-js/sql.js/releases/download/v1.6.2/sqljs-all.zip 获取到sql.js的二进制包。
+将压缩包解压后, 将文件放置到项目third-party 目录下。
+
+
+
+
+#### 先编译获取trace_streamer 的二进制包
+参照:smartperf/trace_streamer/compile_trace_streamer.md 编译出wasm、linux、Windows版本的二进制文件。
+将获取到二进制文件放入到项目bin目录下,如果项目目录中无bin目录 先创建bin目录。
+然后将trace_streamer的二进制文件放入bin目录中。
+
+
+
+
+
+#### 代码编译(依赖于上面node环境 和 go环境)
+在项目目录安装项目依赖:
+```
+ npm install
+```
+在项目目录下运行命令:
+```
+ npm run compile
+```
+
+ 编译成功后会有main 可执行文件生成。
+
+## 项目部署
+linux版本部署需要给trace_stream程序赋予执行权限,cd dist/bin 目录下,执行如下命令:
+```
+ chmod +x trace_streamer_*
+```
+
+
+直接运行 ./main 可执行程序,完成项目的部署。
+
+ ## 访问项目
+在浏览器上打开 https://[部署机器ip地址]:9000/application/
+!!! 注意一定是https。
+
+
+
+ 备注:如果未出现如图所示网页.而是显示 无法访问此网站。
+可以在window cmd 里执行telnet [部署机器ip地址] 9000。
+如果显示端口连接失败 可能是防火墙未对9000 端口放开即可。
\ No newline at end of file
diff --git a/ide/build.js b/ide/build.js
new file mode 100644
index 0000000000000000000000000000000000000000..6d2f658759a210dec8d2b227ba7bafc75848e6eb
--- /dev/null
+++ b/ide/build.js
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const path = require('path');
+const fs = require('fs');
+const child_process = require('child_process');
+const os = require('os');
+const log4js = require('log4js');
+
+const compileServer = true;
+const outDir = 'dist';
+
+const sdkWams = [
+ 'trace_streamer_sdk_builtin.js',
+ 'trace_streamer_sdk_builtin.wasm',
+ 'trace_streamer_dubai_builtin.js',
+ 'trace_streamer_dubai_builtin.wasm',
+];
+
+const staticPath = ['/src/img', '/server/cert', '/src/doc', '/src/figures'];
+
+const staticFiles = [
+ '/server/version.txt',
+ '/src/index.html',
+ '/src/base-ui/icon.svg',
+ '/server/wasm.json',
+ '/server/server-config.txt',
+];
+
+const thirdParty = [
+ {
+ srcFilePath: '/third-party/sql-wasm.wasm',
+ distFilePath: '/trace/database/sql-wasm.wasm',
+ },
+ {
+ srcFilePath: '/third-party/sql-wasm.js',
+ distFilePath: '/trace/database/sql-wasm.js',
+ },
+ {
+ srcFilePath: '/third-party/worker.sql-wasm.js',
+ distFilePath: '/trace/database/worker.sql-wasm.js',
+ },
+];
+
+let log;
+
+function cpFile(from, to) {
+ if (fs.existsSync(from)) {
+ fs.writeFileSync(to, fs.readFileSync(from));
+ log.info('cp file %s to %s', from, to);
+ } else {
+ log.warn('file %s is not exists', from, to);
+ }
+}
+
+function checkEnvironment() {
+ let goVersion = child_process.execSync('go version', {
+ encoding: 'utf-8',
+ });
+ log.info('go is', goVersion);
+ let nodeVersion = child_process.execSync('node -v', {
+ encoding: 'utf-8',
+ });
+ log.info('node version is', nodeVersion);
+ let tscVersion = child_process.execSync('tsc -v', {
+ encoding: 'utf-8',
+ });
+ log.info('tsc version is', tscVersion);
+ if (goVersion == '' || nodeVersion == '' || tscVersion == '') {
+ return false;
+ }
+ let traceStreamer = path.normalize(path.join(__dirname, '/bin'));
+ if (!checkDirExist(traceStreamer + '/trace_streamer_builtin.js')) {
+ log.error(traceStreamer + '/trace_streamer_builtin.js' + ' Must exist');
+ return false;
+ }
+ if (!checkDirExist(traceStreamer + '/trace_streamer_builtin.wasm')) {
+ log.error(traceStreamer + '/trace_streamer_builtin.wasm' + ' Must exist');
+ return false;
+ }
+ return true;
+}
+
+function initLog() {
+ log4js.configure({
+ appenders: {
+ out: { type: 'stdout' },
+ },
+ categories: {
+ default: { appenders: ['out'], level: 'debug' },
+ },
+ });
+ return log4js.getLogger('smartPerf');
+}
+
+function main() {
+ log = initLog();
+ if (!checkEnvironment()) {
+ return;
+ }
+ // clean outDir
+ let outPath = path.normalize(path.join(__dirname, '/', outDir));
+ if (checkDirExist(outPath)) {
+ log.info('delete the last compilation result');
+ removeDir(outPath);
+ log.info('delete the last compilation success');
+ }
+ // run tsc compile
+ log.info('start compiling typeScript code');
+ let rootPath = path.join(__dirname, '/');
+ child_process.execSync('tsc -p ' + rootPath, {
+ encoding: 'utf-8',
+ });
+ log.info('compiling typeScript code success');
+ // run cp to mv all staticFile
+ staticFiles.forEach((value) => {
+ let filePath = path.join(__dirname, value);
+ let distFile;
+ if (value.startsWith('/src')) {
+ distFile = path.join(__dirname, outDir, value.substring(4, value.length + 1));
+ } else if (value.startsWith('/server')) {
+ distFile = path.join(__dirname, outDir, value.substring(7, value.length + 1));
+ }
+ cpFile(filePath, distFile);
+ });
+ staticPath.forEach((value) => {
+ let pa = path.join(__dirname, value);
+ let distPath;
+ if (value.startsWith('/src')) {
+ distPath = path.join(__dirname, outDir, value.substring(4, value.length + 1));
+ } else if (value.startsWith('/server')) {
+ distPath = path.join(__dirname, outDir, value.substring(7, value.length + 1));
+ }
+ copyDirectory(pa, distPath);
+ });
+ thirdParty.forEach((value) => {
+ let thirdFile = path.join(__dirname, value.srcFilePath);
+ let thirdDistFile = path.join(__dirname, outDir, value.distFilePath);
+ cpFile(thirdFile, thirdDistFile);
+ });
+ let traceStreamer = path.normalize(path.join(__dirname, '/bin'));
+ if (checkDirExist(traceStreamer)) {
+ let dest = path.normalize(path.join(__dirname, outDir, '/bin'));
+ copyDirectory(traceStreamer, dest);
+ // to mv traceStream Wasm and js
+ cpFile(
+ traceStreamer + '/trace_streamer_builtin.js',
+ rootPath + outDir + '/trace/database/trace_streamer_builtin.js'
+ );
+ cpFile(
+ traceStreamer + '/trace_streamer_builtin.wasm',
+ rootPath + outDir + '/trace/database/trace_streamer_builtin.wasm'
+ );
+ if (sdkWams.length > 0) {
+ sdkWams.forEach((fileName) => {
+ cpFile(traceStreamer + '/' + fileName, rootPath + outDir + '/trace/database/' + fileName);
+ });
+ }
+ } else {
+ log.error('traceStreamer dir is not Exits');
+ return;
+ }
+ // compile server
+ if (compileServer) {
+ log.log('start compile server');
+ let serverSrc = path.normalize(path.join(__dirname, '/server/main.go'));
+ let rs;
+ if (os.type() === 'Windows_NT') {
+ rs = child_process.spawnSync('go', ['build', '-o', outPath, serverSrc], {
+ encoding: 'utf-8',
+ });
+ } else if (os.type() == 'Darwin') {
+ rs = child_process.spawnSync('go', ['build', '-o', outPath + '/main', serverSrc], {
+ encoding: 'utf-8',
+ });
+ } else {
+ rs = child_process.spawnSync('go', ['build', '-o', outPath + '/main', serverSrc], {
+ encoding: 'utf-8',
+ });
+ }
+ if (rs.status == 0) {
+ log.log('compile server success');
+ } else {
+ log.error('compile server failed', rs);
+ }
+ } else {
+ log.warn('skip compile server');
+ }
+ log.log('smartPerf compile success');
+}
+
+function copyDirectory(src, dest) {
+ if (checkDirExist(dest) == false) {
+ fs.mkdirSync(dest);
+ }
+ if (checkDirExist(src) == false) {
+ return false;
+ }
+ let directories = fs.readdirSync(src);
+ directories.forEach((value) => {
+ let filePath = path.join(src, value);
+ let fileSys = fs.statSync(filePath);
+ if (fileSys.isFile()) {
+ let destPath = path.join(dest, value);
+ log.info('cp file %s to %s', filePath, destPath);
+ fs.copyFileSync(filePath, destPath);
+ } else if (fileSys.isDirectory()) {
+ copyDirectory(filePath, path.join(dest, value));
+ }
+ });
+}
+
+function checkDirExist(dirPath) {
+ return fs.existsSync(dirPath);
+}
+
+function removeDir(outPath) {
+ let files = [];
+ if (fs.existsSync(outPath)) {
+ files = fs.readdirSync(outPath);
+ files.forEach((file, index) => {
+ let curPath = outPath + '/' + file;
+ if (fs.statSync(curPath).isDirectory()) {
+ removeDir(curPath);
+ } else {
+ fs.unlinkSync(curPath);
+ }
+ });
+ fs.rmdirSync(outPath);
+ }
+}
+
+main();
diff --git a/ide/package.json b/ide/package.json
new file mode 100644
index 0000000000000000000000000000000000000000..cf9da3161d6f0bfefd95fcb91af08fc83277f17c
--- /dev/null
+++ b/ide/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "SmartPerf",
+ "version": "1.0.0",
+ "description": "Smart Perf",
+ "main": "index.js",
+ "scripts": {
+ "compile": "node ./build.js",
+ "test": "jest -u",
+ "test-c": "jest --coverage -u"
+ },
+ "jest": {
+ "testEnvironment": "jsdom",
+ "collectCoverageFrom": [
+ "/dist/**/*.js",
+ "!/dist/bin/*",
+ "!/dist/trace/database/pixi.js",
+ "!/dist/trace/database/sql-wasm.js",
+ "!/dist/trace/database/uuidv4.min.js",
+ "!/dist/trace/database/worker.sql-wasm.js",
+ "!/dist/trace/database/worker.sql-wasm-debug.js",
+ "!/dist/trace/database/trace_streamer_builtin.js",
+ "!/dist/trace/database/trace_streamer_sdk_builtin.js",
+ "!/dist/trace/database/trace_streamer_dubai_builtin.js"
+ ],
+ "globals": {
+ "useWb": true
+ },
+ "setupFiles": [
+ "jsdom-worker",
+ "jest-canvas-mock"
+ ],
+ "setupFilesAfterEnv": [
+ "/jest.setup.js"
+ ]
+ },
+ "repository": {
+ "type": "git",
+ "url": ""
+ },
+ "author": "",
+ "license": "Apache License",
+ "devDependencies": {
+ "@babel/plugin-proposal-class-properties": "^7.16.7",
+ "@babel/plugin-proposal-decorators": "^7.17.2",
+ "@babel/preset-env": "*",
+ "@babel/preset-typescript": "*",
+ "@types/jest": "*",
+ "@types/node": "^17.0.10",
+ "jest": "*",
+ "jest-canvas-mock": "^2.3.1",
+ "typescript": "^4.2.3",
+ "jsdom-worker": "^0.2.1",
+ "jest-environment-jsdom": "^28.1.0",
+ "node-fetch": "^2.6.7",
+ "log4js": "^6.4.4",
+ "usb": "^2.4.2"
+ },
+ "dependencies": {}
+}
diff --git a/ide/server/go.mod b/ide/server/go.mod
new file mode 100644
index 0000000000000000000000000000000000000000..aaa88e400a264e7ea35f528c0793e783515c8c70
--- /dev/null
+++ b/ide/server/go.mod
@@ -0,0 +1,21 @@
+// Copyright (C) 2022 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+module dist
+
+go 1.17
+
+require (
+ github.com/djimenez/iconv-go v0.0.0-20160305225143-8960e66bd3da
+ golang.org/x/text v0.3.7
+)
\ No newline at end of file
diff --git a/ide/server/main.go b/ide/server/main.go
new file mode 100644
index 0000000000000000000000000000000000000000..bf73502a52922a30b5bd7110ef0ad22324482bf4
--- /dev/null
+++ b/ide/server/main.go
@@ -0,0 +1,489 @@
+// Copyright (C) 2022 Huawei Device Co., Ltd.
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package main
+
+//遇到报错请在当前目录下执行这个命令: go mod download golang.org/x/text
+import (
+ "bufio"
+ "bytes"
+ "crypto/rand"
+ "crypto/rsa"
+ "crypto/tls"
+ "crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/json"
+ "encoding/pem"
+ "fmt"
+ "io"
+ "io/fs"
+ "log"
+ "math/big"
+ "mime"
+ "net"
+ "net/http"
+ "net/http/cookiejar"
+ "os"
+ "os/exec"
+ "path"
+ "path/filepath"
+ "regexp"
+ "runtime"
+ "strconv"
+ "strings"
+ "time"
+)
+
+const HttpPort = 9000
+
+var exPath string
+var serveInfo string
+
+// CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build main.go
+// CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build main.go
+func cors(fs http.Handler, version string) http.HandlerFunc {
+ return func(w http.ResponseWriter, r *http.Request) {
+ // return if you do not want the FileServer handle a specific request
+ r.Header.Add("Cross-Origin-Opener-Policy", "same-origin")
+ r.Header.Add("Cross-Origin-Embedder-Policy", "require-corp")
+ w.Header().Add("Cross-Origin-Opener-Policy", "same-origin")
+ w.Header().Add("Cross-Origin-Embedder-Policy", "require-corp")
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ w.Header().Set("Access-Control-Allow-Credentials", "true")
+ w.Header().Set("Access-Control-Allow-Headers", "x-requested-with, authorization, blade-auth") //*
+ w.Header().Set("Access-Control-Allow-Methods", "*") //*
+ w.Header().Set("Access-Control-Max-Age", "3600")
+ w.Header().Set("data-version", version)
+ fs.ServeHTTP(w, r)
+ }
+}
+
+func exist(path string) bool {
+ _, err := os.Stat(path)
+ if err != nil {
+ if os.IsExist(err) {
+ return true
+ }
+ return false
+ }
+ return true
+}
+func genSSL() {
+ if exist("cert/keyFile.key") || exist("cert/certFile.pem") {
+ fmt.Println("keyFile.key exists")
+ return
+ }
+ max := new(big.Int).Lsh(big.NewInt(1), 128)
+ serialNumber, _ := rand.Int(rand.Reader, max)
+ subject := pkix.Name{
+ Organization: []string{"www.smartperf.com"},
+ OrganizationalUnit: []string{"ITs"},
+ CommonName: "www.smartperf.com",
+ }
+ certificate509 := x509.Certificate{
+ SerialNumber: serialNumber,
+ Subject: subject,
+ NotBefore: time.Now(),
+ NotAfter: time.Now().AddDate(10, 0, 0),
+ KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
+ ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
+ IPAddresses: []net.IP{net.ParseIP("127.0.0.1")},
+ }
+ chekDir("cert")
+ pk, _ := rsa.GenerateKey(rand.Reader, 1024)
+ derBytes, _ := x509.CreateCertificate(rand.Reader, &certificate509, &certificate509, &pk.PublicKey, pk)
+ certOut, _ := os.Create("cert/certFile.pem")
+ pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
+ certOut.Close()
+ keyOut, _ := os.Create("cert/keyFile.key")
+ pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(pk)})
+ keyOut.Close()
+}
+func main() {
+ checkPort(HttpPort)
+ genSSL()
+ exPath = getCurrentAbPath()
+ fmt.Println(exPath)
+ go func() {
+ version := ""
+ readVersion, versionErr := os.ReadFile(exPath + "/version.txt")
+ if versionErr != nil {
+ version = ""
+ } else {
+ version = string(readVersion)
+ }
+ readReqServerConfig()
+ mux := http.NewServeMux()
+ mime.TypeByExtension(".js")
+ mime.AddExtensionType(".js", "application/javascript")
+ log.Println(mime.TypeByExtension(".js"))
+ mux.HandleFunc("/logger", consoleHandler)
+ mux.Handle("/upload/", http.StripPrefix("/upload/", http.FileServer(http.Dir(filepath.FromSlash(exPath+"/upload")))))
+ mux.HandleFunc("/download-file", downloadHandler)
+ mux.HandleFunc("/application/serverInfo", serverInfo)
+ fs := http.FileServer(http.Dir(exPath + "/"))
+ mux.Handle("/application/", http.StripPrefix("/application/", cors(fs, version)))
+ go func() {
+ ser := &http.Server{
+ Addr: fmt.Sprintf(":%d", HttpPort),
+ Handler: mux,
+ }
+ log.Println(fmt.Sprintf("HTTPS[%d]服务启动", HttpPort))
+ err := ser.ListenAndServeTLS("cert/certFile.pem", "cert/keyFile.key")
+ CheckErr(err)
+ }()
+ go func() {
+ ser := &http.Server{
+ Addr: fmt.Sprintf(":%d", HttpPort+1),
+ Handler: mux,
+ }
+ log.Println(fmt.Sprintf("HTTP[%d]服务启动", HttpPort))
+ err := ser.ListenAndServe()
+ CheckErr(err)
+ }()
+ open(fmt.Sprintf("https://localhost:%d/application", HttpPort))
+ }()
+ select {}
+}
+
+func getPidByPort(portNumber int) int {
+ resPid := -1
+ var out bytes.Buffer
+ cmdRes := exec.Command("cmd", "/c", fmt.Sprintf("netstat -ano -p tcp | findstr %d", portNumber))
+ cmdRes.Stdout = &out
+ cmdRes.Run()
+ cmdResStr := out.String()
+ findStr := regexp.MustCompile(`\s\d+\s`).FindAllString(cmdResStr, -1)
+ if len(findStr) > 0 {
+ pid, err := strconv.Atoi(strings.TrimSpace(findStr[0]))
+ if err != nil {
+ resPid = -1
+ } else {
+ resPid = pid
+ }
+ }
+ return resPid
+}
+
+type LoggerReq struct {
+ FileName string `json:"fileName"`
+ FileSize string `json:"fileSize"`
+}
+
+func consoleHandler(w http.ResponseWriter, r *http.Request) {
+ chekDir(exPath + "/logger")
+ var now = time.Now()
+ var fileName = fmt.Sprintf("%d-%d-%d", now.Year(), now.Month(), now.Day())
+ dst, err := os.OpenFile(exPath+"/logger/"+fileName, os.O_WRONLY|os.O_CREATE|os.O_APPEND|os.O_SYNC, 0666)
+ CheckErr(err)
+ contentType := r.Header["Content-Type"]
+ if len(contentType) > 0 {
+ contentTypeName := contentType[0]
+ if strings.HasPrefix(contentTypeName, "application/json") {
+ decoder := json.NewDecoder(r.Body)
+ var req LoggerReq
+ decoder.Decode(&req)
+ dst.WriteString(fmt.Sprintf("%s %s (%s M)\n", now.Format("2006-01-02 15:04:05"), req.FileName, req.FileSize))
+ fmt.Fprintf(w, fmt.Sprintf("日志写入成功%s", exPath))
+ }
+ }
+}
+
+func serverInfo(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Access-Control-Allow-Origin", "*")
+ w.Header().Set("request_info", serveInfo)
+ w.WriteHeader(200)
+}
+
+func readReqServerConfig() string {
+ readServerConfig, serverConfigErr := os.ReadFile(exPath + "/server-config.txt")
+ if serverConfigErr != nil {
+ serveInfo = ""
+ } else {
+ serveInfo = string(readServerConfig)
+ }
+ return serveInfo
+}
+
+func mapToJson(m map[string]interface{}) (string, error) {
+ marshal, err := json.Marshal(m)
+ if err != nil {
+ return "", err
+ }
+ var str = string(marshal)
+ return str, nil
+}
+func jsonToMap(str string) (map[string]interface{}, error) {
+ var m = make(map[string]interface{})
+ err := json.Unmarshal([]byte(str), &m)
+ if err != nil {
+ return nil, err
+ }
+ return m, nil
+}
+
+// MkDir 创建目录
+func MkDir(path string) {
+ dir := path[0:strings.LastIndex(path, string(os.PathSeparator))] //从文件路径获取目录
+ if _, err := os.Stat(dir); err != nil { //如果目录不存在,创建目录
+ os.MkdirAll(dir, os.ModePerm)
+ }
+}
+
+func resp(w *http.ResponseWriter) func(bool, int, string, map[string]interface{}) {
+ return func(success bool, code int, msg string, obj map[string]interface{}) {
+ toJson, err := mapToJson(map[string]interface{}{
+ "success": success,
+ "code": code,
+ "msg": msg,
+ "data": obj,
+ })
+ if err != nil {
+ errRes, _ := mapToJson(map[string]interface{}{
+ "success": false,
+ "code": -1,
+ "msg": err.Error(),
+ })
+ fmt.Fprintf(*w, errRes)
+ } else {
+ fmt.Fprintf(*w, toJson)
+ }
+ }
+}
+
+func get(url string) (*http.Response, error) {
+ jar, _ := cookiejar.New(nil)
+ c := &http.Client{
+ Transport: &http.Transport{TLSClientConfig: &tls.Config{InsecureSkipVerify: true}},
+ CheckRedirect: nil,
+ Jar: jar,
+ Timeout: time.Duration(3600) * time.Second,
+ }
+ return c.Get(url)
+}
+
+func clearOverdueFile() {
+ MkDir(filepath.FromSlash(fmt.Sprintf("./upload/")))
+ now := time.Now()
+ loc, err := time.LoadLocation("Asia/Shanghai")
+ if err != nil {
+ return
+ }
+ var checkDue = func(fileName string) bool {
+ f := getSuffixByUrl(fileName)
+ parseTime, err := time.ParseInLocation("20060102150405000", f.fileName, loc)
+ if err != nil {
+ return false
+ }
+ sub := now.Sub(parseTime)
+ if sub.Minutes() > 60 { //bigger than 60 min flag due
+ return true
+ }
+ return false
+ }
+ slash := filepath.FromSlash(fmt.Sprintf("./upload/"))
+ filepath.WalkDir(slash, func(path string, d fs.DirEntry, err error) error {
+ if checkDue(d.Name()) {
+ fmt.Println(now, "delete->", path, d.Name(), err)
+ os.Remove(path)
+ }
+ return nil
+ })
+}
+func getSuffixByUrl(u string) struct {
+ fileName string
+ suffix string
+} {
+ lastIndex := strings.LastIndex(u, "/")
+ var f string
+ if lastIndex != -1 {
+ f = u[lastIndex:]
+ } else {
+ f = u
+ }
+ index := strings.LastIndex(f, ".")
+ if index != -1 {
+ return struct {
+ fileName string
+ suffix string
+ }{
+ f[0:index],
+ f[index:],
+ }
+ } else {
+ return struct {
+ fileName string
+ suffix string
+ }{
+ f,
+ "",
+ }
+ }
+}
+
+func downloadHandler(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("content-type", "text/json")
+ clearOverdueFile()
+ contentType := r.Header["Content-Type"]
+ if len(contentType) > 0 {
+ contentTypeName := contentType[0]
+ if strings.HasPrefix(contentTypeName, "application/x-www-form-urlencoded") {
+ url := r.PostFormValue("url")
+ res, err := get(url)
+ if err != nil {
+ resp(&w)(false, -1, err.Error(), nil)
+ return
+ }
+ pth := filepath.FromSlash(fmt.Sprintf("/upload/%s%s", time.Now().Format("20060102150405000"), getSuffixByUrl(url).suffix))
+ MkDir("." + pth)
+ create, err := os.Create("." + pth)
+ if err != nil {
+ resp(&w)(false, -1, err.Error(), nil)
+ return
+ }
+ written, err := io.Copy(create, res.Body)
+ if err != nil {
+ resp(&w)(false, -1, err.Error(), nil)
+ return
+ }
+ fmt.Println(url, written)
+ resp(&w)(true, 0, "success", map[string]interface{}{
+ "url": pth,
+ "size": written,
+ })
+ return
+ }
+ }
+ resp(&w)(false, -1, "请求方式错误", nil)
+}
+
+func SplitLines(s string) []string {
+ var lines []string
+ sc := bufio.NewScanner(strings.NewReader(s))
+ for sc.Scan() {
+ lines = append(lines, sc.Text())
+ }
+ return lines
+}
+
+func readFileFirstLine(path string) string {
+ file, err := os.Open(path)
+ if err != nil {
+ return ""
+ }
+ defer file.Close()
+
+ readFile := bufio.NewReader(file)
+ line, readErr := readFile.ReadString('\n')
+ if readErr != nil || io.EOF == err {
+ return ""
+ }
+ return line
+}
+
+func PathExists(path string) (bool, error) {
+ _, err := os.Stat(path)
+ if err == nil {
+ return true, nil
+ }
+ if os.IsNotExist(err) {
+ return false, nil
+ }
+ return false, err
+}
+
+func chekDir(path string) {
+ _, err := os.Stat(path)
+ if err != nil {
+ err := os.Mkdir(path, os.ModePerm)
+ if err != nil {
+ fmt.Printf("mkdir failed![%v]\n", err)
+ } else {
+ fmt.Printf("mkdir success!\n")
+ }
+ }
+}
+func CheckErr(err error) {
+ if err != nil {
+ log.Panicln(err)
+ }
+}
+
+func open(url string) error {
+ if isWindows() {
+ return openUrlWindows(url)
+ } else if isDarwin() {
+ return openUrlDarwin(url)
+ } else {
+ return openUrlOther(url)
+ }
+}
+
+func openUrlWindows(url string) error {
+ cmd := "cmd"
+ args := []string{"/c", "start", url}
+ return exec.Command(cmd, args...).Start()
+}
+func openUrlDarwin(url string) error {
+ var cmd = "open"
+ var args = []string{url}
+ return exec.Command(cmd, args...).Start()
+}
+func openUrlOther(url string) error {
+ var cmd = "xdg-open"
+ var args = []string{url}
+ return exec.Command(cmd, args...).Start()
+}
+
+func isWindows() bool {
+ return runtime.GOOS == "windows"
+}
+func isDarwin() bool {
+ return runtime.GOOS == "darwin"
+}
+
+func getCurrentAbPath() string {
+ dir := getExecutePath()
+ tmpDir, _ := filepath.EvalSymlinks(os.TempDir())
+ if strings.Contains(dir, tmpDir) {
+ return getCallerPath()
+ }
+ return dir
+}
+
+func getCallerPath() string {
+ var pth string
+ _, fName, _, ok := runtime.Caller(0)
+ if ok {
+ pth = path.Dir(fName)
+ }
+ return pth
+}
+func getExecutePath() string {
+ pth, err := os.Executable()
+ if err != nil {
+ log.Fatal(err)
+ }
+ res, _ := filepath.EvalSymlinks(filepath.Dir(pth))
+ return res
+}
+
+func checkPort(port int) {
+ if isWindows() {
+ pid := getPidByPort(port)
+ if pid != -1 {
+ res := exec.Command("cmd", "/c", fmt.Sprintf("taskkill /F /PID %d /T", pid))
+ res.Run()
+ }
+ }
+}
diff --git a/ide/server/server-config.txt b/ide/server/server-config.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2c740bc33cf0647e6a12c33e7b9694db960a0fea
--- /dev/null
+++ b/ide/server/server-config.txt
@@ -0,0 +1 @@
+127.0.0.1:9100
\ No newline at end of file
diff --git a/ide/server/version.txt b/ide/server/version.txt
new file mode 100644
index 0000000000000000000000000000000000000000..13eb9fd76e77a11fbeea787631d5e10c91685e24
--- /dev/null
+++ b/ide/server/version.txt
@@ -0,0 +1 @@
+v1.0.001
\ No newline at end of file
diff --git a/ide/server/wasm.json b/ide/server/wasm.json
new file mode 100644
index 0000000000000000000000000000000000000000..a6ad72382448bff1c3142afd7a070ed989d4fc48
--- /dev/null
+++ b/ide/server/wasm.json
@@ -0,0 +1,20 @@
+{
+ "WasmFiles": [
+ {
+ "disPlayName": "common_mock",
+ "componentId": 0,
+ "pluginName": "mock-plugin",
+ "sampleInterval": 5000,
+ "wasmJsName": "trace_streamer_sdk_builtin.js",
+ "wasmName": "trace_streamer_sdk_builtin_wasm"
+ },
+ {
+ "disPlayName": "dubai-plugin",
+ "componentId": 1,
+ "pluginName": "dubai-plugin",
+ "sampleInterval": 5000,
+ "wasmJsName": "trace_streamer_dubai_builtin.js",
+ "wasmName": "trace_streamer_dubai_builtin_wasm"
+ }
+ ]
+}
diff --git a/ide/src/base-ui/BaseElement.ts b/ide/src/base-ui/BaseElement.ts
new file mode 100644
index 0000000000000000000000000000000000000000..aa81e5ece740cb022218acf09299f0ac72a58d35
--- /dev/null
+++ b/ide/src/base-ui/BaseElement.ts
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function element(tag: string) {
+ return (el: any) => {
+ if (!customElements.get(tag)) {
+ customElements.define(tag, el);
+ }
+ };
+}
+
+export abstract class BaseElement extends HTMLElement {
+ args: any;
+
+ public constructor(args: any | undefined | null = null) {
+ super();
+ this.args = args;
+ this.attachShadow({ mode: 'open' }).innerHTML = this.initHtml();
+ this.initElements();
+ }
+
+ abstract initElements(): void;
+
+ abstract initHtml(): string;
+
+ public connectedCallback() {}
+
+ public disconnectedCallback() {}
+
+ public adoptedCallback() {}
+
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {}
+}
diff --git a/ide/src/base-ui/button/LitButton.ts b/ide/src/base-ui/button/LitButton.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c3604bf9b27d8be198f73658dd0bf87bf14b49c7
--- /dev/null
+++ b/ide/src/base-ui/button/LitButton.ts
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+
+@element('lit-button')
+export class LitButton extends BaseElement {
+ private slotHtml: HTMLElement | undefined;
+ private button: HTMLButtonElement | null | undefined;
+ private litIcon: LitButton | null | undefined;
+
+ static get observedAttributes() {
+ return [
+ 'text',
+ 'back',
+ 'icon',
+ 'height',
+ 'width',
+ 'color',
+ 'font_size',
+ 'border',
+ 'padding',
+ 'justify_content',
+ 'border_radius',
+ 'margin_icon',
+ 'opacity',
+ ];
+ }
+
+ get text() {
+ return this.getAttribute('text') || '';
+ }
+
+ set text(text: string) {
+ this.setAttribute('text', text);
+ }
+
+ get back() {
+ return this.getAttribute('back') || '';
+ }
+
+ set back(backColor: string) {
+ this.button!.style.backgroundColor = backColor;
+ this.setAttribute('back', backColor);
+ }
+
+ get icon() {
+ return this.getAttribute('icon') || '';
+ }
+
+ set icon(icon: string) {
+ this.litIcon?.setAttribute('name', icon);
+ this.setAttribute('icon', icon);
+ if (icon) {
+ this.litIcon!.style.display = 'block';
+ }
+ }
+
+ get height() {
+ return this.getAttribute('height') || '';
+ }
+
+ set height(height: string) {
+ this.setAttribute('height', height);
+ }
+
+ get width() {
+ return this.getAttribute('width') || '';
+ }
+
+ set width(width: string) {
+ this.setAttribute('width', width);
+ }
+
+ set color(color: string) {
+ this.setAttribute('color', color);
+ }
+
+ set font_size(fontSize: string) {
+ this.setAttribute('font_size', fontSize);
+ }
+
+ set border(border: string) {
+ this.setAttribute('border', border);
+ }
+
+ set padding(padding: string) {
+ this.setAttribute('padding', padding);
+ }
+
+ set justify_content(justifyContent: string) {
+ this.setAttribute('justify_content', justifyContent);
+ }
+
+ set border_radius(borderRadius: string) {
+ this.setAttribute('border_radius', borderRadius);
+ }
+
+ set margin_icon(value: string) {
+ this.litIcon?.setAttribute('margin_icon', value);
+ }
+
+ set opacity(value: string) {
+ this.litIcon?.setAttribute('opacity', value);
+ }
+
+ set hidden(hidden: boolean) {
+ if (hidden) {
+ this.setAttribute('hidden', 'true');
+ this.style.display = 'none';
+ } else {
+ this.removeAttribute('hidden');
+ this.style.display = 'block';
+ }
+ }
+
+ initHtml(): string {
+ return `
+
+
+
+
+ `;
+ }
+
+ initElements(): void {
+ this.slotHtml = this.shadowRoot?.querySelector('#sl') as HTMLElement;
+ this.button = this.shadowRoot?.querySelector('#custom-button');
+ this.litIcon = this.shadowRoot?.querySelector('#button-icon') as LitButton;
+ if (this.litIcon.getAttribute('name') == '') {
+ this.litIcon!.style.display = 'none';
+ }
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, value: string) {
+ switch (name) {
+ case 'text':
+ this.slotHtml!.innerText = value;
+ break;
+ case 'back':
+ this.button!.style.backgroundColor = value;
+ break;
+ case 'icon':
+ this.litIcon?.setAttribute('name', value);
+ if (value) {
+ this.litIcon!.style.display = 'block';
+ }
+ break;
+ case 'height':
+ this.button!.style.height = value;
+ break;
+ case 'color':
+ this.button!.style.color = value;
+ break;
+ case 'font_size':
+ this.button!.style.fontSize = value;
+ break;
+ case 'border':
+ this.button!.style.border = value;
+ break;
+ case 'padding':
+ this.button!.style.padding = value;
+ break;
+ case 'justify_content':
+ this.button!.style.justifyContent = value;
+ break;
+ case 'border_radius':
+ this.button!.style.borderRadius = value;
+ break;
+ case 'margin_icon':
+ this.litIcon!.style.margin = value;
+ break;
+ case 'opacity':
+ this.button!.style.opacity = value;
+ break;
+ }
+ }
+}
diff --git a/ide/src/base-ui/chart/column/LitChartColumn.ts b/ide/src/base-ui/chart/column/LitChartColumn.ts
new file mode 100644
index 0000000000000000000000000000000000000000..9b7464b54066482035430bfda1f02f36da70200a
--- /dev/null
+++ b/ide/src/base-ui/chart/column/LitChartColumn.ts
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../../BaseElement.js';
+import { LitChartColumnConfig } from './LitChartColumnConfig.js';
+import { resizeCanvas } from '../helper.js';
+
+class Pillar {
+ obj?: any;
+ xLabel?: string;
+ yLabel?: string;
+ type?: string;
+ root?: boolean;
+ bgFrame?: {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ };
+ frame?: {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+ };
+ height?: number;
+ process?: boolean;
+ heightStep?: number;
+ centerX?: number;
+ centerY?: number;
+ color?: string;
+ hover?: boolean;
+}
+
+interface RLine {
+ label: string;
+ y: number;
+}
+
+@element('lit-chart-column')
+export class LitChartColumn extends BaseElement {
+ private tipEL: HTMLDivElement | null | undefined;
+ canvas: HTMLCanvasElement | undefined | null;
+ ctx: CanvasRenderingContext2D | undefined | null;
+ cfg: LitChartColumnConfig | null | undefined;
+ offset?: { x: number | undefined; y: number | undefined };
+ data: Pillar[] = [];
+ rowLines: RLine[] = [];
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.tipEL = this.shadowRoot!.querySelector('#tip');
+ this.canvas = this.shadowRoot!.querySelector('#canvas');
+ this.ctx = this.canvas!.getContext('2d', { alpha: true });
+ resizeCanvas(this.canvas!);
+ this.offset = { x: 40, y: 20 };
+ this.canvas!.onmouseout = (e) => {
+ this.hideTip();
+ this.data.forEach((it) => (it.hover = false));
+ this.render();
+ };
+ this.canvas!.onmousemove = (ev) => {
+ let rect = this.getBoundingClientRect();
+ let x = ev.pageX - rect.left;
+ let y = ev.pageY - rect.top;
+ this.data.forEach((it) => {
+ if (contains(it.bgFrame!, x, y)) {
+ it.hover = true;
+ this.cfg?.hoverHandler?.(it.obj.no);
+ } else {
+ it.hover = false;
+ }
+ });
+ let pillars = this.data.filter((it) => it.hover);
+ if (this.cfg?.seriesField) {
+ if (pillars.length > 0) {
+ let title = ``;
+ let msg = pillars.map((it) => ``).join('');
+ let sum = ``;
+ let innerHtml = `
${title}${msg}${sum}
`;
+ if (x >= this.clientWidth - this.tipEL!.clientWidth) {
+ this.showTip(x - this.tipEL!.clientWidth - 10, y - 20, this.cfg!.tip ? this.cfg!.tip(pillars) : innerHtml);
+ } else {
+ this.showTip(x + 10, y - 20, this.cfg!.tip ? this.cfg!.tip(pillars) : innerHtml);
+ }
+ }
+ } else {
+ if (pillars.length > 0) {
+ let title = ``;
+ let innerHtml = `
${title}
`;
+ if (x >= this.clientWidth - this.tipEL!.clientWidth) {
+ this.showTip(x - this.tipEL!.clientWidth - 10, y - 20, this.cfg!.tip ? this.cfg!.tip(pillars) : innerHtml);
+ } else {
+ this.showTip(x + 10, y - 20, this.cfg!.tip ? this.cfg!.tip(pillars) : innerHtml);
+ }
+ }
+ }
+
+ if (this.data.filter((it) => it.process).length == 0) {
+ this.render();
+ }
+ };
+ this.render();
+ }
+
+ showHoverColumn(index: number) {
+ this.data.forEach((it) => {
+ if (it.obj.no === index) {
+ it.hover = true;
+ } else {
+ it.hover = false;
+ }
+ });
+ let pillars = this.data.filter((it) => it.hover);
+ if (this.cfg?.seriesField) {
+ if (pillars.length > 0) {
+ let hoverData = pillars[0];
+ let title = ``;
+ let msg = pillars.map((it) => ``).join('');
+ let sum = ``;
+ let innerHtml = `
${title}${msg}${sum}
`;
+ this.showTip(this.clientWidth / 2, this.clientHeight / 2, this.cfg!.tip ? this.cfg!.tip(pillars) : innerHtml);
+ }
+ } else {
+ if (pillars.length > 0) {
+ let hoverData = pillars[0];
+ let title = ``;
+ let innerHtml = `
`;
+ }
+}
+
+function contains(rect: { x: number; y: number; w: number; h: number }, x: number, y: number): boolean {
+ return rect.x <= x && x <= rect.x + rect.w && rect.y <= y && y <= rect.y + rect.h;
+}
diff --git a/ide/src/base-ui/chart/column/LitChartColumnConfig.ts b/ide/src/base-ui/chart/column/LitChartColumnConfig.ts
new file mode 100644
index 0000000000000000000000000000000000000000..b508e8278349182ee2f1a533a1d24374657c32fd
--- /dev/null
+++ b/ide/src/base-ui/chart/column/LitChartColumnConfig.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export interface LitChartColumnConfig {
+ data: any[];
+ appendPadding: number;
+ xField: string;
+ yField: string;
+ seriesField: string;
+ color: (a: any) => string;
+ tip: ((a: any) => string) | undefined;
+ hoverHandler?: (no: number) => void;
+ label:
+ | {
+ offset: number;
+ content: (it: any) => string;
+ }
+ | undefined
+ | null;
+}
diff --git a/ide/src/base-ui/chart/helper.ts b/ide/src/base-ui/chart/helper.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2fd7479c266a885e6597b333629322c2d0bb7308
--- /dev/null
+++ b/ide/src/base-ui/chart/helper.ts
@@ -0,0 +1,24 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export const resizeCanvas = (c: HTMLCanvasElement) => {
+ let el: Element = (c.getRootNode({ composed: false }) as ShadowRoot).host;
+ let dpr = window.devicePixelRatio || 1;
+ c.width = Math.ceil(el.clientWidth * dpr);
+ c.height = Math.ceil(el.clientHeight * dpr);
+ c.style.width = `${el.clientWidth}px`;
+ c.style.height = `${el.clientHeight}px`;
+ c.getContext('2d', { alpha: true })?.scale(dpr, dpr);
+};
diff --git a/ide/src/base-ui/chart/pagenation/PageNation.ts b/ide/src/base-ui/chart/pagenation/PageNation.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6529ca14ed3b879fcc6dc0d38b2bffecc60b42c0
--- /dev/null
+++ b/ide/src/base-ui/chart/pagenation/PageNation.ts
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export class PageNation {
+ element: any;
+ pageInfo: any;
+ first: any;
+ prev: any;
+ next: any;
+ last: any;
+ inputBox: any;
+ btn: any;
+ list: any;
+ origin: HTMLElement | undefined;
+ static BtnBackColor = '#6C9BFA';
+ static BtnColor = '#fff';
+ constructor(selector: any, options = {}) {
+ selector!.innerHTML = '';
+ //最大容器
+ this.element = selector;
+ // 默认值
+ this.pageInfo = {
+ current: 1,
+ total: 100,
+ pageSize: 15,
+ };
+ //等待创建的元素
+ this.first = null;
+ this.prev = null;
+ this.next = null;
+ this.last = null;
+ // 输入框
+ this.inputBox = null;
+ // 跳转按钮
+ this.btn = null;
+ // 中间的按钮组
+ this.list = null;
+ this.setPageOptions(options);
+ this.setItemStyles();
+ this.createPageElement();
+ this.bindPageHtml();
+ this.bindPageEvent();
+ }
+
+ setPageOptions(options: any) {
+ // 当前页
+ this.pageInfo.current = options.current || 1;
+ // 一页显示多少条
+ this.pageInfo.pageSize = options.pageSize || 15;
+ if (options.totalpage) {
+ //用户传递了多少页
+ this.pageInfo.totalpage = options.totalpage;
+ } else {
+ //没有传递多少页
+ if (options.total) {
+ // 如果传递了总条数
+ this.pageInfo.totalpage = Math.ceil(options.total / this.pageInfo.pageSize);
+ } else {
+ // 如果没有传递总条数
+ this.pageInfo.totalpage = 9;
+ }
+ }
+ this.pageInfo.first = options.first || '<<';
+ this.pageInfo.change = options.change || function () {};
+ }
+
+ setElementStyles(ele: any, styles: any) {
+ for (let key in styles) {
+ ele.style[key] = styles[key];
+ }
+ }
+
+ setItemStyles() {
+ this.setElementStyles(this.element, {
+ margin: '18px auto',
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ });
+ }
+
+ // 创建元素 首页 上一页 按钮组 下一页 尾页 输入框 按钮
+ createPageElement() {
+ //首页
+ this.origin = document.createElement('p');
+ this.setElementStyles(this.origin, {
+ 'border-radius': '4px',
+ padding: '5px',
+ border: '1px solid rgba(0,0,0,0.6)',
+ cursor: 'pointer',
+ margin: '0 5px',
+ });
+
+ this.first = this.origin.cloneNode(true);
+ this.first.innerText = this.pageInfo.first;
+ this.first.name = 'first';
+ this.element.appendChild(this.first);
+
+ this.prev = this.origin.cloneNode(true);
+ this.prev.innerText = '<';
+ this.prev.name = 'prev';
+ this.prev.style.padding = '5px 10px';
+ this.element.appendChild(this.prev);
+
+ // 创建ul
+ this.list = document.createElement('ul');
+ this.setElementStyles(this.list, {
+ display: 'flex',
+ padding: '0',
+ });
+ this.element.appendChild(this.list);
+ this.next = this.origin.cloneNode(true);
+ this.next.innerText = '>';
+ this.next.name = 'next';
+ this.next.style.padding = '5px 10px';
+ this.next.style.margin = '0px 5px';
+ this.element.appendChild(this.next);
+ this.last = this.origin.cloneNode(true);
+ this.last.innerText = '>>';
+ this.last.name = 'last';
+ this.last.style.padding = '5px';
+ this.last.style.margin = '0px 5px';
+ this.element.appendChild(this.last);
+ let jumpDiv = document.createElement('div');
+ jumpDiv.style.display = 'flex';
+ jumpDiv.style.border = '1px solid rgba(0,0,0,0.6)';
+ jumpDiv.style.borderRadius = '4px';
+ jumpDiv.style.width = '70px';
+ jumpDiv.style.height = '32px';
+ jumpDiv.style.marginLeft = '10px';
+
+ // 创建输入框
+ this.inputBox = document.createElement('input');
+ this.inputBox.value = this.pageInfo.current;
+ this.setElementStyles(this.inputBox, {
+ width: '35px',
+ height: '30px',
+ textAlign: 'center',
+ outline: 'none',
+ padding: '0',
+ border: '0',
+ 'border-radius': '5px',
+ });
+ jumpDiv.appendChild(this.inputBox);
+ let span = document.createElement('span');
+ span.style.width = '1px';
+ span.style.height = '24px';
+ span.style.alignSelf = 'center';
+ span.style.backgroundColor = '#999999';
+ jumpDiv.appendChild(span);
+ // 创建按钮
+ this.btn = document.createElement('button');
+ this.btn.innerText = '';
+ this.btn.name = 'goto';
+ this.setElementStyles(this.btn, {
+ height: '32px',
+ width: '30px',
+ cursor: 'pointer',
+ backgroundColor: '#FFF',
+ border: '0',
+ 'border-radius': '5px',
+ });
+ this.btn.style.background = `url('img/arrowright.png') no-repeat 98% center var(--dark-background3,#FFFFFF)`;
+ this.btn.style.backgroundPosition = 'center';
+ jumpDiv.appendChild(this.btn);
+ this.element.appendChild(jumpDiv);
+ }
+
+ // 判断首页 上一页 下一页 尾页 是否可以点击
+ bindPageHtml() {
+ const { current, totalpage } = this.pageInfo;
+ const disable = { color: '#999999', cursor: 'not-allowed' };
+ const enable = {
+ color: '#000',
+ cursor: 'pointer',
+ };
+ // 判断当前页是否是第一页 如果是第一页 那么首页和上一页就不能点击
+ if (current <= 1) {
+ this.setElementStyles(this.first, disable);
+ this.setElementStyles(this.prev, disable);
+ } else {
+ this.setElementStyles(this.first, enable);
+ this.setElementStyles(this.prev, enable);
+ }
+ // 判断当前页是否是最后一页 如果是最后一页 那么下一页和尾页就不能点击
+ if (current >= totalpage) {
+ this.setElementStyles(this.next, disable);
+ this.setElementStyles(this.last, disable);
+ } else {
+ this.setElementStyles(this.next, enable);
+ this.setElementStyles(this.last, enable);
+ }
+ this.inputBox.value = current;
+ //渲染的时候判断ul列表的显示情况
+ this.bindPageList();
+ this.pageInfo.change(this.pageInfo.current);
+ }
+
+ bindPageList() {
+ // clear ul里面的内容
+ this.list.innerHTML = '';
+ //每次加载之前先清空ul里面的内容
+ const { pageSize, current, totalpage } = this.pageInfo;
+ const origin = document.createElement('li');
+ origin.dataset.name = 'item';
+ this.setElementStyles(origin, {
+ listStyle: 'none',
+ 'border-radius': '4px',
+ border: '1px solid rgba(0,0,0,0.6)',
+ padding: '5px 10px',
+ margin: '0 5px',
+ cursor: 'pointer',
+ });
+ if (totalpage <= 9) {
+ for (let i = 0; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return;
+ }
+ // 左边5个 中间 ... 右边2个
+ if (this.bindLeftList(current, totalpage, origin)) {
+ return;
+ }
+ // 当前页面 大于5页 小于倒数第5页
+ for (let i = 0; i < 2; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = current - 3; i < current + 2; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = totalpage - 2; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ }
+
+ bindLeftList(current: number, totalpage: number, origin: HTMLElement): boolean {
+ if (current < 5) {
+ // 左边5个 中间 ... 右边2个
+ for (let i = 0; i < 5; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = totalpage - 2; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return true;
+ }
+ if (current == 5) {
+ // 左边5个 中间 ... 右边2个
+ for (let i = 0; i < 7; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+
+ for (let i = totalpage - 2; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return true;
+ }
+ // 当前页面 大于倒数第5页
+ if (current > totalpage - 4) {
+ // 左边5个 中间 ... 右边2个
+ for (let i = 0; i < 2; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = totalpage - 5; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return true;
+ }
+ if (current == totalpage - 4) {
+ // 左边5个 中间 ... 右边2个
+ for (let i = 0; i < 2; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = totalpage - 7; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return true;
+ }
+ if (current == totalpage - 4) {
+ // 左边5个 中间 ... 右边2个
+ for (let i = 0; i < 2; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ var span = document.createElement('span');
+ span.innerText = '...';
+ this.list.appendChild(span);
+ for (let i = totalpage - 7; i < totalpage; i++) {
+ const li = origin.cloneNode(true);
+ // @ts-ignore
+ li.innerText = i + 1;
+ if (i + 1 === current) {
+ this.setElementStyles(li, {
+ backgroundColor: PageNation.BtnBackColor,
+ color: PageNation.BtnColor,
+ });
+ }
+ this.list.appendChild(li);
+ }
+ return true;
+ }
+ return false;
+ }
+
+ bindPageEvent() {
+ this.element.addEventListener(
+ 'click',
+ (event: {
+ target: {
+ name: string;
+ dataset: { name: string };
+ innerText: number;
+ };
+ }) => {
+ if (event.target.name === 'first') {
+ if (this.pageInfo.current === 1) return;
+ this.pageInfo.current = 1;
+ this.bindPageHtml();
+ }
+ if (event.target.name === 'prev') {
+ if (this.pageInfo.current === 1) return;
+ this.pageInfo.current--;
+ this.bindPageHtml();
+ }
+ if (event.target.name === 'next') {
+ if (this.pageInfo.current === this.pageInfo.totalpage) return;
+ this.pageInfo.current++;
+ this.bindPageHtml();
+ }
+ if (event.target.name === 'last') {
+ if (this.pageInfo.current === this.pageInfo.totalpage) return;
+ this.pageInfo.current = this.pageInfo.totalpage;
+ this.bindPageHtml();
+ }
+ if (event.target.name === 'goto') {
+ // 拿到你文本的内容
+ let page = this.inputBox.value - 0;
+ if (isNaN(page)) {
+ page = 1;
+ }
+ if (page <= 1) {
+ page = 1;
+ }
+ if (page >= this.pageInfo.totalpage) {
+ page = this.pageInfo.totalpage;
+ }
+ this.pageInfo.current = page;
+ this.bindPageHtml();
+ }
+ if (event.target.dataset.name === 'item') {
+ this.pageInfo.current = event.target.innerText - 0;
+ this.bindPageHtml();
+ }
+ }
+ );
+ }
+}
diff --git a/ide/src/base-ui/chart/pagenation/pagination-box.ts b/ide/src/base-ui/chart/pagenation/pagination-box.ts
new file mode 100644
index 0000000000000000000000000000000000000000..bf57bd6b9e0c2bedaafebbdb2811550c193bec8a
--- /dev/null
+++ b/ide/src/base-ui/chart/pagenation/pagination-box.ts
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../../BaseElement.js';
+
+@element('pagination-box')
+export class PaginationBox extends BaseElement {
+ private page: any;
+
+ static get observedAttributes() {
+ return ['text', 'height', 'width'];
+ }
+
+ get text() {
+ return this.getAttribute('text') || '';
+ }
+
+ set text(text: string) {
+ this.setAttribute('text', text);
+ }
+
+ get height() {
+ return this.getAttribute('height') || '';
+ }
+
+ set height(height: string) {
+ this.setAttribute('height', height);
+ }
+
+ initHtml(): string {
+ return `
+
+
+ `;
+ }
+
+ initElements(): void {
+ this.page = this.shadowRoot?.querySelector('#box');
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, value: string) {
+ switch (name) {
+ }
+ }
+}
diff --git a/ide/src/base-ui/chart/pie/LitChartPie.ts b/ide/src/base-ui/chart/pie/LitChartPie.ts
new file mode 100644
index 0000000000000000000000000000000000000000..360106d1a9d96c99c04f8670fc48ff7bdd155984
--- /dev/null
+++ b/ide/src/base-ui/chart/pie/LitChartPie.ts
@@ -0,0 +1,543 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { resizeCanvas } from '../helper.js';
+import { BaseElement, element } from '../../BaseElement.js';
+import { LitChartPieConfig } from './LitChartPieConfig.js';
+import { isPointIsCircle, pieChartColors, randomRgbColor } from './LitChartPieData.js';
+import { Utils } from '../../../trace/component/trace/base/Utils.js';
+
+interface Rectangle {
+ x: number;
+ y: number;
+ w: number;
+ h: number;
+}
+
+class Sector {
+ id?: any;
+ obj?: any;
+ key: any;
+ value: any;
+ startAngle?: number;
+ endAngle?: number;
+ startDegree?: number;
+ endDegree?: number;
+ color?: string;
+ percent?: number;
+ hover?: boolean;
+ ease?: {
+ initVal?: number;
+ step?: number;
+ process?: boolean;
+ };
+}
+
+@element('lit-chart-pie')
+export class LitChartPie extends BaseElement {
+ private eleShape: Element | null | undefined;
+ private tipEL: HTMLDivElement | null | undefined;
+ private labelsEL: HTMLDivElement | null | undefined;
+ canvas: HTMLCanvasElement | undefined | null;
+ ctx: CanvasRenderingContext2D | undefined | null;
+ cfg: LitChartPieConfig | null | undefined;
+ centerX: number | null | undefined;
+ centerY: number | null | undefined;
+ data: Sector[] = [];
+ radius: number | undefined;
+ private textRects: Rectangle[] = [];
+
+ set config(cfg: LitChartPieConfig | null | undefined) {
+ if (!cfg) return;
+ this.cfg = cfg;
+ (this.shadowRoot!.querySelector('#root') as HTMLDivElement).className =
+ cfg && cfg.data.length > 0 ? 'bg_hasdata' : 'bg_nodata';
+ this.measure();
+ this.render();
+ }
+
+ set dataSource(arr: any[]) {
+ if (this.cfg) {
+ this.cfg.data = arr;
+ this.measure();
+ this.render();
+ }
+ }
+
+ showHover() {
+ let hasHover = false;
+ this.data.forEach((it) => {
+ it.hover = it.obj.isHover;
+ if (it.hover) {
+ hasHover = true;
+ }
+ this.updateHoverItemStatus(it);
+ if (it.hover) {
+ this.showTip(
+ this.centerX || 0,
+ this.centerY || 0,
+ this.cfg!.tip ? this.cfg!.tip(it) : `${it.key}: ${it.value}`
+ );
+ }
+ });
+ if (!hasHover) {
+ this.hideTip();
+ }
+ this.render();
+ }
+
+ measure() {
+ if (!this.cfg) return;
+ this.data = [];
+ this.radius = (Math.min(this.clientHeight, this.clientWidth) * 0.65) / 2 - 10;
+ let cfg = this.cfg!;
+ let startAngle = 0;
+ let startDegree = 0;
+ let full = Math.PI / 180; //每度
+ let fullDegree = 0; //每度
+ let sum = this.cfg.data.reduce((previousValue, currentValue) => currentValue[cfg.angleField] + previousValue, 0);
+ this.labelsEL!.textContent = '';
+ let labelArray: string[] = [];
+ this.cfg.data.forEach((it, index) => {
+ let item: Sector = {
+ id: `id-${Utils.uuid()}`,
+ color: this.cfg!.label.color ? this.cfg!.label.color(it) : pieChartColors[index % pieChartColors.length],
+ obj: it,
+ key: it[cfg.colorField],
+ value: it[cfg.angleField],
+ startAngle: startAngle,
+ endAngle: startAngle + full * ((it[cfg.angleField] / sum) * 360),
+ startDegree: startDegree,
+ endDegree: startDegree + fullDegree + (it[cfg.angleField] / sum) * 360,
+ ease: {
+ initVal: 0,
+ step: (startAngle + full * ((it[cfg.angleField] / sum) * 360)) / startDegree,
+ process: true,
+ },
+ };
+ this.data.push(item);
+ startAngle += full * ((it[cfg.angleField] / sum) * 360);
+ startDegree += fullDegree + (it[cfg.angleField] / sum) * 360;
+ labelArray.push(``);
+ });
+ this.labelsEL!.innerHTML = labelArray.join('');
+ }
+
+ get config(): LitChartPieConfig | null | undefined {
+ return this.cfg;
+ }
+
+ connectedCallback() {
+ super.connectedCallback();
+ this.eleShape = this.shadowRoot!.querySelector('#shape');
+ this.tipEL = this.shadowRoot!.querySelector('#tip');
+ this.labelsEL = this.shadowRoot!.querySelector('#labels');
+ this.canvas = this.shadowRoot!.querySelector('#canvas');
+ this.ctx = this.canvas!.getContext('2d', { alpha: true });
+ resizeCanvas(this.canvas!);
+ this.radius = (Math.min(this.clientHeight, this.clientWidth) * 0.65) / 2 - 10;
+ this.centerX = this.clientWidth / 2;
+ this.centerY = this.clientHeight / 2 - 40;
+ this.ctx?.translate(this.centerX, this.centerY);
+ this.canvas!.onmouseout = (e) => {
+ this.hideTip();
+ this.data.forEach((it) => {
+ it.hover = false;
+ this.updateHoverItemStatus(it);
+ });
+ this.render();
+ };
+ //增加点击事件
+ this.canvas!.onclick = (ev) => {
+ let rect = this.getBoundingClientRect();
+ let x = ev.pageX - rect.left - this.centerX!;
+ let y = ev.pageY - rect.top - this.centerY!;
+ if (isPointIsCircle(0, 0, x, y, this.radius!)) {
+ let degree = this.computeDegree(x, y);
+ this.data.forEach((it) => {
+ if (degree >= it.startDegree! && degree <= it.endDegree!) {
+ this.config?.angleClick?.(it.obj);
+ }
+ });
+ }
+ };
+ this.canvas!.onmousemove = (ev) => {
+ let rect = this.getBoundingClientRect();
+ let x = ev.pageX - rect.left - this.centerX!;
+ let y = ev.pageY - rect.top - this.centerY!;
+ if (isPointIsCircle(0, 0, x, y, this.radius!)) {
+ let degree = this.computeDegree(x, y);
+ this.data.forEach((it) => {
+ it.hover = degree >= it.startDegree! && degree <= it.endDegree!;
+ this.updateHoverItemStatus(it);
+ it.obj.isHover = it.hover;
+ if (it.hover && this.cfg) {
+ this.cfg.hoverHandler?.(it.obj);
+ this.showTip(
+ ev.pageX - rect.left + 10,
+ ev.pageY - this.offsetTop - 10,
+ this.cfg.tip ? this.cfg!.tip(it) : `${it.key}: ${it.value}`
+ );
+ }
+ });
+ } else {
+ this.hideTip();
+ this.data.forEach((it) => {
+ it.hover = false;
+ it.obj.isHover = false;
+ this.updateHoverItemStatus(it);
+ });
+ this.cfg?.hoverHandler?.(undefined);
+ }
+ this.render();
+ };
+ this.render();
+ }
+
+ updateHoverItemStatus(item: any) {
+ let label = this.shadowRoot!.querySelector(`#${item.id}`);
+ if (label) {
+ (label as HTMLLabelElement).style.boxShadow = item.hover ? '0 0 5px #22ffffff' : '';
+ }
+ }
+
+ computeDegree(x: number, y: number) {
+ let degree = (360 * Math.atan(y / x)) / (2 * Math.PI);
+ if (x >= 0 && y >= 0) {
+ degree = degree;
+ } else if (x < 0 && y >= 0) {
+ degree = 180 + degree;
+ } else if (x < 0 && y < 0) {
+ degree = 180 + degree;
+ } else {
+ degree = 270 + (90 + degree);
+ }
+ return degree;
+ }
+
+ initElements(): void {
+ new ResizeObserver((entries, observer) => {
+ entries.forEach((it) => {
+ resizeCanvas(this.canvas!);
+ this.centerX = this.clientWidth / 2;
+ this.centerY = this.clientHeight / 2 - 40;
+ this.ctx?.translate(this.centerX, this.centerY);
+ this.measure();
+ this.render();
+ });
+ }).observe(this);
+ }
+
+ render(ease: boolean = true) {
+ if (!this.canvas || !this.cfg) return;
+ if (this.radius! <= 0) return;
+ this.ctx?.clearRect(0 - this.centerX!, 0 - this.centerY!, this.clientWidth, this.clientHeight);
+ this.data.forEach((it) => {
+ this.ctx!.beginPath();
+ this.ctx!.fillStyle = it.color as string;
+ this.ctx!.strokeStyle = this.data.length > 1 ? '#fff' : (it.color as string);
+ this.ctx?.moveTo(0, 0);
+ if (it.hover) {
+ this.ctx!.lineWidth = 1;
+ this.ctx!.arc(0, 0, this.radius!, it.startAngle!, it.endAngle!, false);
+ } else {
+ this.ctx!.lineWidth = 1;
+ if (ease) {
+ if (it.ease!.initVal! < it.endAngle! - it.startAngle!) {
+ it.ease!.process = true;
+ this.ctx!.arc(0, 0, this.radius!, it.startAngle!, it.startAngle! + it.ease!.initVal!, false);
+ it.ease!.initVal! += it.ease!.step!;
+ } else {
+ it.ease!.process = false;
+ this.ctx!.arc(0, 0, this.radius!, it.startAngle!, it.endAngle!, false);
+ }
+ } else {
+ this.ctx!.arc(0, 0, this.radius!, it.startAngle!, it.endAngle!, false);
+ }
+ }
+ this.ctx?.lineTo(0, 0);
+ this.ctx?.fill();
+ this.ctx!.stroke();
+ this.ctx?.closePath();
+ });
+
+ this.data
+ .filter((it) => it.hover)
+ .forEach((it) => {
+ this.ctx!.beginPath();
+ this.ctx!.fillStyle = it.color as string;
+ this.ctx!.lineWidth = 1;
+ this.ctx?.moveTo(0, 0);
+ this.ctx!.arc(0, 0, this.radius!, it.startAngle!, it.endAngle!, false);
+ this.ctx?.lineTo(0, 0);
+ this.ctx!.strokeStyle = this.data.length > 1 ? '#000' : (it.color as string);
+ this.ctx!.stroke();
+ this.ctx?.closePath();
+ });
+
+ this.textRects = [];
+ if (this.cfg.showChartLine) {
+ this.data.forEach((it) => {
+ let text = `${it.value}`;
+ let metrics = this.ctx!.measureText(text);
+ let textWidth = metrics.width;
+ let textHeight = metrics.fontBoundingBoxAscent + metrics.fontBoundingBoxDescent;
+ this.ctx!.beginPath();
+ this.ctx!.strokeStyle = it.color!;
+ this.ctx!.fillStyle = '#595959';
+ let deg = it.startDegree! + (it.endDegree! - it.startDegree!) / 2;
+ let dep = 25;
+ let x1 = 0 + this.radius! * Math.cos((deg * Math.PI) / 180);
+ let y1 = 0 + this.radius! * Math.sin((deg * Math.PI) / 180);
+ let x2 = 0 + (this.radius! + 13) * Math.cos((deg * Math.PI) / 180);
+ let y2 = 0 + (this.radius! + 13) * Math.sin((deg * Math.PI) / 180);
+ let x3 = 0 + (this.radius! + dep) * Math.cos((deg * Math.PI) / 180);
+ let y3 = 0 + (this.radius! + dep) * Math.sin((deg * Math.PI) / 180);
+ this.ctx!.moveTo(x1, y1);
+ this.ctx!.lineTo(x2, y2);
+ this.ctx!.stroke();
+ let rect = this.correctRect({
+ x: x3 - textWidth / 2,
+ y: y3 + textHeight / 2,
+ w: textWidth,
+ h: textHeight,
+ });
+ this.ctx?.fillText(text, rect.x, rect.y);
+ this.ctx?.closePath();
+ });
+ }
+ if (this.data.filter((it) => it.ease!.process).length > 0) {
+ requestAnimationFrame(() => this.render(ease));
+ }
+ }
+
+ correctRect(rect: Rectangle): Rectangle {
+ if (this.textRects.length == 0) {
+ this.textRects.push(rect);
+ return rect;
+ } else {
+ let rectangles = this.textRects.filter((it) => this.intersect(it, rect).cross);
+ if (rectangles.length == 0) {
+ this.textRects.push(rect);
+ return rect;
+ } else {
+ let it = rectangles[0];
+ let inter = this.intersect(it, rect);
+ if (inter.direction == 'Right') {
+ rect.x += inter.crossW;
+ } else if (inter.direction == 'Bottom') {
+ rect.y += inter.crossH;
+ } else if (inter.direction == 'Left') {
+ rect.x -= inter.crossW;
+ } else if (inter.direction == 'Top') {
+ rect.y -= inter.crossH;
+ } else if (inter.direction == 'Right-Top') {
+ rect.y -= inter.crossH;
+ } else if (inter.direction == 'Right-Bottom') {
+ rect.y += inter.crossH;
+ } else if (inter.direction == 'Left-Top') {
+ rect.y -= inter.crossH;
+ } else if (inter.direction == 'Left-Bottom') {
+ rect.y += inter.crossH;
+ }
+ this.textRects.push(rect);
+ return rect;
+ }
+ }
+ }
+
+ intersect(
+ r1: Rectangle,
+ rect: Rectangle
+ ): {
+ cross: boolean;
+ direction: string;
+ crossW: number;
+ crossH: number;
+ } {
+ let cross: boolean;
+ let direction: string;
+ let crossW: number;
+ let crossH: number;
+ let maxX = r1.x + r1.w > rect.x + rect.w ? r1.x + r1.w : rect.x + rect.w;
+ let maxY = r1.y + r1.h > rect.y + rect.h ? r1.y + r1.h : rect.y + rect.h;
+ let minX = r1.x < rect.x ? r1.x : rect.x;
+ let minY = r1.y < rect.y ? r1.y : rect.y;
+ if (maxX - minX < rect.w + r1.w && maxY - minY < r1.h + rect.h) {
+ cross = true;
+ } else {
+ cross = false;
+ }
+ crossW = Math.abs(maxX - minX - (rect.w + r1.w));
+ crossH = Math.abs(maxY - minY - (rect.y + r1.y));
+ if (rect.x > r1.x) {
+ //right
+ if (rect.y > r1.y) {
+ //bottom
+ direction = 'Right-Bottom';
+ } else if (rect.y == r1.y) {
+ //middle
+ direction = 'Right';
+ } else {
+ //top
+ direction = 'Right-Top';
+ }
+ } else if (rect.x < r1.x) {
+ //left
+ if (rect.y > r1.y) {
+ //bottom
+ direction = 'Left-Bottom';
+ } else if (rect.y == r1.y) {
+ //middle
+ direction = 'Left';
+ } else {
+ //top
+ direction = 'Left-Top';
+ }
+ } else {
+ if (rect.y > r1.y) {
+ //bottom
+ direction = 'Bottom';
+ } else if (rect.y == r1.y) {
+ //middle
+ direction = 'Right'; //superposition default right
+ } else {
+ //top
+ direction = 'Top';
+ }
+ }
+ return {
+ cross,
+ direction,
+ crossW,
+ crossH,
+ };
+ }
+
+ showTip(x: number, y: number, msg: string) {
+ this.tipEL!.style.display = 'flex';
+ this.tipEL!.style.top = `${y}px`;
+ this.tipEL!.style.left = `${x}px`;
+ this.tipEL!.innerHTML = msg;
+ }
+
+ hideTip() {
+ this.tipEL!.style.display = 'none';
+ }
+
+ initHtml(): string {
+ return `
+
+
+
+
+
+
+
`;
+ }
+}
diff --git a/ide/src/base-ui/chart/pie/LitChartPieConfig.ts b/ide/src/base-ui/chart/pie/LitChartPieConfig.ts
new file mode 100644
index 0000000000000000000000000000000000000000..50846a86dc5adee92b0d2de6d6fda115476b36ed
--- /dev/null
+++ b/ide/src/base-ui/chart/pie/LitChartPieConfig.ts
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export interface LitChartPieConfig {
+ appendPadding: number;
+ data: any[];
+ angleField: string;
+ colorField: string;
+ radius: number;
+ angleClick?: (it: object) => void;
+ label: {
+ type: string; // spider|inner|outer
+ offset?: string;
+ content?: (it: object) => string;
+ color?: (it: object) => string;
+ style?: {
+ fontSize: number;
+ textAlign: string;
+ };
+ };
+ hoverHandler?: (data: any) => void;
+ showChartLine?: boolean;
+ tip: ((a: any) => string) | undefined;
+ interactions: {
+ type: string; //element-active | element-selected
+ }[];
+}
diff --git a/ide/src/base-ui/chart/pie/LitChartPieData.ts b/ide/src/base-ui/chart/pie/LitChartPieData.ts
new file mode 100644
index 0000000000000000000000000000000000000000..af3a4b8e17d02ba0c290837313c05f3aca834c25
--- /dev/null
+++ b/ide/src/base-ui/chart/pie/LitChartPieData.ts
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+export function randomRgbColor() {
+ const letters = '0123456789ABCDEF';
+ let color = '#';
+ for (let i = 0; i < 6; i++) {
+ color += letters[Math.floor(Math.random() * 16)];
+ }
+ return color;
+}
+
+export function isPointIsCircle(x1: number, y1: number, x2: number, y2: number, radius: number): boolean {
+ return Math.sqrt(Math.pow(Math.abs(x2 - x1), 2) + Math.pow(Math.abs(y2 - y1), 2)) < radius;
+}
+
+export const pieChartColors = [
+ '#5b8ff9',
+ '#5ad8a6',
+ '#5d7092',
+ '#f6bd16',
+ '#e8684a',
+ '#6DC8EC',
+ '#9270CA',
+ '#FF9D4D',
+ '#269A99',
+ '#FF99C3',
+ '#0039AC',
+ '#229D00',
+ '#AEAEAE',
+ '#FFEE00',
+ '#FF3000',
+ '#CBE1FF',
+ '#6000FF',
+ '#A24900',
+ '#70FFFE',
+ '#FF00C4',
+];
diff --git a/ide/src/base-ui/checkbox/LitCheckBox.ts b/ide/src/base-ui/checkbox/LitCheckBox.ts
new file mode 100644
index 0000000000000000000000000000000000000000..2b51cbac91ad00af3aa42b2f85ac532b7fd0b00a
--- /dev/null
+++ b/ide/src/base-ui/checkbox/LitCheckBox.ts
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+
+@element('lit-check-box')
+export class LitCheckBox extends BaseElement {
+ private checkbox: HTMLInputElement | undefined;
+
+ static get observedAttributes() {
+ return ['checked', 'value'];
+ }
+
+ get indeterminate() {
+ return this.checkbox!.indeterminate;
+ }
+
+ set indeterminate(value) {
+ if (value === null || value === false) {
+ this.checkbox!.indeterminate = false;
+ } else {
+ this.checkbox!.indeterminate = true;
+ }
+ }
+
+ get checked() {
+ return this.getAttribute('checked') !== null;
+ }
+
+ set checked(value: boolean) {
+ if (value === null || !value) {
+ this.removeAttribute('checked');
+ } else {
+ this.setAttribute('checked', '');
+ }
+ }
+
+ get value() {
+ return this.getAttribute('value') || '';
+ }
+
+ set value(value: string) {
+ this.setAttribute('value', value);
+ }
+
+ initHtml(): string {
+ return `
+
+
+
+ `;
+ }
+
+ initElements(): void {
+ this.checkbox = this.shadowRoot?.getElementById('checkbox') as HTMLInputElement;
+ }
+
+ connectedCallback() {
+ this.checkbox!.addEventListener('change', (ev) => {
+ this.checked = this.checkbox!.checked;
+ this.dispatchEvent(
+ new CustomEvent('change', {
+ detail: {
+ checked: this.checked,
+ },
+ })
+ );
+ });
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
+ if (name == 'checked' && this.checkbox) {
+ this.checkbox.checked = newValue !== null;
+ }
+ if (name == 'value') {
+ let slot = this.shadowRoot?.getElementById('slot');
+ slot!.textContent = newValue;
+ }
+ }
+}
diff --git a/ide/src/base-ui/checkbox/LitCheckBoxWithText.ts b/ide/src/base-ui/checkbox/LitCheckBoxWithText.ts
new file mode 100644
index 0000000000000000000000000000000000000000..cba91f29b9d76efee9bbc9297e0074c4e6fc2f61
--- /dev/null
+++ b/ide/src/base-ui/checkbox/LitCheckBoxWithText.ts
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+import { SpCheckDesBox } from '../../trace/component/setting/SpCheckDesBox.js';
+
+@element('lit-check-text')
+export class LitCheckBoxWithText extends BaseElement {
+ private _checkBox: SpCheckDesBox | undefined;
+ private _lowerLimit: HTMLInputElement | undefined;
+ private _upLimit: HTMLInputElement | undefined;
+
+ static get observedAttributes() {
+ return ['text', 'lowerLimit', 'upLimit', 'checked'];
+ }
+
+ get text(): string {
+ return this.getAttribute('text') || '';
+ }
+
+ set text(text: string) {
+ this.setAttribute('text', text);
+ }
+
+ get lowerLimit(): string {
+ return this.getAttribute('lowerLimit') || '0';
+ }
+
+ set lowerLimit(lower: string) {
+ this.setAttribute('lowerLimit', lower);
+ }
+
+ get upLimit(): string {
+ return this.getAttribute('upLimit') || '∞';
+ }
+
+ set upLimit(upLimit: string) {
+ this.setAttribute('upLimit', upLimit);
+ }
+
+ get checked() {
+ return this.getAttribute('checked') != null;
+ }
+
+ set checked(checked: boolean) {
+ if (checked) {
+ this.setAttribute('checked', '');
+ } else {
+ this.removeAttribute('checked');
+ }
+ }
+
+ initElements(): void {
+ this._checkBox = this.shadowRoot?.getElementById('checkbox') as SpCheckDesBox;
+ this._lowerLimit = this.shadowRoot?.getElementById('textLowerLimit') as HTMLInputElement;
+ this._upLimit = this.shadowRoot?.getElementById('_upLimit') as HTMLInputElement;
+ }
+
+ initHtml(): string {
+ return `
+
+
+
+
+ `;
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
+ if (name == 'checked') {
+ this._checkBox!.checked = newValue !== null;
+ }
+ if (name == 'text') {
+ this._checkBox?.setAttribute('value', newValue);
+ }
+ if (name == 'lowerLimit') {
+ this._lowerLimit!.textContent = newValue;
+ }
+ if (name == 'upLimit') {
+ this._upLimit!.textContent = newValue;
+ }
+ }
+}
diff --git a/ide/src/base-ui/checkbox/LitCheckGroup.ts b/ide/src/base-ui/checkbox/LitCheckGroup.ts
new file mode 100644
index 0000000000000000000000000000000000000000..1cb09f244607567a5ce4ac18aa0078db5f9bd6c8
--- /dev/null
+++ b/ide/src/base-ui/checkbox/LitCheckGroup.ts
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+import { LitCheckBox } from './LitCheckBox.js';
+
+@element('lit-check-group')
+export class LitCheckGroup extends BaseElement {
+ get direction() {
+ return this.getAttribute('direction');
+ }
+
+ get value(): Array {
+ let values = [];
+ for (const litCheckBoxElement of this.querySelectorAll('lit-check-box[checked]')) {
+ values.push(litCheckBoxElement.value);
+ }
+ return values;
+ }
+
+ initElements(): void {}
+
+ initHtml(): string {
+ return `
+
+ `;
+ }
+}
diff --git a/ide/src/base-ui/drawer/LitDrawer.ts b/ide/src/base-ui/drawer/LitDrawer.ts
new file mode 100644
index 0000000000000000000000000000000000000000..6bd2efce48959a7bedeff37a339d90c686225a8d
--- /dev/null
+++ b/ide/src/base-ui/drawer/LitDrawer.ts
@@ -0,0 +1,347 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+
+@element('lit-drawer')
+export class LitDrawer extends BaseElement {
+ static get observedAttributes() {
+ return ['title', 'visible', 'placement', 'mask', 'mask-closable', 'closeable', 'content-padding', 'content-width'];
+ }
+
+ initHtml(): string {
+ return `
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ }
+ get contentWidth() {
+ return this.getAttribute('content-width') || '400px';
+ }
+ set contentWidth(value) {
+ this.shadowRoot!.querySelector('.drawer')!.style.width = value;
+ this.setAttribute('content-width', value);
+ }
+ get contentPadding() {
+ return this.getAttribute('content-padding') || '20px';
+ }
+ set contentPadding(value) {
+ this.shadowRoot!.querySelector('slot')!.style.padding = value;
+ this.setAttribute('content-padding', value);
+ }
+ get placement() {
+ return this.getAttribute('placement');
+ }
+ set placement(value: any) {
+ this.setAttribute('placement', value);
+ }
+ get title() {
+ return this.getAttribute('title') || '';
+ }
+ set title(value) {
+ this.shadowRoot!.querySelector('#drawer-tittle-text')!.textContent = value;
+ this.setAttribute('title', value);
+ }
+ get visible() {
+ return this.getAttribute('visible') !== null;
+ }
+ set visible(value: any) {
+ if (value) {
+ this.setAttribute('visible', value);
+ } else {
+ this.removeAttribute('visible');
+ }
+ }
+ get mask() {
+ return this.getAttribute('mask') !== null;
+ }
+ set mask(value) {
+ if (value) {
+ this.setAttribute('mask', '');
+ } else {
+ this.removeAttribute('mask');
+ }
+ }
+ get maskCloseable() {
+ return this.getAttribute('mask-closeable') !== null;
+ }
+ set maskCloseable(value) {
+ if (value) {
+ this.setAttribute('mask-closeable', '');
+ } else {
+ this.removeAttribute('mask-closeable');
+ }
+ }
+ get closeable() {
+ return this.getAttribute('closeable') !== null;
+ }
+
+ set closeable(value) {
+ if (value) {
+ this.setAttribute('closeable', '');
+ } else {
+ this.removeAttribute('closeable');
+ }
+ }
+
+ //当 custom element首次被插入文档DOM时,被调用。
+ initElements(): void {
+ let bg: HTMLDivElement | null = this.shadowRoot!.querySelector('.bg');
+ if (this.maskCloseable) {
+ bg!.onclick = (e: any) => {
+ e.stopPropagation();
+ this.visible = false;
+ this.dispatchEvent(new CustomEvent('onClose', e));
+ };
+ }
+ if (this.closeable) {
+ (this.shadowRoot!.querySelector('.close-icon') as any).onclick = (e: any) => {
+ this.visible = false;
+ this.dispatchEvent(new CustomEvent('onClose', e));
+ };
+ }
+ }
+ set onClose(fn: any) {
+ this.addEventListener('onClose', fn);
+ }
+ //当 custom element从文档DOM中删除时,被调用。
+ disconnectedCallback() {}
+
+ //当 custom element被移动到新的文档时,被调用。
+ adoptedCallback() {
+ console.log('Custom square element moved to new page.');
+ }
+
+ //当 custom element增加、删除、修改自身属性时,被调用。
+ attributeChangedCallback(name: string, oldValue: string, newValue: string) {
+ if (this.mask) {
+ if (name === 'visible') {
+ if (newValue !== null) {
+ this.style.pointerEvents = 'all';
+ } else {
+ this.style.pointerEvents = 'none';
+ }
+ } else if (name === 'placement') {
+ if (newValue === 'bottom') {
+ let el = this.shadowRoot!.querySelector('.drawer');
+ }
+ }
+ }
+ }
+}
+
+if (!customElements.get('lit-drawer')) {
+ customElements.define('lit-drawer', LitDrawer);
+}
diff --git a/ide/src/base-ui/icon.svg b/ide/src/base-ui/icon.svg
new file mode 100644
index 0000000000000000000000000000000000000000..87156b88af11e43482ea4d5930bb9251d1027bce
--- /dev/null
+++ b/ide/src/base-ui/icon.svg
@@ -0,0 +1,321 @@
+
diff --git a/ide/src/base-ui/icon/LitIcon.ts b/ide/src/base-ui/icon/LitIcon.ts
new file mode 100644
index 0000000000000000000000000000000000000000..c93b26f8ac6b85474282f571ba11681686ea2a8b
--- /dev/null
+++ b/ide/src/base-ui/icon/LitIcon.ts
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+
+@element('lit-icon')
+export class LitIcon extends BaseElement {
+ private view?: number;
+ private icon: HTMLElement | undefined | null;
+ private use: SVGUseElement | undefined | null;
+ private d: SVGPathElement | undefined | null;
+ private _name?: string;
+ private _size?: number;
+ private _color?: string;
+ private _path?: string;
+
+ static get observedAttributes() {
+ return ['name', 'size', 'color', 'path'];
+ }
+
+ get name(): string {
+ return this.getAttribute('name') || '';
+ }
+
+ set name(value: string) {
+ this._name = value;
+ this.setAttribute('name', value);
+ }
+
+ get size(): number {
+ return parseInt(this.getAttribute('size') || '0', 10);
+ }
+
+ set size(value: number) {
+ this._size = value;
+ this.setAttribute('size', `${value}`);
+ }
+
+ set color(value: string) {
+ this._color = value;
+ this.setAttribute('color', value);
+ }
+
+ set path(value: string) {
+ this._path = value;
+ this.setAttribute('path', value);
+ }
+
+ initHtml(): string {
+ return `
+
+
+ `;
+ }
+
+ initElements() {
+ if (this.shadowRoot) {
+ this.icon = this.shadowRoot.getElementById('icon');
+ this.use = this.shadowRoot.querySelector('use');
+ this.d = this.shadowRoot.querySelector('path');
+ }
+ }
+
+ attributeChangedCallback(name: string, oldValue: string, value: string) {
+ switch (name) {
+ case 'name':
+ if (this.use)
+ this.use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', `./base-ui/icon.svg#icon-${value}`);
+ break;
+ case 'path':
+ if (this.d) this.d.setAttribute('d', value);
+ break;
+ case 'color':
+ if (this.icon) this.icon.style.color = value as string;
+ break;
+ case 'size':
+ if (this.icon) this.icon.style.fontSize = `${value}px`;
+ break;
+ }
+ }
+}
diff --git a/ide/src/base-ui/menu/LitMainMenu.ts b/ide/src/base-ui/menu/LitMainMenu.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e92b74a9bc060a2be1ba2f852abb53aee3328742
--- /dev/null
+++ b/ide/src/base-ui/menu/LitMainMenu.ts
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2022 Huawei Device Co., Ltd.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { BaseElement, element } from '../BaseElement.js';
+import './LitMainMenuItem.js';
+import './LitMainMenuGroup.js';
+import { LitMainMenuGroup } from './LitMainMenuGroup.js';
+import { LitMainMenuItem } from './LitMainMenuItem.js';
+let backgroundColor = sessionStorage.getItem('backgroundColor');
+
+@element('lit-main-menu')
+export class LitMainMenu extends BaseElement {
+ private slotElements: Element[] | undefined;
+ private _menus: Array | undefined;
+
+ static get observedAttributes() {
+ return [];
+ }
+
+ get menus(): Array | undefined {
+ return this._menus;
+ }
+
+ set menus(value: Array | undefined) {
+ this._menus = value;
+ this.shadowRoot?.querySelectorAll('lit-main-menu-group').forEach((a) => a.remove());
+ let menuBody = this.shadowRoot?.querySelector('.menu-body');
+ value?.forEach((it) => {
+ let group = new LitMainMenuGroup();
+ group.setAttribute('title', it.title || '');
+ group.setAttribute('describe', it.describe || '');
+ if (it.collapsed) {
+ group.setAttribute('collapsed', '');
+ } else {
+ group.removeAttribute('collapsed');
+ }
+ menuBody?.appendChild(group);
+ it.children?.forEach((item: any) => {
+ let th = new LitMainMenuItem();
+ th.setAttribute('icon', item.icon || '');
+ th.setAttribute('title', item.title || '');
+ if (item.fileChoose) {
+ th.setAttribute('file', '');
+ th.addEventListener('file-change', (e) => {
+ if (item.fileHandler && !th.disabled) {
+ item.fileHandler(e);
+ }
+ });
+ } else {
+ th.removeAttribute('file');
+ th.addEventListener('click', (e) => {
+ if (item.clickHandler && !th.disabled) {
+ item.clickHandler(item);
+ }
+ });
+ }
+ if (item.disabled != undefined) {
+ th.disabled = item.disabled;
+ }
+ group?.appendChild(th);
+ });
+ });
+ }
+
+ initElements(): void {
+ let st: HTMLSlotElement | null | undefined = this.shadowRoot?.querySelector('#st');
+ st?.addEventListener('slotchange', (e) => {
+ this.slotElements = st?.assignedElements();
+ this.slotElements?.forEach((it) => {
+ it.querySelectorAll('lit-main-menu-item').forEach((cell) => {});
+ });
+ });
+ let versionDiv: HTMLElement | null | undefined = this.shadowRoot?.querySelector('.version');
+ versionDiv!.innerText = (window as any).version || '';
+ }
+
+ initHtml(): string {
+ return `
+
+