diff --git a/.gitignore b/.gitignore
index 18c1099..863012a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ coverage/
.umi-production/
.log
.eslintcache
+*.pem
diff --git a/README.md b/README.md
index fa349d5..2c583cf 100644
--- a/README.md
+++ b/README.md
@@ -70,6 +70,23 @@ yarn
yarn start
```
+### Local Https certificate
+
+If you need to use https in the local development environment, you can use the following command to generate a certificate:
+
+```bash
+brew install mkcert
+
+# install the local certificate
+mkcert -install
+
+# enter the playground app directory
+cd apps/playground
+
+# generate a certificate
+mkcert local.netease.com # or change to your own domain name
+```
+
## 💬 Community
Join NetEase Tango Community to share your ideas, suggestions, or questions and connect with other users and contributors.
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 211e836..0e93935 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -70,6 +70,23 @@ yarn
yarn start
```
+### 本地 https 证书
+
+如果需要在本地开发环境中使用 https,可以使用以下命令生成证书:
+
+```bash
+brew install mkcert
+
+# 将 mkcert 添加到本地根 CA,仅在本地生效
+mkcert -install
+
+# 进入 playground 应用目录
+cd apps/playground
+
+# 为网站生成一个由 mkcert 签名的证书
+mkcert local.netease.com
+```
+
## 💬 社区讨论
参与 NetEase Tango 的社区,以分享您的想法、建议或问题,并与其他用户和贡献者建立联系。
diff --git a/apps/playground/local.netease.com-key.pem b/apps/playground/local.netease.com-key.pem
deleted file mode 100644
index 6a8e358..0000000
--- a/apps/playground/local.netease.com-key.pem
+++ /dev/null
@@ -1,28 +0,0 @@
------BEGIN PRIVATE KEY-----
-MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC4sAqDE3nhtSWD
-YqV4mlglnzIs2AioZr2uxfj/OKrpE1ZML/dflJyc5NGUpOZWV+W1vRc70wWVNFxB
-Si8nrYrrseoID7Ehxxe6+J1m/JsEbhZWHwV3M1iGW7JD7VhujaDpSLg67wMTMpSW
-3BUS+0NT+MNfCisp+u/ls4fXPCTgXiWcjMULm95oLlTeYwurPHfxwn9f9hh85I5I
-K3dI7w8MsQN7EFNrJz3UfrNLpmARMtSUBMwBYqrCitoUxRT0q7KYCru7kHxGPB6A
-SQrFpSpOWT79vp8GBSp9g3axhRsK+aEgzQ0QGrFPHBwBP/kWgmdB1URc/K7STSC9
-xyMNZOM7AgMBAAECggEATbyDYDKVbaRymr+tyHqmuYctdmSkGEXOdz8LFFoEzj/+
-ZekWpVuHJB7H/FoAPb2XiYyCuAKVafz1C+IuwPOx8o0bZbM9KmuDmIZGsm+GzDGO
-I0fBZC/vhfYYrbC8NSRV43thWCT8VVtH4cvW7vtllnWxvlIoYu4lhbiwZ68AEpO1
-8PlJsOhnagfkFpG8LehSDs9if5L+Ig0geQEcUY3pGUM9LKYsRoxFv6Vh8wQku1XD
-YOxLvLWDPqJ75m816t50RARe3e6eDJ+ukzDCoDjmLP8uhf1sk1FcL5aOgUNU3VOi
-P6sIH5uzuD0MA3FXx2mItYLOc44r4uaFJZiRUMEXGQKBgQDCjUYblE4rh0nKFkqx
-AhgfcRgpgLbP42OwFtD0u20RXdZAEJDRDQ43NdOgjFp2B50k75LBPIoFXYUZdzKj
-l7S2FH5O9Cc6hEgZGI4hCiYd+nQVI1mxv820YnzZJN4oooxyL+B5RlnW5cDT7LJN
-11Q5PRewkM+XpfbE3lKUjTNcDwKBgQDzBS+j8T9GJSzV2xgA+mpGHe1RjDq6yQtk
-Rl5GWpflW4Nr3/58BbmCWNA8WP0NOWutJhrkltwLCqJC8OZX/WHnmcmq2k+UAfQQ
-z98p+xcMWOTcl5XIbveuhYoZb2udt9WVJLcpRVxVhEWcBrtHsrpScZXsKYf/TaZ1
-JtCjPU5KFQKBgGXj/UqmYkYzxXZ8NEP998pHvcLGsXew/F2VpXv2yQlmXrYQtvd1
-YfOSUjJsL1hPZoKYogBoB1UGBJYwsimxYyhVFU2eWwkvjF7wWEd/fDUJHVCQwgHw
-lPga+I303BDmCK4o2uRI7vY4P8P92+gelPKhR03mmYIvnky+rvsz9CkLAoGAVerl
-du6Z0jDecxUsnVvrKrL17jjHorXlYyRFvFXXEe2SvxbIIAzeEylXJZ7IiF5epS6t
-n1d+oCC4UTZeGYmpjXofhFn+fHNcWH1FhtAQy/q5nwuwltY0luz7cBamU3Jk/n+m
-id7N0CYdfhYbLDQSD4e822ureyV2zkBUzwGjpikCgYEAoCVbhkxVrQHRmxLC2jDx
-pM94W1ij9TqblfGg3OMpPXjpTdlknatWjBw7JkoyxYiYJq5Gi2p4QV6HHUvS1osj
-BB+4A99/PD9A0ICoJglz8SXlGe/895zwHwpKT9f0zjizQrEd2KCPf75URa+t2FGY
-B2GkXsbChRWVrey5+uMzUD8=
------END PRIVATE KEY-----
diff --git a/apps/playground/local.netease.com.pem b/apps/playground/local.netease.com.pem
deleted file mode 100644
index 276d72d..0000000
--- a/apps/playground/local.netease.com.pem
+++ /dev/null
@@ -1,25 +0,0 @@
------BEGIN CERTIFICATE-----
-MIIEQjCCAqqgAwIBAgIRAIeLJ2jwt4MSKycHFmFXL6YwDQYJKoZIhvcNAQELBQAw
-fTEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMSkwJwYDVQQLDCB3d3N1
-bkBzdW4tbWJwLmxvY2FsIChTVU4gV2Vpd2VpKTEwMC4GA1UEAwwnbWtjZXJ0IHd3
-c3VuQHN1bi1tYnAubG9jYWwgKFNVTiBXZWl3ZWkpMB4XDTIyMDcyODAxNTIxMFoX
-DTI0MTAyODAxNTIxMFowVDEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNl
-cnRpZmljYXRlMSkwJwYDVQQLDCB3d3N1bkBzdW4tbWJwLmxvY2FsIChTVU4gV2Vp
-d2VpKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALiwCoMTeeG1JYNi
-pXiaWCWfMizYCKhmva7F+P84qukTVkwv91+UnJzk0ZSk5lZX5bW9FzvTBZU0XEFK
-Lyetiuux6ggPsSHHF7r4nWb8mwRuFlYfBXczWIZbskPtWG6NoOlIuDrvAxMylJbc
-FRL7Q1P4w18KKyn67+Wzh9c8JOBeJZyMxQub3mguVN5jC6s8d/HCf1/2GHzkjkgr
-d0jvDwyxA3sQU2snPdR+s0umYBEy1JQEzAFiqsKK2hTFFPSrspgKu7uQfEY8HoBJ
-CsWlKk5ZPv2+nwYFKn2DdrGFGwr5oSDNDRAasU8cHAE/+RaCZ0HVRFz8rtJNIL3H
-Iw1k4zsCAwEAAaNmMGQwDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUF
-BwMBMB8GA1UdIwQYMBaAFDILZR+SW32Ug0HkAKrH1KT2a1ttMBwGA1UdEQQVMBOC
-EWxvY2FsLm5ldGVhc2UuY29tMA0GCSqGSIb3DQEBCwUAA4IBgQCEdeV2Likl4MUt
-wWeN/X3tAR3L7mkl7e4f0CQ4v2lOs8cekOP7p3ZFk5nSyVbxda3usPB0OtbVTibw
-qZ1n/TMRb5epHroXIMleqngP82zFZNg+WVyHm/ZAml8gVD0VZjYM1Tb+U9+SrgGw
-lvZndbuUXa3h/d0irPH90KsdEeAR1SqwzAzvLO2JgvBuimHRceQWcTiiGWDZOyii
-tux4U+pEzh0Dia40uFozFAoYZPDUpZ+gsiQ3MR3HOrAu2OR0a+CfelDXZNLR3C4z
-cBKr53ajAbHstnpxKJh9IfeKph44PrbDo6HMe1XPprl7idoEesX3Ma3SBgm4lmId
-cfEOUWNIYOgtOT5bflXNNyzXOgYVjpLnjwg4EOASO/XIjhvqdjIyCu7HJMIcZ9oo
-Aob2exeluv5LVwic1UJQDl2McDBIgJ8R32oEN4skCKIBSbWlxWyjnxL5I5WkJF4y
-vlSx8bKAjVr8map0C3rWISuZdgheJkF96qjwhVSxOv++p6srq6M=
------END CERTIFICATE-----
diff --git a/apps/playground/src/pages/index.tsx b/apps/playground/src/pages/index.tsx
index 4d41fd0..81c8fa0 100644
--- a/apps/playground/src/pages/index.tsx
+++ b/apps/playground/src/pages/index.tsx
@@ -187,6 +187,9 @@ export default function App() {
/>
+
+
+
{
@@ -208,9 +211,6 @@ export default function App() {
navigatorExtra={}
/>
-
-
-
diff --git a/packages/core/src/models/designer.ts b/packages/core/src/models/designer.ts
index 91c3975..f8989e9 100644
--- a/packages/core/src/models/designer.ts
+++ b/packages/core/src/models/designer.ts
@@ -239,6 +239,13 @@ export class Designer {
setActiveView(view: DesignerViewType) {
this._activeView = view;
+ if (view === 'dual') {
+ this.setActiveSidebarPanel('');
+ this.toggleRightPanel(false);
+ this.toggleIsPreview(true);
+ } else if (view === 'design') {
+ this.toggleIsPreview(false);
+ }
}
setActiveSidebarPanel(panel: string) {
diff --git a/packages/core/src/models/workspace.ts b/packages/core/src/models/workspace.ts
index d57a650..f39857c 100644
--- a/packages/core/src/models/workspace.ts
+++ b/packages/core/src/models/workspace.ts
@@ -442,7 +442,9 @@ export class Workspace extends EventTarget implements IWorkspace {
updateFile(filename: string, code: string, shouldFormatCode = false) {
const file = this.getFile(filename);
file.update(code);
- if (shouldFormatCode && file instanceof TangoViewModule) {
+
+ const shouldFormat = shouldFormatCode ?? this.projectConfig?.designerConfig?.autoFormatCode;
+ if (shouldFormat && file instanceof TangoViewModule) {
file.removeUnusedImportSpecifiers().update();
}
this.history.push({
diff --git a/packages/designer/src/editor.tsx b/packages/designer/src/editor.tsx
index 084f951..a6bccd1 100644
--- a/packages/designer/src/editor.tsx
+++ b/packages/designer/src/editor.tsx
@@ -23,113 +23,111 @@ const ideConfig = {
export interface CodeEditorProps extends Partial {
/**
- * 是否自动清楚未使用的导入
+ * 是否自动清除未使用的导入
*/
autoRemoveUnusedImports?: boolean;
}
-export const CodeEditor = observer(
- ({ autoRemoveUnusedImports = true, ...rest }: CodeEditorProps) => {
- const editorRef = useRef(null);
- const workspace = useWorkspace();
- const designer = useDesigner();
- const files = workspace.listFiles();
- const activeFile = workspace.activeFile;
+export const CodeEditor = observer(({ autoRemoveUnusedImports, ...rest }: CodeEditorProps) => {
+ const editorRef = useRef(null);
+ const workspace = useWorkspace();
+ const designer = useDesigner();
+ const files = workspace.listFiles();
+ const activeFile = workspace.activeFile;
- let loc: any; // 记录视图代码的选中位置
- const selectNode = workspace.selectSource.firstNode;
- if (selectNode && activeFile === workspace.activeViewFile) {
- loc = selectNode.loc;
- }
+ let loc: any; // 记录视图代码的选中位置
+ const selectNode = workspace.selectSource.firstNode;
+ if (selectNode && activeFile === workspace.activeViewFile) {
+ loc = selectNode.loc;
+ }
- useEffect(() => {
- editorRef.current?.refresh(files, activeFile, loc);
- }, [files, activeFile, loc]);
+ useEffect(() => {
+ editorRef.current?.refresh(files, activeFile, loc);
+ }, [files, activeFile, loc]);
- const fileSave = useCallback(
- (path: string, value: string) => {
- if (!isJsFile(path)) {
- // 非 js 文件直接保存
- workspace.updateFile(path, value, autoRemoveUnusedImports);
- return;
+ const fileSave = useCallback(
+ (path: string, value: string) => {
+ if (!isJsFile(path)) {
+ // 非 js 文件直接保存
+ workspace.updateFile(path, value, autoRemoveUnusedImports);
+ return;
+ }
+
+ // js 文件需要先检查语法,只有语法正确才会保存
+ if (isValidCode(value)) {
+ workspace.updateFile(path, value, autoRemoveUnusedImports);
+ } else {
+ Modal.confirm({
+ title: '检测到代码中存在语法错误,暂时无法将代码同步给设计器,是否回退到安全代码?',
+ onOk: () => {
+ editorRef.current?.refresh(files, activeFile);
+ },
+ onCancel: () => {},
+ });
+ }
+ },
+ [workspace, autoRemoveUnusedImports, activeFile, files],
+ );
+
+ const handleFileChange = useCallback(
+ (type: string, info: any) => {
+ switch (type) {
+ case 'addFile':
+ workspace.addFile(info.path, info.value);
+ break;
+ case 'deleteFile':
+ case 'deleteFolder':
+ workspace.removeFile(info.path);
+ break;
+ case 'renameFile':
+ workspace.renameFile(info.path, info.newpath);
+ workspace.setActiveFile(info.newpath);
+ break;
+ case 'renameFolder':
+ workspace.renameFolder(info.path, info.newpath);
+ break;
+ case 'addFolder':
+ default:
+ break;
+ }
+ },
+ [workspace],
+ );
+
+ const handlePathChange = useCallback(
+ (path: string) => {
+ workspace.setActiveFile(path);
+ },
+ [workspace],
+ );
+
+ const borderStyle =
+ designer.activeView === 'dual'
+ ? {
+ borderLeft: 'solid 1px var(--tango-colors-line2)',
}
+ : {};
- // js 文件需要先检查语法,只有语法正确才会保存
- if (isValidCode(value)) {
- workspace.updateFile(path, value, autoRemoveUnusedImports);
- } else {
- Modal.confirm({
- title: '检测到代码中存在语法错误,暂时无法将代码同步给设计器,是否回退到安全代码?',
- onOk: () => {
- editorRef.current?.refresh(files, activeFile);
- },
- onCancel: () => {},
- });
- }
- },
- [workspace, autoRemoveUnusedImports, activeFile, files],
- );
-
- const handleFileChange = useCallback(
- (type: string, info: any) => {
- switch (type) {
- case 'addFile':
- workspace.addFile(info.path, info.value);
- break;
- case 'deleteFile':
- case 'deleteFolder':
- workspace.removeFile(info.path);
- break;
- case 'renameFile':
- workspace.renameFile(info.path, info.newpath);
- workspace.setActiveFile(info.newpath);
- break;
- case 'renameFolder':
- workspace.renameFolder(info.path, info.newpath);
- break;
- case 'addFolder':
- default:
- break;
- }
- },
- [workspace],
- );
-
- const handlePathChange = useCallback(
- (path: string) => {
- workspace.setActiveFile(path);
- },
- [workspace],
- );
-
- const borderStyle =
- designer.activeView === 'dual'
- ? {
- borderLeft: 'solid 1px var(--tango-colors-line2)',
- }
- : {};
-
- return (
-
-
-
- );
- },
-);
+ return (
+
+
+
+ );
+});
function isJsFile(path: string) {
return /.jsx?$/.test(path);