From 8a4e3d026cee94e0c84e00f18b6e991b28eaaf57 Mon Sep 17 00:00:00 2001
From: scarqin <1054139596@qq.com>
Date: Mon, 5 Sep 2022 19:35:43 +0800
Subject: [PATCH 01/21] style:extension-detail

---
 src/app/electron-main/appView.ts              |   2 +-
 .../node/extension-manager/lib/loader.ts      |   3 +-
 .../node/extension-manager/types/manager.ts   |  11 +-
 .../detail/extension-detail.component.html    | 116 +++++++-----------
 .../pages/extension/extension.component.ts    |   5 +
 .../app/pages/extension/extension.module.ts   |  11 +-
 .../list/extension-list.component.html        |  42 +++++--
 .../list/extension-list.component.scss        |  15 +--
 .../src/app/shared/directives/index.ts        |   2 +-
 .../stop-propagation.directive.ts             |  15 +++
 .../webview/webview.directive.spec.ts         |   8 --
 .../directives/webview/webview.directive.ts   |   8 --
 .../browser/src/app/shared/shared.module.ts   |   6 +-
 .../browser/src/assets/theme/antd.less        |   6 +-
 14 files changed, 125 insertions(+), 125 deletions(-)
 create mode 100644 src/workbench/browser/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts
 delete mode 100644 src/workbench/browser/src/app/shared/directives/webview/webview.directive.spec.ts
 delete mode 100644 src/workbench/browser/src/app/shared/directives/webview/webview.directive.ts

diff --git a/src/app/electron-main/appView.ts b/src/app/electron-main/appView.ts
index 41307db78..e40f193d0 100644
--- a/src/app/electron-main/appView.ts
+++ b/src/app/electron-main/appView.ts
@@ -18,7 +18,7 @@ export class AppViews {
     if (module && module.moduleType === ModuleType.app) {
       if (module.isApp && this.mainModuleID !== module.moduleID) {
         this.mainModuleID = module.moduleID;
-        this.sidePosition = module.sidePosition;
+        // this.sidePosition = module.sidePosition;
       }
       this.createAppView(module);
 
diff --git a/src/platform/node/extension-manager/lib/loader.ts b/src/platform/node/extension-manager/lib/loader.ts
index 0296a4cb6..aecda76bc 100644
--- a/src/platform/node/extension-manager/lib/loader.ts
+++ b/src/platform/node/extension-manager/lib/loader.ts
@@ -40,8 +40,7 @@ export class ModuleLoader implements ModuleLoaderInterface {
    */
   loadModule(module: ModuleInfo): void {
     if ((this.runtime === ModuleRuntime.main && ![ModuleType.system, ModuleType.app].indexOf(module.moduleType))
-      || (this.runtime === ModuleRuntime.render && ![ModuleType.feature].indexOf(module.moduleType))
-      || (this.runtime === ModuleRuntime.web && !module.web)) {
+      || (this.runtime === ModuleRuntime.render && ![ModuleType.feature].indexOf(module.moduleType))) {
       console.log(`The [${module.moduleType}] module [${module.name}] can not run in runtime [${this.runtime}].`);
       return;
     }
diff --git a/src/platform/node/extension-manager/types/manager.ts b/src/platform/node/extension-manager/types/manager.ts
index 57c35b82a..a689549e4 100644
--- a/src/platform/node/extension-manager/types/manager.ts
+++ b/src/platform/node/extension-manager/types/manager.ts
@@ -36,6 +36,7 @@ export interface ModuleInfo {
   // extension name
   moduleName: string;
   // extension type
+  //!TODO what usage
   moduleType: ModuleType;
   // extension logo
   logo: string;
@@ -48,19 +49,17 @@ export interface ModuleInfo {
   // inject script before start app
   preload?: string;
   // 判断是不是顶层App
+  //!TODO use feature contribution to control page
   isApp?: boolean;
-  // web运行支持
-  web?: boolean;
+
   // 模块对应上层模块ID
+  //!TODO what usage?
   belongs?: Array<string>;
   // 下层关联模块ID集合
+  //!TODO what usage?
   sideItems?: Array<string>;
-  // 下层功能模块ID集合, 待移除
-  featureItems?: Array<string>;
   // 模块路径
   baseDir?: string;
-  // 边栏显示
-  sidePosition?: SidePosition;
   // 配置项
   configuration?: ModuleConfiguration;
   /** 贡献点 */
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 481b84644..7eafa8f7d 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -1,79 +1,57 @@
-<div class="py-3 extension-detail">
+<div class="py-3 extension-detail" *ngIf="extensionDetail">
   <div class="sticky top-0 z-50 bg-white">
-    <div class="pb-3">
-      <a nz-button nzType="link" (click)="backToList()">
-        <eo-iconpark-icon name="left" class="anticon" size="16px"></eo-iconpark-icon><span i18n>Back</span>
-      </a>
-    </div>
-    <div class="bbd"></div>
-  </div>
-  <section class="h-full p-4 max-w-[80vw] mx-auto">
-    <div class="flex">
-      <i class="block w-24 h-24 mr-8 bg-center bg-no-repeat bg-cover border rounded-lg bd_all"
-        [ngStyle]="{ 'background-image': 'url(' + (extensionDetail?.logo || '') + ')' }"></i>
-      <div class="flex flex-col flex-1">
-        <div class="flex flex-col">
-          <span class="mb-2 text-xl font-bold">{{ extensionDetail?.moduleName }}</span>
-          <p class="w-full h-20">{{ extensionDetail?.description }}</p>
+    <div class="pb-3 flex justify-between h-[50px]">
+      <div class="flex items-center">
+        <a nz-button nzType="link" (click)="backToList()">
+          <eo-iconpark-icon name="left" class="anticon" size="16px"></eo-iconpark-icon><span i18n>Back</span>
+        </a>
+        <div class="mr-4">
+          <nz-avatar nzSize="small" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
         </div>
-        <div class="flex"></div>
+        <span class="font-bold text-[16px]">{{ extensionDetail.moduleName }}</span>
+      </div>
+      <div class="flex items-center">
+        <nz-switch class="mr-2.5"></nz-switch>
+        <!-- <button nz-button nzType="primary">Install</button> -->
+        <eo-iconpark-icon size="20px" name="delete" nz-tooltip nzTooltipTitle="Uninstall"> </eo-iconpark-icon>
       </div>
     </div>
-    <div class="flex w-full mt-6 h-[calc(100vh-350px)]">
-      <div class="flex-auto min-w-0">
-        <h2 class="text-lg font-bold" i18n>Intro</h2>
-        <!-- <nz-divider></nz-divider> -->
+    <!-- <div class="bbd"></div> -->
+  </div>
+  <section class="h-full px-4 max-w-[80vw] mx-auto">
+    <p class="w-full">{{ extensionDetail.description }}</p>
+    <nz-tabset>
+      <!-- Setting -->
+      <nz-tab nzTitle="Settings">
+        <div class="ant-form-item">
+          <label for="eoapi-extensions.token"> Token</label>
+          <div _ngcontent-xnd-c228="" class="text-[12px] mb-[8px] text-gray-400">
+            Eolink OpenAPI token, which can only be accessed remotely after configuration
+          </div>
+          <input nz-input placeholder="Basic usage" />
+        </div>
+        <button nz-button nzType="primary">Save</button>
+      </nz-tab>
+      <!-- Details -->
+      <nz-tab nzTitle="Details">
         <div class="h-full overflow-auto markdown-desc">
           <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
-            <eo-shadow-dom class="md-preview" [text]="extensionDetail?.introduction" [options]="{ html: true }">
+            <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
             </eo-shadow-dom>
-          </nz-skeleton>
-        </div>
-      </div>
-      <div class="w-[1px] bg-[#f2f2f2] mr-[10px]"></div>
-      <div class="shrink-0 w-[200px] 2xl:w-[250px] overflow-auto h-full">
-        <h2 class="text-lg font-bold" i18n>Install</h2>
-        <div class="flex items-center mt-[22px]" *ngIf="isElectron && !extensionDetail?.installed">
-          <nz-skeleton-element *ngIf="isNotLoaded" nzType="button" nzSize="large" [nzActive]="true" style="width:100%">
-          </nz-skeleton-element>
-          <button *ngIf="isElectron && !isNotLoaded" nz-button nzType="primary" nzBlock nzSize="large"
-            [nzLoading]="isOperating" (click)="manageExtension('install', extensionDetail?.name)" i18n>
-            Install
-          </button>
-        </div>
-        <div *ngIf="isElectron && extensionDetail?.installed">
-          <nz-skeleton-element *ngIf="isNotLoaded" nzType="button" [nzActive]="true" nzSize="large" style="width:100%">
-          </nz-skeleton-element>
-          <button *ngIf="!isNotLoaded" nz-button nzBlock nzType="primary" nzDanger nzSize="large"
-            [nzLoading]="isOperating" class="mt-[12px]" (click)="manageExtension('uninstall', extensionDetail?.name)"
-            i18n>
-            Uninstall
-          </button>
-        </div>
-
-        <div *ngIf="!isElectron">
-          <button nz-button nzType="primary" nz-dropdown [nzDropdownMenu]="download" class="!w-full" i18n>
-            Download Client
-          </button>
-          <nz-dropdown-menu #download="nzDropdownMenu">
-            <ul nz-menu>
-              <ng-container *ngFor="let item of resourceInfo; let index = index">
-                <a [href]="item.link" nz-menu-item>{{ item.name }}</a>
-                <li nz-menu-divider *ngIf="index !== resourceInfo.length - 1"></li>
-              </ng-container>
-            </ul>
-          </nz-dropdown-menu>
-          <div class="bg-[#FF1744] p-[6px] mt-[14px] rounded-[3px] text-white" i18n>
-            The extensions can only be installed on the client at present. Please download the client first~
-          </div>
-        </div>
-
-        <h2 class="text-lg font-bold mt-[30px]" i18n>Support</h2>
+          </nz-skeleton></div
+      ></nz-tab>
+      <nz-tab nzTitle="Support">
         <nz-descriptions [nzColumn]="1" nzTitle="">
-          <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail?.author }}</nz-descriptions-item>
-          <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail?.version }} </nz-descriptions-item>
-        </nz-descriptions>
-      </div>
-    </div>
+          <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail.author }}</nz-descriptions-item>
+          <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
+          <nz-descriptions-item i18n-nzTitle nzTitle="Repository">dfsdfds </nz-descriptions-item>
+          <nz-descriptions-item i18n-nzTitle nzTitle="Homepage">dsfdsf </nz-descriptions-item>
+        </nz-descriptions></nz-tab>
+      <nz-tab nzTitle="ChangeLog">
+        <h1>Change Log</h1>
+        <p>All notable changes to this project will be documented in this file.</p>
+        <h2>[12.0.1] - 2022-03-03</h2>
+      </nz-tab>
+    </nz-tabset>
   </section>
 </div>
diff --git a/src/workbench/browser/src/app/pages/extension/extension.component.ts b/src/workbench/browser/src/app/pages/extension/extension.component.ts
index 5308c97ca..919d23293 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/extension.component.ts
@@ -22,6 +22,11 @@ export class ExtensionComponent implements OnInit {
       title: $localize`Official`,
       isLeaf: true,
     },
+    {
+      key: 'installed',
+      title: $localize`Installed`,
+      isLeaf: true,
+    }
   ];
   fixedTreeNode: GroupTreeItem[] | NzTreeNode[] = [
     {
diff --git a/src/workbench/browser/src/app/pages/extension/extension.module.ts b/src/workbench/browser/src/app/pages/extension/extension.module.ts
index 2597db78d..d7563111a 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.module.ts
+++ b/src/workbench/browser/src/app/pages/extension/extension.module.ts
@@ -15,14 +15,18 @@ import { NzTagModule } from 'ng-zorro-antd/tag';
 import { NzDividerModule } from 'ng-zorro-antd/divider';
 import { NzTreeModule } from 'ng-zorro-antd/tree';
 import { NzDropDownModule } from 'ng-zorro-antd/dropdown';
+import { NzSwitchModule } from 'ng-zorro-antd/switch';
 import { NzSkeletonModule } from 'ng-zorro-antd/skeleton';
 import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
-
+import { NzCardModule } from 'ng-zorro-antd/card';
+import { NzAvatarModule } from 'ng-zorro-antd/avatar';
+import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
 @NgModule({
   imports: [
     SharedModule,
     FormsModule,
     NzTabsModule,
+    NzToolTipModule,
     NzTagModule,
     NzDescriptionsModule,
     NzInputModule,
@@ -31,8 +35,11 @@ import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module'
     CommonModule,
     NzDividerModule,
     NzTreeModule,
+    NzSwitchModule,
     NzDropDownModule,
-    NzSkeletonModule
+    NzSkeletonModule,
+    NzCardModule,
+    NzAvatarModule
   ],
   declarations: [ExtensionComponent, ExtensionListComponent, ExtensionDetailComponent],
 })
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index b6e1d44c0..e41615b41 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -1,22 +1,38 @@
 <div class="px-3">
-
   <div class="grid grid-cols-2 gap-6 py-5 list-block">
     <div
-      class="bd_all w-full min-h-[140px] p-5 flex flex-col flex-wrap items-center plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
-      *ngFor="let it of renderList" (click)="clickExtension(it)">
+      class="bd_all w-full min-h-[140px] p-5 flex flex-col flex-wrap plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
+      *ngFor="let it of renderList"
+      (click)="clickExtension(it)"
+    >
       <div class="flex w-full">
-        <div class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
-          [ngClass]="{ 'bg-gray-100': it.logo }" [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }">
+        <div
+          class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
+          [ngClass]="{ 'bg-gray-100': it.logo }"
+          [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }"
+        ></div>
+        <div class="flex flex-col">
+          <div class="flex flex-col flex-auto">
+            <span class="text-lg font-bold">{{ it.moduleName }}</span>
+            <span class="my-2 text-gray-400">{{ it.author }}</span>
+            <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
+          </div>
+          <div class="btn-group">
+            <a nz-button nzType="link">Setting</a>
+            <nz-divider nzType="vertical"></nz-divider>
+            <a nz-button nzType="link">Details</a>
+          </div>
         </div>
-
-        <div class="flex flex-col flex-auto">
-          <span class="text-lg font-bold">{{ it.moduleName }}</span>
-          <span class="my-2 text-gray-400">{{ it.author }}</span>
-          <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
+        <div *ngIf="type === 'installed'">
+          <nz-switch click-stop-propagation [(ngModel)]="it.isOpen"></nz-switch>
         </div>
-        <div>
-          <span *ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
-            class="p-1 text-xs text-green-700 border-green-700 rounded-sm  bd_all whitespace-nowrap" i18n>Installed</span>
+        <div *ngIf="type !== 'installed'">
+          <span
+            *ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
+            class="p-1 text-xs text-green-700 border-green-700 rounded-sm bd_all whitespace-nowrap"
+            i18n
+            >Installed</span
+          >
         </div>
       </div>
     </div>
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
index 6d29e8594..61ed4c3f5 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
@@ -3,7 +3,8 @@
   display: -webkit-box;
   text-overflow: ellipsis;
   -webkit-box-orient: vertical;
-  -webkit-line-clamp: 3;
+  -webkit-line-clamp: 2;
+  height: 2rem;
 }
 
 .plugin-block {
@@ -18,17 +19,13 @@
     border-radius: 3px;
   }
 }
-.warn-color {
-  color: var(--MAIN_THEME_COLOR);
-}
 @media (max-width: 1279.9px) {
   .grid-cols-4 {
     grid-template-columns: repeat(3, minmax(0, 1fr));
   }
 }
-.w-60{
-  width: 15rem;
-}
-.h-76{
-  height: 17rem;
+.btn-group{
+  .ant-btn-link{
+    padding: 0;
+  }
 }
\ No newline at end of file
diff --git a/src/workbench/browser/src/app/shared/directives/index.ts b/src/workbench/browser/src/app/shared/directives/index.ts
index 84937176f..978b43166 100644
--- a/src/workbench/browser/src/app/shared/directives/index.ts
+++ b/src/workbench/browser/src/app/shared/directives/index.ts
@@ -1 +1 @@
-export * from './webview/webview.directive';
+export * from './stop-propagation/stop-propagation.directive';
diff --git a/src/workbench/browser/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts b/src/workbench/browser/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts
new file mode 100644
index 000000000..e8c9ccd26
--- /dev/null
+++ b/src/workbench/browser/src/app/shared/directives/stop-propagation/stop-propagation.directive.ts
@@ -0,0 +1,15 @@
+import { Directive, HostListener } from '@angular/core';
+
+@Directive({
+  selector: '[click-stop-propagation]',
+})
+export class ClickStopPropagationDirective {
+  @HostListener('click', ['$event'])
+  public onClick(event: any): void {
+    event.stopPropagation();
+  }
+  @HostListener('mousedown', ['$event'])
+  public onMousedown(event: any): void {
+    event.stopPropagation();
+  }
+}
diff --git a/src/workbench/browser/src/app/shared/directives/webview/webview.directive.spec.ts b/src/workbench/browser/src/app/shared/directives/webview/webview.directive.spec.ts
deleted file mode 100644
index d157f015b..000000000
--- a/src/workbench/browser/src/app/shared/directives/webview/webview.directive.spec.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { WebviewDirective } from './webview.directive';
-
-describe('WebviewDirective', () => {
-  it('should create an instance', () => {
-    const directive = new WebviewDirective();
-    expect(directive).toBeTruthy();
-  });
-});
diff --git a/src/workbench/browser/src/app/shared/directives/webview/webview.directive.ts b/src/workbench/browser/src/app/shared/directives/webview/webview.directive.ts
deleted file mode 100644
index 239fed4a3..000000000
--- a/src/workbench/browser/src/app/shared/directives/webview/webview.directive.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { Directive } from '@angular/core';
-
-@Directive({
-  selector: 'webview'
-})
-export class WebviewDirective {
-  constructor() { }
-}
diff --git a/src/workbench/browser/src/app/shared/shared.module.ts b/src/workbench/browser/src/app/shared/shared.module.ts
index 59e0e6ec9..3f94f1854 100644
--- a/src/workbench/browser/src/app/shared/shared.module.ts
+++ b/src/workbench/browser/src/app/shared/shared.module.ts
@@ -1,7 +1,7 @@
 import { NgModule } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { PageNotFoundComponent, ToolbarComponent, SidebarComponent } from './components';
-import { WebviewDirective } from './directives';
+import { ClickStopPropagationDirective } from './directives';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
 import { NzDrawerModule } from 'ng-zorro-antd/drawer';
@@ -69,10 +69,10 @@ const COMPONENTS = [
     NzCodeEditorModule,
     NzResizableModule,
   ],
-  declarations: [WebviewDirective, ...COMPONENTS, ApiParamsNumPipe, PageBlankComponent, EnvListComponent],
+  declarations: [ClickStopPropagationDirective, ...COMPONENTS, ApiParamsNumPipe, PageBlankComponent, EnvListComponent],
   providers: [ModalService],
   exports: [
-    WebviewDirective,
+    ClickStopPropagationDirective,
     ...COMPONENTS,
     ApiParamsNumPipe,
     NzPopoverModule,
diff --git a/src/workbench/browser/src/assets/theme/antd.less b/src/workbench/browser/src/assets/theme/antd.less
index 26eb0a663..7d87a23c7 100644
--- a/src/workbench/browser/src/assets/theme/antd.less
+++ b/src/workbench/browser/src/assets/theme/antd.less
@@ -16,6 +16,9 @@
 body {
   line-height: initial;
 }
+.ant-btn-link,.ant-btn-link:hover{
+  color: var(--BLUE_NORMAL);
+}
 // .ant-message-notice-content {
 // background-color: @theme-color;
 // color: #fff;
@@ -71,9 +74,6 @@ body {
 p {
   margin-bottom: 0;
 }
-.ant-btn {
-  border-radius: 3px !important;
-}
 .ant-dropdown {
   background-color: white;
 }

From b0f316218c49f970579b0886caeb0190c6d3d250 Mon Sep 17 00:00:00 2001
From: scarqin <1054139596@qq.com>
Date: Tue, 6 Sep 2022 08:17:23 +0800
Subject: [PATCH 02/21] style: optimizi

---
 .../detail/extension-detail.component.html    | 24 +++++++---
 .../detail/extension-detail.component.scss    |  7 ++-
 .../pages/extension/extension.component.html  |  5 +--
 .../pages/extension/extension.component.scss  | 44 ++++++++++---------
 .../list/extension-list.component.html        |  2 +-
 .../list/extension-list.component.scss        |  5 ---
 6 files changed, 45 insertions(+), 42 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 7eafa8f7d..de621a2d9 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -1,11 +1,12 @@
 <div class="py-3 extension-detail" *ngIf="extensionDetail">
-  <div class="sticky top-0 z-50 bg-white">
+  <div class="sticky px-2 top-0 z-50 bg-white">
     <div class="pb-3 flex justify-between h-[50px]">
       <div class="flex items-center">
         <a nz-button nzType="link" (click)="backToList()">
           <eo-iconpark-icon name="left" class="anticon" size="16px"></eo-iconpark-icon><span i18n>Back</span>
         </a>
-        <div class="mr-4">
+        <nz-divider nzType="vertical"></nz-divider>
+        <div class="mr-2">
           <nz-avatar nzSize="small" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
         </div>
         <span class="font-bold text-[16px]">{{ extensionDetail.moduleName }}</span>
@@ -13,7 +14,7 @@
       <div class="flex items-center">
         <nz-switch class="mr-2.5"></nz-switch>
         <!-- <button nz-button nzType="primary">Install</button> -->
-        <eo-iconpark-icon size="20px" name="delete" nz-tooltip nzTooltipTitle="Uninstall"> </eo-iconpark-icon>
+        <button nz-button nzType="default">Uninstall</button>
       </div>
     </div>
     <!-- <div class="bbd"></div> -->
@@ -34,23 +35,32 @@
       </nz-tab>
       <!-- Details -->
       <nz-tab nzTitle="Details">
-        <div class="h-full overflow-auto markdown-desc">
+        <div class="h-full overflow-auto w-4/5 m-auto">
           <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
             <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
             </eo-shadow-dom>
-          </nz-skeleton></div
-      ></nz-tab>
+          </nz-skeleton>
+        </div>
+      </nz-tab>
       <nz-tab nzTitle="Support">
         <nz-descriptions [nzColumn]="1" nzTitle="">
           <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail.author }}</nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Repository">dfsdfds </nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Homepage">dsfdsf </nz-descriptions-item>
-        </nz-descriptions></nz-tab>
+        </nz-descriptions></nz-tab
+      >
       <nz-tab nzTitle="ChangeLog">
         <h1>Change Log</h1>
         <p>All notable changes to this project will be documented in this file.</p>
         <h2>[12.0.1] - 2022-03-03</h2>
+        <!-- !show be markdown preview -->
+        <!-- <div class="h-full overflow-auto w-4/5 m-auto">
+          <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
+            <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
+            </eo-shadow-dom>
+          </nz-skeleton>
+        </div> -->
       </nz-tab>
     </nz-tabset>
   </section>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
index 3afa25b61..f631ef9b7 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
@@ -1,3 +1,6 @@
+.ant-divider-vertical{
+  margin:0 16px;
+}
 ::ng-deep {
   eo-extension-detail {
     .ant-tabs-content-holder {
@@ -13,10 +16,6 @@
     }
   }
   .extension-detail {
-    .markdown-desc {
-      overflow: auto;
-    }
-
     .ant-descriptions-row > td {
       padding-bottom: 8px;
     }
diff --git a/src/workbench/browser/src/app/pages/extension/extension.component.html b/src/workbench/browser/src/app/pages/extension/extension.component.html
index a0f97c4f5..f3df717d9 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.component.html
+++ b/src/workbench/browser/src/app/pages/extension/extension.component.html
@@ -1,4 +1,4 @@
-<section class="main">
+<section class="eo-extension-main">
   <section class="flex-shrink-0 p-0 left">
     <div class="mb-2">
       <input type="text" nz-input class="flex-1 w-full px-3 input" i18n-placeholder="@@Search" placeholder="Search"
@@ -24,9 +24,6 @@
       <div class="pl-[18px]">
         <div class="tree_node f_row f_js_ac" *ngIf="!node.origin?.isFixed && node.isLeaf">
           <div class="overflow-hidden f_row_ac text-ellipsis">
-            <b class="method_text method_text_{{ node.origin.method }} mr5" *ngIf="node.origin.method">
-              {{ node.origin.method }}
-            </b>
             <span class="text_omit node_title">{{ node.title }}</span>
           </div>
         </div>
diff --git a/src/workbench/browser/src/app/pages/extension/extension.component.scss b/src/workbench/browser/src/app/pages/extension/extension.component.scss
index b243e295b..f65fbefd0 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/extension.component.scss
@@ -1,12 +1,14 @@
-::ng-deep .main {
+::ng-deep .eo-extension-main {
   height: 100%;
   width: 100%;
   display: flex;
-
+  .ant-btn-link {
+    padding: 0;
+  }
   .input {
     height: 30px;
     border-color: transparent;
-    background-color: rgba(0,0,0,0.05);
+    background-color: rgba(0, 0, 0, 0.05);
     &:hover {
       border-color: #158565;
     }
@@ -14,27 +16,27 @@
   .tree_node {
     font-size: 12px;
   }
-    .ant-tree .ant-tree-treenode {
-      padding: 0;
-      margin: 2px 0;
-      border-radius: 3px;
-      transition: all 0.3s, border 0s, line-height 0s, box-shadow 0s;
-      &:hover {
-        background-color: #f5f5f5;
-      }
-    }
-    .ant-tree-treenode-selected {
-      background-color: var(--NAVBAR_BTN_BG);
-    }
-    .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected {
-      background-color: transparent;
-      transition: none;
+  .ant-tree .ant-tree-treenode {
+    padding: 0;
+    margin: 2px 0;
+    border-radius: 3px;
+    transition: all 0.3s, border 0s, line-height 0s, box-shadow 0s;
+    &:hover {
+      background-color: #f5f5f5;
     }
-  .ant-tree .ant-tree-node-content-wrapper{
+  }
+  .ant-tree-treenode-selected {
+    background-color: var(--NAVBAR_BTN_BG);
+  }
+  .ant-tree .ant-tree-node-content-wrapper.ant-tree-node-selected {
+    background-color: transparent;
+    transition: none;
+  }
+  .ant-tree .ant-tree-node-content-wrapper {
     border-radius: 0;
     &.ant-tree-node-selected {
-      opacity: .8;
-   }
+      opacity: 0.8;
+    }
   }
 
   .ant-tree-switcher {
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index e41615b41..b19e0b84a 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -17,7 +17,7 @@
             <span class="my-2 text-gray-400">{{ it.author }}</span>
             <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
           </div>
-          <div class="btn-group">
+          <div class="btn-group" *ngIf="type === 'installed'">
             <a nz-button nzType="link">Setting</a>
             <nz-divider nzType="vertical"></nz-divider>
             <a nz-button nzType="link">Details</a>
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
index 61ed4c3f5..4435efc18 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
@@ -24,8 +24,3 @@
     grid-template-columns: repeat(3, minmax(0, 1fr));
   }
 }
-.btn-group{
-  .ant-btn-link{
-    padding: 0;
-  }
-}
\ No newline at end of file

From 2b6113a34b2e9956d20dea9fed93ce8d07247060 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Tue, 6 Sep 2022 19:22:37 +0800
Subject: [PATCH 03/21] feat: update UI

---
 .../detail/components/extensions.component.ts | 119 ++++++++++++++++++
 .../detail/extension-detail.component.html    |  45 +++----
 .../detail/extension-detail.component.scss    |  19 ++-
 .../detail/extension-detail.module.ts         |  13 ++
 .../app/pages/extension/extension.module.ts   |   7 +-
 .../list/extension-list.component.html        |  59 +++++----
 .../list/extension-list.component.ts          |  26 ++--
 .../components/setting/setting.component.html |  31 ++---
 .../components/setting/setting.component.ts   |  16 ++-
 .../shadow/eo-shadow-dom.component.scss       |  40 ++++++
 .../shadow-dom-encapsulation.component.ts     |  10 +-
 .../browser/src/app/shared/shared.module.ts   |  22 +++-
 12 files changed, 297 insertions(+), 110 deletions(-)
 create mode 100644 src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
 create mode 100644 src/workbench/browser/src/app/pages/extension/detail/extension-detail.module.ts
 create mode 100644 src/workbench/browser/src/app/shared/components/shadow/eo-shadow-dom.component.scss

diff --git a/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
new file mode 100644
index 000000000..607d6d588
--- /dev/null
+++ b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
@@ -0,0 +1,119 @@
+import { Component, Input, OnInit } from '@angular/core';
+import { FormBuilder, FormGroup, Validators } from '@angular/forms';
+import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
+import { SettingService } from 'eo/workbench/browser/src/app/core/services/settings/settings.service';
+
+@Component({
+  selector: 'eo-extension-setting',
+  template: `
+    <div
+      class="bg-white sticky top-0 py-[6px] border-solid border-0 border-b-[1px] z-10 mb-[3px]"
+      style="border-color: var(--BORDER)"
+    >
+      <button nz-button nzType="primary">Save</button>
+    </div>
+
+    <form nz-form [nzLayout]="'vertical'" [formGroup]="validateForm" class="form">
+      <nz-form-item nz-col class="fg1" *ngFor="let field of objectKeys(properties)">
+        <ng-container *ngIf="properties[field]?.label">
+          <nz-form-label nzFor="{{ field }}" [nzRequired]="properties[field]?.required" class="label">
+            {{ properties[field]?.label }}
+          </nz-form-label>
+        </ng-container>
+        <!-- 二级说明 -->
+        <div
+          *ngIf="properties[field]?.type !== 'boolean' && properties[field]?.description"
+          class="text-[12px] mb-[8px] text-gray-400"
+        >
+          {{ properties[field]?.description }}
+        </div>
+        <nz-form-control i18n-nzErrorTip nzErrorTip="Please Enter {{ properties[field]?.label }}" class="form-control">
+          <!-- 字符串类型 -->
+          <!-- <ng-container *ngIf="properties[field]?.type === 'string'"> -->
+          <input
+            type="text"
+            nz-input
+            id="{{ field }}"
+            [disabled]="properties[field]?.disabled"
+            i18n-placeholder
+            placeholder="{{ properties[field]?.placeholder ?? 'Please Enter ' + properties[field]?.label }}"
+            formControlName="{{ field }}"
+            [(ngModel)]="localSettings[field]"
+          />
+          <!-- </ng-container> -->
+
+          <!-- 布尔类型 -->
+          <ng-container *ngIf="properties[field]?.type === 'boolean'">
+            <label
+              nz-checkbox
+              [(ngModel)]="localSettings[field]"
+              id="{{ field }}"
+              [nzDisabled]="properties[field]?.disabled"
+              formControlName="{{ field }}"
+              >{{ properties[field]?.description }}</label
+            >
+          </ng-container>
+
+          <!-- 数字类型 -->
+          <ng-container *ngIf="properties[field]?.type === 'number'">
+            <nz-input-number
+              [(ngModel)]="localSettings[field]"
+              id="{{ field }}"
+              [nzDisabled]="properties[field]?.disabled"
+              formControlName="{{ field }}"
+            >
+              {{ properties[field]?.description }}</nz-input-number
+            >
+          </ng-container>
+        </nz-form-control>
+      </nz-form-item>
+    </form>
+  `,
+})
+export class ExtensionSettingComponent implements OnInit {
+  @Input() configuration = {} as any;
+  localSettings = {} as Record<string, any>;
+  validateForm!: FormGroup;
+  objectKeys = Object.keys;
+  get properties() {
+    return this.configuration?.properties || {};
+  }
+
+  constructor(
+    public languageService: LanguageService,
+    private fb: FormBuilder,
+    private settingService: SettingService
+  ) {}
+
+  ngOnInit(): void {
+    this.init();
+  }
+
+  private init() {
+    this.localSettings = this.settingService.getSettings();
+    const controls = {};
+
+    this.setSettingsModel(this.configuration.properties, controls);
+
+    this.validateForm = this.fb.group(controls);
+  }
+
+  /**
+   * set data
+   *
+   * @param properties
+   */
+  private setSettingsModel(properties, controls) {
+    //  Flat configuration object
+    Object.keys(properties).forEach((fieldKey) => {
+      const props = properties[fieldKey];
+      this.localSettings[fieldKey] ??= props.default;
+      // Extensible to add more default checks
+      if (props.required) {
+        controls[fieldKey] = [null, [Validators.required]];
+      } else {
+        controls[fieldKey] = [null];
+      }
+    });
+  }
+}
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index de621a2d9..e302627df 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -1,41 +1,34 @@
-<div class="py-3 extension-detail" *ngIf="extensionDetail">
-  <div class="sticky px-2 top-0 z-50 bg-white">
-    <div class="pb-3 flex justify-between h-[50px]">
+<div class="h-full py-3 extension-detail" *ngIf="extensionDetail">
+  <div class="sticky top-0 z-50 px-2 bg-white">
+    <div class="pb-3 flex justify-between h-[36px]">
       <div class="flex items-center">
         <a nz-button nzType="link" (click)="backToList()">
           <eo-iconpark-icon name="left" class="anticon" size="16px"></eo-iconpark-icon><span i18n>Back</span>
         </a>
         <nz-divider nzType="vertical"></nz-divider>
         <div class="mr-2">
-          <nz-avatar nzSize="small" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
+          <nz-avatar nzSize="small" class="!w-[20px] !h-[20px]" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
         </div>
-        <span class="font-bold text-[16px]">{{ extensionDetail.moduleName }}</span>
-      </div>
-      <div class="flex items-center">
-        <nz-switch class="mr-2.5"></nz-switch>
-        <!-- <button nz-button nzType="primary">Install</button> -->
-        <button nz-button nzType="default">Uninstall</button>
+        <span class="font-bold text-[14px]">{{ extensionDetail.moduleName }}</span>
+        <nz-switch nzSize="small" class="ml-2.5"></nz-switch>
       </div>
     </div>
     <!-- <div class="bbd"></div> -->
   </div>
-  <section class="h-full px-4 max-w-[80vw] mx-auto">
-    <p class="w-full">{{ extensionDetail.description }}</p>
-    <nz-tabset>
+  <section class="h-full px-4 max-w-[80vw] mx-auto flex flex-col">
+    <p class="w-full text-[#888] text-[12px]">{{ extensionDetail.description }}</p>
+    <div>
+      <button nz-button nzSize="small" nzType="default" class="mt-[8px]">Uninstall</button>
+    </div>
+
+    <nz-tabset [nzAnimated]="false" class="settings">
       <!-- Setting -->
-      <nz-tab nzTitle="Settings">
-        <div class="ant-form-item">
-          <label for="eoapi-extensions.token"> Token</label>
-          <div _ngcontent-xnd-c228="" class="text-[12px] mb-[8px] text-gray-400">
-            Eolink OpenAPI token, which can only be accessed remotely after configuration
-          </div>
-          <input nz-input placeholder="Basic usage" />
-        </div>
-        <button nz-button nzType="primary">Save</button>
+      <nz-tab *ngIf="extensionDetail?.features?.configuration" nzTitle="Settings">
+        <eo-extension-setting [configuration]="extensionDetail?.features?.configuration"></eo-extension-setting>
       </nz-tab>
       <!-- Details -->
       <nz-tab nzTitle="Details">
-        <div class="h-full overflow-auto w-4/5 m-auto">
+        <div class="w-4/5 h-full m-auto overflow-auto">
           <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
             <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
             </eo-shadow-dom>
@@ -48,14 +41,14 @@
           <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Repository">dfsdfds </nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Homepage">dsfdsf </nz-descriptions-item>
-        </nz-descriptions></nz-tab
-      >
+        </nz-descriptions>
+      </nz-tab>
       <nz-tab nzTitle="ChangeLog">
         <h1>Change Log</h1>
         <p>All notable changes to this project will be documented in this file.</p>
         <h2>[12.0.1] - 2022-03-03</h2>
         <!-- !show be markdown preview -->
-        <!-- <div class="h-full overflow-auto w-4/5 m-auto">
+        <!-- <div class="w-4/5 h-full m-auto overflow-auto">
           <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
             <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
             </eo-shadow-dom>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
index f631ef9b7..30258a8c5 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
@@ -1,14 +1,27 @@
-.ant-divider-vertical{
-  margin:0 16px;
+:host {
+  overflow: initial;
+}
+.ant-divider-vertical {
+  margin: 0 16px;
 }
 ::ng-deep {
   eo-extension-detail {
+    .settings .ant-tabs-content-holder {
+      padding-top: 0;
+    }
     .ant-tabs-content-holder {
-      padding-top: 20px;
+      padding-top: 12px;
+      padding-bottom: 8px;
+      margin-bottom: 24px;
+      overflow: auto;
     }
     .ant-skeleton-element .ant-skeleton-button {
       width: 100%;
     }
+    .ant-tabs-tab {
+      color: #333;
+      padding: 10px 0;
+    }
   }
   .md-preview {
     img {
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.module.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.module.ts
new file mode 100644
index 000000000..7539eb64a
--- /dev/null
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.module.ts
@@ -0,0 +1,13 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { ExtensionSettingComponent } from './components/extensions.component';
+import { ExtensionDetailComponent } from './extension-detail.component';
+import { SharedModule } from 'eo/workbench/browser/src/app/shared/shared.module';
+
+@NgModule({
+  imports: [SharedModule, FormsModule, CommonModule],
+  declarations: [ExtensionSettingComponent, ExtensionDetailComponent],
+})
+export class ExtensionDetailModule {}
diff --git a/src/workbench/browser/src/app/pages/extension/extension.module.ts b/src/workbench/browser/src/app/pages/extension/extension.module.ts
index d7563111a..36df86bc9 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.module.ts
+++ b/src/workbench/browser/src/app/pages/extension/extension.module.ts
@@ -5,7 +5,7 @@ import { FormsModule } from '@angular/forms';
 import { ExtensionRoutingModule } from './extension-routing.module';
 import { ExtensionComponent } from './extension.component';
 import { ExtensionListComponent } from './list/extension-list.component';
-import { ExtensionDetailComponent } from './detail/extension-detail.component';
+import { ExtensionDetailModule } from './detail/extension-detail.module';
 
 import { NzButtonModule } from 'ng-zorro-antd/button';
 import { NzInputModule } from 'ng-zorro-antd/input';
@@ -39,8 +39,9 @@ import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
     NzDropDownModule,
     NzSkeletonModule,
     NzCardModule,
-    NzAvatarModule
+    NzAvatarModule,
+    ExtensionDetailModule,
   ],
-  declarations: [ExtensionComponent, ExtensionListComponent, ExtensionDetailComponent],
+  declarations: [ExtensionComponent, ExtensionListComponent],
 })
 export class ExtensionModule {}
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index b19e0b84a..e4954b087 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -2,39 +2,38 @@
   <div class="grid grid-cols-2 gap-6 py-5 list-block">
     <div
       class="bd_all w-full min-h-[140px] p-5 flex flex-col flex-wrap plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
-      *ngFor="let it of renderList"
-      (click)="clickExtension(it)"
-    >
-      <div class="flex w-full">
-        <div
-          class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
-          [ngClass]="{ 'bg-gray-100': it.logo }"
-          [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }"
-        ></div>
-        <div class="flex flex-col">
-          <div class="flex flex-col flex-auto">
-            <span class="text-lg font-bold">{{ it.moduleName }}</span>
-            <span class="my-2 text-gray-400">{{ it.author }}</span>
-            <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
+      *ngFor="let it of renderList" (click)="clickExtension(it)">
+      <nz-skeleton [nzLoading]="loading" [nzActive]="true" [nzAvatar]="true">
+        <div class="flex justify-between w-full">
+          <div class="flex">
+            <div class="shrink-0 block w-[40px] h-[40px] rounded-lg bg-[length:90%] bg-center bg-no-repeat mr-[20px]"
+              [ngClass]="{ 'bg-gray-100': it.logo }" [ngStyle]="{ 'background-image': 'url(' + (it.logo || '') + ')' }">
+            </div>
+            <div class="flex flex-col">
+              <div class="flex flex-col flex-auto">
+                <span class="text-lg font-bold">{{ it.moduleName }}</span>
+                <span class="my-2 text-gray-400">{{ it.author }}</span>
+                <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
+              </div>
+              <div class="btn-group" *ngIf="type === 'installed'">
+                <ng-container *ngIf="it.features?.configuration">
+                  <a nz-button nzType="link">Setting</a>
+                  <nz-divider nzType="vertical"></nz-divider>
+                </ng-container>
+                <a nz-button nzType="link">Details</a>
+              </div>
+            </div>
           </div>
-          <div class="btn-group" *ngIf="type === 'installed'">
-            <a nz-button nzType="link">Setting</a>
-            <nz-divider nzType="vertical"></nz-divider>
-            <a nz-button nzType="link">Details</a>
+          <div *ngIf="type === 'installed'">
+            <nz-switch click-stop-propagation [(ngModel)]="it.isOpen"></nz-switch>
+          </div>
+          <div *ngIf="type !== 'installed'">
+            <span *ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
+              class="p-1 text-xs text-green-700 border-green-700 rounded-sm bd_all whitespace-nowrap"
+              i18n>Installed</span>
           </div>
         </div>
-        <div *ngIf="type === 'installed'">
-          <nz-switch click-stop-propagation [(ngModel)]="it.isOpen"></nz-switch>
-        </div>
-        <div *ngIf="type !== 'installed'">
-          <span
-            *ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
-            class="p-1 text-xs text-green-700 border-green-700 rounded-sm bd_all whitespace-nowrap"
-            i18n
-            >Installed</span
-          >
-        </div>
-      </div>
+      </nz-skeleton>
     </div>
   </div>
 </div>
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
index efcff1c36..12cbf621e 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
@@ -25,6 +25,7 @@ export class ExtensionListComponent implements OnInit {
   type: ExtensionGroupType = ExtensionGroupType.all;
   keyword = '';
   renderList = [];
+  loading = false;
   seachChanged$: Subject<string> = new Subject<string>();
   private destroy$: Subject<void> = new Subject<void>();
 
@@ -45,15 +46,24 @@ export class ExtensionListComponent implements OnInit {
     this.watchSearchKeywordChange();
   }
   async searchPlugin(keyword = '') {
-    if (this.type === 'installed') {
-      const installedList = new ExtensionList(this.extensionService.getInstalledList());
-      return installedList.search(keyword);
+    const timer = setTimeout(() => (this.loading = true), 80);
+    const timeStart = Date.now();
+    try {
+      if (this.type === 'installed') {
+        const installedList = new ExtensionList(this.extensionService.getInstalledList());
+        return installedList.search(keyword);
+      }
+      const res: any = await this.extensionService.requestList();
+      if (this.type === 'official') {
+        return new ExtensionList(res.data.filter((it) => it.author === 'Eoapi')).search(keyword);
+      }
+      return new ExtensionList(res.data).search(keyword);
+    } catch (error) {
+    } finally {
+      clearTimeout(timer);
+      const timeout = Date.now() - timeStart > 300 ? 0 : 300;
+      setTimeout(() => (this.loading = false), timeout);
     }
-    const res: any = await this.extensionService.requestList();
-    if (this.type === 'official') {
-      return new ExtensionList(res.data.filter((it) => it.author === 'Eoapi')).search(keyword);
-    }
-    return new ExtensionList(res.data).search(keyword);
   }
   onSeachChange(keyword) {
     this.seachChanged$.next(keyword);
diff --git a/src/workbench/browser/src/app/shared/components/setting/setting.component.html b/src/workbench/browser/src/app/shared/components/setting/setting.component.html
index 89f8999ae..32fedcd32 100644
--- a/src/workbench/browser/src/app/shared/components/setting/setting.component.html
+++ b/src/workbench/browser/src/app/shared/components/setting/setting.component.html
@@ -1,21 +1,13 @@
-<nz-modal
-  [(nzVisible)]="isShowModal"
-  nzWidth="70%"
-  [nzFooter]="null"
-  [nzBodyStyle]="{ paddingLeft: '6px' }"
-  (nzOnCancel)="handleCancel()"
->
+<nz-modal [(nzVisible)]="isShowModal" nzWidth="70%" [nzFooter]="null" [nzBodyStyle]="{ paddingLeft: '6px' }"
+  (nzOnCancel)="handleCancel()">
   <section *nzModalContent class="container">
     <div class="tree-view">
       <img src="assets/images/logo-text.svg" class="h-[40px]" />
       <nz-tree-view [nzTreeControl]="treeControl" [nzDataSource]="dataSource" [nzBlockNode]="true">
         <nz-tree-node *nzTreeNodeDef="let node" nzTreeNodePadding>
           <nz-tree-node-toggle nzTreeNodeNoopToggle></nz-tree-node-toggle>
-          <nz-tree-node-option
-            [nzDisabled]="node.disabled"
-            [nzSelected]="selectListSelection.isSelected(node)"
-            (nzClick)="selectModule(node)"
-          >
+          <nz-tree-node-option [nzDisabled]="node.disabled" [nzSelected]="selectListSelection.isSelected(node)"
+            (nzClick)="selectModule(node)">
             {{ node.name }}
           </nz-tree-node-option>
         </nz-tree-node>
@@ -24,11 +16,8 @@
           <nz-tree-node-toggle>
             <i nz-icon nzType="caret-down" nzTreeNodeToggleRotateIcon></i>
           </nz-tree-node-toggle>
-          <nz-tree-node-option
-            [nzDisabled]="node.disabled"
-            [nzSelected]="selectListSelection.isSelected(node)"
-            (nzClick)="selectModule(node)"
-          >
+          <nz-tree-node-option [nzDisabled]="node.disabled" [nzSelected]="selectListSelection.isSelected(node)"
+            (nzClick)="selectModule(node)">
             {{ node.name }}
           </nz-tree-node-option>
         </nz-tree-node>
@@ -42,16 +31,16 @@
       <!-- Language -->
       <eo-language-switcher [(model)]="settings" [id]="treeNodes[1].moduleID"> </eo-language-switcher>
       <nz-divider></nz-divider>
-      <eo-extension-setting
+      <!-- <eo-extension-setting
         [(model)]="settings"
         [(isShowModal)]="isShowModal"
         [localSettings]="localSettings"
         [id]="treeNodes[2].moduleID"
         [extensitonConfigurations]="extensitonConfigurations"
-      ></eo-extension-setting>
-      <nz-divider></nz-divider>
+      ></eo-extension-setting> -->
+      <!-- <nz-divider></nz-divider> -->
       <!-- About -->
-      <eo-about [id]="treeNodes[3].moduleID"></eo-about>
+      <eo-about [id]="treeNodes[2].moduleID"></eo-about>
     </div>
   </section>
   <!-- <div *nzModalFooter class="footer"> -->
diff --git a/src/workbench/browser/src/app/shared/components/setting/setting.component.ts b/src/workbench/browser/src/app/shared/components/setting/setting.component.ts
index f0ff9ba8d..4721d5051 100644
--- a/src/workbench/browser/src/app/shared/components/setting/setting.component.ts
+++ b/src/workbench/browser/src/app/shared/components/setting/setting.component.ts
@@ -5,7 +5,6 @@ import { FlatTreeControl } from '@angular/cdk/tree';
 import { NzTreeFlatDataSource, NzTreeFlattener } from 'ng-zorro-antd/tree-view';
 import { Message, MessageService } from '../../../shared/services/message';
 import { Subject, takeUntil } from 'rxjs';
-import { NzMessageService } from 'ng-zorro-antd/message';
 import { RemoteService } from 'eo/workbench/browser/src/app/shared/services/remote/remote.service';
 import { SettingService } from 'eo/workbench/browser/src/app/core/services/settings/settings.service';
 import { debounce } from 'eo/workbench/browser/src/app/utils';
@@ -105,10 +104,10 @@ export class SettingComponent implements OnInit {
       name: $localize`:@@Language:Language`,
       moduleID: 'eoapi-language',
     },
-    {
-      name: $localize`Extensions`,
-      moduleID: 'eoapi-extensions',
-    },
+    // {
+    //   name: $localize`Extensions`,
+    //   moduleID: 'eoapi-extensions',
+    // },
     {
       name: $localize`About`,
       moduleID: 'eoapi-about',
@@ -130,7 +129,6 @@ export class SettingComponent implements OnInit {
   private destroy$: Subject<void> = new Subject<void>();
   constructor(
     private messageService: MessageService,
-    private message: NzMessageService,
     private remoteService: RemoteService,
     private settingService: SettingService
   ) {}
@@ -231,9 +229,9 @@ export class SettingComponent implements OnInit {
       }, []);
     // All settings
     const treeData = JSON.parse(JSON.stringify(this.treeNodes));
-    const extensions = treeData.find((n) => n.moduleID === 'eoapi-extensions');
-    extensions.children = generateTreeData(this.extensitonConfigurations);
-    extensions.configuration = this.extensitonConfigurations;
+    // const extensions = treeData.find((n) => n.moduleID === 'eoapi-extensions');
+    // extensions.children = generateTreeData(this.extensitonConfigurations);
+    // extensions.configuration = this.extensitonConfigurations;
     this.dataSource.setData(treeData);
     this.treeControl.expandAll();
 
diff --git a/src/workbench/browser/src/app/shared/components/shadow/eo-shadow-dom.component.scss b/src/workbench/browser/src/app/shared/components/shadow/eo-shadow-dom.component.scss
new file mode 100644
index 000000000..fd4eec811
--- /dev/null
+++ b/src/workbench/browser/src/app/shared/components/shadow/eo-shadow-dom.component.scss
@@ -0,0 +1,40 @@
+:host {
+  .markdown-body {
+    font-size: 14px;
+    > :first-child {
+      margin-top: 12px !important;
+    }
+    img {
+      max-width: 600px;
+    }
+    h1 {
+      font-size: 16px;
+    }
+    h2 {
+      font-size: 16px;
+    }
+    h3 {
+      font-size: 16px;
+    }
+    h4,
+    h5,
+    h6,
+    h7 {
+      font-size: 14px;
+    }
+    h1,
+    h2,
+    h3,
+    h4,
+    h5,
+    h6,
+    h7 {
+      color: black;
+      font-weight: 600;
+    }
+  }
+  img {
+    margin: 0 auto;
+    display: block;
+  }
+}
diff --git a/src/workbench/browser/src/app/shared/components/shadow/shadow-dom-encapsulation.component.ts b/src/workbench/browser/src/app/shared/components/shadow/shadow-dom-encapsulation.component.ts
index b615de310..94e145c50 100644
--- a/src/workbench/browser/src/app/shared/components/shadow/shadow-dom-encapsulation.component.ts
+++ b/src/workbench/browser/src/app/shared/components/shadow/shadow-dom-encapsulation.component.ts
@@ -8,15 +8,9 @@ import MarkdownIt from 'markdown-it/dist/markdown-it';
       rel="stylesheet"
       href="https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/github-markdown-css/5.1.0/github-markdown.min.css"
     />
-    <style>
-      article.markdown-body img {
-        max-width: 600px;
-      }
-    </style>
-    <article class="markdown-body">
-      <div part="eo-shadow-dom" [innerHTML]="content"></div>
-    </article>
+    <article class="markdown-body" [innerHTML]="content"></article>
   `,
+  styleUrls: ['./eo-shadow-dom.component.scss'],
   encapsulation: ViewEncapsulation.ShadowDom,
 })
 export class ShadowDomEncapsulationComponent implements OnInit {
diff --git a/src/workbench/browser/src/app/shared/shared.module.ts b/src/workbench/browser/src/app/shared/shared.module.ts
index f73f14289..7820f79b7 100644
--- a/src/workbench/browser/src/app/shared/shared.module.ts
+++ b/src/workbench/browser/src/app/shared/shared.module.ts
@@ -5,7 +5,15 @@ import { ClickStopPropagationDirective } from './directives';
 import { FormsModule, ReactiveFormsModule } from '@angular/forms';
 
 import { NzDrawerModule } from 'ng-zorro-antd/drawer';
+import { NzFormModule } from 'ng-zorro-antd/form';
+import { NzInputModule } from 'ng-zorro-antd/input';
+import { NzInputNumberModule } from 'ng-zorro-antd/input-number';
 import { NzRadioModule } from 'ng-zorro-antd/radio';
+import { NzCheckboxModule } from 'ng-zorro-antd/checkbox';
+import { NzAvatarModule } from 'ng-zorro-antd/avatar';
+import { NzSwitchModule } from 'ng-zorro-antd/switch';
+import { NzTabsModule } from 'ng-zorro-antd/tabs';
+import { NzSkeletonModule } from 'ng-zorro-antd/skeleton';
 import { NzButtonModule } from 'ng-zorro-antd/button';
 import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
 import { NzResultModule } from 'ng-zorro-antd/result';
@@ -16,6 +24,7 @@ import { NzCardModule } from 'ng-zorro-antd/card';
 import { NzPopoverModule } from 'ng-zorro-antd/popover';
 import { NzCodeEditorModule } from 'ng-zorro-antd/code-editor';
 import { NzResizableModule } from 'ng-zorro-antd/resizable';
+import { NzDividerModule } from 'ng-zorro-antd/divider';
 
 import { NzNotificationModule } from 'ng-zorro-antd/notification';
 import { NzMessageModule } from 'ng-zorro-antd/message';
@@ -53,6 +62,7 @@ const SHARED_MODULE = [
   FormsModule,
   RouterModule,
   ReactiveFormsModule,
+  NzFormModule,
   NzDrawerModule,
   NzRadioModule,
   NzButtonModule,
@@ -65,18 +75,26 @@ const SHARED_MODULE = [
   NzEmptyModule,
   NzMessageModule,
   NzDescriptionsModule,
+  NzInputModule,
+  NzCheckboxModule,
   EouiModule,
   NzTreeModule,
+  NzAvatarModule,
+  NzTabsModule,
+  NzSkeletonModule,
   NzPopoverModule,
   NzCodeEditorModule,
   NzResizableModule,
   EoIconparkIconModule,
+  NzInputNumberModule,
+  NzSwitchModule,
+  NzDividerModule,
 ] as const;
 
 @NgModule({
   imports: [...SHARED_MODULE],
-  declarations: [ ...COMPONENTS,ClickStopPropagationDirective, ApiParamsNumPipe, PageBlankComponent, EnvListComponent],
+  declarations: [...COMPONENTS, ClickStopPropagationDirective, ApiParamsNumPipe, PageBlankComponent, EnvListComponent],
   providers: [ModalService],
-  exports: [...SHARED_MODULE, ...COMPONENTS,  ClickStopPropagationDirective,ApiParamsNumPipe],
+  exports: [...SHARED_MODULE, ...COMPONENTS, ClickStopPropagationDirective, ApiParamsNumPipe],
 })
 export class SharedModule {}

From 92f1d4d8b38f07fffb1cb08de065dd28289620e5 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 15:04:15 +0800
Subject: [PATCH 04/21] test: publish

---
 .github/workflows/release.yml                 |  4 +-
 build/afterBuild.js                           | 11 -----
 build/notarize.js                             | 13 ------
 electron-builder.json                         | 17 +++++--
 package.json                                  |  2 +-
 {build => scripts}/entitlements.mac.plist     |  0
 scripts/urlProtoco.nsh                        |  9 ++++
 .../detail/extension-detail.component.html    | 27 +++++++-----
 .../detail/extension-detail.component.ts      | 44 +++++++++++++++++++
 .../app/pages/extension/extension.model.ts    | 10 +++--
 10 files changed, 92 insertions(+), 45 deletions(-)
 delete mode 100644 build/afterBuild.js
 delete mode 100644 build/notarize.js
 rename {build => scripts}/entitlements.mac.plist (100%)
 create mode 100644 scripts/urlProtoco.nsh

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 5006926b7..7c37515e4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,6 +4,8 @@ on:
   push:
     tags:
       - 'v*.*.*'
+   branches:
+      - feat/extension-manage
 
 jobs:
   release:
@@ -77,7 +79,7 @@ jobs:
           # apply provisioning profile
           # mkdir -p ~/Library/MobileDevice/Provisioning\ Profiles
           # cp $PP_PATH ~/Library/MobileDevice/Provisioning\ Profiles
-          echo "${{ secrets.NOTARIZE_JS }}" > build/notarize.js
+          echo "${{ secrets.NOTARIZE_JS }}" > scripts/notarize.js
           yarn release:m1
           yarn release
 
diff --git a/build/afterBuild.js b/build/afterBuild.js
deleted file mode 100644
index 287538efc..000000000
--- a/build/afterBuild.js
+++ /dev/null
@@ -1,11 +0,0 @@
-const fs = require('fs');
-const glob = require('glob');
-const { resolve } = require('path');
-
-glob.sync(resolve('./release', './*.exe')).forEach((item, i) => {
-  const newName = item.replace(/ /g, '-');
-  fs.rename(item, newName, (err) => {
-    if (err) throw err;
-    console.log('Rename complete!');
-  });
-});
diff --git a/build/notarize.js b/build/notarize.js
deleted file mode 100644
index c98fa1214..000000000
--- a/build/notarize.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- * Use electron-notarize to notarize app
- *
- * @description The app should be notarized after it has been signed and before it’s packaged into a dmg.
- * Electron-builder has a hook for this called afterSign. You can link that to a javascript file that will be called (and waited for) after sign.
- * You add it to your top level “build” configuration
- *
- * @link https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/
- */
-
-exports.default = function notarizing(context) {
-  return context;
-};
diff --git a/electron-builder.json b/electron-builder.json
index 763cf2013..c0e0c645c 100644
--- a/electron-builder.json
+++ b/electron-builder.json
@@ -28,8 +28,17 @@
   "nsis": {
     "oneClick": false,
     "allowElevation": true,
-    "allowToChangeInstallationDirectory": true
+    "allowToChangeInstallationDirectory": true,
+    // for win - 将协议写入主机的脚本
+    "include": "scripts/urlProtoco.nsh"
   },
+  "protocols": [
+    // for macOS - 用于在主机注册指定协议
+    {
+      "name": "eoapi",
+      "schemes": ["eoapi"]
+    }
+  ],
   "win": {
     "icon": "src/app/common/images/256x256.png",
     "target": [
@@ -46,14 +55,14 @@
     "icon": "src/app/common/images/512x512.png",
     "hardenedRuntime": true,
     "gatekeeperAssess": false,
-    "entitlements": "build/entitlements.mac.plist",
-    "entitlementsInherit": "build/entitlements.mac.plist",
+    "entitlements": "scripts/entitlements.mac.plist",
+    "entitlementsInherit": "scripts/entitlements.mac.plist",
     "target": ["dmg", "zip"]
   },
   "dmg": {
     "sign": false
   },
-  "afterSign": "build/notarize.js",
+  "afterSign": "scripts/notarize.js",
   "linux": {
     "icon": "src/app/common/images/",
     "target": ["AppImage"]
diff --git a/package.json b/package.json
index 650bb4b7c..066ca8461 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "eoapi",
   "souceLocale": "zh-Hans",
-  "version": "1.6.0",
+  "version": "1.6.1",
   "main": "out/app/electron-main/main.js",
   "description": "A lightweight, extensible API tool",
   "homepage": "https://github.com/eolinker/eoapi.git",
diff --git a/build/entitlements.mac.plist b/scripts/entitlements.mac.plist
similarity index 100%
rename from build/entitlements.mac.plist
rename to scripts/entitlements.mac.plist
diff --git a/scripts/urlProtoco.nsh b/scripts/urlProtoco.nsh
new file mode 100644
index 000000000..c94b4b350
--- /dev/null
+++ b/scripts/urlProtoco.nsh
@@ -0,0 +1,9 @@
+!macro customInstall
+  DetailPrint "Register eoapi URI Handler"
+  DeleteRegKey HKCR "eoapi"
+  WriteRegStr HKCR "eoapi" "" "URL:eoapi"
+  WriteRegStr HKCR "eoapi" "URL Protocol" ""
+  WriteRegStr HKCR "eoapi\shell" "" ""
+  WriteRegStr HKCR "eoapi\shell\Open" "" ""
+  WriteRegStr HKCR "eoapi\shell\Open\command" "" "$INSTDIR\${APP_EXECUTABLE_FILENAME} %1"
+!macroend
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index e302627df..ed699600b 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -21,7 +21,7 @@
       <button nz-button nzSize="small" nzType="default" class="mt-[8px]">Uninstall</button>
     </div>
 
-    <nz-tabset [nzAnimated]="false" class="settings">
+    <nz-tabset [nzAnimated]="false" class="settings" (nzSelectChange)="handleTabChange($event)">
       <!-- Setting -->
       <nz-tab *ngIf="extensionDetail?.features?.configuration" nzTitle="Settings">
         <eo-extension-setting [configuration]="extensionDetail?.features?.configuration"></eo-extension-setting>
@@ -39,21 +39,26 @@
         <nz-descriptions [nzColumn]="1" nzTitle="">
           <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail.author }}</nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
-          <nz-descriptions-item i18n-nzTitle nzTitle="Repository">dfsdfds </nz-descriptions-item>
-          <nz-descriptions-item i18n-nzTitle nzTitle="Homepage">dsfdsf </nz-descriptions-item>
+          <nz-descriptions-item *ngIf="extensionDetail.repository?.url" i18n-nzTitle nzTitle="Repository">
+            <a [href]="extensionDetail.repository.url" target="_blank">{{ extensionDetail.repository.url }}</a>
+          </nz-descriptions-item>
+          <nz-descriptions-item *ngIf="extensionDetail.homepage" i18n-nzTitle nzTitle="Homepage">
+            <a [href]="extensionDetail.homepage" target="_blank">{{ extensionDetail.homepage }}</a>
+          </nz-descriptions-item>
+          <nz-descriptions-item *ngIf="extensionDetail.bugs?.url" i18n-nzTitle nzTitle="BugReport">
+            <a [href]="extensionDetail.bugs.url" target="_blank">{{extensionDetail.bugs.url }}</a>
+          </nz-descriptions-item>
         </nz-descriptions>
       </nz-tab>
       <nz-tab nzTitle="ChangeLog">
-        <h1>Change Log</h1>
+        <!-- <h1>Change Log</h1>
         <p>All notable changes to this project will be documented in this file.</p>
-        <h2>[12.0.1] - 2022-03-03</h2>
+        <h2>[12.0.1] - 2022-03-03</h2> -->
         <!-- !show be markdown preview -->
-        <!-- <div class="w-4/5 h-full m-auto overflow-auto">
-          <nz-skeleton [nzLoading]="introLoading" [nzActive]="true">
-            <eo-shadow-dom class="md-preview" [text]="extensionDetail.introduction" [options]="{ html: true }">
-            </eo-shadow-dom>
-          </nz-skeleton>
-        </div> -->
+        <nz-skeleton [nzLoading]="changelogLoading" [nzActive]="true">
+          <eo-shadow-dom class="md-preview" [text]="changeLog">
+          </eo-shadow-dom>
+        </nz-skeleton>
       </nz-tab>
     </nz-tabset>
   </section>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index 2c5bbdebc..1b7ee7d9a 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -14,9 +14,11 @@ import { LanguageService } from 'eo/workbench/browser/src/app/core/services/lang
 export class ExtensionDetailComponent implements OnInit {
   isOperating = false;
   introLoading = false;
+  changelogLoading = false;
   isNotLoaded = true;
   extensionDetail: EoExtensionInfo;
   resourceInfo = ResourceInfo;
+  changeLog = '';
   get isElectron() {
     return this.electronService.isElectron;
   }
@@ -42,6 +44,42 @@ export class ExtensionDetailComponent implements OnInit {
     this.extensionDetail.introduction ||= $localize`This plugin has no documentation yet.`;
   }
 
+  async fetchChangelog(locale = '') {
+    //Default locale en-US
+    if (locale === 'en-US') locale = '';
+    const timer = setTimeout(() => (this.changelogLoading = true), 200);
+    try {
+      const response = await fetch(
+        `https://unpkg.com/${this.extensionDetail.name}@${this.extensionDetail.version}/changeLog.${
+          locale ? locale + '.' : ''
+        }md`
+      );
+      if (response.status === 200) {
+        this.changeLog = await response.text();
+      } else if (!locale && response.status === 404) {
+        const result = await fetch(`https://registry.npmjs.org/${this.extensionDetail.name}`, {
+          headers: {
+            // if fullmeta
+            // accept: ' application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
+          },
+        });
+        const data = await result.json();
+        this.changeLog = Object.entries<any>(data.versions).reduceRight((log, [key, value]) => {
+          return `
+${log}
+## [${key}](${value.dist.tarball}) - ${new Date(data.time[key]).toLocaleString()}
+          `;
+        }, '# Change Log');
+      } else if (locale) {
+        //If locale README not find,fetch default locale(en-US)
+        this.fetchChangelog();
+      }
+    } catch (error) {
+    } finally {
+      clearTimeout(timer);
+      this.changelogLoading = false;
+    }
+  }
   async fetchReadme(locale = '') {
     //Default locale en-US
     if (locale === 'en-US') locale = '';
@@ -64,6 +102,12 @@ export class ExtensionDetailComponent implements OnInit {
     }
   }
 
+  handleTabChange = (e) => {
+    if (e.tab?.nzTitle === 'ChangeLog') {
+      this.fetchChangelog();
+    }
+  };
+
   private findLinkInSingleAssets(assets, item) {
     let result = '';
     const assetIndex = assets.findIndex(
diff --git a/src/workbench/browser/src/app/pages/extension/extension.model.ts b/src/workbench/browser/src/app/pages/extension/extension.model.ts
index c3a7b698a..e9a8cfb8f 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.model.ts
+++ b/src/workbench/browser/src/app/pages/extension/extension.model.ts
@@ -6,9 +6,11 @@ export enum ExtensionGroupType {
   installed = 'installed',
 }
 
-export interface EoExtensionInfo extends ModuleInfo{
-  installed?:boolean;
-  bugs:{
-    url:string
+export interface EoExtensionInfo extends ModuleInfo {
+  installed?: boolean;
+  changeLog?: string;
+  bugs: {
+    url: string;
   };
+  [key: string]: any;
 }

From 82bd7a40a7f622e6ed3eabc02cde1eae7c71ff66 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 15:10:01 +0800
Subject: [PATCH 05/21] test: build

---
 .github/workflows/release.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 7c37515e4..ec6bdf993 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,7 +4,7 @@ on:
   push:
     tags:
       - 'v*.*.*'
-   branches:
+    branches:
       - feat/extension-manage
 
 jobs:

From f5243d67760f498996e981808204ee20c246f4a1 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 15:20:10 +0800
Subject: [PATCH 06/21] chore: update .gitignore

---
 src/workbench/browser/.gitignore                               | 2 +-
 .../app/pages/extension/detail/extension-detail.component.html | 3 ++-
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/workbench/browser/.gitignore b/src/workbench/browser/.gitignore
index f96895499..8db20701a 100644
--- a/src/workbench/browser/.gitignore
+++ b/src/workbench/browser/.gitignore
@@ -7,7 +7,7 @@ dist/
 /app-builds
 /release
 src/**/*.js
-!build/*.js
+!scripts/*.js
 !src/karma.conf.js
 !src/ng1/**/*.js
 *.js.map
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index ed699600b..0336bb9c3 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -40,7 +40,8 @@
           <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail.author }}</nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
           <nz-descriptions-item *ngIf="extensionDetail.repository?.url" i18n-nzTitle nzTitle="Repository">
-            <a [href]="extensionDetail.repository.url" target="_blank">{{ extensionDetail.repository.url }}</a>
+            <a [href]="extensionDetail.repository.url.replace('git+', '')" target="_blank">{{
+              extensionDetail.repository.url }}</a>
           </nz-descriptions-item>
           <nz-descriptions-item *ngIf="extensionDetail.homepage" i18n-nzTitle nzTitle="Homepage">
             <a [href]="extensionDetail.homepage" target="_blank">{{ extensionDetail.homepage }}</a>

From f851aab821c885c7cee64a6b32a670072d0fa2fe Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 15:53:49 +0800
Subject: [PATCH 07/21] testt: publish

---
 .gitignore            |  3 +--
 electron-builder.json |  2 +-
 package.json          |  2 +-
 scripts/afterBuild.js | 11 +++++++++++
 scripts/notarize.js   | 13 +++++++++++++
 5 files changed, 27 insertions(+), 4 deletions(-)
 create mode 100644 scripts/afterBuild.js
 create mode 100644 scripts/notarize.js

diff --git a/.gitignore b/.gitignore
index ea7afdabf..9e6d795a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,11 +14,10 @@ out/
 !src/workbench/node/request/**/*.js
 !src/workbench/node/server/**/*.js
 !/api/*.js
-!/build/*.js
+!/scripts/*.js
 !*.config.js
 !upload.js
 
-scripts/notarize.js
 # dependencies
 node_modules
 # package-lock.json
diff --git a/electron-builder.json b/electron-builder.json
index c0e0c645c..43f16fe38 100644
--- a/electron-builder.json
+++ b/electron-builder.json
@@ -1,5 +1,5 @@
 {
-  "appId": ".eolinker.com",
+  "appId": ".eoapi.io",
   "asar": true,
   "directories": {
     "output": "release/"
diff --git a/package.json b/package.json
index 066ca8461..6983af538 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
     "electron:serve": "wait-on tcp:4200 && npm run electron:dev",
     "electron:static": "npm run electron:tsc && electron .",
     "electron:dev": "npm run electron:tsc && electron . --development",
-    "build": "npx patch-package && npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
+    "build": "npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
     "build:static": "npm run clear:electron:tsc&&npm run electron:tsc && electron-builder build",
     "release": "npm-run-all -s build:workbench electron:tsc && electron-builder --publish=always",
     "release:m1": "npm-run-all -s build:workbench electron:tsc && electron-builder -m=dmg --arm64 -p onTagOrDraft",
diff --git a/scripts/afterBuild.js b/scripts/afterBuild.js
new file mode 100644
index 000000000..287538efc
--- /dev/null
+++ b/scripts/afterBuild.js
@@ -0,0 +1,11 @@
+const fs = require('fs');
+const glob = require('glob');
+const { resolve } = require('path');
+
+glob.sync(resolve('./release', './*.exe')).forEach((item, i) => {
+  const newName = item.replace(/ /g, '-');
+  fs.rename(item, newName, (err) => {
+    if (err) throw err;
+    console.log('Rename complete!');
+  });
+});
diff --git a/scripts/notarize.js b/scripts/notarize.js
new file mode 100644
index 000000000..c98fa1214
--- /dev/null
+++ b/scripts/notarize.js
@@ -0,0 +1,13 @@
+/**
+ * Use electron-notarize to notarize app
+ *
+ * @description The app should be notarized after it has been signed and before it’s packaged into a dmg.
+ * Electron-builder has a hook for this called afterSign. You can link that to a javascript file that will be called (and waited for) after sign.
+ * You add it to your top level “build” configuration
+ *
+ * @link https://kilianvalkhof.com/2019/electron/notarizing-your-electron-application/
+ */
+
+exports.default = function notarizing(context) {
+  return context;
+};

From 10b1c1596945b3a100c55e35cea23f22adc55f21 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 15:55:45 +0800
Subject: [PATCH 08/21] test: relese

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 6983af538..066ca8461 100644
--- a/package.json
+++ b/package.json
@@ -22,7 +22,7 @@
     "electron:serve": "wait-on tcp:4200 && npm run electron:dev",
     "electron:static": "npm run electron:tsc && electron .",
     "electron:dev": "npm run electron:tsc && electron . --development",
-    "build": "npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
+    "build": "npx patch-package && npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
     "build:static": "npm run clear:electron:tsc&&npm run electron:tsc && electron-builder build",
     "release": "npm-run-all -s build:workbench electron:tsc && electron-builder --publish=always",
     "release:m1": "npm-run-all -s build:workbench electron:tsc && electron-builder -m=dmg --arm64 -p onTagOrDraft",

From 85a6695aa0f332a7de65d3d86c9dcfc2615e372d Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 18:23:13 +0800
Subject: [PATCH 09/21] feat: check eoapi client is install

---
 .gitignore                                    |   1 +
 package.json                                  |   1 +
 src/app/electron-main/main.ts                 |   3 +
 .../detail/extension-detail.component.html    |  23 +++-
 .../detail/extension-detail.component.ts      |  29 ++++-
 .../browser/src/app/shared/shared.module.ts   |   2 +
 src/workbench/browser/src/index.html          | 107 +++++++++---------
 7 files changed, 112 insertions(+), 54 deletions(-)

diff --git a/.gitignore b/.gitignore
index 9e6d795a4..d0295ae5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -13,6 +13,7 @@ out/
 !src/workbench/node/electron/**/*.js
 !src/workbench/node/request/**/*.js
 !src/workbench/node/server/**/*.js
+!src/workbench/browser/src/libs/
 !/api/*.js
 !/scripts/*.js
 !*.config.js
diff --git a/package.json b/package.json
index 066ca8461..6dcd80ce9 100644
--- a/package.json
+++ b/package.json
@@ -23,6 +23,7 @@
     "electron:static": "npm run electron:tsc && electron .",
     "electron:dev": "npm run electron:tsc && electron . --development",
     "build": "npx patch-package && npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
+    "build:local": "npm-run-all -s build:workbench clear:electron:tsc electron:tsc && electron-builder build",
     "build:static": "npm run clear:electron:tsc&&npm run electron:tsc && electron-builder build",
     "release": "npm-run-all -s build:workbench electron:tsc && electron-builder --publish=always",
     "release:m1": "npm-run-all -s build:workbench electron:tsc && electron-builder -m=dmg --arm64 -p onTagOrDraft",
diff --git a/src/app/electron-main/main.ts b/src/app/electron-main/main.ts
index d9ef0e259..d1d8d0ec4 100644
--- a/src/app/electron-main/main.ts
+++ b/src/app/electron-main/main.ts
@@ -19,6 +19,8 @@ export const subView = {
   appView: null,
   mainView: null,
 };
+const PROTOCOL = 'eoapi';
+app.setAsDefaultProtocolClient(PROTOCOL); // 注册协议
 const eoUpdater = new EoUpdater();
 const mockServer = new MockServer();
 const moduleManager: ModuleManagerInterface = new ModuleManager();
@@ -92,6 +94,7 @@ class EoBrowserWindow {
         contextIsolation: false, // false if you want to run e2e test with Spectron
       },
     });
+
     proxyOpenExternal(this.win);
     this.loadURL();
     this.startMock();
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 0336bb9c3..92f787986 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -18,7 +18,10 @@
   <section class="h-full px-4 max-w-[80vw] mx-auto flex flex-col">
     <p class="w-full text-[#888] text-[12px]">{{ extensionDetail.description }}</p>
     <div>
-      <button nz-button nzSize="small" nzType="default" class="mt-[8px]">Uninstall</button>
+      <button nz-button nzSize="small" nzType="default" class="mt-[8px]" [nzLoading]="isOperating"
+        (click)="handleInstall()">
+        {{extensionDetail?.installed ? 'Uninstall' : 'Install'}}
+      </button>
     </div>
 
     <nz-tabset [nzAnimated]="false" class="settings" (nzSelectChange)="handleTabChange($event)">
@@ -64,3 +67,21 @@ <h2>[12.0.1] - 2022-03-03</h2> -->
     </nz-tabset>
   </section>
 </div>
+
+<nz-modal [(nzVisible)]="isVisible" nzTitle="Eoapi is required to install this extension." [nzOkText]="null"
+  (nzOnCancel)="handleOk()">
+  <ng-container *nzModalContent>
+    Don't have Eoapi?
+    <a nz-button nzType="link" nz-dropdown [nzDropdownMenu]="download" i18n>
+      Get it now
+    </a>.
+    <nz-dropdown-menu #download="nzDropdownMenu">
+      <ul nz-menu>
+        <ng-container *ngFor="let item of resourceInfo; let index = index">
+          <a [href]="item.link" nz-menu-item>{{ item.name }}</a>
+          <li nz-menu-divider *ngIf="index !== resourceInfo.length - 1"></li>
+        </ng-container>
+      </ul>
+    </nz-dropdown-menu>
+  </ng-container>
+</nz-modal>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index 1b7ee7d9a..f03b05307 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -15,9 +15,11 @@ export class ExtensionDetailComponent implements OnInit {
   isOperating = false;
   introLoading = false;
   changelogLoading = false;
+  isVisible = false;
   isNotLoaded = true;
   extensionDetail: EoExtensionInfo;
   resourceInfo = ResourceInfo;
+
   changeLog = '';
   get isElectron() {
     return this.electronService.isElectron;
@@ -32,6 +34,27 @@ export class ExtensionDetailComponent implements OnInit {
     this.getDetail();
     this.getInstaller();
   }
+
+  ngOnInit(): void {}
+
+  handleInstall() {
+    if (this.electronService.isElectron) {
+      this.manageExtension(this.extensionDetail?.installed ? 'uninstall' : 'install', this.extensionDetail?.name);
+    } else {
+      const PROTOCOL = 'eoapi://';
+      (window as any).protocolCheck(
+        PROTOCOL,
+        () => {
+          // alert("检测到您电脑Eoapi Client本地客户端未安装 请下载");
+          this.isVisible = true;
+        },
+        () => {
+          window.location.href = PROTOCOL;
+        }
+      );
+    }
+  }
+
   async getDetail() {
     this.extensionDetail = await this.extensionService.getDetail(
       this.route.snapshot.queryParams.id,
@@ -170,7 +193,6 @@ ${log}
       this.isOperating = false;
     }, 100);
   }
-  ngOnInit(): void {}
 
   backToList() {
     this.router.navigate(['/home/extension/list'], {
@@ -179,4 +201,9 @@ ${log}
       },
     });
   }
+
+  handleOk(): void {
+    console.log('Button ok clicked!');
+    this.isVisible = false;
+  }
 }
diff --git a/src/workbench/browser/src/app/shared/shared.module.ts b/src/workbench/browser/src/app/shared/shared.module.ts
index 7820f79b7..d88589fd5 100644
--- a/src/workbench/browser/src/app/shared/shared.module.ts
+++ b/src/workbench/browser/src/app/shared/shared.module.ts
@@ -25,6 +25,7 @@ import { NzPopoverModule } from 'ng-zorro-antd/popover';
 import { NzCodeEditorModule } from 'ng-zorro-antd/code-editor';
 import { NzResizableModule } from 'ng-zorro-antd/resizable';
 import { NzDividerModule } from 'ng-zorro-antd/divider';
+import { NzModalModule } from 'ng-zorro-antd/modal';
 
 import { NzNotificationModule } from 'ng-zorro-antd/notification';
 import { NzMessageModule } from 'ng-zorro-antd/message';
@@ -89,6 +90,7 @@ const SHARED_MODULE = [
   NzInputNumberModule,
   NzSwitchModule,
   NzDividerModule,
+  NzModalModule,
 ] as const;
 
 @NgModule({
diff --git a/src/workbench/browser/src/index.html b/src/workbench/browser/src/index.html
index d6130a42d..a4633934f 100644
--- a/src/workbench/browser/src/index.html
+++ b/src/workbench/browser/src/index.html
@@ -1,60 +1,63 @@
 <!DOCTYPE html>
 <html>
-  <head>
-    <meta charset="utf-8" />
-    <title>Eoapi - Easy & Open Source API Ecosystem</title>
-    <base href="/" />
-    <meta name="viewport" content="width=device-width, initial-scale=1" />
-    <script src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_12799_45.f89f4e0243155f9bf80e55f76674be32.js"></script>
-    <link rel="icon" type="image/x-icon" href="assets/icons/icon.ico" />
-  </head>
-  <!-- Angular Cli need for node modules -->
-  <script>
-    var global = global || window;
-    var Buffer = Buffer || [];
-    var process = process || {
-      env: { DEBUG: undefined },
-      version: [],
-    };
-  </script>
 
-  <body eo-drop-root>
-    <eo-root>
-      <div class="loading_container f_column">
-        <img src="assets/images/logo.svg" />
-        <div class="mt-2.5 ant-spin ant-spin-spinning ant-spin-lg">
-          <span class="ant-spin-dot ant-spin-dot-spin ng-star-inserted">
-            <i class="ant-spin-dot-item"></i>
-            <i class="ant-spin-dot-item"></i>
-            <i class="ant-spin-dot-item"></i>
-            <i class="ant-spin-dot-item"></i>
-          </span>
-        </div>
+<head>
+  <meta charset="utf-8" />
+  <title>Eoapi - Easy & Open Source API Ecosystem</title>
+  <base href="/" />
+  <meta name="viewport" content="width=device-width, initial-scale=1" />
+  <script async
+    src="https://lf1-cdn-tos.bytegoofy.com/obj/iconpark/icons_12799_45.f89f4e0243155f9bf80e55f76674be32.js"></script>
+  <script src="assets/libs/protocolcheck.js"></script>
+  <link rel="icon" type="image/x-icon" href="assets/icons/icon.ico" />
+</head>
+<!-- Angular Cli need for node modules -->
+<script>
+  var global = global || window;
+  var Buffer = Buffer || [];
+  var process = process || {
+    env: { DEBUG: undefined },
+    version: [],
+  };
+</script>
+
+<body eo-drop-root>
+  <eo-root>
+    <div class="loading_container f_column">
+      <img src="assets/images/logo.svg" />
+      <div class="mt-2.5 ant-spin ant-spin-spinning ant-spin-lg">
+        <span class="ant-spin-dot ant-spin-dot-spin ng-star-inserted">
+          <i class="ant-spin-dot-item"></i>
+          <i class="ant-spin-dot-item"></i>
+          <i class="ant-spin-dot-item"></i>
+          <i class="ant-spin-dot-item"></i>
+        </span>
       </div>
-    </eo-root>
-    <div id="mask"></div>
-  </body>
-  <style>
-    #mask {
-      position: absolute;
-      width: 100%;
-      height: 100%;
-      left: 0;
-      top: 0;
-      z-index: 99999;
-      display: none;
-    }
+    </div>
+  </eo-root>
+  <div id="mask"></div>
+</body>
+<style>
+  #mask {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    left: 0;
+    top: 0;
+    z-index: 99999;
+    display: none;
+  }
 
-    .loading_container {
-      position: absolute;
-      top: 40%;
-      left: 50%;
-      transform: translateX(-50%);
-    }
+  .loading_container {
+    position: absolute;
+    top: 40%;
+    left: 50%;
+    transform: translateX(-50%);
+  }
 
-    .loading_container img {
-      width: 200px;
-    }
-  </style>
+  .loading_container img {
+    width: 200px;
+  }
+</style>
 
 </html>

From c921536700efeed75da9ba513e0e3878dc62c521 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 18:24:55 +0800
Subject: [PATCH 10/21] test: relese

---
 package.json | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/package.json b/package.json
index 6dcd80ce9..004e6d185 100644
--- a/package.json
+++ b/package.json
@@ -1,7 +1,7 @@
 {
   "name": "eoapi",
   "souceLocale": "zh-Hans",
-  "version": "1.6.1",
+  "version": "1.6.2",
   "main": "out/app/electron-main/main.js",
   "description": "A lightweight, extensible API tool",
   "homepage": "https://github.com/eolinker/eoapi.git",

From 394f934b6207f1a5920fbcb2a45ed5bae9644d2e Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 18:47:10 +0800
Subject: [PATCH 11/21] feat: add save

---
 .../detail/components/extensions.component.ts        | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
index 607d6d588..af0b3b1fc 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
@@ -2,6 +2,7 @@ import { Component, Input, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
 import { SettingService } from 'eo/workbench/browser/src/app/core/services/settings/settings.service';
+import { NzMessageService } from 'ng-zorro-antd/message';
 
 @Component({
   selector: 'eo-extension-setting',
@@ -10,7 +11,7 @@ import { SettingService } from 'eo/workbench/browser/src/app/core/services/setti
       class="bg-white sticky top-0 py-[6px] border-solid border-0 border-b-[1px] z-10 mb-[3px]"
       style="border-color: var(--BORDER)"
     >
-      <button nz-button nzType="primary">Save</button>
+      <button nz-button nzType="primary" (click)="handleSave()">Save</button>
     </div>
 
     <form nz-form [nzLayout]="'vertical'" [formGroup]="validateForm" class="form">
@@ -82,7 +83,8 @@ export class ExtensionSettingComponent implements OnInit {
   constructor(
     public languageService: LanguageService,
     private fb: FormBuilder,
-    private settingService: SettingService
+    private settingService: SettingService,
+    private message: NzMessageService
   ) {}
 
   ngOnInit(): void {
@@ -116,4 +118,10 @@ export class ExtensionSettingComponent implements OnInit {
       }
     });
   }
+
+  handleSave = () => {
+    this.settingService.saveSetting(this.localSettings);
+    window.eo?.saveSettings?.({ ...this.localSettings });
+    this.message.create('success', `Save Success`);
+  };
 }

From 0612575e6081c14b6938edb271452bef079b09f3 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 18:59:29 +0800
Subject: [PATCH 12/21] feat: clickExtension tab query params

---
 .../pages/extension/detail/extension-detail.component.html   | 3 ++-
 .../app/pages/extension/detail/extension-detail.component.ts | 5 ++++-
 .../app/pages/extension/list/extension-list.component.html   | 2 +-
 .../src/app/pages/extension/list/extension-list.component.ts | 4 ++--
 4 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 92f787986..13f9d2588 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -24,7 +24,8 @@
       </button>
     </div>
 
-    <nz-tabset [nzAnimated]="false" class="settings" (nzSelectChange)="handleTabChange($event)">
+    <nz-tabset [(nzSelectedIndex)]="nzSelectedIndex" [nzAnimated]="false" class="settings"
+      (nzSelectChange)="handleTabChange($event)">
       <!-- Setting -->
       <nz-tab *ngIf="extensionDetail?.features?.configuration" nzTitle="Settings">
         <eo-extension-setting [configuration]="extensionDetail?.features?.configuration"></eo-extension-setting>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index f03b05307..7207c2497 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -19,6 +19,7 @@ export class ExtensionDetailComponent implements OnInit {
   isNotLoaded = true;
   extensionDetail: EoExtensionInfo;
   resourceInfo = ResourceInfo;
+  nzSelectedIndex = 0;
 
   changeLog = '';
   get isElectron() {
@@ -35,7 +36,9 @@ export class ExtensionDetailComponent implements OnInit {
     this.getInstaller();
   }
 
-  ngOnInit(): void {}
+  ngOnInit(): void {
+    this.nzSelectedIndex = ~~this.route.snapshot.queryParams.tab;
+  }
 
   handleInstall() {
     if (this.electronService.isElectron) {
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index e4954b087..a4debf96f 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -2,7 +2,7 @@
   <div class="grid grid-cols-2 gap-6 py-5 list-block">
     <div
       class="bd_all w-full min-h-[140px] p-5 flex flex-col flex-wrap plugin-block hover:border-green-700 hover:shadow-lg transition-shadow duration-300"
-      *ngFor="let it of renderList" (click)="clickExtension(it)">
+      *ngFor="let it of renderList" (click)="clickExtension($event,it)">
       <nz-skeleton [nzLoading]="loading" [nzActive]="true" [nzAvatar]="true">
         <div class="flex justify-between w-full">
           <div class="flex">
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
index 12cbf621e..242801222 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
@@ -68,14 +68,14 @@ export class ExtensionListComponent implements OnInit {
   onSeachChange(keyword) {
     this.seachChanged$.next(keyword);
   }
-  clickExtension(item) {
+  clickExtension(event, item) {
     this.router
       .navigate(['home/extension/detail'], {
         queryParams: {
           type: this.route.snapshot.queryParams.type,
           id: item.moduleID,
           name: item.name,
-          jump: 'setting',
+          tab: event?.target?.innerText === 'Details' ? 1 : 0,
         },
       })
       .finally();

From c03ef4b4c6adc727db5fd1a6e0bc85843e67e8ee Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 19:03:12 +0800
Subject: [PATCH 13/21] fix: update code

---
 .../pages/extension/detail/extension-detail.component.ts  | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index 7207c2497..2b538149b 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -36,9 +36,7 @@ export class ExtensionDetailComponent implements OnInit {
     this.getInstaller();
   }
 
-  ngOnInit(): void {
-    this.nzSelectedIndex = ~~this.route.snapshot.queryParams.tab;
-  }
+  ngOnInit(): void {}
 
   handleInstall() {
     if (this.electronService.isElectron) {
@@ -68,6 +66,10 @@ export class ExtensionDetailComponent implements OnInit {
     }
     this.isNotLoaded = false;
     this.extensionDetail.introduction ||= $localize`This plugin has no documentation yet.`;
+
+    if (this.extensionDetail?.features?.configuration) {
+      this.nzSelectedIndex = ~~this.route.snapshot.queryParams.tab;
+    }
   }
 
   async fetchChangelog(locale = '') {

From f2cc2a676f0b1d5b6f07f7772aba4c7cb8a6fbad Mon Sep 17 00:00:00 2001
From: scarqin <1054139596@qq.com>
Date: Wed, 7 Sep 2022 22:12:08 +0800
Subject: [PATCH 14/21] add i18n

---
 .../app/pages/extension/list/extension-list.component.html    | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index a4debf96f..4d1b726c0 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -17,10 +17,10 @@
               </div>
               <div class="btn-group" *ngIf="type === 'installed'">
                 <ng-container *ngIf="it.features?.configuration">
-                  <a nz-button nzType="link">Setting</a>
+                  <a nz-button i18n nzType="link">Setting</a>
                   <nz-divider nzType="vertical"></nz-divider>
                 </ng-container>
-                <a nz-button nzType="link">Details</a>
+                <a nz-button i18n nzType="link">Details</a>
               </div>
             </div>
           </div>

From d9d2aef06fb8a6fd649ee2f9b498274781d6b200 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Wed, 7 Sep 2022 23:42:41 +0800
Subject: [PATCH 15/21] chore: merge code

---
 .../src/app/pages/api/overview/api-overview.component.html | 6 +++---
 .../src/app/pages/api/overview/api-overview.component.ts   | 6 ++++++
 .../extension/detail/components/extensions.component.ts    | 2 +-
 .../pages/extension/detail/extension-detail.component.html | 7 ++++---
 .../pages/extension/detail/extension-detail.component.scss | 2 +-
 .../app/pages/extension/list/extension-list.component.html | 7 ++++---
 .../app/pages/extension/list/extension-list.component.ts   | 3 ++-
 src/workbench/browser/src/app/shared/shared.module.ts      | 2 ++
 8 files changed, 23 insertions(+), 12 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html
index 249f0ec6d..a9d6326b0 100644
--- a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html
+++ b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.html
@@ -1,13 +1,13 @@
 <div class="overview_container">
   <h1 class="fs20 fwb" i18n="@@API Index">Index</h1>
   <nz-divider></nz-divider>
-  <section class="grid gap-8 grid-cols-2 xl:grid-cols-3">
+  <section class="grid grid-cols-2 gap-8 xl:grid-cols-3">
     <div class="card_item" *ngFor="let item of overviewList">
-      <nz-card class="h-full" [nzActions]="[card]" [nzHoverable]="true">
+      <nz-card class="h-full" [nzActions]="[card]" [nzHoverable]="true" (click)="handleClickCard($event, item)">
         <nz-card-meta [nzTitle]="item.title" [nzDescription]="item.desc"></nz-card-meta>
       </nz-card>
       <ng-template #card>
-        <eo-iconpark-icon (click)="clickCard(item)" class="mr5" name="{{ item.icon }}" size="16px"></eo-iconpark-icon>
+        <eo-iconpark-icon class="mr5" name="{{ item.icon }}" size="16px"></eo-iconpark-icon>
       </ng-template>
     </div>
   </section>
diff --git a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts
index 1771e0177..ab08284cc 100644
--- a/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts
+++ b/src/workbench/browser/src/app/pages/api/overview/api-overview.component.ts
@@ -43,6 +43,12 @@ export class ApiOverviewComponent implements OnDestroy {
     },
   ];
 
+  handleClickCard = (event, item) => {
+    if (event.target?.classList?.contains?.('ant-card-actions') || event.target?.closest('.ant-card-actions')) {
+      this.clickCard(item);
+    }
+  };
+
   clickCard({ title, desc, type }) {
     this.modal = this.modalService.create({
       nzTitle: desc,
diff --git a/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
index af0b3b1fc..560dc736b 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/components/extensions.component.ts
@@ -8,7 +8,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
   selector: 'eo-extension-setting',
   template: `
     <div
-      class="bg-white sticky top-0 py-[6px] border-solid border-0 border-b-[1px] z-10 mb-[3px]"
+      class="bg-white sticky top-0 py-[10px] border-solid border-0 border-b-[1px] z-10 mb-[3px]"
       style="border-color: var(--BORDER)"
     >
       <button nz-button nzType="primary" (click)="handleSave()">Save</button>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 13f9d2588..580968848 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -18,13 +18,14 @@
   <section class="h-full px-4 max-w-[80vw] mx-auto flex flex-col">
     <p class="w-full text-[#888] text-[12px]">{{ extensionDetail.description }}</p>
     <div>
-      <button nz-button nzSize="small" nzType="default" class="mt-[8px]" [nzLoading]="isOperating"
-        (click)="handleInstall()">
+      <button nz-button nzSize="small" [nzType]="extensionDetail?.installed ? 'default': 'primary'" class="mt-[8px]"
+        [nzLoading]="isOperating" (click)="handleInstall()">
         {{extensionDetail?.installed ? 'Uninstall' : 'Install'}}
       </button>
     </div>
 
-    <nz-tabset [(nzSelectedIndex)]="nzSelectedIndex" [nzAnimated]="false" class="settings"
+    <nz-tabset [(nzSelectedIndex)]="nzSelectedIndex" [nzAnimated]="false"
+      [class]="{settings: nzSelectedIndex === 0 && extensionDetail?.features?.configuration}"
       (nzSelectChange)="handleTabChange($event)">
       <!-- Setting -->
       <nz-tab *ngIf="extensionDetail?.features?.configuration" nzTitle="Settings">
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
index 30258a8c5..44db87638 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
@@ -10,7 +10,7 @@
       padding-top: 0;
     }
     .ant-tabs-content-holder {
-      padding-top: 12px;
+      padding-top: 15px;
       padding-bottom: 8px;
       margin-bottom: 24px;
       overflow: auto;
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index 4d1b726c0..2f02837f5 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -13,14 +13,15 @@
               <div class="flex flex-col flex-auto">
                 <span class="text-lg font-bold">{{ it.moduleName }}</span>
                 <span class="my-2 text-gray-400">{{ it.author }}</span>
-                <span class="my-1 text-gray-500 desc">{{ it.description }}</span>
+                <span nz-typography nzEllipsis nzType="secondary" [nzContent]="it.description" class="my-1"></span>
+
               </div>
               <div class="btn-group" *ngIf="type === 'installed'">
                 <ng-container *ngIf="it.features?.configuration">
-                  <a nz-button i18n nzType="link">Setting</a>
+                  <a nz-button nzType="link" i18n>Setting</a>
                   <nz-divider nzType="vertical"></nz-divider>
                 </ng-container>
-                <a nz-button i18n nzType="link">Details</a>
+                <a nz-button nzType="link"><span data-id="details" i18n>Details</span></a>
               </div>
             </div>
           </div>
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
index 242801222..ab69d64b5 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
@@ -69,13 +69,14 @@ export class ExtensionListComponent implements OnInit {
     this.seachChanged$.next(keyword);
   }
   clickExtension(event, item) {
+    console.log('event?.target?.dataset', event?.target);
     this.router
       .navigate(['home/extension/detail'], {
         queryParams: {
           type: this.route.snapshot.queryParams.type,
           id: item.moduleID,
           name: item.name,
-          tab: event?.target?.innerText === 'Details' ? 1 : 0,
+          tab: event?.target?.dataset?.id === 'details' ? 1 : 0,
         },
       })
       .finally();
diff --git a/src/workbench/browser/src/app/shared/shared.module.ts b/src/workbench/browser/src/app/shared/shared.module.ts
index d88589fd5..b257094ab 100644
--- a/src/workbench/browser/src/app/shared/shared.module.ts
+++ b/src/workbench/browser/src/app/shared/shared.module.ts
@@ -26,6 +26,7 @@ import { NzCodeEditorModule } from 'ng-zorro-antd/code-editor';
 import { NzResizableModule } from 'ng-zorro-antd/resizable';
 import { NzDividerModule } from 'ng-zorro-antd/divider';
 import { NzModalModule } from 'ng-zorro-antd/modal';
+import { NzTypographyModule } from 'ng-zorro-antd/typography';
 
 import { NzNotificationModule } from 'ng-zorro-antd/notification';
 import { NzMessageModule } from 'ng-zorro-antd/message';
@@ -91,6 +92,7 @@ const SHARED_MODULE = [
   NzSwitchModule,
   NzDividerModule,
   NzModalModule,
+  NzTypographyModule,
 ] as const;
 
 @NgModule({

From 1515babe180d0c2e33288dcba3d9cf8f544331ed Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 15:15:02 +0800
Subject: [PATCH 16/21] feat: extension support swtich enable status

---
 src/workbench/browser/src/app/app.module.ts   |  2 +-
 .../detail/extension-detail.component.html    |  8 +++--
 .../detail/extension-detail.component.scss    |  4 +++
 .../detail/extension-detail.component.ts      | 14 +++++++-
 .../app/pages/extension/extension.service.ts  | 33 +++++++++++++++++++
 .../list/extension-list.component.html        |  3 +-
 .../list/extension-list.component.ts          | 14 +++++++-
 .../export-api/export-api.component.ts        | 13 +++++---
 .../import-api/import-api.component.ts        | 14 +++++---
 .../components/sync-api/sync-api.component.ts | 13 +++++---
 .../src/app/shared/constant/storageKeys.ts    |  2 ++
 .../browser/src/assets/theme/antd.less        |  7 ++--
 12 files changed, 102 insertions(+), 25 deletions(-)
 create mode 100644 src/workbench/browser/src/app/shared/constant/storageKeys.ts

diff --git a/src/workbench/browser/src/app/app.module.ts b/src/workbench/browser/src/app/app.module.ts
index 76d470489..6ea5cd478 100644
--- a/src/workbench/browser/src/app/app.module.ts
+++ b/src/workbench/browser/src/app/app.module.ts
@@ -76,7 +76,7 @@ registerLocaleData(zh);
         }
       },
       deps: [LOCALE_ID],
-    }
+    },
   ],
   bootstrap: [AppComponent],
 })
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 580968848..73fb0461f 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -10,7 +10,8 @@
           <nz-avatar nzSize="small" class="!w-[20px] !h-[20px]" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
         </div>
         <span class="font-bold text-[14px]">{{ extensionDetail.moduleName }}</span>
-        <nz-switch nzSize="small" class="ml-2.5"></nz-switch>
+        <nz-switch [(ngModel)]="isEnable" nzSize="small" class="ml-2.5" (ngModelChange)="handleEnableExtension($event)">
+        </nz-switch>
       </div>
     </div>
     <!-- <div class="bbd"></div> -->
@@ -45,8 +46,9 @@
           <nz-descriptions-item i18n-nzTitle nzTitle="Author">{{ extensionDetail.author }}</nz-descriptions-item>
           <nz-descriptions-item i18n-nzTitle nzTitle="Version">{{ extensionDetail.version }} </nz-descriptions-item>
           <nz-descriptions-item *ngIf="extensionDetail.repository?.url" i18n-nzTitle nzTitle="Repository">
-            <a [href]="extensionDetail.repository.url.replace('git+', '')" target="_blank">{{
-              extensionDetail.repository.url }}</a>
+            <a [href]="extensionDetail.repository.url.replace('git+', '')" target="_blank">
+              {{ extensionDetail.repository.url.replace('git+', '') }}
+            </a>
           </nz-descriptions-item>
           <nz-descriptions-item *ngIf="extensionDetail.homepage" i18n-nzTitle nzTitle="Homepage">
             <a [href]="extensionDetail.homepage" target="_blank">{{ extensionDetail.homepage }}</a>
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
index 44db87638..53f496228 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.scss
@@ -38,3 +38,7 @@
     }
   }
 }
+
+a {
+  color: var(--BLUE_NORMAL);
+}
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index 2b538149b..656716a54 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -16,6 +16,7 @@ export class ExtensionDetailComponent implements OnInit {
   introLoading = false;
   changelogLoading = false;
   isVisible = false;
+  isEnable = false;
   isNotLoaded = true;
   extensionDetail: EoExtensionInfo;
   resourceInfo = ResourceInfo;
@@ -61,6 +62,9 @@ export class ExtensionDetailComponent implements OnInit {
       this.route.snapshot.queryParams.id,
       this.route.snapshot.queryParams.name
     );
+
+    this.isEnable = this.extensionService.isEnable(this.extensionDetail.name);
+
     if (!this.extensionDetail?.installed) {
       await this.fetchReadme(this.language.systemLanguage);
     }
@@ -97,7 +101,7 @@ export class ExtensionDetailComponent implements OnInit {
 ${log}
 ## [${key}](${value.dist.tarball}) - ${new Date(data.time[key]).toLocaleString()}
           `;
-        }, '# Change Log');
+        }, '');
       } else if (locale) {
         //If locale README not find,fetch default locale(en-US)
         this.fetchChangelog();
@@ -211,4 +215,12 @@ ${log}
     console.log('Button ok clicked!');
     this.isVisible = false;
   }
+
+  handleEnableExtension(isEnable) {
+    if (isEnable) {
+      this.extensionService.enableExtension(this.extensionDetail.name);
+    } else {
+      this.extensionService.disableExtension(this.extensionDetail.name);
+    }
+  }
 }
diff --git a/src/workbench/browser/src/app/pages/extension/extension.service.ts b/src/workbench/browser/src/app/pages/extension/extension.service.ts
index 59441487e..742ef4914 100644
--- a/src/workbench/browser/src/app/pages/extension/extension.service.ts
+++ b/src/workbench/browser/src/app/pages/extension/extension.service.ts
@@ -6,12 +6,14 @@ import { ModuleInfo } from 'eo/platform/node/extension-manager/types/index';
 import { TranslateService } from 'eo/platform/common/i18n';
 import { LanguageService } from 'eo/workbench/browser/src/app/core/services/language/language.service';
 import { APP_CONFIG } from 'eo/workbench/browser/src/environments/environment';
+import { DISABLE_EXTENSION_NAMES } from 'eo/workbench/browser/src/app/shared/constant/storageKeys';
 
 @Injectable({
   providedIn: 'root',
 })
 export class ExtensionService {
   ignoreList = ['default'];
+  disabledExtensionNames: string[] = this.getExtensionNames();
   extensionIDs: Array<string> = [];
   HOST = '';
   localExtensions: Map<string, ModuleInfo>;
@@ -28,6 +30,10 @@ export class ExtensionService {
     // Local extension exception for ignore list
     return Array.from(this.localExtensions.values()).filter((it) => this.extensionIDs.includes(it.moduleID));
   }
+  isInstalled(name) {
+    const installList = this.getInstalledList();
+    return installList.includes(name);
+  }
   private translateModule(module: ModuleInfo) {
     const lang = this.language.systemLanguage;
     const locale = module.i18n?.find((val) => val.locale === lang)?.package;
@@ -86,6 +92,33 @@ export class ExtensionService {
     console.error(data);
     return false;
   }
+
+  isEnable(name: string) {
+    return !this.disabledExtensionNames.includes(name);
+  }
+
+  enableExtension(names: string | string[]) {
+    const enableNames = Array().concat(names) as string[];
+    this.setExtension(this.disabledExtensionNames.filter((n) => !enableNames.includes(n)));
+  }
+
+  disableExtension(names: string | string[]) {
+    this.setExtension([...new Set(this.disabledExtensionNames.concat(names))]);
+  }
+
+  setExtension(arr: string[]) {
+    localStorage.setItem(DISABLE_EXTENSION_NAMES, JSON.stringify(arr));
+    this.disabledExtensionNames = arr;
+  }
+
+  getExtensionNames() {
+    try {
+      return (this.disabledExtensionNames = JSON.parse(localStorage.getItem(DISABLE_EXTENSION_NAMES) || '[]'));
+    } catch (error) {
+      return [];
+    }
+  }
+
   private async requestDetail(id) {
     return await lastValueFrom(this.http.get(`${this.HOST}/detail/${id}?locale=${this.language.systemLanguage}`)).catch(
       (err) => [0, err]
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index 2f02837f5..130de1af3 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -26,7 +26,8 @@
             </div>
           </div>
           <div *ngIf="type === 'installed'">
-            <nz-switch click-stop-propagation [(ngModel)]="it.isOpen"></nz-switch>
+            <nz-switch click-stop-propagation [(ngModel)]="it.isEnable"
+              (ngModelChange)="handleEnableExtension($event, it)"></nz-switch>
           </div>
           <div *ngIf="type !== 'installed'">
             <span *ngIf="electron.isElectron ? extensionService.localExtensions.has(it.moduleID) : it.installed"
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
index ab69d64b5..03e43371a 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.ts
@@ -51,7 +51,10 @@ export class ExtensionListComponent implements OnInit {
     try {
       if (this.type === 'installed') {
         const installedList = new ExtensionList(this.extensionService.getInstalledList());
-        return installedList.search(keyword);
+        return installedList.search(keyword).map((n) => {
+          n.isEnable = this.extensionService.isEnable(n.name);
+          return n;
+        });
       }
       const res: any = await this.extensionService.requestList();
       if (this.type === 'official') {
@@ -81,6 +84,15 @@ export class ExtensionListComponent implements OnInit {
       })
       .finally();
   }
+
+  handleEnableExtension(isEnable, item) {
+    if (isEnable) {
+      this.extensionService.enableExtension(item.name);
+    } else {
+      this.extensionService.disableExtension(item.name);
+    }
+  }
+
   private watchSearchConditionChange() {
     this.route.queryParamMap.subscribe(async (params) => {
       this.type = this.route.snapshot.queryParams.type;
diff --git a/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts b/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
index 6cc9ac8ce..2d311be91 100644
--- a/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
@@ -4,6 +4,7 @@ import { StorageRes, StorageResStatus } from '../../services/storage/index.model
 import packageJson from '../../../../../../../../package.json';
 import { FeatureType } from '../../types';
 import { ModuleInfo } from 'eo/platform/node/extension-manager';
+import { ExtensionService } from 'eo/workbench/browser/src/app/pages/extension/extension.service';
 
 @Component({
   selector: 'eo-export-api',
@@ -13,13 +14,15 @@ export class ExportApiComponent implements OnInit {
   currentExtension = 'eoapi';
   supportList: Array<FeatureType> = [];
   featureMap = window.eo.getFeature('apimanage.export');
-  constructor(private storage: StorageService) {}
+  constructor(private storage: StorageService, public extensionService: ExtensionService) {}
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      this.supportList.push({
-        key,
-        ...data,
-      });
+      if (this.extensionService.isEnable(key)) {
+        this.supportList.push({
+          key,
+          ...data,
+        });
+      }
     });
     {
       const { key } = this.supportList.at(0);
diff --git a/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts b/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
index 1674aa4ad..30d594227 100644
--- a/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
@@ -4,6 +4,7 @@ import { EoMessageService } from 'eo/workbench/browser/src/app/eoui/message/eo-m
 import { MessageService } from 'eo/workbench/browser/src/app/shared/services/message/message.service';
 import { StorageService } from 'eo/workbench/browser/src/app/shared/services/storage';
 import { StorageRes, StorageResStatus } from 'eo/workbench/browser/src/app/shared/services/storage/index.model';
+import { ExtensionService } from 'eo/workbench/browser/src/app/pages/extension/extension.service';
 
 // const optionList = [
 //   {
@@ -53,14 +54,17 @@ export class ImportApiComponent implements OnInit {
   constructor(
     private messageService: MessageService,
     private storage: StorageService,
-    private eoMessage: EoMessageService
+    private eoMessage: EoMessageService,
+    public extensionService: ExtensionService
   ) {}
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      this.supportList.push({
-        key,
-        ...data,
-      });
+      if (this.extensionService.isEnable(key)) {
+        this.supportList.push({
+          key,
+          ...data,
+        });
+      }
     });
     {
       const { key } = this.supportList.at(0);
diff --git a/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts b/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
index 4f0d1dc55..70f754b26 100644
--- a/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
@@ -3,6 +3,7 @@ import { StorageRes, StorageResStatus } from '../../services/storage/index.model
 import { StorageService } from '../../services/storage';
 import packageJson from '../../../../../../../../package.json';
 import { FeatureType } from '../../types';
+import { ExtensionService } from 'eo/workbench/browser/src/app/pages/extension/extension.service';
 
 @Component({
   selector: 'eo-sync-api',
@@ -12,14 +13,16 @@ export class SyncApiComponent implements OnInit {
   currentExtension = '';
   supportList: any[] = [];
   featureMap = window.eo.getFeature('apimanage.sync');
-  constructor(private storage: StorageService) {}
+  constructor(private storage: StorageService, public extensionService: ExtensionService) {}
 
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      this.supportList.push({
-        key,
-        ...data,
-      });
+      if (this.extensionService.isEnable(key)) {
+        this.supportList.push({
+          key,
+          ...data,
+        });
+      }
     });
     {
       const { key } = this.supportList.at(0);
diff --git a/src/workbench/browser/src/app/shared/constant/storageKeys.ts b/src/workbench/browser/src/app/shared/constant/storageKeys.ts
new file mode 100644
index 000000000..fb39a9863
--- /dev/null
+++ b/src/workbench/browser/src/app/shared/constant/storageKeys.ts
@@ -0,0 +1,2 @@
+/** disable extensions */
+export const DISABLE_EXTENSION_NAMES = 'DISABLE_EXTENSION_NAMES';
diff --git a/src/workbench/browser/src/assets/theme/antd.less b/src/workbench/browser/src/assets/theme/antd.less
index 7d87a23c7..a31505493 100644
--- a/src/workbench/browser/src/assets/theme/antd.less
+++ b/src/workbench/browser/src/assets/theme/antd.less
@@ -10,14 +10,15 @@
 @primary-color: @theme-color;
 @primary-color-hover: @theme-color;
 @success-color: @theme-color;
-@link-color:#333;
+@link-color: #333;
 @processing-color: @theme-color;
 @tree-title-height: 30px;
 body {
   line-height: initial;
 }
-.ant-btn-link,.ant-btn-link:hover{
-  color: var(--BLUE_NORMAL);
+.ant-btn-link,
+.ant-btn-link:hover {
+  color: var(--BLUE_DEEPER);
 }
 // .ant-message-notice-content {
 // background-color: @theme-color;

From da3604f31bc4b49e8933f956b8d8570af226e299 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 15:26:21 +0800
Subject: [PATCH 17/21] fix: extension name not sure

---
 src/platform/node/extension-manager/lib/manager.ts              | 2 +-
 .../app/shared/components/export-api/export-api.component.ts    | 2 +-
 .../app/shared/components/import-api/import-api.component.ts    | 2 +-
 .../src/app/shared/components/sync-api/sync-api.component.ts    | 2 +-
 src/workbench/browser/src/app/shared/types.ts                   | 1 +
 5 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/src/platform/node/extension-manager/lib/manager.ts b/src/platform/node/extension-manager/lib/manager.ts
index 4464c6206..1eb8f43d1 100644
--- a/src/platform/node/extension-manager/lib/manager.ts
+++ b/src/platform/node/extension-manager/lib/manager.ts
@@ -211,7 +211,7 @@ export class ModuleManager implements ModuleManagerInterface {
         if (!this.features.has(key)) {
           this.features.set(key, new Map());
         }
-        this.features.get(key).set(moduleInfo.moduleID, value);
+        this.features.get(key).set(moduleInfo.moduleID, { name: moduleInfo.name, ...value });
       });
     }
   }
diff --git a/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts b/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
index 2d311be91..1d2eca8b8 100644
--- a/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/export-api/export-api.component.ts
@@ -17,7 +17,7 @@ export class ExportApiComponent implements OnInit {
   constructor(private storage: StorageService, public extensionService: ExtensionService) {}
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      if (this.extensionService.isEnable(key)) {
+      if (this.extensionService.isEnable(data.name)) {
         this.supportList.push({
           key,
           ...data,
diff --git a/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts b/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
index 30d594227..bb566926c 100644
--- a/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/import-api/import-api.component.ts
@@ -59,7 +59,7 @@ export class ImportApiComponent implements OnInit {
   ) {}
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      if (this.extensionService.isEnable(key)) {
+      if (this.extensionService.isEnable(data.name)) {
         this.supportList.push({
           key,
           ...data,
diff --git a/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts b/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
index 70f754b26..0b6101b41 100644
--- a/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
+++ b/src/workbench/browser/src/app/shared/components/sync-api/sync-api.component.ts
@@ -17,7 +17,7 @@ export class SyncApiComponent implements OnInit {
 
   ngOnInit(): void {
     this.featureMap?.forEach((data: FeatureType, key: string) => {
-      if (this.extensionService.isEnable(key)) {
+      if (this.extensionService.isEnable(data.name)) {
         this.supportList.push({
           key,
           ...data,
diff --git a/src/workbench/browser/src/app/shared/types.ts b/src/workbench/browser/src/app/shared/types.ts
index 264779fa8..6fd99cd3e 100644
--- a/src/workbench/browser/src/app/shared/types.ts
+++ b/src/workbench/browser/src/app/shared/types.ts
@@ -1,6 +1,7 @@
 export type FeatureType = {
   icon: string;
   label: string;
+  name: string;
   description: string;
   key?: string;
   properties?: object;

From 6b79610e0087cc41e72d6b6efb124bd011da4111 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 15:43:09 +0800
Subject: [PATCH 18/21] fix: css style

---
 .../pages/extension/detail/extension-detail.component.html   | 5 +++--
 .../app/pages/extension/list/extension-list.component.html   | 2 +-
 .../app/pages/extension/list/extension-list.component.scss   | 5 ++---
 3 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 73fb0461f..40e062c0f 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -19,8 +19,9 @@
   <section class="h-full px-4 max-w-[80vw] mx-auto flex flex-col">
     <p class="w-full text-[#888] text-[12px]">{{ extensionDetail.description }}</p>
     <div>
-      <button nz-button nzSize="small" [nzType]="extensionDetail?.installed ? 'default': 'primary'" class="mt-[8px]"
-        [nzLoading]="isOperating" (click)="handleInstall()">
+      <button nz-button [nzSize]="extensionDetail?.installed ? 'small' :'default'"
+        [nzType]="extensionDetail?.installed ? 'default': 'primary'" class="mt-[8px]" [nzLoading]="isOperating"
+        (click)="handleInstall()">
         {{extensionDetail?.installed ? 'Uninstall' : 'Install'}}
       </button>
     </div>
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
index 130de1af3..f41143b84 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.html
@@ -13,7 +13,7 @@
               <div class="flex flex-col flex-auto">
                 <span class="text-lg font-bold">{{ it.moduleName }}</span>
                 <span class="my-2 text-gray-400">{{ it.author }}</span>
-                <span nz-typography nzEllipsis nzType="secondary" [nzContent]="it.description" class="my-1"></span>
+                <span class="my-1 desc">{{it.description}}</span>
 
               </div>
               <div class="btn-group" *ngIf="type === 'installed'">
diff --git a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
index 4435efc18..7d8a7149b 100644
--- a/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
+++ b/src/workbench/browser/src/app/pages/extension/list/extension-list.component.scss
@@ -3,8 +3,7 @@
   display: -webkit-box;
   text-overflow: ellipsis;
   -webkit-box-orient: vertical;
-  -webkit-line-clamp: 2;
-  height: 2rem;
+  -webkit-line-clamp: 1;
 }
 
 .plugin-block {
@@ -15,7 +14,7 @@
 .list-block {
   height: calc(100% - 65px);
   overflow: auto;
-  >div{
+  > div {
     border-radius: 3px;
   }
 }

From d7f0b630379532c3e8324f1bc1445d9022360ab8 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 16:04:54 +0800
Subject: [PATCH 19/21] chore: update desc

---
 .../app/pages/extension/detail/extension-detail.component.html  | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 40e062c0f..1d71e184e 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -78,7 +78,7 @@ <h2>[12.0.1] - 2022-03-03</h2> -->
   <ng-container *nzModalContent>
     Don't have Eoapi?
     <a nz-button nzType="link" nz-dropdown [nzDropdownMenu]="download" i18n>
-      Get it now
+      Download
     </a>.
     <nz-dropdown-menu #download="nzDropdownMenu">
       <ul nz-menu>

From 1fc7d3722c2620610b091630db30599c1b1d9582 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 19:00:59 +0800
Subject: [PATCH 20/21] fix: some css style

---
 .github/workflows/release.yml                              | 2 --
 src/app/electron-main/main.ts                              | 7 ++++++-
 .../pages/extension/detail/extension-detail.component.html | 7 +++++--
 .../pages/extension/detail/extension-detail.component.ts   | 4 +++-
 4 files changed, 14 insertions(+), 6 deletions(-)

diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ec6bdf993..34f66cb96 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,8 +4,6 @@ on:
   push:
     tags:
       - 'v*.*.*'
-    branches:
-      - feat/extension-manage
 
 jobs:
   release:
diff --git a/src/app/electron-main/main.ts b/src/app/electron-main/main.ts
index d1d8d0ec4..2dda76e97 100644
--- a/src/app/electron-main/main.ts
+++ b/src/app/electron-main/main.ts
@@ -20,7 +20,12 @@ export const subView = {
   mainView: null,
 };
 const PROTOCOL = 'eoapi';
-app.setAsDefaultProtocolClient(PROTOCOL); // 注册协议
+// app.setAsDefaultProtocolClient(PROTOCOL); // 注册协议
+if (app.isPackaged) {
+  app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, ['--']);
+} else {
+  app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [path.resolve(process.argv[1]), '--']);
+}
 const eoUpdater = new EoUpdater();
 const mockServer = new MockServer();
 const moduleManager: ModuleManagerInterface = new ModuleManager();
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
index 1d71e184e..7577f3311 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.html
@@ -10,8 +10,11 @@
           <nz-avatar nzSize="small" class="!w-[20px] !h-[20px]" nzSrc="{{ extensionDetail.logo }}"></nz-avatar>
         </div>
         <span class="font-bold text-[14px]">{{ extensionDetail.moduleName }}</span>
-        <nz-switch [(ngModel)]="isEnable" nzSize="small" class="ml-2.5" (ngModelChange)="handleEnableExtension($event)">
-        </nz-switch>
+        <ng-container *ngIf="extensionDetail?.installed">
+          <nz-switch [(ngModel)]="isEnable" nzSize="small" class="ml-2.5"
+            (ngModelChange)="handleEnableExtension($event)">
+          </nz-switch>
+        </ng-container>
       </div>
     </div>
     <!-- <div class="bbd"></div> -->
diff --git a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
index 656716a54..3beb4624f 100644
--- a/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
+++ b/src/workbench/browser/src/app/pages/extension/detail/extension-detail.component.ts
@@ -167,7 +167,7 @@ ${log}
   getInstaller() {
     fetch('https://api.github.com/repos/eolinker/eoapi/releases')
       .then((response) => response.json())
-      .then((data) => {
+      .then((data = []) => {
         [...this.resourceInfo]
           .sort((a1, a2) => a2.suffix.length - a1.suffix.length)
           .forEach((item) => {
@@ -190,11 +190,13 @@ ${log}
       switch (operate) {
         case 'install': {
           this.extensionDetail.installed = this.extensionService.install(id);
+          this.handleEnableExtension(true);
           this.getDetail();
           break;
         }
         case 'uninstall': {
           this.extensionDetail.installed = !this.extensionService.uninstall(id);
+          this.handleEnableExtension(false);
           this.fetchReadme(this.language.systemLanguage);
           break;
         }

From 1ae00992ca77f40f2026c97797a15b1556cf5571 Mon Sep 17 00:00:00 2001
From: buqiyuan <1743369777@qq.com>
Date: Thu, 8 Sep 2022 23:00:48 +0800
Subject: [PATCH 21/21] feat: add single instance lock

---
 src/app/electron-main/main.ts | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/app/electron-main/main.ts b/src/app/electron-main/main.ts
index 2dda76e97..c3c066f4e 100644
--- a/src/app/electron-main/main.ts
+++ b/src/app/electron-main/main.ts
@@ -19,6 +19,13 @@ export const subView = {
   appView: null,
   mainView: null,
 };
+
+// 获取单实例锁
+const gotTheLock = app.requestSingleInstanceLock();
+if (!gotTheLock) {
+  // 如果获取失败，说明已经有实例在运行了，直接退出
+  app.quit();
+}
 const PROTOCOL = 'eoapi';
 // app.setAsDefaultProtocolClient(PROTOCOL); // 注册协议
 if (app.isPackaged) {
@@ -26,6 +33,7 @@ if (app.isPackaged) {
 } else {
   app.setAsDefaultProtocolClient(PROTOCOL, process.execPath, [path.resolve(process.argv[1]), '--']);
 }
+
 const eoUpdater = new EoUpdater();
 const mockServer = new MockServer();
 const moduleManager: ModuleManagerInterface = new ModuleManager();
