Repository: gethexon/hexon Branch: master Commit: 45743c1f5580 Files: 557 Total size: 13.5 MB Directory structure: gitextract_2kg3lz_o/ ├── .github/ │ └── ISSUE_TEMPLATE/ │ └── bug.yaml ├── .gitignore ├── .prettierignore ├── .prettierrc ├── .vscode/ │ └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── README_CN.md ├── client/ │ ├── .demo/ │ │ ├── App.vue │ │ ├── Layout.vue │ │ ├── Welcome.vue │ │ ├── index.html │ │ ├── main.ts │ │ ├── router.ts │ │ ├── utils.ts │ │ └── vite.config.ts │ ├── .vscode/ │ │ └── extensions.json │ ├── README.md │ ├── dist/ │ │ ├── assets/ │ │ │ ├── HCreateArticleModal.079bef88.js │ │ │ ├── HCreateArticleModal.079bef88.js.br │ │ │ ├── HCreateArticleModal.3ac45e74.css │ │ │ ├── HMonacoEditor.213cf05a.css │ │ │ ├── HMonacoEditor.213cf05a.css.br │ │ │ ├── HMonacoEditor.4fbcd301.js │ │ │ ├── HMonacoEditor.4fbcd301.js.br │ │ │ ├── HPopover.d0eeed1b.js │ │ │ ├── HPopover.d0eeed1b.js.br │ │ │ ├── HPopover.f93dedac.css │ │ │ ├── HSettingsModal.b6bd3c3a.js │ │ │ ├── HToggle.5d828612.js │ │ │ ├── HToggle.5d828612.js.br │ │ │ ├── HToggle.c1b32eab.css │ │ │ ├── SettingsView.49df3ee8.js │ │ │ ├── SettingsView.49df3ee8.js.br │ │ │ ├── SettingsView.ac9e9c18.css │ │ │ ├── _source_.0a6f257a.css │ │ │ ├── _source_.cd90784b.js │ │ │ ├── _source_.cd90784b.js.br │ │ │ ├── abap.d6ef40ee.js │ │ │ ├── abap.d6ef40ee.js.br │ │ │ ├── apex.7f8de8aa.js │ │ │ ├── apex.7f8de8aa.js.br │ │ │ ├── azcli.0519e511.js │ │ │ ├── azcli.0519e511.js.br │ │ │ ├── bat.f2d79e9d.js │ │ │ ├── bat.f2d79e9d.js.br │ │ │ ├── bicep.e8099262.js │ │ │ ├── bicep.e8099262.js.br │ │ │ ├── cameligo.180434f7.js │ │ │ ├── cameligo.180434f7.js.br │ │ │ ├── clojure.c88913fe.js │ │ │ ├── clojure.c88913fe.js.br │ │ │ ├── coffee.8a1694f5.js │ │ │ ├── coffee.8a1694f5.js.br │ │ │ ├── cpp.c409f790.js │ │ │ ├── cpp.c409f790.js.br │ │ │ ├── csharp.41a26d75.js │ │ │ ├── csharp.41a26d75.js.br │ │ │ ├── csp.a35fa773.js │ │ │ ├── csp.a35fa773.js.br │ │ │ ├── css.c24c8988.js │ │ │ ├── css.c24c8988.js.br │ │ │ ├── css.worker.57c3cb89.js │ │ │ ├── css.worker.57c3cb89.js.br │ │ │ ├── cssMode.51a330cf.js │ │ │ ├── cssMode.51a330cf.js.br │ │ │ ├── dart.320b7cd1.js │ │ │ ├── dart.320b7cd1.js.br │ │ │ ├── dockerfile.a340fefe.js │ │ │ ├── dockerfile.a340fefe.js.br │ │ │ ├── ecl.311d1611.js │ │ │ ├── ecl.311d1611.js.br │ │ │ ├── editor.worker.6369e042.js │ │ │ ├── editor.worker.6369e042.js.br │ │ │ ├── elixir.f66025dc.js │ │ │ ├── elixir.f66025dc.js.br │ │ │ ├── flow9.2c0e9b6f.js │ │ │ ├── flow9.2c0e9b6f.js.br │ │ │ ├── freemarker2.355133f8.js │ │ │ ├── freemarker2.355133f8.js.br │ │ │ ├── fsharp.da385d01.js │ │ │ ├── fsharp.da385d01.js.br │ │ │ ├── go.b5c8acf0.js │ │ │ ├── go.b5c8acf0.js.br │ │ │ ├── graphql.031cd53e.js │ │ │ ├── graphql.031cd53e.js.br │ │ │ ├── handlebars.0696c272.js │ │ │ ├── handlebars.0696c272.js.br │ │ │ ├── hcl.2a98b883.js │ │ │ ├── hcl.2a98b883.js.br │ │ │ ├── html.1d2c5b54.js │ │ │ ├── html.1d2c5b54.js.br │ │ │ ├── html.worker.ae09e20d.js │ │ │ ├── html.worker.ae09e20d.js.br │ │ │ ├── htmlMode.48899aa2.js │ │ │ ├── htmlMode.48899aa2.js.br │ │ │ ├── index.becaf6b8.js │ │ │ ├── index.becaf6b8.js.br │ │ │ ├── index.e0fa4e9b.css │ │ │ ├── index.e0fa4e9b.css.br │ │ │ ├── ini.b415f46c.js │ │ │ ├── ini.b415f46c.js.br │ │ │ ├── java.f01bc6f9.js │ │ │ ├── java.f01bc6f9.js.br │ │ │ ├── javascript.b56ed86b.js │ │ │ ├── javascript.b56ed86b.js.br │ │ │ ├── json.worker.cc26763b.js │ │ │ ├── json.worker.cc26763b.js.br │ │ │ ├── jsonMode.a02fce60.js │ │ │ ├── jsonMode.a02fce60.js.br │ │ │ ├── julia.033b1357.js │ │ │ ├── julia.033b1357.js.br │ │ │ ├── kotlin.f702729c.js │ │ │ ├── kotlin.f702729c.js.br │ │ │ ├── less.ca69536d.js │ │ │ ├── less.ca69536d.js.br │ │ │ ├── lexon.98577e19.js │ │ │ ├── lexon.98577e19.js.br │ │ │ ├── liquid.c793915f.js │ │ │ ├── liquid.c793915f.js.br │ │ │ ├── logo.0fab0ac6.js │ │ │ ├── lua.fdd96ef3.js │ │ │ ├── lua.fdd96ef3.js.br │ │ │ ├── m3.696565eb.js │ │ │ ├── m3.696565eb.js.br │ │ │ ├── markdown.c830577a.js │ │ │ ├── markdown.c830577a.js.br │ │ │ ├── mips.5568383b.js │ │ │ ├── mips.5568383b.js.br │ │ │ ├── msdax.07a600d0.js │ │ │ ├── msdax.07a600d0.js.br │ │ │ ├── mysql.7908aeda.js │ │ │ ├── mysql.7908aeda.js.br │ │ │ ├── objective-c.09d904b0.js │ │ │ ├── objective-c.09d904b0.js.br │ │ │ ├── pascal.50da1bc1.js │ │ │ ├── pascal.50da1bc1.js.br │ │ │ ├── pascaligo.244049b6.js │ │ │ ├── pascaligo.244049b6.js.br │ │ │ ├── perl.649d12f9.js │ │ │ ├── perl.649d12f9.js.br │ │ │ ├── pgsql.5f6e4267.js │ │ │ ├── pgsql.5f6e4267.js.br │ │ │ ├── php.052c065c.js │ │ │ ├── php.052c065c.js.br │ │ │ ├── pla.bb236946.js │ │ │ ├── pla.bb236946.js.br │ │ │ ├── postiats.14dcf648.js │ │ │ ├── postiats.14dcf648.js.br │ │ │ ├── powerquery.9eb89934.js │ │ │ ├── powerquery.9eb89934.js.br │ │ │ ├── powershell.8fd167fe.js │ │ │ ├── powershell.8fd167fe.js.br │ │ │ ├── protobuf.fc81f9f6.js │ │ │ ├── protobuf.fc81f9f6.js.br │ │ │ ├── pug.16a0c97a.js │ │ │ ├── pug.16a0c97a.js.br │ │ │ ├── python.b2175380.js │ │ │ ├── python.b2175380.js.br │ │ │ ├── qsharp.657dcf33.js │ │ │ ├── qsharp.657dcf33.js.br │ │ │ ├── r.2f32f10f.js │ │ │ ├── r.2f32f10f.js.br │ │ │ ├── razor.ef01513e.js │ │ │ ├── razor.ef01513e.js.br │ │ │ ├── redis.9808fa4a.js │ │ │ ├── redis.9808fa4a.js.br │ │ │ ├── redshift.d34bf7aa.js │ │ │ ├── redshift.d34bf7aa.js.br │ │ │ ├── restructuredtext.f3690597.js │ │ │ ├── restructuredtext.f3690597.js.br │ │ │ ├── ruby.94797058.js │ │ │ ├── ruby.94797058.js.br │ │ │ ├── rust.8826d0a9.js │ │ │ ├── rust.8826d0a9.js.br │ │ │ ├── sb.4bb8b85c.js │ │ │ ├── sb.4bb8b85c.js.br │ │ │ ├── scala.87a6558f.js │ │ │ ├── scala.87a6558f.js.br │ │ │ ├── scheme.0544bd16.js │ │ │ ├── scheme.0544bd16.js.br │ │ │ ├── scss.646efa7b.js │ │ │ ├── scss.646efa7b.js.br │ │ │ ├── shell.bafd6f5f.js │ │ │ ├── shell.bafd6f5f.js.br │ │ │ ├── signin.b6a1b593.js │ │ │ ├── signin.b6a1b593.js.br │ │ │ ├── solidity.79ca2cec.js │ │ │ ├── solidity.79ca2cec.js.br │ │ │ ├── sophia.0a583064.js │ │ │ ├── sophia.0a583064.js.br │ │ │ ├── sparql.7c6df35b.js │ │ │ ├── sparql.7c6df35b.js.br │ │ │ ├── sql.377e3088.js │ │ │ ├── sql.377e3088.js.br │ │ │ ├── st.4b380b23.js │ │ │ ├── st.4b380b23.js.br │ │ │ ├── swift.e0a9351c.js │ │ │ ├── swift.e0a9351c.js.br │ │ │ ├── systemverilog.4c73c512.js │ │ │ ├── systemverilog.4c73c512.js.br │ │ │ ├── tcl.dbdb133c.js │ │ │ ├── tcl.dbdb133c.js.br │ │ │ ├── ts.worker.bcb033c8.js │ │ │ ├── ts.worker.bcb033c8.js.br │ │ │ ├── tsMode.6b562640.js │ │ │ ├── tsMode.6b562640.js.br │ │ │ ├── twig.79989254.js │ │ │ ├── twig.79989254.js.br │ │ │ ├── typescript.0e1e0a69.js │ │ │ ├── typescript.0e1e0a69.js.br │ │ │ ├── unauthorized.18917336.js │ │ │ ├── vb.cce9947c.js │ │ │ ├── vb.cce9947c.js.br │ │ │ ├── xml.7fe3cde4.js │ │ │ ├── xml.7fe3cde4.js.br │ │ │ ├── yaml.e0b57566.js │ │ │ └── yaml.e0b57566.js.br │ │ ├── index.html │ │ └── robots.txt │ ├── docs/ │ │ ├── HArticleItem.md │ │ ├── HArticleList.md │ │ ├── HHeaderEditor.md │ │ ├── HImage.md │ │ ├── HInstallForm.md │ │ ├── HLoginForm.md │ │ ├── HMonacoEditor.md │ │ ├── HNavList.md │ │ ├── HNavSetting.md │ │ ├── HNotification.md │ │ ├── HNotificationItem.md │ │ ├── HSearchBar.md │ │ ├── HTitle.md │ │ ├── HToolbar.md │ │ ├── HViewerContent.md │ │ ├── HViewerHeader.md │ │ ├── HViewerToolbar.md │ │ ├── helper.ts │ │ ├── hexo-example.ts │ │ ├── index.md │ │ └── markdown-example.ts │ ├── find-unused-file-plugin.ts │ ├── index.html │ ├── jest.config.js │ ├── package.json │ ├── public/ │ │ └── robots.txt │ ├── src/ │ │ ├── App.vue │ │ ├── api/ │ │ │ ├── auth.ts │ │ │ ├── entities.ts │ │ │ ├── http-api-provider.ts │ │ │ ├── index.ts │ │ │ ├── instance.ts │ │ │ ├── interface.ts │ │ │ ├── settings.ts │ │ │ └── template.ts │ │ ├── components/ │ │ │ ├── DemoPad.vue │ │ │ ├── HEditorToolbar.vue │ │ │ ├── HImage.vue │ │ │ ├── HNavSetting.vue │ │ │ ├── HSearchBar.vue │ │ │ ├── HTitle.vue │ │ │ ├── HToolbar.vue │ │ │ ├── article/ │ │ │ │ ├── HArticleItem.vue │ │ │ │ ├── HArticleList.vue │ │ │ │ ├── HArticleMenu.vue │ │ │ │ ├── interface.ts │ │ │ │ └── utils.ts │ │ │ ├── editors/ │ │ │ │ ├── FrontMatterTemplate.vue │ │ │ │ ├── FrontMatterTemplateEdit.vue │ │ │ │ ├── HCategoriesEditor.vue │ │ │ │ ├── HDateEditor.vue │ │ │ │ ├── HFrontmatterEditor.vue │ │ │ │ ├── HHeaderEditor.vue │ │ │ │ ├── HLayoutEditor.vue │ │ │ │ ├── HMonacoEditor.vue │ │ │ │ ├── HTagEditor.vue │ │ │ │ ├── custom-monaco.ts │ │ │ │ ├── markdown-image-ext.ts │ │ │ │ ├── monaco-markdown/ │ │ │ │ │ ├── completion.ts │ │ │ │ │ ├── contribution.ts │ │ │ │ │ ├── errors.ts │ │ │ │ │ ├── extHostTypes.ts │ │ │ │ │ ├── formatting.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── latex.ts │ │ │ │ │ ├── listEditing.ts │ │ │ │ │ ├── markdown.contribution.ts │ │ │ │ │ ├── markdown.ts │ │ │ │ │ ├── tableFormatter.ts │ │ │ │ │ ├── toc.ts │ │ │ │ │ ├── util.ts │ │ │ │ │ ├── vscode-common.ts │ │ │ │ │ ├── vscode-converters.ts │ │ │ │ │ ├── vscode-monaco.ts │ │ │ │ │ ├── vscode-utils.ts │ │ │ │ │ └── wordHelper.ts │ │ │ │ ├── monaco.ts │ │ │ │ ├── prettier-formatter-ext.ts │ │ │ │ ├── prettier-formatter.spec.ts │ │ │ │ ├── prettier-formatter.ts │ │ │ │ ├── theme.ts │ │ │ │ └── workers.ts │ │ │ ├── forms/ │ │ │ │ ├── HChangePasswordForm.vue │ │ │ │ ├── HChangeUsernameForm.vue │ │ │ │ ├── HCreateArticleForm.vue │ │ │ │ ├── HLoginForm.vue │ │ │ │ ├── demo/ │ │ │ │ │ ├── change-password.demo.vue │ │ │ │ │ └── create-article.demo.vue │ │ │ │ └── interface.ts │ │ │ ├── modals/ │ │ │ │ ├── HCreateArticleModal.vue │ │ │ │ ├── HHexoInitFailModal.vue │ │ │ │ ├── HSettingsModal.vue │ │ │ │ └── hexo-init-fail-modal.ts │ │ │ ├── others/ │ │ │ │ ├── HDialog.vue │ │ │ │ └── HNotificationItem.vue │ │ │ ├── transitions/ │ │ │ │ ├── FadeScaleTransitionGroup.vue │ │ │ │ ├── FadeTransition.vue │ │ │ │ ├── FadeTransitionGroup.vue │ │ │ │ ├── TranslateDownTransitionGroup.vue │ │ │ │ ├── TranslateTransitionGroup.vue │ │ │ │ └── interface.ts │ │ │ ├── types.ts │ │ │ ├── ui/ │ │ │ │ ├── badge/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HBadge.vue │ │ │ │ ├── button/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── HButton.vue │ │ │ │ │ │ └── interface.ts │ │ │ │ │ └── styles/ │ │ │ │ │ ├── dark.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── light.ts │ │ │ │ ├── checkbox/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HCheckbox.vue │ │ │ │ ├── date-picker/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── HDatePicker.vue │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── utils.spec.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── divider/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HDivider.vue │ │ │ │ ├── icon/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ ├── default.demo.vue │ │ │ │ │ │ └── icon-map.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── HIcon.vue │ │ │ │ │ └── interface.ts │ │ │ │ ├── input/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── HInput.vue │ │ │ │ │ │ └── interface.ts │ │ │ │ │ └── styles/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── light.ts │ │ │ │ ├── loading/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── src/ │ │ │ │ │ │ └── HLoading.vue │ │ │ │ │ └── styles/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── light.ts │ │ │ │ ├── modal/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── HBaseModal.vue │ │ │ │ │ ├── HModal.vue │ │ │ │ │ └── ModalBackground.vue │ │ │ │ ├── nav-list/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ ├── default.demo.vue │ │ │ │ │ │ ├── nav-item.demo.vue │ │ │ │ │ │ └── nav-title.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── src/ │ │ │ │ │ │ ├── HNavItem.vue │ │ │ │ │ │ ├── HNavList.vue │ │ │ │ │ │ ├── HNavTitle.vue │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── interface.ts │ │ │ │ │ └── styles/ │ │ │ │ │ ├── dark.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── light.ts │ │ │ │ ├── popover/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── HPopover.vue │ │ │ │ │ ├── get-placement.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ └── utils.ts │ │ │ │ ├── slide-view/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── HSlideView.vue │ │ │ │ │ └── interface.ts │ │ │ │ ├── slider/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HSlider.vue │ │ │ │ ├── textarea/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HTextarea.vue │ │ │ │ ├── theme/ │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ ├── common/ │ │ │ │ │ │ ├── dark.ts │ │ │ │ │ │ └── light.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── interface.ts │ │ │ │ │ ├── lib.ts │ │ │ │ │ └── themes/ │ │ │ │ │ ├── dark.ts │ │ │ │ │ ├── light.ts │ │ │ │ │ └── unknown.ts │ │ │ │ ├── toggle/ │ │ │ │ │ ├── demo/ │ │ │ │ │ │ └── default.demo.vue │ │ │ │ │ ├── index.ts │ │ │ │ │ └── src/ │ │ │ │ │ └── HToggle.vue │ │ │ │ └── vertical-center/ │ │ │ │ ├── index.ts │ │ │ │ └── src/ │ │ │ │ └── HVerticalCenter.vue │ │ │ └── viewer/ │ │ │ ├── HViewerContent.vue │ │ │ ├── HViewerHeader.vue │ │ │ └── HViewerToolbar.vue │ │ ├── composables/ │ │ │ └── cookies.ts │ │ ├── env.d.ts │ │ ├── errors/ │ │ │ └── index.ts │ │ ├── ext.d.ts │ │ ├── interface.ts │ │ ├── layouts/ │ │ │ ├── default.vue │ │ │ └── unauthorized.vue │ │ ├── lib/ │ │ │ ├── async-queue/ │ │ │ │ └── index.ts │ │ │ ├── dialog/ │ │ │ │ ├── demo/ │ │ │ │ │ └── default.demo.vue │ │ │ │ ├── index.ts │ │ │ │ └── src/ │ │ │ │ ├── DialogContainer.vue │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ └── lib.ts │ │ │ ├── http-secure/ │ │ │ │ ├── index.ts │ │ │ │ └── src/ │ │ │ │ └── index.ts │ │ │ ├── list2tree/ │ │ │ │ ├── index.spec.ts │ │ │ │ └── index.ts │ │ │ ├── loading/ │ │ │ │ └── index.ts │ │ │ ├── modal/ │ │ │ │ ├── index.ts │ │ │ │ └── src/ │ │ │ │ ├── ModalContainer.vue │ │ │ │ └── index.ts │ │ │ ├── notification/ │ │ │ │ ├── index.ts │ │ │ │ └── src/ │ │ │ │ ├── FadeTransitionGroup.vue │ │ │ │ ├── Notification.vue │ │ │ │ ├── index.ts │ │ │ │ ├── interface.ts │ │ │ │ ├── lib.spec.ts │ │ │ │ └── lib.ts │ │ │ └── splitview/ │ │ │ ├── SplitView.vue │ │ │ ├── index.ts │ │ │ ├── useEventListener.ts │ │ │ └── useResizeObserver.ts │ │ ├── main.ts │ │ ├── pages/ │ │ │ ├── edit/ │ │ │ │ └── [type]/ │ │ │ │ └── [source].vue │ │ │ ├── index/ │ │ │ │ └── view/ │ │ │ │ └── [type]/ │ │ │ │ └── [source].vue │ │ │ ├── index.vue │ │ │ └── signin.vue │ │ ├── plugins/ │ │ │ ├── dayjs.ts │ │ │ ├── dialog.ts │ │ │ ├── loading.ts │ │ │ ├── modal.ts │ │ │ ├── notification.ts │ │ │ ├── pinia.ts │ │ │ ├── router.ts │ │ │ └── theme.ts │ │ ├── store/ │ │ │ ├── actions.ts │ │ │ ├── articleList.ts │ │ │ ├── detail.ts │ │ │ ├── dispatcher.ts │ │ │ ├── main.ts │ │ │ └── settings.ts │ │ ├── styles/ │ │ │ ├── font.less │ │ │ ├── index.less │ │ │ └── mixins.less │ │ ├── themes.ts │ │ ├── utils/ │ │ │ ├── article.ts │ │ │ ├── color.ts │ │ │ ├── create-classnames.ts │ │ │ ├── create-key.ts │ │ │ ├── date.spec.ts │ │ │ ├── date.ts │ │ │ ├── hfm.ts │ │ │ ├── parent.ts │ │ │ ├── preload.ts │ │ │ ├── scroll.ts │ │ │ └── string.ts │ │ ├── utils.spec.ts │ │ ├── utils.ts │ │ └── views/ │ │ ├── ArticleListView.vue │ │ ├── ErroredView.vue │ │ ├── HomeNavView.vue │ │ ├── ViewerView.vue │ │ └── settings/ │ │ ├── SettingsTabContainer.vue │ │ ├── SettingsView.vue │ │ ├── SignoutButton.vue │ │ ├── TabSwitcher.vue │ │ ├── index.ts │ │ ├── interface.ts │ │ ├── nav-config.ts │ │ └── tabs/ │ │ ├── AboutView.vue │ │ ├── HelpView.vue │ │ ├── SecurityView.vue │ │ ├── StyleView.vue │ │ └── UserView.vue │ ├── tailwind.config.js │ ├── tsconfig.json │ └── vite.config.ts ├── eslint.config.mjs ├── package.json ├── pnpm-workspace.yaml ├── scripts/ │ └── fresh_installation.test.sh ├── server/ │ ├── README.md │ ├── bin/ │ │ └── index.js │ ├── dist/ │ │ └── index.js │ ├── jest.config.js │ ├── nodemon.json │ ├── package.json │ ├── scripts/ │ │ └── build.mjs │ └── src/ │ ├── app.ts │ ├── errors.ts │ ├── index.ts │ ├── lib/ │ │ └── http-secure.ts │ ├── middlewares/ │ │ ├── auth.ts │ │ └── statics.ts │ ├── routes/ │ │ ├── git.ts │ │ ├── health.ts │ │ ├── hexo.ts │ │ ├── index.ts │ │ ├── settings.ts │ │ └── template.ts │ ├── services/ │ │ ├── auth-storage-service.ts │ │ ├── block-service.ts │ │ ├── exec-service.ts │ │ ├── frontmatter-template-service.ts │ │ ├── git-service.ts │ │ ├── hexo-instance-service.ts │ │ ├── hexo-service.ts │ │ └── settings-service.ts │ ├── types/ │ │ └── node-jsencrypt/ │ │ └── index.d.ts │ ├── utils/ │ │ ├── exec.ts │ │ └── hexo.ts │ └── utils.ts ├── server-scripts/ │ ├── assets/ │ │ └── logo.art │ ├── bin/ │ │ └── index.js │ ├── package.json │ ├── scripts/ │ │ └── build.mjs │ └── src/ │ ├── constants.ts │ ├── index.ts │ ├── install.ts │ ├── prompts.ts │ ├── reset-password.ts │ ├── script.ts │ └── utils.ts ├── server-shared/ │ ├── package.json │ └── src/ │ ├── account-storage-service.ts │ ├── constants.ts │ ├── env-service.ts │ ├── log-service.ts │ ├── storage-service.ts │ ├── store.ts │ └── utils.ts ├── shared/ │ ├── package.json │ └── src/ │ ├── constants.ts │ └── types/ │ ├── api.ts │ └── hexo.ts └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/ISSUE_TEMPLATE/bug.yaml ================================================ name: Bug Report description: File a bug report. title: "bug: " labels: ["bug"] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! - type: textarea id: what-happened attributes: label: What happened? description: Also tell us, what did you expect to happen? placeholder: | After I did X1, X2 and X3, Y happened. I expected Z. validations: required: true - type: textarea id: reproduction attributes: label: Reproduction description: | Please provide a minimal reproduction of the bug. placeholder: | Step1: do X Step2: do Y Step3: do Z and the bug happens - type: textarea id: version attributes: label: Version description: What version of the software are you using? value: | hexo: x.x.x hexon: x.x.x your-hexo-theme: your-theme@x.x.x - type: checkboxes id: before-submit attributes: label: Before you submit description: | Please check the following options: - label: I have included version information required: true ================================================ FILE: .gitignore ================================================ node_modules *.log stats.html data log .DS_Store *.local .env ================================================ FILE: .prettierignore ================================================ bin dist CHANGELOG.md lerna.json #gitignore node_modules *.log stats.html data log .DS_Store *.local ================================================ FILE: .prettierrc ================================================ { "semi": false, "htmlWhitespaceSensitivity": "ignore" } ================================================ FILE: .vscode/settings.json ================================================ { // Disable the default formatter, use eslint instead "prettier.enable": false, "editor.formatOnSave": false, // Auto fix "editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit", "source.organizeImports": "never" }, // Silent the stylistic rules in you IDE, but still auto fix them "eslint.rules.customizations": [ { "rule": "style/*", "severity": "off", "fixable": true }, { "rule": "format/*", "severity": "off", "fixable": true }, { "rule": "*-indent", "severity": "off", "fixable": true }, { "rule": "*-spacing", "severity": "off", "fixable": true }, { "rule": "*-spaces", "severity": "off", "fixable": true }, { "rule": "*-order", "severity": "off", "fixable": true }, { "rule": "*-dangle", "severity": "off", "fixable": true }, { "rule": "*-newline", "severity": "off", "fixable": true }, { "rule": "*quotes", "severity": "off", "fixable": true }, { "rule": "*semi", "severity": "off", "fixable": true } ], // Enable eslint for all supported languages "eslint.validate": [ "javascript", "javascriptreact", "typescript", "typescriptreact", "vue", "html", "markdown", "json", "jsonc", "yaml", "toml", "xml", "gql", "graphql", "astro", "css", "less", "scss", "pcss", "postcss" ] } ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. ## [1.0.0](https://github.com/gethexon/hexon/compare/v0.12.8...v1.0.0) (2026-03-21) ### ⚠ BREAKING CHANGES * conflict with system wild env ### Bug Fixes * conflict with system wild env ([5c81e1b](https://github.com/gethexon/hexon/commit/5c81e1bbe977d6d84562a0e6f3a04e53d8f5b02c)) * date format ([2f80a76](https://github.com/gethexon/hexon/commit/2f80a766646af30710a747c45dc474ace7254b1d)) ### [0.12.8](https://github.com/gethexon/hexon/compare/v0.12.7...v0.12.8) (2024-07-11) ### [0.12.7](https://github.com/gethexon/hexon/compare/v0.12.6...v0.12.7) (2024-06-07) ### Bug Fixes * article type ([e4dd713](https://github.com/gethexon/hexon/commit/e4dd7135470558659bcc47cb9ddf9d199bd1425d)) * show error message when auth failed ([83b4cda](https://github.com/gethexon/hexon/commit/83b4cdaa8ed91878911fbe0fbd40846ce61baf0b)) ### [0.12.6](https://github.com/gethexon/hexon/compare/v0.12.5...v0.12.6) (2023-09-27) ### Features * install via env ([f0b91aa](https://github.com/gethexon/hexon/commit/f0b91aa39d4f7ab8f050f4ed8c470843299c5bc2)) ### Bug Fixes * 修正成正確的專案名稱 ([fb38e7b](https://github.com/gethexon/hexon/commit/fb38e7bcb93b7c7c0e3bf7aea60ab685af0c8bba)) * 因缺少 type 檢查導致回傳值缺少 Article 屬性的問題 ([#36](https://github.com/gethexon/hexon/issues/36)) ([e75fea6](https://github.com/gethexon/hexon/commit/e75fea693248849a18a8dd1dbb0299416e9b52db)) ### [0.12.5](https://github.com/gethexon/hexon/compare/v0.12.4...v0.12.5) (2023-03-28) ### [0.12.4](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.4-0...v0.12.4) (2022-06-23) ### [0.12.4-0](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.3...v0.12.4-0) (2022-06-23) ### [0.12.3](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.3-0...v0.12.3) (2022-05-15) ### [0.12.3-0](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.2...v0.12.3-0) (2022-05-15) ### Bug Fixes * asset is treat as page ([4926de0](https://git.yujianghao.cn/winwin2011/hexon/commit/4926de08be077b682b779b2bc57547a245d60d32)) ### [0.12.2](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.1...v0.12.2) (2022-05-01) ### Bug Fixes * wrong install place hint ([e4aa8b2](https://git.yujianghao.cn/winwin2011/hexon/commit/e4aa8b25fb647734b88031470d284376fb7b6992)) ### [0.12.1](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.12.0...v0.12.1) (2022-05-01) ### Bug Fixes * 401 fix [#41](https://git.yujianghao.cn/winwin2011/hexon/issues/41) ([2004a93](https://git.yujianghao.cn/winwin2011/hexon/commit/2004a9313bae33d4076f1f30cac1facd2ca852af)) * cant find newly created or published post ([0abb162](https://git.yujianghao.cn/winwin2011/hexon/commit/0abb16228c649942c4f1252d62df7cd01de36e21)), closes [#36](https://git.yujianghao.cn/winwin2011/hexon/issues/36) * cant find newly published post ([fb0db36](https://git.yujianghao.cn/winwin2011/hexon/commit/fb0db36eb579bc08badc1336eb4182876d08fb2a)), closes [#36](https://git.yujianghao.cn/winwin2011/hexon/issues/36) # [0.12.0](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.11.3...v0.12.0) (2022-04-17) ### Features * custom script ([#40](https://git.yujianghao.cn/winwin2011/hexon/issues/40)) ([55e4830](https://git.yujianghao.cn/winwin2011/hexon/commits/55e48302c568c10fa795a9a1900907fbabdb98a9)), closes [#39](https://git.yujianghao.cn/winwin2011/hexon/issues/39) * monaco save action ([#37](https://git.yujianghao.cn/winwin2011/hexon/issues/37)) ([213902f](https://git.yujianghao.cn/winwin2011/hexon/commits/213902fbc33862ebec32db85c7b46b6e71cdbd93)), closes [#33](https://git.yujianghao.cn/winwin2011/hexon/issues/33) ## [0.11.3](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.11.2...v0.11.3) (2022-04-13) ### Bug Fixes * content compress conflict with http secure ([7775e17](https://git.yujianghao.cn/winwin2011/hexon/commits/7775e1744ea54db45cbe4677b942d9b1810c2876)) ## [0.11.2](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.11.1...v0.11.2) (2022-04-13) ### Bug Fixes * install route is blocked by auth ([15b80d1](https://git.yujianghao.cn/winwin2011/hexon/commits/15b80d15872e1e56f500e2e0ea4f2b4de64e86d7)) ## [0.11.1](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.11.0...v0.11.1) (2022-04-11) ### Bug Fixes * settings unset ([5906264](https://git.yujianghao.cn/winwin2011/hexon/commits/590626443a5d64974f3c8ae7d244c2b9fb9933e2)) # [0.11.0](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.10.0...v0.11.0) (2022-03-25) ### Bug Fixes * avatar text color ([#16](https://git.yujianghao.cn/winwin2011/hexon/issues/16)) ([caed466](https://git.yujianghao.cn/winwin2011/hexon/commits/caed466d494ab0b79b53cbc81cdc299408b710a7)), closes [#15](https://git.yujianghao.cn/winwin2011/hexon/issues/15) ### Features * markdown hover show image ([#18](https://git.yujianghao.cn/winwin2011/hexon/issues/18)) ([b77782a](https://git.yujianghao.cn/winwin2011/hexon/commits/b77782a58f5efd6427731ddc675359e4596988a8)), closes [#17](https://git.yujianghao.cn/winwin2011/hexon/issues/17) # [0.10.0](https://git.yujianghao.cn/winwin2011/hexon/compare/v0.9.1...v0.10.0) (2022-03-12) ### Bug Fixes * infinite loop after login expires ([#11](https://git.yujianghao.cn/winwin2011/hexon/issues/11)) ([451a94c](https://git.yujianghao.cn/winwin2011/hexon/commits/451a94c4be3e80eeeefd841d084e6f47ea195a47)), closes [#10](https://git.yujianghao.cn/winwin2011/hexon/issues/10) * wrong teleport index ([#7](https://git.yujianghao.cn/winwin2011/hexon/issues/7)) ([b967483](https://git.yujianghao.cn/winwin2011/hexon/commits/b9674839128789d71b7d703170def6b3c8336514)), closes [#5](https://git.yujianghao.cn/winwin2011/hexon/issues/5) ### Features * editor font family ([#4](https://git.yujianghao.cn/winwin2011/hexon/issues/4)) ([46c3d52](https://git.yujianghao.cn/winwin2011/hexon/commits/46c3d52c812716c07c7da5eeca8b60f5ecefea2c)), closes [#2](https://git.yujianghao.cn/winwin2011/hexon/issues/2) * template for frontmatters ([#14](https://git.yujianghao.cn/winwin2011/hexon/issues/14)) ([9032813](https://git.yujianghao.cn/winwin2011/hexon/commits/90328133a77d9988c9a1771bf5262e57c55bf607)), closes [#1](https://git.yujianghao.cn/winwin2011/hexon/issues/1) # [0.9.1](https://bitbucket.org/winwin2011/hexon/compare/v0.9.0...v0.9.1) (2022-02-21) ### Bug Fixes * @winwin/vue-global-theming@0.1.5 security ([c51494e](https://bitbucket.org/winwin2011/hexon/commits/c51494ed79c84ea74e4a31aab1ae3ce46cb13f18)), closes [#8](https://bitbucket.org/winwin2011/hexon/issue/8) * slider word wrap ([ed39050](https://bitbucket.org/winwin2011/hexon/commits/ed39050909b0b3961ef7f1e46a7f443efaa6c0c3)), closes [#11](https://bitbucket.org/winwin2011/hexon/issue/11) ### Features * hexo new path secure and hexo core auto init ([1edd7be](https://bitbucket.org/winwin2011/hexon/commits/1edd7beeca624c1e50c8e6d8148a56a36af8a921)), closes [#5](https://bitbucket.org/winwin2011/hexon/issue/5) # [0.9.0-beta.5](https://github.com/gethexon/hexon/compare/v0.9.0-beta.4...v0.9.0-beta.5) (2022-01-19) ### Bug Fixes * **web:** loading not hide ([2a74dc8](https://github.com/gethexon/hexon/commit/2a74dc8866e0e0b56c81f7cb31d65b5eb3054824)) ### Features * reset password ([3c1fa1b](https://github.com/gethexon/hexon/commit/3c1fa1b88df8489150c57f7d805e8552a1baa119)) * retry and show errors when fail to init hexo ([4c4206b](https://github.com/gethexon/hexon/commit/4c4206bde5c7f75380a1eb58a21a9a70265d0e15)) # [0.9.0-beta.4](https://github.com/gethexon/hexon/compare/v0.9.0-beta.2...v0.9.0-beta.4) (2022-01-19) ### Bug Fixes * question dot operation fail ([b6afde8](https://github.com/gethexon/hexon/commit/b6afde857ef8af7123fcd8a838252b66eda2ecd0)) ### Features * better slide view ([dcf9e3f](https://github.com/gethexon/hexon/commit/dcf9e3f13fefa325673beccee1ec74d413a9db7f)) * change password ([f5c23e9](https://github.com/gethexon/hexon/commit/f5c23e9520e5d3804d238be453d1c7e4849f5215)) * change username ([96afd10](https://github.com/gethexon/hexon/commit/96afd10fd3dbbcc39a7b18e0191559454216a825)) * show loading when creating article ([f5b060d](https://github.com/gethexon/hexon/commit/f5b060d5bde84d722e2507193934771dc21e0d4e)) * signin and signout ([29bb349](https://github.com/gethexon/hexon/commit/29bb349313f12348a3a1ee52f1a84236cc120ff4)) * slide view ([021f4e1](https://github.com/gethexon/hexon/commit/021f4e16144bf88724f1217454c951ad8c0a41d2)) * username ([181c4a7](https://github.com/gethexon/hexon/commit/181c4a76ea04f93994960fb5604bb3db97d2649d)) # [0.9.0-beta.3](https://github.com/gethexon/hexon/compare/v0.9.0-beta.2...v0.9.0-beta.3) (2022-01-11) ### Features * better slide view ([dcf9e3f](https://github.com/gethexon/hexon/commit/dcf9e3f13fefa325673beccee1ec74d413a9db7f)) * change password ([f5c23e9](https://github.com/gethexon/hexon/commit/f5c23e9520e5d3804d238be453d1c7e4849f5215)) * change username ([96afd10](https://github.com/gethexon/hexon/commit/96afd10fd3dbbcc39a7b18e0191559454216a825)) * show loading when creating article ([f5b060d](https://github.com/gethexon/hexon/commit/f5b060d5bde84d722e2507193934771dc21e0d4e)) * signin and signout ([29bb349](https://github.com/gethexon/hexon/commit/29bb349313f12348a3a1ee52f1a84236cc120ff4)) * slide view ([021f4e1](https://github.com/gethexon/hexon/commit/021f4e16144bf88724f1217454c951ad8c0a41d2)) * username ([181c4a7](https://github.com/gethexon/hexon/commit/181c4a76ea04f93994960fb5604bb3db97d2649d)) # [0.9.0-beta.2](https://github.com/gethexon/hexon/compare/v0.9.0-beta.1...v0.9.0-beta.2) (2022-01-10) ### Bug Fixes * bump vue@3.2.26 ([e490d3b](https://github.com/gethexon/hexon/commit/e490d3bf55d67141467b05655c16d0f9ab7af355)) # [0.9.0-beta.1](https://github.com/gethexon/hexon/compare/v0.9.0-beta.0...v0.9.0-beta.1) (2022-01-10) ### Bug Fixes * remove nodegit ([70ef9be](https://github.com/gethexon/hexon/commit/70ef9be7f3f786f98e832db21cdce74fb3b508e3)) # [0.9.0-beta.0](https://github.com/gethexon/hexon/compare/v0.8.0-beta.5...v0.9.0-beta.0) (2022-01-10) ### Bug Fixes * article detail load ([d839cd8](https://github.com/gethexon/hexon/commit/d839cd86ed6001989d8bc0bc69d35167ec320670)) * article item 颜色 ([a436294](https://github.com/gethexon/hexon/commit/a436294715b15e3d0d9e36a746ee212a79a76e55)) * article list 和 search bar 样式 ([48bfd22](https://github.com/gethexon/hexon/commit/48bfd22100c191e4caa5d719cf5586410dd92414)) * badge style ([a60a8fc](https://github.com/gethexon/hexon/commit/a60a8fcd10bcd66b8d41f5fb11ce959e4128d5eb)) * build bug ([8793939](https://github.com/gethexon/hexon/commit/87939394e6c2d5cab3ebee8660f0edfeb343c7b4)) * can not run install process ([ea27b93](https://github.com/gethexon/hexon/commit/ea27b9338069d7d7467eaa59bf2826c7b71ca20b)) * chalk ([ea2ff6f](https://github.com/gethexon/hexon/commit/ea2ff6f5dd5d07c39a4852ec6eee051774e147eb)) * color classes ([671d942](https://github.com/gethexon/hexon/commit/671d942f0b4a497cda3c2acb5467e771af50a4b3)) * css class name ([2d6a601](https://github.com/gethexon/hexon/commit/2d6a601cc73782bcfc9842489a37e56c94f1f35f)) * data unexpected marked changed ([6c01f60](https://github.com/gethexon/hexon/commit/6c01f606c50f6a71478a66b5a996724635839f57)) * data unexpected marked changed ([f7555c5](https://github.com/gethexon/hexon/commit/f7555c540c37b9997db5b05ec8086d0e08756acd)) * data unexpected marked changed ([3a7270c](https://github.com/gethexon/hexon/commit/3a7270c5cdf1594eec564ffc8b6dc6758ed45925)) * date format ([7045adc](https://github.com/gethexon/hexon/commit/7045adc9aefb11edad102284e4d1454ff53ac7d7)) * dialog close ([b68bfb9](https://github.com/gethexon/hexon/commit/b68bfb97182b23eacc16e939ffa079173de3648a)) * disabled button style ([0a4cb3c](https://github.com/gethexon/hexon/commit/0a4cb3cba60a6d33ee5318c800f113a7aec360d9)) * duplicate dts ([7214e19](https://github.com/gethexon/hexon/commit/7214e1954d035b791d38797935db7f29ebf0b045)) * editor load and document title ([a5ba9d1](https://github.com/gethexon/hexon/commit/a5ba9d197185b1174286ad0d0f56dd4c8a16801e)) * fix home page background color ([9cc8f84](https://github.com/gethexon/hexon/commit/9cc8f84369700ce348caa91bda640bb20dcfb79a)) * flash when switching route ([dd93f97](https://github.com/gethexon/hexon/commit/dd93f9794784e00de67630a3f37c34812f4baf46)) * fm editor style ([da39b91](https://github.com/gethexon/hexon/commit/da39b91208baacfed673102ec9389594dc7efe0c)) * fm parse and display ([22c13ab](https://github.com/gethexon/hexon/commit/22c13abd87aac7d01b93049a704828e72eb05b36)) * form style ([6454be7](https://github.com/gethexon/hexon/commit/6454be71e3f507d2607f201b2db75843c1fbb61b)) * form submit bug ([33f363f](https://github.com/gethexon/hexon/commit/33f363fb986c2ff9edec2c1c3fbbcab50bdc8aba)) * git remote ([ac49d60](https://github.com/gethexon/hexon/commit/ac49d60d9e87e1dbb30bdb5fd1d77f081f815e3f)) * h-button shrink ([7a40720](https://github.com/gethexon/hexon/commit/7a40720a6bc62fcfc03776b663c5a743a4b16642)) * h-nav-list ([9e71621](https://github.com/gethexon/hexon/commit/9e716212cfdd051b0bb0a49759d3ea3f99a29b2a)) * h-nav-list count ([6b8c8ab](https://github.com/gethexon/hexon/commit/6b8c8ab7afc125654ff03c530cb7fba6b6df2256)) * input style bug ([6d6e9f6](https://github.com/gethexon/hexon/commit/6d6e9f62cf59f33cf8b070c76978836f03964e69)) * layout ([3f1d031](https://github.com/gethexon/hexon/commit/3f1d0312621242b6e1838d1fdfc4e3dd3fd12fd6)) * load after save ([e88ea6d](https://github.com/gethexon/hexon/commit/e88ea6d876ef064a73873154b27b0934e12e0500)) * login form not w full ([e71824a](https://github.com/gethexon/hexon/commit/e71824aa5639e47d866d60b47d35725b4d8ae1fc)) * monaco dark mode ([35e77eb](https://github.com/gethexon/hexon/commit/35e77eb9f69f73be3e9405c4d924fce0a947b135)) * multiple fixes ([2a931f5](https://github.com/gethexon/hexon/commit/2a931f5a0eec4fd854cb5173d94109f7508c13e2)) * notification animation ([e16076c](https://github.com/gethexon/hexon/commit/e16076cf22ad2f9f87893a19719c185dd4995eea)) * path name ([92b1733](https://github.com/gethexon/hexon/commit/92b1733ab5e3cb594a76b36aa75de39a9cb2fbfe)) * post attr ([9790198](https://github.com/gethexon/hexon/commit/9790198530446b708ae1ecb6ab498404d72ca843)) * remove unused error type ([ca4bc37](https://github.com/gethexon/hexon/commit/ca4bc37e5f5877125ce1f468346c6badb4efca18)) * show viewer by hexo ([c8b6616](https://github.com/gethexon/hexon/commit/c8b6616498922b0af96f0763b2a81dbb14f719f2)) * slug 缺失 ([6df9f32](https://github.com/gethexon/hexon/commit/6df9f32ca3ee07717fcdde15fdc36d918ee071e1)) * small round button style ([63e4f54](https://github.com/gethexon/hexon/commit/63e4f5447d3187e5137a3d555c68319dff15fed3)) * source uri component ([c9eab46](https://github.com/gethexon/hexon/commit/c9eab460c52241b4ec49e760eca27c061af9bd6b)) * statics ([5d86937](https://github.com/gethexon/hexon/commit/5d869376f1830353db5be33a9fc6768332008d7d)) * style ([29f0b15](https://github.com/gethexon/hexon/commit/29f0b15de1faba412683b9f118d57809c071c55f)) * style ([4cf4b2e](https://github.com/gethexon/hexon/commit/4cf4b2e7e7ba31df9cb3abd616fbad14f8ffc5bb)) * toggle bias ([8cadab1](https://github.com/gethexon/hexon/commit/8cadab1d129410664ba375611c7ecf723c3721bd)) * toolbar flex cascade ([72ea784](https://github.com/gethexon/hexon/commit/72ea7849043cd0bac7e40070bb5522e29ae777a3)) * type export ([b763d39](https://github.com/gethexon/hexon/commit/b763d39521380b120145ae40dd4e28b2ba1ddbd0)) * viewer flash ([5cfbe80](https://github.com/gethexon/hexon/commit/5cfbe80edb09caf2479c5fb000d10f275f9e8c5f)) * vitepress doc and css ([1d01597](https://github.com/gethexon/hexon/commit/1d01597d9fef8bc0ceabe02c3e168bb31e8a518e)) * vitepress 路由 ([f6a2ceb](https://github.com/gethexon/hexon/commit/f6a2cebadf46d178cc39917dc285c4153adce9fa)) * vue-demi ([27b3248](https://github.com/gethexon/hexon/commit/27b32482d974a394693fb8551090f730a3b6fc3c)) * 不显示草稿 ([3cccfa8](https://github.com/gethexon/hexon/commit/3cccfa8b4b64c75cf9ca865428d04e76cdbe5e08)) * 关闭 viewer 后没有清空数据 ([b3c2fa2](https://github.com/gethexon/hexon/commit/b3c2fa2a7504da88e9b612bf399659b860e0ee2e)) * 整理 viewer 样式 ([7188a97](https://github.com/gethexon/hexon/commit/7188a97a090a0d085bbedbc6f8d451d4ed41145e)) * 文章查看页面跳转数据重新加载 ([de09412](https://github.com/gethexon/hexon/commit/de09412a407dc76d279417e833d22e9defdc18bf)) * 查看器样式 ([cd0874e](https://github.com/gethexon/hexon/commit/cd0874e7849130e7cef18c352cef994a40c4a021)) * 第一次打开页面不加载文章 ([301f6ce](https://github.com/gethexon/hexon/commit/301f6ce5d3dbffe9fe34ce74bd78973b6ea8aeb2)) * 统一颜色,使用语义颜色 ([687f37c](https://github.com/gethexon/hexon/commit/687f37cde7b5be05045b8d9f0bc52d8bdff2c46d)) * **server:** build ([2b617ea](https://github.com/gethexon/hexon/commit/2b617eaef6695167887ff371225a5ec6c99a2684)) * **server:** route block ([cc7d583](https://github.com/gethexon/hexon/commit/cc7d583928942b8cd9e3f3c561d3e601f2680e7c)) * **vue:** reset less ([f36b564](https://github.com/gethexon/hexon/commit/f36b564cfe8b856a3b5481048f385f7b6b7b450f)) * **web:** doc alias ([39f2212](https://github.com/gethexon/hexon/commit/39f2212bf7b2dafcb2647219b14d472aa12f1164)) * **web:** fix style cat parent and load onmounted ([fce21ec](https://github.com/gethexon/hexon/commit/fce21ec93cdc0f1e44858558cda8b503c21c99c5)) ### Features * advance markdown support ([581711a](https://github.com/gethexon/hexon/commit/581711a5d8339734e9661afeb62f6d22a5848314)) * api ([8745200](https://github.com/gethexon/hexon/commit/8745200e10f022d9acfc97f883a1e09b7cc2b91a)) * article list filter ([bdfdab8](https://github.com/gethexon/hexon/commit/bdfdab85ddd822f50f84ababbaa13df89ae2e705)) * article menu ([bd8838a](https://github.com/gethexon/hexon/commit/bd8838a0f7fc03e0fe45efeccaf3dac868adf354)) * article type icon ([dd6caad](https://github.com/gethexon/hexon/commit/dd6caad16fe9eed3b1ae0064dcf1f60f7b429197)) * badge ([d4f2467](https://github.com/gethexon/hexon/commit/d4f2467395cfecdfbe0bb2210376c57a2bc16fbd)) * base monac editor ([b40cde0](https://github.com/gethexon/hexon/commit/b40cde09bd2abbf03f33e0cff191edf36e4ca965)) * basic sort ([594bf5d](https://github.com/gethexon/hexon/commit/594bf5d459b92d44e3fcb263cd37652407ec9a79)) * better http secure ([0f38524](https://github.com/gethexon/hexon/commit/0f38524bea729876927bdb726d505045c42b1187)) * button size ([8c3736a](https://github.com/gethexon/hexon/commit/8c3736a635b715869446f92d39f93a3d23c3a5d2)) * categories editor ([ded8aa9](https://github.com/gethexon/hexon/commit/ded8aa9f849f75392b3cf7b547c8eb97e09bef75)) * create api ([5584f0f](https://github.com/gethexon/hexon/commit/5584f0f20e8d51ded8b6a10fa4c23dc9940b6a6f)) * create article ([748de06](https://github.com/gethexon/hexon/commit/748de06f81dca66cfb434d39f21d26e4a199a7d4)) * create article modal ([ef579f1](https://github.com/gethexon/hexon/commit/ef579f13baec967730c1c0213f5f0a8cbcdb684e)) * dark mode ([e26b029](https://github.com/gethexon/hexon/commit/e26b029086ea5158deb9eb0ad1ed4efd17612d74)) * dark theme ([11dab35](https://github.com/gethexon/hexon/commit/11dab35d6a4caa4e850744254736db45b4ad79ee)) * date editor ([126a389](https://github.com/gethexon/hexon/commit/126a389f61867d11c6f791b66bd683caf02a8cc5)) * date picker ([c045f9c](https://github.com/gethexon/hexon/commit/c045f9c85b89b69f72bca203cbe80ceb618f2b22)) * date picker support time ([634011f](https://github.com/gethexon/hexon/commit/634011fb8d7587bdf3df598231988417613edd4c)) * delete post or page ([b6c72a2](https://github.com/gethexon/hexon/commit/b6c72a2322d5d237d12e6c865aaaea89e32c2a0d)) * detail store ([fc5f63c](https://github.com/gethexon/hexon/commit/fc5f63c827177a4d8893538d902d418fac85d31d)) * dialog plugin ([24b6df2](https://github.com/gethexon/hexon/commit/24b6df28de2df216ea75afa3f2da0f8762ffc423)) * dispatcher ([60843a5](https://github.com/gethexon/hexon/commit/60843a5ae92aff2282c20d8a4cd13875caa6914c)) * editor view ([fbcdf5c](https://github.com/gethexon/hexon/commit/fbcdf5c1e4478dfcd03af1a8520b012354e4f17f)) * formatter ([bfcbd9b](https://github.com/gethexon/hexon/commit/bfcbd9b58bbef35054031d88b416fff440389dc7)) * frontmatter ([eb9de0f](https://github.com/gethexon/hexon/commit/eb9de0fbd6dfb790f7ac1b1e9103c71590c5ac60)) * get page post by source api ([869558b](https://github.com/gethexon/hexon/commit/869558b8a0483ae444c4727da5a3da2b623b2d5e)) * git actions ([5d10f84](https://github.com/gethexon/hexon/commit/5d10f84d2e555475af2bf03c46d9d2d37b7d3022)) * h-article-item ([c4ca2c5](https://github.com/gethexon/hexon/commit/c4ca2c59d5f647575307561a6d2c9205b0049e3d)) * h-article-list ([fa9acb4](https://github.com/gethexon/hexon/commit/fa9acb4ba1a160e515b467901cbd4793a48c470b)) * h-checkbox ([003d36f](https://github.com/gethexon/hexon/commit/003d36f49774a7b8c88069eade9cc387a461c7b6)) * h-header-editor ([345b0ee](https://github.com/gethexon/hexon/commit/345b0eed28d33d6b2de92de437aeefe19cbeb9c7)) * h-loading ([36e20a2](https://github.com/gethexon/hexon/commit/36e20a2682705227cc6698b86d32aa7d06066ec7)) * h-modal transition ([fa52b37](https://github.com/gethexon/hexon/commit/fa52b375d458f549e0483838fbeaae42049a06b6)) * h-nav-setting ([d60c4fb](https://github.com/gethexon/hexon/commit/d60c4fb7642086502bfee956e72fd1a00014e1aa)) * h-search-bar ([d656078](https://github.com/gethexon/hexon/commit/d656078d570e9a76cb77cb2f81f0ee7c58677aed)) * h-toggle ([573f360](https://github.com/gethexon/hexon/commit/573f360d7300f2eb91b12b2fa0d559e440a07be9)) * h-viewer-content ([7c2b800](https://github.com/gethexon/hexon/commit/7c2b8001a06d29f87413fa93102d55a5ac44ebf7)) * h-viewer-header ([7acb9ac](https://github.com/gethexon/hexon/commit/7acb9accb682f2f263b83c8db2c08a934901ea15)) * h-viewer-toolbar ([17af85b](https://github.com/gethexon/hexon/commit/17af85bb3116ac3553464aa5f622f7368e46bdbd)) * hexo actions ([b217ee1](https://github.com/gethexon/hexon/commit/b217ee18aa654e4d07b4410013c446aa93ceed75)) * home page ([ff530d7](https://github.com/gethexon/hexon/commit/ff530d7b81b125963ace9035002c1a97aa97de76)) * http security auto update ([02e23d3](https://github.com/gethexon/hexon/commit/02e23d3e8024542c6140cc4fa6b4b38362662906)) * http-secure ([975c637](https://github.com/gethexon/hexon/commit/975c637e8d6ae4944b4a8807a3fb3813206d54d2)) * http-secure more log ([c160884](https://github.com/gethexon/hexon/commit/c160884ae939cd2bb1a6d1c979ae0949c4eeab18)) * input error ([0caccf8](https://github.com/gethexon/hexon/commit/0caccf8419a35805253d207c93f6505fddfa20b9)) * input focus blur ([9e35d0d](https://github.com/gethexon/hexon/commit/9e35d0d318e238b5ef42b06133625279e8639b0a)) * install api ([3fff854](https://github.com/gethexon/hexon/commit/3fff854548c27f329371f8210a6e6ed15416acff)) * install page ([988389e](https://github.com/gethexon/hexon/commit/988389e784d01fba977209a040ecc7e6f02880aa)) * install-cli ([94f070b](https://github.com/gethexon/hexon/commit/94f070b8bfdcebbf45953a94926c5cfb12c34c8d)) * layout editor ([fc28d16](https://github.com/gethexon/hexon/commit/fc28d16301ba079b375acb10c46d517def9a7aee)) * list to tree ([69d390b](https://github.com/gethexon/hexon/commit/69d390bbf33b8965497eb4a816d9baabcc32f2af)) * loading ([8471971](https://github.com/gethexon/hexon/commit/84719713d805d52de939a679cefba6bff6a9ce2f)) * modal system ([4bdaa7e](https://github.com/gethexon/hexon/commit/4bdaa7eab44fa3512e0d05a15d9eff3cd9763c44)) * navitem uppercase prop ([e9f90da](https://github.com/gethexon/hexon/commit/e9f90dacc5343475602b51fe9f7c2f6772b9b6e2)) * new nav list ([be2df3b](https://github.com/gethexon/hexon/commit/be2df3b61c7539805d7a60417c589df93715e7ed)) * new theme system ([a2f1fcf](https://github.com/gethexon/hexon/commit/a2f1fcfb4a8a98922b882427dc5c8eb54e9e9323)) * notification actions ([a1ac24d](https://github.com/gethexon/hexon/commit/a1ac24d92253db120f6ba782d7820feef6d8c2ba)) * notification system ([c26cfda](https://github.com/gethexon/hexon/commit/c26cfda07514bd29bc10214c3c51f9dc42f9b6aa)) * password encode ([2bf84fd](https://github.com/gethexon/hexon/commit/2bf84fdd6c3ebe4c09f7122808e3c5eb435e6a81)) * popover ([baa6f39](https://github.com/gethexon/hexon/commit/baa6f39fb9798e698eb87d2444b61106a475a133)) * popover ([78164d3](https://github.com/gethexon/hexon/commit/78164d33cf7e36fdf4e48418176975c2e7464442)) * post and page delete api ([6d08b14](https://github.com/gethexon/hexon/commit/6d08b14f92ace29494bc560e2e24748c02c62796)) * post and page update api ([82919de](https://github.com/gethexon/hexon/commit/82919deb91b5712b899ec683df65da8d70dde8cd)) * prettier markdown formatter ([592b646](https://github.com/gethexon/hexon/commit/592b6461e04e6f93c149c64a33272f4936d0e3b1)) * publish api ([c76849a](https://github.com/gethexon/hexon/commit/c76849a2d113dfd957460a9c5ca6d57b380a05df)) * save article ([c812fe9](https://github.com/gethexon/hexon/commit/c812fe91af8d85f65852ed68af1625ea2d20431e)) * saving instruction ([a689e95](https://github.com/gethexon/hexon/commit/a689e951e74c0f8863fd0e8e77a35e109fd8edc5)) * settings modal ([fa52432](https://github.com/gethexon/hexon/commit/fa524325134f4ab0f869067968ac565a177fe587)) * slider ([ba80d2d](https://github.com/gethexon/hexon/commit/ba80d2d092a7948808fe8b986ff569c5fcfe1883)) * tab switch ([f431ba7](https://github.com/gethexon/hexon/commit/f431ba7f37844636c5040fea2722e0cb3a62953b)) * tag editor ([2d75f51](https://github.com/gethexon/hexon/commit/2d75f5107a519cbc5e118ffb1ff2216bde08ae51)) * textarea ([c5446fd](https://github.com/gethexon/hexon/commit/c5446fd5d68e9ab195884595f254abcef3fb75ac)) * transition ([a69c0bd](https://github.com/gethexon/hexon/commit/a69c0bddf38ec781df8cfaf067aa53e4133d67c2)) * unsaved edit confirm ([5f70d08](https://github.com/gethexon/hexon/commit/5f70d08d36e8f59c9dad786a8f9006a9d8800231)) * viewer view router ([531008c](https://github.com/gethexon/hexon/commit/531008c998895bcdb03ac9f76db9c44c5fa50492)) * viewerview action ([e4be40b](https://github.com/gethexon/hexon/commit/e4be40b3a020880a784be5ffcb114a28e2e62fb8)) * web publish ([b99c830](https://github.com/gethexon/hexon/commit/b99c8303849294c5ace89b8486b98cff0be2a734)) * 更改数据后返回可能的更新 ([40e8097](https://github.com/gethexon/hexon/commit/40e8097017c17e5ed9a9228967472f37a25c40f0)) * 添加 brief 字段到 listxxx api ([8d30450](https://github.com/gethexon/hexon/commit/8d30450e368bf453ac45979ecc7465301050eb2d)) * **server:** change to brief to save bandwidth ([bb07249](https://github.com/gethexon/hexon/commit/bb072495c7fae68873135a0dc733664072082aa5)) * **server:** 接入 koa-account ([7c502f2](https://github.com/gethexon/hexon/commit/7c502f2e2ee0f1c00c96065f16e1d14f8796eaa3)) * **server:** 支持修改用户名和密码 ([f55578c](https://github.com/gethexon/hexon/commit/f55578cf1bf587902f4ebb8c28ced8e11735ff4a)) * **vue:** h-modal ([3f1ab96](https://github.com/gethexon/hexon/commit/3f1ab960c4e51820754f82d5ba58d3abec10a322)) * **vue:** h-nav-list ([530e45e](https://github.com/gethexon/hexon/commit/530e45e221b3bdcbd7049392b11a54755d461c8c)) * **vue:** h-toolbar h-title ([e18bb11](https://github.com/gethexon/hexon/commit/e18bb119cf75c65e7720744f64c74f6c63e3f624)) * **vue:** login form ([3895838](https://github.com/gethexon/hexon/commit/389583854999b8dbeecca93dd83636842231b052)) * **vue:** signin page ([b651b15](https://github.com/gethexon/hexon/commit/b651b15cd95d75a396cccdcfd37dec00447d8ca1)) * **vue:** split-view ([9ea7115](https://github.com/gethexon/hexon/commit/9ea711557afd7dd10eeba1cf5fe8446cc3fd0035)) * **vue:** 重写 account 为 vue 插件 ([380d90f](https://github.com/gethexon/hexon/commit/380d90f964a242a82f2e1b5fa838ff1bcaf987f9)) * **vue:** 重写主题为 vue 插件 ([3b0b8be](https://github.com/gethexon/hexon/commit/3b0b8be3d0cc06e948421ecacd4d6e58d47ced10)) * **web:** ellipsis support ([4b2bf6f](https://github.com/gethexon/hexon/commit/4b2bf6fd0d6e62fdc88fe02fdbeb838572c2bcc4)) * **web:** h-badge ([1cbbe3d](https://github.com/gethexon/hexon/commit/1cbbe3d9f2ef9b8de4aebf83ad8cacd0ec525126)) * **web:** h-button ([54dfe3a](https://github.com/gethexon/hexon/commit/54dfe3adcbad1003f52a6db99738d7330ddc89d0)) * **web:** h-button block ([7ef674b](https://github.com/gethexon/hexon/commit/7ef674bddea94095c5d25d2e9a7055c7dec2e815)) * **web:** h-icon ([5b815f9](https://github.com/gethexon/hexon/commit/5b815f97cdc742c010277f60b648ac4128cd5d22)) * **web:** h-icon clickable ([cdf6ee1](https://github.com/gethexon/hexon/commit/cdf6ee11f6ffba2c0de6c1d6af86714a968f0d66)) * **web:** h-input ([3c2ebf8](https://github.com/gethexon/hexon/commit/3c2ebf8ed11e6eda8786b59b053e7f098dd0ac24)) * **web:** h-input-clearable ([ff05faf](https://github.com/gethexon/hexon/commit/ff05faf2b1b1495f47c4c2eb22aec9227a5f2356)) * **web:** h-input-type ([875b4d6](https://github.com/gethexon/hexon/commit/875b4d6f6a53ce96393c8baea55aa693670416ea)) * **web:** install tailwind css ([9a84356](https://github.com/gethexon/hexon/commit/9a843560271bb3927d5f1ad6590d0bd527d2f866)) * 使用 slug 当作 id ([6802ebc](https://github.com/gethexon/hexon/commit/6802ebc002ad1b33eb5d0ecbf3b6b98c60a9e7c1)) * 添加 slug 编解码支持 ([9c6cffe](https://github.com/gethexon/hexon/commit/9c6cffe6cac3b160ddb7f5411d04258f4346f44d)) * **web:** init and account ([b1792e6](https://github.com/gethexon/hexon/commit/b1792e61a4e84a7d0b1d37257d43c3498fb06a75)) * **web:** 使用 fluent icon font ([5c344e1](https://github.com/gethexon/hexon/commit/5c344e1e7cd8987fec174218529f97ef08bdf33b)) * **web:** 添加 storybook 和主题 ([b204aba](https://github.com/gethexon/hexon/commit/b204abafdf712fd20c3dcdf1ec242e96e1a8662f)) * **web:** 添加 vuex 和博客内容加载 ([073ea86](https://github.com/gethexon/hexon/commit/073ea86a7c04d2a46044548240363a3d1d3db51a)) * **web:** 添加颜色类型 ([54f6fee](https://github.com/gethexon/hexon/commit/54f6fee1c2f3b7947fd26da6ce3dbc69757d3c78)) * **web:** 登出时清理 store 数据 ([2998964](https://github.com/gethexon/hexon/commit/29989647ae354e16a6214558a553ae9c7f23e66b)) * base command ([71db15e](https://github.com/gethexon/hexon/commit/71db15e23dad870d6da7c62c80a0669d2e654a47)) * hexo list ([b9d9b8d](https://github.com/gethexon/hexon/commit/b9d9b8d2cfc2c4c87d1dfb89e1b8fb3edd261bce)) * 支持显示错误 ([a77f049](https://github.com/gethexon/hexon/commit/a77f049948304b75a6c7379473ae6af07ee78c9f)) * 重构基础 支持 ts ([4cb26a3](https://github.com/gethexon/hexon/commit/4cb26a3bb94f8c09a7dbda6cadb20923acc4e3b1)) # [0.8.0-beta.5](https://github.com/gethexon/hexon/compare/v0.8.0-beta.4...v0.8.0-beta.5) (2021-11-03) ### Bug Fixes * path 为空时新建文章崩溃 ([bcc2ee9](https://github.com/gethexon/hexon/commit/bcc2ee9189b0d1e3785e9937f70ef39d9ffdc3f3)) * prd script 路径错误 ([21e2cfb](https://github.com/gethexon/hexon/commit/21e2cfb61fb32b0562740123e3c258a99bc64749)) # [0.8.0-beta.4](https://github.com/gethexon/hexon/compare/v0.8.0-beta.3...v0.8.0-beta.4) (2021-10-21) ### Bug Fixes * show 500 instead of 404 when error ([208f9eb](https://github.com/gethexon/hexon/commit/208f9ebca656a48d39c56728c1a02d582c3d3595)) # [0.8.0-beta.3](https://github.com/gethexon/hexon/compare/v0.8.0-beta.2...v0.8.0-beta.3) (2021-10-21) ### Bug Fixes * 转换反斜杠为正斜杠 ([44d8844](https://github.com/gethexon/hexon/commit/44d88441cd63db296a99bbcff728a97a5ae913a4)) # [0.8.0-beta.2](https://github.com/gethexon/hexon/compare/v0.8.0-beta.1...v0.8.0-beta.2) (2021-04-23) ### Features * 正则搜索文章 ([b180b88](https://github.com/gethexon/hexon/commit/b180b88e7c705623e6c0a89652bce91a7e6f87a2)) # [0.8.0-beta.1](https://github.com/gethexon/hexon/compare/v0.8.0-beta.0...v0.8.0-beta.1) (2021-04-16) ### Features * 添加 MonacoEditor 黑暗模式支持 ([3fe02f7](https://github.com/gethexon/hexon/commit/3fe02f71d50f18cb9eba121a055a641ee6fe4a1f)) * 添加 pwa 刷新和返回按钮 ([532bb36](https://github.com/gethexon/hexon/commit/532bb3644cb8e43addbc7efe790450d3090c781d)) * 重置密码 ([dea9fc5](https://github.com/gethexon/hexon/commit/dea9fc51db8af9236a4ccc53e2a6a76179a22ef3)) * 自动 prerelease ([67d72ec](https://github.com/gethexon/hexon/commit/67d72eca56727e9815aaa5bc308c2caa9e4122ca)) ================================================ FILE: LICENSE ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . ================================================ FILE: README.md ================================================ # Hexon Let's hexo online! [![GitHub release (latest by date)](https://img.shields.io/github/v/release/gethexon/hexon?style=flat-square)](https://github.com/gethexon/hexon/releases/) English | [中文](./README_CN.md) ![home screen](./images/home-screen.png) ## ❔ How it works? Hexon is a GUI for hexo with git, run commands and manage content for you. ## ⭐️ Features - Post and page management - Front-matter template - ~~Online image management~~ use [imageur](https://github.com/YuJianghao/imageur) ## 📘 Guide ### Before Install Make sure your `git`, `hexo` and `Node.js` workflow is fine. Hexon only provide a GUI for these commands, but **not implement them**. ### Install ```bash git clone https://github.com/gethexon/hexon --depth 1 cd hexon pnpm install pnpm run setup ``` ### Uninstall Just remove the folder you just cloned ```bash rm -rf hexon ``` ### Start For plain Node.js: ```bash pnpm start ``` For better devOps with [pm2](https://pm2.keymetrics.io/) ```bash pnpm prd # or integrate pm2 with `pnpm start` manually ``` ### Update Just uninstall the old one and install the new one. ## 💻 Commands - `pnpm run setup`: install and config - `pnpm start`: start hexon with node - `pnpm prd`: start hexon with pm2 - `pnpm resetpwd`: reset password - `pnpm script`: manage custom script You can replace hexon commands with your own bash script inside hexo blog folder. e.g. modify `git sync` script 1. write your script in `/git_sync.sh` 2. run `pnpm script` 3. select `git sync` 4. set script to `bash ./git_sync.sh` ## 🖥️ Develop - Check out `develop` branch. - Run `pnpm dev-init` install dependencies and config hexon. - Run `pnpm dev` and show your magic! ###### When submitting a PR, remember to set the target branch to `develop`. ## 💩 Have trouble? - Read FAQs below. - Search in [Issue list](https://github.com/gethexon/hexon/issues). - [Add new Issue](https://github.com/gethexon/hexon/issueI/new). ## ❓ Want to know more? Start a [discussion](https://github.com/gethexon/hexon/discussions). ## 👌🏻 FAQ ### 404 Error Maybe mistakes in revert proxy config(e.g. Nginx or Apache config). To verify, use `curl` on your server directly request assets without any revert proxy. This should be a command looks like: ```bash curl http://localhost:5777/assets/HMonacoEditor.5101bbae.js ``` Or hexon failure. Just raise an issue.
If you are using Apache's reverse proxy... Please make sure to add `AllowEncodedSlashes NoDecode` in your `VirtualHost` configuration, and add `nocanon` at the end of the `ProxyPass` setting. See https://stackoverflow.com/questions/52034899/express-nodejs-server-through-apache-proxy-error-404-for-route-with-express-par and https://stackoverflow.com/questions/4390436/need-to-allow-encoded-slashes-on-apache Example: ```conf ServerName blog-admin.example.com SSLCertificateFile /etc/certificates/example.com.crt SSLCertificateKeyFile /etc/certificates/example.com.key SSLCertificateChainFile /etc/certificates/example.com.crt SSLEngine On SSLProxyEngine On ProxyRequests Off ProxyPreserveHost On AllowEncodedSlashes NoDecode ProxyPass / http://localhost:5777/ nocanon ProxyPassReverse / http://localhost:5777/ ```
## Star history [![Star History Chart](https://api.star-history.com/svg?repos=gethexon/hexon&type=Date)](https://star-history.com/#gethexon/hexon&Date) ## Contributors ## License GPL-3.0 © winwin2011 ================================================ FILE: README_CN.md ================================================ # Hexon Let's hexo online! [![GitHub release (latest by date)](https://img.shields.io/github/v/release/gethexon/hexon?style=flat-square)](https://github.com/gethexon/hexon/releases/) [English](./README.md) | 中文 ![home screen](./images/home-screen.png) ## ❔ Hexon 是什么 Hexon 是一个集成了git、命令行并能够帮你管理 hexo 内容的图形化界面 ## ⭐️ 特性 - 文章和页面管理 - Front-matter 模板 - ~~在线图片管理~~ 请使用 [imageur](https://github.com/YuJianghao/imageur) ## 📘 指南 ### 安装前 确保您的 `git`, `hexo` and `Node.js` 工作流正常。Hexon 仅为这些命令提供了图形用户界面,但**不会实现它们**. ### 安装 ```bash git clone https://github.com/gethexon/hexon --depth 1 cd hexon pnpm install pnpm run setup ``` ### 卸载 只需删除您刚克隆的文件夹 ```bash rm -rf hexon ``` ### 启动 使用 Node.js 启动 ```bash pnpm start ``` 使用 [pm2](https://pm2.keymetrics.io/) 启动以获得更好的devOps体验 ```bash pnpm prd # 或者手动将 pm2 与 `pnpm start` 集成 ``` ### 更新 删除老版本并重新安装新版本。 ## 💻 命令 - `pnpm run setup`: 安装和配置 - `pnpm start`: 使用 Node 启动 Hexon - `pnpm prd`: 使用 pm2 启动 Hexon - `pnpm resetpwd`: 重置密码 - `pnpm script`: 管理自定义脚本 您可以在 Hexo 博客文件夹内替换 Hexon 命令为您自己的 Bash 脚本。 例如,修改 git sync 脚本的步骤如下: 1. 在 `/git_sync.sh` 中编写您的脚本 2. 运行 `pnpm script` 3. 选择 `git sync` 4. 将脚本设置为 `bash ./git_sync.sh` ## 🖥️ 开发 - 切换到 `develop` 分支。 - 运行 `pnpm dev-init` 安装依赖并配置 Hexon。 - 运行 `pnpm dev` ,开始大展身手! ###### 提交 PR 时,请记得将目标分支设置为 `develop。` ## 💩 遇到问题? - 请查看下面的 FAQs。 - 尝试在 [issue list](https://github.com/gethexon/hexon/issues) 中寻找答案。 - 发起一个新的 [issue](https://github.com/gethexon/hexon/issues/new). ## ❓ 想了解更多? 发起一个新的 [discussion](https://github.com/gethexon/hexon/discussions). ## 👌🏻 FAQ ### 404 Error 可能是反向代理配置错误(例如 Nginx 或 Apache 配置)。请在您的服务器上不经过反向代理,直接使用 curl 请求资源。这应该是一个类似以下的命令: ```bash curl http://localhost:5777/assets/HMonacoEditor.5101bbae.js ``` 如果是 hexon failure。请发起一个新的 issue。
如果您正在使用 Apache 的反向代理... 请确保在您的 `VirtualHost` 配置中添加 `AllowEncodedSlashes NoDecode`,并在 `ProxyPass` 设置的末尾添加 `nocanon`。 参见 https://stackoverflow.com/questions/52034899/express-nodejs-server-through-apache-proxy-error-404-for-route-with-express-par 和 https://stackoverflow.com/questions/4390436/need-to-allow-encoded-slashes-on-apache 样例: ```conf ServerName blog-admin.example.com SSLCertificateFile /etc/certificates/example.com.crt SSLCertificateKeyFile /etc/certificates/example.com.key SSLCertificateChainFile /etc/certificates/example.com.crt SSLEngine On SSLProxyEngine On ProxyRequests Off ProxyPreserveHost On AllowEncodedSlashes NoDecode ProxyPass / http://localhost:5777/ nocanon ProxyPassReverse / http://localhost:5777/ ```
## Star 历史 [![Star History Chart](https://api.star-history.com/svg?repos=gethexon/hexon&type=Date)](https://star-history.com/#gethexon/hexon&Date) ## 贡献者 ## 许可证 GPL-3.0 © winwin2011 ================================================ FILE: client/.demo/App.vue ================================================ ================================================ FILE: client/.demo/Layout.vue ================================================ ================================================ FILE: client/.demo/Welcome.vue ================================================ ================================================ FILE: client/.demo/index.html ================================================ Hexon UI Doc
================================================ FILE: client/.demo/main.ts ================================================ import { createApp } from "vue" import App from "./App.vue" import themes from "~/themes" import theme from "~/plugins/theme" import notification from "~/plugins/notification" import dialog from "~/plugins/dialog" import "~/plugins/dayjs" import { router } from "./router" import "@unocss/reset/tailwind.css" import "uno.css" createApp(App) .use(router) .use(themes) .use(theme) .use(notification) .use(dialog) .mount("#app") ================================================ FILE: client/.demo/router.ts ================================================ import { RouteRecordRaw } from "vue-router" import { createWebHashHistory } from "vue-router" import { createRouter } from "vue-router" import { getNameFromPath, modules } from "./utils" export const demos: RouteRecordRaw[] = Object.entries(modules).map( ([path, imp]) => { return { path: encodeURIComponent(path), component: imp, meta: { name: getNameFromPath(path), }, } } ) const routes: RouteRecordRaw[] = [ { path: "/", component: () => import("./Layout.vue"), children: [ ...demos, { path: "", component: () => import("./Welcome.vue") }, ], }, { path: "/:pathMatch(.*)*", redirect: "/", }, ] export const router = createRouter({ history: createWebHashHistory(), routes }) ================================================ FILE: client/.demo/utils.ts ================================================ export const modules = import.meta.glob("../src/**/demo/*.demo.vue") export const getNameFromPath = (path: string) => { return path .split("../src/")[1] .split("lib/") .join("") .split("components/") .join("") .split("ui/") .join("") .split("/demo/") .join("/") .split(".demo.vue")[0] .split("/default") .join("") } ================================================ FILE: client/.demo/vite.config.ts ================================================ import path from "path" import { defineConfig } from "vite" import vue from "@vitejs/plugin-vue" import Unocss from "unocss/vite" import presetUno from "@unocss/preset-uno" import presetAttributify from "@unocss/preset-attributify" import transformerDirective from "@unocss/transformer-directives" const projectRootDir = path.resolve(__dirname, "..") // https://vitejs.dev/config/ export default defineConfig({ root: __dirname, server: { port: 4000 }, plugins: [ vue(), Unocss({ presets: [presetUno(), presetAttributify()], transformers: [transformerDirective()], }), ], resolve: { alias: [ { find: "~", replacement: path.resolve(projectRootDir, "src"), }, { find: "@", replacement: path.resolve(projectRootDir, "src/components"), }, ], }, }) ================================================ FILE: client/.vscode/extensions.json ================================================ { "recommendations": ["johnsoncodehk.volar"] } ================================================ FILE: client/README.md ================================================ # Vue 3 + Typescript + Vite This template should help get you started developing with Vue 3 and Typescript in Vite. The template uses Vue 3 ` ================================================ FILE: client/src/components/HToolbar.vue ================================================ ================================================ FILE: client/src/components/article/HArticleItem.vue ================================================ ================================================ FILE: client/src/components/article/HArticleList.vue ================================================ ================================================ FILE: client/src/components/article/HArticleMenu.vue ================================================ ================================================ FILE: client/src/components/article/interface.ts ================================================ import { Dayjs } from "dayjs" import { IArticleIdentifier } from "~/interface" export interface IShowMenuPaylod { article: IHArticleListData e: MouseEvent } export interface IHArticleListData { type: "post" | "page" isDraft: boolean title: string brief: string tags: string[] categories: string[] date: Dayjs | null updated: Dayjs | null source: string } export type IHarticleMenuActionPayload = | { type: "edit" id: IArticleIdentifier } | { type: "delete" id: IArticleIdentifier } | { type: "publish" source: string } export type IHArticleMenuActionType = IHarticleMenuActionPayload["type"] ================================================ FILE: client/src/components/article/utils.ts ================================================ import dayjs from "dayjs" import { IHArticleListData } from "./interface" export function sortArticleByTime( articles: IHArticleListData[] ): IHArticleListData[] { return articles.sort((a, b) => { const da = dayjs(a.date) const db = dayjs(b.date) if (!da.isValid()) return -1 if (!db.isValid()) return 1 return da.valueOf() > db.valueOf() ? -1 : 1 }) } ================================================ FILE: client/src/components/editors/FrontMatterTemplate.vue ================================================ ================================================ FILE: client/src/components/editors/FrontMatterTemplateEdit.vue ================================================ ================================================ FILE: client/src/components/editors/HCategoriesEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HDateEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HFrontmatterEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HHeaderEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HLayoutEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HMonacoEditor.vue ================================================ ================================================ FILE: client/src/components/editors/HTagEditor.vue ================================================ ================================================ FILE: client/src/components/editors/custom-monaco.ts ================================================ import * as monaco from "monaco-editor/esm/vs/editor/editor.api" export { monaco } ================================================ FILE: client/src/components/editors/markdown-image-ext.ts ================================================ import { monaco } from "./custom-monaco" export class MarkdownImageExtension { private static _disposiable: monaco.IDisposable | null public activate() { MarkdownImageExtension._disposiable?.dispose() MarkdownImageExtension._disposiable = monaco.languages.registerHoverProvider("markdown", { provideHover(model, position) { const res = model .findMatches( `!\\[(.*?)\\]\\((.*?)\\)`, false, true, false, null, true ) .filter((fm) => fm.range.containsPosition(position))[0] const value = res?.matches?.[0] const image = res?.matches?.[2] if (!value || !image) return null return { range: res.range, contents: [ { value: `[${value}](${image})`, }, ], } }, }) } } ================================================ FILE: client/src/components/editors/monaco-markdown/completion.ts ================================================ // @ts-nocheck 'use strict' import * as TypeConverters from './vscode-converters' import {CancellationToken, editor, languages, Position as _Position} from 'monaco-editor'; import {TextDocument, TextEditor,} from './vscode-monaco' import {Range, SnippetString, Position} from './extHostTypes' import {slugify} from './util'; import {buildToc} from './toc'; import * as latex from './latex' let completionActivated = false export function activateCompletion(editor: TextEditor) { if (!completionActivated) { //TODO: remove provider when context is disposed let provider = new MdCompletionItemProvider(); languages.registerCompletionItemProvider(editor.languageId, provider); completionActivated = true; } } function completionList(items: languages.CompletionItem[]): languages.CompletionList { return {suggestions: items.map((v, _,) => Object.assign({}, v))}; } function newCompletionItem(text: string, kind: languages.CompletionItemKind): languages.CompletionItem { return { label: text, kind: kind, additionalTextEdits: undefined, command: undefined, commitCharacters: undefined, detail: undefined, documentation: undefined, filterText: undefined, insertTextRules: undefined, preselect: false, range: undefined, sortText: undefined, insertText: undefined } } class MdCompletionItemProvider implements languages.CompletionItemProvider { triggerCharacters = ['(', '\\', '/', '[', '#'] // Suffixes explained: // \cmd -> 0 // \cmd{$1} -> 1 // \cmd{$1}{$2} -> 2 // // Use linebreak to mimic the structure of the KaTeX [Support Table](https://katex.org/docs/supported.html) mathCompletions: languages.CompletionItem[]; constructor() { // \cmd let c1 = latex._c1.map(cmd => { let item = newCompletionItem('\\' + cmd, languages.CompletionItemKind.Function); item.insertText = cmd; return item; }); // \cmd{$1} let c2 = latex._c2.map(cmd => { let item = newCompletionItem('\\' + cmd, languages.CompletionItemKind.Function); item.insertText = new SnippetString(`${cmd}\{$1\}`).value; item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet return item; }); // \cmd{$1}{$2} let c3 = latex._c3.map(cmd => { let item = newCompletionItem('\\' + cmd, languages.CompletionItemKind.Function); item.insertText = new SnippetString(`${cmd}\{$1\}\{$2\}`).value; item.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet return item; }); let envSnippet = newCompletionItem('\\begin', languages.CompletionItemKind.Snippet); envSnippet.insertText = new SnippetString('begin{${1|aligned,alignedat,array,bmatrix,Bmatrix,cases,darray,dcases,gathered,matrix,pmatrix,vmatrix,Vmatrix|}}\n\t$2\n\\end{$1}').value; envSnippet.insertTextRules = languages.CompletionItemInsertTextRule.InsertAsSnippet this.mathCompletions = [...c1, ...c2, ...c3, envSnippet]; // Sort this.mathCompletions.forEach(item => { item.sortText = (typeof item.label === 'string' ? item.label : item.label.label).replace(/[a-zA-Z]/g, c => { if (/[a-z]/.test(c)) { return `0${c}`; } else { return `1${c.toLowerCase()}`; } }); }); } provideCompletionItems(model: editor.ITextModel, _position: _Position, _context: languages.CompletionContext, _token: CancellationToken): languages.ProviderResult { let document = new TextDocument(model) let position = TypeConverters.Position.to(_position) const lineTextBefore = document.lineAt(position.line).text.substring(0, position.character); const lineTextAfter = document.lineAt(position.line).text.substring(position.character); let matches; if ( (matches = lineTextBefore.match(/\\[^$]*$/)) !== null ) { /* ┌────────────────┐ │ Math functions │ └────────────────┘ */ if ( /(^|[^\$])\$(|[^ \$].*)\\\w*$/.test(lineTextBefore) && lineTextAfter.includes('$') ) { // Complete math functions (inline math) return completionList(this.mathCompletions); } else { const textBefore = document.getText(new Range(new Position(0, 0), position)); const textAfter = document.getText().substr(document.offsetAt(position)); if ( (matches = textBefore.match(/\$\$/g)) !== null && matches.length % 2 !== 0 && textAfter.includes('\$\$') ) { // Complete math functions ($$ ... $$) return completionList(this.mathCompletions) } else { return completionList([]); } } } else if (/\[[^\]]*?\]\[[^\]]*$/.test(lineTextBefore)) { /* ┌───────────────────────┐ │ Reference link labels │ └───────────────────────┘ */ let startIndex = lineTextBefore.lastIndexOf('['); const range = new Range(position.with({character: startIndex + 1}), position); return new Promise((res, _) => { const lines = document.getText().split(/\r?\n/); const usageCounts = lines.reduce((useCounts, currentLine) => { let match: RegExpExecArray; const pattern = /\[[^\]]+\]\[([^\]]*?)\]/g; while ((match = pattern.exec(currentLine)) !== null) { let usedRef = match[1]; if (!useCounts.has(usedRef)) { useCounts.set(usedRef, 0); } useCounts.set(usedRef, useCounts.get(usedRef) + 1); } return useCounts; }, new Map()); let refLabels = lines.reduce((prev, curr) => { let match; if ((match = /^\[([^\]]*?)\]: (\S*)( .*)?/.exec(curr)) !== null) { const ref = match[1]; let item = newCompletionItem(ref, languages.CompletionItemKind.Reference); const usages = usageCounts.get(ref) || 0; item.insertText = ref item.documentation = {value: (match[2])}; item.detail = usages === 1 ? `1 usage` : `${usages} usages`; // Prefer unused items item.sortText = usages === 0 ? `0-${ref}` : item.sortText = `1-${ref}`; item.range = TypeConverters.Range.from(range); prev.push(item); } return prev; }, []); res(completionList(refLabels)); }); } else if (/\[[^\]]*\]\(#[^\)]*$/.test(lineTextBefore)) { /* ┌───────────────────────────┐ │ Anchor tags from headings │ └───────────────────────────┘ */ let startIndex = lineTextBefore.lastIndexOf('('); let endPosition = position; let addClosingParen = false; if (/^([^\) ]+\s*|^\s*)\)/.test(lineTextAfter)) { // try to detect if user wants to replace a link (i.e. matching closing paren and ) // Either: ... something ) // or: ... ) // or: ... ) (endPosition assignment is a no-op for this case) // in every case, we want to remove all characters after the cursor and before that first closing paren endPosition = position.with({character: +endPosition.character + lineTextAfter.indexOf(')')}); } else { // If no closing paren is found, replace all trailing non-white-space chars and add a closing paren // distance to first non-whitespace or EOL let toReplace = 0; while (toReplace { const toc = buildToc(document); const headingCompletions: languages.CompletionItem[] = toc.reduce((prev: languages.CompletionItem[], curr: any) => { let item = newCompletionItem('#' + slugify(curr.text), languages.CompletionItemKind.Reference); let label = typeof item.label === 'string' ? item.label : item.label.label; if (addClosingParen) { item.insertText = label + ')'; } else { item.insertText = label } item.documentation = curr.text; item.range = TypeConverters.Range.from(range); prev.push(item); return prev; }, []); res(completionList(headingCompletions)); }); } else { return completionList([]); } } } ================================================ FILE: client/src/components/editors/monaco-markdown/contribution.ts ================================================ // @ts-nocheck /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import {languages} from "monaco-editor" interface ILang extends languages.ILanguageExtensionPoint { loader: () => Promise; } interface ILangImpl { conf: languages.LanguageConfiguration; language: languages.IMonarchLanguage; } let languageDefinitions: { [languageId: string]: ILang } = {}; function _loadLanguage(languageId: string): Promise { const loader = languageDefinitions[languageId].loader; return loader().then((mod) => { languages.setMonarchTokensProvider(languageId, mod.language); languages.setLanguageConfiguration(languageId, mod.conf); }); } let languagePromises: { [languageId: string]: Promise } = {}; export function loadLanguage(languageId: string): Promise { if (!languagePromises[languageId]) { languagePromises[languageId] = _loadLanguage(languageId); } return languagePromises[languageId]; } export function registerLanguage(def: ILang): void { let languageId = def.id; languageDefinitions[languageId] = def; languages.register(def); languages.onLanguage(languageId, () => { loadLanguage(languageId); }); } ================================================ FILE: client/src/components/editors/monaco-markdown/errors.ts ================================================ // @ts-nocheck /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ export interface ErrorListenerCallback { (error: any): void; } export interface ErrorListenerUnbind { (): void; } // Avoid circular dependency on EventEmitter by implementing a subset of the interface. export class ErrorHandler { private unexpectedErrorHandler: (e: any) => void; private listeners: ErrorListenerCallback[]; constructor() { this.listeners = []; this.unexpectedErrorHandler = function (e: any) { setTimeout(() => { if (e.stack) { throw new Error(e.message + '\n\n' + e.stack); } throw e; }, 0); }; } public addListener(listener: ErrorListenerCallback): ErrorListenerUnbind { this.listeners.push(listener); return () => { this._removeListener(listener); }; } private emit(e: any): void { this.listeners.forEach((listener) => { listener(e); }); } private _removeListener(listener: ErrorListenerCallback): void { this.listeners.splice(this.listeners.indexOf(listener), 1); } public setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { this.unexpectedErrorHandler = newUnexpectedErrorHandler; } public getUnexpectedErrorHandler(): (e: any) => void { return this.unexpectedErrorHandler; } public onUnexpectedError(e: any): void { this.unexpectedErrorHandler(e); this.emit(e); } // For external errors, we don't want the listeners to be called public onUnexpectedExternalError(e: any): void { this.unexpectedErrorHandler(e); } } export const errorHandler = new ErrorHandler(); export function setUnexpectedErrorHandler(newUnexpectedErrorHandler: (e: any) => void): void { errorHandler.setUnexpectedErrorHandler(newUnexpectedErrorHandler); } export function onUnexpectedError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedError(e); } return undefined; } export function onUnexpectedExternalError(e: any): undefined { // ignore errors from cancelled promises if (!isPromiseCanceledError(e)) { errorHandler.onUnexpectedExternalError(e); } return undefined; } export interface SerializedError { readonly $isError: true; readonly name: string; readonly message: string; readonly stack: string; } export function transformErrorForSerialization(error: Error): SerializedError; export function transformErrorForSerialization(error: any): any; export function transformErrorForSerialization(error: any): any { if (error instanceof Error) { let { name, message } = error; const stack: string = (error).stacktrace || (error).stack; return { $isError: true, name, message, stack }; } // return as is return error; } // see https://github.com/v8/v8/wiki/Stack%20Trace%20API#basic-stack-traces export interface V8CallSite { getThis(): any; getTypeName(): string; getFunction(): string; getFunctionName(): string; getMethodName(): string; getFileName(): string; getLineNumber(): number; getColumnNumber(): number; getEvalOrigin(): string; isToplevel(): boolean; isEval(): boolean; isNative(): boolean; isConstructor(): boolean; toString(): string; } const canceledName = 'Canceled'; /** * Checks if the given error is a promise in canceled state */ export function isPromiseCanceledError(error: any): boolean { return error instanceof Error && error.name === canceledName && error.message === canceledName; } /** * Returns an error that signals cancellation. */ export function canceled(): Error { const error = new Error(canceledName); error.name = error.message; return error; } export function illegalArgument(name?: string): Error { if (name) { return new Error(`Illegal argument: ${name}`); } else { return new Error('Illegal argument'); } } export function illegalState(name?: string): Error { if (name) { return new Error(`Illegal state: ${name}`); } else { return new Error('Illegal state'); } } export function readonly(name?: string): Error { return name ? new Error(`readonly property '${name} cannot be changed'`) : new Error('readonly property cannot be changed'); } export function disposed(what: string): Error { const result = new Error(`${what} has been disposed`); result.name = 'DISPOSED'; return result; } export function getErrorMessage(err: any): string { if (!err) { return 'Error'; } if (err.message) { return err.message; } if (err.stack) { return err.stack.split('\n')[0]; } return String(err); } ================================================ FILE: client/src/components/editors/monaco-markdown/extHostTypes.ts ================================================ // @ts-nocheck import { illegalArgument } from "./errors" import { Uri } from "monaco-editor" // export function values(set: Set): V[]; // export function values(map: Map): V[]; export function values(forEachable: { forEach(callback: (value: V, ...more: any[]) => any): void }): V[] { const result: V[] = [] forEachable.forEach((value) => result.push(value)) return result } export class Position { static Min(...positions: Position[]): Position { if (positions.length === 0) { throw new TypeError() } let result = positions[0] for (let i = 1; i < positions.length; i++) { const p = positions[i] if (p.isBefore(result!)) { result = p } } return result } static Max(...positions: Position[]): Position { if (positions.length === 0) { throw new TypeError() } let result = positions[0] for (let i = 1; i < positions.length; i++) { const p = positions[i] if (p.isAfter(result!)) { result = p } } return result } static isPosition(other: any): other is Position { if (!other) { return false } if (other instanceof Position) { return true } let { line, character } = other if (typeof line === "number" && typeof character === "number") { return true } return false } private _line: number private _character: number get line(): number { return this._line } get character(): number { return this._character } constructor(line: number, character: number) { if (line < 0) { throw illegalArgument("line must be non-negative") } if (character < 0) { throw illegalArgument("character must be non-negative") } this._line = line this._character = character } isBefore(other: Position): boolean { if (this._line < other._line) { return true } if (other._line < this._line) { return false } return this._character < other._character } isBeforeOrEqual(other: Position): boolean { if (this._line < other._line) { return true } if (other._line < this._line) { return false } return this._character <= other._character } isAfter(other: Position): boolean { return !this.isBeforeOrEqual(other) } isAfterOrEqual(other: Position): boolean { return !this.isBefore(other) } isEqual(other: Position): boolean { return this._line === other._line && this._character === other._character } compareTo(other: Position): number { if (this._line < other._line) { return -1 } else if (this._line > other.line) { return 1 } else { // equal line if (this._character < other._character) { return -1 } else if (this._character > other._character) { return 1 } else { // equal line and character return 0 } } } translate(change: { lineDelta?: number; characterDelta?: number }): Position translate(lineDelta?: number, characterDelta?: number): Position translate( lineDeltaOrChange: | number | undefined | { lineDelta?: number; characterDelta?: number }, characterDelta: number = 0 ): Position { if (lineDeltaOrChange === null || characterDelta === null) { throw illegalArgument() } let lineDelta: number if (typeof lineDeltaOrChange === "undefined") { lineDelta = 0 } else if (typeof lineDeltaOrChange === "number") { lineDelta = lineDeltaOrChange } else { lineDelta = typeof lineDeltaOrChange.lineDelta === "number" ? lineDeltaOrChange.lineDelta : 0 characterDelta = typeof lineDeltaOrChange.characterDelta === "number" ? lineDeltaOrChange.characterDelta : 0 } if (lineDelta === 0 && characterDelta === 0) { return this } return new Position(this.line + lineDelta, this.character + characterDelta) } with(change: { line?: number; character?: number }): Position with(line?: number, character?: number): Position with( lineOrChange: number | undefined | { line?: number; character?: number }, character: number = this.character ): Position { if (lineOrChange === null || character === null) { throw illegalArgument() } let line: number if (typeof lineOrChange === "undefined") { line = this.line } else if (typeof lineOrChange === "number") { line = lineOrChange } else { line = typeof lineOrChange.line === "number" ? lineOrChange.line : this.line character = typeof lineOrChange.character === "number" ? lineOrChange.character : this.character } if (line === this.line && character === this.character) { return this } return new Position(line, character) } toJSON(): any { return { line: this.line, character: this.character } } } export class Range { static isRange(thing: any): boolean { if (thing instanceof Range) { return true } if (!thing) { return false } return ( Position.isPosition((thing).start) && Position.isPosition(thing.end) ) } protected _start: Position protected _end: Position get start(): Position { return this._start } get end(): Position { return this._end } constructor(start: Position, end: Position) constructor( startLine: number, startColumn: number, endLine: number, endColumn: number ) constructor( startLineOrStart: number | Position, startColumnOrEnd: number | Position, endLine?: number, endColumn?: number ) { let start: Position | undefined let end: Position | undefined if ( typeof startLineOrStart === "number" && typeof startColumnOrEnd === "number" && typeof endLine === "number" && typeof endColumn === "number" ) { start = new Position(startLineOrStart, startColumnOrEnd) end = new Position(endLine, endColumn) } else if ( startLineOrStart instanceof Position && startColumnOrEnd instanceof Position ) { start = startLineOrStart end = startColumnOrEnd } if (!start || !end) { throw new Error("Invalid arguments") } if (start.isBefore(end)) { this._start = start this._end = end } else { this._start = end this._end = start } } contains(positionOrRange: Position | Range): boolean { if (positionOrRange instanceof Range) { return ( this.contains(positionOrRange._start) && this.contains(positionOrRange._end) ) } else if (positionOrRange instanceof Position) { if (positionOrRange.isBefore(this._start)) { return false } if (this._end.isBefore(positionOrRange)) { return false } return true } return false } isEqual(other: Range): boolean { return this._start.isEqual(other._start) && this._end.isEqual(other._end) } intersection(other: Range): Range | undefined { const start = Position.Max(other.start, this._start) const end = Position.Min(other.end, this._end) if (start.isAfter(end)) { // this happens when there is no overlap: // |-----| // |----| return undefined } return new Range(start, end) } union(other: Range): Range { if (this.contains(other)) { return this } else if (other.contains(this)) { return other } const start = Position.Min(other.start, this._start) const end = Position.Max(other.end, this.end) return new Range(start, end) } get isEmpty(): boolean { return this._start.isEqual(this._end) } get isSingleLine(): boolean { return this._start.line === this._end.line } with(change: { start?: Position; end?: Position }): Range with(start?: Position, end?: Position): Range with( startOrChange: Position | undefined | { start?: Position; end?: Position }, end: Position = this.end ): Range { if (startOrChange === null || end === null) { throw illegalArgument() } let start: Position if (!startOrChange) { start = this.start } else if (Position.isPosition(startOrChange)) { start = startOrChange } else { start = startOrChange.start || this.start end = startOrChange.end || this.end } if (start.isEqual(this._start) && end.isEqual(this.end)) { return this } return new Range(start, end) } toJSON(): any { return [this.start, this.end] } } export class Selection extends Range { static isSelection(thing: any): thing is Selection { if (thing instanceof Selection) { return true } if (!thing) { return false } return ( Range.isRange(thing) && Position.isPosition((thing).anchor) && Position.isPosition((thing).active) && typeof (thing).isReversed === "boolean" ) } private _anchor: Position public get anchor(): Position { return this._anchor } private _active: Position public get active(): Position { return this._active } constructor(anchor: Position, active: Position) constructor( anchorLine: number, anchorColumn: number, activeLine: number, activeColumn: number ) constructor( anchorLineOrAnchor: number | Position, anchorColumnOrActive: number | Position, activeLine?: number, activeColumn?: number ) { let anchor: Position | undefined let active: Position | undefined if ( typeof anchorLineOrAnchor === "number" && typeof anchorColumnOrActive === "number" && typeof activeLine === "number" && typeof activeColumn === "number" ) { anchor = new Position(anchorLineOrAnchor, anchorColumnOrActive) active = new Position(activeLine, activeColumn) } else if ( anchorLineOrAnchor instanceof Position && anchorColumnOrActive instanceof Position ) { anchor = anchorLineOrAnchor active = anchorColumnOrActive } if (!anchor || !active) { throw new Error("Invalid arguments") } super(anchor, active) this._anchor = anchor this._active = active } get isReversed(): boolean { return this._anchor === this._end } toJSON() { return { start: this.start, end: this.end, active: this.active, anchor: this.anchor, } } } export enum EndOfLine { LF = 1, CRLF = 2, } export class TextEdit { static isTextEdit(thing: any): thing is TextEdit { if (thing instanceof TextEdit) { return true } if (!thing) { return false } return ( Range.isRange(thing) && typeof (thing).newText === "string" ) } static replace(range: Range, newText: string): TextEdit { return new TextEdit(range, newText) } static insert(position: Position, newText: string): TextEdit { return TextEdit.replace(new Range(position, position), newText) } static delete(range: Range): TextEdit { return TextEdit.replace(range, "") } static setEndOfLine(eol: EndOfLine): TextEdit { const ret = new TextEdit( new Range(new Position(0, 0), new Position(0, 0)), "" ) ret.newEol = eol return ret } protected _range: Range protected _newText: string | null protected _newEol: EndOfLine get range(): Range { return this._range } set range(value: Range) { if (value && !Range.isRange(value)) { throw illegalArgument("range") } this._range = value } get newText(): string { return this._newText || "" } set newText(value: string) { if (value && typeof value !== "string") { throw illegalArgument("newText") } this._newText = value } get newEol(): EndOfLine { return this._newEol } set newEol(value: EndOfLine) { if (value && typeof value !== "number") { throw illegalArgument("newEol") } this._newEol = value } constructor(range: Range, newText: string | null) { this.range = range this._newText = newText } toJSON(): any { return { range: this.range, newText: this.newText, newEol: this._newEol, } } } export interface IFileOperationOptions { overwrite?: boolean ignoreIfExists?: boolean ignoreIfNotExists?: boolean recursive?: boolean } export interface IFileOperation { _type: 1 from?: Uri to?: Uri options?: IFileOperationOptions } export interface IFileTextEdit { _type: 2 uri: Uri edit: TextEdit } export class WorkspaceEdit { private _edits = new Array() renameFile( from: Uri, to: Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean } ): void { this._edits.push({ _type: 1, from, to, options }) } createFile( uri: Uri, options?: { overwrite?: boolean; ignoreIfExists?: boolean } ): void { this._edits.push({ _type: 1, from: undefined, to: uri, options }) } deleteFile( uri: Uri, options?: { recursive?: boolean; ignoreIfNotExists?: boolean } ): void { this._edits.push({ _type: 1, from: uri, to: undefined, options }) } replace(uri: Uri, range: Range, newText: string): void { this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) }) } insert(resource: Uri, position: Position, newText: string): void { this.replace(resource, new Range(position, position), newText) } delete(resource: Uri, range: Range): void { this.replace(resource, range, "") } has(uri: Uri): boolean { for (const edit of this._edits) { if (edit._type === 2 && edit.uri.toString() === uri.toString()) { return true } } return false } set(uri: Uri, edits: TextEdit[]): void { if (!edits) { // remove all text edits for `uri` for (let i = 0; i < this._edits.length; i++) { const element = this._edits[i] if (element._type === 2 && element.uri.toString() === uri.toString()) { this._edits[i] = undefined! // will be coalesced down below } } // this._edits = coalesce(this._edits); TODO } else { // append edit to the end for (const edit of edits) { if (edit) { this._edits.push({ _type: 2, uri, edit }) } } } } get(uri: Uri): TextEdit[] { const res: TextEdit[] = [] for (let candidate of this._edits) { if ( candidate._type === 2 && candidate.uri.toString() === uri.toString() ) { res.push(candidate.edit) } } return res } entries(): [Uri, TextEdit[]][] { const textEdits = new Map() for (let candidate of this._edits) { if (candidate._type === 2) { let textEdit = textEdits.get(candidate.uri.toString()) if (!textEdit) { textEdit = [candidate.uri, []] textEdits.set(candidate.uri.toString(), textEdit) } textEdit[1].push(candidate.edit) } } return values(textEdits) } _allEntries(): ([Uri, TextEdit[]] | [Uri?, Uri?, IFileOperationOptions?])[] { const res: ([Uri, TextEdit[]] | [Uri?, Uri?, IFileOperationOptions?])[] = [] for (let edit of this._edits) { if (edit._type === 1) { res.push([edit.from, edit.to, edit.options]) } else { res.push([edit.uri, [edit.edit]]) } } return res } get size(): number { return this.entries().length } toJSON(): any { return this.entries() } } export enum TextEditorRevealType { Default = 0, InCenter = 1, InCenterIfOutsideViewport = 2, AtTop = 3, } export enum TextEditorSelectionChangeKind { Keyboard = 1, Mouse = 2, Command = 3, } export class SnippetString { static isSnippetString(thing: any): thing is SnippetString { if (thing instanceof SnippetString) { return true } if (!thing) { return false } return typeof (thing).value === "string" } private static _escape(value: string): string { return value.replace(/\$|}|\\/g, "\\$&") } private _tabstop: number = 1 value: string constructor(value?: string) { this.value = value || "" } appendText(string: string): SnippetString { this.value += SnippetString._escape(string) return this } appendTabstop(number: number = this._tabstop++): SnippetString { this.value += "$" this.value += number return this } appendPlaceholder( value: string | ((snippet: SnippetString) => any), number: number = this._tabstop++ ): SnippetString { if (typeof value === "function") { const nested = new SnippetString() nested._tabstop = this._tabstop value(nested) this._tabstop = nested._tabstop value = nested.value } else { value = SnippetString._escape(value) } this.value += "${" this.value += number this.value += ":" this.value += value this.value += "}" return this } appendVariable( name: string, defaultValue?: string | ((snippet: SnippetString) => any) ): SnippetString { if (typeof defaultValue === "function") { const nested = new SnippetString() nested._tabstop = this._tabstop defaultValue(nested) this._tabstop = nested._tabstop defaultValue = nested.value } else if (typeof defaultValue === "string") { defaultValue = defaultValue.replace(/\$|}/g, "\\$&") } this.value += "${" this.value += name if (defaultValue) { this.value += ":" this.value += defaultValue } this.value += "}" return this } } ================================================ FILE: client/src/components/editors/monaco-markdown/formatting.ts ================================================ // @ts-nocheck 'use strict'; import {editor, KeyCode, KeyMod} from 'monaco-editor'; import { fixMarker } from './listEditing'; import {TextDocument, TextEditor} from "./vscode-monaco"; import {Position, Selection, Range, WorkspaceEdit} from "./extHostTypes"; export function addKeybinding(editor: TextEditor, name: String, fun: CallableFunction, keybindings: number[], label?: string, context?: string, contextMenuGroupId = "markdown.extension.editing") { editor.addAction({ contextMenuGroupId: contextMenuGroupId, contextMenuOrder: 0, id: "markdown.extension.editing." + name, keybindingContext: context, keybindings: keybindings, label: label, precondition: "", run(_: editor.ICodeEditor): void | Promise { fun(editor) return undefined; } }); } export function activateFormatting(editor: TextEditor) { addKeybinding(editor, "toggleBold", toggleBold, [KeyMod.CtrlCmd | KeyCode.KeyB], "Toggle bold"); addKeybinding(editor, "toggleItalic", toggleItalic, [KeyMod.CtrlCmd | KeyCode.KeyI], "Toggle italic"); addKeybinding(editor, "toggleCodeSpan", toggleCodeSpan, [KeyMod.CtrlCmd | KeyCode.Backquote], "Toggle code span"); addKeybinding(editor, "toggleStrikethrough", toggleStrikethrough, [KeyMod.Alt | KeyCode.KeyS], "Toggle strikethrough"); addKeybinding(editor, "toggleMath", toggleMath, [KeyMod.CtrlCmd | KeyCode.KeyM], "Toggle math"); addKeybinding(editor, "toggleMathReverse", toggleMathReverse, [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyM], "Toggle math reverse"); addKeybinding(editor, "toggleHeadingUp", toggleHeadingUp, [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.BracketLeft], "Heading up"); addKeybinding(editor, "toggleHeadingDown", toggleHeadingDown, [KeyMod.WinCtrl | KeyMod.Shift | KeyCode.BracketRight], "Heading down"); addKeybinding(editor, "toggleList", toggleList, [KeyMod.CtrlCmd | KeyCode.KeyL], "Toggle list"); // addKeybinding(editor, paste, [KeyMod.CtrlCmd | KeyCode.KEY_B], "Toggle bold"); } /** * Here we store Regexp to check if the text is the single link. */ const singleLinkRegex: RegExp = createLinkRegex(); // Return Promise because need to chain operations in unit tests function toggleBold(editor: TextEditor) { return styleByWrapping(editor, '**'); } function toggleItalic(editor: TextEditor) { // let indicator = workspace.getConfiguration('markdown.extension.italic').get('indicator'); return styleByWrapping(editor, '*'); } function toggleCodeSpan(editor: TextEditor) { return styleByWrapping(editor, '`'); } function toggleStrikethrough(editor: TextEditor) { return styleByWrapping(editor, '~~'); } const maxHeading = '######'; function toggleHeadingUp(editor: TextEditor) { let lineIndex = editor.selection.active.line; let lineText = editor.document.lineAt(lineIndex).text; return editor.edit((editBuilder) => { if (!lineText.startsWith('#')) { // Not a heading editBuilder.insert(new Position(lineIndex, 0), '# '); } else if (lineText.startsWith(maxHeading)) { // Reset heading at 6 level let deleteIndex = lineText.startsWith(maxHeading + ' ') ? maxHeading.length + 1 : maxHeading.length; editBuilder.delete(new Range(new Position(lineIndex, 0), new Position(lineIndex, deleteIndex))); } else { editBuilder.insert(new Position(lineIndex, 0), '#'); } }); } function toggleHeadingDown(editor: TextEditor) { let lineIndex = editor.selection.active.line; let lineText = editor.document.lineAt(lineIndex).text; editor.edit((editBuilder) => { if (lineText.startsWith('# ')) { // Heading level 1 editBuilder.delete(new Range(new Position(lineIndex, 0), new Position(lineIndex, 2))); } else if (lineText.startsWith('#')) { // Heading (but not level 1) editBuilder.delete(new Range(new Position(lineIndex, 0), new Position(lineIndex, 1))); } else { // No heading editBuilder.insert(new Position(lineIndex, 0), maxHeading + ' '); } }); } enum MathBlockState { // State 1: not in any others states NONE, // State 2: $|$ INLINE, // State 3: $$ | $$ SINGLE_DISPLAYED, // State 4: // $$ // | // $$ MULTI_DISPLAYED } function getMathState(editor: TextEditor, cursor: Position): MathBlockState { if (getContext(editor, cursor, '$') === '$|$') { return MathBlockState.INLINE; } else if (getContext(editor, cursor, '$$ ', ' $$') === '$$ | $$') { return MathBlockState.SINGLE_DISPLAYED; } else if ( editor.document.lineAt(cursor.line).text === '' && cursor.line > 0 && editor.document.lineAt(cursor.line - 1).text === '$$' && cursor.line < editor.document.lineCount - 1 && editor.document.lineAt(cursor.line + 1).text === '$$' ) { return MathBlockState.MULTI_DISPLAYED } else { return MathBlockState.NONE; } } /** * Modify the document, change from `oldMathBlockState` to `newMathBlockState`. * @param editor * @param cursor * @param oldMathBlockState * @param newMathBlockState */ function setMathState(editor: TextEditor, cursor: Position, oldMathBlockState: MathBlockState, newMathBlockState: MathBlockState) { // Step 1: Delete old math block. editor.edit(editBuilder => { let rangeToBeDeleted: Range switch (oldMathBlockState) { case MathBlockState.NONE: rangeToBeDeleted = new Range(cursor, cursor); break; case MathBlockState.INLINE: rangeToBeDeleted = new Range(new Position(cursor.line, cursor.character - 1), new Position(cursor.line, cursor.character + 1)); break; case MathBlockState.SINGLE_DISPLAYED: rangeToBeDeleted = new Range(new Position(cursor.line, cursor.character - 3), new Position(cursor.line, cursor.character + 3)); break; case MathBlockState.MULTI_DISPLAYED: rangeToBeDeleted = new Range(new Position(cursor.line - 1, 0), new Position(cursor.line + 1, 2)); break; } editBuilder.delete(rangeToBeDeleted) }).then(() => { // Step 2: Insert new math block. editor.edit(editBuilder => { let newCursor = editor.selection.active; let stringToBeInserted: string switch (newMathBlockState) { case MathBlockState.NONE: stringToBeInserted = '' break; case MathBlockState.INLINE: stringToBeInserted = '$$' break; case MathBlockState.SINGLE_DISPLAYED: stringToBeInserted = '$$ $$' break; case MathBlockState.MULTI_DISPLAYED: stringToBeInserted = '$$\n\n$$' break; } editBuilder.insert(newCursor, stringToBeInserted); }).then(() => { // Step 3: Move cursor to the middle. let newCursor = editor.selection.active; let newPosition: Position; switch (newMathBlockState) { case MathBlockState.NONE: newPosition = newCursor break; case MathBlockState.INLINE: newPosition = newCursor.with(newCursor.line, newCursor.character - 1) break; case MathBlockState.SINGLE_DISPLAYED: newPosition = newCursor.with(newCursor.line, newCursor.character - 3) break; case MathBlockState.MULTI_DISPLAYED: newPosition = newCursor.with(newCursor.line - 1, 0) break; } editor.selection = new Selection(newPosition, newPosition); }) }); } const transTable = [ MathBlockState.NONE, MathBlockState.INLINE, MathBlockState.MULTI_DISPLAYED, MathBlockState.SINGLE_DISPLAYED ]; const reverseTransTable = new Array(...transTable).reverse(); function toggleMath(editor: TextEditor) { _toggleMath(editor, transTable) } function toggleMathReverse(editor: TextEditor) { _toggleMath(editor, reverseTransTable) } function _toggleMath(editor: TextEditor, transTable: MathBlockState[]) { if (!editor.selection.isEmpty) return; let cursor = editor.selection.active; let oldMathBlockState = getMathState(editor, cursor) let currentStateIndex = transTable.indexOf(oldMathBlockState); setMathState(editor, cursor, oldMathBlockState, transTable[(currentStateIndex + 1) % transTable.length]) } function toggleList(editor: TextEditor) { const doc = editor.document; let batchEdit = new WorkspaceEdit(); editor.selections.forEach(selection => { if (selection.isEmpty) { toggleListSingleLine(doc, selection.active.line, batchEdit); } else { for (let i = selection.start.line; i <= selection.end.line; i++) { toggleListSingleLine(doc, i, batchEdit); } } }); return editor.applyEdit(batchEdit, []) .then(() => fixMarker(editor)); } function toggleListSingleLine(doc: TextDocument, line: number, wsEdit: WorkspaceEdit) { const lineText = doc.lineAt(line).text; const indentation = lineText.trim().length === 0 ? lineText.length : lineText.indexOf(lineText.trim()); const lineTextContent = lineText.substr(indentation); if (lineTextContent.startsWith("- ")) { wsEdit.replace(doc.uri, new Range(line, indentation, line, indentation + 2), "* "); } else if (lineTextContent.startsWith("* ")) { wsEdit.replace(doc.uri, new Range(line, indentation, line, indentation + 2), "+ "); } else if (lineTextContent.startsWith("+ ")) { wsEdit.replace(doc.uri, new Range(line, indentation, line, indentation + 2), "1. "); } else if (/^\d\. /.test(lineTextContent)) { wsEdit.replace(doc.uri, new Range(line, indentation + 1, line, indentation + 2), ")"); } else if (/^\d\) /.test(lineTextContent)) { wsEdit.delete(doc.uri, new Range(line, indentation, line, indentation + 3)); } else { wsEdit.insert(doc.uri, new Position(line, indentation), "- "); } } // async function paste() { // const editor = window.activeTextEditor; // const selection = editor.selection; // if (selection.isSingleLine && !isSingleLink(editor.document.getText(selection))) { // const text = await env.clipboard.readText(); // if (isSingleLink(text)) { // return commands.executeCommand("editor.action.insertSnippet", { "snippet": `[$TM_SELECTED_TEXT$0](${text})` }); // } // } // return commands.executeCommand("editor.action.clipboardPasteAction"); // } /** * Creates Regexp to check if the text is a link (further detailes in the isSingleLink() documentation). * * @return Regexp */ function createLinkRegex(): RegExp { // unicode letters range(must not be a raw string) const ul = '\\u00a1-\\uffff'; // IP patterns const ipv4_re = '(?:25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(?:\\.(?:25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}'; const ipv6_re = '\\[[0-9a-f:\\.]+\\]'; // simple regex (in django it is validated additionally) // Host patterns const hostname_re = '[a-z' + ul + '0-9](?:[a-z' + ul + '0-9-]{0,61}[a-z' + ul + '0-9])?'; // Max length for domain name labels is 63 characters per RFC 1034 sec. 3.1 const domain_re = '(?:\\.(?!-)[a-z' + ul + '0-9-]{1,63})*'; const tld_re = '' + '\\.' // dot + '(?!-)' // can't start with a dash + '(?:[a-z' + ul + '-]{2,63}' // domain label + '|xn--[a-z0-9]{1,59})' // or punycode label // + '(? { let cursorPos = selection.active; const shift = shifts.map(([pos, s]) => (selection.start.line == pos.line && selection.start.character >= pos.character) ? s : 0) .reduce((a, b) => a + b, 0); if (selection.isEmpty) { // No selected text if (startPattern !== '~~' && getContext(editor, cursorPos, startPattern) === `${startPattern}text|${endPattern}`) { // `**text|**` to `**text**|` let newCursorPos = cursorPos.with({character: cursorPos.character + shift + endPattern.length}); newSelections[i] = new Selection(newCursorPos, newCursorPos); return; } else if (getContext(editor, cursorPos, startPattern) === `${startPattern}|${endPattern}`) { // `**|**` to `|` let start = cursorPos.with({character: cursorPos.character - startPattern.length}); let end = cursorPos.with({character: cursorPos.character + endPattern.length}); wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, new Range(start, end), false, startPattern); } else { // Select word under cursor let wordRange = editor.document.getWordRangeAtPosition(cursorPos); if (wordRange == undefined) { wordRange = selection; } // One special case: toggle strikethrough in task list const currentTextLine = editor.document.lineAt(cursorPos.line); if (startPattern === '~~' && /^\s*[\*\+\-] (\[[ x]\] )? */g.test(currentTextLine.text)) { wordRange = currentTextLine.range.with(new Position(cursorPos.line, currentTextLine.text.match(/^\s*[\*\+\-] (\[[ x]\] )? */g)[0].length)); } wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, wordRange, false, startPattern); } } else { // Text selected wrapRange(editor, batchEdit, shifts, newSelections, i, shift, cursorPos, selection, true, startPattern); } }); const hasSelection = editor.selection && !editor.selection.isEmpty; return editor.applyEdit(batchEdit, newSelections).then(() => { if (!hasSelection) { editor.selections = newSelections; } }); } /** * Add or remove `startPattern`/`endPattern` according to the context * @param editor * @param options The undo/redo behavior * @param cursor cursor position * @param range range to be replaced * @param isSelected is this range selected * @param startPtn * @param endPtn */ function wrapRange(editor: TextEditor, wsEdit: WorkspaceEdit, shifts: [Position, number][], newSelections: Selection[], i: number, shift: number, cursor: Position, range: Range, isSelected: boolean, startPtn: string, endPtn?: string) { if (endPtn == undefined) { endPtn = startPtn; } let text = editor.document.getText(range); const prevSelection = newSelections[i]; const ptnLength = (startPtn + endPtn).length; let newCursorPos = cursor.with({character: cursor.character + shift}); let newSelection: Selection; if (isWrapped(text, startPtn)) { // remove start/end patterns from range wsEdit.replace(editor.document.uri, range, text.substr(startPtn.length, text.length - ptnLength)); shifts.push([range.end, -ptnLength]); // Fix cursor position if (!isSelected) { if (!range.isEmpty) { // means quick styling if (cursor.character == range.end.character) { newCursorPos = cursor.with({character: cursor.character + shift - ptnLength}); } else { newCursorPos = cursor.with({character: cursor.character + shift - startPtn.length}); } } else { // means `**|**` -> `|` newCursorPos = cursor.with({character: cursor.character + shift + startPtn.length}); } newSelection = new Selection(newCursorPos, newCursorPos); } else { newSelection = new Selection( prevSelection.start.with({character: prevSelection.start.character + shift}), prevSelection.end.with({character: prevSelection.end.character + shift - ptnLength}) ); } } else { // add start/end patterns around range wsEdit.replace(editor.document.uri, range, startPtn + text + endPtn); shifts.push([range.end, ptnLength]); // Fix cursor position if (!isSelected) { if (!range.isEmpty) { // means quick styling if (cursor.character == range.end.character) { newCursorPos = cursor.with({character: cursor.character + shift + ptnLength}); } else { newCursorPos = cursor.with({character: cursor.character + shift + startPtn.length}); } } else { // means `|` -> `**|**` newCursorPos = cursor.with({character: cursor.character + shift + startPtn.length}); } newSelection = new Selection(newCursorPos, newCursorPos); } else { newSelection = new Selection( prevSelection.start.with({character: prevSelection.start.character + shift}), prevSelection.end.with({character: prevSelection.end.character + shift + ptnLength}) ); } } newSelections[i] = newSelection; } function isWrapped(text: string, startPattern: string, endPattern?: string): boolean { if (endPattern == undefined) { endPattern = startPattern; } return text.startsWith(startPattern) && text.endsWith(endPattern); } function getContext(editor: TextEditor, cursorPos: Position, startPattern: string, endPattern?: string): string { if (endPattern == undefined) { endPattern = startPattern; } let startPositionCharacter = cursorPos.character - startPattern.length; let endPositionCharacter = cursorPos.character + endPattern.length; if (startPositionCharacter < 0) { startPositionCharacter = 0; } let leftText = editor.document.getText(new Range(cursorPos.line, startPositionCharacter, cursorPos.line, cursorPos.character)); let rightText = editor.document.getText(new Range(cursorPos.line, cursorPos.character, cursorPos.line, endPositionCharacter)); if (rightText == endPattern) { if (leftText == startPattern) { return `${startPattern}|${endPattern}`; } else { return `${startPattern}text|${endPattern}`; } } return '|'; } ================================================ FILE: client/src/components/editors/monaco-markdown/index.ts ================================================ // @ts-nocheck import { editor } from "monaco-editor" import {activateFormatting} from "./formatting"; import {setWordDefinitionFor, TextEditor} from "./vscode-monaco"; import {activateListEditing} from "./listEditing"; import {activateCompletion} from "./completion"; import {activateTableFormatter} from "./tableFormatter"; import {activateMarkdownMath} from "./markdown.contribution"; export class MonacoMarkdownExtension { activate(editor: editor.IStandaloneCodeEditor) { let textEditor = new TextEditor(editor) activateFormatting(textEditor) activateListEditing(textEditor) activateCompletion(textEditor) activateTableFormatter(textEditor) // Allow `*` in word pattern for quick styling setWordDefinitionFor(textEditor.languageId, /(-?\d*\.\d\w*)|([^\!\@\#\%\^\&\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s\,\。\《\》\?\;\:\‘\“\’\”\(\)\【\】\、]+)/g ); } } activateMarkdownMath() ================================================ FILE: client/src/components/editors/monaco-markdown/latex.ts ================================================ // @ts-nocheck 'use strict'; //copied from ../completion.ts //TODO: refactor? let accents1 = [ 'tilde', 'mathring', 'widetilde', 'overgroup', 'utilde', 'undergroup', 'acute', 'vec', 'Overrightarrow', 'bar', 'overleftarrow', 'overrightarrow', 'breve', 'underleftarrow', 'underrightarrow', 'check', 'overleftharpoon', 'overrightharpoon', 'dot', 'overleftrightarrow', 'overbrace', 'ddot', 'underleftrightarrow', 'underbrace', 'grave', 'overline', 'overlinesegment', 'hat', 'underline', 'underlinesegment', 'widehat', 'widecheck' ]; let delimiters0 = [ 'lparen', 'rparen', 'lceil', 'rceil', 'uparrow', 'lbrack', 'rbrack', 'lfloor', 'rfloor', 'downarrow', 'updownarrow', 'langle', 'rangle', 'lgroup', 'rgroup', 'Uparrow', 'vert', 'ulcorner', 'urcorner', 'Downarrow', 'Vert', 'llcorner', 'lrcorner', 'Updownarrow', 'lvert', 'rvert', 'lVert', 'rVert', 'backslash', 'lang', 'rang', 'lt', 'gt' ]; let delimeterSizing0 = [ 'left', 'big', 'bigl', 'bigm', 'bigr', 'middle', 'Big', 'Bigl', 'Bigm', 'Bigr', 'right', 'bigg', 'biggl', 'biggm', 'biggr', 'Bigg', 'Biggl', 'Biggm', 'Biggr' ]; let greekLetters0 = [ 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi', 'Omicron', 'Pi', 'Sigma', 'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega', 'varGamma', 'varDelta', 'varTheta', 'varLambda', 'varXi', 'varPi', 'varSigma', 'varUpsilon', 'varPhi', 'varPsi', 'varOmega', 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega', 'varepsilon', 'varkappa', 'vartheta', 'thetasym', 'varpi', 'varrho', 'varsigma', 'varphi', 'digamma' ]; let otherLetters0 = [ 'imath', 'nabla', 'Im', 'Reals', 'jmath', 'partial', 'image', 'wp', 'aleph', 'Game', 'Bbbk', 'weierp', 'alef', 'Finv', 'N', 'Z', 'alefsym', 'cnums', 'natnums', 'beth', 'Complex', 'R', 'gimel', 'ell', 'Re', 'daleth', 'hbar', 'real', 'eth', 'hslash', 'reals' ]; let annotation1 = [ 'cancel', 'overbrace', 'bcancel', 'underbrace', 'xcancel', 'not =', 'sout', 'boxed', 'tag', 'tag*' ]; let verticalLayout0 = ['atop'] let verticalLayout2 = ['stackrel', 'overset', 'underset', 'raisebox']; let overlap1 = ['mathllap', 'mathrlap', 'mathclap', 'llap', 'rlap', 'clap', 'smash']; let spacing0 = [ 'thinspace', 'medspace', 'thickspace', 'enspace', 'quad', 'qquad', 'negthinspace', 'negmedspace', 'nobreakspace', 'negthickspace' ]; let spacing1 = [ 'kern', 'mkern', 'mskip', 'hskip', 'hspace', 'hspace*', 'phantom', 'hphantom', 'vphantom' ]; let logicAndSetTheory0 = [ 'forall', 'complement', 'therefore', 'emptyset', 'exists', 'subset', 'because', 'empty', 'exist', 'supset', 'mapsto', 'varnothing', 'nexists', 'mid', 'to', 'implies', 'in', 'land', 'gets', 'impliedby', 'isin', 'lor', 'leftrightarrow', 'iff', 'notin', 'ni', 'notni', 'neg', 'lnot' ]; let bigOperators0 = [ 'sum', 'prod', 'bigotimes', 'bigvee', 'int', 'coprod', 'bigoplus', 'bigwedge', 'iint', 'intop', 'bigodot', 'bigcap', 'iiint', 'smallint', 'biguplus', 'bigcup', 'oint', 'oiint', 'oiiint', 'bigsqcup' ]; let binaryOperators0 = [ 'cdot', 'gtrdot', 'pmod', 'cdotp', 'intercal', 'pod', 'centerdot', 'land', 'rhd', 'circ', 'leftthreetimes', 'rightthreetimes', 'amalg', 'circledast', 'ldotp', 'rtimes', 'And', 'circledcirc', 'lor', 'setminus', 'ast', 'circleddash', 'lessdot', 'smallsetminus', 'barwedge', 'Cup', 'lhd', 'sqcap', 'bigcirc', 'cup', 'ltimes', 'sqcup', 'bmod', 'curlyvee', 'times', 'boxdot', 'curlywedge', 'mp', 'unlhd', 'boxminus', 'div', 'odot', 'unrhd', 'boxplus', 'divideontimes', 'ominus', 'uplus', 'boxtimes', 'dotplus', 'oplus', 'vee', 'bullet', 'doublebarwedge', 'otimes', 'veebar', 'Cap', 'doublecap', 'oslash', 'wedge', 'cap', 'doublecup', 'pm', 'plusmn', 'wr' ]; let fractions0 = ['over', 'above']; let fractions2 = ['frac', 'dfrac', 'tfrac', 'cfrac', 'genfrac']; let binomialCoefficients0 = ['choose']; let binomialCoefficients2 = ['binom', 'dbinom', 'tbinom', 'brace', 'brack']; let mathOperators0 = [ 'arcsin', 'cotg', 'ln', 'det', 'arccos', 'coth', 'log', 'gcd', 'arctan', 'csc', 'sec', 'inf', 'arctg', 'ctg', 'sin', 'lim', 'arcctg', 'cth', 'sinh', 'liminf', 'arg', 'deg', 'sh', 'limsup', 'ch', 'dim', 'tan', 'max', 'cos', 'exp', 'tanh', 'min', 'cosec', 'hom', 'tg', 'Pr', 'cosh', 'ker', 'th', 'sup', 'cot', 'lg', 'argmax', 'argmin', 'limits' ]; let mathOperators1 = ['operatorname']; let sqrt1 = ['sqrt']; let relations0 = [ 'eqcirc', 'lesseqgtr', 'sqsupset', 'eqcolon', 'lesseqqgtr', 'sqsupseteq', 'Eqcolon', 'lessgtr', 'Subset', 'eqqcolon', 'lesssim', 'subset', 'approx', 'Eqqcolon', 'll', 'subseteq', 'sube', 'approxeq', 'eqsim', 'lll', 'subseteqq', 'asymp', 'eqslantgtr', 'llless', 'succ', 'backepsilon', 'eqslantless', 'lt', 'succapprox', 'backsim', 'equiv', 'mid', 'succcurlyeq', 'backsimeq', 'fallingdotseq', 'models', 'succeq', 'between', 'frown', 'multimap', 'succsim', 'bowtie', 'ge', 'owns', 'Supset', 'bumpeq', 'geq', 'parallel', 'supset', 'Bumpeq', 'geqq', 'perp', 'supseteq', 'circeq', 'geqslant', 'pitchfork', 'supseteqq', 'colonapprox', 'gg', 'prec', 'thickapprox', 'Colonapprox', 'ggg', 'precapprox', 'thicksim', 'coloneq', 'gggtr', 'preccurlyeq', 'trianglelefteq', 'Coloneq', 'gt', 'preceq', 'triangleq', 'coloneqq', 'gtrapprox', 'precsim', 'trianglerighteq', 'Coloneqq', 'gtreqless', 'propto', 'varpropto', 'colonsim', 'gtreqqless', 'risingdotseq', 'vartriangle', 'Colonsim', 'gtrless', 'shortmid', 'vartriangleleft', 'cong', 'gtrsim', 'shortparallel', 'vartriangleright', 'curlyeqprec', 'in', 'sim', 'vcentcolon', 'curlyeqsucc', 'Join', 'simeq', 'vdash', 'dashv', 'le', 'smallfrown', 'vDash', 'dblcolon', 'leq', 'smallsmile', 'Vdash', 'doteq', 'leqq', 'smile', 'Vvdash', 'Doteq', 'leqslant', 'sqsubset', 'doteqdot', 'lessapprox', 'sqsubseteq' ]; let negatedRelations0 = [ 'gnapprox', 'ngeqslant', 'nsubseteq', 'precneqq', 'gneq', 'ngtr', 'nsubseteqq', 'precnsim', 'gneqq', 'nleq', 'nsucc', 'subsetneq', 'gnsim', 'nleqq', 'nsucceq', 'subsetneqq', 'gvertneqq', 'nleqslant', 'nsupseteq', 'succnapprox', 'lnapprox', 'nless', 'nsupseteqq', 'succneqq', 'lneq', 'nmid', 'ntriangleleft', 'succnsim', 'lneqq', 'notin', 'ntrianglelefteq', 'supsetneq', 'lnsim', 'notni', 'ntriangleright', 'supsetneqq', 'lvertneqq', 'nparallel', 'ntrianglerighteq', 'varsubsetneq', 'ncong', 'nprec', 'nvdash', 'varsubsetneqq', 'ne', 'npreceq', 'nvDash', 'varsupsetneq', 'neq', 'nshortmid', 'nVDash', 'varsupsetneqq', 'ngeq', 'nshortparallel', 'nVdash', 'ngeqq', 'nsim', 'precnapprox' ]; let arrows0 = [ 'circlearrowleft', 'leftharpoonup', 'rArr', 'circlearrowright', 'leftleftarrows', 'rarr', 'curvearrowleft', 'leftrightarrow', 'restriction', 'curvearrowright', 'Leftrightarrow', 'rightarrow', 'Darr', 'leftrightarrows', 'Rightarrow', 'dArr', 'leftrightharpoons', 'rightarrowtail', 'darr', 'leftrightsquigarrow', 'rightharpoondown', 'dashleftarrow', 'Lleftarrow', 'rightharpoonup', 'dashrightarrow', 'longleftarrow', 'rightleftarrows', 'downarrow', 'Longleftarrow', 'rightleftharpoons', 'Downarrow', 'longleftrightarrow', 'rightrightarrows', 'downdownarrows', 'Longleftrightarrow', 'rightsquigarrow', 'downharpoonleft', 'longmapsto', 'Rrightarrow', 'downharpoonright', 'longrightarrow', 'Rsh', 'gets', 'Longrightarrow', 'searrow', 'Harr', 'looparrowleft', 'swarrow', 'hArr', 'looparrowright', 'to', 'harr', 'Lrarr', 'twoheadleftarrow', 'hookleftarrow', 'lrArr', 'twoheadrightarrow', 'hookrightarrow', 'lrarr', 'Uarr', 'iff', 'Lsh', 'uArr', 'impliedby', 'mapsto', 'uarr', 'implies', 'nearrow', 'uparrow', 'Larr', 'nleftarrow', 'Uparrow', 'lArr', 'nLeftarrow', 'updownarrow', 'larr', 'nleftrightarrow', 'Updownarrow', 'leadsto', 'nLeftrightarrow', 'upharpoonleft', 'leftarrow', 'nrightarrow', 'upharpoonright', 'Leftarrow', 'nRightarrow', 'upuparrows', 'leftarrowtail', 'nwarrow', 'leftharpoondown', 'Rarr' ]; let extensibleArrows1 = [ 'xleftarrow', 'xrightarrow', 'xLeftarrow', 'xRightarrow', 'xleftrightarrow', 'xLeftrightarrow', 'xhookleftarrow', 'xhookrightarrow', 'xtwoheadleftarrow', 'xtwoheadrightarrow', 'xleftharpoonup', 'xrightharpoonup', 'xleftharpoondown', 'xrightharpoondown', 'xleftrightharpoons', 'xrightleftharpoons', 'xtofrom', 'xmapsto', 'xlongequal' ]; let classAssignment1 = [ 'mathbin', 'mathclose', 'mathinner', 'mathop', 'mathopen', 'mathord', 'mathpunct', 'mathrel' ]; let color2 = ['color', 'textcolor', 'colorbox']; let font0 = ['rm', 'bf', 'it', 'sf', 'tt']; let font1 = [ 'mathrm', 'mathbf', 'mathit', 'mathnormal', 'textbf', 'textit', 'textrm', 'bold', 'Bbb', 'textnormal', 'boldsymbol', 'mathbb', 'text', 'bm', 'frak', 'mathsf', 'mathtt', 'mathfrak', 'textsf', 'texttt', 'mathcal', 'mathscr' ]; let size0 = [ 'Huge', 'huge', 'LARGE', 'Large', 'large', 'normalsize', 'small', 'footnotesize', 'scriptsize', 'tiny' ]; let style0 = [ 'displaystyle', 'textstyle', 'scriptstyle', 'scriptscriptstyle', 'limits', 'nolimits', 'verb' ]; let symbolsAndPunctuation0 = [ 'cdots', 'LaTeX', 'ddots', 'TeX', 'ldots', 'nabla', 'vdots', 'infty', 'dotsb', 'infin', 'dotsc', 'checkmark', 'dotsi', 'dag', 'dotsm', 'dagger', 'dotso', 'sdot', 'ddag', 'mathellipsis', 'ddagger', 'Box', 'Dagger', 'lq', 'square', 'angle', 'blacksquare', 'measuredangle', 'rq', 'triangle', 'sphericalangle', 'triangledown', 'top', 'triangleleft', 'bot', 'triangleright', 'colon', 'bigtriangledown', 'backprime', 'bigtriangleup', 'pounds', 'prime', 'blacktriangle', 'mathsterling', 'blacktriangledown', 'blacktriangleleft', 'yen', 'blacktriangleright', 'surd', 'diamond', 'degree', 'Diamond', 'lozenge', 'mho', 'blacklozenge', 'diagdown', 'star', 'diagup', 'bigstar', 'flat', 'clubsuit', 'natural', 'copyright', 'clubs', 'sharp', 'circledR', 'diamondsuit', 'heartsuit', 'diamonds', 'hearts', 'circledS', 'spadesuit', 'spades', 'maltese' ]; export let _c1 = Array.from(new Set( [ ...delimiters0, ...delimeterSizing0, ...greekLetters0, ...otherLetters0, ...spacing0, ...verticalLayout0, ...logicAndSetTheory0, ...bigOperators0, ...binaryOperators0, ...binomialCoefficients0, ...fractions0, ...mathOperators0, ...relations0, ...negatedRelations0, ...arrows0, ...font0, ...size0, ...style0, ...symbolsAndPunctuation0 ] )) export let _c2 = Array.from(new Set( [ ...accents1, ...annotation1, ...overlap1, ...spacing1, ...mathOperators1, ...sqrt1, ...extensibleArrows1, ...font1, ...classAssignment1 ] )) export let _c3 = Array.from(new Set( [ ...verticalLayout2, ...binomialCoefficients2, ...fractions2, ...color2 ] )) export let _begin = ['begin', 'end'] export let begin_args = ['aligned', 'alignedat', 'array', 'bmatrix', 'Bmatrix', 'cases', 'darray', 'dcases', 'gathered', 'matrix', 'pmatrix', 'vmatrix', 'Vmatrix'] export let all = [..._c1, ..._c2, ..._c3, ..._begin].map(i => '\\' + i) ================================================ FILE: client/src/components/editors/monaco-markdown/listEditing.ts ================================================ // @ts-nocheck "use strict" import { TextEditor, TextEditorEdit } from "./vscode-monaco" import { isInFencedCodeBlock } from "./util" import { KeyCode, KeyMod, Thenable } from "monaco-editor" import { Position, WorkspaceEdit, Range, Selection } from "./extHostTypes" import { addKeybinding } from "./formatting" function onShiftTabKey(editor: TextEditor) { onTabKey(editor, "shift") } export function activateListEditing(editor: TextEditor) { let editorContext = "editorTextFocus && !editorReadonly && !suggestWidgetVisible" addKeybinding( editor, "onEnterKey", onEnterKey, [KeyCode.Enter], "", editorContext, null ) addKeybinding( editor, "onCtrlEnterKey", onCtrlEnterKey, [KeyCode.Enter | KeyMod.CtrlCmd], "", editorContext, null ) addKeybinding( editor, "onShiftEnterKey", onShiftEnterKey, [KeyCode.Enter | KeyMod.Shift], "", editorContext, null ) addKeybinding( editor, "onTabKey", onTabKey, [KeyCode.Tab], "", editorContext, null ) addKeybinding( editor, "onShiftTabKey", onShiftTabKey, [KeyCode.Tab | KeyMod.Shift], "", editorContext, null ) addKeybinding( editor, "onBackspaceKey", onBackspaceKey, [KeyCode.Backspace], "", editorContext, null ) // // context.subscriptions.push( // commands.registerCommand('markdown.extension.checkTaskList', checkTaskList), // commands.registerCommand('markdown.extension.onMoveLineDown', onMoveLineDown), // commands.registerCommand('markdown.extension.onMoveLineUp', onMoveLineUp), // commands.registerCommand('markdown.extension.onCopyLineDown', onCopyLineDown), // commands.registerCommand('markdown.extension.onCopyLineUp', onCopyLineUp), // commands.registerCommand('markdown.extension.onIndentLines', onIndentLines), // commands.registerCommand('markdown.extension.onOutdentLines', onOutdentLines) // ); } function onShiftEnterKey(editor: TextEditor) { onEnterKey(editor, "shift") } function onCtrlEnterKey(editor: TextEditor) { onEnterKey(editor, "ctrl") } function onEnterKey(editor: TextEditor, modifiers?: string) { let cursorPos: Position = editor.selection.active let line = editor.document.lineAt(cursorPos.line) let textBeforeCursor = line.text.substr(0, cursorPos.character) let textAfterCursor = line.text.substr(cursorPos.character) let lineBreakPos = cursorPos if (modifiers == "ctrl") { lineBreakPos = line.range.end } if ( modifiers == "shift" || isInFencedCodeBlock(editor.document, cursorPos.line) ) { return asNormal(editor, "enter", modifiers) } // If it's an empty list item, remove it if ( /^(>|([-+*]|[0-9]+[.)])( +\[[ x]\])?)$/.test(textBeforeCursor.trim()) && textAfterCursor.trim().length == 0 ) { return editor .edit((editBuilder: TextEditorEdit) => { editBuilder.delete(line.range) editBuilder.insert(line.range.end, "\n") }) .then(() => { editor.revealRange(editor.selection) }) .then(() => fixMarker(editor, findNextMarkerLineNumber(editor))) } let matches: RegExpExecArray if (/^> /.test(textBeforeCursor)) { // Quote block return editor .edit((editBuilder: TextEditorEdit) => { editBuilder.insert(lineBreakPos, `\n> `) }) .then(() => { // Fix cursor position if (modifiers == "ctrl" && !cursorPos.isEqual(lineBreakPos)) { let newCursorPos = cursorPos.with(line.lineNumber + 1, 2) editor.selection = new Selection(newCursorPos, newCursorPos) } }) .then(() => { editor.revealRange(editor.selection) }) } else if ( (matches = /^(\s*[-+*] +(\[[ x]\] +)?)/.exec(textBeforeCursor)) !== null ) { // Unordered list return editor .edit((editBuilder: TextEditorEdit) => { editBuilder.insert( lineBreakPos, `\n${matches[1].replace("[x]", "[ ]")}` ) }) .then(() => { // Fix cursor position if (modifiers == "ctrl" && !cursorPos.isEqual(lineBreakPos)) { let newCursorPos = cursorPos.with( line.lineNumber + 1, matches[1].length ) editor.selection = new Selection(newCursorPos, newCursorPos) } }) .then(() => { editor.revealRange(editor.selection) }) } else if ( (matches = /^(\s*)([0-9]+)([.)])( +)((\[[ x]\] +)?)/.exec( textBeforeCursor )) !== null ) { // Ordered list let config = editor .getConfiguration("markdown.extension.orderedList") .get("marker") let marker = "1" let leadingSpace = matches[1] let previousMarker = matches[2] let delimiter = matches[3] let trailingSpace = matches[4] let gfmCheckbox = matches[5].replace("[x]", "[ ]") let textIndent = (previousMarker + delimiter + trailingSpace).length if (config == "ordered") { marker = String(Number(previousMarker) + 1) } // Add enough trailing spaces so that the text is aligned with the previous list item, but always keep at least one space trailingSpace = " ".repeat( Math.max(1, textIndent - (marker + delimiter).length) ) const toBeAdded = leadingSpace + marker + delimiter + trailingSpace + gfmCheckbox return editor .edit( (editBuilder: TextEditorEdit) => { editBuilder.insert(lineBreakPos, `\n${toBeAdded}`) }, { undoStopBefore: true, undoStopAfter: false } ) .then(() => { // Fix cursor position if (modifiers == "ctrl" && !cursorPos.isEqual(lineBreakPos)) { let newCursorPos = cursorPos.with( line.lineNumber + 1, toBeAdded.length ) editor.selection = new Selection(newCursorPos, newCursorPos) } }) .then(() => fixMarker(editor)) .then(() => { editor.revealRange(editor.selection) }) } else { return asNormal(editor, "enter", modifiers) } } function onTabKey(editor: TextEditor, modifiers?: string) { let cursorPos = editor.selection.start let lineText = editor.document.lineAt(cursorPos.line).text if (isInFencedCodeBlock(editor.document, cursorPos.line)) { return asNormal(editor, "tab", modifiers) } let match = /^\s*([-+*]|[0-9]+[.)]) +(\[[ x]\] +)?/.exec(lineText) if ( match && (modifiers === "shift" || !editor.selection.isEmpty || (editor.selection.isEmpty && cursorPos.character <= match[0].length)) ) { if (modifiers === "shift") { return outdent(editor).then(() => fixMarker(editor)) } else { return indent(editor).then(() => fixMarker(editor)) } } else { return asNormal(editor, "tab", modifiers) } } function onBackspaceKey(editor: TextEditor) { let cursor = editor.selection.active let document = editor.document let textBeforeCursor = document .lineAt(cursor.line) .text.substr(0, cursor.character) if (isInFencedCodeBlock(document, cursor.line)) { return asNormal(editor, "backspace") } if (!editor.selection.isEmpty) { return asNormal(editor, "backspace").then(() => fixMarker(editor, findNextMarkerLineNumber(editor)) ) } else if (/^\s+([-+*]|[0-9]+[.)]) $/.test(textBeforeCursor)) { // e.g. textBeforeCursor === ` - `, ` 1. ` return outdent(editor).then(() => fixMarker(editor)) } else if (/^([-+*]|[0-9]+[.)]) $/.test(textBeforeCursor)) { // e.g. textBeforeCursor === `- `, `1. ` return editor .edit((editBuilder: TextEditorEdit) => { editBuilder.replace( new Range(cursor.with({ character: 0 }), cursor), " ".repeat(textBeforeCursor.length) ) }) .then(() => fixMarker(editor, findNextMarkerLineNumber(editor))) } else if (/^\s*([-+*]|[0-9]+[.)]) +(\[[ x]\] )$/.test(textBeforeCursor)) { // e.g. textBeforeCursor === `- [ ]`, `1. [x]`, ` - [x]` return deleteRange( editor, new Range(cursor.with({ character: textBeforeCursor.length - 4 }), cursor) ).then(() => fixMarker(editor, findNextMarkerLineNumber(editor))) } else { return asNormal(editor, "backspace") } } function asNormal( editor: TextEditor, key: string, modifiers?: string ): Thenable { switch (key) { case "enter": if (modifiers === "ctrl") { return editor.executeCommand("editor.action.insertLineAfter") } else { return editor.executeCommand("type", { source: "keyboard", text: "\n" }) } case "tab": if ( editor.getConfiguration("emmet").get("triggerExpansionOnTab") ) { return editor.executeCommand("editor.emmet.action.expandAbbreviation") } else if (modifiers === "shift") { return editor.executeCommand("editor.action.outdentLines") } else { return editor.executeCommand("tab") } case "backspace": return editor.executeCommand("deleteLeft") } } /** * If * * 1. it is not the first line * 2. there is a Markdown list item before this line * * then indent the current line to align with the previous list item. */ function indent(editor: TextEditor): Thenable { if ( editor .getConfiguration("markdown.extension.list") .get("indentationSize") === "adaptive" ) { try { const selection = editor.selection const indentationSize = tryDetermineIndentationSize( editor, selection.start.line, editor.document.lineAt(selection.start.line) .firstNonWhitespaceCharacterIndex ) let edit = new WorkspaceEdit() for (let i = selection.start.line; i <= selection.end.line; i++) { if ( i === selection.end.line && !selection.isEmpty && selection.end.character === 0 ) { break } if (editor.document.lineAt(i).text.length !== 0) { edit.insert( editor.document.uri, new Position(i, 0), " ".repeat(indentationSize) ) } } return editor.applyEdit(edit) } catch (error) {} } return editor.executeCommand("editor.action.indentLines") } /** * Similar to `indent`-function */ function outdent(editor?: TextEditor): Thenable { if ( editor .getConfiguration("markdown.extension.list") .get("indentationSize") === "adaptive" ) { try { const selection = editor.selection const indentationSize = tryDetermineIndentationSize( editor, selection.start.line, editor.document.lineAt(selection.start.line) .firstNonWhitespaceCharacterIndex ) let edit = new WorkspaceEdit() for (let i = selection.start.line; i <= selection.end.line; i++) { if ( i === selection.end.line && !selection.isEmpty && selection.end.character === 0 ) { break } const lineText = editor.document.lineAt(i).text let maxOutdentSize: number if (lineText.trim().length === 0) { maxOutdentSize = lineText.length } else { maxOutdentSize = editor.document.lineAt(i).firstNonWhitespaceCharacterIndex } if (maxOutdentSize > 0) { edit.delete( editor.document.uri, new Range(i, 0, i, Math.min(indentationSize, maxOutdentSize)) ) } } return editor.applyEdit(edit) } catch (error) {} } return editor.executeCommand("editor.action.outdentLines") } function tryDetermineIndentationSize( editor: TextEditor, line: number, currentIndentation: number ) { while (--line >= 0) { const lineText = editor.document.lineAt(line).text let matches if ( (matches = /^(\s*)(([-+*]|[0-9]+[.)]) +)(\[[ x]\] +)?/.exec(lineText)) !== null ) { if (matches[1].length <= currentIndentation) { return matches[2].length } } } throw "No previous Markdown list item" } /** * Returns the line number of the next ordered list item starting either from * the specified line or the beginning of the current selection. */ function findNextMarkerLineNumber(editor: TextEditor, line?: number): number { if (line === undefined) { // Use start.line instead of active.line so that we can find the first // marker following either the cursor or the entire selected range line = editor.selection.start.line } while (line < editor.document.lineCount) { const lineText = editor.document.lineAt(line).text if (/^\s*[0-9]+[.)] +/.exec(lineText) !== null) { return line } line++ } return undefined } /** * Looks for the previous ordered list marker at the same indentation level * and returns the marker number that should follow it. * * @returns the fixed marker number */ function lookUpwardForMarker( editor: TextEditor, line: number, currentIndentation: number ): number { while (--line >= 0) { const lineText = editor.document.lineAt(line).text let matches if ((matches = /^(\s*)(([0-9]+)[.)] +)/.exec(lineText)) !== null) { let leadingSpace: string = matches[1] let marker = matches[3] if (leadingSpace.length === currentIndentation) { return Number(marker) + 1 } else if ( (!leadingSpace.includes("\t") && leadingSpace.length + matches[2].length <= currentIndentation) || (leadingSpace.includes("\t") && leadingSpace.length + 1 <= currentIndentation) ) { return 1 } } else if ((matches = /^(\s*)\S/.exec(lineText)) !== null) { if (matches[1].length <= currentIndentation) { break } } } return 1 } /** * Fix ordered list marker *iteratively* starting from current line */ export function fixMarker(editor: TextEditor, line?: number): Promise { // if (!workspace.getConfiguration('markdown.extension.orderedList').get('autoRenumber')) return; // if (workspace.getConfiguration('markdown.extension.orderedList').get('marker') == 'one') return; if (line === undefined) { // Use either the first line containing an ordered list marker within the selection or the active line line = findNextMarkerLineNumber(editor) if (line === undefined || line > editor.selection.end.line) { line = editor.selection.active.line } } if (line < 0 || editor.document.lineCount <= line) { return } let currentLineText = editor.document.lineAt(line).text let matches if ((matches = /^(\s*)([0-9]+)([.)])( +)/.exec(currentLineText)) !== null) { // ordered list let leadingSpace = matches[1] let marker = matches[2] let delimiter = matches[3] let trailingSpace = matches[4] let fixedMarker = lookUpwardForMarker(editor, line, leadingSpace.length) let listIndent = marker.length + delimiter.length + trailingSpace.length let fixedMarkerString = String(fixedMarker) return editor .edit( (editBuilder: TextEditorEdit) => { if (marker === fixedMarkerString) { return } // Add enough trailing spaces so that the text is still aligned at the same indentation level as it was previously, but always keep at least one space fixedMarkerString += delimiter + " ".repeat( Math.max(1, listIndent - (fixedMarkerString + delimiter).length) ) editBuilder.replace( new Range( line, leadingSpace.length, line, leadingSpace.length + listIndent ), fixedMarkerString ) }, { undoStopBefore: false, undoStopAfter: false } ) .then(() => { let nextLine = line + 1 let indentString = " ".repeat(listIndent) while (editor.document.lineCount > nextLine) { const nextLineText = editor.document.lineAt(nextLine).text if (/^\s*[0-9]+[.)] +/.test(nextLineText)) { return fixMarker(editor, nextLine) } else if (/^\s*$/.test(nextLineText)) { nextLine++ } else if ( listIndent <= 4 && !nextLineText.startsWith(indentString) ) { return } else { nextLine++ } } }) } } function deleteRange(editor: TextEditor, range: Range): Thenable { return editor.edit( (editBuilder) => { editBuilder.delete(range) }, // We will enable undoStop after fixing markers { undoStopBefore: true, undoStopAfter: false } ) } // function checkTaskList(editor: TextEditor) { // let cursorPos = editor.selection.active; // let line = editor.document.lineAt(cursorPos.line).text; // // let matches: RegExpExecArray; // if (matches = /^(\s*([-+*]|[0-9]+[.)]) +\[) \]/.exec(line)) { // return editor.edit((editBuilder:TextEditorEdit) => { // editBuilder.replace(new Range(cursorPos.with({ character: matches[1].length }), cursorPos.with({ character: matches[1].length + 1 })), 'x'); // }); // } else if (matches = /^(\s*([-+*]|[0-9]+[.)]) +\[)x\]/.exec(line)) { // return editor.edit((editBuilder:TextEditorEdit) => { // editBuilder.replace(new Range(cursorPos.with({ character: matches[1].length }), cursorPos.with({ character: matches[1].length + 1 })), ' '); // }); // } // } // function onMoveLineUp() { // return commands.executeCommand('editor.action.moveLinesUpAction') // .then(() => fixMarker()); // } // // function onMoveLineDown() { // return commands.executeCommand('editor.action.moveLinesDownAction') // .then(() => fixMarker(findNextMarkerLineNumber(window.activeTextEditor.selection.start.line - 1))); // } // // function onCopyLineUp() { // return commands.executeCommand('editor.action.copyLinesUpAction') // .then(() => fixMarker()); // } // // function onCopyLineDown() { // return commands.executeCommand('editor.action.copyLinesDownAction') // .then(() => fixMarker()); // } // // function onIndentLines() { // return indent().then(() => fixMarker()); // } // // function onOutdentLines() { // return outdent().then(() => fixMarker()); // } export function deactivate() {} ================================================ FILE: client/src/components/editors/monaco-markdown/markdown.contribution.ts ================================================ // @ts-nocheck /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ 'use strict'; import { registerLanguage } from './contribution'; import {conf, language} from "./markdown"; export function activateMarkdownMath() { registerLanguage({ id: 'markdown-math', extensions: ['.md', '.markdown', '.mdown', '.mkdn', '.mkd', '.mdwn', '.mdtxt', '.mdtext'], aliases: ['Markdown', 'markdown'], loader: () => { return Promise.resolve({ conf: conf, language: language }) } }); } ================================================ FILE: client/src/components/editors/monaco-markdown/markdown.ts ================================================ // @ts-nocheck /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ "use strict" import { languages } from "monaco-editor" import { begin_args, all } from "./latex" export const conf: languages.LanguageConfiguration = { comments: { blockComment: [""], }, brackets: [ ["{", "}"], ["[", "]"], ["(", ")"], ], autoClosingPairs: [ { open: "{", close: "}" }, { open: "[", close: "]" }, { open: "(", close: ")" }, { open: "<", close: ">", notIn: ["string"] }, ], surroundingPairs: [ { open: "(", close: ")" }, { open: "[", close: "]" }, { open: "`", close: "`" }, ], folding: { markers: { start: new RegExp("^\\s*"), end: new RegExp("^\\s*"), }, }, } export const language = { defaultToken: "", tokenPostfix: ".md", // escape codes control: /[\\`*_\[\]{}()#+\-\.!]/, noncontrol: /[^\\`*_\[\]{}()#+\-\.!]/, escapes: /\\(?:@control)/, // escape codes for javascript/CSS strings jsescapes: /\\(?:[btnfr\\"']|[0-7][0-7]?|[0-3][0-7]{2})/, // non matched elements empty: [ "area", "base", "basefont", "br", "col", "frame", "hr", "img", "input", "isindex", "link", "meta", "param", ], latexKeywords: all, latexBeginKeywords: begin_args, tokenizer: { root: [ // headers (with #) [ /^(\s{0,3})(#+)((?:[^\\#]|@escapes)+)((?:#+)?)/, ["white", "keyword", "keyword", "keyword"], ], // headers (with =) [/^\s*(=+|\-+)\s*$/, "keyword"], // headers (with ***) [/^\s*((\*[ ]?)+)\s*$/, "meta.separator"], // quote [/^\s*>+/, "comment"], // list (starting with * or number) [/^\s*([\*\-+:]|\d+\.)\s/, "keyword"], // code block (4 spaces indent) [/^(\t|[ ]{4})[^ ].*$/, "string"], // code block (3 tilde) [ /^\s*~~~\s*((?:\w|[\/\-#])+)?\s*$/, { token: "string", next: "@codeblock" }, ], // github style code blocks (with backticks and language) [ /^\s*```\s*((?:\w|[\/\-#])+).*$/, { token: "string", next: "@codeblockgh", nextEmbedded: "$1" }, ], // github style code blocks (with backticks but no language) [/^\s*```\s*$/, { token: "string", next: "@codeblock" }], //math [/(^\${2})/, { token: "comment.math", next: "math", bracket: "@open" }], // markup within lines { include: "@linecontent" }, ], codeblock: [ [/^\s*~~~\s*$/, { token: "string", next: "@pop" }], [/^\s*```\s*$/, { token: "string", next: "@pop" }], [/.*$/, "variable.source"], ], // github style code blocks codeblockgh: [ [ /```\s*$/, { token: "variable.source", next: "@pop", nextEmbedded: "@pop" }, ], [/[^`]+/, "variable.source"], ], linecontent: [ // escapes [/&\w+;/, "string.escape"], [/@escapes/, "escape"], // various markup [/\b__([^\\_]|@escapes|_(?!_))+__\b/, "strong"], [/\*\*([^\\*]|@escapes|\*(?!\*))+\*\*/, "strong"], [/\b_[^_]+_\b/, "emphasis"], [/\*([^\\*]|@escapes)+\*/, "emphasis"], [/`([^\\`]|@escapes)+`/, "variable"], // links [/\{+[^}]+\}+/, "string.target"], [ /(!?\[)((?:[^\]\\]|@escapes)*)(\]\([^\)]+\))/, ["string.link", "", "string.link"], ], [/(!?\[)((?:[^\]\\]|@escapes)*)(\])/, "string.link"], //inline math [ /(\$\$)([^$]*)(\$\$)/, [ { token: "comment.math", bracket: "@open" }, { token: "@rematch", next: "mathInline", goBack: 2 }, { token: "comment.math", bracket: "@close" }, ], ], [ /(\$)([^$]+)(\$)/, [ { token: "comment.math", bracket: "@open" }, { token: "@rematch", next: "mathInline", goBack: 1 }, { token: "comment.math", bracket: "@close" }, ], ], // or html { include: "html" }, ], // Note: it is tempting to rather switch to the real HTML mode instead of building our own here // but currently there is a limitation in Monarch that prevents us from doing it: The opening // '<' would start the HTML mode, however there is no way to jump 1 character back to let the // HTML mode also tokenize the opening angle bracket. Thus, even though we could jump to HTML, // we cannot correctly tokenize it in that mode yet. html: [ // html tags [/<(\w+)\/>/, "tag"], [ /<(\w+)/, { cases: { "@empty": { token: "tag", next: "@tag.$1" }, "@default": { token: "tag", next: "@tag.$1" }, }, }, ], [/<\/(\w+)\s*>/, { token: "tag" }], [//, "comment", "@pop"], [//g, '< omit in toc >') // Escape magic comment .replace(//, '') // Remove comments .replace(/^---[\W\w]+?(\r?\n)---/, '') // Remove YAML front matter .split(/\r?\n/g); // Transform setext headings to ATX headings lines.forEach((lineText, i, arr) => { if ( i < arr.length - 1 && lineText.match(/^ {0,3}\S.*$/) && arr[i + 1].match(/^ {0,3}(=+|-{2,}) *$/) ) { arr[i] = (arr[i + 1].includes('=') ? '# ' : '## ') + lineText; } }); toc = lines.filter(lineText => { return lineText.startsWith('#') && lineText.includes('# ') && !lineText.includes('< omit in toc >'); }).map(lineText => { let matches = /^(#+) (.*)/.exec(lineText); return {level: matches[1].length, text: matches[2].replace(/#+$/, '').trim()}; }); return toc; } ================================================ FILE: client/src/components/editors/monaco-markdown/util.ts ================================================ // @ts-nocheck 'use strict' import {TextDocument} from "./vscode-monaco"; import {Position, Range} from "./extHostTypes"; export function isInFencedCodeBlock(doc: TextDocument, lineNum: number): boolean { let textBefore = doc.getText(new Range(new Position(0, 0), new Position(lineNum, 0))); let matches = textBefore.match(/^```[\w ]*$/gm); if (matches == null) { return false; } else { return matches.length % 2 != 0; } } /* ┌─────────────────┐ │ Text Extraction │ └─────────────────┘ */ /** * For example: [text](link) -> text * @param text */ export function extractText(text: string) { return textInHtml(textInMd(text)); } // [text](link) -> text. In case there are links in heading (#83) // 💩 function textInMd(text: string) { return text.replace(/\[([^\]]+?)\]\([^\)]+?\)/g, (_, g1) => g1); } // Convert HTML entities (#175) // Strip HTML tags (#179) // 💩 function textInHtml(text: string) { return text.replace(/( )/g, _ => ' ') .replace(/()/g, '') // remove .replace(/]*>(.*?)<\/span>/g, (_, g1) => g1) // remove .replace(/ +/g, ' '); } /* ┌─────────┐ │ Slugify │ └─────────┘ */ // Converted from `/[^\p{Word}\- ]/u` // `\p{Word}` => ASCII plus Letter (Ll/Lm/Lo/Lt/Lu), Mark (Mc/Me/Mn), Number (Nd/Nl/No), Connector_Punctuation (Pc) // Using const PUNCTUATION_REGEXP = /[^0-9A-Z_a-z\- ª²-³µ¹-º¼-¾À-ÖØ-öø-ˁˆ-ˑˠ-ˤˬˮ\u0300-ʹͶ-ͷͺ-ͽΆΈ-ΊΌΎ-ΡΣ-ϵϷ-ҁ\u0483-ԣԱ-Ֆՙա-և\u0591-\u05bd\u05bf\u05c1-\u05c2\u05c4-\u05c5\u05c7א-תװ-ײ\u0610-\u061aء-\u065e٠-٩ٮ-ۓە-\u06dc\u06de-\u06e8\u06ea-ۼۿܐ-\u074aݍ-ޱ߀-ߵߺ\u0901-ह\u093c-\u094dॐ-\u0954क़-\u0963०-९ॱ-ॲॻ-ॿ\u0981-\u0983অ-ঌএ-ঐও-নপ-রলশ-হ\u09bc-\u09c4\u09c7-\u09c8\u09cb-ৎ\u09d7ড়-ঢ়য়-\u09e3০-ৱ৴-৹\u0a01-\u0a03ਅ-ਊਏ-ਐਓ-ਨਪ-ਰਲ-ਲ਼ਵ-ਸ਼ਸ-ਹ\u0a3c\u0a3e-\u0a42\u0a47-\u0a48\u0a4b-\u0a4d\u0a51ਖ਼-ੜਫ਼੦-\u0a75\u0a81-\u0a83અ-ઍએ-ઑઓ-નપ-રલ-ળવ-હ\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acdૐૠ-\u0ae3૦-૯\u0b01-\u0b03ଅ-ଌଏ-ଐଓ-ନପ-ରଲ-ଳଵ-ହ\u0b3c-\u0b44\u0b47-\u0b48\u0b4b-\u0b4d\u0b56-\u0b57ଡ଼-ଢ଼ୟ-\u0b63୦-୯ୱ\u0b82-ஃஅ-ஊஎ-ஐஒ-கங-சஜஞ-டண-தந-பம-ஹ\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcdௐ\u0bd7௦-௲\u0c01-\u0c03అ-ఌఎ-ఐఒ-నప-ళవ-హఽ-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55-\u0c56ౘ-ౙౠ-\u0c63౦-౯౸-౾\u0c82-\u0c83ಅ-ಌಎ-ಐಒ-ನಪ-ಳವ-ಹ\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5-\u0cd6ೞೠ-\u0ce3೦-೯\u0d02-\u0d03അ-ഌഎ-ഐഒ-നപ-ഹഽ-\u0d44\u0d46-\u0d48\u0d4a-\u0d4d\u0d57ൠ-\u0d63൦-൵ൺ-ൿ\u0d82-\u0d83අ-ඖක-නඳ-රලව-ෆ\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2-\u0df3ก-\u0e3aเ-\u0e4e๐-๙ກ-ຂຄງ-ຈຊຍດ-ທນ-ຟມ-ຣລວສ-ຫອ-\u0eb9\u0ebb-ຽເ-ໄໆ\u0ec8-\u0ecd໐-໙ໜ-ໝༀ\u0f18-\u0f19༠-༳\u0f35\u0f37\u0f39\u0f3e-ཇཉ-ཬ\u0f71-\u0f84\u0f86-ྋ\u0f90-\u0f97\u0f99-\u0fbc\u0fc6က-၉ၐ-႙Ⴀ-Ⴥა-ჺჼᄀ-ᅙᅟ-ᆢᆨ-ᇹሀ-ቈቊ-ቍቐ-ቖቘቚ-ቝበ-ኈኊ-ኍነ-ኰኲ-ኵኸ-ኾዀዂ-ዅወ-ዖዘ-ጐጒ-ጕጘ-ፚ\u135f፩-፼ᎀ-ᎏᎠ-Ᏼᐁ-ᙬᙯ-ᙶᚁ-ᚚᚠ-ᛪ\u16ee-\u16f0ᜀ-ᜌᜎ-\u1714ᜠ-\u1734ᝀ-\u1753ᝠ-ᝬᝮ-ᝰ\u1772-\u1773ក-ឳ\u17b6-\u17d3ៗៜ-\u17dd០-៩៰-៹\u180b-\u180d᠐-᠙ᠠ-ᡷᢀ-ᢪᤀ-ᤜ\u1920-\u192b\u1930-\u193b᥆-ᥭᥰ-ᥴᦀ-ᦩ\u19b0-\u19c9᧐-᧙ᨀ-\u1a1b\u1b00-ᭋ᭐-᭙\u1b6b-\u1b73\u1b80-\u1baaᮮ-᮹ᰀ-\u1c37᱀-᱉ᱍ-ᱽᴀ-\u1de6\u1dfe-ἕἘ-Ἕἠ-ὅὈ-Ὅὐ-ὗὙὛὝὟ-ώᾀ-ᾴᾶ-ᾼιῂ-ῄῆ-ῌῐ-ΐῖ-Ίῠ-Ῥῲ-ῴῶ-ῼ‿-⁀⁔⁰-ⁱ⁴-⁹ⁿ-₉ₐ-ₔ\u20d0-\u20f0ℂℇℊ-ℓℕℙ-ℝℤΩℨK-ℭℯ-ℹℼ-ℿⅅ-ⅉⅎ⅓-\u2188①-⒛⓪-⓿❶-➓Ⰰ-Ⱞⰰ-ⱞⱠ-Ɐⱱ-ⱽⲀ-ⳤ⳽ⴀ-ⴥⴰ-ⵥⵯⶀ-ⶖⶠ-ⶦⶨ-ⶮⶰ-ⶶⶸ-ⶾⷀ-ⷆⷈ-ⷎⷐ-ⷖⷘ-ⷞ\u2de0-\u2dffⸯ々-\u3007\u3021-\u302f〱-〵\u3038-〼ぁ-ゖ\u3099-\u309aゝ-ゟァ-ヺー-ヿㄅ-ㄭㄱ-ㆎ㆒-㆕ㆠ-ㆷㇰ-ㇿ㈠-㈩㉑-㉟㊀-㊉㊱-㊿㐀-䶵一-鿃ꀀ-ꒌꔀ-ꘌꘐ-ꘫꙀ-ꙟꙢ-\ua672\ua67c-\ua67dꙿ-ꚗꜗ-ꜟꜢ-ꞈꞋ-ꞌꟻ-\ua827ꡀ-ꡳ\ua880-\ua8c4꣐-꣙꤀-\ua92dꤰ-\ua953ꨀ-\uaa36ꩀ-\uaa4d꩐-꩙가-힣豈-鶴侮-頻並-龎ff-stﬓ-ﬗיִ-ﬨשׁ-זּטּ-לּמּנּ-סּףּ-פּצּ-ﮱﯓ-ﴽﵐ-ﶏﶒ-ﷇﷰ-ﷻ\ufe00-\ufe0f\ufe20-\ufe26︳-︴﹍-﹏ﹰ-ﹴﹶ-ﻼ0-9A-Z_a-zヲ-하-ᅦᅧ-ᅬᅭ-ᅲᅳ-ᅵ]/gu; export function slugify(heading: string) { // GitHub slugify function // let slug = extractText(heading.trim()) // .replace(/[A-Z]/g, match => match.toLowerCase()) // only downcase ASCII region .replace(PUNCTUATION_REGEXP, '') .replace(/ /g, '-'); // if (doDowncase) { slug = slug.replace(/[A-Z]/g, match => match.toLowerCase()) // } return slug; } ================================================ FILE: client/src/components/editors/monaco-markdown/vscode-common.ts ================================================ // @ts-nocheck import { Range } from "./extHostTypes" /** * Represents a line of text, such as a line of source code. * * TextLine objects are __immutable__. When a [document](#TextDocument) changes, * previously retrieved lines will not represent the latest state. */ export interface TextLine { /** * The zero-based line number. */ readonly lineNumber: number; /** * The text of this line without the line separator characters. */ readonly text: string; /** * The range this line covers without the line separator characters. */ readonly range: Range; /** * The range this line covers with the line separator characters. */ readonly rangeIncludingLineBreak: Range; /** * The offset of the first character which is not a whitespace character as defined * by `/\s/`. **Note** that if a line is all whitespace the length of the line is returned. */ readonly firstNonWhitespaceCharacterIndex: number; /** * Whether this line is whitespace only, shorthand * for [TextLine.firstNonWhitespaceCharacterIndex](#TextLine.firstNonWhitespaceCharacterIndex) === [TextLine.text.length](#TextLine.text). */ readonly isEmptyOrWhitespace: boolean; } ================================================ FILE: client/src/components/editors/monaco-markdown/vscode-converters.ts ================================================ // @ts-nocheck import { editor, Range as _Range, Position as _Position, IRange, Selection as _Selection, IPosition, } from "monaco-editor" import * as vscode from "./extHostTypes"; export interface PositionLike { line: number; character: number; } export interface RangeLike { start: PositionLike; end: PositionLike; } export interface SelectionLike extends RangeLike { anchor: PositionLike; active: PositionLike; } export namespace Selection { export function to(selection: _Selection): vscode.Selection { const {selectionStartLineNumber, selectionStartColumn, positionLineNumber, positionColumn} = selection; const start = new vscode.Position(selectionStartLineNumber - 1, selectionStartColumn - 1); const end = new vscode.Position(positionLineNumber - 1, positionColumn - 1); return new vscode.Selection(start, end); } export function from(selection: SelectionLike): _Selection { const {anchor, active} = selection; return new _Selection( anchor.line + 1, anchor.character + 1, active.line + 1, active.character + 1); } } export namespace Range { export function from(range: undefined): undefined; export function from(range: RangeLike): _Range; export function from(range: RangeLike | undefined): _Range | undefined; export function from(range: RangeLike | undefined): _Range | undefined { if (!range) { return undefined; } const {start, end} = range; return new _Range( start.line + 1, start.character + 1, end.line + 1, end.character + 1 ); } export function to(range: undefined): vscode.Range; export function to(range: IRange): vscode.Range; export function to(range: IRange | undefined): vscode.Range | undefined; export function to(range: IRange | undefined): vscode.Range | undefined { if (!range) { return undefined; } const {startLineNumber, startColumn, endLineNumber, endColumn} = range; return new vscode.Range(startLineNumber - 1, startColumn - 1, endLineNumber - 1, endColumn - 1); } } export namespace Position { export function to(position: IPosition): vscode.Position { return new vscode.Position(position.lineNumber - 1, position.column - 1); } export function from(position: vscode.Position): IPosition { return {lineNumber: position.line + 1, column: position.character + 1}; } } export namespace EndOfLine { export function from(eol: vscode.EndOfLine): editor.EndOfLineSequence | undefined { if (eol === vscode.EndOfLine.CRLF) { return editor.EndOfLineSequence.CRLF; } else if (eol === vscode.EndOfLine.LF) { return editor.EndOfLineSequence.LF; } return undefined; } export function to(eol: editor.EndOfLineSequence): vscode.EndOfLine | undefined { if (eol === editor.EndOfLineSequence.CRLF) { return vscode.EndOfLine.CRLF; } else if (eol === editor.EndOfLineSequence.LF) { return vscode.EndOfLine.LF; } return undefined; } } export namespace WorkspaceEdit { export function from(value: vscode.WorkspaceEdit): editor.IIdentifiedSingleEditOperation[] { let edits: editor.IIdentifiedSingleEditOperation[] = [] for (const entry of value._allEntries()) { const [uri, uriOrEdits] = entry; if (Array.isArray(uriOrEdits)) { // text edits for (const e of uriOrEdits) { edits.push({ range: Range.from(e.range), text: e.newText, forceMoveMarkers: false }); } } else { // resource edits throw new Error("Not implemented for " + uri) } } return edits; } } ================================================ FILE: client/src/components/editors/monaco-markdown/vscode-monaco.ts ================================================ // @ts-nocheck import { EndOfLine, Position, Range, Selection, TextEditorRevealType, WorkspaceEdit, } from './extHostTypes'; import { editor, Selection as _Selection, IRange, Uri, Thenable } from "monaco-editor"; import {regExpLeadsToEndlessLoop} from "./vscode-utils"; import {ensureValidWordDefinition, getWordAtText} from "./wordHelper"; import * as TypeConverters from './vscode-converters' import {TextLine} from "./vscode-common"; const _modeId2WordDefinition = new Map(); export function setWordDefinitionFor(modeId: string, wordDefinition: RegExp | undefined): void { _modeId2WordDefinition.set(modeId, wordDefinition); } export function getWordDefinitionFor(modeId: string): RegExp | undefined { return _modeId2WordDefinition.get(modeId); } function revealRangeInEditor(_editor: editor.ICodeEditor, range: IRange, revealType: TextEditorRevealType): void { switch (revealType) { case TextEditorRevealType.Default: case undefined: _editor.revealRange(range, editor.ScrollType.Smooth); break; case TextEditorRevealType.InCenter: _editor.revealRangeInCenter(range, editor.ScrollType.Smooth); break; case TextEditorRevealType.InCenterIfOutsideViewport: _editor.revealRangeInCenterIfOutsideViewport(range, editor.ScrollType.Smooth); break; case TextEditorRevealType.AtTop: _editor.revealRangeAtTop(range, editor.ScrollType.Smooth); break; default: console.warn(`Unknown revealType: ${revealType}`); break; } } export class TextDocument { readonly uri: Uri; readonly version: number; readonly model: editor.ITextModel; private _textLines: TextLine[] = []; constructor(model: editor.ITextModel) { this.model = model; this.languageId = getLanguageId(model) } get eol(): EndOfLine { switch (this.model.getEOL()) { case '\n': { return EndOfLine.LF } case '\r\n': { return EndOfLine.CRLF } default: { throw new Error("invalid argument") } } }; get fileName(): string { return '' } get isClosed(): boolean { return false } get isDirty(): boolean { return false } get isUntitled(): boolean { return true; } readonly languageId: string; get lineCount(): number { return this.model.getLineCount() } private get _lines(): string[] { return this.model.getLinesContent() } getText(range?: Range): string { if (!range) { return this.model.getValue() } return this.model.getValueInRange(TypeConverters.Range.from(range)); } lineAt(lineOrPosition: number | Position): TextLine { let line: number | undefined; if (lineOrPosition instanceof Position) { line = lineOrPosition.line; } else if (typeof lineOrPosition === 'number') { line = lineOrPosition; } if (typeof line !== 'number' || line < 0 || line >= this._lines.length) { throw new Error('Illegal value for `line`'); } let result = this._textLines[line]; if (!result || result.lineNumber !== line || result.text !== this._lines[line]) { const text = this._lines[line]; const firstNonWhitespaceCharacterIndex = /^(\s*)/.exec(text)![1].length; const range = new Range(line, 0, line, text.length); const rangeIncludingLineBreak = line < this._lines.length - 1 ? new Range(line, 0, line + 1, 0) : range; result = Object.freeze({ lineNumber: line, range, rangeIncludingLineBreak, text, firstNonWhitespaceCharacterIndex, //TODO@api, rename to 'leadingWhitespaceLength' isEmptyOrWhitespace: firstNonWhitespaceCharacterIndex === text.length }); this._textLines[line] = result; } return result; } offsetAt(position: Position): number { return this.model.getOffsetAt(TypeConverters.Position.from(position)) } positionAt(offset: number): Position { return TypeConverters.Position.to(this.model.getPositionAt(offset)) } save(): Thenable { throw new Error("Not implemented") } validateRange(range: Range): Range { if (!(range instanceof Range)) { throw new Error('Invalid argument'); } const start = this.validatePosition(range.start); const end = this.validatePosition(range.end); if (start === range.start && end === range.end) { return range; } return new Range(start.line, start.character, end.line, end.character); } validatePosition(position: Position): Position { if (!(position instanceof Position)) { throw new Error('Invalid argument'); } let {line, character} = position; let hasChanged = false; if (line < 0) { line = 0; character = 0; hasChanged = true; } else if (line >= this._lines.length) { line = this._lines.length - 1; character = this._lines[line].length; hasChanged = true; } else { const maxCharacter = this._lines[line].length; if (character < 0) { character = 0; hasChanged = true; } else if (character > maxCharacter) { character = maxCharacter; hasChanged = true; } } if (!hasChanged) { return position; } return new Position(line, character); } getWordRangeAtPosition(_position: Position, regexp?: RegExp): Range | undefined { const position = this.validatePosition(_position); if (!regexp) { // use default when custom-regexp isn't provided regexp = getWordDefinitionFor(this.languageId); } else if (regExpLeadsToEndlessLoop(regexp)) { // use default when custom-regexp is bad console.warn(`[getWordRangeAtPosition]: ignoring custom regexp '${regexp.source}' because it matches the empty string.`); regexp = getWordDefinitionFor(this.languageId); } const wordAtText = getWordAtText( position.character + 1, ensureValidWordDefinition(regexp), this._lines[position.line], 0 ); if (wordAtText) { return new Range(position.line, wordAtText.startColumn - 1, position.line, wordAtText.endColumn - 1); } return undefined; } } function getLanguageId(model: editor.ITextModel) { // @ts-ignore return model.getLanguageId(); } export class TextEditor { public readonly editor: editor.IStandaloneCodeEditor; private _disposed: boolean = false; public get languageId(): string { return getLanguageId(this.editor.getModel()) } constructor(editor: editor.IStandaloneCodeEditor) { this.editor = editor; } get document(): TextDocument { return new TextDocument(this.editor.getModel()) } get selection(): Selection { return TypeConverters.Selection.to(this.editor.getSelection()) } set selection(value: Selection) { this.editor.setSelection(TypeConverters.Selection.from(value)) } get selections(): Selection[] { return this.editor.getSelections().map(s => TypeConverters.Selection.to(s)) } set selections(value: Selection[]) { this.editor.setSelections(value.map(s => TypeConverters.Selection.from(s))) } get visibleRanges(): Range[] { return this.editor.getVisibleRanges().map(r => TypeConverters.Range.to(r)) } edit(callback: (edit: TextEditorEdit) => void, options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { undoStopBefore: true, undoStopAfter: true }): Promise { if (this._disposed) { return Promise.reject(new Error('TextEditor#edit not possible on closed editors')); } const edit = new TextEditorEdit(this.document, options); callback(edit); return this._applyEdit(edit); } private _applyEdit(editBuilder: TextEditorEdit): Promise { const editData = editBuilder.finalize(); // return when there is nothing to do if (editData.edits.length === 0 && !editData.setEndOfLine) { return Promise.resolve(null); } // check that the edits are not overlapping (i.e. illegal) const editRanges = editData.edits.map(edit => edit.range); // sort ascending (by end and then by start) editRanges.sort((a, b) => { if (a.end.line === b.end.line) { if (a.end.character === b.end.character) { if (a.start.line === b.start.line) { return a.start.character - b.start.character; } return a.start.line - b.start.line; } return a.end.character - b.end.character; } return a.end.line - b.end.line; }); // check that no edits are overlapping for (let i = 0, count = editRanges.length - 1; i < count; i++) { const rangeEnd = editRanges[i].end; const nextRangeStart = editRanges[i + 1].start; if (nextRangeStart.isBefore(rangeEnd)) { // overlapping ranges return Promise.reject( new Error('Overlapping ranges are not allowed!') ); } } // prepare data for serialization const edits = editData.edits.map((edit): editor.IIdentifiedSingleEditOperation => { return { range: TypeConverters.Range.from(edit.range), text: edit.text, forceMoveMarkers: edit.forceMoveMarkers }; }); this.editor.getModel().pushEditOperations(this.editor.getSelections(), edits, (): _Selection[] => { return []; }) return Promise.resolve(null) } // insertSnippet(snippet: SnippetString, where?: Position | readonly Position[] | Range | readonly Range[], options: { undoStopBefore: boolean; undoStopAfter: boolean; } = { // undoStopBefore: true, // undoStopAfter: true // }): Promise {} // hide(): void { // throw new Error("Not implemented") // } // @ts-ignore // show(column?: ViewColumn): void { // throw new Error("Not implemented") // } revealRange(range: Range, revealType?: TextEditorRevealType): void { revealRangeInEditor(this.editor, TypeConverters.Range.from(range), revealType) } // @ts-ignore // setDecorations(decorationType: TextEditorDecorationType, rangesOrOptions: Range[] | DecorationOptions[]): void { // throw new Error("Not implemented") // } applyEdit(edit: WorkspaceEdit, newSelections?: Selection[]): Thenable { if (!newSelections) { newSelections = [] } this.editor.getModel().pushEditOperations(this.editor.getSelections(), TypeConverters.WorkspaceEdit.from(edit), (): _Selection[] => { return newSelections.map(s => TypeConverters.Selection.from(s)); }) return Promise.resolve(null) } addAction(param: { contextMenuOrder: number; keybindingContext: string; run(editor: editor.ICodeEditor): (void | Promise); id: string; label: string; precondition: string; contextMenuGroupId: string; keybindings: number[] }) { this.editor.addAction(param) } executeCommand(commandId: string, ...rest: any[]): Promise { switch (commandId) { case 'type': this.editor.trigger('keyboard', commandId, rest[0]) return Promise.resolve() case 'tab': case 'deleteLeft': this.editor.trigger('keyboard', commandId, undefined) return Promise.resolve() default: let action = this.editor.getAction(commandId) if (action) { if (action.isSupported()) { return action.run(); } } else { new Error("unknown action id " + commandId) } } } getConfiguration(configurationId: string) { switch (configurationId) { case '': break default: return new UndefinedConfiguration() } } } class UndefinedConfiguration { get(_: string): T { return undefined; } } export interface ITextEditOperation { range: Range; text: string | null; forceMoveMarkers: boolean; } export interface IEditData { documentVersionId: number; edits: ITextEditOperation[]; setEndOfLine: EndOfLine | undefined; undoStopBefore: boolean; undoStopAfter: boolean; } export class TextEditorEdit { private readonly _document: TextDocument; private readonly _documentVersionId: number; private readonly _undoStopBefore: boolean; private readonly _undoStopAfter: boolean; private _collectedEdits: ITextEditOperation[] = []; private _setEndOfLine: EndOfLine | undefined = undefined; private _finalized: boolean = false; constructor(document: TextDocument, options: { undoStopBefore: boolean; undoStopAfter: boolean; }) { this._document = document; this._documentVersionId = document.version; this._undoStopBefore = options.undoStopBefore; this._undoStopAfter = options.undoStopAfter; } finalize(): IEditData { this._finalized = true; return { documentVersionId: this._documentVersionId, edits: this._collectedEdits, setEndOfLine: this._setEndOfLine, undoStopBefore: this._undoStopBefore, undoStopAfter: this._undoStopAfter }; } private _throwIfFinalized() { if (this._finalized) { throw new Error('Edit is only valid while callback runs'); } } replace(location: Position | Range | Selection, value: string): void { this._throwIfFinalized(); let range: Range | null = null; if (location instanceof Position) { range = new Range(location, location); } else if (location instanceof Range) { range = location; } else { throw new Error('Unrecognized location'); } this._pushEdit(range, value, false); } insert(location: Position, value: string): void { this._throwIfFinalized(); this._pushEdit(new Range(location, location), value, true); } delete(location: Range | Selection): void { this._throwIfFinalized(); let range: Range | null = null; if (location instanceof Range) { range = location; } else { throw new Error('Unrecognized location'); } this._pushEdit(range, null, true); } private _pushEdit(range: Range, text: string | null, forceMoveMarkers: boolean): void { const validRange = this._document.validateRange(range); this._collectedEdits.push({ range: validRange, text: text, forceMoveMarkers: forceMoveMarkers }); } setEndOfLine(endOfLine: EndOfLine): void { this._throwIfFinalized(); if (endOfLine !== EndOfLine.LF && endOfLine !== EndOfLine.CRLF) { throw new Error('Illegal argument endOfLine'); } this._setEndOfLine = endOfLine; } } ================================================ FILE: client/src/components/editors/monaco-markdown/vscode-utils.ts ================================================ // @ts-nocheck export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean { // Exit early if it's one of these special cases which are meant to match // against an empty string if ( regexp.source === "^" || regexp.source === "^$" || regexp.source === "$" || regexp.source === "^\\s*$" ) { return false } // We check against an empty string. If the regular expression doesn't advance // (e.g. ends in an endless loop) it will match an empty string. const match = regexp.exec("") return !!(match && regexp.lastIndex === 0) } ================================================ FILE: client/src/components/editors/monaco-markdown/wordHelper.ts ================================================ // @ts-nocheck /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import {editor} from "monaco-editor"; export const USUAL_WORD_SEPARATORS = '`~!@#$%^&*()-=+[{]}\\|;:\'",.<>/?'; /** * Create a word definition regular expression based on default word separators. * Optionally provide allowed separators that should be included in words. * * The default would look like this: * /(-?\d*\.\d\w*)|([^\`\~\!\@\#\$\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g */ function createWordRegExp(allowInWords: string = ''): RegExp { let source = '(-?\\d*\\.\\d\\w*)|([^'; for (const sep of USUAL_WORD_SEPARATORS) { if (allowInWords.indexOf(sep) >= 0) { continue; } source += '\\' + sep; } source += '\\s]+)'; return new RegExp(source, 'g'); } // catches numbers (including floating numbers) in the first group, and alphanum in the second export const DEFAULT_WORD_REGEXP = createWordRegExp(); export function ensureValidWordDefinition(wordDefinition?: RegExp | null): RegExp { let result: RegExp = DEFAULT_WORD_REGEXP; if (wordDefinition && (wordDefinition instanceof RegExp)) { if (!wordDefinition.global) { let flags = 'g'; if (wordDefinition.ignoreCase) { flags += 'i'; } if (wordDefinition.multiline) { flags += 'm'; } if ((wordDefinition as any).unicode) { flags += 'u'; } result = new RegExp(wordDefinition.source, flags); } else { result = wordDefinition; } } result.lastIndex = 0; return result; } function getWordAtPosFast(column: number, wordDefinition: RegExp, text: string, textOffset: number): editor.IWordAtPosition | null { // find whitespace enclosed text around column and match from there let pos = column - 1 - textOffset; let start = text.lastIndexOf(' ', pos - 1) + 1; wordDefinition.lastIndex = start; let match: RegExpMatchArray | null; while (match = wordDefinition.exec(text)) { const matchIndex = match.index || 0; if (matchIndex <= pos && wordDefinition.lastIndex >= pos) { return { word: match[0], startColumn: textOffset + 1 + matchIndex, endColumn: textOffset + 1 + wordDefinition.lastIndex }; } } return null; } function getWordAtPosSlow(column: number, wordDefinition: RegExp, text: string, textOffset: number): editor.IWordAtPosition | null { // matches all words starting at the beginning // of the input until it finds a match that encloses // the desired column. slow but correct let pos = column - 1 - textOffset; wordDefinition.lastIndex = 0; let match: RegExpMatchArray | null; while (match = wordDefinition.exec(text)) { const matchIndex = match.index || 0; if (matchIndex > pos) { // |nW -> matched only after the pos return null; } else if (wordDefinition.lastIndex >= pos) { // W|W -> match encloses pos return { word: match[0], startColumn: textOffset + 1 + matchIndex, endColumn: textOffset + 1 + wordDefinition.lastIndex }; } } return null; } export function getWordAtText(column: number, wordDefinition: RegExp, text: string, textOffset: number): editor.IWordAtPosition | null { // if `words` can contain whitespace character we have to use the slow variant // otherwise we use the fast variant of finding a word wordDefinition.lastIndex = 0; let match = wordDefinition.exec(text); if (!match) { return null; } // todo@joh the `match` could already be the (first) word const ret = match[0].indexOf(' ') >= 0 // did match a word which contains a space character -> use slow word find ? getWordAtPosSlow(column, wordDefinition, text, textOffset) // sane word definition -> use fast word find : getWordAtPosFast(column, wordDefinition, text, textOffset); // both (getWordAtPosFast and getWordAtPosSlow) leave the wordDefinition-RegExp // in an undefined state and to not confuse other users of the wordDefinition // we reset the lastIndex wordDefinition.lastIndex = 0; return ret; } ================================================ FILE: client/src/components/editors/monaco.ts ================================================ import * as monaco from "monaco-editor" export const editorOptions: monaco.editor.IStandaloneEditorConstructionOptions = { theme: "hexon", language: "markdown", folding: false, readOnly: false, roundedSelection: true, minimap: { enabled: false }, occurrencesHighlight: false, wordBasedSuggestions: false, hideCursorInOverviewRuler: true, automaticLayout: true, overviewRulerBorder: false, renderLineHighlight: "none", scrollbar: { horizontalScrollbarSize: 10, verticalScrollbarSize: 10, useShadows: false, }, fontSize: 14, lineHeight: 18, wordWrap: "on", lineNumbers: "off", cursorBlinking: "smooth", // TODO 支持更换字体 // fontFamily: "Menlo,Consolas,Monaco,Andale Mono,Ubuntu Mono,monospace", fontFamily: "PingFang SC,-apple-system,SF UI Text,Lucida Grande,STheiti,Microsoft YaHei,sans-serif", contextmenu: true, // 反正也很少用,关掉避免出现概率为30%的误操作bug:打开右键菜单后会立即执行鼠标指针所在的操作。 } ================================================ FILE: client/src/components/editors/prettier-formatter-ext.ts ================================================ import { editor, KeyCode, KeyMod } from "monaco-editor" import { formatMarkdown } from "./prettier-formatter" export class PrettierFormatterExtension { constructor(private readonly id = "prettier.formatter") {} activate(editor: editor.IStandaloneCodeEditor) { editor.addAction({ id: this.id, label: "Format With Prettier", keybindings: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KeyF], contextMenuGroupId: "navigation", run(editor) { const model = editor.getModel() if (!model) return const str = editor.getValue() editor.executeEdits(this.id, [ { range: model.getFullModelRange(), text: formatMarkdown(str), }, ]) }, }) } } ================================================ FILE: client/src/components/editors/prettier-formatter.spec.ts ================================================ import { formatMarkdown } from "./prettier-formatter" describe("format", () => { it("should format markdown", () => { const str = "# title" const res = formatMarkdown(str) expect(res).toMatch("# title") }) }) ================================================ FILE: client/src/components/editors/prettier-formatter.ts ================================================ import prettier from "prettier/standalone" import markdown from "prettier/parser-markdown" import typescript from "prettier/parser-typescript" import html from "prettier/parser-html" import yaml from "prettier/parser-yaml" import postcss from "prettier/parser-postcss" export function formatMarkdown(value: string) { return prettier.format(value, { parser: "markdown", plugins: [markdown, typescript, html, yaml, postcss], semi: false, }) } ================================================ FILE: client/src/components/editors/theme.ts ================================================ import * as monaco from "monaco-editor" import { computed, watch } from "vue" import { useThemeVars } from "@/ui/theme" function removeHash(str: string) { return str.slice(1) } export function useMonacoTheme() { const vars = useThemeVars() const custom = computed(() => { return { base: (vars.value.isDark ? "vs-dark" : "vs") as monaco.editor.BuiltinTheme, inherit: true, rules: [ { foreground: removeHash(vars.value.textColorSecondary), token: "comment.content.md", }, { foreground: removeHash(vars.value.textColorSecondary), token: "comment.md", }, { foreground: removeHash(vars.value.textColorPrimary), token: "string.md", }, { // 链接 foreground: removeHash(vars.value.colorPrimary), token: "string.link.md", fontStyle: "blod", }, { // 标题 foreground: removeHash(vars.value.colorPrimary), token: "keyword.md", }, { // 标题 foreground: removeHash(vars.value.colorPrimary), token: "keyword", }, { foreground: removeHash(vars.value.colorPrimary), fontStyle: "bold", token: "variable.md", }, ], colors: { "editor.foreground": vars.value.textColorPrimary, "editor.background": vars.value.backgroundColorPrimary, "editorCursor.foreground": vars.value.colorPrimary, "editor.selectionBackground": vars.value.isDark ? "#ffffff35" : "#00000015", }, } }) const update = () => { monaco.editor.defineTheme("hexon", custom.value) } watch( () => custom.value, () => { update() }, { immediate: true, deep: true } ) } ================================================ FILE: client/src/components/editors/workers.ts ================================================ import editorWorker from "monaco-editor/esm/vs/editor/editor.worker?worker" import jsonWorker from "monaco-editor/esm/vs/language/json/json.worker?worker" import cssWorker from "monaco-editor/esm/vs/language/css/css.worker?worker" import htmlWorker from "monaco-editor/esm/vs/language/html/html.worker?worker" import tsWorker from "monaco-editor/esm/vs/language/typescript/ts.worker?worker" declare global { interface globalThis { MonacoEnvironment: any } interface Window { MonacoEnvironment: any } } self.MonacoEnvironment = { getWorker(_: string, label: string) { if (label === "json") { return new jsonWorker() } if (label === "css" || label === "scss" || label === "less") { return new cssWorker() } if (label === "html" || label === "handlebars" || label === "razor") { return new htmlWorker() } if (label === "typescript" || label === "javascript") { return new tsWorker() } return new editorWorker() }, } ================================================ FILE: client/src/components/forms/HChangePasswordForm.vue ================================================ ================================================ FILE: client/src/components/forms/HChangeUsernameForm.vue ================================================ ================================================ FILE: client/src/components/forms/HCreateArticleForm.vue ================================================ ================================================ FILE: client/src/components/forms/HLoginForm.vue ================================================ ================================================ FILE: client/src/components/forms/demo/change-password.demo.vue ================================================ ================================================ FILE: client/src/components/forms/demo/create-article.demo.vue ================================================ ================================================ FILE: client/src/components/forms/interface.ts ================================================ export interface IChangePasswordFormPayload { oldPassword: string newPassword: string } ================================================ FILE: client/src/components/modals/HCreateArticleModal.vue ================================================ ================================================ FILE: client/src/components/modals/HHexoInitFailModal.vue ================================================ ================================================ FILE: client/src/components/modals/HSettingsModal.vue ================================================ ================================================ FILE: client/src/components/modals/hexo-init-fail-modal.ts ================================================ import { defineComponent, h } from "vue" import HHexoInitFailModal from "@/modals/HHexoInitFailModal.vue" import HBaseModal from "@/ui/modal/src/HBaseModal.vue" import modal from "~/plugins/modal" export default function showHexoInitFailModal(text: string) { modal.create( defineComponent({ name: "HErrorInfo", components: { HHexoInitFailModal, HBaseModal, }, props: { close: Function, }, render() { return h( HBaseModal, { onOnClose: this.close, }, () => h(HHexoInitFailModal, { text }) ) }, }) ) } ================================================ FILE: client/src/components/others/HDialog.vue ================================================ ================================================ FILE: client/src/components/others/HNotificationItem.vue ================================================ ================================================ FILE: client/src/components/transitions/FadeScaleTransitionGroup.vue ================================================ ================================================ FILE: client/src/components/transitions/FadeTransition.vue ================================================ ================================================ FILE: client/src/components/transitions/FadeTransitionGroup.vue ================================================ ================================================ FILE: client/src/components/transitions/TranslateDownTransitionGroup.vue ================================================ ================================================ FILE: client/src/components/transitions/TranslateTransitionGroup.vue ================================================ ================================================ FILE: client/src/components/transitions/interface.ts ================================================ export type TranslateTransitionDirection = "up" | "down" | "left" | "right" ================================================ FILE: client/src/components/types.ts ================================================ export type HNavListActionPayload = | { type: "deploy" } | { type: "generate" } | { type: "clean" } | { type: "gitsave" } | { type: "gitsync" } | { type: "all" } | { type: "post" } | { type: "page" } | { type: "draft" } | { type: "category"; slug: string } | { type: "tag"; slug: string } export type HViewerToolbarActionPayload = | { type: "edit" } | { type: "delete" } | { type: "publish" } | { type: "code" } export type HEditorToolbarActionPayload = | { type: "back" } | { type: "save" } | { type: "delete" } | { type: "publish" } | { type: "code" } export interface IFormData { username: string password: string password2: string secret: string expiresIn: string refreshableIn: string } ================================================ FILE: client/src/components/ui/badge/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/badge/index.ts ================================================ export { default as HBadge } from "./src/HBadge.vue" ================================================ FILE: client/src/components/ui/badge/src/HBadge.vue ================================================ ================================================ FILE: client/src/components/ui/button/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/button/index.ts ================================================ export { default as HButton } from "./src/HButton.vue" export * from "./src/interface" ================================================ FILE: client/src/components/ui/button/src/HButton.vue ================================================ ================================================ FILE: client/src/components/ui/button/src/interface.ts ================================================ export type HButtonType = "primary" | "success" | "warning" | "error" | "common" export type HButtonSize = "medium" | "small" ================================================ FILE: client/src/components/ui/button/styles/dark.ts ================================================ import type { CommonTheme } from "@/ui/theme" import { alpha, dark, light } from "~/utils/color" import { ButtonTheme } from "./light" const self = (common: CommonTheme) => { function hover(color: string) { return light(color, 0.2) } function active(color: string) { return dark(color, 0.4) } function hoverInverted(color: string) { return alpha(color, 0.9) } function activeInverted(color: string) { return alpha(color, 0.8) } return { ...common, backgroundColorHoverPrimary: hover(common.colorPrimary), backgroundColorHoverSuccess: hover(common.colorSuccess), backgroundColorHoverWarning: hover(common.colorWarning), backgroundColorHoverError: hover(common.colorError), backgroundColorHoverCommon: hover(common.colorCommon), backgroundColorHoverInvertedPrimary: hoverInverted(common.colorPrimary), backgroundColorHoverInvertedSuccess: hoverInverted(common.colorSuccess), backgroundColorHoverInvertedWarning: hoverInverted(common.colorWarning), backgroundColorHoverInvertedError: hoverInverted(common.colorError), backgroundColorHoverInvertedCommon: hoverInverted(common.colorCommon), backgroundColorActivePrimary: active(common.colorPrimary), backgroundColorActiveSuccess: active(common.colorSuccess), backgroundColorActiveWarning: active(common.colorWarning), backgroundColorActiveError: active(common.colorError), backgroundColorActiveCommon: active(common.colorCommon), backgroundColorActiveInvertedPrimary: activeInverted(common.colorPrimary), backgroundColorActiveInvertedSuccess: activeInverted(common.colorSuccess), backgroundColorActiveInvertedWarning: activeInverted(common.colorWarning), backgroundColorActiveInvertedError: activeInverted(common.colorError), backgroundColorActiveInvertedCommon: activeInverted(common.colorCommon), colorActiveInvertedPrimary: active(common.colorPrimary), colorActiveInvertedSuccess: active(common.colorSuccess), colorActiveInvertedWarning: active(common.colorWarning), colorActiveInvertedError: active(common.colorError), colorActiveInvertedCommon: active(common.colorCommon), } } const buttonDark: ButtonTheme = { name: "Button", self, } export default buttonDark ================================================ FILE: client/src/components/ui/button/styles/index.ts ================================================ export { default as buttonLight } from "./light" export { default as buttonDark } from "./dark" export type { ButtonVars, ButtonTheme } from "./light" ================================================ FILE: client/src/components/ui/button/styles/light.ts ================================================ import type { Theme, CommonTheme } from "@/ui/theme" import { alpha, light } from "~/utils/color" const self = (common: CommonTheme) => { function hover(color: string) { return light(color, 0.2) } function active(color: string) { return light(color, 0.4) } function hoverInverted(color: string) { return alpha(color, 0.9) } function activeInverted(color: string) { return alpha(color, 0.8) } return { ...common, backgroundColorHoverPrimary: hover(common.colorPrimary), backgroundColorHoverSuccess: hover(common.colorSuccess), backgroundColorHoverWarning: hover(common.colorWarning), backgroundColorHoverError: hover(common.colorError), backgroundColorHoverCommon: hover(common.colorCommon), backgroundColorHoverInvertedPrimary: hoverInverted(common.colorPrimary), backgroundColorHoverInvertedSuccess: hoverInverted(common.colorSuccess), backgroundColorHoverInvertedWarning: hoverInverted(common.colorWarning), backgroundColorHoverInvertedError: hoverInverted(common.colorError), backgroundColorHoverInvertedCommon: hoverInverted(common.colorCommon), backgroundColorActivePrimary: active(common.colorPrimary), backgroundColorActiveSuccess: active(common.colorSuccess), backgroundColorActiveWarning: active(common.colorWarning), backgroundColorActiveError: active(common.colorError), backgroundColorActiveCommon: active(common.colorCommon), backgroundColorActiveInvertedPrimary: activeInverted(common.colorPrimary), backgroundColorActiveInvertedSuccess: activeInverted(common.colorSuccess), backgroundColorActiveInvertedWarning: activeInverted(common.colorWarning), backgroundColorActiveInvertedError: activeInverted(common.colorError), backgroundColorActiveInvertedCommon: activeInverted(common.colorCommon), colorActiveInvertedPrimary: active(common.colorPrimary), colorActiveInvertedSuccess: active(common.colorSuccess), colorActiveInvertedWarning: active(common.colorWarning), colorActiveInvertedError: active(common.colorError), colorActiveInvertedCommon: active(common.colorCommon), } } export type ButtonVars = ReturnType const buttonLight: Theme<"Button", ButtonVars> = { name: "Button", self, } export default buttonLight export type ButtonTheme = typeof buttonLight ================================================ FILE: client/src/components/ui/checkbox/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/checkbox/index.ts ================================================ export { default as HCheckbox } from "./src/HCheckbox.vue" ================================================ FILE: client/src/components/ui/checkbox/src/HCheckbox.vue ================================================ ================================================ FILE: client/src/components/ui/date-picker/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/date-picker/index.ts ================================================ export { default as HDatePicker } from "./src/HDatePicker.vue" ================================================ FILE: client/src/components/ui/date-picker/src/HDatePicker.vue ================================================ ================================================ FILE: client/src/components/ui/date-picker/src/interface.ts ================================================ import type { Dayjs } from "dayjs" export interface IDateData { text: string current: boolean selected: boolean date: Dayjs } ================================================ FILE: client/src/components/ui/date-picker/src/utils.spec.ts ================================================ import dayjs from "dayjs" import { isAM, getDataByDate, getFirstDayInSameMonth, getLastMonday, lastMondayInCurrentMonth, } from "./utils" describe("date", () => { it("should get first day", () => { const date = getFirstDayInSameMonth(dayjs()) expect(date.date()).toBe(1) }) it("should get monday", () => { const date = getLastMonday(dayjs()) expect(date.day()).toBe(1) }) it("should not in current month", () => { const date = dayjs("2022-1-6") const monday = getLastMonday(date) const res = lastMondayInCurrentMonth(date, monday) expect(res).toBe(false) }) it("should in current month", () => { const date = dayjs("2007-6-30") const monday = getLastMonday(date) const res = lastMondayInCurrentMonth(date, monday) expect(res).toBe(false) }) it("should be am", () => { const res = isAM(dayjs("2020-1-1 11:00")) expect(res).toBe(true) }) }) ================================================ FILE: client/src/components/ui/date-picker/src/utils.ts ================================================ import { Dayjs } from "dayjs" import { IDateData } from "./interface" export function lastMondayInCurrentMonth(date: Dayjs, monday: Dayjs): boolean { return monday.month() === date.month() } export function getLastMonday(date: Dayjs): Dayjs { const firstDayInMouth = getFirstDayInSameMonth(date) const weekNo = firstDayInMouth.day() const monday = firstDayInMouth.subtract(weekNo - 1, "day") if (monday.month() === date.month()) return monday.subtract(7, "day") else return monday } export function getFirstDayInSameMonth(date: Dayjs): Dayjs { const currentDayInMouth = date.date() return date.subtract(currentDayInMouth - 1, "day") } export function getDataByDate(date: Dayjs): IDateData[] { const data: IDateData[] = [] const monthDay1 = getFirstDayInSameMonth(date) const currentDayInMouth = date.date() const lastMonday = getLastMonday(date) const lastMondayInMonth = lastMonday.date() data.push( ...new Array(lastMonday.daysInMonth() - lastMonday.date() + 1).fill(0).map( (v, idx): IDateData => ({ text: `${lastMondayInMonth + idx}`, current: false, selected: false, date: lastMonday.add(idx, "day"), }) ) ) data.push( ...new Array(date.daysInMonth()).fill(0).map( (v, idx): IDateData => ({ text: `${idx + 1}`, current: true, selected: idx + 1 === currentDayInMouth, date: monthDay1.add(idx, "day"), }) ) ) const nextMonthDay1 = monthDay1.add(monthDay1.daysInMonth(), "day") data.push( ...new Array(42 - data.length).fill(0).map( (v, idx): IDateData => ({ text: `${idx + 1}`, current: false, selected: false, date: nextMonthDay1.add(idx, "day"), }) ) ) return data } export function isAM(date: Dayjs): boolean { return date.hour() < 12 } export function getTimeData(date: Dayjs): IDateData[] { const hour = date.hour() const base = date.subtract(hour < 12 ? hour : hour - 12, "hour") return new Array(12).fill(0).map( (v, idx): IDateData => ({ text: `0${idx}`.slice(-2), current: idx === hour % 12, selected: idx === hour % 12, date: base.add(idx, "hour"), }) ) } ================================================ FILE: client/src/components/ui/divider/index.ts ================================================ export { default as HDivider } from "./src/HDivider.vue" ================================================ FILE: client/src/components/ui/divider/src/HDivider.vue ================================================ ================================================ FILE: client/src/components/ui/icon/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/icon/demo/icon-map.vue ================================================ ================================================ FILE: client/src/components/ui/icon/index.ts ================================================ export { default as HIcon } from "./src/HIcon.vue" export { HIconName } from "./src/interface" ================================================ FILE: client/src/components/ui/icon/src/HIcon.vue ================================================ ================================================ FILE: client/src/components/ui/icon/src/interface.ts ================================================ /** * @see https://docs.microsoft.com/en-us/windows/apps/design/style/segoe-fluent-icons-font#pua-e700-e900 * @deprecated */ export enum HIconName { GlobalNavButton = "\ue700", Wifi = "\ue701", Bluetooth = "\ue702", Connect = "\ue703", InternetSharing = "\ue704", VPN = "\ue705", Brightness = "\ue706", MapPin = "\ue707", QuietHours = "\ue708", Airplane = "\ue709", Tablet = "\ue70a", QuickNote = "\ue70b", RememberedDevice = "\ue70c", ChevronDown = "\ue70d", ChevronUp = "\ue70e", Edit = "\ue70f", Add = "\ue710", Cancel = "\ue711", More = "\ue712", Settings = "\ue713", Video = "\ue714", Mail = "\ue715", People = "\ue716", Phone = "\ue717", Pin = "\ue718", Shop = "\ue719", Stop = "\ue71a", Link = "\ue71b", Filter = "\ue71c", AllApps = "\ue71d", Zoom = "\ue71e", ZoomOut = "\ue71f", Microphone = "\ue720", Search = "\ue721", Camera = "\ue722", Attach = "\ue723", Send = "\ue724", SendFill = "\ue725", WalkSolid = "\ue726", InPrivate = "\ue727", FavoriteList = "\ue728", PageSolid = "\ue729", Forward = "\ue72a", Back = "\ue72b", Refresh = "\ue72c", Share = "\ue72d", Lock = "\ue72e", ReportHacked = "\ue730", EMI = "\ue731", FavoriteStar = "\ue734", FavoriteStarFill = "\ue735", ReadingMode = "\ue736", Favicon = "\ue737", Remove = "\ue738", Checkbox = "\ue739", CheckboxComposite = "\ue73a", CheckboxFill = "\ue73b", CheckboxIndeterminate = "\ue73c", CheckboxCompositeReversed = "\ue73d", CheckMark = "\ue73e", BackToWindow = "\ue73f", FullScreen = "\ue740", ResizeTouchLarger = "\ue741", ResizeTouchSmaller = "\ue742", ResizeMouseSmall = "\ue743", ResizeMouseMedium = "\ue744", ResizeMouseWide = "\ue745", ResizeMouseTall = "\ue746", ResizeMouseLarge = "\ue747", SwitchUser = "\ue748", Print = "\ue749", Up = "\ue74a", Down = "\ue74b", OEM = "\ue74c", Delete = "\ue74d", Save = "\ue74e", Mute = "\ue74f", BackSpaceQWERTY = "\ue750", ReturnKey = "\ue751", UpArrowShiftKey = "\ue752", Cloud = "\ue753", Flashlight = "\ue754", RotationLock = "\ue755", CommandPrompt = "\ue756", SIPMove = "\ue759", SIPUndock = "\ue75a", SIPRedock = "\ue75b", EraseTool = "\ue75c", UnderscoreSpace = "\ue75d", GripperTool = "\ue75e", Dialpad = "\ue75f", PageLeft = "\ue760", PageRight = "\ue761", MultiSelect = "\ue762", KeyboardLeftHanded = "\ue763", KeyboardRightHanded = "\ue764", KeyboardClassic = "\ue765", KeyboardSplit = "\ue766", Volume = "\ue767", Play = "\ue768", Pause = "\ue769", ChevronLeft = "\ue76b", ChevronRight = "\ue76c", InkingTool = "\ue76d", Emoji2 = "\ue76e", GripperBarHorizontal = "\ue76f", System = "\ue770", Personalize = "\ue771", Devices = "\ue772", SearchAndApps = "\ue773", Globe = "\ue774", TimeLanguage = "\ue775", EaseOfAccess = "\ue776", UpdateRestore = "\ue777", HangUp = "\ue778", ContactInfo = "\ue779", Unpin = "\ue77a", Contact = "\ue77b", Memo = "\ue77c", IncomingCall = "\ue77e", Paste = "\ue77f", PhoneBook = "\ue780", LEDLight = "\ue781", Error = "\ue783", GripperBarVertical = "\ue784", Unlock = "\ue785", Slideshow = "\ue786", Calendar = "\ue787", GripperResize = "\ue788", Megaphone = "\ue789", Trim = "\ue78a", NewWindow = "\ue78b", SaveLocal = "\ue78c", Color = "\ue790", DataSense = "\ue791", SaveAs = "\ue792", Light = "\ue793", AspectRatio = "\ue799", DataSenseBar = "\ue7a5", Redo = "\ue7a6", Undo = "\ue7a7", Crop = "\ue7a8", OpenWith = "\ue7ac", Rotate = "\ue7ad", RedEye = "\ue7b3", SetlockScreen = "\ue7b5", MapPin2 = "\ue7b7", Package = "\ue7b8", Warning = "\ue7ba", ReadingList = "\ue7bc", Education = "\ue7be", ShoppingCart = "\ue7bf", Train = "\ue7c0", Flag = "\ue7c1", Move = "\ue7c2", Page = "\ue7c3", TaskView = "\ue7c4", BrowsePhotos = "\ue7c5", HalfStarLeft = "\ue7c6", HalfStarRight = "\ue7c7", Record = "\ue7c8", TouchPointer = "\ue7c9", LangJPN = "\ue7de", Ferry = "\ue7e3", Highlight = "\ue7e6", ActionCenterNotification = "\ue7e7", PowerButton = "\ue7e8", ResizeTouchNarrower = "\ue7ea", ResizeTouchShorter = "\ue7eb", DrivingMode = "\ue7ec", RingerSilent = "\ue7ed", OtherUser = "\ue7ee", Admin = "\ue7ef", CC = "\ue7f0", SDCard = "\ue7f1", CallForwarding = "\ue7f2", SettingsDisplaySound = "\ue7f3", TVMonitor = "\ue7f4", Speakers = "\ue7f5", Headphone = "\ue7f6", DeviceLaptopPic = "\ue7f7", DeviceLaptopNoPic = "\ue7f8", DeviceMonitorRightPic = "\ue7f9", DeviceMonitorLeftPic = "\ue7fa", DeviceMonitorNoPic = "\ue7fb", Game = "\ue7fc", HorizontalTabKey = "\ue7fd", StreetsideSplitMinimize = "\ue802", StreetsideSplitExpand = "\ue803", Car = "\ue804", Walk = "\ue805", Bus = "\ue806", TiltUp = "\ue809", TiltDown = "\ue80a", CallControl = "\ue80b", RotateMapRight = "\ue80c", RotateMapLeft = "\ue80d", Home = "\ue80f", ParkingLocation = "\ue811", MapCompassTop = "\ue812", MapCompassBottom = "\ue813", IncidentTriangle = "\ue814", Touch = "\ue815", MapDirections = "\ue816", StartPoint = "\ue819", StopPoint = "\ue81a", EndPoint = "\ue81b", History = "\ue81c", Location = "\ue81d", MapLayers = "\ue81e", Accident = "\ue81f", Work = "\ue821", Construction = "\ue822", Recent = "\ue823", Bank = "\ue825", DownloadMap = "\ue826", InkingToolFill2 = "\ue829", HighlightFill2 = "\ue82a", EraseToolFill = "\ue82b", EraseToolFill2 = "\ue82c", Dictionary = "\ue82d", DictionaryAdd = "\ue82e", ToolTip = "\ue82f", ChromeBack = "\ue830", ProvisioningPackage = "\ue835", AddRemoteDevice = "\ue836", FolderOpen = "\ue838", Ethernet = "\ue839", ShareBroadband = "\ue83a", DirectAccess = "\ue83b", DialUp = "\ue83c", DefenderApp = "\ue83d", BatteryCharging9 = "\ue83e", Battery10 = "\ue83f", Pinned = "\ue840", PinFill = "\ue841", PinnedFill = "\ue842", PeriodKey = "\ue843", PuncKey = "\ue844", RevToggleKey = "\ue845", RightArrowKeyTime1 = "\ue846", RightArrowKeyTime2 = "\ue847", LeftQuote = "\ue848", RightQuote = "\ue849", DownShiftKey = "\ue84a", UpShiftKey = "\ue84b", PuncKey0 = "\ue84c", PuncKeyLeftBottom = "\ue84d", RightArrowKeyTime3 = "\ue84e", RightArrowKeyTime4 = "\ue84f", Battery0 = "\ue850", Battery1 = "\ue851", Battery2 = "\ue852", Battery3 = "\ue853", Battery4 = "\ue854", Battery5 = "\ue855", Battery6 = "\ue856", Battery7 = "\ue857", Battery8 = "\ue858", Battery9 = "\ue859", BatteryCharging0 = "\ue85a", BatteryCharging1 = "\ue85b", BatteryCharging2 = "\ue85c", BatteryCharging3 = "\ue85d", BatteryCharging4 = "\ue85e", BatteryCharging5 = "\ue85f", BatteryCharging6 = "\ue860", BatteryCharging7 = "\ue861", BatteryCharging8 = "\ue862", BatterySaver0 = "\ue863", BatterySaver1 = "\ue864", BatterySaver2 = "\ue865", BatterySaver3 = "\ue866", BatterySaver4 = "\ue867", BatterySaver5 = "\ue868", BatterySaver6 = "\ue869", BatterySaver7 = "\ue86a", BatterySaver8 = "\ue86b", SignalBars1 = "\ue86c", SignalBars2 = "\ue86d", SignalBars3 = "\ue86e", SignalBars4 = "\ue86f", SignalBars5 = "\ue870", SignalNotConnected = "\ue871", Wifi1 = "\ue872", Wifi2 = "\ue873", Wifi3 = "\ue874", MobSIMLock = "\ue875", MobSIMMissing = "\ue876", Vibrate = "\ue877", RoamingInternational = "\ue878", RoamingDomestic = "\ue879", CallForwardInternational = "\ue87a", CallForwardRoaming = "\ue87b", JpnRomanji = "\ue87c", JpnRomanjiLock = "\ue87d", JpnRomanjiShift = "\ue87e", JpnRomanjiShiftLock = "\ue87f", StatusDataTransfer = "\ue880", StatusDataTransferVPN = "\ue881", StatusDualSIM2 = "\ue882", StatusDualSIM2VPN = "\ue883", StatusDualSIM1 = "\ue884", StatusDualSIM1VPN = "\ue885", StatusSGLTE = "\ue886", StatusSGLTECell = "\ue887", StatusSGLTEDataVPN = "\ue888", StatusVPN = "\ue889", WifiHotspot = "\ue88a", LanguageKor = "\ue88b", LanguageCht = "\ue88c", LanguageChs = "\ue88d", USB = "\ue88e", InkingToolFill = "\ue88f", View = "\ue890", HighlightFill = "\ue891", Previous = "\ue892", Next = "\ue893", Clear = "\ue894", Sync = "\ue895", Download = "\ue896", Help = "\ue897", Upload = "\ue898", Emoji = "\ue899", TwoPage = "\ue89a", LeaveChat = "\ue89b", MailForward = "\ue89c", RotateCamera = "\ue89e", ClosePane = "\ue89f", OpenPane = "\ue8a0", PreviewLink = "\ue8a1", AttachCamera = "\ue8a2", ZoomIn = "\ue8a3", Bookmarks = "\ue8a4", Document = "\ue8a5", ProtectedDocument = "\ue8a6", OpenInNewWindow = "\ue8a7", MailFill = "\ue8a8", ViewAll = "\ue8a9", VideoChat = "\ue8aa", Switch = "\ue8ab", Rename = "\ue8ac", Go = "\ue8ad", SurfaceHub = "\ue8ae", Remote = "\ue8af", Click = "\ue8b0", Shuffle = "\ue8b1", Movies = "\ue8b2", SelectAll = "\ue8b3", Orientation = "\ue8b4", Import = "\ue8b5", ImportAll = "\ue8b6", Folder = "\ue8b7", Webcam = "\ue8b8", Picture = "\ue8b9", Caption = "\ue8ba", ChromeClose = "\ue8bb", ShowResults = "\ue8bc", Message = "\ue8bd", Leaf = "\ue8be", CalendarDay = "\ue8bf", CalendarWeek = "\ue8c0", Characters = "\ue8c1", MailReplyAll = "\ue8c2", Read = "\ue8c3", ShowBcc = "\ue8c4", HideBcc = "\ue8c5", Cut = "\ue8c6", PaymentCard = "\ue8c7", Copy = "\ue8c8", Important = "\ue8c9", MailReply = "\ue8ca", Sort = "\ue8cb", MobileTablet = "\ue8cc", DisconnectDrive = "\ue8cd", MapDrive = "\ue8ce", ContactPresence = "\ue8cf", Priority = "\ue8d0", GotoToday = "\ue8d1", Font = "\ue8d2", FontColor = "\ue8d3", Contact2 = "\ue8d4", FolderFill = "\ue8d5", Audio = "\ue8d6", Permissions = "\ue8d7", DisableUpdates = "\ue8d8", Unfavorite = "\ue8d9", OpenLocal = "\ue8da", Italic = "\ue8db", Underline = "\ue8dc", Bold = "\ue8dd", MoveToFolder = "\ue8de", LikeDislike = "\ue8df", Dislike = "\ue8e0", Like = "\ue8e1", AlignRight = "\ue8e2", AlignCenter = "\ue8e3", AlignLeft = "\ue8e4", OpenFile = "\ue8e5", ClearSelection = "\ue8e6", FontDecrease = "\ue8e7", FontIncrease = "\ue8e8", FontSize = "\ue8e9", CellPhone = "\ue8ea", Reshare = "\ue8eb", Tag = "\ue8ec", RepeatOne = "\ue8ed", RepeatAll = "\ue8ee", Calculator = "\ue8ef", Directions = "\ue8f0", Library = "\ue8f1", ChatBubbles = "\ue8f2", PostUpdate = "\ue8f3", NewFolder = "\ue8f4", CalendarReply = "\ue8f5", UnsyncFolder = "\ue8f6", SyncFolder = "\ue8f7", BlockContact = "\ue8f8", SwitchApps = "\ue8f9", AddFriend = "\ue8fa", Accept = "\ue8fb", GoToStart = "\ue8fc", BulletedList = "\ue8fd", Scan = "\ue8fe", Preview = "\ue8ff", Group = "\ue902", ZeroBars = "\ue904", OneBar = "\ue905", TwoBars = "\ue906", ThreeBars = "\ue907", FourBars = "\ue908", World = "\ue909", Comment = "\ue90a", MusicInfo = "\ue90b", DockLeft = "\ue90c", DockRight = "\ue90d", DockBottom = "\ue90e", Repair = "\ue90f", Accounts = "\ue910", DullSound = "\ue911", Manage = "\ue912", Street = "\ue913", Printer3D = "\ue914", RadioBullet = "\ue915", Stopwatch = "\ue916", Photo = "\ue91b", ActionCenter = "\ue91c", FullCircleMask = "\ue91f", ChromeMinimize = "\ue921", ChromeMaximize = "\ue922", ChromeRestore = "\ue923", Annotation = "\ue924", BackSpaceQWERTYSm = "\ue925", BackSpaceQWERTYMd = "\ue926", Swipe = "\ue927", Fingerprint = "\ue928", Handwriting = "\ue929", ChromeBackToWindow = "\ue92c", ChromeFullScreen = "\ue92d", KeyboardStandard = "\ue92e", KeyboardDismiss = "\ue92f", Completed = "\ue930", ChromeAnnotate = "\ue931", Label = "\ue932", IBeam = "\ue933", IBeamOutline = "\ue934", FlickDown = "\ue935", FlickUp = "\ue936", FlickLeft = "\ue937", FlickRight = "\ue938", FeedbackApp = "\ue939", MusicAlbum = "\ue93c", Streaming = "\ue93e", Code = "\ue943", ReturnToWindow = "\ue944", LightningBolt = "\ue945", Info = "\ue946", CalculatorMultiply = "\ue947", CalculatorAddition = "\ue948", CalculatorSubtract = "\ue949", CalculatorDivide = "\ue94a", CalculatorSquareroot = "\ue94b", CalculatorPercentage = "\ue94c", CalculatorNegate = "\ue94d", CalculatorEqualTo = "\ue94e", CalculatorBackspace = "\ue94f", Component = "\ue950", DMC = "\ue951", Dock = "\ue952", MultimediaDMS = "\ue953", MultimediaDVR = "\ue954", MultimediaPMP = "\ue955", PrintfaxPrinterFile = "\ue956", Sensor = "\ue957", StorageOptical = "\ue958", Communications = "\ue95a", Headset = "\ue95b", Projector = "\ue95d", Health = "\ue95e", Wire = "\ue95f", Webcam2 = "\ue960", Input = "\ue961", Mouse = "\ue962", Smartcard = "\ue963", SmartcardVirtual = "\ue964", MediaStorageTower = "\ue965", ReturnKeySm = "\ue966", GameConsole = "\ue967", Network = "\ue968", StorageNetworkWireless = "\ue969", StorageTape = "\ue96a", ChevronUpSmall = "\ue96d", ChevronDownSmall = "\ue96e", ChevronLeftSmall = "\ue96f", ChevronRightSmall = "\ue970", ChevronUpMed = "\ue971", ChevronDownMed = "\ue972", ChevronLeftMed = "\ue973", ChevronRightMed = "\ue974", Devices2 = "\ue975", ExpandTile = "\ue976", PC1 = "\ue977", PresenceChicklet = "\ue978", PresenceChickletVideo = "\ue979", Reply = "\ue97a", SetTile = "\ue97b", Type = "\ue97c", Korean = "\ue97d", HalfAlpha = "\ue97e", FullAlpha = "\ue97f", Key12On = "\ue980", ChineseChangjie = "\ue981", QWERTYOn = "\ue982", QWERTYOff = "\ue983", ChineseQuick = "\ue984", Japanese = "\ue985", FullHiragana = "\ue986", FullKatakana = "\ue987", HalfKatakana = "\ue988", ChineseBoPoMoFo = "\ue989", ChinesePinyin = "\ue98a", ConstructionCone = "\ue98f", XboxOneConsole = "\ue990", Volume0 = "\ue992", Volume1 = "\ue993", Volume2 = "\ue994", Volume3 = "\ue995", BatteryUnknown = "\ue996", WifiAttentionOverlay = "\ue998", Robot = "\ue99a", TapAndSend = "\ue9a1", FitPage = "\ue9a6", PasswordKeyShow = "\ue9a8", PasswordKeyHide = "\ue9a9", BidiLtr = "\ue9aa", BidiRtl = "\ue9ab", ForwardSm = "\ue9ac", CommaKey = "\ue9ad", DashKey = "\ue9ae", DullSoundKey = "\ue9af", HalfDullSound = "\ue9b0", RightDoubleQuote = "\ue9b1", LeftDoubleQuote = "\ue9b2", PuncKeyRightBottom = "\ue9b3", PuncKey1 = "\ue9b4", PuncKey2 = "\ue9b5", PuncKey3 = "\ue9b6", PuncKey4 = "\ue9b7", PuncKey5 = "\ue9b8", PuncKey6 = "\ue9b9", PuncKey9 = "\ue9ba", PuncKey7 = "\ue9bb", PuncKey8 = "\ue9bc", Frigid = "\ue9ca", Unknown = "\ue9ce", AreaChart = "\ue9d2", CheckList = "\ue9d5", Diagnostic = "\ue9d9", Equalizer = "\ue9e9", Process = "\ue9f3", Processing = "\ue9f5", ReportDocument = "\ue9f9", VideoSolid = "\uea0c", MixedMediaBadge = "\uea0d", DisconnectDisplay = "\uea14", Shield = "\uea18", Info2 = "\uea1f", ActionCenterAsterisk = "\uea21", Beta = "\uea24", SaveCopy = "\uea35", List = "\uea37", Asterisk = "\uea38", ErrorBadge = "\uea39", CircleRing = "\uea3a", CircleFill = "\uea3b", MergeCall = "\uea3c", PrivateCall = "\uea3d", Record2 = "\uea3f", AllAppsMirrored = "\uea40", BookmarksMirrored = "\uea41", BulletedListMirrored = "\uea42", CallForwardInternationalMirrored = "\uea43", CallForwardRoamingMirrored = "\uea44", ChromeBackMirrored = "\uea47", ClearSelectionMirrored = "\uea48", ClosePaneMirrored = "\uea49", ContactInfoMirrored = "\uea4a", DockRightMirrored = "\uea4b", DockLeftMirrored = "\uea4c", ExpandTileMirrored = "\uea4e", GoMirrored = "\uea4f", GripperResizeMirrored = "\uea50", HelpMirrored = "\uea51", ImportMirrored = "\uea52", ImportAllMirrored = "\uea53", LeaveChatMirrored = "\uea54", ListMirrored = "\uea55", MailForwardMirrored = "\uea56", MailReplyMirrored = "\uea57", MailReplyAllMirrored = "\uea58", OpenPaneMirrored = "\uea5b", OpenWithMirrored = "\uea5c", ParkingLocationMirrored = "\uea5e", ResizeMouseMediumMirrored = "\uea5f", ResizeMouseSmallMirrored = "\uea60", ResizeMouseTallMirrored = "\uea61", ResizeTouchNarrowerMirrored = "\uea62", SendMirrored = "\uea63", SendFillMirrored = "\uea64", ShowResultsMirrored = "\uea65", Media = "\uea69", SyncError = "\uea6a", Devices3 = "\uea6c", SlowMotionOn = "\uea79", Lightbulb = "\uea80", StatusCircle = "\uea81", StatusTriangle = "\uea82", StatusError = "\uea83", StatusWarning = "\uea84", Puzzle = "\uea86", CalendarSolid = "\uea89", HomeSolid = "\uea8a", ParkingLocationSolid = "\uea8b", ContactSolid = "\uea8c", ConstructionSolid = "\uea8d", AccidentSolid = "\uea8e", Ringer = "\uea8f", PDF = "\uea90", ThoughtBubble = "\uea91", HeartBroken = "\uea92", BatteryCharging10 = "\uea93", BatterySaver9 = "\uea94", BatterySaver10 = "\uea95", CallForwardingMirrored = "\uea97", MultiSelectMirrored = "\uea98", Broom = "\uea99", ForwardCall = "\ueac2", Trackers = "\ueadf", Market = "\ueafc", PieSingle = "\ueb05", StockDown = "\ueb0f", StockUp = "\ueb11", Design = "\ueb3c", Website = "\ueb41", Drop = "\ueb42", Radar = "\ueb44", BusSolid = "\ueb47", FerrySolid = "\ueb48", StartPointSolid = "\ueb49", StopPointSolid = "\ueb4a", EndPointSolid = "\ueb4b", AirplaneSolid = "\ueb4c", TrainSolid = "\ueb4d", WorkSolid = "\ueb4e", ReminderFill = "\ueb4f", Reminder = "\ueb50", Heart = "\ueb51", HeartFill = "\ueb52", EthernetError = "\ueb55", EthernetWarning = "\ueb56", StatusConnecting1 = "\ueb57", StatusConnecting2 = "\ueb58", StatusUnsecure = "\ueb59", WifiError0 = "\ueb5a", WifiError1 = "\ueb5b", WifiError2 = "\ueb5c", WifiError3 = "\ueb5d", WifiError4 = "\ueb5e", WifiWarning0 = "\ueb5f", WifiWarning1 = "\ueb60", WifiWarning2 = "\ueb61", WifiWarning3 = "\ueb62", WifiWarning4 = "\ueb63", Devices4 = "\ueb66", NUIIris = "\ueb67", NUIFace = "\ueb68", GatewayRouter = "\ueb77", EditMirrored = "\ueb7e", NUIFPStartSlideHand = "\ueb82", NUIFPStartSlideAction = "\ueb83", NUIFPContinueSlideHand = "\ueb84", NUIFPContinueSlideAction = "\ueb85", NUIFPRollRightHand = "\ueb86", NUIFPRollRightHandAction = "\ueb87", NUIFPRollLeftHand = "\ueb88", NUIFPRollLeftAction = "\ueb89", NUIFPPressHand = "\ueb8a", NUIFPPressAction = "\ueb8b", NUIFPPressRepeatHand = "\ueb8c", NUIFPPressRepeatAction = "\ueb8d", StatusErrorFull = "\ueb90", TaskViewExpanded = "\ueb91", Certificate = "\ueb95", BackSpaceQWERTYLg = "\ueb96", ReturnKeyLg = "\ueb97", FastForward = "\ueb9d", Rewind = "\ueb9e", Photo2 = "\ueb9f", MobBattery0 = "\ueba0", MobBattery1 = "\ueba1", MobBattery2 = "\ueba2", MobBattery3 = "\ueba3", MobBattery4 = "\ueba4", MobBattery5 = "\ueba5", MobBattery6 = "\ueba6", MobBattery7 = "\ueba7", MobBattery8 = "\ueba8", MobBattery9 = "\ueba9", MobBattery10 = "\uebaa", MobBatteryCharging0 = "\uebab", MobBatteryCharging1 = "\uebac", MobBatteryCharging2 = "\uebad", MobBatteryCharging3 = "\uebae", MobBatteryCharging4 = "\uebaf", MobBatteryCharging5 = "\uebb0", MobBatteryCharging6 = "\uebb1", MobBatteryCharging7 = "\uebb2", MobBatteryCharging8 = "\uebb3", MobBatteryCharging9 = "\uebb4", MobBatteryCharging10 = "\uebb5", MobBatterySaver0 = "\uebb6", MobBatterySaver1 = "\uebb7", MobBatterySaver2 = "\uebb8", MobBatterySaver3 = "\uebb9", MobBatterySaver4 = "\uebba", MobBatterySaver5 = "\uebbb", MobBatterySaver6 = "\uebbc", MobBatterySaver7 = "\uebbd", MobBatterySaver8 = "\uebbe", MobBatterySaver9 = "\uebbf", MobBatterySaver10 = "\uebc0", DictionaryCloud = "\uebc3", ResetDrive = "\uebc4", VolumeBars = "\uebc5", Project = "\uebc6", AdjustHologram = "\uebd2", CloudDownload = "\uebd3", MobWifiCallBars = "\uebd4", MobWifiCall0 = "\uebd5", MobWifiCall1 = "\uebd6", MobWifiCall2 = "\uebd7", MobWifiCall3 = "\uebd8", MobWifiCall4 = "\uebd9", Family = "\uebda", LockFeedback = "\uebdb", DeviceDiscovery = "\uebde", WindDirection = "\uebe6", RightArrowKeyTime0 = "\uebe7", Bug = "\uebe8", TabletMode = "\uebfc", StatusCircleLeft = "\uebfd", StatusTriangleLeft = "\uebfe", StatusErrorLeft = "\uebff", StatusWarningLeft = "\uec00", MobBatteryUnknown = "\uec02", NetworkTower = "\uec05", CityNext = "\uec06", CityNext2 = "\uec07", Courthouse = "\uec08", Groceries = "\uec09", Sustainable = "\uec0a", BuildingEnergy = "\uec0b", ToggleFilled = "\uec11", ToggleBorder = "\uec12", SliderThumb = "\uec13", ToggleThumb = "\uec14", MiracastLogoSmall = "\uec15", MiracastLogoLarge = "\uec16", PLAP = "\uec19", Badge = "\uec1b", SignalRoaming = "\uec1e", MobileLocked = "\uec20", InsiderHubApp = "\uec24", PersonalFolder = "\uec25", HomeGroup = "\uec26", MyNetwork = "\uec27", KeyboardFull = "\uec31", Cafe = "\uec32", MobSignal1 = "\uec37", MobSignal2 = "\uec38", MobSignal3 = "\uec39", MobSignal4 = "\uec3a", MobSignal5 = "\uec3b", MobWifi1 = "\uec3c", MobWifi2 = "\uec3d", MobWifi3 = "\uec3e", MobWifi4 = "\uec3f", MobAirplane = "\uec40", MobBluetooth = "\uec41", MobActionCenter = "\uec42", MobLocation = "\uec43", MobWifiHotspot = "\uec44", LanguageJpn = "\uec45", MobQuietHours = "\uec46", MobDrivingMode = "\uec47", SpeedOff = "\uec48", SpeedMedium = "\uec49", SpeedHigh = "\uec4a", ThisPC = "\uec4e", MusicNote = "\uec4f", FileExplorer = "\uec50", FileExplorerApp = "\uec51", LeftArrowKeyTime0 = "\uec52", MicOff = "\uec54", MicSleep = "\uec55", MicError = "\uec56", PlaybackRate1x = "\uec57", PlaybackRateOther = "\uec58", CashDrawer = "\uec59", BarcodeScanner = "\uec5a", ReceiptPrinter = "\uec5b", MagStripeReader = "\uec5c", CompletedSolid = "\uec61", CompanionApp = "\uec64", Favicon2 = "\uec6c", SwipeRevealArt = "\uec6d", MicOn = "\uec71", MicClipping = "\uec72", TabletSelected = "\uec74", MobileSelected = "\uec75", LaptopSelected = "\uec76", TVMonitorSelected = "\uec77", DeveloperTools = "\uec7a", MobCallForwarding = "\uec7e", MobCallForwardingMirrored = "\uec7f", BodyCam = "\uec80", PoliceCar = "\uec81", Draw = "\uec87", DrawSolid = "\uec88", LowerBrightness = "\uec8a", ScrollUpDown = "\uec8f", DateTime = "\uec92", HoloLens = "\uec94", Tiles = "\ueca5", PartyLeader = "\ueca7", AppIconDefault = "\uecaa", Calories = "\uecad", POI = "\uecaf", BandBattery0 = "\uecb9", BandBattery1 = "\uecba", BandBattery2 = "\uecbb", BandBattery3 = "\uecbc", BandBattery4 = "\uecbd", BandBattery5 = "\uecbe", BandBattery6 = "\uecbf", AddSurfaceHub = "\uecc4", DevUpdate = "\uecc5", Unit = "\uecc6", AddTo = "\uecc8", RemoveFrom = "\uecc9", RadioBtnOff = "\uecca", RadioBtnOn = "\ueccb", RadioBullet2 = "\ueccc", ExploreContent = "\ueccd", Blocked2 = "\uece4", ScrollMode = "\uece7", ZoomMode = "\uece8", PanMode = "\uece9", WiredUSB = "\uecf0", WirelessUSB = "\uecf1", USBSafeConnect = "\uecf3", ActionCenterNotificationMirrored = "\ued0c", ActionCenterMirrored = "\ued0d", SubscriptionAdd = "\ued0e", ResetDevice = "\ued10", SubscriptionAddMirrored = "\ued11", QRCode = "\ued14", Feedback = "\ued15", Hide = "\ued1a", Subtitles = "\ued1e", SubtitlesAudio = "\ued1f", OpenFolderHorizontal = "\ued25", CalendarMirrored = "\ued28", MobeSIM = "\ued2a", MobeSIMNoProfile = "\ued2b", MobeSIMLocked = "\ued2c", MobeSIMBusy = "\ued2d", SignalError = "\ued2e", StreamingEnterprise = "\ued2f", Headphone0 = "\ued30", Headphone1 = "\ued31", Headphone2 = "\ued32", Headphone3 = "\ued33", Apps = "\ued35", KeyboardBrightness = "\ued39", KeyboardLowerBrightness = "\ued3a", SkipBack10 = "\ued3c", SkipForward30 = "\ued3d", TreeFolderFolder = "\ued41", TreeFolderFolderFill = "\ued42", TreeFolderFolderOpen = "\ued43", TreeFolderFolderOpenFill = "\ued44", MultimediaDMP = "\ued47", KeyboardOneHanded = "\ued4c", Narrator = "\ued4d", EmojiTabPeople = "\ued53", EmojiTabSmilesAnimals = "\ued54", EmojiTabCelebrationObjects = "\ued55", EmojiTabFoodPlants = "\ued56", EmojiTabTransitPlaces = "\ued57", EmojiTabSymbols = "\ued58", EmojiTabTextSmiles = "\ued59", EmojiTabFavorites = "\ued5a", EmojiSwatch = "\ued5b", ConnectApp = "\ued5c", CompanionDeviceFramework = "\ued5d", Ruler = "\ued5e", FingerInking = "\ued5f", StrokeErase = "\ued60", PointErase = "\ued61", ClearAllInk = "\ued62", Pencil = "\ued63", Marker = "\ued64", InkingCaret = "\ued65", InkingColorOutline = "\ued66", InkingColorFill = "\ued67", HardDrive = "\ueda2", NetworkAdapter = "\ueda3", Touchscreen = "\ueda4", NetworkPrinter = "\ueda5", CloudPrinter = "\ueda6", KeyboardShortcut = "\ueda7", BrushSize = "\ueda8", NarratorForward = "\ueda9", NarratorForwardMirrored = "\uedaa", SyncBadge12 = "\uedab", RingerBadge12 = "\uedac", AsteriskBadge12 = "\uedad", ErrorBadge12 = "\uedae", CircleRingBadge12 = "\uedaf", CircleFillBadge12 = "\uedb0", ImportantBadge12 = "\uedb1", MailBadge12 = "\uedb3", PauseBadge12 = "\uedb4", PlayBadge12 = "\uedb5", PenWorkspace = "\uedc6", CaretLeft8 = "\uedd5", CaretRight8 = "\uedd6", CaretUp8 = "\uedd7", CaretDown8 = "\uedd8", CaretLeftSolid8 = "\uedd9", CaretRightSolid8 = "\uedda", CaretUpSolid8 = "\ueddb", CaretDownSolid8 = "\ueddc", Strikethrough = "\uede0", Export = "\uede1", ExportMirrored = "\uede2", ButtonMenu = "\uede3", CloudSearch = "\uede4", PinyinIMELogo = "\uede5", CalligraphyPen = "\uedfb", ReplyMirrored = "\uee35", LockscreenDesktop = "\uee3f", TaskViewSettings = "\uee40", MiniExpand2Mirrored = "\uee47", MiniContract2Mirrored = "\uee49", Play36 = "\uee4a", PenPalette = "\uee56", GuestUser = "\uee57", SettingsBattery = "\uee63", TaskbarPhone = "\uee64", LockScreenGlance = "\uee65", GenericScan = "\uee6f", ImageExport = "\uee71", WifiEthernet = "\uee77", ActionCenterQuiet = "\uee79", ActionCenterQuietNotification = "\uee7a", TrackersMirrored = "\uee92", DateTimeMirrored = "\uee93", Wheel = "\uee94", VirtualMachineGroup = "\ueea3", ButtonView2 = "\ueeca", PenWorkspaceMirrored = "\uef15", PenPaletteMirrored = "\uef16", StrokeEraseMirrored = "\uef17", PointEraseMirrored = "\uef18", ClearAllInkMirrored = "\uef19", BackgroundToggle = "\uef1f", Marquee = "\uef20", ChromeCloseContrast = "\uef2c", ChromeMinimizeContrast = "\uef2d", ChromeMaximizeContrast = "\uef2e", ChromeRestoreContrast = "\uef2f", TrafficLight = "\uef31", Replay = "\uef3b", Eyedropper = "\uef3c", LineDisplay = "\uef3d", PINPad = "\uef3e", SignatureCapture = "\uef3f", ChipCardCreditCardReader = "\uef40", MarketDown = "\uef42", PlayerSettings = "\uef58", LandscapeOrientation = "\uef6b", Flow = "\uef90", Touchpad = "\uefa5", Speech = "\uefa9", KnowledgeArticle = "\uf000", Relationship = "\uf003", ZipFolder = "\uf012", DefaultAPN = "\uf080", UserAPN = "\uf081", DoublePinyin = "\uf085", BlueLight = "\uf08c", CaretSolidLeft = "\uf08d", CaretSolidDown = "\uf08e", CaretSolidRight = "\uf08f", CaretSolidUp = "\uf090", ButtonA = "\uf093", ButtonB = "\uf094", ButtonY = "\uf095", ButtonX = "\uf096", ArrowUp8 = "\uf0ad", ArrowDown8 = "\uf0ae", ArrowRight8 = "\uf0af", ArrowLeft8 = "\uf0b0", QuarentinedItems = "\uf0b2", QuarentinedItemsMirrored = "\uf0b3", Protractor = "\uf0b4", ChecklistMirrored = "\uf0b5", StatusCircle7 = "\uf0b6", StatusCheckmark7 = "\uf0b7", StatusErrorCircle7 = "\uf0b8", Connected = "\uf0b9", PencilFill = "\uf0c6", CalligraphyFill = "\uf0c7", QuarterStarLeft = "\uf0ca", QuarterStarRight = "\uf0cb", ThreeQuarterStarLeft = "\uf0cc", ThreeQuarterStarRight = "\uf0cd", QuietHoursBadge12 = "\uf0ce", BackMirrored = "\uf0d2", ForwardMirrored = "\uf0d3", ChromeBackContrast = "\uf0d5", ChromeBackContrastMirrored = "\uf0d6", ChromeBackToWindowContrast = "\uf0d7", ChromeFullScreenContrast = "\uf0d8", GridView = "\uf0e2", ClipboardList = "\uf0e3", ClipboardListMirrored = "\uf0e4", OutlineQuarterStarLeft = "\uf0e5", OutlineQuarterStarRight = "\uf0e6", OutlineHalfStarLeft = "\uf0e7", OutlineHalfStarRight = "\uf0e8", OutlineThreeQuarterStarLeft = "\uf0e9", OutlineThreeQuarterStarRight = "\uf0ea", SpatialVolume0 = "\uf0eb", SpatialVolume1 = "\uf0ec", SpatialVolume2 = "\uf0ed", SpatialVolume3 = "\uf0ee", ApplicationGuard = "\uf0ef", OutlineStarLeftHalf = "\uf0f7", OutlineStarRightHalf = "\uf0f8", ChromeAnnotateContrast = "\uf0f9", DefenderBadge12 = "\uf0fb", DetachablePC = "\uf103", LeftStick = "\uf108", RightStick = "\uf109", TriggerLeft = "\uf10a", TriggerRight = "\uf10b", BumperLeft = "\uf10c", BumperRight = "\uf10d", Dpad = "\uf10e", EnglishPunctuation = "\uf110", ChinesePunctuation = "\uf111", HMD = "\uf119", CtrlSpatialRight = "\uf11b", PaginationDotOutline10 = "\uf126", PaginationDotSolid10 = "\uf127", StrokeErase2 = "\uf128", SmallErase = "\uf129", LargeErase = "\uf12a", FolderHorizontal = "\uf12b", MicrophoneListening = "\uf12e", StatusExclamationCircle7 = "\uf12f", Video360 = "\uf131", GiftboxOpen = "\uf133", StatusCircleOuter = "\uf136", StatusCircleInner = "\uf137", StatusCircleRing = "\uf138", StatusTriangleOuter = "\uf139", StatusTriangleInner = "\uf13a", StatusTriangleExclamation = "\uf13b", StatusCircleExclamation = "\uf13c", StatusCircleErrorX = "\uf13d", StatusCircleCheckmark = "\uf13e", StatusCircleInfo = "\uf13f", StatusCircleBlock = "\uf140", StatusCircleBlock2 = "\uf141", StatusCircleQuestionMark = "\uf142", StatusCircleSync = "\uf143", Dial1 = "\uf146", Dial2 = "\uf147", Dial3 = "\uf148", Dial4 = "\uf149", Dial5 = "\uf14a", Dial6 = "\uf14b", Dial7 = "\uf14c", Dial8 = "\uf14d", Dial9 = "\uf14e", Dial10 = "\uf14f", Dial11 = "\uf150", Dial12 = "\uf151", Dial13 = "\uf152", Dial14 = "\uf153", Dial15 = "\uf154", Dial16 = "\uf155", DialShape1 = "\uf156", DialShape2 = "\uf157", DialShape3 = "\uf158", DialShape4 = "\uf159", ClosedCaptionsInternational = "\uf15f", TollSolid = "\uf161", TrafficCongestionSolid = "\uf163", ExploreContentSingle = "\uf164", CollapseContent = "\uf165", CollapseContentSingle = "\uf166", InfoSolid = "\uf167", GroupList = "\uf168", CaretBottomRightSolidCenter8 = "\uf169", ProgressRingDots = "\uf16a", Checkbox14 = "\uf16b", CheckboxComposite14 = "\uf16c", CheckboxIndeterminateCombo14 = "\uf16d", CheckboxIndeterminateCombo = "\uf16e", StatusPause7 = "\uf175", CharacterAppearance = "\uf17f", Lexicon = "\uf180", ScreenTime = "\uf182", HeadlessDevice = "\uf191", NetworkSharing = "\uf193", EyeGaze = "\uf19d", ToggleLeft = "\uf19e", ToggleRight = "\uf19f", WindowsInsider = "\uf1ad", ChromeSwitch = "\uf1cb", ChromeSwitchContast = "\uf1cc", StatusCheckmark = "\uf1d8", StatusCheckmarkLeft = "\uf1d9", KeyboardLeftAligned = "\uf20c", KeyboardRightAligned = "\uf20d", KeyboardSettings = "\uf210", NetworkPhysical = "\uf211", IOT = "\uf22c", UnknownMirrored = "\uf22e", ViewDashboard = "\uf246", ExploitProtectionSettings = "\uf259", KeyboardNarrow = "\uf260", Keyboard12Key = "\uf261", KeyboardDock = "\uf26b", KeyboardUndock = "\uf26c", KeyboardLeftDock = "\uf26d", KeyboardRightDock = "\uf26e", Ear = "\uf270", PointerHand = "\uf271", Bullseye = "\uf272", DocumentApproval = "\uf28b", LocaleLanguage = "\uf2b7", PassiveAuthentication = "\uf32a", ColorSolid = "\uf354", NetworkOffline = "\uf384", NetworkConnected = "\uf385", NetworkConnectedCheckmark = "\uf386", SignOut = "\uf3b1", StatusInfo = "\uf3cc", StatusInfoLeft = "\uf3cd", NearbySharing = "\uf3e2", CtrlSpatialLeft = "\uf3e7", InteractiveDashboard = "\uf404", DeclineCall = "\uf405", ClippingTool = "\uf406", RectangularClipping = "\uf407", FreeFormClipping = "\uf408", CopyTo = "\uf413", IDBadge = "\uf427", DynamicLock = "\uf439", PenTips = "\uf45e", PenTipsMirrored = "\uf45f", HWPJoin = "\uf460", HWPInsert = "\uf461", HWPStrikeThrough = "\uf462", HWPScratchOut = "\uf463", HWPSplit = "\uf464", HWPNewLine = "\uf465", HWPOverwrite = "\uf466", MobWifiWarning1 = "\uf473", MobWifiWarning2 = "\uf474", MobWifiWarning3 = "\uf475", MobWifiWarning4 = "\uf476", MicLocationCombo = "\uf47f", Globe2 = "\uf49a", SpecialEffectSize = "\uf4a5", GIF = "\uf4a9", Sticker2 = "\uf4aa", SurfaceHubSelected = "\uf4be", HoloLensSelected = "\uf4bf", Earbud = "\uf4c0", MixVolumes = "\uf4c3", Safe = "\uf540", LaptopSecure = "\uf552", PrintDefault = "\uf56d", PageMirrored = "\uf56e", LandscapeOrientationMirrored = "\uf56f", ColorOff = "\uf570", PrintAllPages = "\uf571", PrintCustomRange = "\uf572", PageMarginPortraitNarrow = "\uf573", PageMarginPortraitNormal = "\uf574", PageMarginPortraitModerate = "\uf575", PageMarginPortraitWide = "\uf576", PageMarginLandscapeNarrow = "\uf577", PageMarginLandscapeNormal = "\uf578", PageMarginLandscapeModerate = "\uf579", PageMarginLandscapeWide = "\uf57a", CollateLandscape = "\uf57b", CollatePortrait = "\uf57c", CollatePortraitSeparated = "\uf57d", DuplexLandscapeOneSided = "\uf57e", DuplexLandscapeOneSidedMirrored = "\uf57f", DuplexLandscapeTwoSidedLongEdge = "\uf580", DuplexLandscapeTwoSidedLongEdgeMirrored = "\uf581", DuplexLandscapeTwoSidedShortEdge = "\uf582", DuplexLandscapeTwoSidedShortEdgeMirrored = "\uf583", DuplexPortraitOneSided = "\uf584", DuplexPortraitOneSidedMirrored = "\uf585", DuplexPortraitTwoSidedLongEdge = "\uf586", DuplexPortraitTwoSidedLongEdgeMirrored = "\uf587", DuplexPortraitTwoSidedShortEdge = "\uf588", DuplexPortraitTwoSidedShortEdgeMirrored = "\uf589", PPSOneLandscape = "\uf58a", PPSTwoLandscape = "\uf58b", PPSTwoPortrait = "\uf58c", PPSFourLandscape = "\uf58d", PPSFourPortrait = "\uf58e", HolePunchOff = "\uf58f", HolePunchPortraitLeft = "\uf590", HolePunchPortraitRight = "\uf591", HolePunchPortraitTop = "\uf592", HolePunchPortraitBottom = "\uf593", HolePunchLandscapeLeft = "\uf594", HolePunchLandscapeRight = "\uf595", HolePunchLandscapeTop = "\uf596", HolePunchLandscapeBottom = "\uf597", StaplingOff = "\uf598", StaplingPortraitTopLeft = "\uf599", StaplingPortraitTopRight = "\uf59a", StaplingPortraitBottomRight = "\uf59b", StaplingPortraitTwoLeft = "\uf59c", StaplingPortraitTwoRight = "\uf59d", StaplingPortraitTwoTop = "\uf59e", StaplingPortraitTwoBottom = "\uf59f", StaplingPortraitBookBinding = "\uf5a0", StaplingLandscapeTopLeft = "\uf5a1", StaplingLandscapeTopRight = "\uf5a2", StaplingLandscapeBottomLeft = "\uf5a3", StaplingLandscapeBottomRight = "\uf5a4", StaplingLandscapeTwoLeft = "\uf5a5", StaplingLandscapeTwoRight = "\uf5a6", StaplingLandscapeTwoTop = "\uf5a7", StaplingLandscapeTwoBottom = "\uf5a8", StaplingLandscapeBookBinding = "\uf5a9", StatusDataTransferRoaming = "\uf5aa", MobSIMError = "\uf5ab", CollateLandscapeSeparated = "\uf5ac", PPSOnePortrait = "\uf5ad", StaplingPortraitBottomLeft = "\uf5ae", PlaySolid = "\uf5b0", RepeatOff = "\uf5e7", Set = "\uf5ed", SetSolid = "\uf5ee", FuzzyReading = "\uf5ef", VerticalBattery0 = "\uf5f2", VerticalBattery1 = "\uf5f3", VerticalBattery2 = "\uf5f4", VerticalBattery3 = "\uf5f5", VerticalBattery4 = "\uf5f6", VerticalBattery5 = "\uf5f7", VerticalBattery6 = "\uf5f8", VerticalBattery7 = "\uf5f9", VerticalBattery8 = "\uf5fa", VerticalBattery9 = "\uf5fb", VerticalBattery10 = "\uf5fc", VerticalBatteryCharging0 = "\uf5fd", VerticalBatteryCharging1 = "\uf5fe", VerticalBatteryCharging2 = "\uf5ff", VerticalBatteryCharging3 = "\uf600", VerticalBatteryCharging4 = "\uf601", VerticalBatteryCharging5 = "\uf602", VerticalBatteryCharging6 = "\uf603", VerticalBatteryCharging7 = "\uf604", VerticalBatteryCharging8 = "\uf605", VerticalBatteryCharging9 = "\uf606", VerticalBatteryCharging10 = "\uf607", VerticalBatteryUnknown = "\uf608", SIMError = "\uf618", SIMMissing = "\uf619", SIMLock = "\uf61a", eSIM = "\uf61b", eSIMNoProfile = "\uf61c", eSIMLocked = "\uf61d", eSIMBusy = "\uf61e", NoiseCancelation = "\uf61f", NoiseCancelationOff = "\uf620", MusicSharing = "\uf623", MusicSharingOff = "\uf624", CircleShapeSolid = "\uf63c", WifiCallBars = "\uf657", WifiCall0 = "\uf658", WifiCall1 = "\uf659", WifiCall2 = "\uf65a", WifiCall3 = "\uf65b", WifiCall4 = "\uf65c", CHTLanguageBar = "\uf69e", ComposeMode = "\uf6a9", ExpressiveInputEntry = "\uf6b8", EmojiTabMoreSymbols = "\uf6ba", WebSearch = "\uf6fa", Kiosk = "\uf712", RTTLogo = "\uf714", VoiceCall = "\uf715", GoToMessage = "\uf716", ReturnToCall = "\uf71a", StartPresenting = "\uf71c", StopPresenting = "\uf71d", ProductivityMode = "\uf71e", SetHistoryStatus = "\uf738", SetHistoryStatus2 = "\uf739", Keyboardsettings20 = "\uf73d", OneHandedRight20 = "\uf73e", OneHandedLeft20 = "\uf73f", Split20 = "\uf740", Full20 = "\uf741", Handwriting20 = "\uf742", ChevronLeft20 = "\uf743", ChevronLeft32 = "\uf744", ChevronRight20 = "\uf745", ChevronRight32 = "\uf746", Event12 = "\uf763", MicOff2 = "\uf781", DeliveryOptimization = "\uf785", CancelMedium = "\uf78a", SearchMedium = "\uf78b", AcceptMedium = "\uf78c", RevealPasswordMedium = "\uf78d", DeleteWord = "\uf7ad", DeleteWordFill = "\uf7ae", DeleteLines = "\uf7af", DeleteLinesFill = "\uf7b0", InstertWords = "\uf7b1", InstertWordsFill = "\uf7b2", JoinWords = "\uf7b3", JoinWordsFill = "\uf7b4", OverwriteWords = "\uf7b5", OverwriteWordsFill = "\uf7b6", AddNewLine = "\uf7b7", AddNewLineFill = "\uf7b8", OverwriteWordsKorean = "\uf7b9", OverwriteWordsFillKorean = "\uf7ba", EducationIcon = "\uf7bb", WindowSnipping = "\uf7ed", VideoCapture = "\uf7ee", StatusSecured = "\uf809", NarratorApp = "\uf83b", PowerButtonUpdate = "\uf83d", RestartUpdate = "\uf83e", UpdateStatusDot = "\uf83f", Eject = "\uf847", Spelling = "\uf87b", SpellingKorean = "\uf87c", SpellingSerbian = "\uf87d", SpellingChinese = "\uf87e", FolderSelect = "\uf89a", SmartScreen = "\uf8a5", ExploitProtection = "\uf8a6", AddBold = "\uf8aa", SubtractBold = "\uf8ab", BackSolidBold = "\uf8ac", ForwardSolidBold = "\uf8ad", PauseBold = "\uf8ae", ClickSolid = "\uf8af", SettingsSolid = "\uf8b0", MicrophoneSolidBold = "\uf8b1", SpeechSolidBold = "\uf8b2", ClickedOutLoudSolidBold = "\uf8b3", } ================================================ FILE: client/src/components/ui/input/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/input/index.ts ================================================ export { default as HInput } from "./src/HInput.vue" export * from "./src/interface" ================================================ FILE: client/src/components/ui/input/src/HInput.vue ================================================ ================================================ FILE: client/src/components/ui/input/src/interface.ts ================================================ export interface HInputRef { focus(): void blur(): void } ================================================ FILE: client/src/components/ui/input/styles/index.ts ================================================ export { default as inputLight } from "./light" export type { InputVars, InputTheme } from "./light" ================================================ FILE: client/src/components/ui/input/styles/light.ts ================================================ import type { Theme, CommonTheme } from "@/ui/theme" const self = (common: CommonTheme) => { return { ...common, } } export type InputVars = ReturnType const inputLight: Theme<"Input", InputVars> = { name: "Input", self, } export default inputLight export type InputTheme = typeof inputLight ================================================ FILE: client/src/components/ui/loading/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/loading/index.ts ================================================ export { default as HLoading } from "./src/HLoading.vue" ================================================ FILE: client/src/components/ui/loading/src/HLoading.vue ================================================ ================================================ FILE: client/src/components/ui/loading/styles/index.ts ================================================ export { default as loadingLight } from "./light" export type { LoadingVars, LoadingTheme } from "./light" ================================================ FILE: client/src/components/ui/loading/styles/light.ts ================================================ import type { Theme, CommonTheme } from "@/ui/theme" import { alpha } from "~/utils/color" const self = (common: CommonTheme) => { return { ...common, backgroundColorIcon: alpha(common.colorPrimary, 0.8), background: alpha(common.backgroundColorPrimary, 0.2), } } export type LoadingVars = ReturnType const loadingLight: Theme<"Loading", LoadingVars> = { name: "Loading", self, } export default loadingLight export type LoadingTheme = typeof loadingLight ================================================ FILE: client/src/components/ui/modal/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/modal/index.ts ================================================ export { default as HModal } from "./src/HModal.vue" export { default as HBaseModal } from "./src/HBaseModal.vue" export { default as ModalBackground } from "./src/ModalBackground.vue" ================================================ FILE: client/src/components/ui/modal/src/HBaseModal.vue ================================================ ================================================ FILE: client/src/components/ui/modal/src/HModal.vue ================================================ ================================================ FILE: client/src/components/ui/modal/src/ModalBackground.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/demo/nav-item.demo.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/demo/nav-title.demo.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/components/ui/nav-list/src/HNavItem.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/src/HNavList.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/src/HNavTitle.vue ================================================ ================================================ FILE: client/src/components/ui/nav-list/src/index.ts ================================================ export { default as HNavTitle } from "./HNavTitle.vue" export { default as HNavItem } from "./HNavItem.vue" export { default as HNavList } from "./HNavList.vue" export * from "./interface" ================================================ FILE: client/src/components/ui/nav-list/src/interface.ts ================================================ import { HIconName } from "../../icon" export interface INavItemProps { icon: HIconName text: string indent?: number selected?: boolean color?: string sub?: string | number uppercase?: boolean } export interface INavItem extends INavItemProps { type: "item" key: string } export interface INavTitle { type: "title" label: string } export type NavListItem = INavItem | INavTitle ================================================ FILE: client/src/components/ui/nav-list/styles/dark.ts ================================================ import type { CommonTheme } from "@/ui/theme" import { NavListTheme } from "./light" const self = (common: CommonTheme) => { return { ...common, } } const navListDark: NavListTheme = { name: "NavList", self, } export default navListDark ================================================ FILE: client/src/components/ui/nav-list/styles/index.ts ================================================ export { default as navListLight } from "./light" export { default as navListDark } from "./dark" export type { NavListVars, NavListTheme } from "./light" ================================================ FILE: client/src/components/ui/nav-list/styles/light.ts ================================================ import type { Theme, CommonTheme } from "@/ui/theme" const self = (common: CommonTheme) => { return { ...common, } } export type NavListVars = ReturnType const navListLight: Theme<"NavList", NavListVars> = { name: "NavList", self, } export default navListLight export type NavListTheme = typeof navListLight ================================================ FILE: client/src/components/ui/popover/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/popover/index.ts ================================================ export { default as HPopover } from "./src/HPopover.vue" export * from "./src/interface" ================================================ FILE: client/src/components/ui/popover/src/HPopover.vue ================================================ ================================================ FILE: client/src/components/ui/popover/src/get-placement.ts ================================================ import { IRect, Position } from "./interface" export function getPlacement( position: Position, containerRect: IRect, contentRect: IRect ) { let x = 0, y = 0, margin = { top: 0, left: 0, bottom: 0, right: 0, } switch (position) { case "top-left": x = containerRect.left y = containerRect.top - contentRect.height margin.bottom = 4 break case "top": x = containerRect.left + containerRect.width / 2 - contentRect.width / 2 y = containerRect.top - contentRect.height margin.bottom = 4 break case "top-right": x = containerRect.left + containerRect.width - contentRect.width y = containerRect.top - contentRect.height margin.bottom = 4 break case "right-top": x = containerRect.left + containerRect.width y = containerRect.top margin.left = 4 break case "right": x = containerRect.left + containerRect.width y = containerRect.top + containerRect.height / 2 - contentRect.height / 2 margin.left = 4 break case "right-bottom": x = containerRect.left + containerRect.width y = containerRect.top + containerRect.height - contentRect.height margin.left = 4 break case "bottom-left": x = containerRect.left y = containerRect.top + containerRect.height margin.top = 4 break case "bottom": x = containerRect.left + containerRect.width / 2 - contentRect.width / 2 y = containerRect.top + containerRect.height margin.top = 4 break case "bottom-right": x = containerRect.left + containerRect.width - contentRect.width y = containerRect.top + containerRect.height margin.top = 4 break case "left-top": x = containerRect.left - contentRect.width y = containerRect.top margin.right = 4 break case "left": x = containerRect.left - contentRect.width y = containerRect.top + containerRect.height / 2 - contentRect.height / 2 margin.right = 4 break case "left-bottom": x = containerRect.left - contentRect.width y = containerRect.top + containerRect.height - contentRect.height margin.right = 4 break default: break } return { x, y, margin } } ================================================ FILE: client/src/components/ui/popover/src/interface.ts ================================================ export interface IRect { left: number top: number height: number width: number } export type Position = | "top-left" | "top" | "top-right" | "left-top" | "left" | "left-bottom" | "right-top" | "right" | "right-bottom" | "bottom-left" | "bottom" | "bottom-right" export type TriggerType = "click" | "hover" ================================================ FILE: client/src/components/ui/popover/src/utils.ts ================================================ import { onBeforeUnmount, ref, Ref, unref, watch } from "vue" import { getScrollParent } from "~/utils/parent" import { useOnParentScroll } from "~/utils/scroll" import { IRect } from "./interface" export function useRect(elRef: Ref): Ref { const rect = ref({ left: 0, top: 0, height: 0, width: 0, }) useInit(elRef, rect) useScroll(elRef, rect) useResize(elRef, rect) return rect } function useInit(elRef: Ref, rect: Ref) { watch( () => unref(elRef), (el) => { if (!el) return rect.value = getRect(el) }, { immediate: true } ) } function useScroll(elRef: Ref, rect: Ref) { useOnParentScroll(elRef, () => { if (!elRef.value) return rect.value = getRect(elRef.value) }) } function useResize(elRef: Ref, rect: Ref) { const removeEventListenerFnMap: Map<() => void, () => void> = new Map() const removeAllEventListener = () => { for (const [, fn] of removeEventListenerFnMap) { fn() } removeEventListenerFnMap.clear() } watch( () => unref(elRef.value), (el) => { removeAllEventListener() if (!el) return const resiableNodesSet: Set = new Set() let cursor: Element | Document | null = el while (true) { cursor = getScrollParent(cursor) if (cursor === null) break resiableNodesSet.add(cursor) } for (const node of [...resiableNodesSet, window]) { const onResize = () => { if (!elRef.value) return rect.value = getRect(elRef.value) } node.addEventListener("resize", onResize) removeEventListenerFnMap.set(onResize, () => { node.removeEventListener("resize", onResize) }) } }, { immediate: true, } ) onBeforeUnmount(() => { removeAllEventListener() }) } export function getRect(el: HTMLElement): IRect { const { left, top, height, width } = el.getBoundingClientRect() return { left, top, height, width, } } ================================================ FILE: client/src/components/ui/slide-view/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/slide-view/index.ts ================================================ export { default as HSlideView } from "./src/HSlideView.vue" export * from "./src/interface" ================================================ FILE: client/src/components/ui/slide-view/src/HSlideView.vue ================================================ ================================================ FILE: client/src/components/ui/slide-view/src/interface.ts ================================================ import type { Component } from "vue" export interface ISlideViewItem { component: Component } ================================================ FILE: client/src/components/ui/slider/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/slider/index.ts ================================================ export { default as HSlider } from "./src/HSlider.vue" ================================================ FILE: client/src/components/ui/slider/src/HSlider.vue ================================================ ================================================ FILE: client/src/components/ui/textarea/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/textarea/index.ts ================================================ export { default as HTextarea } from "./src/HTextarea.vue" ================================================ FILE: client/src/components/ui/textarea/src/HTextarea.vue ================================================ ================================================ FILE: client/src/components/ui/theme/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/components/ui/theme/src/common/dark.ts ================================================ import { light } from "~/utils/color" import { base, CommonTheme } from "./light" export const commonDark: CommonTheme = { ...base, isDark: true, backgroundColorPrimary: "#323232", backgroundColorSecondary: "#282828", backgroundColorTertiary: "#1f1f1f", backgroundColorBadge: "#3e3e3e", textColorPrimary: "#cccccc", textColorBack: "#000000", colorPrimaryHover: light(base.colorPrimary, 0.2), colorPrimaryActive: light(base.colorPrimary, 0.4), colorErrorHover: light(base.colorError, 0.2), colorErrorActive: light(base.colorError, 0.4), } ================================================ FILE: client/src/components/ui/theme/src/common/light.ts ================================================ import { light } from "~/utils/color" export const base = { isDark: false, colorPrimary: "#3498db", colorSuccess: "#27ae60", colorWarning: "#f39c12", colorError: "#e74c3c", colorCommon: "#888888", colorWhite: "#ffffff", colorTransparent: "transparent", backgroundColorPrimary: "#ffffff", backgroundColorSecondary: "#f8f8f8", backgroundColorTertiary: "#eeeeee", backgroundColorTransparent: "transparent", backgroundColorSelected: "#77777720", backgroundColorHover: "#77777730", backgroundColorActive: "#77777740", backgroundColorBadge: "#cccccc", backgroundColorPop: "#171717", textColorPrimary: "#484848", textColorSecondary: "#757575", textColorBack: "#ffffff", textColorPop: "#cccccc", textColorWhite: "#ffffff", colorFolder: "#f3c04f", colorAll: "#27ae60", colorPost: "#3883c7", colorPage: "#52bad1", colorDraft: "#f1c40f", } export const commonLight = { ...base, colorPrimaryHover: light(base.colorPrimary, 0.2), colorPrimaryActive: light(base.colorPrimary, 0.4), colorErrorHover: light(base.colorError, 0.2), colorErrorActive: light(base.colorError, 0.4), } export type CommonTheme = typeof commonLight ================================================ FILE: client/src/components/ui/theme/src/index.ts ================================================ export { createThemePlugin, useThemeVars, useTheme, useThemeController, } from "./lib" export * from "./interface" export * from "./common/light" export * from "./themes/light" export * from "./common/dark" export * from "./themes/dark" ================================================ FILE: client/src/components/ui/theme/src/interface.ts ================================================ import type { CommonTheme } from "./common/light" import type { UnkownTheme } from "./themes/unknown" import type { ButtonTheme } from "@/ui/button/styles" import type { InputTheme } from "@/ui/input/styles" import type { LoadingTheme } from "@/ui/loading/styles" import type { NavListTheme } from "@/ui/nav-list/styles" export interface IGlobalTheme { common: CommonTheme unknown: UnkownTheme Button: ButtonTheme Input: InputTheme Loading: LoadingTheme NavList: NavListTheme } export interface Theme { name: N self: (common: CommonTheme) => T } export type GlobalOverrides = Partial<{ [key in keyof IGlobalTheme]: Partial< IGlobalTheme[key] extends Theme ? R : IGlobalTheme[key] > }> ================================================ FILE: client/src/components/ui/theme/src/lib.ts ================================================ import { merge } from "lodash-es" import { App, computed, inject, InjectionKey, onMounted, Ref, ref } from "vue" import { IGlobalTheme } from "./interface" import { lightTheme } from "./themes/light" export const key: InjectionKey = Symbol("key") export interface IThemePlugin { globalThemeRef: Ref setTheme: (theme: IGlobalTheme) => void setGlobal: () => void install(app: App): void } export function createThemePlugin(): IThemePlugin { const globalThemeRef: Ref = ref(lightTheme) const setTheme = (theme: IGlobalTheme) => { globalThemeRef.value = theme setGlobal() } const setGlobal = () => { const theme = globalThemeRef.value document.body.style.backgroundColor = theme.common.backgroundColorTertiary document.body.style.color = theme.common.textColorPrimary } return { globalThemeRef, setTheme, setGlobal, install(app) { const theme = this app.provide(key, theme) app.mixin({ mounted() { setGlobal() }, }) }, } } export function useThemeController() { const { setTheme, setGlobal } = inject(key)! return { setTheme, setGlobal } } export function useTheme>( id: N ) { const { globalThemeRef } = inject(key)! const themeRef = computed(() => { const { common } = globalThemeRef.value return merge( {}, common, globalThemeRef.value[id]?.self(common) as ReturnType< IGlobalTheme[N]["self"] > ) }) return themeRef } export function useThemeVars() { const { globalThemeRef } = inject(key)! const themeVarsRef = computed(() => { const { common } = globalThemeRef.value return { ...common } }) return themeVarsRef } ================================================ FILE: client/src/components/ui/theme/src/themes/dark.ts ================================================ import type { IGlobalTheme } from "../interface" import { commonDark } from "../common/dark" import unkown from "./unknown" import { buttonDark } from "@/ui/button/styles" import { inputLight } from "@/ui/input/styles" import { loadingLight } from "@/ui/loading/styles" import { navListDark } from "@/ui/nav-list/styles" export const darkTheme: IGlobalTheme = { unknown: unkown, common: commonDark, Button: buttonDark, Input: inputLight, Loading: loadingLight, NavList: navListDark, } ================================================ FILE: client/src/components/ui/theme/src/themes/light.ts ================================================ import type { IGlobalTheme } from "../interface" import { commonLight } from "../common/light" import unkown from "./unknown" import { buttonLight } from "@/ui/button/styles" import { inputLight } from "@/ui/input/styles" import { loadingLight } from "@/ui/loading/styles" import { navListLight } from "@/ui/nav-list/styles" export const lightTheme: IGlobalTheme = { unknown: unkown, common: commonLight, Button: buttonLight, Input: inputLight, Loading: loadingLight, NavList: navListLight, } ================================================ FILE: client/src/components/ui/theme/src/themes/unknown.ts ================================================ /** * use to make type in useTheme hook work */ import { Theme } from "../interface" const unkown: Theme<"unknown", unknown> = { name: "unknown", self(c) { return c }, } export type UnkownTheme = typeof unkown export default unkown ================================================ FILE: client/src/components/ui/toggle/demo/default.demo.vue ================================================ ================================================ FILE: client/src/components/ui/toggle/index.ts ================================================ export { default as HToggle } from "./src/HToggle.vue" ================================================ FILE: client/src/components/ui/toggle/src/HToggle.vue ================================================ ================================================ FILE: client/src/components/ui/vertical-center/index.ts ================================================ export { default as HVerticalCenter } from "./src/HVerticalCenter.vue" ================================================ FILE: client/src/components/ui/vertical-center/src/HVerticalCenter.vue ================================================ ================================================ FILE: client/src/components/viewer/HViewerContent.vue ================================================ ================================================ FILE: client/src/components/viewer/HViewerHeader.vue ================================================ ================================================ FILE: client/src/components/viewer/HViewerToolbar.vue ================================================ ================================================ FILE: client/src/composables/cookies.ts ================================================ import { useCookies } from '@vueuse/integrations/useCookies' export const cookies = useCookies(null, { autoUpdateDependencies: true }) ================================================ FILE: client/src/env.d.ts ================================================ /// /// declare module "*.vue" { import { DefineComponent } from "vue" // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types const component: DefineComponent<{}, {}, any> export default component } ================================================ FILE: client/src/errors/index.ts ================================================ export function getErrorId(err:any){ return err?.response?.data?.id } export function getErrorMessage(err: any) { const data = err?.response?.data const id = getErrorId(err) switch (id) { case "PostOrPageNotFoundError": return "没找到,回主页看看?" case "HexoInitError": return "hexo 初始化中,请稍后再试" case "InvalidCreatePathError": return "非法的新文章路径" case "GitSyncScriptError": return "git sync 脚本运行失败。请前往服务器后台使用 `pnpm run script` 修改脚本" case "GitSaveScriptError": return "git save 脚本运行失败。请前往服务器后台使用 `pnpm run script` 修改脚本" case "HexoCleanScriptError": return "hexo clean 脚本运行失败。请前往服务器后台使用 `pnpm run script` 修改脚本" case "HexoDeployScriptError": return "hexo deploy 脚本运行失败。请前往服务器后台使用 `pnpm run script` 修改脚本" case "HexoGenerateScriptError": return "hexo generate 脚本运行失败。请前往服务器后台使用 `pnpm run script` 修改脚本" default: return data.message || err.message } } ================================================ FILE: client/src/ext.d.ts ================================================ import "pinia" import { Router } from "vue-router" import { Dialog } from "./lib/dialog" import { Loading } from "./lib/loading" import { ModalController } from "./lib/modal" import { Notification } from "./lib/notification" declare module "pinia" { export interface PiniaCustomProperties { dialog: Dialog router: Router notification: Notification modal: ModalController loading: Loading } } ================================================ FILE: client/src/interface.ts ================================================ export interface IArticleIdentifier { type: "post" | "page" source: string } export type PostOrPage = "post" | "page" ================================================ FILE: client/src/layouts/default.vue ================================================ ================================================ FILE: client/src/layouts/unauthorized.vue ================================================ ================================================ FILE: client/src/lib/async-queue/index.ts ================================================ export class AsyncQueue { private fns: (() => Promise | void)[] = [] /** * @param time 暂停时间(秒) * @returns */ sleep(time = 0) { this.fns.push(() => { return new Promise((resolve) => { setTimeout(resolve, time * 1000) }) }) return this } exec(fn: () => Promise | void) { this.fns.push(fn) return this } /** * @param interval 间隔时间(秒) * @param count 重复次数 */ interval(fn: () => Promise | void, interval: number, count: number) { this.exec(fn) for (let i = 0; i < count - 1; i++) { this.sleep(interval) this.exec(fn) } return this } async start() { for (const fn of this.fns) { await fn() } } } ================================================ FILE: client/src/lib/dialog/demo/default.demo.vue ================================================ ================================================ FILE: client/src/lib/dialog/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/lib/dialog/src/DialogContainer.vue ================================================ ================================================ FILE: client/src/lib/dialog/src/index.ts ================================================ export { createDialogPlugin, useDialog } from "./lib" export type { Dialog } from "./lib" export { default as DialogContainer } from "./DialogContainer.vue" export * from "./interface" ================================================ FILE: client/src/lib/dialog/src/interface.ts ================================================ export interface IDialogOption { type?: DialogType title: string content?: string persistent?: boolean actions: IDialogActionOption[] } export interface IDialogActionOption { label: string type: DialogActionType /** * 热 * @param item DialogItem */ run?: (item: IDialog) => Promise | void } export type DialogType = "success" | "warning" | "error" | "info" export type DialogActionType = | "success" | "warning" | "error" | "info" | "common" export interface IDialog { id: string type: DialogType title: string content: string persistent: boolean actions: IDialogAction[] close: () => void } export interface IDialogAction { label: string type: DialogActionType run?: () => Promise | void } ================================================ FILE: client/src/lib/dialog/src/lib.ts ================================================ import { App, inject, InjectionKey, provide, Ref, ref } from "vue" import { v4 as uuid } from "uuid" import { DialogType, IDialog, IDialogAction, IDialogOption } from "./interface" import { noop } from "~/utils" const key: InjectionKey = Symbol("hdialog") class DialogItem implements IDialog { public id: string public type: DialogType public title: string public content: string public persistent: boolean public actions: IDialogAction[] public close: () => void constructor(option: IDialogOption, dialogs: Ref) { this.id = uuid() this.type = option.type ?? "info" this.title = option.title this.content = option.content ?? "" this.persistent = option.persistent ?? false this.close = () => (dialogs.value = dialogs.value.filter((item) => item.id !== this.id)) this.actions = option.actions.map((action) => { const run = async () => { this.close() const exec = action.run ?? noop await exec(this) } return { ...action, run } }) } } export interface Dialog { dialogs: Ref create(option: IDialogOption): void install(app: App): void } export function createDialogPlugin(): Dialog { const dialogs = ref([]) function create(option: IDialogOption) { const item = new DialogItem(option, dialogs) dialogs.value.push(item) } return { dialogs, create, install(app: App) { const dialog = this app.provide(key, dialog) }, } } export function useDialog() { return inject(key)! } ================================================ FILE: client/src/lib/http-secure/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/lib/http-secure/src/index.ts ================================================ import axios, { AxiosRequestConfig, AxiosResponse } from "axios" import CryptoJS from "crypto-js" import JSEncrypt from "jsencrypt" interface IHttpSecureOption { onDisable?(): void } interface IRawData { url: string data: any } interface IData { /** * real data passed to axios config.data */ data: any } function stringifyData(data: IData): string { return JSON.stringify(data) } function parseData(data: string): IData { const str = data return JSON.parse(str) } const isHttps = window.location.protocol === "https:" const isLocalhost = window.location.hostname === "localhost" if (!isHttps && !isLocalhost) { console.warn( "`https` not available. Hexon will enable encrypt. Please use https for your safety." ) } function parseConfig(config: AxiosRequestConfig = {}) { config.httpSecureDisabled = isHttps || isLocalhost || config.httpSecureDisabled return config } export default function createHttpSecureAxios( config?: AxiosRequestConfig, option: IHttpSecureOption = {} ) { let id = 1 const rawMap = new Map() const parsedConfig = parseConfig(config) const onDisable = option.onDisable ?? (() => {}) const instance = axios.create(parsedConfig) instance.defaults.httpSecureDisabled = parsedConfig.httpSecureDisabled const storage: { serverPublicKey?: string key: string } = { key: Math.random().toString(), } function decryptAES(data: string) { return CryptoJS.AES.decrypt(data, storage.key).toString(CryptoJS.enc.Utf8) } function encryptAES(data: string) { return CryptoJS.AES.encrypt(data, storage.key).toString() } function encryptRSA(data?: string) { if (!data) return const o = new JSEncrypt() o.setPublicKey(storage.serverPublicKey!) const res = o.encrypt(data) return res } async function fetchPublicKey(config: AxiosRequestConfig) { const res = await axios .get("/publickey", { baseURL: config.baseURL }) .catch(async (err) => { if (err.response) { if ((err.response as AxiosResponse).status === 404) { console.log("server not support, http-secure has been disabled") config.httpSecureDisabled = true await onDisable() return undefined } } throw err }) storage.serverPublicKey = res?.data } const prefix = "/secure/" instance.interceptors.request.use(async (config) => { if (config.httpSecureDisabled) return config //#region save url config.httpSecureId = id rawMap.set(id, { url: config.url || "", data: config.data }) id++ //#endregion //#region get server public key if (!storage.serverPublicKey) { await fetchPublicKey(config) if (!storage.serverPublicKey) return config } //#endregion //#region encrypt const key = storage.key const url = config.url config.url = prefix + encodeURIComponent(encryptRSA(JSON.stringify({ key, url })) ?? "") const content = encryptAES(stringifyData({ data: config.data })) config.data = { content } //#endregion return config }) instance.interceptors.response.use( (res) => { if (res.config.httpSecureDisabled) return res //#region restore url if (res.config.httpSecureId !== void 0) { const { url = "", data = {} } = rawMap.get(res.config.httpSecureId) || {} res.config.url = url res.config.data = data rawMap.delete(res.config.httpSecureId) } //#endregion //#region decrypt if (res.data.content) { const { content } = res.data as { content: string } res.data = parseData(decryptAES(content)).data } //#endregion //#region log because devtools network won't work console.groupCollapsed( `[${res.status}][${res.config.method?.toUpperCase()}]${res.config.url}` ) console.log("%c[config] %O", "font-weight:bolder", res.config) console.log("%c[request] %O", "font-weight:bolder", res.request) console.log("%c[data] %O", "font-weight:bolder", res.data) console.log("%c[headers] %O", "font-weight:bolder", res.headers) console.groupEnd() //#endregion return res }, async (err) => { if (err.response && !err.response.config.httpSecureDisabled) { const res = err.response as AxiosResponse //#region restore url if (res.config.httpSecureId !== void 0) { const { url = "", data = {} } = rawMap.get(res.config.httpSecureId) || {} res.config.url = url res.config.data = data rawMap.delete(res.config.httpSecureId) } //#endregion //#region invalid publicKey if (res.status === 403 && res.data.code === "EHTTPSECURE") { await fetchPublicKey(res.config) return instance(res.config) } //#endregion //#region decrypt if (res.data.content) { const { content } = res.data as { content: string } res.data = parseData(decryptAES(content)).data } //#endregion //#region log because devtools network won't work const style = "color: red" console.groupCollapsed( `%c[${res.status}][${res.config.method?.toUpperCase()}]${ res.config.url }`, style ) console.log("%c[config] %O", "font-weight:bolder", res.config) console.log("%c[request] %O", "font-weight:bolder", res.request) console.log("%c[data] %O", "font-weight:bolder", res.data) console.log("%c[headers] %O", "font-weight:bolder", res.headers) console.groupEnd() //#endregion } throw err } ) return instance } declare module "axios" { interface AxiosRequestConfig { /** * disable http security */ httpSecureDisabled?: boolean httpSecureId?: number } } ================================================ FILE: client/src/lib/list2tree/index.spec.ts ================================================ import { list2Tree } from "." it("should transform list to tree", () => { const list = [ { id: "1" }, { id: "2" }, { id: "3", p: "1" }, { id: "4", p: "1" }, { id: "5", p: "4" }, ] const correct = [ { id: "1", c: [ { id: "3", p: "1" }, { id: "4", p: "1", c: [{ id: "5", p: "4" }] }, ], }, { id: "2" }, ] const res = list2Tree(list, (item) => !item.p, { idKey: "id", parentKey: "p", childrenKey: "c", }) expect(res).toEqual(correct) }) it("should work if no top node found", () => { const list = [ { id: "3", p: "1" }, { id: "4", p: "1" }, ] const run = () => { list2Tree(list, (item) => !item.p, { idKey: "id", parentKey: "p", childrenKey: "c", }) } expect(run).not.toThrow() }) ================================================ FILE: client/src/lib/list2tree/index.ts ================================================ export type TreeNode = T & { [key in C]?: TreeNode[] } export function list2Tree< T extends { [key in T1]: string } & { [key in T2]?: string }, T1 extends string, T2 extends string, T3 extends string >( list: T[], isParentFn: (item: T) => boolean, config: { idKey: T1 parentKey: T2 childrenKey: T3 } ): TreeNode[] { const { idKey, parentKey, childrenKey } = config const grouped = groupBy(list, parentKey) function findChildren(item: T): TreeNode { const o = (grouped[item[idKey]] || []).map(findChildren) return o.length ? { ...item, [childrenKey]: o } : (item as TreeNode) } const trees: TreeNode[] = list.filter(isParentFn).map(findChildren) return trees } function groupBy( list: T1[], parentKey: T2 ) { const o: { [parentKey: string]: T1[] } = {} list.map((item) => { const pKey = item[parentKey] if (!o[pKey]) o[pKey] = [item] else o[pKey].push(item) }) return o } ================================================ FILE: client/src/lib/loading/index.ts ================================================ import { App, computed, ComputedRef, inject, InjectionKey, ref } from "vue" const key: InjectionKey = Symbol("loading") export interface Loading { loading: ComputedRef start(): void stop(): void install(app: App): void } export function createLoadingPlugin(): Loading { let token: NodeJS.Timeout | undefined const loading = ref(false) return { loading: computed(() => loading.value), start() { token = setTimeout(() => { loading.value = true }, 500) }, stop() { if (token) { clearTimeout(token) token = undefined } loading.value = false }, install(app: App) { const loading = this app.provide(key, loading) }, } } export function useLoading() { return inject(key)! } ================================================ FILE: client/src/lib/modal/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/lib/modal/src/ModalContainer.vue ================================================ ================================================ FILE: client/src/lib/modal/src/index.ts ================================================ import { App, Component, inject, InjectionKey, markRaw, Ref, ref } from "vue" import { v4 as uuid } from "uuid" export { default as ModalContainer } from "./ModalContainer.vue" const key: InjectionKey = Symbol("modal-controller") interface IModalItem { id: string component: Component close: () => void } export interface ModalController { modals: Ref create(component: Component): IModalItem install(app: App): void } export function createModalPlugin(): ModalController { const modals = ref([]) function create(component: Component) { const id = uuid() const item = { id, component: markRaw(component), close: () => { modals.value = modals.value.filter((item) => item.id !== id) }, } modals.value.push(item) return item } return { modals, create, install(app: App) { const controller = this app.provide(key, controller) }, } } export function useModal() { return inject(key)! } ================================================ FILE: client/src/lib/notification/index.ts ================================================ export * from "./src" ================================================ FILE: client/src/lib/notification/src/FadeTransitionGroup.vue ================================================ ================================================ FILE: client/src/lib/notification/src/Notification.vue ================================================ ================================================ FILE: client/src/lib/notification/src/index.ts ================================================ export { useNotification, createNotification } from "./lib" export type { Notification } from "./lib" export { default as Notifications } from "./Notification.vue" export * from "./interface" ================================================ FILE: client/src/lib/notification/src/interface.ts ================================================ import { ComputedRef } from "vue" export type INotificationType = "success" | "warning" | "error" | "info" export type INotificationPosition = | "top-left" | "top" | "top-right" | "bottom-left" | "bottom" | "bottom-right" export const DEFAULT_NOTIFY_TYPE: INotificationType = "info" export const DEFAULT_NOTIFICATION_POSITION: INotificationPosition = "bottom-right" export const DEFAULT_NOTIFICATION_DURATION: number = 3000 export interface INotificationItem { id: string title: string type: INotificationType desc: string duration: number permanent: boolean close: () => void onClick?: () => void show: boolean token?: NodeJS.Timeout createdAt: Date actions: { label: string run: () => void }[] } export interface INotificationOptions { title: string type?: INotificationType /** * extra description */ desc?: string duration?: number permanent?: boolean onClick?: () => void actions?: { label: string run: () => void }[] } export interface INotification { notifications: ComputedRef<{ [key: string]: INotificationItem }> /** * later-top list */ notificationList: ComputedRef defaults: ComputedRef position: ComputedRef /** * create a notification * @returns */ notify: (options: INotificationOptions) => string /** * close a notification by id */ close: (id: string) => void /** * close all notification */ closeAll: () => void /** * change notification plugin default settings */ setDefaults: (options: Partial) => void /** * change notification plugin position */ setPosition: (position: INotificationPosition) => void } export interface INotificationDefaults { type: INotificationType duration: number } ================================================ FILE: client/src/lib/notification/src/lib.spec.ts ================================================ import { createNotification } from "./lib" describe("notification", () => { beforeEach(() => { jest.useFakeTimers() }) afterEach(() => { jest.runOnlyPendingTimers() jest.useRealTimers() }) it("notify", () => { const notification = createNotification() const id = notification.notify({ title: "test notify" }) expect(notification.notifications.value[id].title).toBe("test notify") expect(notification.notifications.value[id].show).toBe(true) }) it("notify autoclose", () => { const notification = createNotification() const id = notification.notify({ title: "test notify" }) jest.advanceTimersByTime(notification.notifications.value[id].duration) expect(notification.notifications.value[id].token).toBe(undefined) expect(notification.notifications.value[id].show).toBe(false) }) it("close", () => { const notification = createNotification() const id = notification.notify({ title: "test notify" }) notification.close(id) expect(notification.notifications.value[id].token).toBe(undefined) expect(notification.notifications.value[id].show).toBe(false) }) it("close later", async () => { const notification = createNotification() const id = notification.notify({ title: "test notify" }) jest.advanceTimersByTime(notification.notifications.value[id].duration / 2) notification.close(id) expect(notification.notifications.value[id].token).toBe(undefined) expect(notification.notifications.value[id].show).toBe(false) }) it("close a closed item", async () => { const notification = createNotification() const id = notification.notify({ title: "test notify" }) jest.advanceTimersByTime( notification.notifications.value[id].duration + 1000 ) notification.close(id) expect(notification.notifications.value[id].token).toBe(undefined) expect(notification.notifications.value[id].show).toBe(false) }) it("close all", () => { const notification = createNotification() notification.notify({ title: "test notify1" }) notification.notify({ title: "test notify2" }) notification.notify({ title: "test notify3" }) notification.closeAll() jest.advanceTimersByTime(notification.notificationList.value[0].duration) expect( notification.notificationList.value.filter( (item) => item.show || item.token ) ).toEqual([]) }) it("sort by createdAt", () => { const notification = createNotification() const id1 = notification.notify({ title: "test notify1" }) jest.advanceTimersByTime(10) const id2 = notification.notify({ title: "test notify2" }) jest.advanceTimersByTime(10) const id3 = notification.notify({ title: "test notify3" }) const list = notification.notificationList.value expect(list).toEqual([ notification.notifications.value[id1], notification.notifications.value[id2], notification.notifications.value[id3], ]) }) it("set defaults", () => { const notification = createNotification() notification.setDefaults({ type: "success", duration: 5000 }) expect(notification.defaults.value.type).toBe("success") expect(notification.defaults.value.duration).toBe(5000) }) }) ================================================ FILE: client/src/lib/notification/src/lib.ts ================================================ import { App, computed, InjectionKey, ref, inject } from "vue" import { v4 as uuid } from "uuid" import { noop, object2list } from "~/utils" import { DEFAULT_NOTIFICATION_DURATION, DEFAULT_NOTIFICATION_POSITION, DEFAULT_NOTIFY_TYPE, INotification, INotificationDefaults, INotificationItem, INotificationOptions, INotificationPosition, } from "./interface" //#region plugin api const notificationProviderInjectionKey: InjectionKey = Symbol("notification") export const useNotification = () => { const notification = inject(notificationProviderInjectionKey) if (!notification) { console.warn(`Did you forget to load notification plugin?`) } return notification! } export interface Notification extends INotification { install(app: App): void } export function createNotification(): Notification { const _notifications = ref<{ [key: string]: INotificationItem }>({}) const _position = ref(DEFAULT_NOTIFICATION_POSITION) const _defaults = ref({ type: DEFAULT_NOTIFY_TYPE, duration: DEFAULT_NOTIFICATION_DURATION, }) const defaults = computed(() => _defaults.value) const position = computed(() => _position.value) const notifications = computed(() => _notifications.value) const notificationList = computed(() => object2list(_notifications.value, "id").sort( (a, b) => a.createdAt.getTime() - b.createdAt.getTime() ) ) function _createNotificationItem( options: INotificationOptions ): INotificationItem { const id = uuid() const _close = () => close(id) const { title, type = _defaults.value.type, desc = "", duration = _defaults.value.duration, permanent = false, onClick, actions = [], } = options const show = true const createdAt = new Date() return { id, title, type, desc, duration, permanent: !actions.length ? permanent : true, onClick: onClick !== void 0 ? () => { _close() onClick() } : void 0, show, createdAt, close: _close, actions: actions.map((item) => { const run = () => { _close() item.run() } return { ...item, run } }), } } function notify(options: INotificationOptions) { // create item const item = _createNotificationItem(options) // append to store _notifications.value[item.id] = item if (!item.permanent) { // set timeout item.token = setTimeout(() => { _notifications.value[item.id].show = false delete _notifications.value[item.id].token }, item.duration) } return item.id } function close(id: string) { const item = _notifications.value[id] if (item) { if (item.token) clearTimeout(item.token) _notifications.value[id].show = false delete _notifications.value[id].token } } function closeAll() { notificationList.value.forEach((item) => close(item.id)) } function setDefaults(options: Partial = {}) { _defaults.value = { ..._defaults.value, ...options } } function setPosition(position: INotificationPosition) { _position.value = position } return { notifications, notificationList, defaults, position, notify, close, closeAll, setDefaults, setPosition, install(app: App) { const notification = this app.provide(notificationProviderInjectionKey, notification) }, } } //#endregion ================================================ FILE: client/src/lib/splitview/SplitView.vue ================================================ ================================================ FILE: client/src/lib/splitview/index.ts ================================================ export { default as default } from "./SplitView.vue" ================================================ FILE: client/src/lib/splitview/useEventListener.ts ================================================ import { watchEffect } from "vue" const useEventListener = ( ele: HTMLElement | Window, type: keyof HTMLElementEventMap, listener: (evt: Event) => void ) => { watchEffect((onInvalidate) => { ele.addEventListener(type, listener) onInvalidate(() => { ele.removeEventListener(type, listener) }) }) } export default useEventListener ================================================ FILE: client/src/lib/splitview/useResizeObserver.ts ================================================ import { Ref, watchEffect } from "vue" const useResizeObserver = ( ele: Ref, cb: (entry: ResizeObserverEntry) => void ) => { watchEffect((onInvalidate) => { const ro = new ResizeObserver((entries) => { for (let entry of entries) { cb(entry) } }) if (ele.value) { ro.observe(ele.value) } onInvalidate(() => { ro.disconnect() }) }) } export default useResizeObserver ================================================ FILE: client/src/main.ts ================================================ import { createApp } from "vue" import App from "./App.vue" import dialog from "./plugins/dialog" import loading from "./plugins/loading" import modal from "./plugins/modal" import notification from "./plugins/notification" import pinia from "./plugins/pinia" import router from "./plugins/router" import theme from "./plugins/theme" import themes from "./themes" import "./plugins/dayjs" import "@unocss/reset/tailwind.css" import "uno.css" createApp(App) .use(loading) .use(pinia) .use(router) .use(themes) .use(theme) .use(modal) .use(notification) .use(dialog) .mount("#app") ================================================ FILE: client/src/pages/edit/[type]/[source].vue ================================================ { name: "edit", } ================================================ FILE: client/src/pages/index/view/[type]/[source].vue ================================================ { name: "view", } ================================================ FILE: client/src/pages/index.vue ================================================ { name: "home", } ================================================ FILE: client/src/pages/signin.vue ================================================ meta: name: signin layout: unauthorized ================================================ FILE: client/src/plugins/dayjs.ts ================================================ import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" import localizedFormat from "dayjs/plugin/localizedFormat" import "dayjs/locale/zh-cn" dayjs.extend(relativeTime) dayjs.extend(localizedFormat) dayjs.locale("zh-cn") ================================================ FILE: client/src/plugins/dialog.ts ================================================ import { createDialogPlugin } from "~/lib/dialog" export default createDialogPlugin() ================================================ FILE: client/src/plugins/loading.ts ================================================ import { createLoadingPlugin } from "~/lib/loading" export default createLoadingPlugin() ================================================ FILE: client/src/plugins/modal.ts ================================================ import { createModalPlugin } from "~/lib/modal/src" export default createModalPlugin() ================================================ FILE: client/src/plugins/notification.ts ================================================ import { createNotification } from "../lib/notification" export default createNotification() ================================================ FILE: client/src/plugins/pinia.ts ================================================ import { createPinia } from "pinia" import router from "./router" import dialog from "./dialog" import notification from "./notification" import modal from "./modal" import loading from "./loading" const pinia = createPinia() pinia.use(() => ({ loading, dialog, router, notification, modal, })) export default pinia ================================================ FILE: client/src/plugins/router.ts ================================================ import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router" import { isLogin } from "~/api/auth" import { setupLayouts } from "virtual:generated-layouts" import generatedRoutes from "virtual:generated-pages" const routes = setupLayouts(generatedRoutes) const path = { home: "/", signin: "/signin", } const router = createRouter({ history: createWebHashHistory(), routes }) router.beforeEach(async (to, from) => { if (isLogin() && to.path === path.signin) return path.home if (!isLogin() && to.path !== path.signin) return path.signin }) export default router ================================================ FILE: client/src/plugins/theme.ts ================================================ import { createThemePlugin } from "@/ui/theme" export default createThemePlugin() ================================================ FILE: client/src/store/actions.ts ================================================ import { defineStore } from "pinia" import { api } from "~/api" import { getErrorMessage } from "~/errors" import notification from "~/plugins/notification" import { useDispatcher } from "./dispatcher" function success() { notification.notify({ type: "success", title: "成功", }) } function fail(e: unknown) { const err = e instanceof Error ? e : new Error(e as any) console.log(getErrorMessage(err)) const id = notification.notify({ title: "失败", desc: getErrorMessage(err), type: "error", actions: [ { label: "好的", run() { notification.close(id) }, }, ], }) return err } export const useActionsStore = defineStore("actions", { actions: { async deploy() { this.loading.start() await api.deploy().then(success, fail) this.loading.stop() }, async generate() { this.loading.start() await api.generate().then(success, fail) this.loading.stop() }, async clean() { this.loading.start() await api.clean().then(success, fail) this.loading.stop() }, async gitSync() { this.loading.start() await api.gitSync().then(() => { success() const dispatcher = useDispatcher() dispatcher.loadBlogData() }, fail) this.loading.stop() }, async gitSave() { this.loading.start() await api.gitSave().then(() => { success() const dispatcher = useDispatcher() dispatcher.loadBlogData() }, fail) this.loading.stop() }, }, }) ================================================ FILE: client/src/store/articleList.ts ================================================ import { defineStore } from "pinia" import { BriefPage, BriefPost } from "~/api" import { isDraft, isPage, isPost } from "~/utils/article" type AllFilter = { type: "all" } type PostFilter = { type: "post" } type pageFilter = { type: "page" } type DraftFilter = { type: "draft" } type CategorieFilter = { type: "category" slug: string } type TagFilter = { type: "tag" slug: string } type Filter = | AllFilter | PostFilter | pageFilter | DraftFilter | CategorieFilter | TagFilter interface IState { filter: Filter } export const useArticleListStore = defineStore("article-list", { state: (): IState => ({ filter: { type: "all" } }), actions: { setFilter(filter: Filter) { this.filter = filter }, }, getters: { articleFilter(state) { const filter = state.filter return (articles: (BriefPage | BriefPost)[]) => { switch (filter.type) { case "all": return articles case "page": return articles.filter(isPage) case "post": return articles.filter(isPost) case "draft": return articles.filter(isDraft) case "category": return (articles.filter(isPost) as BriefPost[]).filter((post) => post.categories?.includes(filter.slug) ) case "tag": return (articles.filter(isPost) as BriefPost[]).filter((post) => post.tags?.includes(filter.slug) ) default: return [] as never[] } } }, }, }) ================================================ FILE: client/src/store/detail.ts ================================================ import { defineStore } from "pinia" import { Page, Post, api } from "~/api" import { PostOrPage } from "~/interface" import { isDraft, isPage, isPost } from "~/utils/article" interface IState { article: Post | Page | null error: boolean _loading: boolean saving: boolean } export const useDetailStore = defineStore("detail", { state: (): IState => ({ article: null, error: false, _loading: false, saving: false, }), getters: { isLoading(): boolean { return (this._loading || !this.article) && !this.error }, isDraft(): boolean { return isDraft(this.article) }, isPost(): boolean { return isPost(this.article) }, isPage(): boolean { return isPage(this.article) }, }, actions: { async getArticle(type: PostOrPage, source: string) { const token = setTimeout(() => { this._loading = true }, 500) this.error = false try { const res = await api.getArticle(type, source) this.article = res } catch (err) { console.error(err) this.error = true throw err } finally { clearTimeout(token) this._loading = false } }, async saveArticle(raw: string) { if (!this.article) return this.saving = true try { await api.saveArticle( isPost(this.article) ? "post" : "page", this.article.source, raw ) } catch (err) { console.error(err) throw err } finally { this.saving = false } }, clearArticle() { this.$reset() }, }, }) ================================================ FILE: client/src/store/dispatcher.ts ================================================ import { defineStore } from "pinia" import { defineAsyncComponent } from "vue" import { ICreateOptions } from "~/api" import { changePassword, getInfo, login, changeUsername } from "~/api/auth" import { IChangePasswordFormPayload } from "~/components/forms/interface" import { getErrorId, getErrorMessage } from "~/errors" import { IArticleIdentifier } from "~/interface" import { isPost } from "~/utils/article" import { useDetailStore } from "./detail" import { useMainStore } from "./main" import { useSettingsStore } from "./settings" const HCreateArticleModal = defineAsyncComponent( () => import("@/modals/HCreateArticleModal.vue") ) const HSettingsModal = defineAsyncComponent( () => import("@/modals/HSettingsModal.vue") ) export const useDispatcher = defineStore("dispatcher", { state: () => ({}), actions: { init() { const mainStore = useMainStore() mainStore.loadUsername() }, //#region user async getInfo() { return Promise.all([this.getUsername(), this.getSettings()]) }, async getSettings() { const settingsStore = useSettingsStore() await settingsStore.load() }, async getUsername() { const mainStore = useMainStore() const { username } = await getInfo() mainStore.setUsername(username) }, async signIn({ username, password, }: { username: string password: string }) { try { await login(username, password) this.getInfo() this.router.push({ name: "home" }) } catch (e: any) { this.notification.notify({ title: "登陆失败", type: "error", desc: e?.response?.data?.message, }) } }, async changePassword(payload: IChangePasswordFormPayload) { return changePassword(payload.oldPassword, payload.newPassword).then( () => { this.notification.notify({ type: "success", title: "密码修改成功" }) }, (err) => { this.notification.notify({ title: "密码修改失败", desc: (err as Error).message, type: "error", duration: 5000, }) } ) }, async changeUsername(username: string) { return changeUsername(username).then( () => { this.notification.notify({ type: "success", title: "用户名修改成功" }) return this.getUsername() }, (err) => { this.notification.notify({ title: "用户名修改失败", desc: (err as Error).message, type: "error", duration: 5000, }) } ) }, //#endregion //#region modals showCreateArticleModal() { this.modal.create(HCreateArticleModal) }, showSettingsModal() { this.modal.create(HSettingsModal) }, //#endregion async createArticle(title: string, options: ICreateOptions) { const mainStore = useMainStore() this.loading.start() try { await mainStore.createArticle(title, options).then( () => { this.notification.notify({ type: "success", title: "新建成功", }) }, (err) => { this.notification.notify({ title: "新建失败", desc: (err as Error).message, type: "error", duration: 5000, }) } ) } catch (err) { } finally { this.loading.stop() } }, deleteArticle(id: IArticleIdentifier) { const mainStore = useMainStore() this.dialog.create({ type: "warning", title: "删除确认", content: "删除后需手动恢复", actions: [ { type: "common", label: "取消" }, { type: "error", label: "删除", run: () => { mainStore.deleteArticle(id.type, id.source).then(() => { this.notification.notify({ type: "success", title: "删除成功", }) // FIXME 不是每次都要跳转 this.router.push({ name: "home" }) }) }, }, ], }) }, async saveArticle(raw: string) { this.loading.start() try { const detailStore = useDetailStore() await detailStore.saveArticle(raw).then( () => { this.notification.notify({ title: "保存成功", type: "success", }) this.reloadBlogData() }, (err) => { this.notification.notify({ title: "文章保存失败", desc: (err as Error).message, type: "error", duration: 5000, }) throw err } ) } catch (err) { } finally { this.loading.stop() } }, editArticle(id: IArticleIdentifier) { this.router.push({ name: "edit", params: { ...id } }) }, viewArticle(id: IArticleIdentifier) { this.router.push({ name: "view", params: { ...id } }) }, getArticle(id: IArticleIdentifier) { const detailStore = useDetailStore() detailStore.getArticle(id.type, id.source).catch((err) => { if (getErrorId(err) === "PostOrPageNotFoundError") { this.goHome() } else { this.notification.notify({ title: "文章载入失败", desc: getErrorMessage(err), type: "error", duration: 5000, }) } }) }, clearArticle() { const detailStore = useDetailStore() detailStore.clearArticle() }, async publishArticle(source: string) { this.dialog.create({ type: "warning", title: "发布确认", content: "发布后需手动恢复", actions: [ { type: "common", label: "取消" }, { type: "info", label: "发布", run: () => { this.doPublishArticle(source) }, }, ], }) }, async doPublishArticle(source: string) { const prefix = "_drafts/" if (!source.startsWith(prefix)) return this.loading.start() try { const removePrefixAndExt = (source: string) => { return source.slice(prefix.length, -3) } const mainStore = useMainStore() await mainStore.publishArticle(removePrefixAndExt(source)).then( (article) => { this.notification.notify({ title: "发布成功", type: "success", }) const detailStore = useDetailStore() if ( detailStore.article && isPost(detailStore.article) && detailStore.article.source === source ) { this.router.push({ name: "view", params: { source: article.source }, }) } }, (err) => { this.notification.notify({ title: "文章发布失败", desc: (err as Error).message, type: "error", duration: 5000, }) } ) } catch (err) { } finally { this.loading.stop() } }, goHome() { this.router.push({ name: "home" }) }, reloadBlogData() { const mainStore = useMainStore() mainStore.getBlogData().catch((err) => { this.notification.notify({ title: `博客数据刷新失败`, desc: (err as Error).message, type: "error", actions: [ { label: "重试", run: () => { this.reloadBlogData() }, }, ], }) }) }, loadBlogData() { const mainStore = useMainStore() mainStore.getBlogData().catch((err) => { this.notification.notify({ title: `博客数据载入失败`, desc: (err as Error).message, type: "error", duration: 5000, }) }) }, }, }) ================================================ FILE: client/src/store/main.ts ================================================ import { defineStore } from "pinia" import { list2Tree, TreeNode } from "~/lib/list2tree" import { api, BriefPage, BriefPost, Category, ICreateOptions, Tag } from "~/api" import { list2object, object2list } from "~/utils" import { PostOrPage } from "~/interface" import { useDispatcher } from "./dispatcher" export interface IState { username: string posts: { [key: string]: BriefPost } pages: { [key: string]: BriefPage } categories: { [key: string]: Category } tags: { [key: string]: Tag } } export const useMainStore = defineStore("main", { state: (): IState => ({ username: "", posts: {}, pages: {}, categories: {}, tags: {}, }), actions: { setUsername(name: string) { this.username = name localStorage.setItem("username", name) }, loadUsername() { this.username = localStorage.getItem("username") || "" }, async getBlogData() { const { posts, pages, tags, categories } = await api.getAllData() this.posts = list2object(posts, "source") this.pages = list2object(pages, "source") this.tags = list2object(tags, "slug") this.categories = list2object(categories, "slug") }, async deleteArticle(type: PostOrPage, source: string) { const { posts, pages, tags, categories } = await api.deleteArticle( type, source ) this.posts = list2object(posts, "source") this.pages = list2object(pages, "source") this.tags = list2object(tags, "slug") this.categories = list2object(categories, "slug") }, async createArticle(title: string, options: ICreateOptions = {}) { const data = await api.createArticle(title, options) const dispatcher = useDispatcher() if ("post" in data) { dispatcher.editArticle({ type: "post", source: data.post.source }) } else { dispatcher.editArticle({ type: "page", source: data.page.source }) } }, async publishArticle(filename: string) { const article = await api.publishArticle(filename) await this.getBlogData() return article }, }, getters: { articles(state): (BriefPost | BriefPage)[] { return object2list( state.pages, "source" ).concat( object2list(state.posts, "source") ) }, allPostsList(state): BriefPost[] { return object2list(state.posts, "source") }, publishedPostsList(state): BriefPost[] { return this.allPostsList.filter((post) => post.published) }, draftsList(state): BriefPost[] { return this.allPostsList.filter((post) => !post.published) }, pagesList(state): BriefPage[] { return object2list(state.pages, "source") }, categoriesTree(): TreeNode[] { return list2Tree(this.categoriesList, (item) => !item.parent, { idKey: "id", parentKey: "parent", childrenKey: "children", }) }, categoriesList(): Category[] { return object2list(this.categories, "slug") }, tagNamesList(): string[] { return object2list(this.tags, "slug").map((item) => item.name) }, catNamesList(): string[] { return this.categoriesList.map((cat) => cat.name) }, }, }) ================================================ FILE: client/src/store/settings.ts ================================================ import { ISettings } from "@server/server/types/api" import { merge } from "lodash-es" import { defineStore } from "pinia" import { getSettings, setSettings } from "~/api/settings" const DEFAULT: ISettings = { ui: { editor: { fontFamily: "PingFang SC,-apple-system,SF UI Text,Lucida Grande,STheiti,Microsoft YaHei,sans-serif", }, }, } function withDefault(settings: Partial = {}) { return merge({}, DEFAULT, settings) } export const useSettingsStore = defineStore("settings", { state: (): { settings: ISettings } => ({ settings: withDefault() }), actions: { async load() { const part = await getSettings() this.settings = withDefault(part) }, async save() { await setSettings(this.settings) }, }, }) ================================================ FILE: client/src/styles/font.less ================================================ @font-face { font-family: "Segoe Fluent Icons"; src: url("../assets/segoe-fluent-icons.ttf"); } ================================================ FILE: client/src/styles/index.less ================================================ @import "./font.less"; body { -webkit-font-smoothing: antialiased; } .modal-panel { width: 960px; max-width: calc(100vw - 100px) !important; height: 640px; max-height: calc(100vh - 100px) !important; } ================================================ FILE: client/src/styles/mixins.less ================================================ .ellipsis(@count) { display: -webkit-box; -webkit-line-clamp: @count; -webkit-box-orient: vertical; overflow: hidden; } ================================================ FILE: client/src/themes.ts ================================================ import { hex, rgb } from "color-convert" import { createTheme } from "@winwin/vue-global-theming" type modifier = "l" | "a" | "d" type amount = 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 type ModifiedColor = { [key in `${modifier}${amount}`]: string } type modifyFn = (name: string, amt: number) => string type modifierFnMap = { [key in modifier]: modifyFn } const modifiers: modifierFnMap = { l: (name, amt) => { // 变白 const [r, g, b] = hex.rgb(name) const go = (c: number) => c + Math.abs(255 - c) * amt return `#${rgb.hex([go(r), go(g), go(b)])}` }, d: (name, amt) => { // 变黑 const [r, g, b] = hex.rgb(name) const go = (c: number) => c * (1 - amt) return `#${rgb.hex([go(r), go(g), go(b)])}` }, a: (name, amt) => { // 变透明 const alpha = Math.round((1 - amt) * 255).toString(16) return `${name}${alpha}` }, } function createColor(name: string): ColorPack { const indexs = new Array(9).fill(0).map((v, i) => (i + 1) / 10) function createModifiedColor(type: modifier) { return indexs .map((amt) => modifiers[type](name, amt)) .reduce((o, v, idx) => { o[`${type}${(idx + 1) as amount}`] = v return o }, {} as ModifiedColor) } return { n: name, ...createModifiedColor("a"), ...createModifiedColor("d"), ...createModifiedColor("l"), } } type ColorPack = ModifiedColor & { n: string } export type HTheme = { color: { primary: ColorPack success: ColorPack warning: ColorPack error: ColorPack common: ColorPack folder: string all: string post: string page: string draft: string black: string white: string background: { transparent: string hover: string active: string selected: string /** * 右侧栏 */ base1: string /** * 中间栏 */ base2: string /** * 左侧栏 */ base3: string badge: string /** * 亮主题的白 */ max: string /** * 亮主题的黑 */ min: string secondInput: string } foreground: { transparent: string main: string sub: string /** * 亮主题的黑 */ max: string /** * 亮主题的白 */ min: string } } } export const lightTheme: HTheme = { color: { primary: createColor("#3498db"), success: createColor("#27ae60"), warning: createColor("#f39c12"), error: createColor("#e74c3c"), common: createColor("#888888"), folder: "#f3c04f", all: "#27ae60", post: "#3883c7", page: "#52bad1", draft: "#f1c40f", black: "#000000", white: "#ffffff", background: { transparent: "transparent", hover: "#00000015", active: "#00000020", selected: "#00000010", base1: "#ffffff", base2: "#f8f8f8", base3: "#eeeeee", badge: "#cccccc", max: "#ffffff", min: "#000000", secondInput: "#eeeeee", }, foreground: { transparent: "transparent", main: "#484848", sub: "#757575", max: "#000000", min: "#ffffff", }, }, } export const darkTheme: HTheme = { color: { primary: createColor("#3498db"), success: createColor("#27ae60"), warning: createColor("#f39c12"), error: createColor("#e74c3c"), common: createColor("#888888"), folder: "#f3c04f", all: "#27ae60", post: "#3883c7", page: "#52bad1", draft: "#f1c40f", black: "#000000", white: "#ffffff", background: { transparent: "transparent", hover: "#ffffff15", active: "#ffffff20", selected: "#ffffff10", base1: "#323232", base2: "#282828", base3: "#1f1f1f", badge: "#3e3e3e", max: "#000000", min: "#ffffff", secondInput: "#424242", }, foreground: { transparent: "transparent", main: "#dddddd", sub: "#a9a9a9", max: "#ffffff", min: "#000000", }, }, } export default createTheme({ default: lightTheme, dark: darkTheme }) ================================================ FILE: client/src/utils/article.ts ================================================ import { Dayjs } from "dayjs" import { IPage, IPost, Page, Post } from "~/api" import { dateFromString } from "./date" import { parseHfm } from "./hfm" export function isDraft(article: IPage | IPost | null): boolean { if (article && "published" in article && !article.published) return true else return false } export function isPost(article: IPage | IPost | null): boolean { if (article && "__post" in article) return true else return false } export function isPage(article: IPage | IPost | null): boolean { if (article && "__page" in article) return true else return false } export interface IParsedArticleMeta { type: "post" | "page" isDraft: boolean title: string tags: string[] categories: string[] date: Dayjs | null updated: Dayjs | null source: string fm: { [key: string]: unknown } } export function parseArticleData( article: Page | Post | null ): IParsedArticleMeta { if (!article) return { type: "post" as const, isDraft: false, title: "", tags: [], categories: [], date: null, updated: null, source: "", fm: {}, } const { tags, categories, fm } = parseHfm(article.raw) return { type: isPage(article) ? "page" : "post", isDraft: isDraft(article), title: article.title, tags: isPage(article) ? [] : tags, categories: isPage(article) ? [] : categories, date: dateFromString(article.date), updated: dateFromString(article.updated), source: article.source, fm, } } ================================================ FILE: client/src/utils/color.ts ================================================ import { rgb, hex } from "color-convert" export function light(name: string, amt: number) { const [r, g, b] = hex.rgb(name) const go = (c: number) => c + Math.abs(255 - c) * amt return `#${rgb.hex([go(r), go(g), go(b)])}` } export function dark(name: string, amt: number) { const [r, g, b] = hex.rgb(name) const go = (c: number) => c - c * amt return `#${rgb.hex([go(r), go(g), go(b)])}` } export function alpha(name: string, amt: number) { // 变透明 const alpha = Math.round((1 - amt) * 255).toString(16) return `${name}${alpha}` } ================================================ FILE: client/src/utils/create-classnames.ts ================================================ import { computed } from "vue" /** * @param prefix prefix * @param fn config function * @returns */ export function createClassNames( prefix: string, fn: ( add: (name: string) => void, m: (...names: string[]) => string ) => void = () => {} ) { const classNames = computed(() => { const classNames: any[] = [prefix] /** * add a classname * @param name classname */ function add(name: string) { classNames.push(name) } /** * create a classname with a modification name `prefix-mod1-mod2-*-modn` * @param names modification name * @returns */ function m(...names: string[]) { return `${prefix}-${names.join("-")}` } fn(add, m) return classNames }) return { classNames, prefix } } ================================================ FILE: client/src/utils/create-key.ts ================================================ interface characterMap { 1: "1" 2: "2" 3: "3" 4: "4" 5: "5" 6: "6" 7: "7" 8: "8" 9: "9" 0: "0" q: "Q" w: "W" e: "E" r: "R" t: "T" y: "Y" u: "U" i: "I" o: "O" p: "P" a: "A" s: "S" d: "D" f: "F" g: "G" h: "H" j: "J" k: "K" l: "L" z: "Z" x: "X" c: "C" v: "V" b: "B" n: "N" m: "M" [key: string]: string } type char = | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" | "0" | "q" | "w" | "e" | "r" | "t" | "y" | "u" | "i" | "o" | "p" | "a" | "s" | "d" | "f" | "g" | "h" | "j" | "k" | "l" | "z" | "x" | "c" | "v" | "b" | "n" | "m" type RestChars = T extends `${char}${infer P}` ? P : "" type UpperFirst = T extends `${infer P}${string}` ? `${characterMap[P]}${RestChars}` : T export function createKey

( prefix: P, suffix: S ): S extends "default" ? P : `${P}${UpperFirst}` { // eslint-disable-next-line @typescript-eslint/no-explicit-any return (prefix + (suffix === "default" ? "" : suffix.replace(/^[a-z]/, (startChar) => startChar.toUpperCase() ))) as any } ================================================ FILE: client/src/utils/date.spec.ts ================================================ import dayjs from "dayjs" import { dateFromString } from "./date" describe("date", () => { it("should pass normal", () => { const date = dayjs() const str = date.toISOString() expect(dateFromString(str)!.valueOf()).toBe(date.valueOf()) }) it("should pass invalid date", () => { expect(dateFromString("blabla")).toEqual(null) }) it("should pass empty string", () => { expect(dateFromString("")).toEqual(null) }) it("should pass undefined", () => { expect(dateFromString(undefined)).toEqual(null) }) }) ================================================ FILE: client/src/utils/date.ts ================================================ import dayjs, { Dayjs } from "dayjs" export function dateFromString(date?: string): Dayjs | null { if (!date) return null const res = dayjs(date) return res.format("") === "Invalid Date" ? null : res } ================================================ FILE: client/src/utils/hfm.ts ================================================ /** * @file 处理 frontmatter 的格式化 * * invernal_raw * => title, _conent ... categories and more * => Editors * => update:title * => pasetHfm(internal_raw | raw) // set defaults * => { ..oldObj,...updatedObj } * => stringifyHfm(newObj) // remove defaults * => internal_raw */ import * as hfm from "hexo-front-matter" import { categories2Array2d } from "~/utils" type FrontMatterRequired = { _content: string } type FrontMatterPart = { title: string date: string updated: string layout: string tags: string[] categories: string[] fm: { [key: string]: unknown } } type FrontMatterResult = FrontMatterRequired & FrontMatterPart type FrontMatterInput = FrontMatterRequired & Partial export const parseHfm = (str: string = ""): FrontMatterResult => { const { _content: d_content, title: dtitle, date: ddate, updated: dupdated, layout: dlayout, tags: dtags, categories: dcategories, ...rest } = hfm.parse(str) const parseString = (val: unknown): string => { return typeof val === "string" ? val : "" } const parseStringArray = (val: unknown): string[] => { if (!Array.isArray(val)) return [] return val.map(parseString) } const parseStringOrArray = (val: unknown): (string | string[])[] => { if (!Array.isArray(val)) return [] return val.map((v) => Array.isArray(v) ? parseStringArray(v) : parseString(v) ) } const _content = parseString(d_content) const title = parseString(dtitle) const tags = parseStringArray(dtags) const date = parseString(ddate) const updated = parseString(dupdated) const layout = parseString(dlayout) const categories = categories2Array2d(parseStringOrArray(dcategories))[0] ?? [] return { _content, title, date, updated, layout, tags, categories, fm: rest, } } export const stringifyHfm = ({ _content: d_content, title: dtitle, date: ddate, updated: dupdated, layout: dlayout, tags: dtags, categories: dcategories, fm, }: FrontMatterInput) => { const _content = d_content const title = dtitle || undefined const date = ddate || undefined const updated = dupdated || undefined const layout = dlayout || undefined const tags = dtags?.length ? dtags : undefined const categories = dcategories?.length ? dcategories : undefined let o: FrontMatterInput = { _content, ...fm } title && (o = { ...o, title }) date && (o = { ...o, date }) updated && (o = { ...o, updated }) layout && (o = { ...o, layout }) tags && (o = { ...o, tags }) categories && (o = { ...o, categories }) return hfm.stringify(o) } export const updateStringByObj = ( str: string, obj: FrontMatterInput ): string => { return stringifyHfm({ ...parseHfm(str), ...obj }) } ================================================ FILE: client/src/utils/parent.ts ================================================ /** * @see https://github.com/07akioni/vueuc/blob/b3a59ae8f0/src/binder/src/utils.ts * @param node * @returns */ export function getParentNode(node: Node): Node | null { // document type if (node.nodeType === 9) { return null } return node.parentNode } /** * @see https://github.com/07akioni/vueuc/blob/b3a59ae8f0/src/binder/src/utils.ts * @param node * @returns */ export function getScrollParent( node: Node | null ): HTMLElement | Document | null { if (node === null) return null const parentNode = getParentNode(node) if (parentNode === null) { return null } // Document if (parentNode.nodeType === 9) { return document } // Element if (parentNode.nodeType === 1) { // Firefox want us to check `-x` and `-y` variations as well const { overflow, overflowX, overflowY } = getComputedStyle( parentNode as HTMLElement ) if (/(auto|scroll|overlay)/.test(overflow + overflowY + overflowX)) { return parentNode as HTMLElement } } return getScrollParent(parentNode) } ================================================ FILE: client/src/utils/preload.ts ================================================ export function preLoadMonacoEditor() { ;(() => import("@/editors/HMonacoEditor.vue"))() } export function preLoadSettingTabs() { ;(() => import("~/views/settings/SettingsView.vue"))() } export default function preLoadAll() { preLoadMonacoEditor() preLoadSettingTabs() } ================================================ FILE: client/src/utils/scroll.ts ================================================ import { onBeforeUnmount, Ref, watch } from "vue" import { getScrollParent } from "./parent" export function useOnParentScroll( elRef: Ref, onScroll: () => void ) { const removeEventListenerFnMap: Map<() => void, () => void> = new Map() const removeAllEventListener = () => { for (const [, fn] of removeEventListenerFnMap) { fn() } removeEventListenerFnMap.clear() } watch( () => elRef.value, (el) => { removeAllEventListener() if (!el) return const scrollableNodesSet: Set = new Set() let cursor: Element | Document | null = el while (true) { cursor = getScrollParent(cursor) if (cursor === null) break scrollableNodesSet.add(cursor) } for (const node of scrollableNodesSet) { node.addEventListener("scroll", onScroll) removeEventListenerFnMap.set(onScroll, () => { node.removeEventListener("scroll", onScroll) }) } }, { immediate: true, } ) onBeforeUnmount(() => { removeAllEventListener() }) } ================================================ FILE: client/src/utils/string.ts ================================================ export function stringArrayEqual(n: string[], o: string[]) { return n.join("") === o.join("") } ================================================ FILE: client/src/utils.spec.ts ================================================ import { categories2Array2d, isMultiCategories } from "./utils" describe("multi-categories", () => { it("should be multi-categories", () => { const categories = ["C1", ["C21", "C22"]] expect(isMultiCategories(categories)).toBe(true) }) it("should not be multi-categories", () => { const categories = ["C1", "C2"] expect(isMultiCategories(categories)).toBe(false) }) }) describe("categories2Array2d", () => { it("should transform multi-categories to string[][]", () => { const categories = ["C1", ["C21", "C22"]] expect(categories2Array2d(categories)).toEqual([["C1"], ["C21", "C22"]]) }) it("should transform not multi-categories to string[][]", () => { const categories = ["C1", "C2", "C3"] expect(categories2Array2d(categories)).toEqual([["C1", "C2", "C3"]]) }) it("should transform [] to []", () => { const categories: string[] = [] expect(categories2Array2d(categories)).toEqual([]) }) }) ================================================ FILE: client/src/utils.ts ================================================ import { computed, ComputedRef, defineAsyncComponent, ref } from "vue" import { INotificationType } from "./lib/notification" export const forceReloadWindow = () => { window.onbeforeunload = () => {} window.location.reload() } export function list2object< T extends { [key in K]: string }, K extends string >(list: T[], key: K): { [key: string]: T } { const o: { [key: string]: T } = {} list.forEach((item) => (o[item[key]] = item)) return o } /** * @param object 需要转换的对象 * @param key 用来当作 key 的键 * @returns */ export function object2list< T extends { [key in K]: string }, K extends string >( object: { [key: string]: T }, key: K ): T[] { return Object.entries(object).map(([key, value]) => value) } export function isMultiCategories( categories: string[] | (string | string[])[] ) { return !!categories.filter((category) => Array.isArray(category)).length } export function categories2Array2d( categories: string[] | (string | string[])[] ): string[][] { if (JSON.stringify(categories) === JSON.stringify([])) return [] if (!isMultiCategories(categories)) return [categories as string[]] else return categories.map((category) => Array.isArray(category) ? category : [category] ) } export const noop = () => {} export function randomString(length: number = 8) { let result = "" const characters = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" const charactersLength = characters.length for (let i = 0; i < length; i++) { result += characters.charAt(Math.floor(Math.random() * charactersLength)) } return result } export function useAsyncComponentWithLoading( loader: Parameters[0] ): [ReturnType, ComputedRef] { const loading = ref(true) const _loader = "loader" in loader ? loader.loader : loader return [ defineAsyncComponent(() => _loader().then((res) => { loading.value = false return res }) ), computed(() => loading.value), ] } ================================================ FILE: client/src/views/ArticleListView.vue ================================================ ================================================ FILE: client/src/views/ErroredView.vue ================================================ ================================================ FILE: client/src/views/HomeNavView.vue ================================================ ================================================ FILE: client/src/views/ViewerView.vue ================================================ ================================================ FILE: client/src/views/settings/SettingsTabContainer.vue ================================================ ================================================ FILE: client/src/views/settings/SettingsView.vue ================================================ ================================================ FILE: client/src/views/settings/SignoutButton.vue ================================================ ================================================ FILE: client/src/views/settings/TabSwitcher.vue ================================================ ================================================ FILE: client/src/views/settings/index.ts ================================================ export { default as SettingsView } from "./SettingsView.vue" ================================================ FILE: client/src/views/settings/interface.ts ================================================ import type { Component } from "vue" export type SettingsTab = "user" | "security" | "style" | "about" | "help" export interface ISettingsTab { key: string title: string comp: Component } ================================================ FILE: client/src/views/settings/nav-config.ts ================================================ import { computed, markRaw } from "vue" import { HIconName } from "@/ui/icon" import { useThemeVars } from "@/ui/theme" import UserView from "./tabs/UserView.vue" import SecurityView from "./tabs/SecurityView.vue" import StyleView from "./tabs/StyleView.vue" import AboutView from "./tabs/AboutView.vue" import HelpView from "./tabs/HelpView.vue" export function useNavConfig() { const vars = useThemeVars() const config = [ { type: "item" as const, text: "用户", icon: HIconName.Contact, color: vars.value.colorSuccess, key: "user" as const, comp: markRaw(UserView), }, { type: "item" as const, text: "样式", icon: HIconName.OEM, color: vars.value.colorWarning, key: "style" as const, comp: markRaw(StyleView), }, { type: "item" as const, text: "关于", icon: HIconName.Info, color: vars.value.textColorSecondary, key: "about" as const, comp: markRaw(AboutView), }, ] const fullConfig = computed(() => { return config.map((value, idx) => ({ idx, ...value })) }) function getConfig(key: typeof fullConfig.value[number]["key"]) { return fullConfig.value.find((item) => item.key === key)! } return { config: fullConfig, getConfig } } ================================================ FILE: client/src/views/settings/tabs/AboutView.vue ================================================ ================================================ FILE: client/src/views/settings/tabs/HelpView.vue ================================================ ================================================ FILE: client/src/views/settings/tabs/SecurityView.vue ================================================ ================================================ FILE: client/src/views/settings/tabs/StyleView.vue ================================================ ================================================ FILE: client/src/views/settings/tabs/UserView.vue ================================================ ================================================ FILE: client/tailwind.config.js ================================================ module.exports = { purge: ["./index.html", "./src/**/*.{vue,js,ts,jsx,tsx}"], darkMode: false, // or 'media' or 'class' theme: { extend: { lineHeight: { full: "100%", 11: "3rem", }, }, }, variants: { extend: {}, }, plugins: [], } ================================================ FILE: client/tsconfig.json ================================================ { "compilerOptions": { "target": "esnext", "useDefineForClassFields": true, "module": "esnext", "moduleResolution": "node", "strict": true, "jsx": "preserve", "sourceMap": true, "resolveJsonModule": true, "esModuleInterop": true, "lib": ["esnext", "dom"], "types": [ "vite-plugin-pages/client", "vite-plugin-vue-layouts/client", "jest" ], "paths": { "~/*": ["./src/*"], "@/*": ["./src/components/*"], "@shared/*": ["../shared/src/*"], } }, "include": [ ".demo/**/*.ts", ".demo/**/*.d.ts", ".demo/**/*.tsx", ".demo/**/*.vue", "src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue", ".docs/**/*.ts", ".docs/**/*.d.ts", ".docs/**/*.tsx", ".docs/**/*.vue" ] } ================================================ FILE: client/vite.config.ts ================================================ import path from "path" import { defineConfig } from "vite" import vue from "@vitejs/plugin-vue" import visualizer from "rollup-plugin-visualizer" import Unocss from "unocss/vite" import presetUno from "@unocss/preset-uno" import presetAttributify from "@unocss/preset-attributify" import transformerDirective from "@unocss/transformer-directives" import Pages from "vite-plugin-pages" import Layouts from "vite-plugin-vue-layouts" import compression from "vite-plugin-compression" import unused from "./find-unused-file-plugin" const projectRootDir = path.resolve(__dirname) // https://vitejs.dev/config/ export default defineConfig({ server: { port: 4000, proxy: { "/proxy": { target: "http://localhost:5777", changeOrigin: true, rewrite: (path) => path.replace(/^\/proxy/, ""), }, "/api": { target: "http://localhost:5777", changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ""), }, }, }, plugins: [ vue(), Unocss({ presets: [presetUno(), presetAttributify()], transformers: [transformerDirective()], }), unused(), Pages(), Layouts(), compression({ algorithm: "brotliCompress", ext: ".br" }), ], resolve: { alias: [ { find: "~", replacement: path.resolve(projectRootDir, "src"), }, { find: "@", replacement: path.resolve(projectRootDir, "src/components"), }, { find: "@shared", replacement: path.resolve(projectRootDir, "../shared/src"), }, ], }, build: { rollupOptions: { plugins: [visualizer()], }, }, }) ================================================ FILE: eslint.config.mjs ================================================ import antfu from '@antfu/eslint-config' export default antfu({ unocss: true, vue: true, }) ================================================ FILE: package.json ================================================ { "name": "hexon", "version": "1.0.0", "private": true, "author": "winwin2011 ", "license": "GPL-3.0", "scripts": { "release": "standard-version --releaseCommitMessageFormat='chore: release v{{currentTag}}' && pnpm build && git add . && git commit -m 'chore: build released'", "prerelease": "standard-version --releaseCommitMessageFormat='chore: release v{{currentTag}}' --prerelease && pnpm build && git add . && git commit -m 'chore: build released'", "build": "pnpm -r exec npm run build", "setup": "pnpm install -P && cd ./server-scripts && node ./bin/index.js install", "script": "cd ./server-scripts && node ./bin/index.js script", "resetpwd": "cd ./server-scripts && node ./bin/index.js resetpwd", "start": "cd ./server && pnpm start", "prd": "cd ./server && pnpm prd", "dev-init": "pnpm install && pnpm run build && cd ./server-scripts && node ./bin/index.js install", "dev": "concurrently -p [{name}] -n 'server,client' -c 'green.bold,blue.bold' 'cd ./server && pnpm dev' 'cd ./client && pnpm dev' ", "test:fresh-install": "bash ./scripts/fresh_installation.test.sh" }, "devDependencies": { "@antfu/eslint-config": "^2.23.2", "@types/animejs": "^3.1.12", "@types/basic-auth": "^1.1.8", "@types/color": "^3.0.6", "@types/color-convert": "^2.0.3", "@types/crypto-js": "^4.2.2", "@types/debug": "^4.1.12", "@types/faker": "^6.6.9", "@types/hexo": "^3.8.12", "@types/http-errors": "^2.0.4", "@types/inquirer": "^8.2.10", "@types/jest": "^27.5.2", "@types/js-yaml": "^4.0.9", "@types/jsonwebtoken": "^8.5.9", "@types/koa": "^2.15.0", "@types/koa__cors": "^3.3.1", "@types/koa__router": "^8.0.11", "@types/koa-bodyparser": "^4.3.12", "@types/koa-compress": "^4.0.6", "@types/koa-logger": "^3.1.5", "@types/koa-mount": "^4.0.5", "@types/koa-router": "^7.4.8", "@types/koa-static": "^4.0.4", "@types/koa2-cors": "^2.0.5", "@types/lodash-es": "^4.17.12", "@types/node": "^17.0.45", "@types/uuid": "^8.3.4", "@types/ws": "^8.5.11", "@unocss/eslint-plugin": "^0.61.5", "@unocss/preset-attributify": "^0.33.5", "@unocss/preset-uno": "^0.33.5", "@unocss/reset": "^0.33.5", "@unocss/transformer-directives": "^0.33.5", "@vicons/fluent": "^0.12.0", "@vicons/utils": "^0.1.4", "@vitejs/plugin-vue": "^2.3.4", "@vue/compiler-sfc": "^3.4.34", "@vue/test-utils": "^2.4.6", "autoprefixer": "^10.4.19", "babel-loader": "^8.3.0", "concurrently": "^7.6.0", "esbuild": "^0.14.54", "esbuild-plugin-node-externals": "^0.3.0", "eslint": "^9.7.0", "faker": "^6.6.6", "jest": "^27.5.1", "jsdom": "^19.0.0", "less": "^4.2.0", "less-loader": "^10.2.0", "naive-ui": "^2.39.0", "nodemon": "^2.0.22", "picocolors": "^1.0.1", "postcss": "^8.4.40", "prettier": "^2.8.8", "rimraf": "^3.0.2", "rollup-plugin-visualizer": "^5.12.0", "standard-version": "^9.5.0", "stylus": "^0.57.0", "ts-jest": "^27.1.5", "tsconfig-paths": "^3.15.0", "typescript": "^5.5.4", "unocss": "^0.33.5", "vite": "^2.9.18", "vite-plugin-pages": "^0.22.0", "vite-plugin-vue-layouts": "^0.6.0", "vue-tsc": "^2.0.28" } } ================================================ FILE: pnpm-workspace.yaml ================================================ packages: - './client' - './server' - './server-scripts' - './server-shared' ================================================ FILE: scripts/fresh_installation.test.sh ================================================ pnpm run build rm -rf server/data rm -rf ./**/node_modules pnpm run setup pnpm run start ================================================ FILE: server/README.md ================================================ # `winwin-backend` > TODO: description ## Usage ``` src ├── app.js // 宿主应用 ├── apps │ └── admin // 用户管理应用 │ ├── app.js │ ├── routes │ └── services ├── entry.js // 应用载入 ├── index.js // 入口文件 ├── middlewares // 基础中间件 │ └── auth.ts ├── services // 基础服务 │ ├── storage.ts │ └── token.ts └── utils.ts // 基础工具库 ``` ================================================ FILE: server/bin/index.js ================================================ var __create = Object.create; var __defProp = Object.defineProperty; var __defProps = Object.defineProperties; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropDescs = Object.getOwnPropertyDescriptors; var __getOwnPropNames = Object.getOwnPropertyNames; var __getOwnPropSymbols = Object.getOwnPropertySymbols; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __propIsEnum = Object.prototype.propertyIsEnumerable; var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value; var __spreadValues = (a, b) => { for (var prop in b || (b = {})) if (__hasOwnProp.call(b, prop)) __defNormalProp(a, prop, b[prop]); if (__getOwnPropSymbols) for (var prop of __getOwnPropSymbols(b)) { if (__propIsEnum.call(b, prop)) __defNormalProp(a, prop, b[prop]); } return a; }; var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b)); var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index); // node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/dist/shared.cjs.prod.js var require_shared_cjs_prod = __commonJS({ "node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/dist/shared.cjs.prod.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const map = /* @__PURE__ */ Object.create(null); const list = str.split(","); for (let i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? (val) => !!map[val.toLowerCase()] : (val) => !!map[val]; } var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `HYDRATE_EVENTS`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_WHITE_LISTED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt"; var isGloballyWhitelisted = /* @__PURE__ */ makeMap(GLOBALS_WHITE_LISTED); var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push(`${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max(1, end > count ? lineLength - pad : end - start); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isNoUnitNumericStyleProp = /* @__PURE__ */ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width`); var isKnownHtmlAttr = /* @__PURE__ */ makeMap(`accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`); var isKnownSvgAttr = /* @__PURE__ */ makeMap(`xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan`); function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value)) { return value; } else if (isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:(.+)/; function parseStringStyle(cssText) { const ret = {}; cssText.split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles2) { let ret = ""; if (!styles2 || isString(styles2)) { return ret; } for (const key in styles2) { const value = styles2[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number" && isNoUnitNumericStyleProp(normalizedKey)) { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val2]) => { entries[`${key} =>`] = val2; return entries; }, {}) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()] }; } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var EMPTY_OBJ = {}; var EMPTY_ARR = []; var NOOP = () => { }; var NO = () => false; var onRE = /^on[^a-z]/; var isOn = (key) => onRE.test(key); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => val instanceof Date; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return isObject(val) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"); var isBuiltInDirective = /* @__PURE__ */ makeMap("bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo"); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, "-$1").toLowerCase()); var capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1)); var toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var toNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isModelListener = isModelListener; exports.isNoUnitNumericStyleProp = isNoUnitNumericStyleProp; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/dist/shared.cjs.js var require_shared_cjs = __commonJS({ "node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/dist/shared.cjs.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const map = /* @__PURE__ */ Object.create(null); const list = str.split(","); for (let i = 0; i < list.length; i++) { map[list[i]] = true; } return expectsLowerCase ? (val) => !!map[val.toLowerCase()] : (val) => !!map[val]; } var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `HYDRATE_EVENTS`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_WHITE_LISTED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt"; var isGloballyWhitelisted = /* @__PURE__ */ makeMap(GLOBALS_WHITE_LISTED); var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push(`${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}`); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max(1, end > count ? lineLength - pad : end - start); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected`); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isNoUnitNumericStyleProp = /* @__PURE__ */ makeMap(`animation-iteration-count,border-image-outset,border-image-slice,border-image-width,box-flex,box-flex-group,box-ordinal-group,column-count,columns,flex,flex-grow,flex-positive,flex-shrink,flex-negative,flex-order,grid-row,grid-row-end,grid-row-span,grid-row-start,grid-column,grid-column-end,grid-column-span,grid-column-start,font-weight,line-clamp,line-height,opacity,order,orphans,tab-size,widows,z-index,zoom,fill-opacity,flood-opacity,stop-opacity,stroke-dasharray,stroke-dashoffset,stroke-miterlimit,stroke-opacity,stroke-width`); var isKnownHtmlAttr = /* @__PURE__ */ makeMap(`accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap`); var isKnownSvgAttr = /* @__PURE__ */ makeMap(`xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan`); function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value)) { return value; } else if (isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:(.+)/; function parseStringStyle(cssText) { const ret = {}; cssText.split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles2) { let ret = ""; if (!styles2 || isString(styles2)) { return ret; } for (const key in styles2) { const value = styles2[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number" && isNoUnitNumericStyleProp(normalizedKey)) { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistanceLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce((entries, [key, val2]) => { entries[`${key} =>`] = val2; return entries; }, {}) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()] }; } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var EMPTY_OBJ = Object.freeze({}); var EMPTY_ARR = Object.freeze([]); var NOOP = () => { }; var NO = () => false; var onRE = /^on[^a-z]/; var isOn = (key) => onRE.test(key); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => val instanceof Date; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return isObject(val) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap(",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted"); var isBuiltInDirective = /* @__PURE__ */ makeMap("bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo"); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction((str) => str.replace(hyphenateRE, "-$1").toLowerCase()); var capitalize = cacheStringFunction((str) => str.charAt(0).toUpperCase() + str.slice(1)); var toHandlerKey = cacheStringFunction((str) => str ? `on${capitalize(str)}` : ``); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var toNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isModelListener = isModelListener; exports.isNoUnitNumericStyleProp = isNoUnitNumericStyleProp; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/index.js var require_shared = __commonJS({ "node_modules/.pnpm/@vue+shared@3.2.33/node_modules/@vue/shared/index.js"(exports, module2) { "use strict"; if (process.env.NODE_ENV === "production") { module2.exports = require_shared_cjs_prod(); } else { module2.exports = require_shared_cjs(); } } }); // node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.2.33_@vue+shared@3.2.33/node_modules/@vue-reactivity/watch/dist/index.js var require_dist = __commonJS({ "node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.2.33_@vue+shared@3.2.33/node_modules/@vue-reactivity/watch/dist/index.js"(exports, module2) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; var __hasOwnProp2 = Object.prototype.hasOwnProperty; var __markAsModule = (target) => __defProp2(target, "__esModule", { value: true }); var __export = (target, all) => { for (var name in all) __defProp2(target, name, { get: all[name], enumerable: true }); }; var __reExport = (target, module22, copyDefault, desc) => { if (module22 && typeof module22 === "object" || typeof module22 === "function") { for (let key of __getOwnPropNames2(module22)) if (!__hasOwnProp2.call(target, key) && (copyDefault || key !== "default")) __defProp2(target, key, { get: () => module22[key], enumerable: !(desc = __getOwnPropDesc2(module22, key)) || desc.enumerable }); } return target; }; var __toCommonJS = /* @__PURE__ */ ((cache) => { return (module22, temp) => { return cache && cache.get(module22) || (temp = __reExport(__markAsModule({}), module22, 1), cache && cache.set(module22, temp), temp); }; })(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0); var src_exports = {}; __export(src_exports, { watch: () => watch2, watchEffect: () => watchEffect }); var import_reactivity3 = require("@vue/reactivity"); var import_shared2 = require_shared(); var import_shared = require_shared(); function callWithErrorHandling(fn, type, args) { let res; try { res = args ? fn(...args) : fn(); } catch (err) { handleError(err, type); } return res; } function callWithAsyncErrorHandling(fn, type, args) { if ((0, import_shared.isFunction)(fn)) { const res = callWithErrorHandling(fn, type, args); if (res && (0, import_shared.isPromise)(res)) { res.catch((err) => { handleError(err, type); }); } return res; } const values = []; for (let i = 0; i < fn.length; i++) values.push(callWithAsyncErrorHandling(fn[i], type, args)); return values; } function handleError(err, type) { console.error(new Error(`[@vue-reactivity/watch]: ${type}`)); console.error(err); } function warn2(message) { console.warn(createError(message)); } function createError(message) { return new Error(`[reactivue]: ${message}`); } var INITIAL_WATCHER_VALUE = {}; function watchEffect(effect, options) { return doWatch(effect, null, options); } function watch2(source, cb, options) { return doWatch(source, cb, options); } function doWatch(source, cb, { immediate, deep, flush } = {}) { let getter; let forceTrigger = false; let isMultiSource = false; if ((0, import_reactivity3.isRef)(source)) { getter = () => source.value; forceTrigger = (0, import_reactivity3.isShallow)(source); } else if ((0, import_reactivity3.isReactive)(source)) { getter = () => source; deep = true; } else if ((0, import_shared2.isArray)(source)) { isMultiSource = true; forceTrigger = source.some(import_reactivity3.isReactive); getter = () => source.map((s) => { if ((0, import_reactivity3.isRef)(s)) return s.value; else if ((0, import_reactivity3.isReactive)(s)) return traverse(s); else if ((0, import_shared2.isFunction)(s)) return callWithErrorHandling(s, "watch getter"); else return warn2("invalid source"); }); } else if ((0, import_shared2.isFunction)(source)) { if (cb) { getter = () => callWithErrorHandling(source, "watch getter"); } else { getter = () => { if (cleanup) cleanup(); return callWithAsyncErrorHandling(source, "watch callback", [onCleanup]); }; } } else { getter = import_shared2.NOOP; } if (cb && deep) { const baseGetter = getter; getter = () => traverse(baseGetter()); } let cleanup; let onCleanup = (fn) => { cleanup = effect.onStop = () => { callWithErrorHandling(fn, "watch cleanup"); }; }; let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE; const job = () => { if (!effect.active) return; if (cb) { const newValue = effect.run(); if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => (0, import_shared2.hasChanged)(v, oldValue[i])) : (0, import_shared2.hasChanged)(newValue, oldValue))) { if (cleanup) cleanup(); callWithAsyncErrorHandling(cb, "watch value", [ newValue, oldValue === INITIAL_WATCHER_VALUE ? void 0 : oldValue, onCleanup ]); oldValue = newValue; } } else { effect.run(); } }; job.allowRecurse = !!cb; let scheduler; if (flush === "sync") { scheduler = job; } else { scheduler = () => { job(); }; } const effect = new import_reactivity3.ReactiveEffect(getter, scheduler); if (cb) { if (immediate) job(); else oldValue = effect.run(); } else { effect.run(); } return () => effect.stop(); } function traverse(value, seen = /* @__PURE__ */ new Set()) { if (!(0, import_shared2.isObject)(value) || seen.has(value)) return value; seen.add(value); if ((0, import_shared2.isArray)(value)) { for (let i = 0; i < value.length; i++) traverse(value[i], seen); } else if (value instanceof Map) { value.forEach((_, key) => { traverse(value.get(key), seen); }); } else if (value instanceof Set) { value.forEach((v) => { traverse(v, seen); }); } else { for (const key of Object.keys(value)) traverse(value[key], seen); } return value; } module2.exports = __toCommonJS(src_exports); } }); // src/scripts/index.ts var import_reflect_metadata = require("reflect-metadata"); var import_commander = require("commander"); // src/scripts/install.ts var import_path5 = __toESM(require("path")); // node_modules/.pnpm/chalk@5.0.1/node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); const styles2 = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; styles2.color.gray = styles2.color.blackBright; styles2.bgColor.bgGray = styles2.bgColor.bgBlackBright; styles2.color.grey = styles2.color.blackBright; styles2.bgColor.bgGrey = styles2.bgColor.bgBlackBright; for (const [groupName, group] of Object.entries(styles2)) { for (const [styleName, style] of Object.entries(group)) { styles2[styleName] = { open: `\x1B[${style[0]}m`, close: `\x1B[${style[1]}m` }; group[styleName] = styles2[styleName]; codes.set(style[0], style[1]); } Object.defineProperty(styles2, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles2, "codes", { value: codes, enumerable: false }); styles2.color.close = "\x1B[39m"; styles2.bgColor.close = "\x1B[49m"; styles2.color.ansi = wrapAnsi16(); styles2.color.ansi256 = wrapAnsi256(); styles2.color.ansi16m = wrapAnsi16m(); styles2.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles2.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles2.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles2, { rgbToAnsi256: { value: (red, green, blue) => { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value: (hex) => { const matches = /(?[a-f\d]{6}|[a-f\d]{3})/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let { colorString } = matches.groups; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ integer >> 16 & 255, integer >> 8 & 255, integer & 255 ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles2.rgbToAnsi256(...styles2.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value: (code) => { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles2.ansi256ToAnsi(styles2.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles2.ansi256ToAnsi(styles2.hexToAnsi256(hex)), enumerable: false } }); return styles2; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; // node_modules/.pnpm/chalk@5.0.1/node_modules/chalk/source/vendor/supports-color/index.js var import_node_process = __toESM(require("process"), 1); var import_node_os = __toESM(require("os"), 1); var import_node_tty = __toESM(require("tty"), 1); function hasFlag(flag, argv = import_node_process.default.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = import_node_process.default; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (import_node_process.default.platform === "win32") { const osRelease = import_node_os.default.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "GITHUB_ACTIONS", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (env.COLORTERM === "truecolor") { return 3; } if ("TERM_PROGRAM" in env) { const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": return version2 >= 3 ? 3 : 2; case "Apple_Terminal": return 2; } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, __spreadValues({ streamIsTTY: stream && stream.isTTY }, options)); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }), stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) }) }; var supports_color_default = supportsColor; // node_modules/.pnpm/chalk@5.0.1/node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.substr(endIndex, index - endIndex) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.substr(endIndex, (gotCR ? index - 1 : index) - endIndex) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // node_modules/.pnpm/chalk@5.0.1/node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style] of Object.entries(ansi_styles_default)) { styles[styleName] = { get() { const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, __spreadProps(__spreadValues({}, styles), { level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } })); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self2, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self2; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self2, string) => { if (self2.level <= 0 || !string) { return self2[IS_EMPTY] ? "" : string; } let styler = self2[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles); var chalk = createChalk(); var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // src/scripts/install.ts var import_tsyringe5 = require("tsyringe"); // src/shared/storage-service.ts var import_tsyringe = require("tsyringe"); var import_fs = require("fs"); var import_path = require("path"); var import_simple_json_db = __toESM(require("simple-json-db")); var defaultRoot = (0, import_path.resolve)(process.cwd(), "data"); var defaultFilename = "common.db"; var StorageService = class { constructor() { this._root = defaultRoot; this._filename = defaultFilename; if (!(0, import_fs.existsSync)(this._root)) (0, import_fs.mkdirSync)(this._root); this._db = new import_simple_json_db.default((0, import_path.resolve)(this._root, this._filename)); } get(key) { return this._db.get(key); } set(key, value) { this._db.set(key, value); } delete(key) { return this._db.delete(key); } }; StorageService = __decorateClass([ (0, import_tsyringe.singleton)() ], StorageService); // src/scripts/constants.ts var import_fs3 = __toESM(require("fs")); var import_path2 = __toESM(require("path")); // src/scripts/utils.ts var import_fs2 = __toESM(require("fs")); function section(title) { console.log(); console.log(source_default.blue.bold("\u2699 " + title)); console.log(); } function log(...args) { console.log(...args); } function info(...args) { console.log(source_default.blue(...args)); } function success(...args) { console.log(source_default.green(...args)); } function warn(...args) { console.log(source_default.yellow(...args)); } function error(...args) { console.log(source_default.red(...args)); } var printer = { section, log, success, info, warn, error }; function readJsonFile(filename) { const file = import_fs2.default.readFileSync(filename, { encoding: "utf-8" }); return JSON.parse(file); } function printVersion() { printer.section("Check version"); printer.info(`Current Version: ${version}`); if (version.indexOf("-") >= 0) { printer.warn("This is a preview version!"); } } // src/scripts/constants.ts var logo = (() => { try { return import_fs3.default.readFileSync(import_path2.default.resolve(process.cwd(), "./assets/logo.art"), "utf-8"); } catch (err) { console.error(err); return "Hexon"; } })(); var version = readJsonFile(import_path2.default.resolve(process.cwd(), "../package.json")).version; // src/shared/constants.ts var HEXON_PORT_KEY = "@hexon/port"; var HEXON_DEFAULT_PORT = 5777; // src/server/services/hexo-instance-service.ts var import_tsyringe3 = require("tsyringe"); var import_hexo = __toESM(require("hexo")); var import_path4 = __toESM(require("path")); // src/server/services/log-service.ts var import_tsyringe2 = require("tsyringe"); var import_dayjs = __toESM(require("dayjs")); var DEFAULT_DATE_FORMAT = "YYYY-MM-DD hh:mm:ss.SSS"; var LogService = class { constructor() { this.scope = ""; this.dateFormat = DEFAULT_DATE_FORMAT; } _prefix(type) { let prefix = ""; this.scope && (prefix += source_default[type].bold(`[${this.scope}]`)); prefix += source_default.blue(`[${(0, import_dayjs.default)().format(this.dateFormat)}]`); return prefix; } _log(...args) { console.log(...args); } _error(...args) { console.error(...args); } setScope(scope) { this.scope = scope; } log(...args) { this._log(this._prefix("green"), ...args); } error(...args) { this._error(this._prefix("red"), ...args); } logWithUser(user, ...args) { this._log(this._prefix("green") + source_default.yellow.dim(`[${user.username}:${user.slug}]`), ...args); } static create(scope) { const instance = import_tsyringe2.container.resolve(LogService); instance.setScope(scope); return instance; } }; // src/shared/utils.ts var import_fs4 = __toESM(require("fs")); var import_path3 = __toESM(require("path")); function isBlog(cwd) { var _a; let file; try { file = import_fs4.default.readFileSync(import_path3.default.join(cwd, "package.json"), { encoding: "utf-8" }); import_fs4.default.readFileSync(import_path3.default.join(cwd, "_config.yml"), { encoding: "utf-8" }); } catch (err) { if (err.code === "ENOENT") { return false; } throw err; } const packageJSON = JSON.parse(file); if (!((_a = packageJSON == null ? void 0 : packageJSON.dependencies) == null ? void 0 : _a.hexo)) return false; return true; } function toRealPath(value) { return import_path3.default.isAbsolute(value) ? value : import_path3.default.resolve(process.cwd(), "../..", value); } // src/server/utils.ts var import_debug = __toESM(require("debug")); var DEV = process.env.NODE_ENV !== "production"; // src/server/errors.ts var import_http_errors = require("http-errors"); var HexoInitError = class extends import_http_errors.InternalServerError { constructor() { super(...arguments); this.id = "HexoInitError"; } }; // src/server/services/hexo-instance-service.ts var HexoInstanceService = class { constructor(_logService, _storageService) { this._logService = _logService; this._storageService = _storageService; this._options = null; this._base = null; this._hexo = null; this._ready = false; this._promise = null; this._logService.setScope("hexo-instance-service"); } _withOptionsOverrides(options) { return __spreadProps(__spreadValues({}, options), { draft: true, drafts: true }); } _setHexoBase() { const base = this._storageService.get(HexoInstanceService.HEXO_BASE_DIR_KEY); const base_dir = import_path4.default.resolve(__dirname, toRealPath(base)); if (!isBlog(base_dir)) throw new Error(`"${base_dir}" is not a hexo blog folder`); this._base = base_dir; } _setOptions() { this._options = this._storageService.get(HexoInstanceService.HEXO_OPTIONS_KEY) || {}; this._options.silent = DEV ? false : this._options.silent; } _createHexoInstance() { if (!this._base) throw new Error("please set hexo root first"); this._hexo = new import_hexo.default(this._base, this._withOptionsOverrides(this._options)); } async _init() { this._logService.log("real init start"); this._ready = false; await this._setHexoBase(); await this._setOptions(); await this._createHexoInstance(); await this._hexo.init(); await this._hexo.watch(); this._ready = true; this._logService.log("real init finished"); } async setOptions(options) { this._storageService.set(HexoInstanceService.HEXO_OPTIONS_KEY, options); this._logService.log("options set"); } async _tryInit(count = HexoInstanceService.MAX_RETRY) { try { await this._init(); HexoInstanceService.INITING = false; } catch (err) { this._logService.error(err); this._logService.error(`error when init hexo instance. `); this._logService.error(`retry in ${HexoInstanceService.RETRY_INTERVAL} ms.`, `${count} retry left`); if (count) return new Promise((resolve4) => { setTimeout(() => { resolve4(this._tryInit(count - 1)); }, HexoInstanceService.RETRY_INTERVAL); }); else { HexoInstanceService.INITING = false; throw new HexoInitError(String(err)); } } } async init() { if (!HexoInstanceService.INITING) this._promise = this._tryInit(); return this._promise; } async getBaseDir() { if (!this._ready) await this.init(); return this._base; } async getInstance() { if (!this._ready) await this.init(); this._logService.log("instance required"); return this._hexo; } async getInstanceWithOriginOptions(genOptions = (o) => o) { if (!this._ready) await this.init(); const newOptions = genOptions(this._options); const hexo = new import_hexo.default(this._base, newOptions); await hexo.init(); await hexo.watch(); HexoInstanceService.TO_BE_CLEANED++; this._logService.log("instance with options required"); this._logService.log(`${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned`); const cleanup = async () => { await hexo.unwatch(); HexoInstanceService.TO_BE_CLEANED--; this._logService.log("instance with options cleaned"); if (HexoInstanceService.TO_BE_CLEANED === 0) { this._logService.log("all instances have been cleaned"); } else { this._logService.log(`${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned`); } }; return { hexo, cleanup }; } async runBetweenReload(fn) { if (!this._ready) await this.init(); const unload = async () => { await this._hexo.unwatch(); }; const load = async () => { await this._hexo.watch(); HexoInstanceService.INITING = false; }; const markHexoInitError = (err) => { this._ready = false; HexoInstanceService.INITING = false; }; HexoInstanceService.INITING = true; await unload().catch(markHexoInitError); const res = await Promise.resolve(fn()); await load().catch(markHexoInitError); return res; } }; HexoInstanceService.HEXO_BASE_DIR_KEY = "hexo-basedir"; HexoInstanceService.HEXO_OPTIONS_KEY = "hexo-options"; HexoInstanceService.INITING = false; HexoInstanceService.PENDING_COUNT = 0; HexoInstanceService.RETRY_INTERVAL = 1e3; HexoInstanceService.MAX_RETRY = 2; HexoInstanceService.CURRENT_RETRY = 0; HexoInstanceService.TO_BE_CLEANED = 0; HexoInstanceService = __decorateClass([ (0, import_tsyringe3.injectable)(), (0, import_tsyringe3.singleton)(), __decorateParam(0, (0, import_tsyringe3.inject)(LogService)), __decorateParam(1, (0, import_tsyringe3.inject)(StorageService)) ], HexoInstanceService); // src/shared/account-storage-service.ts var import_crypto_js = require("crypto-js"); var import_tsyringe4 = require("tsyringe"); var AccountService = class { constructor(_storage) { this._storage = _storage; } _encrypt(raw) { return (0, import_crypto_js.SHA1)(raw).toString(); } _toStorage(info2) { this._storage.set(AccountService.KEY, info2); } _fromStorage() { const { username = "", password = "" } = this._storage.get(AccountService.KEY) || {}; return { username, password }; } setUserInfo(username, password) { this._storage.set(AccountService.KEY, { username, password: this._encrypt(password) }); } getUsername() { return this._fromStorage().username; } setUsername(username) { const info2 = this._fromStorage(); info2.username = username; this._toStorage(info2); } setPassword(password) { const info2 = this._fromStorage(); info2.password = this._encrypt(password); this._toStorage(info2); } setEncrptedPassword(password) { const info2 = this._fromStorage(); info2.password = password; this._toStorage(info2); } verify(username, password) { const info2 = this._fromStorage(); if (username !== info2.username) { return false; } if (this._encrypt(password) !== info2.password) { return false; } return true; } }; AccountService.KEY = "userinfo"; AccountService = __decorateClass([ (0, import_tsyringe4.injectable)(), (0, import_tsyringe4.singleton)(), __decorateParam(0, (0, import_tsyringe4.inject)(StorageService)) ], AccountService); // src/scripts/prompts.ts var import_inquirer = __toESM(require("inquirer")); async function requestPassword() { const answer = await import_inquirer.default.prompt({ name: "password", message: "Password ?", type: "password", validate(v) { if (!v) return "Must not empty"; return true; } }); const { password } = answer; return password; } async function requestUsername() { const answer = await import_inquirer.default.prompt({ name: "username", message: "Username ?", validate(v) { if (!v) return "Must not empty"; return true; } }); const { username } = answer; return username; } async function requestUserInfo() { const username = await requestUsername(); const password = await requestPassword(); return { username, password }; } async function requestPort(defaultPort) { const portPrompt = { name: "port", message: "Which port do you like Hexon running at?", default: defaultPort, validate(v) { return !isNaN(v) || `number is required ${typeof v} given`; }, prefix: source_default.blue("?") }; const answer = await import_inquirer.default.prompt(portPrompt); return String(answer.port); } async function requestRoot() { const rootPrompt = { name: "root", message: `Your hexo blog path? ${source_default.grey("Absolute or relative path to hexon.")}`, validate(v) { const truePath = toRealPath(v); try { return isBlog(truePath) || source_default.red.bold(truePath) + source_default.red(" is not a valid hexo blog."); } catch (e) { console.error(e); return source_default.red("Fail to check path " + source_default.bold(truePath)); } } }; const answer = await import_inquirer.default.prompt(rootPrompt); return answer.root; } // src/scripts/install.ts async function install_default() { console.clear(); console.log(source_default.blue(logo)); printVersion(); printer.section("Configuration"); const storage = import_tsyringe5.container.resolve(StorageService); storage.set(HEXON_PORT_KEY, await requestPort(HEXON_DEFAULT_PORT)); storage.set(HexoInstanceService.HEXO_BASE_DIR_KEY, await requestRoot()); const { username, password } = await requestUserInfo(); const account = import_tsyringe5.container.resolve(AccountService); account.setUserInfo(username, password); printer.section("Install"); const base = import_path5.default.resolve(__dirname, "../.."); printer.success(`Hexon has been installed to \`${base}\``); printer.log(`Run \`pnpm start\` to start`); printer.log(`Run \`pnpm prd\` to start with pm2`); printer.log(source_default.grey(`To uninstall, remove the following foler: ${base}`)); } // src/scripts/reset-password.ts var import_tsyringe6 = require("tsyringe"); async function resetPassword() { const account = import_tsyringe6.container.resolve(AccountService); const newpwd = await requestPassword(); account.setPassword(newpwd); console.log(source_default.green(`Password has been reset.`)); } // src/scripts/script.ts var import_inquirer2 = __toESM(require("inquirer")); // src/shared/store.ts var import_path7 = require("path"); var import_reactivity2 = require("@vue/reactivity"); // node_modules/.pnpm/@winwin+server-reactive-store@0.2.2/node_modules/@winwin/server-reactive-store/dist/index.mjs var import_reactivity = require("@vue/reactivity"); var import_watch = __toESM(require_dist(), 1); var import_fs5 = require("fs"); var import_path6 = require("path"); var import_simple_json_db2 = __toESM(require("simple-json-db"), 1); var JSONdbStorageAdapter = class { constructor(root, name = "database.json") { if (!(0, import_fs5.existsSync)(root)) (0, import_fs5.mkdirSync)(root); this.db = new import_simple_json_db2.default((0, import_path6.resolve)(root, name)); } getItem(key) { return this.db.get(key); } setItem(key, value) { this.db.set(key, value); } removeItem(key) { return this.db.delete(key); } }; function createStore(key, adapter, setup, { saveAfterCreate = true } = {}) { const all = setup(); if (!all.state) throw new Error("must return object with state property"); const state = (0, import_reactivity.reactive)(all.state); const load = () => { const loaded = adapter.getItem(key); if (loaded) Object.assign(state, loaded); }; load(); const save = () => adapter.setItem(key, state); (0, import_watch.watch)(state, save, { deep: true, immediate: saveAfterCreate }); return __spreadProps(__spreadValues({}, all), { load, save, state }); } function createStoreCreator(adapter) { return function(key, setup) { return createStore(key, adapter, setup); }; } // src/shared/store.ts var ROOT = (0, import_path7.resolve)(process.cwd(), "data"); var NAME = "database.json"; var createStore2 = createStoreCreator(new JSONdbStorageAdapter(ROOT, NAME)); var scriptStore = createStore2("script", () => { const state = (0, import_reactivity2.reactive)({ items: {} }); const keys = (0, import_reactivity2.computed)(() => Object.keys(state.items)); const getScript = (key) => { var _a; return ((_a = state.items[key]) == null ? void 0 : _a.value) || ""; }; const hasScript = (key) => { var _a; return (_a = state.items[key]) == null ? void 0 : _a.value; }; const setScript = (key, script2) => { state.items[key] = { value: script2 }; }; return { state, keys, getScript, setScript, hasScript }; }); // src/scripts/script.ts function getAvailableKeys() { const keys = /* @__PURE__ */ new Set([ "git-save", "git-sync", "hexo-clean", "hexo-generate", "hexo-deploy", ...scriptStore.keys.value ]); return [...keys.keys()].map((value) => { const v = scriptStore.getScript(value); const name = v ? `${value}: ${v}` : value; return { name, value }; }); } async function pickOneScript() { const { key } = await import_inquirer2.default.prompt({ name: "key", type: "list", message: "Which script do you want to manage?", choices: () => getAvailableKeys() }); return key; } async function getInput(key) { const currnet = scriptStore.hasScript(key) && `- Current: ${scriptStore.getScript(key)}`; const { value } = await import_inquirer2.default.prompt({ name: "value", message: `Please input some script to run your script file (with relative path to your hexo blog folder). ${source_default.gray(`- e.g. \`bash ./my_deploy_script.sh\` - Leave blank to remove. ${currnet ? `${currnet} ` : ""}`)}` }); return value; } async function script() { const key = await pickOneScript(); const value = await getInput(key); scriptStore.setScript(key, value); console.log(source_default.bgGreen.white(` Script ${value ? "Saved" : "Removed"} `)); } // src/scripts/index.ts var program = new import_commander.Command("npx ."); program.command("install").description("install hexon").action(install_default); program.command("resetpwd").description("reset password").action(resetPassword); program.command("script").description("manage custom script").action(script); program.parse(); ================================================ FILE: server/dist/index.js ================================================ "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index); // ../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js var require_windows = __commonJS({ "../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/windows.js"(exports, module2) { module2.exports = isexe; isexe.sync = sync; var fs3 = require("fs"); function checkPathExt(path9, options) { var pathext = options.pathExt !== void 0 ? options.pathExt : process.env.PATHEXT; if (!pathext) { return true; } pathext = pathext.split(";"); if (pathext.indexOf("") !== -1) { return true; } for (var i = 0; i < pathext.length; i++) { var p = pathext[i].toLowerCase(); if (p && path9.substr(-p.length).toLowerCase() === p) { return true; } } return false; } function checkStat(stat, path9, options) { if (!stat.isSymbolicLink() && !stat.isFile()) { return false; } return checkPathExt(path9, options); } function isexe(path9, options, cb) { fs3.stat(path9, function(er, stat) { cb(er, er ? false : checkStat(stat, path9, options)); }); } function sync(path9, options) { return checkStat(fs3.statSync(path9), path9, options); } } }); // ../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js var require_mode = __commonJS({ "../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/mode.js"(exports, module2) { module2.exports = isexe; isexe.sync = sync; var fs3 = require("fs"); function isexe(path9, options, cb) { fs3.stat(path9, function(er, stat) { cb(er, er ? false : checkStat(stat, options)); }); } function sync(path9, options) { return checkStat(fs3.statSync(path9), options); } function checkStat(stat, options) { return stat.isFile() && checkMode(stat, options); } function checkMode(stat, options) { var mod = stat.mode; var uid = stat.uid; var gid = stat.gid; var myUid = options.uid !== void 0 ? options.uid : process.getuid && process.getuid(); var myGid = options.gid !== void 0 ? options.gid : process.getgid && process.getgid(); var u = parseInt("100", 8); var g = parseInt("010", 8); var o = parseInt("001", 8); var ug = u | g; var ret = mod & o || mod & g && gid === myGid || mod & u && uid === myUid || mod & ug && myUid === 0; return ret; } } }); // ../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js var require_isexe = __commonJS({ "../node_modules/.pnpm/isexe@2.0.0/node_modules/isexe/index.js"(exports, module2) { var fs3 = require("fs"); var core; if (process.platform === "win32" || global.TESTING_WINDOWS) { core = require_windows(); } else { core = require_mode(); } module2.exports = isexe; isexe.sync = sync; function isexe(path9, options, cb) { if (typeof options === "function") { cb = options; options = {}; } if (!cb) { if (typeof Promise !== "function") { throw new TypeError("callback not provided"); } return new Promise(function(resolve4, reject) { isexe(path9, options || {}, function(er, is) { if (er) { reject(er); } else { resolve4(is); } }); }); } core(path9, options || {}, function(er, is) { if (er) { if (er.code === "EACCES" || options && options.ignoreErrors) { er = null; is = false; } } cb(er, is); }); } function sync(path9, options) { try { return core.sync(path9, options || {}); } catch (er) { if (options && options.ignoreErrors || er.code === "EACCES") { return false; } else { throw er; } } } } }); // ../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js var require_which = __commonJS({ "../node_modules/.pnpm/which@2.0.2/node_modules/which/which.js"(exports, module2) { var isWindows = process.platform === "win32" || process.env.OSTYPE === "cygwin" || process.env.OSTYPE === "msys"; var path9 = require("path"); var COLON = isWindows ? ";" : ":"; var isexe = require_isexe(); var getNotFoundError = (cmd) => Object.assign(new Error(`not found: ${cmd}`), { code: "ENOENT" }); var getPathInfo = (cmd, opt) => { const colon = opt.colon || COLON; const pathEnv = cmd.match(/\//) || isWindows && cmd.match(/\\/) ? [""] : [ ...isWindows ? [process.cwd()] : [], ...(opt.path || process.env.PATH || "").split(colon) ]; const pathExtExe = isWindows ? opt.pathExt || process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM" : ""; const pathExt = isWindows ? pathExtExe.split(colon) : [""]; if (isWindows) { if (cmd.indexOf(".") !== -1 && pathExt[0] !== "") pathExt.unshift(""); } return { pathEnv, pathExt, pathExtExe }; }; var which = (cmd, opt, cb) => { if (typeof opt === "function") { cb = opt; opt = {}; } if (!opt) opt = {}; const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt); const found = []; const step = (i) => new Promise((resolve4, reject) => { if (i === pathEnv.length) return opt.all && found.length ? resolve4(found) : reject(getNotFoundError(cmd)); const ppRaw = pathEnv[i]; const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw; const pCmd = path9.join(pathPart, cmd); const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd; resolve4(subStep(p, i, 0)); }); const subStep = (p, i, ii) => new Promise((resolve4, reject) => { if (ii === pathExt.length) return resolve4(step(i + 1)); const ext = pathExt[ii]; isexe(p + ext, { pathExt: pathExtExe }, (er, is) => { if (!er && is) { if (opt.all) found.push(p + ext); else return resolve4(p + ext); } return resolve4(subStep(p, i, ii + 1)); }); }); return cb ? step(0).then((res) => cb(null, res), cb) : step(0); }; var whichSync = (cmd, opt) => { opt = opt || {}; const { pathEnv, pathExt, pathExtExe } = getPathInfo(cmd, opt); const found = []; for (let i = 0; i < pathEnv.length; i++) { const ppRaw = pathEnv[i]; const pathPart = /^".*"$/.test(ppRaw) ? ppRaw.slice(1, -1) : ppRaw; const pCmd = path9.join(pathPart, cmd); const p = !pathPart && /^\.[\\\/]/.test(cmd) ? cmd.slice(0, 2) + pCmd : pCmd; for (let j = 0; j < pathExt.length; j++) { const cur = p + pathExt[j]; try { const is = isexe.sync(cur, { pathExt: pathExtExe }); if (is) { if (opt.all) found.push(cur); else return cur; } } catch (ex) { } } } if (opt.all && found.length) return found; if (opt.nothrow) return null; throw getNotFoundError(cmd); }; module2.exports = which; which.sync = whichSync; } }); // ../node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js var require_path_key = __commonJS({ "../node_modules/.pnpm/path-key@3.1.1/node_modules/path-key/index.js"(exports, module2) { "use strict"; var pathKey2 = (options = {}) => { const environment = options.env || process.env; const platform = options.platform || process.platform; if (platform !== "win32") { return "PATH"; } return Object.keys(environment).reverse().find((key) => key.toUpperCase() === "PATH") || "Path"; }; module2.exports = pathKey2; module2.exports.default = pathKey2; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/resolveCommand.js var require_resolveCommand = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/resolveCommand.js"(exports, module2) { "use strict"; var path9 = require("path"); var which = require_which(); var getPathKey = require_path_key(); function resolveCommandAttempt(parsed, withoutPathExt) { const env2 = parsed.options.env || process.env; const cwd = process.cwd(); const hasCustomCwd = parsed.options.cwd != null; const shouldSwitchCwd = hasCustomCwd && process.chdir !== void 0 && !process.chdir.disabled; if (shouldSwitchCwd) { try { process.chdir(parsed.options.cwd); } catch (err) { } } let resolved; try { resolved = which.sync(parsed.command, { path: env2[getPathKey({ env: env2 })], pathExt: withoutPathExt ? path9.delimiter : void 0 }); } catch (e) { } finally { if (shouldSwitchCwd) { process.chdir(cwd); } } if (resolved) { resolved = path9.resolve(hasCustomCwd ? parsed.options.cwd : "", resolved); } return resolved; } function resolveCommand(parsed) { return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true); } module2.exports = resolveCommand; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/escape.js var require_escape = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/escape.js"(exports, module2) { "use strict"; var metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g; function escapeCommand(arg) { arg = arg.replace(metaCharsRegExp, "^$1"); return arg; } function escapeArgument(arg, doubleEscapeMetaChars) { arg = `${arg}`; arg = arg.replace(/(\\*)"/g, '$1$1\\"'); arg = arg.replace(/(\\*)$/, "$1$1"); arg = `"${arg}"`; arg = arg.replace(metaCharsRegExp, "^$1"); if (doubleEscapeMetaChars) { arg = arg.replace(metaCharsRegExp, "^$1"); } return arg; } module2.exports.command = escapeCommand; module2.exports.argument = escapeArgument; } }); // ../node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js var require_shebang_regex = __commonJS({ "../node_modules/.pnpm/shebang-regex@3.0.0/node_modules/shebang-regex/index.js"(exports, module2) { "use strict"; module2.exports = /^#!(.*)/; } }); // ../node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js var require_shebang_command = __commonJS({ "../node_modules/.pnpm/shebang-command@2.0.0/node_modules/shebang-command/index.js"(exports, module2) { "use strict"; var shebangRegex = require_shebang_regex(); module2.exports = (string = "") => { const match = string.match(shebangRegex); if (!match) { return null; } const [path9, argument] = match[0].replace(/#! ?/, "").split(" "); const binary = path9.split("/").pop(); if (binary === "env") { return argument; } return argument ? `${binary} ${argument}` : binary; }; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/readShebang.js var require_readShebang = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/util/readShebang.js"(exports, module2) { "use strict"; var fs3 = require("fs"); var shebangCommand = require_shebang_command(); function readShebang(command) { const size = 150; const buffer = Buffer.alloc(size); let fd; try { fd = fs3.openSync(command, "r"); fs3.readSync(fd, buffer, 0, size, 0); fs3.closeSync(fd); } catch (e) { } return shebangCommand(buffer.toString()); } module2.exports = readShebang; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/parse.js var require_parse = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/parse.js"(exports, module2) { "use strict"; var path9 = require("path"); var resolveCommand = require_resolveCommand(); var escape = require_escape(); var readShebang = require_readShebang(); var isWin = process.platform === "win32"; var isExecutableRegExp = /\.(?:com|exe)$/i; var isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i; function detectShebang(parsed) { parsed.file = resolveCommand(parsed); const shebang = parsed.file && readShebang(parsed.file); if (shebang) { parsed.args.unshift(parsed.file); parsed.command = shebang; return resolveCommand(parsed); } return parsed.file; } function parseNonShell(parsed) { if (!isWin) { return parsed; } const commandFile = detectShebang(parsed); const needsShell = !isExecutableRegExp.test(commandFile); if (parsed.options.forceShell || needsShell) { const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile); parsed.command = path9.normalize(parsed.command); parsed.command = escape.command(parsed.command); parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars)); const shellCommand = [parsed.command].concat(parsed.args).join(" "); parsed.args = ["/d", "/s", "/c", `"${shellCommand}"`]; parsed.command = process.env.comspec || "cmd.exe"; parsed.options.windowsVerbatimArguments = true; } return parsed; } function parse(command, args, options) { if (args && !Array.isArray(args)) { options = args; args = null; } args = args ? args.slice(0) : []; options = Object.assign({}, options); const parsed = { command, args, options, file: void 0, original: { command, args } }; return options.shell ? parsed : parseNonShell(parsed); } module2.exports = parse; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/enoent.js var require_enoent = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/lib/enoent.js"(exports, module2) { "use strict"; var isWin = process.platform === "win32"; function notFoundError(original, syscall) { return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), { code: "ENOENT", errno: "ENOENT", syscall: `${syscall} ${original.command}`, path: original.command, spawnargs: original.args }); } function hookChildProcess(cp, parsed) { if (!isWin) { return; } const originalEmit = cp.emit; cp.emit = function(name, arg1) { if (name === "exit") { const err = verifyENOENT(arg1, parsed, "spawn"); if (err) { return originalEmit.call(cp, "error", err); } } return originalEmit.apply(cp, arguments); }; } function verifyENOENT(status, parsed) { if (isWin && status === 1 && !parsed.file) { return notFoundError(parsed.original, "spawn"); } return null; } function verifyENOENTSync(status, parsed) { if (isWin && status === 1 && !parsed.file) { return notFoundError(parsed.original, "spawnSync"); } return null; } module2.exports = { hookChildProcess, verifyENOENT, verifyENOENTSync, notFoundError }; } }); // ../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/index.js var require_cross_spawn = __commonJS({ "../node_modules/.pnpm/cross-spawn@7.0.3/node_modules/cross-spawn/index.js"(exports, module2) { "use strict"; var cp = require("child_process"); var parse = require_parse(); var enoent = require_enoent(); function spawn(command, args, options) { const parsed = parse(command, args, options); const spawned = cp.spawn(parsed.command, parsed.args, parsed.options); enoent.hookChildProcess(spawned, parsed); return spawned; } function spawnSync(command, args, options) { const parsed = parse(command, args, options); const result = cp.spawnSync(parsed.command, parsed.args, parsed.options); result.error = result.error || enoent.verifyENOENTSync(result.status, parsed); return result; } module2.exports = spawn; module2.exports.spawn = spawn; module2.exports.sync = spawnSync; module2.exports._parse = parse; module2.exports._enoent = enoent; } }); // ../node_modules/.pnpm/signal-exit@3.0.7/node_modules/signal-exit/signals.js var require_signals = __commonJS({ "../node_modules/.pnpm/signal-exit@3.0.7/node_modules/signal-exit/signals.js"(exports, module2) { module2.exports = [ "SIGABRT", "SIGALRM", "SIGHUP", "SIGINT", "SIGTERM" ]; if (process.platform !== "win32") { module2.exports.push( "SIGVTALRM", "SIGXCPU", "SIGXFSZ", "SIGUSR2", "SIGTRAP", "SIGSYS", "SIGQUIT", "SIGIOT" ); } if (process.platform === "linux") { module2.exports.push( "SIGIO", "SIGPOLL", "SIGPWR", "SIGSTKFLT", "SIGUNUSED" ); } } }); // ../node_modules/.pnpm/signal-exit@3.0.7/node_modules/signal-exit/index.js var require_signal_exit = __commonJS({ "../node_modules/.pnpm/signal-exit@3.0.7/node_modules/signal-exit/index.js"(exports, module2) { var process5 = global.process; var processOk = function(process6) { return process6 && typeof process6 === "object" && typeof process6.removeListener === "function" && typeof process6.emit === "function" && typeof process6.reallyExit === "function" && typeof process6.listeners === "function" && typeof process6.kill === "function" && typeof process6.pid === "number" && typeof process6.on === "function"; }; if (!processOk(process5)) { module2.exports = function() { return function() { }; }; } else { assert = require("assert"); signals = require_signals(); isWin = /^win/i.test(process5.platform); EE = require("events"); if (typeof EE !== "function") { EE = EE.EventEmitter; } if (process5.__signal_exit_emitter__) { emitter = process5.__signal_exit_emitter__; } else { emitter = process5.__signal_exit_emitter__ = new EE(); emitter.count = 0; emitter.emitted = {}; } if (!emitter.infinite) { emitter.setMaxListeners(Infinity); emitter.infinite = true; } module2.exports = function(cb, opts) { if (!processOk(global.process)) { return function() { }; } assert.equal(typeof cb, "function", "a callback must be provided for exit handler"); if (loaded === false) { load(); } var ev = "exit"; if (opts && opts.alwaysLast) { ev = "afterexit"; } var remove = function() { emitter.removeListener(ev, cb); if (emitter.listeners("exit").length === 0 && emitter.listeners("afterexit").length === 0) { unload(); } }; emitter.on(ev, cb); return remove; }; unload = function unload2() { if (!loaded || !processOk(global.process)) { return; } loaded = false; signals.forEach(function(sig) { try { process5.removeListener(sig, sigListeners[sig]); } catch (er) { } }); process5.emit = originalProcessEmit; process5.reallyExit = originalProcessReallyExit; emitter.count -= 1; }; module2.exports.unload = unload; emit = function emit2(event, code, signal) { if (emitter.emitted[event]) { return; } emitter.emitted[event] = true; emitter.emit(event, code, signal); }; sigListeners = {}; signals.forEach(function(sig) { sigListeners[sig] = function listener() { if (!processOk(global.process)) { return; } var listeners = process5.listeners(sig); if (listeners.length === emitter.count) { unload(); emit("exit", null, sig); emit("afterexit", null, sig); if (isWin && sig === "SIGHUP") { sig = "SIGINT"; } process5.kill(process5.pid, sig); } }; }); module2.exports.signals = function() { return signals; }; loaded = false; load = function load2() { if (loaded || !processOk(global.process)) { return; } loaded = true; emitter.count += 1; signals = signals.filter(function(sig) { try { process5.on(sig, sigListeners[sig]); return true; } catch (er) { return false; } }); process5.emit = processEmit; process5.reallyExit = processReallyExit; }; module2.exports.load = load; originalProcessReallyExit = process5.reallyExit; processReallyExit = function processReallyExit2(code) { if (!processOk(global.process)) { return; } process5.exitCode = code || 0; emit("exit", process5.exitCode, null); emit("afterexit", process5.exitCode, null); originalProcessReallyExit.call(process5, process5.exitCode); }; originalProcessEmit = process5.emit; processEmit = function processEmit2(ev, arg) { if (ev === "exit" && processOk(global.process)) { if (arg !== void 0) { process5.exitCode = arg; } var ret = originalProcessEmit.apply(this, arguments); emit("exit", process5.exitCode, null); emit("afterexit", process5.exitCode, null); return ret; } else { return originalProcessEmit.apply(this, arguments); } }; } var assert; var signals; var isWin; var EE; var emitter; var unload; var emit; var sigListeners; var loaded; var load; var originalProcessReallyExit; var processReallyExit; var originalProcessEmit; var processEmit; } }); // ../node_modules/.pnpm/get-stream@6.0.1/node_modules/get-stream/buffer-stream.js var require_buffer_stream = __commonJS({ "../node_modules/.pnpm/get-stream@6.0.1/node_modules/get-stream/buffer-stream.js"(exports, module2) { "use strict"; var { PassThrough: PassThroughStream } = require("stream"); module2.exports = (options) => { options = { ...options }; const { array } = options; let { encoding } = options; const isBuffer = encoding === "buffer"; let objectMode = false; if (array) { objectMode = !(encoding || isBuffer); } else { encoding = encoding || "utf8"; } if (isBuffer) { encoding = null; } const stream = new PassThroughStream({ objectMode }); if (encoding) { stream.setEncoding(encoding); } let length = 0; const chunks = []; stream.on("data", (chunk) => { chunks.push(chunk); if (objectMode) { length = chunks.length; } else { length += chunk.length; } }); stream.getBufferedValue = () => { if (array) { return chunks; } return isBuffer ? Buffer.concat(chunks, length) : chunks.join(""); }; stream.getBufferedLength = () => length; return stream; }; } }); // ../node_modules/.pnpm/get-stream@6.0.1/node_modules/get-stream/index.js var require_get_stream = __commonJS({ "../node_modules/.pnpm/get-stream@6.0.1/node_modules/get-stream/index.js"(exports, module2) { "use strict"; var { constants: BufferConstants } = require("buffer"); var stream = require("stream"); var { promisify } = require("util"); var bufferStream = require_buffer_stream(); var streamPipelinePromisified = promisify(stream.pipeline); var MaxBufferError = class extends Error { constructor() { super("maxBuffer exceeded"); this.name = "MaxBufferError"; } }; async function getStream2(inputStream, options) { if (!inputStream) { throw new Error("Expected a stream"); } options = { maxBuffer: Infinity, ...options }; const { maxBuffer } = options; const stream2 = bufferStream(options); await new Promise((resolve4, reject) => { const rejectPromise = (error) => { if (error && stream2.getBufferedLength() <= BufferConstants.MAX_LENGTH) { error.bufferedData = stream2.getBufferedValue(); } reject(error); }; (async () => { try { await streamPipelinePromisified(inputStream, stream2); resolve4(); } catch (error) { rejectPromise(error); } })(); stream2.on("data", () => { if (stream2.getBufferedLength() > maxBuffer) { rejectPromise(new MaxBufferError()); } }); }); return stream2.getBufferedValue(); } module2.exports = getStream2; module2.exports.buffer = (stream2, options) => getStream2(stream2, { ...options, encoding: "buffer" }); module2.exports.array = (stream2, options) => getStream2(stream2, { ...options, array: true }); module2.exports.MaxBufferError = MaxBufferError; } }); // ../node_modules/.pnpm/merge-stream@2.0.0/node_modules/merge-stream/index.js var require_merge_stream = __commonJS({ "../node_modules/.pnpm/merge-stream@2.0.0/node_modules/merge-stream/index.js"(exports, module2) { "use strict"; var { PassThrough } = require("stream"); module2.exports = function() { var sources = []; var output = new PassThrough({ objectMode: true }); output.setMaxListeners(0); output.add = add; output.isEmpty = isEmpty; output.on("unpipe", remove); Array.prototype.slice.call(arguments).forEach(add); return output; function add(source) { if (Array.isArray(source)) { source.forEach(add); return this; } sources.push(source); source.once("end", remove.bind(null, source)); source.once("error", output.emit.bind(output, "error")); source.pipe(output, { end: false }); return this; } function isEmpty() { return sources.length == 0; } function remove(source) { sources = sources.filter(function(it) { return it !== source; }); if (!sources.length && output.readable) { output.end(); } } }; } }); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.prod.js var require_shared_cjs_prod = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.prod.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const set = new Set(str.split(",")); return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); } var EMPTY_OBJ = {}; var EMPTY_ARR = []; var NOOP = () => { }; var NO = () => false; var isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => toTypeString(val) === "[object Date]"; var isRegExp = (val) => toTypeString(val) === "[object RegExp]"; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap( ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" ); var isBuiltInDirective = /* @__PURE__ */ makeMap( "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" ); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction( (str) => str.replace(hyphenateRE, "-$1").toLowerCase() ); var capitalize = cacheStringFunction((str) => { return str.charAt(0).toUpperCase() + str.slice(1); }); var toHandlerKey = cacheStringFunction((str) => { const s = str ? `on${capitalize(str)}` : ``; return s; }); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var looseToNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var toNumber = (val) => { const n = isString(val) ? Number(val) : NaN; return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; var identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; function genPropsAccessExp(name) { return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`; } var PatchFlags = { "TEXT": 1, "1": "TEXT", "CLASS": 2, "2": "CLASS", "STYLE": 4, "4": "STYLE", "PROPS": 8, "8": "PROPS", "FULL_PROPS": 16, "16": "FULL_PROPS", "NEED_HYDRATION": 32, "32": "NEED_HYDRATION", "STABLE_FRAGMENT": 64, "64": "STABLE_FRAGMENT", "KEYED_FRAGMENT": 128, "128": "KEYED_FRAGMENT", "UNKEYED_FRAGMENT": 256, "256": "UNKEYED_FRAGMENT", "NEED_PATCH": 512, "512": "NEED_PATCH", "DYNAMIC_SLOTS": 1024, "1024": "DYNAMIC_SLOTS", "DEV_ROOT_FRAGMENT": 2048, "2048": "DEV_ROOT_FRAGMENT", "HOISTED": -1, "-1": "HOISTED", "BAIL": -2, "-2": "BAIL" }; var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `NEED_HYDRATION`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var ShapeFlags = { "ELEMENT": 1, "1": "ELEMENT", "FUNCTIONAL_COMPONENT": 2, "2": "FUNCTIONAL_COMPONENT", "STATEFUL_COMPONENT": 4, "4": "STATEFUL_COMPONENT", "TEXT_CHILDREN": 8, "8": "TEXT_CHILDREN", "ARRAY_CHILDREN": 16, "16": "ARRAY_CHILDREN", "SLOTS_CHILDREN": 32, "32": "SLOTS_CHILDREN", "TELEPORT": 64, "64": "TELEPORT", "SUSPENSE": 128, "128": "SUSPENSE", "COMPONENT_SHOULD_KEEP_ALIVE": 256, "256": "COMPONENT_SHOULD_KEEP_ALIVE", "COMPONENT_KEPT_ALIVE": 512, "512": "COMPONENT_KEPT_ALIVE", "COMPONENT": 6, "6": "COMPONENT" }; var SlotFlags = { "STABLE": 1, "1": "STABLE", "DYNAMIC": 2, "2": "DYNAMIC", "FORWARDED": 3, "3": "FORWARDED" }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; var isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); var isGloballyWhitelisted = isGloballyAllowed; var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push( `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` ); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max( 1, end > count ? lineLength - pad : end - start ); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value) || isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:([^]+)/; var styleCommentRE = /\/\*[^]*?\*\//g; function parseStringStyle(cssText) { const ret = {}; cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles3) { let ret = ""; if (!styles3 || isString(styles3)) { return ret; } for (const key in styles3) { const value = styles3[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number") { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap( specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` ); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isKnownHtmlAttr = /* @__PURE__ */ makeMap( `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` ); var isKnownSvgAttr = /* @__PURE__ */ makeMap( `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` ); function isRenderableAttrValue(value) { if (value == null) { return false; } const type = typeof value; return type === "string" || type === "number" || type === "boolean"; } var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce( (entries, [key, val2], i) => { entries[stringifySymbol(key, i) + " =>"] = val2; return entries; }, {} ) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) }; } else if (isSymbol(val)) { return stringifySymbol(val); } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var stringifySymbol = (v, i = "") => { var _a; return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.PatchFlags = PatchFlags; exports.ShapeFlags = ShapeFlags; exports.SlotFlags = SlotFlags; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.genPropsAccessExp = genPropsAccessExp; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyAllowed = isGloballyAllowed; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isMathMLTag = isMathMLTag; exports.isModelListener = isModelListener; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isRegExp = isRegExp; exports.isRenderableAttrValue = isRenderableAttrValue; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.looseToNumber = looseToNumber; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.js var require_shared_cjs = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const set = new Set(str.split(",")); return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); } var EMPTY_OBJ = Object.freeze({}); var EMPTY_ARR = Object.freeze([]); var NOOP = () => { }; var NO = () => false; var isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => toTypeString(val) === "[object Date]"; var isRegExp = (val) => toTypeString(val) === "[object RegExp]"; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap( ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" ); var isBuiltInDirective = /* @__PURE__ */ makeMap( "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" ); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction( (str) => str.replace(hyphenateRE, "-$1").toLowerCase() ); var capitalize = cacheStringFunction((str) => { return str.charAt(0).toUpperCase() + str.slice(1); }); var toHandlerKey = cacheStringFunction((str) => { const s = str ? `on${capitalize(str)}` : ``; return s; }); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var looseToNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var toNumber = (val) => { const n = isString(val) ? Number(val) : NaN; return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; var identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; function genPropsAccessExp(name) { return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`; } var PatchFlags = { "TEXT": 1, "1": "TEXT", "CLASS": 2, "2": "CLASS", "STYLE": 4, "4": "STYLE", "PROPS": 8, "8": "PROPS", "FULL_PROPS": 16, "16": "FULL_PROPS", "NEED_HYDRATION": 32, "32": "NEED_HYDRATION", "STABLE_FRAGMENT": 64, "64": "STABLE_FRAGMENT", "KEYED_FRAGMENT": 128, "128": "KEYED_FRAGMENT", "UNKEYED_FRAGMENT": 256, "256": "UNKEYED_FRAGMENT", "NEED_PATCH": 512, "512": "NEED_PATCH", "DYNAMIC_SLOTS": 1024, "1024": "DYNAMIC_SLOTS", "DEV_ROOT_FRAGMENT": 2048, "2048": "DEV_ROOT_FRAGMENT", "HOISTED": -1, "-1": "HOISTED", "BAIL": -2, "-2": "BAIL" }; var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `NEED_HYDRATION`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var ShapeFlags = { "ELEMENT": 1, "1": "ELEMENT", "FUNCTIONAL_COMPONENT": 2, "2": "FUNCTIONAL_COMPONENT", "STATEFUL_COMPONENT": 4, "4": "STATEFUL_COMPONENT", "TEXT_CHILDREN": 8, "8": "TEXT_CHILDREN", "ARRAY_CHILDREN": 16, "16": "ARRAY_CHILDREN", "SLOTS_CHILDREN": 32, "32": "SLOTS_CHILDREN", "TELEPORT": 64, "64": "TELEPORT", "SUSPENSE": 128, "128": "SUSPENSE", "COMPONENT_SHOULD_KEEP_ALIVE": 256, "256": "COMPONENT_SHOULD_KEEP_ALIVE", "COMPONENT_KEPT_ALIVE": 512, "512": "COMPONENT_KEPT_ALIVE", "COMPONENT": 6, "6": "COMPONENT" }; var SlotFlags = { "STABLE": 1, "1": "STABLE", "DYNAMIC": 2, "2": "DYNAMIC", "FORWARDED": 3, "3": "FORWARDED" }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; var isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); var isGloballyWhitelisted = isGloballyAllowed; var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push( `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` ); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max( 1, end > count ? lineLength - pad : end - start ); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value) || isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:([^]+)/; var styleCommentRE = /\/\*[^]*?\*\//g; function parseStringStyle(cssText) { const ret = {}; cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles3) { let ret = ""; if (!styles3 || isString(styles3)) { return ret; } for (const key in styles3) { const value = styles3[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number") { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap( specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` ); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isKnownHtmlAttr = /* @__PURE__ */ makeMap( `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` ); var isKnownSvgAttr = /* @__PURE__ */ makeMap( `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` ); function isRenderableAttrValue(value) { if (value == null) { return false; } const type = typeof value; return type === "string" || type === "number" || type === "boolean"; } var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce( (entries, [key, val2], i) => { entries[stringifySymbol(key, i) + " =>"] = val2; return entries; }, {} ) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) }; } else if (isSymbol(val)) { return stringifySymbol(val); } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var stringifySymbol = (v, i = "") => { var _a; return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.PatchFlags = PatchFlags; exports.ShapeFlags = ShapeFlags; exports.SlotFlags = SlotFlags; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.genPropsAccessExp = genPropsAccessExp; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyAllowed = isGloballyAllowed; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isMathMLTag = isMathMLTag; exports.isModelListener = isModelListener; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isRegExp = isRegExp; exports.isRenderableAttrValue = isRenderableAttrValue; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.looseToNumber = looseToNumber; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/index.js var require_shared = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/index.js"(exports, module2) { "use strict"; if (process.env.NODE_ENV === "production") { module2.exports = require_shared_cjs_prod(); } else { module2.exports = require_shared_cjs(); } } }); // ../node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.4.27_@vue+shared@3.4.25/node_modules/@vue-reactivity/watch/dist/index.js var require_dist = __commonJS({ "../node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.4.27_@vue+shared@3.4.25/node_modules/@vue-reactivity/watch/dist/index.js"(exports, module2) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; var __hasOwnProp2 = Object.prototype.hasOwnProperty; var __markAsModule = (target) => __defProp2(target, "__esModule", { value: true }); var __export = (target, all) => { for (var name in all) __defProp2(target, name, { get: all[name], enumerable: true }); }; var __reExport = (target, module22, copyDefault, desc) => { if (module22 && typeof module22 === "object" || typeof module22 === "function") { for (let key of __getOwnPropNames2(module22)) if (!__hasOwnProp2.call(target, key) && (copyDefault || key !== "default")) __defProp2(target, key, { get: () => module22[key], enumerable: !(desc = __getOwnPropDesc2(module22, key)) || desc.enumerable }); } return target; }; var __toCommonJS = /* @__PURE__ */ ((cache) => { return (module22, temp) => { return cache && cache.get(module22) || (temp = __reExport(__markAsModule({}), module22, 1), cache && cache.set(module22, temp), temp); }; })(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0); var src_exports = {}; __export(src_exports, { watch: () => watch2, watchEffect: () => watchEffect }); var import_reactivity3 = require("@vue/reactivity"); var import_shared2 = require_shared(); var import_shared = require_shared(); function callWithErrorHandling(fn, type, args) { let res; try { res = args ? fn(...args) : fn(); } catch (err) { handleError(err, type); } return res; } function callWithAsyncErrorHandling(fn, type, args) { if ((0, import_shared.isFunction)(fn)) { const res = callWithErrorHandling(fn, type, args); if (res && (0, import_shared.isPromise)(res)) { res.catch((err) => { handleError(err, type); }); } return res; } const values = []; for (let i = 0; i < fn.length; i++) values.push(callWithAsyncErrorHandling(fn[i], type, args)); return values; } function handleError(err, type) { console.error(new Error(`[@vue-reactivity/watch]: ${type}`)); console.error(err); } function warn(message) { console.warn(createError(message)); } function createError(message) { return new Error(`[reactivue]: ${message}`); } var INITIAL_WATCHER_VALUE = {}; function watchEffect(effect, options) { return doWatch(effect, null, options); } function watch2(source, cb, options) { return doWatch(source, cb, options); } function doWatch(source, cb, { immediate, deep, flush } = {}) { let getter; let forceTrigger = false; let isMultiSource = false; if ((0, import_reactivity3.isRef)(source)) { getter = () => source.value; forceTrigger = (0, import_reactivity3.isShallow)(source); } else if ((0, import_reactivity3.isReactive)(source)) { getter = () => source; deep = true; } else if ((0, import_shared2.isArray)(source)) { isMultiSource = true; forceTrigger = source.some(import_reactivity3.isReactive); getter = () => source.map((s) => { if ((0, import_reactivity3.isRef)(s)) return s.value; else if ((0, import_reactivity3.isReactive)(s)) return traverse(s); else if ((0, import_shared2.isFunction)(s)) return callWithErrorHandling(s, "watch getter"); else return warn("invalid source"); }); } else if ((0, import_shared2.isFunction)(source)) { if (cb) { getter = () => callWithErrorHandling(source, "watch getter"); } else { getter = () => { if (cleanup) cleanup(); return callWithAsyncErrorHandling(source, "watch callback", [onCleanup]); }; } } else { getter = import_shared2.NOOP; } if (cb && deep) { const baseGetter = getter; getter = () => traverse(baseGetter()); } let cleanup; let onCleanup = (fn) => { cleanup = effect.onStop = () => { callWithErrorHandling(fn, "watch cleanup"); }; }; let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE; const job = () => { if (!effect.active) return; if (cb) { const newValue = effect.run(); if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => (0, import_shared2.hasChanged)(v, oldValue[i])) : (0, import_shared2.hasChanged)(newValue, oldValue))) { if (cleanup) cleanup(); callWithAsyncErrorHandling(cb, "watch value", [ newValue, oldValue === INITIAL_WATCHER_VALUE ? void 0 : oldValue, onCleanup ]); oldValue = newValue; } } else { effect.run(); } }; job.allowRecurse = !!cb; let scheduler; if (flush === "sync") { scheduler = job; } else { scheduler = () => { job(); }; } const effect = new import_reactivity3.ReactiveEffect(getter, scheduler); if (cb) { if (immediate) job(); else oldValue = effect.run(); } else { effect.run(); } return () => effect.stop(); } function traverse(value, seen = /* @__PURE__ */ new Set()) { if (!(0, import_shared2.isObject)(value) || seen.has(value)) return value; seen.add(value); if ((0, import_shared2.isArray)(value)) { for (let i = 0; i < value.length; i++) traverse(value[i], seen); } else if (value instanceof Map) { value.forEach((_, key) => { traverse(value.get(key), seen); }); } else if (value instanceof Set) { value.forEach((v) => { traverse(v, seen); }); } else { for (const key of Object.keys(value)) traverse(value[key], seen); } return value; } module2.exports = __toCommonJS(src_exports); } }); // src/index.ts var import_reflect_metadata = require("reflect-metadata"); var dotenv = __toESM(require("dotenv")); var import_tsyringe19 = require("tsyringe"); var import_http = __toESM(require("http")); // ../server-shared/src/constants.ts var HEXO_BASE_DIR_KEY = "hexo-basedir"; var HEXO_OPTIONS_KEY = "hexo-options"; var BRIEF_LENGTH = 500; var HEXON_PORT_KEY = "@hexon/port"; var HEXON_DEFAULT_PORT = 5777; // ../server-shared/src/storage-service.ts var import_tsyringe2 = require("tsyringe"); var import_fs = require("fs"); var import_path = require("path"); var import_simple_json_db = __toESM(require("simple-json-db")); // ../server-shared/src/log-service.ts var import_tsyringe = require("tsyringe"); // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; var styles = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; var modifierNames = Object.keys(styles.modifier); var foregroundColorNames = Object.keys(styles.color); var backgroundColorNames = Object.keys(styles.bgColor); var colorNames = [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\x1B[${style[0]}m`, close: `\x1B[${style[1]}m` }; group[styleName] = styles[styleName]; codes.set(style[0], style[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles, "codes", { value: codes, enumerable: false }); styles.color.close = "\x1B[39m"; styles.bgColor.close = "\x1B[49m"; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value(hex) { const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ integer >> 16 & 255, integer >> 8 & 255, integer & 255 ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false } }); return styles; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/supports-color/index.js var import_node_process = __toESM(require("process"), 1); var import_node_os = __toESM(require("os"), 1); var import_node_tty = __toESM(require("tty"), 1); function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = import_node_process.default; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (import_node_process.default.platform === "win32") { const osRelease = import_node_os.default.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if ("GITHUB_ACTIONS" in env || "GITEA_ACTIONS" in env) { return 3; } if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === "truecolor") { return 3; } if (env.TERM === "xterm-kitty") { return 3; } if ("TERM_PROGRAM" in env) { const version = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { return version >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; } } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, { streamIsTTY: stream && stream.isTTY, ...options }); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }), stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) }) }; var supports_color_default = supportsColor; // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.slice(endIndex, index) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles2 = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style] of Object.entries(ansi_styles_default)) { styles2[styleName] = { get() { const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles2.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles2[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles2[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, { ...styles2, level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } }); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self2, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self2; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self2, string) => { if (self2.level <= 0 || !string) { return self2[IS_EMPTY] ? "" : string; } let styler = self2[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles2); var chalk = createChalk(); var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // ../server-shared/src/log-service.ts var import_dayjs = __toESM(require("dayjs")); // ../shared/src/constants.ts var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss"; // ../server-shared/src/log-service.ts var LogService = class { constructor() { this.scope = ""; this.dateFormat = DATE_FORMAT; } _prefix(type) { let prefix = ""; this.scope && (prefix += source_default[type].bold(`[${this.scope}]`)); prefix += source_default.blue(`[${(0, import_dayjs.default)().format(this.dateFormat)}]`); return prefix; } _log(...args) { console.log(...args); } _error(...args) { console.error(...args); } setScope(scope) { this.scope = scope; } log(...args) { this._log(this._prefix("green"), ...args); } error(...args) { this._error(this._prefix("red"), ...args); } logWithUser(user, ...args) { this._log( this._prefix("green") + source_default.yellow.dim(`[${user.username}:${user.slug}]`), ...args ); } static create(scope) { const instance = import_tsyringe.container.resolve(LogService); instance.setScope(scope); return instance; } }; // ../server-shared/src/storage-service.ts var defaultRoot = (0, import_path.resolve)(__dirname, "../../server/data"); var defaultFilename = "common.db"; var StorageService = class { constructor(_logService) { this._logService = _logService; this._root = defaultRoot; this._filename = defaultFilename; this._logService.setScope("storage-service"); if (!(0, import_fs.existsSync)(this._root)) (0, import_fs.mkdirSync)(this._root); this._logService.log(`init storage service with root: ${this._root}`); this._db = new import_simple_json_db.default((0, import_path.resolve)(this._root, this._filename)); } get(key) { return this._db.get(key); } set(key, value) { this._db.set(key, value); } delete(key) { return this._db.delete(key); } }; StorageService = __decorateClass([ (0, import_tsyringe2.singleton)(), __decorateParam(0, (0, import_tsyringe2.inject)(LogService)) ], StorageService); // src/services/hexo-instance-service.ts var import_tsyringe3 = require("tsyringe"); var import_hexo = __toESM(require("hexo")); var import_path4 = __toESM(require("path")); // ../server-shared/src/utils.ts var import_fs2 = __toESM(require("fs")); var import_path2 = __toESM(require("path")); function isBlog(cwd) { var _a; let file; try { file = import_fs2.default.readFileSync(import_path2.default.join(cwd, "package.json"), { encoding: "utf-8" }); import_fs2.default.readFileSync(import_path2.default.join(cwd, "_config.yml"), { encoding: "utf-8" }); } catch (err) { if (err.code === "ENOENT") { return false; } throw err; } const packageJSON = JSON.parse(file); if (!((_a = packageJSON == null ? void 0 : packageJSON.dependencies) == null ? void 0 : _a.hexo)) return false; return true; } function toRealPath(value) { return import_path2.default.isAbsolute(value) ? value : import_path2.default.resolve(process.cwd(), "../..", value); } // src/utils.ts var import_debug = __toESM(require("debug")); var import_path3 = __toESM(require("path")); var DEV = process.env.NODE_ENV !== "production"; function expandHomeDir(fullpath) { const homedir = process.env[process.platform == "win32" ? "USERPROFILE" : "HOME"]; if (!fullpath) return fullpath; if (fullpath == "~") return homedir; if (fullpath.slice(0, 2) != "~/") return fullpath; return import_path3.default.join(homedir, fullpath.slice(2)); } // src/errors.ts var import_http_errors = require("http-errors"); var PostOrPageNotFoundError = class extends import_http_errors.NotFound { constructor(type, msg) { super(msg != null ? msg : `${type} not found`); this.id = "PostOrPageNotFoundError"; } }; var HexoInitError = class extends import_http_errors.InternalServerError { constructor() { super(...arguments); this.id = "HexoInitError"; } }; var InvalidOptionsError = class extends import_http_errors.BadRequest { constructor(message, id = "InvalidOptionsError") { super(message); this.id = id; } }; var ScriptError = class extends import_http_errors.InternalServerError { constructor(message, id = "ScriptError") { super(message); this.id = id; } }; // src/services/hexo-instance-service.ts var HexoInstanceService = class { constructor(_logService, _storageService) { this._logService = _logService; this._storageService = _storageService; this._options = null; this._base = null; this._hexo = null; this._ready = false; this._promise = null; this._logService.setScope("hexo-instance-service"); } _withOptionsOverrides(options) { return { ...options, draft: true, drafts: true }; } _setHexoBase() { const base = this._storageService.get(HEXO_BASE_DIR_KEY); const base_dir = import_path4.default.resolve(__dirname, toRealPath(base)); if (!isBlog(base_dir)) throw new Error(`"${base_dir}" is not a hexo blog folder`); this._base = base_dir; } _setOptions() { this._options = this._storageService.get(HEXO_OPTIONS_KEY) || {}; this._options.silent = DEV ? false : this._options.silent; } _createHexoInstance() { if (!this._base) throw new Error("please set hexo root first"); this._hexo = new import_hexo.default( this._base, this._withOptionsOverrides(this._options) ); } async _init() { this._logService.log("real init start"); this._ready = false; await this._setHexoBase(); await this._setOptions(); await this._createHexoInstance(); await this._hexo.init(); await this._hexo.watch(); this._ready = true; this._logService.log("real init finished"); } async setOptions(options) { this._storageService.set( HEXO_OPTIONS_KEY, options ); this._logService.log("options set"); } async _tryInit(count = HexoInstanceService.MAX_RETRY) { try { await this._init(); HexoInstanceService.INITING = false; } catch (err) { this._logService.error(err); this._logService.error(`error when init hexo instance. `); this._logService.error( `retry in ${HexoInstanceService.RETRY_INTERVAL} ms.`, `${count} retry left` ); if (count) return new Promise((resolve4) => { setTimeout(() => { resolve4(this._tryInit(count - 1)); }, HexoInstanceService.RETRY_INTERVAL); }); else { HexoInstanceService.INITING = false; throw new HexoInitError(String(err)); } } } async init() { if (!HexoInstanceService.INITING) this._promise = this._tryInit(); return this._promise; } async getBaseDir() { if (!this._ready) await this.init(); return this._base; } async getInstance() { if (!this._ready) await this.init(); this._logService.log("instance required"); return this._hexo; } async getInstanceWithOriginOptions(genOptions = (o) => o) { if (!this._ready) await this.init(); const newOptions = genOptions(this._options); const hexo = new import_hexo.default(this._base, newOptions); await hexo.init(); await hexo.watch(); HexoInstanceService.TO_BE_CLEANED++; this._logService.log("instance with options required"); this._logService.log( `${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned` ); const cleanup = async () => { await hexo.unwatch(); HexoInstanceService.TO_BE_CLEANED--; this._logService.log("instance with options cleaned"); if (HexoInstanceService.TO_BE_CLEANED === 0) { this._logService.log("all instances have been cleaned"); } else { this._logService.log( `${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned` ); } }; return { hexo, cleanup }; } async runBetweenReload(fn) { if (!this._ready) await this.init(); const unload = async () => { await this._hexo.unwatch(); }; const load = async () => { await this._hexo.watch(); HexoInstanceService.INITING = false; }; const markHexoInitError = (err) => { this._ready = false; HexoInstanceService.INITING = false; }; HexoInstanceService.INITING = true; await unload().catch(markHexoInitError); const res = await Promise.resolve(fn()); await load().catch(markHexoInitError); return res; } }; HexoInstanceService.INITING = false; HexoInstanceService.PENDING_COUNT = 0; HexoInstanceService.RETRY_INTERVAL = 1e3; HexoInstanceService.MAX_RETRY = 2; HexoInstanceService.CURRENT_RETRY = 0; HexoInstanceService.TO_BE_CLEANED = 0; HexoInstanceService = __decorateClass([ (0, import_tsyringe3.injectable)(), (0, import_tsyringe3.singleton)(), __decorateParam(0, (0, import_tsyringe3.inject)(LogService)), __decorateParam(1, (0, import_tsyringe3.inject)(StorageService)) ], HexoInstanceService); // src/app.ts var import_tsyringe17 = require("tsyringe"); var import_koa = __toESM(require("koa")); var import_koa_bodyparser = __toESM(require("koa-bodyparser")); var import_koa_compress = __toESM(require("koa-compress")); var import_koa_logger = __toESM(require("koa-logger")); var import_koa_mount = __toESM(require("koa-mount")); // src/middlewares/auth.ts var import_koa_authentication = require("@winwin/koa-authentication"); var import_tsyringe6 = require("tsyringe"); // ../server-shared/src/account-storage-service.ts var import_crypto_js = require("crypto-js"); var import_tsyringe4 = require("tsyringe"); var AccountService = class { constructor(_storage, _logService) { this._storage = _storage; this._logService = _logService; this._logService.setScope("account-service"); } _encrypt(raw) { return (0, import_crypto_js.SHA1)(raw).toString(); } _toStorage(info) { this._storage.set(AccountService.KEY, info); } _fromStorage() { const { username = "", password = "" } = this._storage.get(AccountService.KEY) || {}; return { username, password }; } setUserInfo(username, password) { this._storage.set(AccountService.KEY, { username, password: this._encrypt(password) }); this._logService.log("set user info: ", username); } getUsername() { return this._fromStorage().username; } setUsername(username) { const info = this._fromStorage(); info.username = username; this._toStorage(info); this._logService.log("set username: ", username); } setPassword(password) { const info = this._fromStorage(); info.password = this._encrypt(password); this._toStorage(info); this._logService.log("set password"); } setEncryptedPassword(password) { const info = this._fromStorage(); info.password = password; this._toStorage(info); this._logService.log("set encrypted password"); } verify(username, password) { const info = this._fromStorage(); if (username !== info.username) { return false; } if (this._encrypt(password) !== info.password) { return false; } return true; } }; AccountService.KEY = "userinfo"; AccountService = __decorateClass([ (0, import_tsyringe4.injectable)(), (0, import_tsyringe4.singleton)(), __decorateParam(0, (0, import_tsyringe4.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe4.inject)(LogService)) ], AccountService); // src/services/auth-storage-service.ts var import_tsyringe5 = require("tsyringe"); var AuthStorageService = class { constructor(_storage, _logService) { this._storage = _storage; this._logService = _logService; this._logService.setScope("auth-storage-service"); } _toStorage(info) { this._storage.set(AuthStorageService.KEY, info); } _fromStorage() { const { secret = "secret", expiresIn = "1h", refreshableIn = "7d" } = this._storage.get(AuthStorageService.KEY) || {}; return { secret, expiresIn, refreshableIn }; } setAuthInfo(info) { this._toStorage(info); this._logService.log(`set auth info`); } getSecret() { const s = this._fromStorage().secret; this._logService.log(`get secret`); return s; } getAuthInfo() { const s = this._fromStorage(); this._logService.log(`get auth info`); return s; } }; AuthStorageService.KEY = "authinfo"; AuthStorageService = __decorateClass([ (0, import_tsyringe5.injectable)(), (0, import_tsyringe5.singleton)(), __decorateParam(0, (0, import_tsyringe5.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe5.inject)(LogService)) ], AuthStorageService); // src/middlewares/auth.ts var auth = (0, import_koa_authentication.createAuth)({ verify(username, password) { const logger2 = import_tsyringe6.container.resolve(LogService); const account = import_tsyringe6.container.resolve(AccountService); const res = account.verify(username, password); logger2.log(`verify ${res ? "success" : "failed"} for ${username}`); return res; }, secret() { return import_tsyringe6.container.resolve(AuthStorageService).getSecret(); } }); auth.router.post("/password", auth.auth, (ctx) => { const account = import_tsyringe6.container.resolve(AccountService); const { oldPassword, password } = ctx.request.body; const verified = account.verify(ctx.state.user.username, oldPassword); if (!verified) throw new import_koa_authentication.AuthenticationError(); account.setPassword(password); ctx.status = 200; }); auth.router.post( "/username", auth.auth, (ctx, next) => { const account = import_tsyringe6.container.resolve(AccountService); const { username } = ctx.request.body; account.setUsername(username); ctx.state.user.username = username; return next(); }, auth.cookie, (ctx) => ctx.status = 200 ); // src/middlewares/statics.ts var import_path5 = __toESM(require("path")); var import_koa_static = __toESM(require("koa-static")); var ROOT = import_path5.default.resolve(process.cwd(), "../client/dist"); var statics = (0, import_koa_static.default)(ROOT, { setHeaders: (res, fullpath) => { const isHtml = import_path5.default.extname(fullpath).toLowerCase() === ".html"; if (isHtml) res.setHeader("Cache-Control", "no-cache"); else res.setHeader("Cache-Control", "max-age=31536000"); } }); // src/lib/http-secure.ts var import_crypto = __toESM(require("crypto")); var import_crypto_js2 = __toESM(require("crypto-js")); var import_node_jsencrypt = __toESM(require("node-jsencrypt")); function secure(enable = () => true) { const { publicKey, privateKey } = import_crypto.default.generateKeyPairSync("rsa", { modulusLength: 2048, publicKeyEncoding: { type: "spki", format: "pem" }, privateKeyEncoding: { type: "pkcs1", format: "pem" } }); function decryptRSA(data) { const o = new import_node_jsencrypt.default(); o.setPrivateKey(privateKey); const res = o.decrypt(data); return res; } function decryptAES(data, key) { return import_crypto_js2.default.AES.decrypt(data, key).toString(import_crypto_js2.default.enc.Utf8); } function encryptAES(data, key) { return import_crypto_js2.default.AES.encrypt(data, key).toString(); } import_crypto_js2.default.AES.encrypt('"hi"', "123").toString(); function isGetPublicKeyRoute(ctx) { return ctx.request.path.startsWith("/publickey") && ctx.request.method === "GET"; } function stringifyData(data) { return JSON.stringify(data); } function parseData(data) { const str = data; return JSON.parse(str); } return async (ctx, next) => { if (typeof enable === "function" ? !enable() : !enable) { await next(); return; } if (isGetPublicKeyRoute(ctx)) { console.log(source_default.white("GET"), source_default.white.dim("/publickey")); ctx.body = publicKey; return; } const prefix = "/secure/"; const enced = decodeURIComponent(ctx.path.slice(prefix.length)); const secured = ctx.path.startsWith(prefix); if (secured) { const res = decryptRSA(enced); if (!res) { ctx.status = 403; ctx.body = { code: "EHTTPSECURE" }; return; } const decoded = JSON.parse(res); ctx.path = decoded.url; const key = decoded.key; ctx.originalUrl = "[secure]" + ctx.path; ctx.request.body = ctx.request.method !== "GET" && parseData(decryptAES(ctx.request.body.content, key)).data; await next(); const content = encryptAES(stringifyData({ data: ctx.body }), key); ctx.body = { content }; } else { await next(); return; } }; } var http_secure_default = secure; // src/routes/index.ts var import_router6 = __toESM(require("@koa/router")); // src/routes/health.ts var import_router = __toESM(require("@koa/router")); var router = new import_router.default(); router.get("/health", (ctx) => { ctx.status = 200; }); var health_default = router; // src/routes/hexo.ts var import_tsyringe10 = require("tsyringe"); var import_router2 = __toESM(require("@koa/router")); // src/services/hexo-service.ts var import_path8 = __toESM(require("path")); var import_tsyringe9 = require("tsyringe"); var import_fs4 = __toESM(require("fs")); // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/index.js var import_node_buffer = require("buffer"); var import_node_path2 = __toESM(require("path"), 1); var import_node_child_process = __toESM(require("child_process"), 1); var import_node_process3 = __toESM(require("process"), 1); var import_cross_spawn = __toESM(require_cross_spawn(), 1); // ../node_modules/.pnpm/strip-final-newline@3.0.0/node_modules/strip-final-newline/index.js function stripFinalNewline(input) { const LF = typeof input === "string" ? "\n" : "\n".charCodeAt(); const CR = typeof input === "string" ? "\r" : "\r".charCodeAt(); if (input[input.length - 1] === LF) { input = input.slice(0, -1); } if (input[input.length - 1] === CR) { input = input.slice(0, -1); } return input; } // ../node_modules/.pnpm/npm-run-path@5.3.0/node_modules/npm-run-path/index.js var import_node_process2 = __toESM(require("process"), 1); var import_node_path = __toESM(require("path"), 1); var import_node_url = require("url"); // ../node_modules/.pnpm/path-key@4.0.0/node_modules/path-key/index.js function pathKey(options = {}) { const { env: env2 = process.env, platform = process.platform } = options; if (platform !== "win32") { return "PATH"; } return Object.keys(env2).reverse().find((key) => key.toUpperCase() === "PATH") || "Path"; } // ../node_modules/.pnpm/npm-run-path@5.3.0/node_modules/npm-run-path/index.js var npmRunPath = ({ cwd = import_node_process2.default.cwd(), path: pathOption = import_node_process2.default.env[pathKey()], preferLocal = true, execPath = import_node_process2.default.execPath, addExecPath = true } = {}) => { const cwdString = cwd instanceof URL ? (0, import_node_url.fileURLToPath)(cwd) : cwd; const cwdPath = import_node_path.default.resolve(cwdString); const result = []; if (preferLocal) { applyPreferLocal(result, cwdPath); } if (addExecPath) { applyExecPath(result, execPath, cwdPath); } return [...result, pathOption].join(import_node_path.default.delimiter); }; var applyPreferLocal = (result, cwdPath) => { let previous; while (previous !== cwdPath) { result.push(import_node_path.default.join(cwdPath, "node_modules/.bin")); previous = cwdPath; cwdPath = import_node_path.default.resolve(cwdPath, ".."); } }; var applyExecPath = (result, execPath, cwdPath) => { const execPathString = execPath instanceof URL ? (0, import_node_url.fileURLToPath)(execPath) : execPath; result.push(import_node_path.default.resolve(cwdPath, execPathString, "..")); }; var npmRunPathEnv = ({ env: env2 = import_node_process2.default.env, ...options } = {}) => { env2 = { ...env2 }; const pathName = pathKey({ env: env2 }); options.path = env2[pathName]; env2[pathName] = npmRunPath(options); return env2; }; // ../node_modules/.pnpm/mimic-fn@4.0.0/node_modules/mimic-fn/index.js var copyProperty = (to, from, property, ignoreNonConfigurable) => { if (property === "length" || property === "prototype") { return; } if (property === "arguments" || property === "caller") { return; } const toDescriptor = Object.getOwnPropertyDescriptor(to, property); const fromDescriptor = Object.getOwnPropertyDescriptor(from, property); if (!canCopyProperty(toDescriptor, fromDescriptor) && ignoreNonConfigurable) { return; } Object.defineProperty(to, property, fromDescriptor); }; var canCopyProperty = function(toDescriptor, fromDescriptor) { return toDescriptor === void 0 || toDescriptor.configurable || toDescriptor.writable === fromDescriptor.writable && toDescriptor.enumerable === fromDescriptor.enumerable && toDescriptor.configurable === fromDescriptor.configurable && (toDescriptor.writable || toDescriptor.value === fromDescriptor.value); }; var changePrototype = (to, from) => { const fromPrototype = Object.getPrototypeOf(from); if (fromPrototype === Object.getPrototypeOf(to)) { return; } Object.setPrototypeOf(to, fromPrototype); }; var wrappedToString = (withName, fromBody) => `/* Wrapped ${withName}*/ ${fromBody}`; var toStringDescriptor = Object.getOwnPropertyDescriptor(Function.prototype, "toString"); var toStringName = Object.getOwnPropertyDescriptor(Function.prototype.toString, "name"); var changeToString = (to, from, name) => { const withName = name === "" ? "" : `with ${name.trim()}() `; const newToString = wrappedToString.bind(null, withName, from.toString()); Object.defineProperty(newToString, "name", toStringName); Object.defineProperty(to, "toString", { ...toStringDescriptor, value: newToString }); }; function mimicFunction(to, from, { ignoreNonConfigurable = false } = {}) { const { name } = to; for (const property of Reflect.ownKeys(from)) { copyProperty(to, from, property, ignoreNonConfigurable); } changePrototype(to, from); changeToString(to, from, name); return to; } // ../node_modules/.pnpm/onetime@6.0.0/node_modules/onetime/index.js var calledFunctions = /* @__PURE__ */ new WeakMap(); var onetime = (function_, options = {}) => { if (typeof function_ !== "function") { throw new TypeError("Expected a function"); } let returnValue; let callCount = 0; const functionName = function_.displayName || function_.name || ""; const onetime2 = function(...arguments_) { calledFunctions.set(onetime2, ++callCount); if (callCount === 1) { returnValue = function_.apply(this, arguments_); function_ = null; } else if (options.throw === true) { throw new Error(`Function \`${functionName}\` can only be called once`); } return returnValue; }; mimicFunction(onetime2, function_); calledFunctions.set(onetime2, callCount); return onetime2; }; onetime.callCount = (function_) => { if (!calledFunctions.has(function_)) { throw new Error(`The given function \`${function_.name}\` is not wrapped by the \`onetime\` package`); } return calledFunctions.get(function_); }; var onetime_default = onetime; // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/main.js var import_os2 = require("os"); // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/realtime.js var getRealtimeSignals = function() { const length = SIGRTMAX - SIGRTMIN + 1; return Array.from({ length }, getRealtimeSignal); }; var getRealtimeSignal = function(value, index) { return { name: `SIGRT${index + 1}`, number: SIGRTMIN + index, action: "terminate", description: "Application-specific signal (realtime)", standard: "posix" }; }; var SIGRTMIN = 34; var SIGRTMAX = 64; // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/signals.js var import_os = require("os"); // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/core.js var SIGNALS = [ { name: "SIGHUP", number: 1, action: "terminate", description: "Terminal closed", standard: "posix" }, { name: "SIGINT", number: 2, action: "terminate", description: "User interruption with CTRL-C", standard: "ansi" }, { name: "SIGQUIT", number: 3, action: "core", description: "User interruption with CTRL-\\", standard: "posix" }, { name: "SIGILL", number: 4, action: "core", description: "Invalid machine instruction", standard: "ansi" }, { name: "SIGTRAP", number: 5, action: "core", description: "Debugger breakpoint", standard: "posix" }, { name: "SIGABRT", number: 6, action: "core", description: "Aborted", standard: "ansi" }, { name: "SIGIOT", number: 6, action: "core", description: "Aborted", standard: "bsd" }, { name: "SIGBUS", number: 7, action: "core", description: "Bus error due to misaligned, non-existing address or paging error", standard: "bsd" }, { name: "SIGEMT", number: 7, action: "terminate", description: "Command should be emulated but is not implemented", standard: "other" }, { name: "SIGFPE", number: 8, action: "core", description: "Floating point arithmetic error", standard: "ansi" }, { name: "SIGKILL", number: 9, action: "terminate", description: "Forced termination", standard: "posix", forced: true }, { name: "SIGUSR1", number: 10, action: "terminate", description: "Application-specific signal", standard: "posix" }, { name: "SIGSEGV", number: 11, action: "core", description: "Segmentation fault", standard: "ansi" }, { name: "SIGUSR2", number: 12, action: "terminate", description: "Application-specific signal", standard: "posix" }, { name: "SIGPIPE", number: 13, action: "terminate", description: "Broken pipe or socket", standard: "posix" }, { name: "SIGALRM", number: 14, action: "terminate", description: "Timeout or timer", standard: "posix" }, { name: "SIGTERM", number: 15, action: "terminate", description: "Termination", standard: "ansi" }, { name: "SIGSTKFLT", number: 16, action: "terminate", description: "Stack is empty or overflowed", standard: "other" }, { name: "SIGCHLD", number: 17, action: "ignore", description: "Child process terminated, paused or unpaused", standard: "posix" }, { name: "SIGCLD", number: 17, action: "ignore", description: "Child process terminated, paused or unpaused", standard: "other" }, { name: "SIGCONT", number: 18, action: "unpause", description: "Unpaused", standard: "posix", forced: true }, { name: "SIGSTOP", number: 19, action: "pause", description: "Paused", standard: "posix", forced: true }, { name: "SIGTSTP", number: 20, action: "pause", description: 'Paused using CTRL-Z or "suspend"', standard: "posix" }, { name: "SIGTTIN", number: 21, action: "pause", description: "Background process cannot read terminal input", standard: "posix" }, { name: "SIGBREAK", number: 21, action: "terminate", description: "User interruption with CTRL-BREAK", standard: "other" }, { name: "SIGTTOU", number: 22, action: "pause", description: "Background process cannot write to terminal output", standard: "posix" }, { name: "SIGURG", number: 23, action: "ignore", description: "Socket received out-of-band data", standard: "bsd" }, { name: "SIGXCPU", number: 24, action: "core", description: "Process timed out", standard: "bsd" }, { name: "SIGXFSZ", number: 25, action: "core", description: "File too big", standard: "bsd" }, { name: "SIGVTALRM", number: 26, action: "terminate", description: "Timeout or timer", standard: "bsd" }, { name: "SIGPROF", number: 27, action: "terminate", description: "Timeout or timer", standard: "bsd" }, { name: "SIGWINCH", number: 28, action: "ignore", description: "Terminal window size changed", standard: "bsd" }, { name: "SIGIO", number: 29, action: "terminate", description: "I/O is available", standard: "other" }, { name: "SIGPOLL", number: 29, action: "terminate", description: "Watched event", standard: "other" }, { name: "SIGINFO", number: 29, action: "ignore", description: "Request for process information", standard: "other" }, { name: "SIGPWR", number: 30, action: "terminate", description: "Device running out of power", standard: "systemv" }, { name: "SIGSYS", number: 31, action: "core", description: "Invalid system call", standard: "other" }, { name: "SIGUNUSED", number: 31, action: "terminate", description: "Invalid system call", standard: "other" } ]; // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/signals.js var getSignals = function() { const realtimeSignals = getRealtimeSignals(); const signals = [...SIGNALS, ...realtimeSignals].map(normalizeSignal); return signals; }; var normalizeSignal = function({ name, number: defaultNumber, description, action, forced = false, standard }) { const { signals: { [name]: constantSignal } } = import_os.constants; const supported = constantSignal !== void 0; const number = supported ? constantSignal : defaultNumber; return { name, number, description, supported, action, forced, standard }; }; // ../node_modules/.pnpm/human-signals@3.0.1/node_modules/human-signals/build/src/main.js var getSignalsByName = function() { const signals = getSignals(); return signals.reduce(getSignalByName, {}); }; var getSignalByName = function(signalByNameMemo, { name, number, description, supported, action, forced, standard }) { return { ...signalByNameMemo, [name]: { name, number, description, supported, action, forced, standard } }; }; var signalsByName = getSignalsByName(); var getSignalsByNumber = function() { const signals = getSignals(); const length = SIGRTMAX + 1; const signalsA = Array.from({ length }, (value, number) => getSignalByNumber(number, signals)); return Object.assign({}, ...signalsA); }; var getSignalByNumber = function(number, signals) { const signal = findSignalByNumber(number, signals); if (signal === void 0) { return {}; } const { name, description, supported, action, forced, standard } = signal; return { [number]: { name, number, description, supported, action, forced, standard } }; }; var findSignalByNumber = function(number, signals) { const signal = signals.find(({ name }) => import_os2.constants.signals[name] === number); if (signal !== void 0) { return signal; } return signals.find((signalA) => signalA.number === number); }; var signalsByNumber = getSignalsByNumber(); // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/error.js var getErrorPrefix = ({ timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled }) => { if (timedOut) { return `timed out after ${timeout} milliseconds`; } if (isCanceled) { return "was canceled"; } if (errorCode !== void 0) { return `failed with ${errorCode}`; } if (signal !== void 0) { return `was killed with ${signal} (${signalDescription})`; } if (exitCode !== void 0) { return `failed with exit code ${exitCode}`; } return "failed"; }; var makeError = ({ stdout, stderr, all, error, signal, exitCode, command, escapedCommand, timedOut, isCanceled, killed, parsed: { options: { timeout } } }) => { exitCode = exitCode === null ? void 0 : exitCode; signal = signal === null ? void 0 : signal; const signalDescription = signal === void 0 ? void 0 : signalsByName[signal].description; const errorCode = error && error.code; const prefix = getErrorPrefix({ timedOut, timeout, errorCode, signal, signalDescription, exitCode, isCanceled }); const execaMessage = `Command ${prefix}: ${command}`; const isError = Object.prototype.toString.call(error) === "[object Error]"; const shortMessage = isError ? `${execaMessage} ${error.message}` : execaMessage; const message = [shortMessage, stderr, stdout].filter(Boolean).join("\n"); if (isError) { error.originalMessage = error.message; error.message = message; } else { error = new Error(message); } error.shortMessage = shortMessage; error.command = command; error.escapedCommand = escapedCommand; error.exitCode = exitCode; error.signal = signal; error.signalDescription = signalDescription; error.stdout = stdout; error.stderr = stderr; if (all !== void 0) { error.all = all; } if ("bufferedData" in error) { delete error.bufferedData; } error.failed = true; error.timedOut = Boolean(timedOut); error.isCanceled = isCanceled; error.killed = killed && !timedOut; return error; }; // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/stdio.js var aliases = ["stdin", "stdout", "stderr"]; var hasAlias = (options) => aliases.some((alias) => options[alias] !== void 0); var normalizeStdio = (options) => { if (!options) { return; } const { stdio } = options; if (stdio === void 0) { return aliases.map((alias) => options[alias]); } if (hasAlias(options)) { throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${aliases.map((alias) => `\`${alias}\``).join(", ")}`); } if (typeof stdio === "string") { return stdio; } if (!Array.isArray(stdio)) { throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``); } const length = Math.max(stdio.length, aliases.length); return Array.from({ length }, (value, index) => stdio[index]); }; // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/kill.js var import_node_os2 = __toESM(require("os"), 1); var import_signal_exit = __toESM(require_signal_exit(), 1); var DEFAULT_FORCE_KILL_TIMEOUT = 1e3 * 5; var spawnedKill = (kill, signal = "SIGTERM", options = {}) => { const killResult = kill(signal); setKillTimeout(kill, signal, options, killResult); return killResult; }; var setKillTimeout = (kill, signal, options, killResult) => { if (!shouldForceKill(signal, options, killResult)) { return; } const timeout = getForceKillAfterTimeout(options); const t = setTimeout(() => { kill("SIGKILL"); }, timeout); if (t.unref) { t.unref(); } }; var shouldForceKill = (signal, { forceKillAfterTimeout }, killResult) => isSigterm(signal) && forceKillAfterTimeout !== false && killResult; var isSigterm = (signal) => signal === import_node_os2.default.constants.signals.SIGTERM || typeof signal === "string" && signal.toUpperCase() === "SIGTERM"; var getForceKillAfterTimeout = ({ forceKillAfterTimeout = true }) => { if (forceKillAfterTimeout === true) { return DEFAULT_FORCE_KILL_TIMEOUT; } if (!Number.isFinite(forceKillAfterTimeout) || forceKillAfterTimeout < 0) { throw new TypeError(`Expected the \`forceKillAfterTimeout\` option to be a non-negative integer, got \`${forceKillAfterTimeout}\` (${typeof forceKillAfterTimeout})`); } return forceKillAfterTimeout; }; var spawnedCancel = (spawned, context) => { const killResult = spawned.kill(); if (killResult) { context.isCanceled = true; } }; var timeoutKill = (spawned, signal, reject) => { spawned.kill(signal); reject(Object.assign(new Error("Timed out"), { timedOut: true, signal })); }; var setupTimeout = (spawned, { timeout, killSignal = "SIGTERM" }, spawnedPromise) => { if (timeout === 0 || timeout === void 0) { return spawnedPromise; } let timeoutId; const timeoutPromise = new Promise((resolve4, reject) => { timeoutId = setTimeout(() => { timeoutKill(spawned, killSignal, reject); }, timeout); }); const safeSpawnedPromise = spawnedPromise.finally(() => { clearTimeout(timeoutId); }); return Promise.race([timeoutPromise, safeSpawnedPromise]); }; var validateTimeout = ({ timeout }) => { if (timeout !== void 0 && (!Number.isFinite(timeout) || timeout < 0)) { throw new TypeError(`Expected the \`timeout\` option to be a non-negative integer, got \`${timeout}\` (${typeof timeout})`); } }; var setExitHandler = async (spawned, { cleanup, detached }, timedPromise) => { if (!cleanup || detached) { return timedPromise; } const removeExitHandler = (0, import_signal_exit.default)(() => { spawned.kill(); }); return timedPromise.finally(() => { removeExitHandler(); }); }; // ../node_modules/.pnpm/is-stream@3.0.0/node_modules/is-stream/index.js function isStream(stream) { return stream !== null && typeof stream === "object" && typeof stream.pipe === "function"; } // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/stream.js var import_get_stream = __toESM(require_get_stream(), 1); var import_merge_stream = __toESM(require_merge_stream(), 1); var handleInput = (spawned, input) => { if (input === void 0 || spawned.stdin === void 0) { return; } if (isStream(input)) { input.pipe(spawned.stdin); } else { spawned.stdin.end(input); } }; var makeAllStream = (spawned, { all }) => { if (!all || !spawned.stdout && !spawned.stderr) { return; } const mixed = (0, import_merge_stream.default)(); if (spawned.stdout) { mixed.add(spawned.stdout); } if (spawned.stderr) { mixed.add(spawned.stderr); } return mixed; }; var getBufferedData = async (stream, streamPromise) => { if (!stream) { return; } stream.destroy(); try { return await streamPromise; } catch (error) { return error.bufferedData; } }; var getStreamPromise = (stream, { encoding, buffer, maxBuffer }) => { if (!stream || !buffer) { return; } if (encoding) { return (0, import_get_stream.default)(stream, { encoding, maxBuffer }); } return import_get_stream.default.buffer(stream, { maxBuffer }); }; var getSpawnedResult = async ({ stdout, stderr, all }, { encoding, buffer, maxBuffer }, processDone) => { const stdoutPromise = getStreamPromise(stdout, { encoding, buffer, maxBuffer }); const stderrPromise = getStreamPromise(stderr, { encoding, buffer, maxBuffer }); const allPromise = getStreamPromise(all, { encoding, buffer, maxBuffer: maxBuffer * 2 }); try { return await Promise.all([processDone, stdoutPromise, stderrPromise, allPromise]); } catch (error) { return Promise.all([ { error, signal: error.signal, timedOut: error.timedOut }, getBufferedData(stdout, stdoutPromise), getBufferedData(stderr, stderrPromise), getBufferedData(all, allPromise) ]); } }; // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/promise.js var nativePromisePrototype = (async () => { })().constructor.prototype; var descriptors = ["then", "catch", "finally"].map((property) => [ property, Reflect.getOwnPropertyDescriptor(nativePromisePrototype, property) ]); var mergePromise = (spawned, promise) => { for (const [property, descriptor] of descriptors) { const value = typeof promise === "function" ? (...args) => Reflect.apply(descriptor.value, promise(), args) : descriptor.value.bind(promise); Reflect.defineProperty(spawned, property, { ...descriptor, value }); } return spawned; }; var getSpawnedPromise = (spawned) => new Promise((resolve4, reject) => { spawned.on("exit", (exitCode, signal) => { resolve4({ exitCode, signal }); }); spawned.on("error", (error) => { reject(error); }); if (spawned.stdin) { spawned.stdin.on("error", (error) => { reject(error); }); } }); // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/lib/command.js var normalizeArgs = (file, args = []) => { if (!Array.isArray(args)) { return [file]; } return [file, ...args]; }; var NO_ESCAPE_REGEXP = /^[\w.-]+$/; var DOUBLE_QUOTES_REGEXP = /"/g; var escapeArg = (arg) => { if (typeof arg !== "string" || NO_ESCAPE_REGEXP.test(arg)) { return arg; } return `"${arg.replace(DOUBLE_QUOTES_REGEXP, '\\"')}"`; }; var joinCommand = (file, args) => normalizeArgs(file, args).join(" "); var getEscapedCommand = (file, args) => normalizeArgs(file, args).map((arg) => escapeArg(arg)).join(" "); var SPACES_REGEXP = / +/g; var parseCommand = (command) => { const tokens = []; for (const token of command.trim().split(SPACES_REGEXP)) { const previousToken = tokens[tokens.length - 1]; if (previousToken && previousToken.endsWith("\\")) { tokens[tokens.length - 1] = `${previousToken.slice(0, -1)} ${token}`; } else { tokens.push(token); } } return tokens; }; // ../node_modules/.pnpm/execa@6.1.0/node_modules/execa/index.js var DEFAULT_MAX_BUFFER = 1e3 * 1e3 * 100; var getEnv = ({ env: envOption, extendEnv, preferLocal, localDir, execPath }) => { const env2 = extendEnv ? { ...import_node_process3.default.env, ...envOption } : envOption; if (preferLocal) { return npmRunPathEnv({ env: env2, cwd: localDir, execPath }); } return env2; }; var handleArguments = (file, args, options = {}) => { const parsed = import_cross_spawn.default._parse(file, args, options); file = parsed.command; args = parsed.args; options = parsed.options; options = { maxBuffer: DEFAULT_MAX_BUFFER, buffer: true, stripFinalNewline: true, extendEnv: true, preferLocal: false, localDir: options.cwd || import_node_process3.default.cwd(), execPath: import_node_process3.default.execPath, encoding: "utf8", reject: true, cleanup: true, all: false, windowsHide: true, ...options }; options.env = getEnv(options); options.stdio = normalizeStdio(options); if (import_node_process3.default.platform === "win32" && import_node_path2.default.basename(file, ".exe") === "cmd") { args.unshift("/q"); } return { file, args, options, parsed }; }; var handleOutput = (options, value, error) => { if (typeof value !== "string" && !import_node_buffer.Buffer.isBuffer(value)) { return error === void 0 ? void 0 : ""; } if (options.stripFinalNewline) { return stripFinalNewline(value); } return value; }; function execa(file, args, options) { const parsed = handleArguments(file, args, options); const command = joinCommand(file, args); const escapedCommand = getEscapedCommand(file, args); validateTimeout(parsed.options); let spawned; try { spawned = import_node_child_process.default.spawn(parsed.file, parsed.args, parsed.options); } catch (error) { const dummySpawned = new import_node_child_process.default.ChildProcess(); const errorPromise = Promise.reject(makeError({ error, stdout: "", stderr: "", all: "", command, escapedCommand, parsed, timedOut: false, isCanceled: false, killed: false })); return mergePromise(dummySpawned, errorPromise); } const spawnedPromise = getSpawnedPromise(spawned); const timedPromise = setupTimeout(spawned, parsed.options, spawnedPromise); const processDone = setExitHandler(spawned, parsed.options, timedPromise); const context = { isCanceled: false }; spawned.kill = spawnedKill.bind(null, spawned.kill.bind(spawned)); spawned.cancel = spawnedCancel.bind(null, spawned, context); const handlePromise = async () => { const [{ error, exitCode, signal, timedOut }, stdoutResult, stderrResult, allResult] = await getSpawnedResult(spawned, parsed.options, processDone); const stdout = handleOutput(parsed.options, stdoutResult); const stderr = handleOutput(parsed.options, stderrResult); const all = handleOutput(parsed.options, allResult); if (error || exitCode !== 0 || signal !== null) { const returnedError = makeError({ error, exitCode, signal, stdout, stderr, all, command, escapedCommand, parsed, timedOut, isCanceled: context.isCanceled || (parsed.options.signal ? parsed.options.signal.aborted : false), killed: spawned.killed }); if (!parsed.options.reject) { return returnedError; } throw returnedError; } return { command, escapedCommand, exitCode: 0, stdout, stderr, all, failed: false, timedOut: false, isCanceled: false, killed: false }; }; const handlePromiseOnce = onetime_default(handlePromise); handleInput(spawned, parsed.options.input); spawned.all = makeAllStream(spawned, parsed.options); return mergePromise(spawned, handlePromiseOnce); } function execaCommand(command, options) { const [file, ...args] = parseCommand(command); return execa(file, args, options); } // ../node_modules/.pnpm/ansi-regex@6.0.1/node_modules/ansi-regex/index.js function ansiRegex({ onlyFirst = false } = {}) { const pattern = [ "[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)", "(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))" ].join("|"); return new RegExp(pattern, onlyFirst ? void 0 : "g"); } // ../node_modules/.pnpm/strip-ansi@7.1.0/node_modules/strip-ansi/index.js var regex = ansiRegex(); function stripAnsi(string) { if (typeof string !== "string") { throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``); } return string.replace(regex, ""); } // src/utils/exec.ts var import_tsyringe7 = require("tsyringe"); var execLogService = import_tsyringe7.container.resolve(LogService); execLogService.setScope("exec-service"); async function run(command, args = [], opt = { stripAnsi: false }) { const { stripAnsi: stripAnsi2, ...execOpt } = opt; execLogService.log(`run ${command} ${args.join(" ")}`); const { stdout } = await execa(command, args, { ...execOpt, stdio: "pipe" }); if (stripAnsi2) return stripAnsi(stdout); return stdout; } // src/utils/hexo.ts var toPost = (post) => post; var toPage = (post) => post; var toCategory = (post) => post; var toTag = (post) => post; // ../server-shared/src/store.ts var import_path7 = require("path"); var import_reactivity2 = require("@vue/reactivity"); // ../node_modules/.pnpm/@winwin+server-reactive-store@0.2.2/node_modules/@winwin/server-reactive-store/dist/index.mjs var import_reactivity = require("@vue/reactivity"); var import_watch = __toESM(require_dist(), 1); var import_fs3 = require("fs"); var import_path6 = require("path"); var import_simple_json_db2 = __toESM(require("simple-json-db"), 1); var JSONdbStorageAdapter = class { constructor(root, name = "database.json") { if (!(0, import_fs3.existsSync)(root)) (0, import_fs3.mkdirSync)(root); this.db = new import_simple_json_db2.default((0, import_path6.resolve)(root, name)); } getItem(key) { return this.db.get(key); } setItem(key, value) { this.db.set(key, value); } removeItem(key) { return this.db.delete(key); } }; function createStore(key, adapter, setup, { saveAfterCreate = true } = {}) { const all = setup(); if (!all.state) throw new Error("must return object with state property"); const state = (0, import_reactivity.reactive)(all.state); const load = () => { const loaded = adapter.getItem(key); if (loaded) Object.assign(state, loaded); }; load(); const save = () => adapter.setItem(key, state); (0, import_watch.watch)(state, save, { deep: true, immediate: saveAfterCreate }); return { ...all, load, save, state }; } function createStoreCreator(adapter) { return function(key, setup) { return createStore(key, adapter, setup); }; } // ../server-shared/src/store.ts var ROOT2 = (0, import_path7.resolve)(process.cwd(), "data"); var NAME = "database.json"; var createStore2 = createStoreCreator(new JSONdbStorageAdapter(ROOT2, NAME)); var scriptStore = createStore2("script", () => { const state = (0, import_reactivity2.reactive)({ items: {} }); const keys = (0, import_reactivity2.computed)(() => Object.keys(state.items)); const getScript = (key) => { var _a; return ((_a = state.items[key]) == null ? void 0 : _a.value) || ""; }; const hasScript = (key) => { var _a; return (_a = state.items[key]) == null ? void 0 : _a.value; }; const setScript = (key, script) => { state.items[key] = { value: script }; }; return { state, keys, getScript, setScript, hasScript }; }); // src/services/exec-service.ts var import_tsyringe8 = require("tsyringe"); var ExecService = class { constructor(_storage, _logService) { this._storage = _storage; this._logService = _logService; this._logService.setScope("exec-service"); } _getCwd() { return this._storage.get(HEXO_BASE_DIR_KEY); } async run(command) { if (!command) return; const cwd = toRealPath(this._getCwd()); return execaCommand(command, { cwd, stdio: "inherit" }); } }; ExecService = __decorateClass([ (0, import_tsyringe8.injectable)(), (0, import_tsyringe8.singleton)(), __decorateParam(0, (0, import_tsyringe8.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe8.inject)(LogService)) ], ExecService); // src/services/hexo-service.ts function isAsset(pageOrAsset) { return pageOrAsset.layout === "false"; } function transformPost(doc) { var _a, _b, _c, _d; return { ...doc, __post: true, slug: doc.slug, date: doc.date.toString(), updated: (_a = doc.updated) == null ? void 0 : _a.toString(), prev: (_b = doc.prev) == null ? void 0 : _b.source, next: (_c = doc.next) == null ? void 0 : _c.source, tags: doc.tags.data.map((t) => t.slug), categories: (_d = doc.categories) == null ? void 0 : _d.data.map((c) => c.slug), brief: doc._content.slice(0, BRIEF_LENGTH) }; } function transformPostToBrief({ _content, content, raw, ...doc }) { return { ...doc, brief: _content.slice(0, BRIEF_LENGTH) }; } function transformPage(doc) { var _a, _b, _c; return { ...doc, __page: true, slug: doc.slug, date: doc.date.toString(), updated: (_a = doc.updated) == null ? void 0 : _a.toString(), prev: (_b = doc.prev) == null ? void 0 : _b.source, next: (_c = doc.next) == null ? void 0 : _c.source, brief: doc._content.slice(0, BRIEF_LENGTH) }; } function transformPageToBrief({ _content, content, raw, ...doc }) { return { ...doc, brief: _content.slice(0, BRIEF_LENGTH) }; } var HexoService = class { constructor(_logService, _hexoInstanceService, _execService) { this._logService = _logService; this._hexoInstanceService = _hexoInstanceService; this._execService = _execService; this._logService.setScope("hexo-service"); } async runWithoutModifiedOption(fn) { const { hexo, cleanup } = await this._hexoInstanceService.getInstanceWithOriginOptions(); await fn(hexo); await cleanup(); } async getPostByFullSource(fullSource) { const hexo = await this._hexoInstanceService.getInstance(); const post = hexo.locals.get("posts").toArray().find((item) => item.full_source === fullSource); return this.getPostBySource(post.source); } async getPostOrPageByFullSource(fullSource) { const hexo = await this._hexoInstanceService.getInstance(); const post = hexo.locals.get("posts").toArray().find((item) => item.full_source === fullSource); if (post) return this.getPostBySource(post.source); const page = hexo.locals.get("pages").toArray().find((item) => item.full_source === fullSource); return this.getPageBySource(page.source); } writeFile(fullPath, content) { try { import_fs4.default.writeFileSync(fullPath, content); } catch (e) { this._logService.error("fail to write file"); throw e; } } deleteFile(fullPath) { try { import_fs4.default.rmSync(fullPath); } catch (e) { this._logService.error("fail to delete file"); throw e; } } async getFullPathBySource(source, type) { var _a, _b; const hexo = await this._hexoInstanceService.getInstance(); if (type === "post") return (_a = hexo.locals.get("posts").toArray().find((item) => item.source === source)) == null ? void 0 : _a.full_source; else return (_b = hexo.locals.get("pages").toArray().find((item) => item.source === source)) == null ? void 0 : _b.full_source; } async WithCategoriesTagsBriefArticleList(article) { const categories = await this.listCategory(); const tags = await this.listTag(); const pages = await this.listPage(); const posts = await this.listPost(); return { article, categories, tags, pages, posts }; } async listPost() { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("posts").toArray().map(toPost); const res = docs.map((postDoc) => { const post = transformPostToBrief(transformPost(postDoc)); delete post.content; delete post._content; delete post.raw; delete post.more; return post; }); this._logService.log("list post", res.length); return res; } async getPostBySource(source) { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("posts").toArray().map(toPost); const doc = docs.find((item) => item.source === source); if (!doc) return; const res = transformPost(doc); this._logService.log("get post by source", source); return res; } async listPage() { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("pages").toArray().map(toPage).filter((doc) => !isAsset(doc)); const res = docs.map((pageDoc) => { const page = transformPageToBrief(transformPage(pageDoc)); delete page.content; delete page._content; delete page.raw; delete page.more; return page; }); this._logService.log("list page", res.length); return res; } async getPageBySource(source) { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("pages").toArray().map(toPage); const doc = docs.find((item) => item.source === source); if (!doc || isAsset(doc)) throw new PostOrPageNotFoundError("page"); const res = transformPage(doc); this._logService.log("get page by source", source); return res; } async listCategory() { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("categories").toArray().map(toCategory); const res = docs.map((categoryDoc) => ({ ...categoryDoc, posts: categoryDoc.posts.map((p) => p.slug) })); this._logService.log("list category", res.length); return res; } async listTag() { const hexo = await this._hexoInstanceService.getInstance(); const docs = hexo.locals.get("tags").toArray().map(toTag); const res = docs.map((tagDoc) => ({ ...tagDoc, posts: tagDoc.posts.map((p) => p.slug) })); this._logService.log("list tag", res.length); return res; } async deploy(options = {}) { if (scriptStore.hasScript("hexo-deploy")) { await this._execService.run(scriptStore.getScript("hexo-deploy")).catch((err) => { this._logService.error(err); throw new ScriptError( "fail to run hexo deploy script", "HexoDeployScriptError" ); }); return; } const { generate = false } = options; const args = []; if (generate) args.push("--generate"); this.runWithoutModifiedOption(async (hexo) => { await hexo.call("deploy", { _: args }); await hexo.exit(); }); this._logService.log(`run hexo deploy with args:`, args.join(" ")); } async generate(options = {}) { if (scriptStore.hasScript("hexo-generate")) { await this._execService.run(scriptStore.getScript("hexo-generate")).catch((err) => { this._logService.error(err); throw new ScriptError( "fail to run hexo generate script", "HexoGenerateScriptError" ); }); return; } const { deploy = false, watch: watch2 = false, bail = false, force = false, concurrency = false } = options; const args = []; if (deploy) args.push("--deploy"); if (watch2) args.push("--watch"); if (bail) args.push("--bail"); if (force) args.push("--force"); this.runWithoutModifiedOption(async (hexo) => { if (concurrency) args.push("--concurrency"); await hexo.call("generate", { _: args }); await hexo.exit(); }); this._logService.log(`run hexo generate with args:`, args.join(" ")); } async clean() { if (scriptStore.hasScript("hexo-clean")) { await this._execService.run(scriptStore.getScript("hexo-clean")).catch((err) => { this._logService.error(err); throw new ScriptError( "fail to run hexo clean script", "HexoCleanScriptError" ); }); return; } this.runWithoutModifiedOption(async (hexo) => { await hexo.call("clean"); await hexo.exit(); }); this._logService.log("run hexo clean"); } async publish(filename, layout) { const args = ["publish"]; if (layout) args.push(layout); args.push(filename); const info = await this._hexoInstanceService.runBetweenReload( async () => await run("hexo", args, { cwd: await this._hexoInstanceService.getBaseDir(), stripAnsi: true }) ); const fullSource = expandHomeDir(info.split("Published: ")[1].trim()); const article = await this.getPostByFullSource(fullSource); const res = await this.WithCategoriesTagsBriefArticleList(article); this._logService.log(`publish ${filename} with layout: ${layout}`); return res; } async create(title, options = {}) { const args = ["new"]; if (options.layout) args.push(options.layout); if (options.path) { const base = await this._hexoInstanceService.getBaseDir(); const fullPath = import_path8.default.resolve(base, options.path); const relative = import_path8.default.relative(fullPath, base); if (!relative.startsWith("..")) { this._logService.error(`${fullPath} is not valid`); throw new InvalidOptionsError( `${options.path} is not inside hexo blog folder`, "InvalidCreatePathError" ); } args.push("--path"); args.push(options.path); } if (options.replace) args.push("--replace"); if (options.slug) { args.push("--slug"); args.push(options.slug); } if (title) args.push(title); const info = await this._hexoInstanceService.runBetweenReload(async () => { return await run("hexo", args, { cwd: await this._hexoInstanceService.getBaseDir(), stripAnsi: true }); }); const fullSource = expandHomeDir(info.split("Created: ")[1].trim()); const article = await this.getPostOrPageByFullSource(fullSource); const res = this.WithCategoriesTagsBriefArticleList(article); this._logService.log("create succeed", fullSource); return res; } async update(source, raw, type) { const fullPath = await this.getFullPathBySource(source, type); if (!fullPath) throw new PostOrPageNotFoundError(type); await this._hexoInstanceService.runBetweenReload(() => { this.writeFile(fullPath, raw); }); this._logService.log(`${type} update succeed`, fullPath); if (type === "post") { return this.WithCategoriesTagsBriefArticleList(await this.getPostBySource(source)); } else { return this.WithCategoriesTagsBriefArticleList(await this.getPageBySource(source)); } } async delete(source, type) { const fullPath = await this.getFullPathBySource(source, type); if (!fullPath) throw new PostOrPageNotFoundError(type); await this._hexoInstanceService.runBetweenReload(async () => { await this.deleteFile(fullPath); }); return this.WithCategoriesTagsBriefArticleList(void 0); } }; HexoService = __decorateClass([ (0, import_tsyringe9.injectable)(), (0, import_tsyringe9.singleton)(), __decorateParam(0, (0, import_tsyringe9.inject)(LogService)), __decorateParam(1, (0, import_tsyringe9.inject)(HexoInstanceService)), __decorateParam(2, (0, import_tsyringe9.inject)(ExecService)) ], HexoService); // src/routes/hexo.ts var router2 = new import_router2.default(); router2.prefix("/hexo"); router2.get("/posts", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); ctx.body = await hexo.listPost(); }); router2.get("/post/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; if (!source) { ctx.status = 400; ctx.body = "need `source`"; } const post = await hexo.getPostBySource(decodeURIComponent(source)); if (!post) throw new PostOrPageNotFoundError("post"); ctx.body = post; }); router2.get("/pages", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); ctx.body = await hexo.listPage(); }); router2.get("/page/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; if (!source) { ctx.status = 400; ctx.body = "need `source`"; } ctx.body = await hexo.getPageBySource(decodeURIComponent(source)); }); router2.get("/tags", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); ctx.body = await hexo.listTag(); }); router2.get("/categories", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); ctx.body = await hexo.listCategory(); }); router2.post("/deploy", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); await hexo.deploy(ctx.request.body); ctx.status = 200; }); router2.post("/generate", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); await hexo.generate(ctx.request.body); ctx.status = 200; }); router2.post("/clean", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); await hexo.clean(); ctx.status = 200; }); router2.post("/publish", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { filename, layout } = ctx.request.body; if (!filename) { ctx.status = 400; ctx.body = "need `filename`"; return; } ctx.body = await hexo.publish(filename, layout); }); router2.post("/create", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { title, layout, path: path9, slug, replace } = ctx.request.body; if (!title) { ctx.status = 400; ctx.body = "need `title`"; return; } ctx.body = await hexo.create(title, { layout, path: path9, slug, replace }); }); router2.put("/post/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; const { raw } = ctx.request.body; if (!source || !raw) { ctx.status = 400; ctx.body = "need `source` and `raw`"; return; } ctx.body = await hexo.update(source, raw, "post"); }); router2.put("/page/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; const { raw } = ctx.request.body; if (!source || !raw) { ctx.status = 400; ctx.body = "need `source` and `raw`"; return; } ctx.body = await hexo.update(source, raw, "page"); }); router2.delete("/post/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; if (!source) { ctx.status = 400; ctx.body = "need `source` "; return; } ctx.body = await hexo.delete(source, "post"); }); router2.delete("/page/:source", async (ctx) => { const hexo = import_tsyringe10.container.resolve(HexoService); const { source } = ctx.params; if (!source) { ctx.status = 400; ctx.body = "need `source` "; return; } ctx.body = await hexo.delete(source, "page"); }); var hexo_default = router2; // src/routes/git.ts var import_tsyringe12 = require("tsyringe"); var import_router3 = __toESM(require("@koa/router")); // src/services/git-service.ts var import_tsyringe11 = require("tsyringe"); async function isClean(repoPath) { return !await run("git", ["status", "-s"], { cwd: repoPath }); } async function hasRepo(repoPath) { return run("git", ["rev-parse", "--is-inside-work-tree"], { cwd: repoPath }).then( () => true, () => false ); } async function hasRemtoe(repoPath) { return !!await run("git", ["remote", "-v"], { cwd: repoPath }); } var GitService = class { constructor(storage, _logService, _execService) { this.storage = storage; this._logService = _logService; this._execService = _execService; this._logService.setScope("git-service"); } async sync() { if (scriptStore.hasScript("git-sync")) return this._execService.run(scriptStore.getScript("git-sync")).catch((err) => { this._logService.error(err); throw new ScriptError( "fail to run git sync script", "GitSyncScriptError" ); }); const base = this.storage.get(HEXO_BASE_DIR_KEY); const cwd = toRealPath(base); if (!await hasRepo(cwd)) { this._logService.log("not git repo, skipped"); return; } await run("git", ["reset", "--hard"], { cwd }).catch((err) => { this._logService.error(err); this._logService.error("git reset hard error"); throw err; }); this._logService.log("git reset succeed"); if (await hasRemtoe(cwd)) { await run("git", ["pull"], { cwd }).catch((err) => { this._logService.error(err); this._logService.error("git pull error"); throw err; }); } else { this._logService.log("no remote detected, skip pull"); } this._logService.log("sync succeed"); } async save() { if (scriptStore.hasScript("git-save")) return this._execService.run(scriptStore.getScript("git-save")).catch((err) => { this._logService.error(err); throw new ScriptError( "fail to run git save script", "GitSaveScriptError" ); }); const base = this.storage.get(HEXO_BASE_DIR_KEY); const cwd = toRealPath(base); if (!await hasRepo(cwd)) { this._logService.log("not git repo, skipped"); return; } if (await isClean(cwd)) { this._logService.log("work space clean no need to save"); return; } await run("git", ["add", ".", "--all"], { cwd }).catch((err) => { this._logService.error(err); this._logService.error("git add all error"); throw err; }); this._logService.log("git add succeed"); await run( "git", ["commit", "-m", `server update ${new Date().toString()}`], { cwd } ).catch((err) => { this._logService.error(err); this._logService.error("git commit error"); throw err; }); this._logService.log("git commit succeed"); if (await hasRemtoe(cwd)) { await run("git", ["push"], { cwd }).catch((err) => { this._logService.error(err); this._logService.error("git push error"); throw err; }); } else { this._logService.log("no remote detected, skip push"); } this._logService.log("save succeed"); } }; GitService = __decorateClass([ (0, import_tsyringe11.injectable)(), (0, import_tsyringe11.singleton)(), __decorateParam(0, (0, import_tsyringe11.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe11.inject)(LogService)), __decorateParam(2, (0, import_tsyringe11.inject)(ExecService)) ], GitService); // src/routes/git.ts var router3 = new import_router3.default(); router3.prefix("/git"); router3.post("/sync", async (ctx) => { const git = import_tsyringe12.container.resolve(GitService); await git.sync(); ctx.status = 200; }); router3.post("/save", async (ctx) => { const git = import_tsyringe12.container.resolve(GitService); await git.save(); ctx.status = 200; }); var git_default = router3; // src/routes/settings.ts var import_tsyringe14 = require("tsyringe"); var import_router4 = __toESM(require("@koa/router")); // src/services/settings-service.ts var import_tsyringe13 = require("tsyringe"); var SettingsService = class { constructor(_storageService) { this._storageService = _storageService; } async get() { return this._storageService.get(SettingsService.KEY) || {}; } async set(settings) { this._storageService.set(SettingsService.KEY, settings); } }; SettingsService.KEY = "settings"; SettingsService = __decorateClass([ (0, import_tsyringe13.injectable)(), (0, import_tsyringe13.singleton)(), __decorateParam(0, (0, import_tsyringe13.inject)(StorageService)) ], SettingsService); // src/routes/settings.ts var router4 = new import_router4.default(); router4.get("/settings", async (ctx) => { const settingsService = import_tsyringe14.container.resolve(SettingsService); ctx.body = await settingsService.get(); }); router4.post("/settings", async (ctx) => { var _a; const settingsService = import_tsyringe14.container.resolve(SettingsService); const settings = (_a = ctx.request.body) != null ? _a : {}; await settingsService.set(settings); ctx.status = 200; }); var settings_default = router4; // src/routes/template.ts var import_router5 = __toESM(require("@koa/router")); var import_tsyringe16 = require("tsyringe"); // src/services/frontmatter-template-service.ts var import_tsyringe15 = require("tsyringe"); var FrontmatterTemplateService = class { constructor(_storageService) { this._storageService = _storageService; } _list() { return this._storageService.get( FrontmatterTemplateService.KEY ) || []; } _set(items) { this._storageService.set( FrontmatterTemplateService.KEY, items ); } async list() { return this._list(); } async set(items) { this._set(items); } }; FrontmatterTemplateService.KEY = "frontmatter-template"; FrontmatterTemplateService = __decorateClass([ (0, import_tsyringe15.injectable)(), (0, import_tsyringe15.singleton)(), __decorateParam(0, (0, import_tsyringe15.inject)(StorageService)) ], FrontmatterTemplateService); // src/routes/template.ts var router5 = new import_router5.default(); router5.prefix("/template"); router5.get("/frontmatter", async (ctx) => { const frontmatterTemplateService = import_tsyringe16.container.resolve( FrontmatterTemplateService ); const items = await frontmatterTemplateService.list(); ctx.body = { items }; }); router5.post("/frontmatter", async (ctx) => { var _a; const items = (_a = ctx.request.body) == null ? void 0 : _a.items; if (!items) throw new InvalidOptionsError("`raw` is required"); const frontmatterTemplateService = import_tsyringe16.container.resolve( FrontmatterTemplateService ); await frontmatterTemplateService.set(items); ctx.status = 200; ctx.body = { message: "OK" }; }); var template_default = router5; // src/routes/index.ts var router6 = new import_router6.default(); router6.use(auth.auth); router6.use(health_default.routes()); router6.use(hexo_default.routes()); router6.use(git_default.routes()); router6.use(settings_default.routes()); router6.use(template_default.routes()); var routes_default = router6; // src/app.ts var logService = import_tsyringe17.container.resolve(LogService); logService.setScope("app"); var app = new import_koa.default(); app.use(async (ctx, next) => { try { await next(); } catch (e) { const err = e instanceof Error ? e : new Error(e); const status = err.status || 500; const message = err.message || "internal server error"; const id = err.id || "UnkownError"; ctx.status = status; ctx.body = { status, message, id }; if (status === 500) logService.error(err); } }); app.use((0, import_koa_bodyparser.default)()); app.use((0, import_koa_compress.default)()); app.use(http_secure_default()); app.use((0, import_koa_logger.default)()); app.use((0, import_koa_mount.default)("/", statics)); app.use(auth.router.routes()); app.use(routes_default.routes()); var app_default = app; // src/index.ts var import_path9 = __toESM(require("path")); // ../server-shared/src/env-service.ts var import_tsyringe18 = require("tsyringe"); var EnvService = class { constructor(storage, account, _logService) { this.storage = storage; this.account = account; this._logService = _logService; this._logService.setScope("env-service"); } sync() { this.syncAccount(); this.syncHexon(); this.syncHexo(); } syncAccount() { const username = process.env.HEXON_USERNAME; const password = process.env.HEXON_PASSWORD; if (username) { this._logService.log(`sync account from process.env.HEXON_USERNAME`); this.account.setUsername(username); } if (password) { this._logService.log(`sync account from process.env.HEXON_PASSWORD`); this.account.setPassword(password); } } syncHexon() { const port = process.env.HEXON_PORT; if (port) { this._logService.log(`sync hexon port from process.env.HEXON_PORT`); this.storage.set(HEXON_PORT_KEY, port); } } syncHexo() { const base = process.env.HEXO_BASE; if (base) { this._logService.log(`sync hexo base from process.env.HEXO_BASE`); this.storage.set(HEXO_BASE_DIR_KEY, base); } } }; EnvService = __decorateClass([ (0, import_tsyringe18.injectable)(), (0, import_tsyringe18.singleton)(), __decorateParam(0, (0, import_tsyringe18.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe18.inject)(AccountService)), __decorateParam(2, (0, import_tsyringe18.inject)(LogService)) ], EnvService); // src/index.ts (async () => { dotenv.config({ path: process.env.NODE_ENV === "production" ? process.cwd() + "/.env" : import_path9.default.resolve(process.cwd(), "../.env") }); const storage = import_tsyringe19.container.resolve(StorageService); const server = import_http.default.createServer(app_default.callback()); server.on("listening", () => { const addr = server.address(); const bind = typeof addr === "string" ? "pipe " + addr : "http://localhost:" + addr.port; console.log("Server running on " + bind); const his = import_tsyringe19.container.resolve(HexoInstanceService); his.init().catch(console.error); }); const env2 = import_tsyringe19.container.resolve(EnvService); await env2.sync(); server.listen(storage.get(HEXON_PORT_KEY) || HEXON_DEFAULT_PORT); })(); /*! #__NO_SIDE_EFFECTS__ */ /** * @vue/shared v3.4.25 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT **/ ================================================ FILE: server/jest.config.js ================================================ module.exports = { preset: "ts-jest", testEnvironment: "node", } ================================================ FILE: server/nodemon.json ================================================ { "exec": "pnpm run build && pnpm run start", "watch": ["./src"], "ext": "ts", "env": { "NODE_ENV": "development" } } ================================================ FILE: server/package.json ================================================ { "private": true, "scripts": { "setup": "node ./bin/index.js install", "resetpwd": "node ./bin/index.js resetpwd", "script": "node ./bin/index.js script", "start": "cross-env DEBUG=null NODE_ENV=production && node dist/index.js", "prd": "pm2 start dist/index.js --name hexon -o log/pm2-log.log -e log/pm2-err.log", "dev": "pnpm run build && nodemon ./dist/index.js", "build": "rimraf dist && node ./scripts/build.mjs", "test": "jest" }, "dependencies": { "@koa/cors": "^3.4.3", "@koa/router": "^10.1.1", "@vue/reactivity": "^3.4.34", "@winwin/koa-authentication": "^0.2.3", "@winwin/server-reactive-store": "^0.2.2", "basic-auth": "^2.0.1", "chalk": "^5.3.0", "commander": "^9.5.0", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dayjs": "^1.11.12", "debug": "^4.3.5", "dotenv": "^16.4.5", "execa": "^6.1.0", "hexo": "^7.3.0", "http-errors": "^2.0.0", "inquirer": "^8.2.6", "jsencrypt": "^3.3.2", "jsonwebtoken": "^8.5.1", "koa": "^2.15.3", "koa-bodyparser": "^4.4.1", "koa-compose": "^4.1.0", "koa-compress": "^5.1.1", "koa-logger": "^3.2.1", "koa-mount": "^4.0.0", "koa-router": "^10.1.1", "koa-static": "^5.0.0", "koa2-cors": "^2.0.6", "node-jsencrypt": "^1.0.0", "reflect-metadata": "^0.1.14", "simple-json-db": "^2.0.0", "strip-ansi": "^7.1.0", "tsyringe": "^4.8.0", "winston": "^3.13.1" } } ================================================ FILE: server/scripts/build.mjs ================================================ import esbuild from "esbuild" import { esbuildPluginNodeExternals } from "esbuild-plugin-node-externals" import chalk from "chalk" const INCLUDE_MODULES = [ "execa", "chalk", "@winwin/server-reactive-store", // 为了让 store 工作,如果不加,store 中的 watch 会失效 "strip-ansi", ] // FIXME replace with picocolors function buildServer() { console.log(chalk.gray("[server]"), chalk.green("Building server...")) return new Promise((resolve) => { esbuild .build({ entryPoints: ["./src/index.ts"], bundle: true, platform: "node", target: "node12", outfile: "dist/index.js", plugins: [ esbuildPluginNodeExternals({ include: INCLUDE_MODULES, }), ], }) .then(() => { // eslint-disable-next-line no-console console.log(chalk.gray("[server]"), chalk.green("Build finished.")) resolve() }) }) } buildServer() ================================================ FILE: server/src/app.ts ================================================ import { container } from "tsyringe" import cors from "@koa/cors" import Koa from "koa" import bodyParser from "koa-bodyparser" import compress from "koa-compress" import logger from "koa-logger" import mount from "koa-mount" import { auth } from "./middlewares/auth" import { statics } from "./middlewares/statics" import { LogService } from "@server-shared/log-service" import httpSecure from "./lib/http-secure" import router from "./routes" import { HttpError } from "http-errors" const logService = container.resolve(LogService) logService.setScope("app") const app = new Koa() app.use(async (ctx, next) => { try { await next() } catch (e) { const err = (e instanceof Error ? e : new Error(e as any)) as HttpError const status = err.status || 500 const message = err.message || "internal server error" const id = err.id || "UnkownError" ctx.status = status ctx.body = { status, message, id } if (status === 500) logService.error(err) } }) app.use(bodyParser()) app.use(compress()) app.use(httpSecure()) app.use(logger()) app.use(mount("/", statics)) app.use(auth.router.routes()) app.use(router.routes()) export default app ================================================ FILE: server/src/errors.ts ================================================ import { NotFound, BadRequest, InternalServerError } from "http-errors" export class PostOrPageNotFoundError extends NotFound { public id = "PostOrPageNotFoundError" constructor(type: "post" | "page", msg?: string) { super(msg ?? `${type} not found`) } } export class HexoInitError extends InternalServerError { public id = "HexoInitError" } export class InvalidOptionsError extends BadRequest { constructor(message: string, public id: string = "InvalidOptionsError") { super(message) } } export class ScriptError extends InternalServerError { public id: string constructor(message: string, id = "ScriptError") { super(message) this.id = id } } ================================================ FILE: server/src/index.ts ================================================ import "reflect-metadata" import * as dotenv from "dotenv" import { container } from "tsyringe" import http from "http" import { HEXON_DEFAULT_PORT, HEXON_PORT_KEY } from "@server-shared/constants" import { StorageService } from "@server-shared/storage-service" import { HexoInstanceService } from "@server/services/hexo-instance-service" import app from "@server/app" import path from "path" import { EnvService } from "@server-shared/env-service" ;(async () => { dotenv.config({ path: process.env.NODE_ENV === "production" ? process.cwd() + "/.env" : path.resolve(process.cwd(), "../.env"), }) const storage = container.resolve(StorageService) const server = http.createServer(app.callback()) server.on("listening", () => { const addr = server.address() const bind = typeof addr === "string" ? "pipe " + addr : "http://localhost:" + addr!.port console.log("Server running on " + bind) const his = container.resolve(HexoInstanceService) his.init().catch(console.error) }) const env = container.resolve(EnvService) await env.sync() server.listen(storage.get(HEXON_PORT_KEY) || HEXON_DEFAULT_PORT) })() ================================================ FILE: server/src/lib/http-secure.ts ================================================ import chalk from "chalk" import crypto from "crypto" import CryptoJS from "crypto-js" import { Context, Next } from "koa" import JSEncrypt from "node-jsencrypt" function secure(enable = () => true) { const { publicKey, privateKey } = crypto.generateKeyPairSync("rsa", { // The standard secure default length for RSA keys is 2048 bits modulusLength: 2048, publicKeyEncoding: { type: "spki", format: "pem", }, privateKeyEncoding: { type: "pkcs1", format: "pem", }, }) function decryptRSA(data: string) { const o = new JSEncrypt() o.setPrivateKey(privateKey) const res = o.decrypt(data) return res } function decryptAES(data: string, key: string) { return CryptoJS.AES.decrypt(data, key).toString(CryptoJS.enc.Utf8) } function encryptAES(data: string, key: string) { return CryptoJS.AES.encrypt(data, key).toString() } CryptoJS.AES.encrypt('"hi"', "123").toString() function isGetPublicKeyRoute(ctx: Context) { return ( ctx.request.path.startsWith("/publickey") && ctx.request.method === "GET" ) } interface IData { /** * real data passed to axios config.data */ data: any } function stringifyData(data: IData): string { return JSON.stringify(data) } function parseData(data: string): IData { const str = data return JSON.parse(str) } return async (ctx: Context, next: Next) => { if (typeof enable === "function" ? !enable() : !enable) { await next() return } if (isGetPublicKeyRoute(ctx)) { console.log(chalk.white("GET"), chalk.white.dim("/publickey")) ctx.body = publicKey return } const prefix = "/secure/" const enced = decodeURIComponent(ctx.path.slice(prefix.length)) const secured = ctx.path.startsWith(prefix) if (secured) { const res = decryptRSA(enced) if (!res) { ctx.status = 403 ctx.body = { code: "EHTTPSECURE" } return } const decoded = JSON.parse(res) ctx.path = decoded.url const key = decoded.key ctx.originalUrl = "[secure]" + ctx.path ctx.request.body = ctx.request.method !== "GET" && parseData(decryptAES((ctx.request.body as any).content, key)).data await next() const content = encryptAES(stringifyData({ data: ctx.body }), key) ctx.body = { content } } else { await next() return } } } export default secure ================================================ FILE: server/src/middlewares/auth.ts ================================================ import { AuthenticationError, createAuth } from '@winwin/koa-authentication' import { container } from 'tsyringe' import { AccountService } from '@server-shared/account-storage-service' import { AuthStorageService } from '@server/services/auth-storage-service' import { LogService } from '@server-shared/log-service' export const auth = createAuth({ verify(username, password) { const logger = container.resolve(LogService) const account = container.resolve(AccountService) const res = account.verify(username, password) logger.log(`verify ${res ? 'success' : 'failed'} for ${username}`) return res }, secret() { return container.resolve(AuthStorageService).getSecret() }, }) auth.router.post('/password', auth.auth, (ctx) => { const account = container.resolve(AccountService) const { oldPassword, password } = ctx.request.body as { oldPassword: string, password: string } const verified = account.verify(ctx.state.user!.username, oldPassword) if (!verified) throw new AuthenticationError() account.setPassword(password) ctx.status = 200 }) auth.router.post( '/username', auth.auth, (ctx, next) => { const account = container.resolve(AccountService) const { username } = ctx.request.body as { username: string } account.setUsername(username) ctx.state.user!.username = username return next() }, auth.cookie, ctx => (ctx.status = 200), ) ================================================ FILE: server/src/middlewares/statics.ts ================================================ import path from "path" import serve from "koa-static" const ROOT = path.resolve(process.cwd(), "../client/dist") export const statics = serve(ROOT, { setHeaders: (res, fullpath) => { const isHtml = path.extname(fullpath).toLowerCase() === ".html" if (isHtml) res.setHeader("Cache-Control", "no-cache") else res.setHeader("Cache-Control", "max-age=31536000") }, }) ================================================ FILE: server/src/routes/git.ts ================================================ import { container } from "tsyringe" import Router from "@koa/router" import { GitService } from "@server/services/git-service" const router = new Router() router.prefix("/git") router.post("/sync", async (ctx) => { const git = container.resolve(GitService) await git.sync() ctx.status = 200 }) router.post("/save", async (ctx) => { const git = container.resolve(GitService) await git.save() ctx.status = 200 }) export default router ================================================ FILE: server/src/routes/health.ts ================================================ import { Context } from "koa" import Router from "@koa/router" const router = new Router() router.get("/health", (ctx: Context) => { ctx.status = 200 }) export default router ================================================ FILE: server/src/routes/hexo.ts ================================================ import { Context } from "koa" import { container } from "tsyringe" import Router from "@koa/router" import { HexoService } from "@server/services/hexo-service" import { PostOrPageNotFoundError } from "../errors" const router = new Router() router.prefix("/hexo") router.get("/posts", async (ctx: Context) => { const hexo = container.resolve(HexoService) ctx.body = await hexo.listPost() }) router.get("/post/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params if (!source) { ctx.status = 400 ctx.body = "need `source`" } const post = await hexo.getPostBySource(decodeURIComponent(source)) if (!post) throw new PostOrPageNotFoundError("post") ctx.body = post }) router.get("/pages", async (ctx: Context) => { const hexo = container.resolve(HexoService) ctx.body = await hexo.listPage() }) router.get("/page/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params if (!source) { ctx.status = 400 ctx.body = "need `source`" } ctx.body = await hexo.getPageBySource(decodeURIComponent(source)) }) router.get("/tags", async (ctx: Context) => { const hexo = container.resolve(HexoService) ctx.body = await hexo.listTag() }) router.get("/categories", async (ctx: Context) => { const hexo = container.resolve(HexoService) ctx.body = await hexo.listCategory() }) router.post("/deploy", async (ctx: Context) => { const hexo = container.resolve(HexoService) await hexo.deploy(ctx.request.body) ctx.status = 200 }) router.post("/generate", async (ctx: Context) => { const hexo = container.resolve(HexoService) await hexo.generate(ctx.request.body) ctx.status = 200 }) router.post("/clean", async (ctx: Context) => { const hexo = container.resolve(HexoService) await hexo.clean() ctx.status = 200 }) router.post("/publish", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { filename, layout } = ctx.request.body if (!filename) { ctx.status = 400 ctx.body = "need `filename`" return } ctx.body = await hexo.publish(filename, layout) }) router.post("/create", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { title, layout, path, slug, replace } = ctx.request.body if (!title) { ctx.status = 400 ctx.body = "need `title`" return } ctx.body = await hexo.create(title, { layout, path, slug, replace }) }) router.put("/post/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params const { raw } = ctx.request.body if (!source || !raw) { ctx.status = 400 ctx.body = "need `source` and `raw`" return } ctx.body = await hexo.update(source, raw, "post") }) router.put("/page/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params const { raw } = ctx.request.body if (!source || !raw) { ctx.status = 400 ctx.body = "need `source` and `raw`" return } ctx.body = await hexo.update(source, raw, "page") }) router.delete("/post/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params if (!source) { ctx.status = 400 ctx.body = "need `source` " return } ctx.body = await hexo.delete(source, "post") }) router.delete("/page/:source", async (ctx: Context) => { const hexo = container.resolve(HexoService) const { source } = ctx.params if (!source) { ctx.status = 400 ctx.body = "need `source` " return } ctx.body = await hexo.delete(source, "page") }) export default router ================================================ FILE: server/src/routes/index.ts ================================================ import Router from "@koa/router" import health from "./health" import hexo from "./hexo" import git from "./git" import settings from "./settings" import template from "./template" import { auth } from "../middlewares/auth" const router = new Router() router.use(auth.auth) router.use(health.routes()) router.use(hexo.routes()) router.use(git.routes()) router.use(settings.routes()) router.use(template.routes()) export default router ================================================ FILE: server/src/routes/settings.ts ================================================ import { container } from "tsyringe" import Router from "@koa/router" import { SettingsService } from "@server/services/settings-service" const router = new Router() router.get("/settings", async (ctx) => { const settingsService = container.resolve(SettingsService) ctx.body = await settingsService.get() }) router.post("/settings", async (ctx) => { const settingsService = container.resolve(SettingsService) const settings = ctx.request.body ?? {} await settingsService.set(settings) ctx.status = 200 }) export default router ================================================ FILE: server/src/routes/template.ts ================================================ import Router from "@koa/router" import { container } from "tsyringe" import { FrontmatterTemplateService } from "@server/services/frontmatter-template-service" import { InvalidOptionsError } from "@server/errors" const router = new Router() router.prefix("/template") router.get("/frontmatter", async (ctx) => { const frontmatterTemplateService = container.resolve( FrontmatterTemplateService ) const items = await frontmatterTemplateService.list() ctx.body = { items } }) router.post("/frontmatter", async (ctx) => { const items = ctx.request.body?.items if (!items) throw new InvalidOptionsError("`raw` is required") const frontmatterTemplateService = container.resolve( FrontmatterTemplateService ) await frontmatterTemplateService.set(items) ctx.status = 200 ctx.body = { message: "OK" } }) export default router ================================================ FILE: server/src/services/auth-storage-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import { LogService } from "@server-shared/log-service" import { StorageService } from "@server-shared/storage-service" interface IAuthInfo { secret: string expiresIn: string refreshableIn: string } @injectable() @singleton() export class AuthStorageService { public static KEY = "authinfo" constructor( @inject(StorageService) private _storage: StorageService, @inject(LogService) private _logService: LogService ) { this._logService.setScope("auth-storage-service") } private _toStorage(info: IAuthInfo) { this._storage.set(AuthStorageService.KEY, info) } private _fromStorage(): IAuthInfo { const { secret = "secret", expiresIn = "1h", refreshableIn = "7d", } = this._storage.get(AuthStorageService.KEY) || {} return { secret, expiresIn, refreshableIn } } public setAuthInfo(info: IAuthInfo) { this._toStorage(info) this._logService.log(`set auth info`) } public getSecret() { const s = this._fromStorage().secret this._logService.log(`get secret`) return s } public getAuthInfo(): IAuthInfo { const s = this._fromStorage() this._logService.log(`get auth info`) return s } } ================================================ FILE: server/src/services/block-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import { LogService } from "@server-shared/log-service" import { StorageService } from "@server-shared/storage-service" type BlockInfo = string[] @injectable() @singleton() export class BlockService { public static KEY = "blocklist" constructor( @inject(StorageService) private _storage: StorageService, @inject(LogService) private _logService: LogService ) { this._logService.setScope("block-service") } private _toStorage(info: BlockInfo) { this._storage.set(BlockService.KEY, info) } private _fromStorage(): BlockInfo { return this._storage.get(BlockService.KEY) || [] } isBlocked(token: string) { this._logService.log("query token") return this._fromStorage().includes(token) } block(tokens: string[]) { const blocked = this._fromStorage() blocked.push(...tokens) this._toStorage(blocked) this._logService.log("block tokens", tokens.length) } clear() { this._toStorage([]) // FIXME 按照过期时间清理 this._logService.log("clear block") } } ================================================ FILE: server/src/services/exec-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import { LogService } from "@server-shared/log-service" import { StorageService } from "@server-shared/storage-service" import { execaCommand } from "execa" import { toRealPath } from "@server-shared/utils" import { HEXO_BASE_DIR_KEY } from "@server-shared/constants" @injectable() @singleton() export class ExecService { constructor( @inject(StorageService) private _storage: StorageService, @inject(LogService) private _logService: LogService ) { this._logService.setScope("exec-service") } private _getCwd() { return this._storage.get(HEXO_BASE_DIR_KEY) } public async run(command: string) { if (!command) return const cwd = toRealPath(this._getCwd()) return execaCommand(command, { cwd, stdio: "inherit" }) } } ================================================ FILE: server/src/services/frontmatter-template-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import { IFrontmatterTemplateItem } from "@shared/types/api" import { StorageService } from "@server-shared/storage-service" @injectable() @singleton() export class FrontmatterTemplateService { public static KEY = "frontmatter-template" constructor( @inject(StorageService) private _storageService: StorageService ) {} _list() { return ( this._storageService.get( FrontmatterTemplateService.KEY ) || [] ) } _set(items: IFrontmatterTemplateItem[]) { this._storageService.set( FrontmatterTemplateService.KEY, items ) } async list() { return this._list() } async set(items: IFrontmatterTemplateItem[]) { this._set(items) } } ================================================ FILE: server/src/services/git-service.ts ================================================ import { inject , injectable , singleton } from "tsyringe" import { StorageService } from "@server-shared/storage-service" import { scriptStore } from "@server-shared/store" import { toRealPath } from "@server-shared/utils" import { LogService } from "@server-shared/log-service" import { run } from "@server/utils/exec" import { ScriptError } from "../errors" import { ExecService } from "./exec-service" import { HexoInstanceService } from "./hexo-instance-service" import { HEXO_BASE_DIR_KEY } from "@server-shared/constants" async function isClean(repoPath: string) { return !(await run("git", ["status", "-s"], { cwd: repoPath })) } async function hasRepo(repoPath: string) { return run("git", ["rev-parse", "--is-inside-work-tree"], { cwd: repoPath, }).then( () => true, () => false ) } async function hasRemtoe(repoPath: string) { return !!(await run("git", ["remote", "-v"], { cwd: repoPath })) } @injectable() @singleton() export class GitService { constructor( @inject(StorageService) private storage: StorageService, @inject(LogService) private _logService: LogService, @inject(ExecService) private _execService: ExecService ) { this._logService.setScope("git-service") } async sync() { if (scriptStore.hasScript("git-sync")) return this._execService .run(scriptStore.getScript("git-sync")) .catch((err) => { this._logService.error(err) throw new ScriptError( "fail to run git sync script", "GitSyncScriptError" ) }) const base = this.storage.get(HEXO_BASE_DIR_KEY) const cwd = toRealPath(base) if (!(await hasRepo(cwd))) { this._logService.log("not git repo, skipped") return } await run("git", ["reset", "--hard"], { cwd }).catch((err) => { this._logService.error(err) this._logService.error("git reset hard error") throw err }) this._logService.log("git reset succeed") if (await hasRemtoe(cwd)) { await run("git", ["pull"], { cwd }).catch((err) => { this._logService.error(err) this._logService.error("git pull error") throw err }) } else { this._logService.log("no remote detected, skip pull") } this._logService.log("sync succeed") } async save() { if (scriptStore.hasScript("git-save")) return this._execService .run(scriptStore.getScript("git-save")) .catch((err) => { this._logService.error(err) throw new ScriptError( "fail to run git save script", "GitSaveScriptError" ) }) const base = this.storage.get(HEXO_BASE_DIR_KEY) const cwd = toRealPath(base) if (!(await hasRepo(cwd))) { this._logService.log("not git repo, skipped") return } if (await isClean(cwd)) { this._logService.log("work space clean no need to save") return } await run("git", ["add", ".", "--all"], { cwd }).catch((err) => { this._logService.error(err) this._logService.error("git add all error") throw err }) this._logService.log("git add succeed") await run( "git", ["commit", "-m", `server update ${new Date().toString()}`], { cwd } ).catch((err) => { this._logService.error(err) this._logService.error("git commit error") throw err }) this._logService.log("git commit succeed") if (await hasRemtoe(cwd)) { await run("git", ["push"], { cwd }).catch((err) => { this._logService.error(err) this._logService.error("git push error") throw err }) } else { this._logService.log("no remote detected, skip push") } this._logService.log("save succeed") } } ================================================ FILE: server/src/services/hexo-instance-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import HexoCore from "hexo" import path from "path" import { LogService } from "@server-shared/log-service" import { IStorageService, StorageService } from "@server-shared/storage-service" import { isBlog, toRealPath } from "@server-shared/utils" import { DEV } from "../utils" import { HexoInitError } from "../errors" import { HEXO_BASE_DIR_KEY, HEXO_OPTIONS_KEY } from "@server-shared/constants" @injectable() @singleton() export class HexoInstanceService { static INITING = false static PENDING_COUNT = 0 static RETRY_INTERVAL = 1000 static MAX_RETRY = 2 static CURRENT_RETRY = 0 static TO_BE_CLEANED = 0 private _options: HexoCore.InstanceOptions | null = null private _base: string | null = null private _hexo: HexoCore | null = null private _ready = false private _promise: any | null = null constructor( @inject(LogService) private _logService: LogService, @inject(StorageService) private _storageService: IStorageService ) { this._logService.setScope("hexo-instance-service") } private _withOptionsOverrides(options: HexoCore.InstanceOptions) { return { ...options, draft: true, drafts: true } } private _setHexoBase() { const base = this._storageService.get(HEXO_BASE_DIR_KEY) const base_dir = path.resolve(__dirname, toRealPath(base)) if (!isBlog(base_dir)) throw new Error(`"${base_dir}" is not a hexo blog folder`) this._base = base_dir } private _setOptions() { this._options = this._storageService.get(HEXO_OPTIONS_KEY) || {} this._options.silent = DEV ? false : this._options.silent } private _createHexoInstance() { if (!this._base) throw new Error("please set hexo root first") this._hexo = new HexoCore( this._base, this._withOptionsOverrides(this._options!) ) } private async _init(): Promise { this._logService.log("real init start") this._ready = false await this._setHexoBase() await this._setOptions() await this._createHexoInstance() await this._hexo!.init() await this._hexo!.watch() this._ready = true this._logService.log("real init finished") } async setOptions(options: HexoCore.InstanceOptions) { this._storageService.set( HEXO_OPTIONS_KEY, options ) this._logService.log("options set") } async _tryInit(count = HexoInstanceService.MAX_RETRY) { try { await this._init() HexoInstanceService.INITING = false } catch (err) { this._logService.error(err) this._logService.error(`error when init hexo instance. `) this._logService.error( `retry in ${HexoInstanceService.RETRY_INTERVAL} ms.`, `${count} retry left` ) if (count) return new Promise((resolve) => { setTimeout(() => { resolve(this._tryInit(count - 1)) }, HexoInstanceService.RETRY_INTERVAL) }) else { HexoInstanceService.INITING = false // String(err) 用于向客户端显示详细报错信息 throw new HexoInitError(String(err)) } } } async init(): Promise { if (!HexoInstanceService.INITING) this._promise = this._tryInit() return this._promise } async getBaseDir() { if (!this._ready) await this.init() return this._base! } async getInstance() { if (!this._ready) await this.init() this._logService.log("instance required") return this._hexo! } async getInstanceWithOriginOptions( genOptions: ( options: HexoCore.InstanceOptions ) => HexoCore.InstanceOptions = (o) => o ) { if (!this._ready) await this.init() const newOptions = genOptions(this._options!) const hexo = new HexoCore(this._base!, newOptions) await hexo.init() await hexo.watch() HexoInstanceService.TO_BE_CLEANED++ this._logService.log("instance with options required") this._logService.log( `${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned` ) const cleanup = async () => { await hexo.unwatch() HexoInstanceService.TO_BE_CLEANED-- this._logService.log("instance with options cleaned") if (HexoInstanceService.TO_BE_CLEANED === 0) { this._logService.log("all instances have been cleaned") } else { this._logService.log( `${HexoInstanceService.TO_BE_CLEANED} extra instance to be cleaned` ) } } return { hexo, cleanup } } async runBetweenReload(fn: () => T | Promise): Promise { if (!this._ready) await this.init() const unload = async () => { await this._hexo!.unwatch() } const load = async () => { await this._hexo!.watch() HexoInstanceService.INITING = false } const markHexoInitError = (err: any) => { this._ready = false HexoInstanceService.INITING = false } HexoInstanceService.INITING = true await unload().catch(markHexoInitError) const res = await Promise.resolve(fn()) await load().catch(markHexoInitError) return res } } ================================================ FILE: server/src/services/hexo-service.ts ================================================ import path from "path" import { inject, injectable, singleton } from "tsyringe" import fs from "fs" import HexoCore from "hexo" import { BRIEF_LENGTH } from "@server-shared/constants" import { InvalidOptionsError, PostOrPageNotFoundError, ScriptError, } from "@server/errors" import { HexoInstanceService } from "@server/services/hexo-instance-service" import { LogService } from "@server-shared/log-service" import { BriefPage, BriefPost, Category, Page, Post, Tag } from "@shared/types/hexo" import { expandHomeDir } from "@server/utils" import { run } from "@server/utils/exec" import { HexoPage, HexoPost, toCategory, toPage, toPost, toTag, } from "@server/utils/hexo" import { scriptStore } from "@server-shared/store" import { ExecService } from "./exec-service" interface IHexoAPI { listPost(): Promise listPage(): Promise listCategory(): Promise listTag(): Promise getPostBySource(source: string): Promise getPageBySource(source: string): Promise } interface ICreateOptions { layout?: string path?: string slug?: string replace?: boolean } interface IHexoCommand { deploy(options?: IDeployOptions): Promise generate(): Promise clean(): Promise } interface IDeployOptions { generate?: boolean } interface IGenerateOptions { deploy?: boolean watch?: boolean bail?: boolean force?: boolean concurrency?: boolean } interface WithCategoriesTagsBriefArticleList { article: T posts: BriefPost[] pages: BriefPage[] categories: Category[] tags: Tag[] } interface IHexoCli { publish( source: string, layout?: string ): Promise> create( title: string, options?: ICreateOptions ): Promise> update( source: string, raw: string, type: "post" | "page" ): Promise> delete( source: string, type: "post" | "page" ): Promise> } /** * A page object can be asset. e.g. css file. we need to filter it out. * @param pageOrAsset * @returns */ function isAsset(pageOrAsset: HexoPage) { return pageOrAsset.layout === "false" } function transformPost(doc: HexoPost): Post { return { ...doc, __post: true, slug: doc.slug, date: doc.date.toString(), updated: doc.updated?.toString(), prev: doc.prev?.source, next: doc.next?.source, tags: doc.tags.data.map((t) => t.slug), categories: doc.categories?.data.map((c) => c.slug), brief: doc._content.slice(0, BRIEF_LENGTH), } } function transformPostToBrief({ _content, content, raw, ...doc }: Post): BriefPost { return { ...doc, brief: _content.slice(0, BRIEF_LENGTH) } } function transformPage(doc: HexoPage) { return { ...doc, __page: true, slug: doc.slug, date: doc.date.toString(), updated: doc.updated?.toString(), prev: doc.prev?.source, next: doc.next?.source, brief: doc._content.slice(0, BRIEF_LENGTH), } } function transformPageToBrief({ _content, content, raw, ...doc }: Page): BriefPage { return { ...doc, brief: _content.slice(0, BRIEF_LENGTH) } } @injectable() @singleton() export class HexoService implements IHexoAPI, IHexoCommand, IHexoCli { constructor( @inject(LogService) private _logService: LogService, @inject(HexoInstanceService) private _hexoInstanceService: HexoInstanceService, @inject(ExecService) private _execService: ExecService ) { this._logService.setScope("hexo-service") } //#region helpers private async runWithoutModifiedOption( fn: (hexo: HexoCore) => Promise ) { const { hexo, cleanup } = await this._hexoInstanceService.getInstanceWithOriginOptions() await fn(hexo) await cleanup() } private async getPostByFullSource(fullSource: string) { const hexo = await this._hexoInstanceService.getInstance() const post = hexo.locals .get("posts") .toArray() .find((item) => item.full_source === fullSource)! return this.getPostBySource(post.source) } private async getPostOrPageByFullSource(fullSource: string) { const hexo = await this._hexoInstanceService.getInstance() const post = hexo.locals .get("posts") .toArray() .find((item) => item.full_source === fullSource)! if (post) return this.getPostBySource(post.source) const page = hexo.locals .get("pages") .toArray() .find((item) => item.full_source === fullSource)! return this.getPageBySource(page.source) } private writeFile(fullPath: string, content: string) { try { fs.writeFileSync(fullPath, content) } catch (e) { this._logService.error("fail to write file") throw e } } private deleteFile(fullPath: string) { try { fs.rmSync(fullPath) } catch (e) { this._logService.error("fail to delete file") throw e } } private async getFullPathBySource(source: string, type: "post" | "page") { const hexo = await this._hexoInstanceService.getInstance() if (type === "post") return hexo.locals .get("posts") .toArray() .find((item) => item.source === source)?.full_source else return hexo.locals .get("pages") .toArray() .find((item) => item.source === source)?.full_source } private async WithCategoriesTagsBriefArticleList( article: T ): Promise> { const categories = await this.listCategory() const tags = await this.listTag() const pages = await this.listPage() const posts = await this.listPost() return { article, categories, tags, pages, posts } } //#endregion //#region IHexoAPI async listPost() { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals.get("posts").toArray().map(toPost) const res = docs.map((postDoc) => { const post: BriefPost = transformPostToBrief(transformPost(postDoc)) delete post.content delete post._content delete post.raw delete post.more return post }) this._logService.log("list post", res.length) return res } async getPostBySource(source: string): Promise { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals.get("posts").toArray().map(toPost) const doc = docs.find((item) => item.source === source) if (!doc) return const res = transformPost(doc) this._logService.log("get post by source", source) return res } async listPage() { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals .get("pages") .toArray() .map(toPage) .filter((doc) => !isAsset(doc)) const res = docs.map((pageDoc) => { const page: BriefPage = transformPageToBrief(transformPage(pageDoc)) delete page.content delete page._content delete page.raw delete page.more return page }) this._logService.log("list page", res.length) return res } async getPageBySource(source: string): Promise { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals.get("pages").toArray().map(toPage) const doc = docs.find((item) => item.source === source) if (!doc || isAsset(doc)) throw new PostOrPageNotFoundError("page") const res = transformPage(doc) this._logService.log("get page by source", source) return res } async listCategory(): Promise { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals.get("categories").toArray().map(toCategory) const res = docs.map((categoryDoc) => ({ ...categoryDoc, posts: categoryDoc.posts.map((p) => p.slug), })) this._logService.log("list category", res.length) return res } async listTag(): Promise { const hexo = await this._hexoInstanceService.getInstance() const docs = hexo.locals.get("tags").toArray().map(toTag) const res = docs.map((tagDoc) => ({ ...tagDoc, posts: tagDoc.posts.map((p) => p.slug), })) this._logService.log("list tag", res.length) return res } //#endregion //#region IHexoCommand async deploy(options: IDeployOptions = {}) { if (scriptStore.hasScript("hexo-deploy")) { await this._execService .run(scriptStore.getScript("hexo-deploy")) .catch((err) => { this._logService.error(err) throw new ScriptError( "fail to run hexo deploy script", "HexoDeployScriptError" ) }) return } const { generate = false } = options const args: string[] = [] if (generate) args.push("--generate") this.runWithoutModifiedOption(async (hexo) => { await hexo.call("deploy", { _: args }) await hexo.exit() }) this._logService.log(`run hexo deploy with args:`, args.join(" ")) } async generate(options: IGenerateOptions = {}) { if (scriptStore.hasScript("hexo-generate")) { await this._execService .run(scriptStore.getScript("hexo-generate")) .catch((err) => { this._logService.error(err) throw new ScriptError( "fail to run hexo generate script", "HexoGenerateScriptError" ) }) return } const { deploy = false, watch = false, bail = false, force = false, concurrency = false, } = options const args: string[] = [] if (deploy) args.push("--deploy") if (watch) args.push("--watch") if (bail) args.push("--bail") if (force) args.push("--force") this.runWithoutModifiedOption(async (hexo) => { if (concurrency) args.push("--concurrency") await hexo.call("generate", { _: args }) await hexo.exit() }) this._logService.log(`run hexo generate with args:`, args.join(" ")) } async clean() { if (scriptStore.hasScript("hexo-clean")) { await this._execService .run(scriptStore.getScript("hexo-clean")) .catch((err) => { this._logService.error(err) throw new ScriptError( "fail to run hexo clean script", "HexoCleanScriptError" ) }) return } this.runWithoutModifiedOption(async (hexo) => { await hexo.call("clean") await hexo.exit() }) this._logService.log("run hexo clean") } //#endregion //#region IHexoCli async publish(filename: string, layout?: string) { const args: string[] = ["publish"] if (layout) args.push(layout) args.push(filename) const info = await this._hexoInstanceService.runBetweenReload( async () => await run("hexo", args, { cwd: await this._hexoInstanceService.getBaseDir(), stripAnsi: true, }) ) const fullSource = expandHomeDir(info.split("Published: ")[1].trim()) const article = (await this.getPostByFullSource(fullSource))! const res = await this.WithCategoriesTagsBriefArticleList(article) this._logService.log(`publish ${filename} with layout: ${layout}`) return res } async create(title: string, options: ICreateOptions = {}) { const args: string[] = ["new"] if (options.layout) args.push(options.layout) if (options.path) { const base = await this._hexoInstanceService.getBaseDir() const fullPath = path.resolve(base, options.path) const relative = path.relative(fullPath, base) if (!relative.startsWith("..")) { this._logService.error(`${fullPath} is not valid`) throw new InvalidOptionsError( `${options.path} is not inside hexo blog folder`, "InvalidCreatePathError" ) } args.push("--path") args.push(options.path) } if (options.replace) args.push("--replace") if (options.slug) { args.push("--slug") args.push(options.slug) } if (title) args.push(title) const info = await this._hexoInstanceService.runBetweenReload(async () => { return await run("hexo", args, { cwd: await this._hexoInstanceService.getBaseDir(), stripAnsi: true, }) }) const fullSource = expandHomeDir(info.split("Created: ")[1].trim()) const article = (await this.getPostOrPageByFullSource(fullSource))! const res = this.WithCategoriesTagsBriefArticleList(article) this._logService.log("create succeed", fullSource) return res } async update( source: string, raw: string, type: "post" ): Promise> async update( source: string, raw: string, type: "page" ): Promise> async update(source: string, raw: string, type: "post" | "page") { const fullPath = await this.getFullPathBySource(source, type) if (!fullPath) throw new PostOrPageNotFoundError(type) await this._hexoInstanceService.runBetweenReload(() => { this.writeFile(fullPath, raw) }) this._logService.log(`${type} update succeed`, fullPath) if (type === "post") { return this.WithCategoriesTagsBriefArticleList(await this.getPostBySource(source)!) } else { return this.WithCategoriesTagsBriefArticleList(await this.getPageBySource(source))! } } async delete( source: string, type: "post" | "page" ): Promise> { const fullPath = await this.getFullPathBySource(source, type) if (!fullPath) throw new PostOrPageNotFoundError(type) await this._hexoInstanceService.runBetweenReload(async () => { await this.deleteFile(fullPath) }) return this.WithCategoriesTagsBriefArticleList(undefined) } //#endregion } ================================================ FILE: server/src/services/settings-service.ts ================================================ import { ISettings } from "@shared/types/api" import { inject, injectable, singleton } from "tsyringe" import { StorageService } from "@server-shared/storage-service" @injectable() @singleton() export class SettingsService { public static KEY = "settings" constructor( @inject(StorageService) private _storageService: StorageService ) {} async get(): Promise> { return ( this._storageService.get>(SettingsService.KEY) || {} ) } async set(settings: Partial) { this._storageService.set>(SettingsService.KEY, settings) } } ================================================ FILE: server/src/types/node-jsencrypt/index.d.ts ================================================ declare module "node-jsencrypt" { import JSEncrypt from "jsencrypt" export default JSEncrypt } ================================================ FILE: server/src/utils/exec.ts ================================================ import { execa, Options } from "execa" import strip from "strip-ansi" import { container } from "tsyringe" import { LogService } from "@server-shared/log-service" const execLogService = container.resolve(LogService) execLogService.setScope("exec-service") export async function run( command: string, args: string[] = [], opt: Options & { stripAnsi?: boolean } = { stripAnsi: false } ) { const { stripAnsi, ...execOpt } = opt execLogService.log(`run ${command} ${args.join(" ")}`) const { stdout } = await execa(command, args, { ...execOpt, stdio: "pipe" }) if (stripAnsi) return strip(stdout) return stdout } ================================================ FILE: server/src/utils/hexo.ts ================================================ import Hexo from "hexo" interface Query { data: T[] length: number } export interface HexoArticle { _id: string slug: string title: string date: number updated?: number | undefined comments: boolean layout: string content: string _content: string excerpt?: string | undefined more?: string | undefined source: string full_source: string path: string permalink: string prev?: HexoPage | undefined next?: HexoPage | undefined raw?: string | undefined photos?: string[] | undefined link?: string | undefined [key: string]: any } export interface HexoPage extends HexoArticle { __page: boolean } export interface HexoPost extends HexoArticle { published?: boolean | undefined categories?: Query | undefined tags: Query __post: boolean } export interface HexoTag { _id: string name: string slug: string path: string permalink: string posts: HexoPost[] length: number } export interface HexoCategory extends HexoTag { parent: string } export const toPost = (post: Hexo.Locals.Post) => post as unknown as HexoPost export const toPage = (post: Hexo.Locals.Page) => post as unknown as HexoPage export const toCategory = (post: Hexo.Locals.Category) => post as unknown as HexoCategory export const toTag = (post: Hexo.Locals.Tag) => post as unknown as HexoTag ================================================ FILE: server/src/utils.ts ================================================ import Debug from "debug" import path from "path" export const DEV = process.env.NODE_ENV !== "production" export function createDebug(scope: string) { return Debug(`@hexon/server:${scope}`) } export const noop = () => {} export function expandHomeDir(fullpath: string) { const homedir = process.env[process.platform == "win32" ? "USERPROFILE" : "HOME"]! if (!fullpath) return fullpath if (fullpath == "~") return homedir if (fullpath.slice(0, 2) != "~/") return fullpath return path.join(homedir, fullpath.slice(2)) } ================================================ FILE: server-scripts/assets/logo.art ================================================ ░░ ░░ ░░░░░░░ ░░ ░░ ░░░░░░ ░░░ ░░ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒▒▒ ▒▒ ▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▒▒ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ▓▓ ██ ██ ███████ ██ ██ ██████ ██ ████ ================================================ FILE: server-scripts/bin/index.js ================================================ "use strict"; var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); var __decorateClass = (decorators, target, key, kind) => { var result = kind > 1 ? void 0 : kind ? __getOwnPropDesc(target, key) : target; for (var i = decorators.length - 1, decorator; i >= 0; i--) if (decorator = decorators[i]) result = (kind ? decorator(target, key, result) : decorator(result)) || result; if (kind && result) __defProp(target, key, result); return result; }; var __decorateParam = (index, decorator) => (target, key) => decorator(target, key, index); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.prod.js var require_shared_cjs_prod = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.prod.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const set = new Set(str.split(",")); return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); } var EMPTY_OBJ = {}; var EMPTY_ARR = []; var NOOP = () => { }; var NO = () => false; var isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => toTypeString(val) === "[object Date]"; var isRegExp = (val) => toTypeString(val) === "[object RegExp]"; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap( ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" ); var isBuiltInDirective = /* @__PURE__ */ makeMap( "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" ); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction( (str) => str.replace(hyphenateRE, "-$1").toLowerCase() ); var capitalize = cacheStringFunction((str) => { return str.charAt(0).toUpperCase() + str.slice(1); }); var toHandlerKey = cacheStringFunction((str) => { const s = str ? `on${capitalize(str)}` : ``; return s; }); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var looseToNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var toNumber = (val) => { const n = isString(val) ? Number(val) : NaN; return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; var identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; function genPropsAccessExp(name) { return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`; } var PatchFlags = { "TEXT": 1, "1": "TEXT", "CLASS": 2, "2": "CLASS", "STYLE": 4, "4": "STYLE", "PROPS": 8, "8": "PROPS", "FULL_PROPS": 16, "16": "FULL_PROPS", "NEED_HYDRATION": 32, "32": "NEED_HYDRATION", "STABLE_FRAGMENT": 64, "64": "STABLE_FRAGMENT", "KEYED_FRAGMENT": 128, "128": "KEYED_FRAGMENT", "UNKEYED_FRAGMENT": 256, "256": "UNKEYED_FRAGMENT", "NEED_PATCH": 512, "512": "NEED_PATCH", "DYNAMIC_SLOTS": 1024, "1024": "DYNAMIC_SLOTS", "DEV_ROOT_FRAGMENT": 2048, "2048": "DEV_ROOT_FRAGMENT", "HOISTED": -1, "-1": "HOISTED", "BAIL": -2, "-2": "BAIL" }; var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `NEED_HYDRATION`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var ShapeFlags = { "ELEMENT": 1, "1": "ELEMENT", "FUNCTIONAL_COMPONENT": 2, "2": "FUNCTIONAL_COMPONENT", "STATEFUL_COMPONENT": 4, "4": "STATEFUL_COMPONENT", "TEXT_CHILDREN": 8, "8": "TEXT_CHILDREN", "ARRAY_CHILDREN": 16, "16": "ARRAY_CHILDREN", "SLOTS_CHILDREN": 32, "32": "SLOTS_CHILDREN", "TELEPORT": 64, "64": "TELEPORT", "SUSPENSE": 128, "128": "SUSPENSE", "COMPONENT_SHOULD_KEEP_ALIVE": 256, "256": "COMPONENT_SHOULD_KEEP_ALIVE", "COMPONENT_KEPT_ALIVE": 512, "512": "COMPONENT_KEPT_ALIVE", "COMPONENT": 6, "6": "COMPONENT" }; var SlotFlags = { "STABLE": 1, "1": "STABLE", "DYNAMIC": 2, "2": "DYNAMIC", "FORWARDED": 3, "3": "FORWARDED" }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; var isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); var isGloballyWhitelisted = isGloballyAllowed; var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push( `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` ); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max( 1, end > count ? lineLength - pad : end - start ); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value) || isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:([^]+)/; var styleCommentRE = /\/\*[^]*?\*\//g; function parseStringStyle(cssText) { const ret = {}; cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles3) { let ret = ""; if (!styles3 || isString(styles3)) { return ret; } for (const key in styles3) { const value = styles3[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number") { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap( specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` ); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isKnownHtmlAttr = /* @__PURE__ */ makeMap( `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` ); var isKnownSvgAttr = /* @__PURE__ */ makeMap( `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` ); function isRenderableAttrValue(value) { if (value == null) { return false; } const type = typeof value; return type === "string" || type === "number" || type === "boolean"; } var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce( (entries, [key, val2], i) => { entries[stringifySymbol(key, i) + " =>"] = val2; return entries; }, {} ) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) }; } else if (isSymbol(val)) { return stringifySymbol(val); } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var stringifySymbol = (v, i = "") => { var _a; return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.PatchFlags = PatchFlags; exports.ShapeFlags = ShapeFlags; exports.SlotFlags = SlotFlags; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.genPropsAccessExp = genPropsAccessExp; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyAllowed = isGloballyAllowed; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isMathMLTag = isMathMLTag; exports.isModelListener = isModelListener; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isRegExp = isRegExp; exports.isRenderableAttrValue = isRenderableAttrValue; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.looseToNumber = looseToNumber; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.js var require_shared_cjs = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/dist/shared.cjs.js"(exports) { "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); function makeMap(str, expectsLowerCase) { const set = new Set(str.split(",")); return expectsLowerCase ? (val) => set.has(val.toLowerCase()) : (val) => set.has(val); } var EMPTY_OBJ = Object.freeze({}); var EMPTY_ARR = Object.freeze([]); var NOOP = () => { }; var NO = () => false; var isOn = (key) => key.charCodeAt(0) === 111 && key.charCodeAt(1) === 110 && (key.charCodeAt(2) > 122 || key.charCodeAt(2) < 97); var isModelListener = (key) => key.startsWith("onUpdate:"); var extend = Object.assign; var remove = (arr, el) => { const i = arr.indexOf(el); if (i > -1) { arr.splice(i, 1); } }; var hasOwnProperty = Object.prototype.hasOwnProperty; var hasOwn = (val, key) => hasOwnProperty.call(val, key); var isArray = Array.isArray; var isMap = (val) => toTypeString(val) === "[object Map]"; var isSet = (val) => toTypeString(val) === "[object Set]"; var isDate = (val) => toTypeString(val) === "[object Date]"; var isRegExp = (val) => toTypeString(val) === "[object RegExp]"; var isFunction = (val) => typeof val === "function"; var isString = (val) => typeof val === "string"; var isSymbol = (val) => typeof val === "symbol"; var isObject = (val) => val !== null && typeof val === "object"; var isPromise = (val) => { return (isObject(val) || isFunction(val)) && isFunction(val.then) && isFunction(val.catch); }; var objectToString = Object.prototype.toString; var toTypeString = (value) => objectToString.call(value); var toRawType = (value) => { return toTypeString(value).slice(8, -1); }; var isPlainObject = (val) => toTypeString(val) === "[object Object]"; var isIntegerKey = (key) => isString(key) && key !== "NaN" && key[0] !== "-" && "" + parseInt(key, 10) === key; var isReservedProp = /* @__PURE__ */ makeMap( ",key,ref,ref_for,ref_key,onVnodeBeforeMount,onVnodeMounted,onVnodeBeforeUpdate,onVnodeUpdated,onVnodeBeforeUnmount,onVnodeUnmounted" ); var isBuiltInDirective = /* @__PURE__ */ makeMap( "bind,cloak,else-if,else,for,html,if,model,on,once,pre,show,slot,text,memo" ); var cacheStringFunction = (fn) => { const cache = /* @__PURE__ */ Object.create(null); return (str) => { const hit = cache[str]; return hit || (cache[str] = fn(str)); }; }; var camelizeRE = /-(\w)/g; var camelize = cacheStringFunction((str) => { return str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : ""); }); var hyphenateRE = /\B([A-Z])/g; var hyphenate = cacheStringFunction( (str) => str.replace(hyphenateRE, "-$1").toLowerCase() ); var capitalize = cacheStringFunction((str) => { return str.charAt(0).toUpperCase() + str.slice(1); }); var toHandlerKey = cacheStringFunction((str) => { const s = str ? `on${capitalize(str)}` : ``; return s; }); var hasChanged = (value, oldValue) => !Object.is(value, oldValue); var invokeArrayFns = (fns, arg) => { for (let i = 0; i < fns.length; i++) { fns[i](arg); } }; var def = (obj, key, value) => { Object.defineProperty(obj, key, { configurable: true, enumerable: false, value }); }; var looseToNumber = (val) => { const n = parseFloat(val); return isNaN(n) ? val : n; }; var toNumber = (val) => { const n = isString(val) ? Number(val) : NaN; return isNaN(n) ? val : n; }; var _globalThis; var getGlobalThis = () => { return _globalThis || (_globalThis = typeof globalThis !== "undefined" ? globalThis : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : {}); }; var identRE = /^[_$a-zA-Z\xA0-\uFFFF][_$a-zA-Z0-9\xA0-\uFFFF]*$/; function genPropsAccessExp(name) { return identRE.test(name) ? `__props.${name}` : `__props[${JSON.stringify(name)}]`; } var PatchFlags = { "TEXT": 1, "1": "TEXT", "CLASS": 2, "2": "CLASS", "STYLE": 4, "4": "STYLE", "PROPS": 8, "8": "PROPS", "FULL_PROPS": 16, "16": "FULL_PROPS", "NEED_HYDRATION": 32, "32": "NEED_HYDRATION", "STABLE_FRAGMENT": 64, "64": "STABLE_FRAGMENT", "KEYED_FRAGMENT": 128, "128": "KEYED_FRAGMENT", "UNKEYED_FRAGMENT": 256, "256": "UNKEYED_FRAGMENT", "NEED_PATCH": 512, "512": "NEED_PATCH", "DYNAMIC_SLOTS": 1024, "1024": "DYNAMIC_SLOTS", "DEV_ROOT_FRAGMENT": 2048, "2048": "DEV_ROOT_FRAGMENT", "HOISTED": -1, "-1": "HOISTED", "BAIL": -2, "-2": "BAIL" }; var PatchFlagNames = { [1]: `TEXT`, [2]: `CLASS`, [4]: `STYLE`, [8]: `PROPS`, [16]: `FULL_PROPS`, [32]: `NEED_HYDRATION`, [64]: `STABLE_FRAGMENT`, [128]: `KEYED_FRAGMENT`, [256]: `UNKEYED_FRAGMENT`, [512]: `NEED_PATCH`, [1024]: `DYNAMIC_SLOTS`, [2048]: `DEV_ROOT_FRAGMENT`, [-1]: `HOISTED`, [-2]: `BAIL` }; var ShapeFlags = { "ELEMENT": 1, "1": "ELEMENT", "FUNCTIONAL_COMPONENT": 2, "2": "FUNCTIONAL_COMPONENT", "STATEFUL_COMPONENT": 4, "4": "STATEFUL_COMPONENT", "TEXT_CHILDREN": 8, "8": "TEXT_CHILDREN", "ARRAY_CHILDREN": 16, "16": "ARRAY_CHILDREN", "SLOTS_CHILDREN": 32, "32": "SLOTS_CHILDREN", "TELEPORT": 64, "64": "TELEPORT", "SUSPENSE": 128, "128": "SUSPENSE", "COMPONENT_SHOULD_KEEP_ALIVE": 256, "256": "COMPONENT_SHOULD_KEEP_ALIVE", "COMPONENT_KEPT_ALIVE": 512, "512": "COMPONENT_KEPT_ALIVE", "COMPONENT": 6, "6": "COMPONENT" }; var SlotFlags = { "STABLE": 1, "1": "STABLE", "DYNAMIC": 2, "2": "DYNAMIC", "FORWARDED": 3, "3": "FORWARDED" }; var slotFlagsText = { [1]: "STABLE", [2]: "DYNAMIC", [3]: "FORWARDED" }; var GLOBALS_ALLOWED = "Infinity,undefined,NaN,isFinite,isNaN,parseFloat,parseInt,decodeURI,decodeURIComponent,encodeURI,encodeURIComponent,Math,Number,Date,Array,Object,Boolean,String,RegExp,Map,Set,JSON,Intl,BigInt,console,Error"; var isGloballyAllowed = /* @__PURE__ */ makeMap(GLOBALS_ALLOWED); var isGloballyWhitelisted = isGloballyAllowed; var range = 2; function generateCodeFrame(source, start = 0, end = source.length) { let lines = source.split(/(\r?\n)/); const newlineSequences = lines.filter((_, idx) => idx % 2 === 1); lines = lines.filter((_, idx) => idx % 2 === 0); let count = 0; const res = []; for (let i = 0; i < lines.length; i++) { count += lines[i].length + (newlineSequences[i] && newlineSequences[i].length || 0); if (count >= start) { for (let j = i - range; j <= i + range || end > count; j++) { if (j < 0 || j >= lines.length) continue; const line = j + 1; res.push( `${line}${" ".repeat(Math.max(3 - String(line).length, 0))}| ${lines[j]}` ); const lineLength = lines[j].length; const newLineSeqLength = newlineSequences[j] && newlineSequences[j].length || 0; if (j === i) { const pad = start - (count - (lineLength + newLineSeqLength)); const length = Math.max( 1, end > count ? lineLength - pad : end - start ); res.push(` | ` + " ".repeat(pad) + "^".repeat(length)); } else if (j > i) { if (end > count) { const length = Math.max(Math.min(end - count, lineLength), 1); res.push(` | ` + "^".repeat(length)); } count += lineLength + newLineSeqLength; } } break; } } return res.join("\n"); } function normalizeStyle(value) { if (isArray(value)) { const res = {}; for (let i = 0; i < value.length; i++) { const item = value[i]; const normalized = isString(item) ? parseStringStyle(item) : normalizeStyle(item); if (normalized) { for (const key in normalized) { res[key] = normalized[key]; } } } return res; } else if (isString(value) || isObject(value)) { return value; } } var listDelimiterRE = /;(?![^(]*\))/g; var propertyDelimiterRE = /:([^]+)/; var styleCommentRE = /\/\*[^]*?\*\//g; function parseStringStyle(cssText) { const ret = {}; cssText.replace(styleCommentRE, "").split(listDelimiterRE).forEach((item) => { if (item) { const tmp = item.split(propertyDelimiterRE); tmp.length > 1 && (ret[tmp[0].trim()] = tmp[1].trim()); } }); return ret; } function stringifyStyle(styles3) { let ret = ""; if (!styles3 || isString(styles3)) { return ret; } for (const key in styles3) { const value = styles3[key]; const normalizedKey = key.startsWith(`--`) ? key : hyphenate(key); if (isString(value) || typeof value === "number") { ret += `${normalizedKey}:${value};`; } } return ret; } function normalizeClass(value) { let res = ""; if (isString(value)) { res = value; } else if (isArray(value)) { for (let i = 0; i < value.length; i++) { const normalized = normalizeClass(value[i]); if (normalized) { res += normalized + " "; } } } else if (isObject(value)) { for (const name in value) { if (value[name]) { res += name + " "; } } } return res.trim(); } function normalizeProps(props) { if (!props) return null; let { class: klass, style } = props; if (klass && !isString(klass)) { props.class = normalizeClass(klass); } if (style) { props.style = normalizeStyle(style); } return props; } var HTML_TAGS = "html,body,base,head,link,meta,style,title,address,article,aside,footer,header,hgroup,h1,h2,h3,h4,h5,h6,nav,section,div,dd,dl,dt,figcaption,figure,picture,hr,img,li,main,ol,p,pre,ul,a,b,abbr,bdi,bdo,br,cite,code,data,dfn,em,i,kbd,mark,q,rp,rt,ruby,s,samp,small,span,strong,sub,sup,time,u,var,wbr,area,audio,map,track,video,embed,object,param,source,canvas,script,noscript,del,ins,caption,col,colgroup,table,thead,tbody,td,th,tr,button,datalist,fieldset,form,input,label,legend,meter,optgroup,option,output,progress,select,textarea,details,dialog,menu,summary,template,blockquote,iframe,tfoot"; var SVG_TAGS = "svg,animate,animateMotion,animateTransform,circle,clipPath,color-profile,defs,desc,discard,ellipse,feBlend,feColorMatrix,feComponentTransfer,feComposite,feConvolveMatrix,feDiffuseLighting,feDisplacementMap,feDistantLight,feDropShadow,feFlood,feFuncA,feFuncB,feFuncG,feFuncR,feGaussianBlur,feImage,feMerge,feMergeNode,feMorphology,feOffset,fePointLight,feSpecularLighting,feSpotLight,feTile,feTurbulence,filter,foreignObject,g,hatch,hatchpath,image,line,linearGradient,marker,mask,mesh,meshgradient,meshpatch,meshrow,metadata,mpath,path,pattern,polygon,polyline,radialGradient,rect,set,solidcolor,stop,switch,symbol,text,textPath,title,tspan,unknown,use,view"; var MATH_TAGS = "annotation,annotation-xml,maction,maligngroup,malignmark,math,menclose,merror,mfenced,mfrac,mfraction,mglyph,mi,mlabeledtr,mlongdiv,mmultiscripts,mn,mo,mover,mpadded,mphantom,mprescripts,mroot,mrow,ms,mscarries,mscarry,msgroup,msline,mspace,msqrt,msrow,mstack,mstyle,msub,msubsup,msup,mtable,mtd,mtext,mtr,munder,munderover,none,semantics"; var VOID_TAGS = "area,base,br,col,embed,hr,img,input,link,meta,param,source,track,wbr"; var isHTMLTag = /* @__PURE__ */ makeMap(HTML_TAGS); var isSVGTag = /* @__PURE__ */ makeMap(SVG_TAGS); var isMathMLTag = /* @__PURE__ */ makeMap(MATH_TAGS); var isVoidTag = /* @__PURE__ */ makeMap(VOID_TAGS); var specialBooleanAttrs = `itemscope,allowfullscreen,formnovalidate,ismap,nomodule,novalidate,readonly`; var isSpecialBooleanAttr = /* @__PURE__ */ makeMap(specialBooleanAttrs); var isBooleanAttr = /* @__PURE__ */ makeMap( specialBooleanAttrs + `,async,autofocus,autoplay,controls,default,defer,disabled,hidden,inert,loop,open,required,reversed,scoped,seamless,checked,muted,multiple,selected` ); function includeBooleanAttr(value) { return !!value || value === ""; } var unsafeAttrCharRE = /[>/="'\u0009\u000a\u000c\u0020]/; var attrValidationCache = {}; function isSSRSafeAttrName(name) { if (attrValidationCache.hasOwnProperty(name)) { return attrValidationCache[name]; } const isUnsafe = unsafeAttrCharRE.test(name); if (isUnsafe) { console.error(`unsafe attribute name: ${name}`); } return attrValidationCache[name] = !isUnsafe; } var propsToAttrMap = { acceptCharset: "accept-charset", className: "class", htmlFor: "for", httpEquiv: "http-equiv" }; var isKnownHtmlAttr = /* @__PURE__ */ makeMap( `accept,accept-charset,accesskey,action,align,allow,alt,async,autocapitalize,autocomplete,autofocus,autoplay,background,bgcolor,border,buffered,capture,challenge,charset,checked,cite,class,code,codebase,color,cols,colspan,content,contenteditable,contextmenu,controls,coords,crossorigin,csp,data,datetime,decoding,default,defer,dir,dirname,disabled,download,draggable,dropzone,enctype,enterkeyhint,for,form,formaction,formenctype,formmethod,formnovalidate,formtarget,headers,height,hidden,high,href,hreflang,http-equiv,icon,id,importance,inert,integrity,ismap,itemprop,keytype,kind,label,lang,language,loading,list,loop,low,manifest,max,maxlength,minlength,media,min,multiple,muted,name,novalidate,open,optimum,pattern,ping,placeholder,poster,preload,radiogroup,readonly,referrerpolicy,rel,required,reversed,rows,rowspan,sandbox,scope,scoped,selected,shape,size,sizes,slot,span,spellcheck,src,srcdoc,srclang,srcset,start,step,style,summary,tabindex,target,title,translate,type,usemap,value,width,wrap` ); var isKnownSvgAttr = /* @__PURE__ */ makeMap( `xmlns,accent-height,accumulate,additive,alignment-baseline,alphabetic,amplitude,arabic-form,ascent,attributeName,attributeType,azimuth,baseFrequency,baseline-shift,baseProfile,bbox,begin,bias,by,calcMode,cap-height,class,clip,clipPathUnits,clip-path,clip-rule,color,color-interpolation,color-interpolation-filters,color-profile,color-rendering,contentScriptType,contentStyleType,crossorigin,cursor,cx,cy,d,decelerate,descent,diffuseConstant,direction,display,divisor,dominant-baseline,dur,dx,dy,edgeMode,elevation,enable-background,end,exponent,fill,fill-opacity,fill-rule,filter,filterRes,filterUnits,flood-color,flood-opacity,font-family,font-size,font-size-adjust,font-stretch,font-style,font-variant,font-weight,format,from,fr,fx,fy,g1,g2,glyph-name,glyph-orientation-horizontal,glyph-orientation-vertical,glyphRef,gradientTransform,gradientUnits,hanging,height,href,hreflang,horiz-adv-x,horiz-origin-x,id,ideographic,image-rendering,in,in2,intercept,k,k1,k2,k3,k4,kernelMatrix,kernelUnitLength,kerning,keyPoints,keySplines,keyTimes,lang,lengthAdjust,letter-spacing,lighting-color,limitingConeAngle,local,marker-end,marker-mid,marker-start,markerHeight,markerUnits,markerWidth,mask,maskContentUnits,maskUnits,mathematical,max,media,method,min,mode,name,numOctaves,offset,opacity,operator,order,orient,orientation,origin,overflow,overline-position,overline-thickness,panose-1,paint-order,path,pathLength,patternContentUnits,patternTransform,patternUnits,ping,pointer-events,points,pointsAtX,pointsAtY,pointsAtZ,preserveAlpha,preserveAspectRatio,primitiveUnits,r,radius,referrerPolicy,refX,refY,rel,rendering-intent,repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,result,rotate,rx,ry,scale,seed,shape-rendering,slope,spacing,specularConstant,specularExponent,speed,spreadMethod,startOffset,stdDeviation,stemh,stemv,stitchTiles,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,string,stroke,stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,stroke-opacity,stroke-width,style,surfaceScale,systemLanguage,tabindex,tableValues,target,targetX,targetY,text-anchor,text-decoration,text-rendering,textLength,to,transform,transform-origin,type,u1,u2,underline-position,underline-thickness,unicode,unicode-bidi,unicode-range,units-per-em,v-alphabetic,v-hanging,v-ideographic,v-mathematical,values,vector-effect,version,vert-adv-y,vert-origin-x,vert-origin-y,viewBox,viewTarget,visibility,width,widths,word-spacing,writing-mode,x,x-height,x1,x2,xChannelSelector,xlink:actuate,xlink:arcrole,xlink:href,xlink:role,xlink:show,xlink:title,xlink:type,xmlns:xlink,xml:base,xml:lang,xml:space,y,y1,y2,yChannelSelector,z,zoomAndPan` ); function isRenderableAttrValue(value) { if (value == null) { return false; } const type = typeof value; return type === "string" || type === "number" || type === "boolean"; } var escapeRE = /["'&<>]/; function escapeHtml(string) { const str = "" + string; const match = escapeRE.exec(str); if (!match) { return str; } let html = ""; let escaped; let index; let lastIndex = 0; for (index = match.index; index < str.length; index++) { switch (str.charCodeAt(index)) { case 34: escaped = """; break; case 38: escaped = "&"; break; case 39: escaped = "'"; break; case 60: escaped = "<"; break; case 62: escaped = ">"; break; default: continue; } if (lastIndex !== index) { html += str.slice(lastIndex, index); } lastIndex = index + 1; html += escaped; } return lastIndex !== index ? html + str.slice(lastIndex, index) : html; } var commentStripRE = /^-?>||--!>| looseEqual(item, val)); } var toDisplayString = (val) => { return isString(val) ? val : val == null ? "" : isArray(val) || isObject(val) && (val.toString === objectToString || !isFunction(val.toString)) ? JSON.stringify(val, replacer, 2) : String(val); }; var replacer = (_key, val) => { if (val && val.__v_isRef) { return replacer(_key, val.value); } else if (isMap(val)) { return { [`Map(${val.size})`]: [...val.entries()].reduce( (entries, [key, val2], i) => { entries[stringifySymbol(key, i) + " =>"] = val2; return entries; }, {} ) }; } else if (isSet(val)) { return { [`Set(${val.size})`]: [...val.values()].map((v) => stringifySymbol(v)) }; } else if (isSymbol(val)) { return stringifySymbol(val); } else if (isObject(val) && !isArray(val) && !isPlainObject(val)) { return String(val); } return val; }; var stringifySymbol = (v, i = "") => { var _a; return isSymbol(v) ? `Symbol(${(_a = v.description) != null ? _a : i})` : v; }; exports.EMPTY_ARR = EMPTY_ARR; exports.EMPTY_OBJ = EMPTY_OBJ; exports.NO = NO; exports.NOOP = NOOP; exports.PatchFlagNames = PatchFlagNames; exports.PatchFlags = PatchFlags; exports.ShapeFlags = ShapeFlags; exports.SlotFlags = SlotFlags; exports.camelize = camelize; exports.capitalize = capitalize; exports.def = def; exports.escapeHtml = escapeHtml; exports.escapeHtmlComment = escapeHtmlComment; exports.extend = extend; exports.genPropsAccessExp = genPropsAccessExp; exports.generateCodeFrame = generateCodeFrame; exports.getGlobalThis = getGlobalThis; exports.hasChanged = hasChanged; exports.hasOwn = hasOwn; exports.hyphenate = hyphenate; exports.includeBooleanAttr = includeBooleanAttr; exports.invokeArrayFns = invokeArrayFns; exports.isArray = isArray; exports.isBooleanAttr = isBooleanAttr; exports.isBuiltInDirective = isBuiltInDirective; exports.isDate = isDate; exports.isFunction = isFunction; exports.isGloballyAllowed = isGloballyAllowed; exports.isGloballyWhitelisted = isGloballyWhitelisted; exports.isHTMLTag = isHTMLTag; exports.isIntegerKey = isIntegerKey; exports.isKnownHtmlAttr = isKnownHtmlAttr; exports.isKnownSvgAttr = isKnownSvgAttr; exports.isMap = isMap; exports.isMathMLTag = isMathMLTag; exports.isModelListener = isModelListener; exports.isObject = isObject; exports.isOn = isOn; exports.isPlainObject = isPlainObject; exports.isPromise = isPromise; exports.isRegExp = isRegExp; exports.isRenderableAttrValue = isRenderableAttrValue; exports.isReservedProp = isReservedProp; exports.isSSRSafeAttrName = isSSRSafeAttrName; exports.isSVGTag = isSVGTag; exports.isSet = isSet; exports.isSpecialBooleanAttr = isSpecialBooleanAttr; exports.isString = isString; exports.isSymbol = isSymbol; exports.isVoidTag = isVoidTag; exports.looseEqual = looseEqual; exports.looseIndexOf = looseIndexOf; exports.looseToNumber = looseToNumber; exports.makeMap = makeMap; exports.normalizeClass = normalizeClass; exports.normalizeProps = normalizeProps; exports.normalizeStyle = normalizeStyle; exports.objectToString = objectToString; exports.parseStringStyle = parseStringStyle; exports.propsToAttrMap = propsToAttrMap; exports.remove = remove; exports.slotFlagsText = slotFlagsText; exports.stringifyStyle = stringifyStyle; exports.toDisplayString = toDisplayString; exports.toHandlerKey = toHandlerKey; exports.toNumber = toNumber; exports.toRawType = toRawType; exports.toTypeString = toTypeString; } }); // ../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/index.js var require_shared = __commonJS({ "../node_modules/.pnpm/@vue+shared@3.4.25/node_modules/@vue/shared/index.js"(exports, module2) { "use strict"; if (process.env.NODE_ENV === "production") { module2.exports = require_shared_cjs_prod(); } else { module2.exports = require_shared_cjs(); } } }); // ../node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.4.27_@vue+shared@3.4.25/node_modules/@vue-reactivity/watch/dist/index.js var require_dist = __commonJS({ "../node_modules/.pnpm/@vue-reactivity+watch@0.2.0_@vue+reactivity@3.4.27_@vue+shared@3.4.25/node_modules/@vue-reactivity/watch/dist/index.js"(exports, module2) { var __defProp2 = Object.defineProperty; var __getOwnPropDesc2 = Object.getOwnPropertyDescriptor; var __getOwnPropNames2 = Object.getOwnPropertyNames; var __hasOwnProp2 = Object.prototype.hasOwnProperty; var __markAsModule = (target) => __defProp2(target, "__esModule", { value: true }); var __export = (target, all) => { for (var name in all) __defProp2(target, name, { get: all[name], enumerable: true }); }; var __reExport = (target, module22, copyDefault, desc) => { if (module22 && typeof module22 === "object" || typeof module22 === "function") { for (let key of __getOwnPropNames2(module22)) if (!__hasOwnProp2.call(target, key) && (copyDefault || key !== "default")) __defProp2(target, key, { get: () => module22[key], enumerable: !(desc = __getOwnPropDesc2(module22, key)) || desc.enumerable }); } return target; }; var __toCommonJS = /* @__PURE__ */ ((cache) => { return (module22, temp) => { return cache && cache.get(module22) || (temp = __reExport(__markAsModule({}), module22, 1), cache && cache.set(module22, temp), temp); }; })(typeof WeakMap !== "undefined" ? /* @__PURE__ */ new WeakMap() : 0); var src_exports = {}; __export(src_exports, { watch: () => watch2, watchEffect: () => watchEffect }); var import_reactivity3 = require("@vue/reactivity"); var import_shared2 = require_shared(); var import_shared = require_shared(); function callWithErrorHandling(fn, type, args) { let res; try { res = args ? fn(...args) : fn(); } catch (err) { handleError(err, type); } return res; } function callWithAsyncErrorHandling(fn, type, args) { if ((0, import_shared.isFunction)(fn)) { const res = callWithErrorHandling(fn, type, args); if (res && (0, import_shared.isPromise)(res)) { res.catch((err) => { handleError(err, type); }); } return res; } const values = []; for (let i = 0; i < fn.length; i++) values.push(callWithAsyncErrorHandling(fn[i], type, args)); return values; } function handleError(err, type) { console.error(new Error(`[@vue-reactivity/watch]: ${type}`)); console.error(err); } function warn2(message) { console.warn(createError(message)); } function createError(message) { return new Error(`[reactivue]: ${message}`); } var INITIAL_WATCHER_VALUE = {}; function watchEffect(effect, options) { return doWatch(effect, null, options); } function watch2(source, cb, options) { return doWatch(source, cb, options); } function doWatch(source, cb, { immediate, deep, flush } = {}) { let getter; let forceTrigger = false; let isMultiSource = false; if ((0, import_reactivity3.isRef)(source)) { getter = () => source.value; forceTrigger = (0, import_reactivity3.isShallow)(source); } else if ((0, import_reactivity3.isReactive)(source)) { getter = () => source; deep = true; } else if ((0, import_shared2.isArray)(source)) { isMultiSource = true; forceTrigger = source.some(import_reactivity3.isReactive); getter = () => source.map((s) => { if ((0, import_reactivity3.isRef)(s)) return s.value; else if ((0, import_reactivity3.isReactive)(s)) return traverse(s); else if ((0, import_shared2.isFunction)(s)) return callWithErrorHandling(s, "watch getter"); else return warn2("invalid source"); }); } else if ((0, import_shared2.isFunction)(source)) { if (cb) { getter = () => callWithErrorHandling(source, "watch getter"); } else { getter = () => { if (cleanup) cleanup(); return callWithAsyncErrorHandling(source, "watch callback", [onCleanup]); }; } } else { getter = import_shared2.NOOP; } if (cb && deep) { const baseGetter = getter; getter = () => traverse(baseGetter()); } let cleanup; let onCleanup = (fn) => { cleanup = effect.onStop = () => { callWithErrorHandling(fn, "watch cleanup"); }; }; let oldValue = isMultiSource ? [] : INITIAL_WATCHER_VALUE; const job = () => { if (!effect.active) return; if (cb) { const newValue = effect.run(); if (deep || forceTrigger || (isMultiSource ? newValue.some((v, i) => (0, import_shared2.hasChanged)(v, oldValue[i])) : (0, import_shared2.hasChanged)(newValue, oldValue))) { if (cleanup) cleanup(); callWithAsyncErrorHandling(cb, "watch value", [ newValue, oldValue === INITIAL_WATCHER_VALUE ? void 0 : oldValue, onCleanup ]); oldValue = newValue; } } else { effect.run(); } }; job.allowRecurse = !!cb; let scheduler; if (flush === "sync") { scheduler = job; } else { scheduler = () => { job(); }; } const effect = new import_reactivity3.ReactiveEffect(getter, scheduler); if (cb) { if (immediate) job(); else oldValue = effect.run(); } else { effect.run(); } return () => effect.stop(); } function traverse(value, seen = /* @__PURE__ */ new Set()) { if (!(0, import_shared2.isObject)(value) || seen.has(value)) return value; seen.add(value); if ((0, import_shared2.isArray)(value)) { for (let i = 0; i < value.length; i++) traverse(value[i], seen); } else if (value instanceof Map) { value.forEach((_, key) => { traverse(value.get(key), seen); }); } else if (value instanceof Set) { value.forEach((v) => { traverse(v, seen); }); } else { for (const key of Object.keys(value)) traverse(value[key], seen); } return value; } module2.exports = __toCommonJS(src_exports); } }); // src/index.ts var import_reflect_metadata = require("reflect-metadata"); var import_commander = require("commander"); // src/install.ts var import_path4 = __toESM(require("path")); // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/ansi-styles/index.js var ANSI_BACKGROUND_OFFSET = 10; var wrapAnsi16 = (offset = 0) => (code) => `\x1B[${code + offset}m`; var wrapAnsi256 = (offset = 0) => (code) => `\x1B[${38 + offset};5;${code}m`; var wrapAnsi16m = (offset = 0) => (red, green, blue) => `\x1B[${38 + offset};2;${red};${green};${blue}m`; var styles = { modifier: { reset: [0, 0], bold: [1, 22], dim: [2, 22], italic: [3, 23], underline: [4, 24], overline: [53, 55], inverse: [7, 27], hidden: [8, 28], strikethrough: [9, 29] }, color: { black: [30, 39], red: [31, 39], green: [32, 39], yellow: [33, 39], blue: [34, 39], magenta: [35, 39], cyan: [36, 39], white: [37, 39], blackBright: [90, 39], gray: [90, 39], grey: [90, 39], redBright: [91, 39], greenBright: [92, 39], yellowBright: [93, 39], blueBright: [94, 39], magentaBright: [95, 39], cyanBright: [96, 39], whiteBright: [97, 39] }, bgColor: { bgBlack: [40, 49], bgRed: [41, 49], bgGreen: [42, 49], bgYellow: [43, 49], bgBlue: [44, 49], bgMagenta: [45, 49], bgCyan: [46, 49], bgWhite: [47, 49], bgBlackBright: [100, 49], bgGray: [100, 49], bgGrey: [100, 49], bgRedBright: [101, 49], bgGreenBright: [102, 49], bgYellowBright: [103, 49], bgBlueBright: [104, 49], bgMagentaBright: [105, 49], bgCyanBright: [106, 49], bgWhiteBright: [107, 49] } }; var modifierNames = Object.keys(styles.modifier); var foregroundColorNames = Object.keys(styles.color); var backgroundColorNames = Object.keys(styles.bgColor); var colorNames = [...foregroundColorNames, ...backgroundColorNames]; function assembleStyles() { const codes = /* @__PURE__ */ new Map(); for (const [groupName, group] of Object.entries(styles)) { for (const [styleName, style] of Object.entries(group)) { styles[styleName] = { open: `\x1B[${style[0]}m`, close: `\x1B[${style[1]}m` }; group[styleName] = styles[styleName]; codes.set(style[0], style[1]); } Object.defineProperty(styles, groupName, { value: group, enumerable: false }); } Object.defineProperty(styles, "codes", { value: codes, enumerable: false }); styles.color.close = "\x1B[39m"; styles.bgColor.close = "\x1B[49m"; styles.color.ansi = wrapAnsi16(); styles.color.ansi256 = wrapAnsi256(); styles.color.ansi16m = wrapAnsi16m(); styles.bgColor.ansi = wrapAnsi16(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi256 = wrapAnsi256(ANSI_BACKGROUND_OFFSET); styles.bgColor.ansi16m = wrapAnsi16m(ANSI_BACKGROUND_OFFSET); Object.defineProperties(styles, { rgbToAnsi256: { value(red, green, blue) { if (red === green && green === blue) { if (red < 8) { return 16; } if (red > 248) { return 231; } return Math.round((red - 8) / 247 * 24) + 232; } return 16 + 36 * Math.round(red / 255 * 5) + 6 * Math.round(green / 255 * 5) + Math.round(blue / 255 * 5); }, enumerable: false }, hexToRgb: { value(hex) { const matches = /[a-f\d]{6}|[a-f\d]{3}/i.exec(hex.toString(16)); if (!matches) { return [0, 0, 0]; } let [colorString] = matches; if (colorString.length === 3) { colorString = [...colorString].map((character) => character + character).join(""); } const integer = Number.parseInt(colorString, 16); return [ integer >> 16 & 255, integer >> 8 & 255, integer & 255 ]; }, enumerable: false }, hexToAnsi256: { value: (hex) => styles.rgbToAnsi256(...styles.hexToRgb(hex)), enumerable: false }, ansi256ToAnsi: { value(code) { if (code < 8) { return 30 + code; } if (code < 16) { return 90 + (code - 8); } let red; let green; let blue; if (code >= 232) { red = ((code - 232) * 10 + 8) / 255; green = red; blue = red; } else { code -= 16; const remainder = code % 36; red = Math.floor(code / 36) / 5; green = Math.floor(remainder / 6) / 5; blue = remainder % 6 / 5; } const value = Math.max(red, green, blue) * 2; if (value === 0) { return 30; } let result = 30 + (Math.round(blue) << 2 | Math.round(green) << 1 | Math.round(red)); if (value === 2) { result += 60; } return result; }, enumerable: false }, rgbToAnsi: { value: (red, green, blue) => styles.ansi256ToAnsi(styles.rgbToAnsi256(red, green, blue)), enumerable: false }, hexToAnsi: { value: (hex) => styles.ansi256ToAnsi(styles.hexToAnsi256(hex)), enumerable: false } }); return styles; } var ansiStyles = assembleStyles(); var ansi_styles_default = ansiStyles; // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/vendor/supports-color/index.js var import_node_process = __toESM(require("process"), 1); var import_node_os = __toESM(require("os"), 1); var import_node_tty = __toESM(require("tty"), 1); function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : import_node_process.default.argv) { const prefix = flag.startsWith("-") ? "" : flag.length === 1 ? "-" : "--"; const position = argv.indexOf(prefix + flag); const terminatorPosition = argv.indexOf("--"); return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition); } var { env } = import_node_process.default; var flagForceColor; if (hasFlag("no-color") || hasFlag("no-colors") || hasFlag("color=false") || hasFlag("color=never")) { flagForceColor = 0; } else if (hasFlag("color") || hasFlag("colors") || hasFlag("color=true") || hasFlag("color=always")) { flagForceColor = 1; } function envForceColor() { if ("FORCE_COLOR" in env) { if (env.FORCE_COLOR === "true") { return 1; } if (env.FORCE_COLOR === "false") { return 0; } return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3); } } function translateLevel(level) { if (level === 0) { return false; } return { level, hasBasic: true, has256: level >= 2, has16m: level >= 3 }; } function _supportsColor(haveStream, { streamIsTTY, sniffFlags = true } = {}) { const noFlagForceColor = envForceColor(); if (noFlagForceColor !== void 0) { flagForceColor = noFlagForceColor; } const forceColor = sniffFlags ? flagForceColor : noFlagForceColor; if (forceColor === 0) { return 0; } if (sniffFlags) { if (hasFlag("color=16m") || hasFlag("color=full") || hasFlag("color=truecolor")) { return 3; } if (hasFlag("color=256")) { return 2; } } if ("TF_BUILD" in env && "AGENT_NAME" in env) { return 1; } if (haveStream && !streamIsTTY && forceColor === void 0) { return 0; } const min = forceColor || 0; if (env.TERM === "dumb") { return min; } if (import_node_process.default.platform === "win32") { const osRelease = import_node_os.default.release().split("."); if (Number(osRelease[0]) >= 10 && Number(osRelease[2]) >= 10586) { return Number(osRelease[2]) >= 14931 ? 3 : 2; } return 1; } if ("CI" in env) { if ("GITHUB_ACTIONS" in env || "GITEA_ACTIONS" in env) { return 3; } if (["TRAVIS", "CIRCLECI", "APPVEYOR", "GITLAB_CI", "BUILDKITE", "DRONE"].some((sign) => sign in env) || env.CI_NAME === "codeship") { return 1; } return min; } if ("TEAMCITY_VERSION" in env) { return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0; } if (env.COLORTERM === "truecolor") { return 3; } if (env.TERM === "xterm-kitty") { return 3; } if ("TERM_PROGRAM" in env) { const version2 = Number.parseInt((env.TERM_PROGRAM_VERSION || "").split(".")[0], 10); switch (env.TERM_PROGRAM) { case "iTerm.app": { return version2 >= 3 ? 3 : 2; } case "Apple_Terminal": { return 2; } } } if (/-256(color)?$/i.test(env.TERM)) { return 2; } if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) { return 1; } if ("COLORTERM" in env) { return 1; } return min; } function createSupportsColor(stream, options = {}) { const level = _supportsColor(stream, { streamIsTTY: stream && stream.isTTY, ...options }); return translateLevel(level); } var supportsColor = { stdout: createSupportsColor({ isTTY: import_node_tty.default.isatty(1) }), stderr: createSupportsColor({ isTTY: import_node_tty.default.isatty(2) }) }; var supports_color_default = supportsColor; // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/utilities.js function stringReplaceAll(string, substring, replacer) { let index = string.indexOf(substring); if (index === -1) { return string; } const substringLength = substring.length; let endIndex = 0; let returnValue = ""; do { returnValue += string.slice(endIndex, index) + substring + replacer; endIndex = index + substringLength; index = string.indexOf(substring, endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } function stringEncaseCRLFWithFirstIndex(string, prefix, postfix, index) { let endIndex = 0; let returnValue = ""; do { const gotCR = string[index - 1] === "\r"; returnValue += string.slice(endIndex, gotCR ? index - 1 : index) + prefix + (gotCR ? "\r\n" : "\n") + postfix; endIndex = index + 1; index = string.indexOf("\n", endIndex); } while (index !== -1); returnValue += string.slice(endIndex); return returnValue; } // ../node_modules/.pnpm/chalk@5.3.0/node_modules/chalk/source/index.js var { stdout: stdoutColor, stderr: stderrColor } = supports_color_default; var GENERATOR = Symbol("GENERATOR"); var STYLER = Symbol("STYLER"); var IS_EMPTY = Symbol("IS_EMPTY"); var levelMapping = [ "ansi", "ansi", "ansi256", "ansi16m" ]; var styles2 = /* @__PURE__ */ Object.create(null); var applyOptions = (object, options = {}) => { if (options.level && !(Number.isInteger(options.level) && options.level >= 0 && options.level <= 3)) { throw new Error("The `level` option should be an integer from 0 to 3"); } const colorLevel = stdoutColor ? stdoutColor.level : 0; object.level = options.level === void 0 ? colorLevel : options.level; }; var chalkFactory = (options) => { const chalk2 = (...strings) => strings.join(" "); applyOptions(chalk2, options); Object.setPrototypeOf(chalk2, createChalk.prototype); return chalk2; }; function createChalk(options) { return chalkFactory(options); } Object.setPrototypeOf(createChalk.prototype, Function.prototype); for (const [styleName, style] of Object.entries(ansi_styles_default)) { styles2[styleName] = { get() { const builder = createBuilder(this, createStyler(style.open, style.close, this[STYLER]), this[IS_EMPTY]); Object.defineProperty(this, styleName, { value: builder }); return builder; } }; } styles2.visible = { get() { const builder = createBuilder(this, this[STYLER], true); Object.defineProperty(this, "visible", { value: builder }); return builder; } }; var getModelAnsi = (model, level, type, ...arguments_) => { if (model === "rgb") { if (level === "ansi16m") { return ansi_styles_default[type].ansi16m(...arguments_); } if (level === "ansi256") { return ansi_styles_default[type].ansi256(ansi_styles_default.rgbToAnsi256(...arguments_)); } return ansi_styles_default[type].ansi(ansi_styles_default.rgbToAnsi(...arguments_)); } if (model === "hex") { return getModelAnsi("rgb", level, type, ...ansi_styles_default.hexToRgb(...arguments_)); } return ansi_styles_default[type][model](...arguments_); }; var usedModels = ["rgb", "hex", "ansi256"]; for (const model of usedModels) { styles2[model] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "color", ...arguments_), ansi_styles_default.color.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; const bgModel = "bg" + model[0].toUpperCase() + model.slice(1); styles2[bgModel] = { get() { const { level } = this; return function(...arguments_) { const styler = createStyler(getModelAnsi(model, levelMapping[level], "bgColor", ...arguments_), ansi_styles_default.bgColor.close, this[STYLER]); return createBuilder(this, styler, this[IS_EMPTY]); }; } }; } var proto = Object.defineProperties(() => { }, { ...styles2, level: { enumerable: true, get() { return this[GENERATOR].level; }, set(level) { this[GENERATOR].level = level; } } }); var createStyler = (open, close, parent) => { let openAll; let closeAll; if (parent === void 0) { openAll = open; closeAll = close; } else { openAll = parent.openAll + open; closeAll = close + parent.closeAll; } return { open, close, openAll, closeAll, parent }; }; var createBuilder = (self2, _styler, _isEmpty) => { const builder = (...arguments_) => applyStyle(builder, arguments_.length === 1 ? "" + arguments_[0] : arguments_.join(" ")); Object.setPrototypeOf(builder, proto); builder[GENERATOR] = self2; builder[STYLER] = _styler; builder[IS_EMPTY] = _isEmpty; return builder; }; var applyStyle = (self2, string) => { if (self2.level <= 0 || !string) { return self2[IS_EMPTY] ? "" : string; } let styler = self2[STYLER]; if (styler === void 0) { return string; } const { openAll, closeAll } = styler; if (string.includes("\x1B")) { while (styler !== void 0) { string = stringReplaceAll(string, styler.close, styler.open); styler = styler.parent; } } const lfIndex = string.indexOf("\n"); if (lfIndex !== -1) { string = stringEncaseCRLFWithFirstIndex(string, closeAll, openAll, lfIndex); } return openAll + string + closeAll; }; Object.defineProperties(createChalk.prototype, styles2); var chalk = createChalk(); var chalkStderr = createChalk({ level: stderrColor ? stderrColor.level : 0 }); var source_default = chalk; // src/install.ts var import_tsyringe4 = require("tsyringe"); // ../server-shared/src/storage-service.ts var import_tsyringe2 = require("tsyringe"); var import_fs = require("fs"); var import_path = require("path"); var import_simple_json_db = __toESM(require("simple-json-db")); // ../server-shared/src/log-service.ts var import_tsyringe = require("tsyringe"); var import_dayjs = __toESM(require("dayjs")); // ../shared/src/constants.ts var DATE_FORMAT = "YYYY-MM-DD HH:mm:ss"; // ../server-shared/src/log-service.ts var LogService = class { constructor() { this.scope = ""; this.dateFormat = DATE_FORMAT; } _prefix(type) { let prefix = ""; this.scope && (prefix += source_default[type].bold(`[${this.scope}]`)); prefix += source_default.blue(`[${(0, import_dayjs.default)().format(this.dateFormat)}]`); return prefix; } _log(...args) { console.log(...args); } _error(...args) { console.error(...args); } setScope(scope) { this.scope = scope; } log(...args) { this._log(this._prefix("green"), ...args); } error(...args) { this._error(this._prefix("red"), ...args); } logWithUser(user, ...args) { this._log( this._prefix("green") + source_default.yellow.dim(`[${user.username}:${user.slug}]`), ...args ); } static create(scope) { const instance = import_tsyringe.container.resolve(LogService); instance.setScope(scope); return instance; } }; // ../server-shared/src/storage-service.ts var defaultRoot = (0, import_path.resolve)(__dirname, "../../server/data"); var defaultFilename = "common.db"; var StorageService = class { constructor(_logService) { this._logService = _logService; this._root = defaultRoot; this._filename = defaultFilename; this._logService.setScope("storage-service"); if (!(0, import_fs.existsSync)(this._root)) (0, import_fs.mkdirSync)(this._root); this._logService.log(`init storage service with root: ${this._root}`); this._db = new import_simple_json_db.default((0, import_path.resolve)(this._root, this._filename)); } get(key) { return this._db.get(key); } set(key, value) { this._db.set(key, value); } delete(key) { return this._db.delete(key); } }; StorageService = __decorateClass([ (0, import_tsyringe2.singleton)(), __decorateParam(0, (0, import_tsyringe2.inject)(LogService)) ], StorageService); // src/constants.ts var import_fs3 = __toESM(require("fs")); var import_path2 = __toESM(require("path")); // src/utils.ts var import_fs2 = __toESM(require("fs")); function section(title) { console.log(); console.log(source_default.blue.bold("\u2699 " + title)); console.log(); } function log(...args) { console.log(...args); } function info(...args) { console.log(source_default.blue(...args)); } function success(...args) { console.log(source_default.green(...args)); } function warn(...args) { console.log(source_default.yellow(...args)); } function error(...args) { console.log(source_default.red(...args)); } var printer = { section, log, success, info, warn, error }; function readJsonFile(filename) { const file = import_fs2.default.readFileSync(filename, { encoding: "utf-8" }); return JSON.parse(file); } function printVersion() { printer.section("Check version"); printer.info(`Current Version: ${version}`); if (version.indexOf("-") >= 0) { printer.warn("This is a preview version!"); } } // src/constants.ts var logo = (() => { try { return import_fs3.default.readFileSync( import_path2.default.resolve(process.cwd(), "./assets/logo.art"), "utf-8" ); } catch (err) { console.error(err); return "Hexon"; } })(); var version = readJsonFile( import_path2.default.resolve(process.cwd(), "../package.json") ).version; // ../server-shared/src/constants.ts var HEXO_BASE_DIR_KEY = "hexo-basedir"; var HEXON_PORT_KEY = "@hexon/port"; var HEXON_DEFAULT_PORT = 5777; // ../server-shared/src/account-storage-service.ts var import_crypto_js = require("crypto-js"); var import_tsyringe3 = require("tsyringe"); var AccountService = class { constructor(_storage, _logService) { this._storage = _storage; this._logService = _logService; this._logService.setScope("account-service"); } _encrypt(raw) { return (0, import_crypto_js.SHA1)(raw).toString(); } _toStorage(info2) { this._storage.set(AccountService.KEY, info2); } _fromStorage() { const { username = "", password = "" } = this._storage.get(AccountService.KEY) || {}; return { username, password }; } setUserInfo(username, password) { this._storage.set(AccountService.KEY, { username, password: this._encrypt(password) }); this._logService.log("set user info: ", username); } getUsername() { return this._fromStorage().username; } setUsername(username) { const info2 = this._fromStorage(); info2.username = username; this._toStorage(info2); this._logService.log("set username: ", username); } setPassword(password) { const info2 = this._fromStorage(); info2.password = this._encrypt(password); this._toStorage(info2); this._logService.log("set password"); } setEncryptedPassword(password) { const info2 = this._fromStorage(); info2.password = password; this._toStorage(info2); this._logService.log("set encrypted password"); } verify(username, password) { const info2 = this._fromStorage(); if (username !== info2.username) { return false; } if (this._encrypt(password) !== info2.password) { return false; } return true; } }; AccountService.KEY = "userinfo"; AccountService = __decorateClass([ (0, import_tsyringe3.injectable)(), (0, import_tsyringe3.singleton)(), __decorateParam(0, (0, import_tsyringe3.inject)(StorageService)), __decorateParam(1, (0, import_tsyringe3.inject)(LogService)) ], AccountService); // src/prompts.ts var import_inquirer = __toESM(require("inquirer")); // ../server-shared/src/utils.ts var import_fs4 = __toESM(require("fs")); var import_path3 = __toESM(require("path")); function isBlog(cwd) { var _a; let file; try { file = import_fs4.default.readFileSync(import_path3.default.join(cwd, "package.json"), { encoding: "utf-8" }); import_fs4.default.readFileSync(import_path3.default.join(cwd, "_config.yml"), { encoding: "utf-8" }); } catch (err) { if (err.code === "ENOENT") { return false; } throw err; } const packageJSON = JSON.parse(file); if (!((_a = packageJSON == null ? void 0 : packageJSON.dependencies) == null ? void 0 : _a.hexo)) return false; return true; } function toRealPath(value) { return import_path3.default.isAbsolute(value) ? value : import_path3.default.resolve(process.cwd(), "../..", value); } // src/prompts.ts async function requestPassword() { const answer = await import_inquirer.default.prompt({ name: "password", message: "Password ?", type: "password", validate(v) { if (!v) return "Must not empty"; return true; } }); const { password } = answer; return password; } async function requestUsername() { const answer = await import_inquirer.default.prompt({ name: "username", message: "Username ?", validate(v) { if (!v) return "Must not empty"; return true; } }); const { username } = answer; return username; } async function requestUserInfo() { const username = await requestUsername(); const password = await requestPassword(); return { username, password }; } async function requestPort(defaultPort) { const portPrompt = { name: "port", message: "Which port do you like Hexon running at?", default: defaultPort, validate(v) { return !isNaN(v) || `number is required ${typeof v} given`; }, prefix: source_default.blue("?") }; const answer = await import_inquirer.default.prompt(portPrompt); return String(answer.port); } async function requestRoot() { const rootPrompt = { name: "root", message: `Your hexo blog path? ${source_default.grey( "Absolute or relative path to hexon." )}`, validate(v) { const truePath = toRealPath(v); try { return isBlog(truePath) || source_default.red.bold(truePath) + source_default.red(" is not a valid hexo blog."); } catch (e) { console.error(e); return source_default.red("Fail to check path " + source_default.bold(truePath)); } } }; const answer = await import_inquirer.default.prompt(rootPrompt); return answer.root; } // src/install.ts async function install_default() { console.clear(); console.log(source_default.blue(logo)); printVersion(); printer.section("Configuration"); const storage = import_tsyringe4.container.resolve(StorageService); storage.set(HEXON_PORT_KEY, await requestPort(HEXON_DEFAULT_PORT)); storage.set( HEXO_BASE_DIR_KEY, await requestRoot() ); const { username, password } = await requestUserInfo(); const account = import_tsyringe4.container.resolve(AccountService); account.setUserInfo(username, password); printer.section("Install"); const base = import_path4.default.resolve(__dirname, "../.."); printer.success(`Hexon has been installed to \`${base}\``); printer.log(`Run \`pnpm start\` to start`); printer.log(`Run \`pnpm prd\` to start with pm2`); printer.log(source_default.grey(`To uninstall, remove the following foler: ${base}`)); } // src/reset-password.ts var import_tsyringe5 = require("tsyringe"); async function resetPassword() { const account = import_tsyringe5.container.resolve(AccountService); console.log(source_default.yellow(`Resetting password for user '${account.getUsername()}' ...`)); const newpwd = await requestPassword(); account.setPassword(newpwd); console.log(source_default.green(`Password has been reset.`)); } // src/script.ts var import_inquirer2 = __toESM(require("inquirer")); // ../server-shared/src/store.ts var import_path6 = require("path"); var import_reactivity2 = require("@vue/reactivity"); // ../node_modules/.pnpm/@winwin+server-reactive-store@0.2.2/node_modules/@winwin/server-reactive-store/dist/index.mjs var import_reactivity = require("@vue/reactivity"); var import_watch = __toESM(require_dist(), 1); var import_fs5 = require("fs"); var import_path5 = require("path"); var import_simple_json_db2 = __toESM(require("simple-json-db"), 1); var JSONdbStorageAdapter = class { constructor(root, name = "database.json") { if (!(0, import_fs5.existsSync)(root)) (0, import_fs5.mkdirSync)(root); this.db = new import_simple_json_db2.default((0, import_path5.resolve)(root, name)); } getItem(key) { return this.db.get(key); } setItem(key, value) { this.db.set(key, value); } removeItem(key) { return this.db.delete(key); } }; function createStore(key, adapter, setup, { saveAfterCreate = true } = {}) { const all = setup(); if (!all.state) throw new Error("must return object with state property"); const state = (0, import_reactivity.reactive)(all.state); const load = () => { const loaded = adapter.getItem(key); if (loaded) Object.assign(state, loaded); }; load(); const save = () => adapter.setItem(key, state); (0, import_watch.watch)(state, save, { deep: true, immediate: saveAfterCreate }); return { ...all, load, save, state }; } function createStoreCreator(adapter) { return function(key, setup) { return createStore(key, adapter, setup); }; } // ../server-shared/src/store.ts var ROOT = (0, import_path6.resolve)(process.cwd(), "data"); var NAME = "database.json"; var createStore2 = createStoreCreator(new JSONdbStorageAdapter(ROOT, NAME)); var scriptStore = createStore2("script", () => { const state = (0, import_reactivity2.reactive)({ items: {} }); const keys = (0, import_reactivity2.computed)(() => Object.keys(state.items)); const getScript = (key) => { var _a; return ((_a = state.items[key]) == null ? void 0 : _a.value) || ""; }; const hasScript = (key) => { var _a; return (_a = state.items[key]) == null ? void 0 : _a.value; }; const setScript = (key, script2) => { state.items[key] = { value: script2 }; }; return { state, keys, getScript, setScript, hasScript }; }); // src/script.ts function getAvailableKeys() { const keys = /* @__PURE__ */ new Set([ "git-save", "git-sync", "hexo-clean", "hexo-generate", "hexo-deploy", ...scriptStore.keys.value ]); return [...keys.keys()].map((value) => { const v = scriptStore.getScript(value); const name = v ? `${value}: ${v}` : value; return { name, value }; }); } async function pickOneScript() { const { key } = await import_inquirer2.default.prompt({ name: "key", type: "list", message: "Which script do you want to manage?", choices: () => getAvailableKeys() }); return key; } async function getInput(key) { const currnet = scriptStore.hasScript(key) && `- Current: ${scriptStore.getScript(key)}`; const { value } = await import_inquirer2.default.prompt({ name: "value", message: `Please input some script to run your script file (with relative path to your hexo blog folder). ${source_default.gray( `- e.g. \`bash ./my_deploy_script.sh\` - Leave blank to remove. ${currnet ? `${currnet} ` : ""}` )}` }); return value; } async function script() { const key = await pickOneScript(); const value = await getInput(key); scriptStore.setScript(key, value); console.log(source_default.bgGreen.white(` Script ${value ? "Saved" : "Removed"} `)); } // src/index.ts var program = new import_commander.Command("npx ."); program.command("install").description("install hexon").action(install_default); program.command("resetpwd").description("reset password").action(resetPassword); program.command("script").description("manage custom script").action(script); program.parse(); /*! #__NO_SIDE_EFFECTS__ */ /** * @vue/shared v3.4.25 * (c) 2018-present Yuxi (Evan) You and Vue contributors * @license MIT **/ ================================================ FILE: server-scripts/package.json ================================================ { "name": "server-scripts", "private": true, "scripts": { "build": "node ./scripts/build.mjs" }, "dependencies": { "@koa/cors": "^3.4.3", "@koa/router": "^10.1.1", "@vue/reactivity": "^3.4.34", "@winwin/koa-authentication": "^0.2.3", "@winwin/server-reactive-store": "^0.2.2", "basic-auth": "^2.0.1", "chalk": "^5.3.0", "commander": "^9.5.0", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dayjs": "^1.11.12", "debug": "^4.3.5", "dotenv": "^16.4.5", "execa": "^6.1.0", "hexo": "^7.3.0", "http-errors": "^2.0.0", "inquirer": "^8.2.6", "jsencrypt": "^3.3.2", "jsonwebtoken": "^8.5.1", "koa": "^2.15.3", "koa-bodyparser": "^4.4.1", "koa-compose": "^4.1.0", "koa-compress": "^5.1.1", "koa-logger": "^3.2.1", "koa-mount": "^4.0.0", "koa-router": "^10.1.1", "koa-static": "^5.0.0", "koa2-cors": "^2.0.6", "node-jsencrypt": "^1.0.0", "reflect-metadata": "^0.1.14", "simple-json-db": "^2.0.0", "strip-ansi": "^7.1.0", "tsyringe": "^4.8.0", "winston": "^3.13.1" } } ================================================ FILE: server-scripts/scripts/build.mjs ================================================ import esbuild from "esbuild" import { esbuildPluginNodeExternals } from "esbuild-plugin-node-externals" import chalk from "chalk" const INCLUDE_MODULES = [ "execa", "chalk", "@winwin/server-reactive-store", // 为了让 store 工作,如果不加,store 中的 watch 会失效 "strip-ansi", ] function buildBin() { console.log(chalk.gray("[bin]"), chalk.green("Building bin...")) return new Promise((resolve) => { esbuild .build({ entryPoints: ["./src/index.ts"], bundle: true, platform: "node", target: "node12", outfile: "./bin/index.js", plugins: [ esbuildPluginNodeExternals({ include: INCLUDE_MODULES, }), ], }) .then(() => { // eslint-disable-next-line no-console console.log(chalk.gray("[bin]"), chalk.green("Build finished.")) resolve() }) }) } buildBin() ================================================ FILE: server-scripts/src/constants.ts ================================================ import fs from "fs" import path from "path" import { readJsonFile } from "./utils" export const logo: string = (() => { try { return fs.readFileSync( path.resolve(process.cwd(), "./assets/logo.art"), "utf-8" ) } catch (err) { console.error(err) return "Hexon" } })() export const version: string = readJsonFile( path.resolve(process.cwd(), "../package.json") ).version ================================================ FILE: server-scripts/src/index.ts ================================================ import "reflect-metadata" import { Command } from "commander" import install from "./install" import resetPassword from "./reset-password" import script from "./script" const program = new Command("npx .") program.command("install").description("install hexon").action(install) program.command("resetpwd").description("reset password").action(resetPassword) program.command("script").description("manage custom script").action(script) program.parse() ================================================ FILE: server-scripts/src/install.ts ================================================ import path from "path" import chalk from "chalk" import { container } from "tsyringe" import { StorageService } from "@server-shared/storage-service" import { logo } from "./constants" import { printer, printVersion } from "./utils" import { HEXO_BASE_DIR_KEY, HEXON_DEFAULT_PORT, HEXON_PORT_KEY } from "@server-shared/constants" import { AccountService } from "@server-shared/account-storage-service" import { requestPort, requestRoot, requestUserInfo } from "./prompts" export default async function () { console.clear() console.log(chalk.blue(logo)) printVersion() printer.section("Configuration") const storage = container.resolve(StorageService) storage.set(HEXON_PORT_KEY, await requestPort(HEXON_DEFAULT_PORT)) storage.set( HEXO_BASE_DIR_KEY, await requestRoot() ) const { username, password } = await requestUserInfo() const account = container.resolve(AccountService) account.setUserInfo(username, password) printer.section("Install") const base = path.resolve(__dirname, "../..") printer.success(`Hexon has been installed to \`${base}\``) printer.log(`Run \`pnpm start\` to start`) printer.log(`Run \`pnpm prd\` to start with pm2`) printer.log(chalk.grey(`To uninstall, remove the following foler: ${base}`)) } ================================================ FILE: server-scripts/src/prompts.ts ================================================ import inquirer from "inquirer" import chalk from "chalk" import { isBlog, toRealPath } from "@server-shared/utils" export async function requestPassword() { const answer = await inquirer.prompt({ name: "password", message: "Password ?", type: "password", validate(v: string) { if (!v) return "Must not empty" return true }, }) const { password } = answer as { password: string } return password } export async function requestUsername() { const answer = await inquirer.prompt({ name: "username", message: "Username ?", validate(v: string) { if (!v) return "Must not empty" return true }, }) const { username } = answer as { username: string } return username } export async function requestUserInfo() { const username = await requestUsername() const password = await requestPassword() return { username, password } } export async function requestPort(defaultPort: number) { const portPrompt = { name: "port", message: "Which port do you like Hexon running at?", default: defaultPort, validate(v: number) { return !isNaN(v) || `number is required ${typeof v} given` }, prefix: chalk.blue("?"), } const answer = await inquirer.prompt(portPrompt) return String(answer.port as number) } export async function requestRoot() { const rootPrompt = { name: "root", message: `Your hexo blog path? ${chalk.grey( "Absolute or relative path to hexon." )}`, validate(v: string) { const truePath = toRealPath(v) try { return ( isBlog(truePath) || chalk.red.bold(truePath) + chalk.red(" is not a valid hexo blog.") ) } catch (e) { console.error(e) return chalk.red("Fail to check path " + chalk.bold(truePath)) } }, } const answer = await inquirer.prompt(rootPrompt) return answer.root as string } ================================================ FILE: server-scripts/src/reset-password.ts ================================================ import chalk from "chalk" import { container } from "tsyringe" import { AccountService } from "@server-shared/account-storage-service" import { requestPassword } from "./prompts" export default async function resetPassword() { const account = container.resolve(AccountService) console.log(chalk.yellow(`Resetting password for user '${account.getUsername()}' ...`)) const newpwd = await requestPassword() account.setPassword(newpwd) console.log(chalk.green(`Password has been reset.`)) } ================================================ FILE: server-scripts/src/script.ts ================================================ import chalk from "chalk" import inquirer from "inquirer" import { scriptStore } from "@server-shared/store" function getAvailableKeys() { const keys = new Set([ "git-save", "git-sync", "hexo-clean", "hexo-generate", "hexo-deploy", ...scriptStore.keys.value, ]) return [...keys.keys()].map((value) => { const v = scriptStore.getScript(value) const name = v ? `${value}: ${v}` : value return { name, value } }) } async function pickOneScript() { const { key } = await inquirer.prompt({ name: "key", type: "list", message: "Which script do you want to manage?", choices: () => getAvailableKeys(), }) return key } async function getInput(key: string) { const currnet = scriptStore.hasScript(key) && `- Current: ${scriptStore.getScript(key)}` const { value } = await inquirer.prompt({ name: "value", message: `Please input some script to run your script file (with relative path to your hexo blog folder). ${chalk.gray( `- e.g. \`bash ./my_deploy_script.sh\` - Leave blank to remove. ${currnet ? `${currnet}\n` : ""}` )}`, }) return value } export default async function script() { const key = await pickOneScript() const value = await getInput(key) scriptStore.setScript(key, value) console.log(chalk.bgGreen.white(` Script ${value ? "Saved" : "Removed"} `)) } ================================================ FILE: server-scripts/src/utils.ts ================================================ import fs from "fs" import chalk from "chalk" import { version } from "./constants" function section(title: string) { console.log() console.log(chalk.blue.bold("⚙ " + title)) console.log() } function log(...args: any[]) { console.log(...args) } function info(...args: any[]) { console.log(chalk.blue(...args)) } function success(...args: any[]) { console.log(chalk.green(...args)) } function warn(...args: any[]) { console.log(chalk.yellow(...args)) } function error(...args: any[]) { console.log(chalk.red(...args)) } export const printer = { section, log, success, info, warn, error, } export function readJsonFile(filename: string) { const file = fs.readFileSync(filename, { encoding: "utf-8" }) return JSON.parse(file) } export function printVersion(){ printer.section("Check version") printer.info(`Current Version: ${version}`) if (version.indexOf("-") >= 0) { printer.warn("This is a preview version!") } } ================================================ FILE: server-shared/package.json ================================================ { "name": "server-shared", "private": true, "scripts": { "build": "exit 0" }, "dependencies": { "@koa/cors": "^3.4.3", "@koa/router": "^10.1.1", "@vue/reactivity": "^3.4.34", "@winwin/koa-authentication": "^0.2.3", "@winwin/server-reactive-store": "^0.2.2", "basic-auth": "^2.0.1", "chalk": "^5.3.0", "commander": "^9.5.0", "cross-env": "^7.0.3", "crypto-js": "^4.2.0", "dayjs": "^1.11.12", "debug": "^4.3.5", "dotenv": "^16.4.5", "execa": "^6.1.0", "hexo": "^7.3.0", "http-errors": "^2.0.0", "inquirer": "^8.2.6", "jsencrypt": "^3.3.2", "jsonwebtoken": "^8.5.1", "koa": "^2.15.3", "koa-bodyparser": "^4.4.1", "koa-compose": "^4.1.0", "koa-compress": "^5.1.1", "koa-logger": "^3.2.1", "koa-mount": "^4.0.0", "koa-router": "^10.1.1", "koa-static": "^5.0.0", "koa2-cors": "^2.0.6", "node-jsencrypt": "^1.0.0", "reflect-metadata": "^0.1.14", "simple-json-db": "^2.0.0", "strip-ansi": "^7.1.0", "tsyringe": "^4.8.0", "winston": "^3.13.1" } } ================================================ FILE: server-shared/src/account-storage-service.ts ================================================ import { SHA1 } from 'crypto-js' import { inject, injectable, singleton } from 'tsyringe' import { StorageService } from './storage-service' import { LogService } from './log-service' interface IUserInfo { password: string username: string } export class BasicAuthError extends Error { public name = 'BasicAuthError' } @injectable() @singleton() export class AccountService { public static KEY = 'userinfo' as const constructor( @inject(StorageService) private _storage: StorageService, @inject(LogService) private _logService: LogService, ) { this._logService.setScope('account-service') } private _encrypt(raw: string) { return SHA1(raw).toString() } private _toStorage(info: IUserInfo) { this._storage.set(AccountService.KEY, info) } private _fromStorage(): IUserInfo { const { username = '', password = '' } = this._storage.get(AccountService.KEY) || {} return { username, password } } setUserInfo(username: string, password: string) { this._storage.set(AccountService.KEY, { username, password: this._encrypt(password), }) this._logService.log('set user info: ', username) } getUsername() { return this._fromStorage().username } setUsername(username: string) { const info = this._fromStorage() info.username = username this._toStorage(info) this._logService.log('set username: ', username) } setPassword(password: string) { const info = this._fromStorage() info.password = this._encrypt(password) this._toStorage(info) this._logService.log('set password') } setEncryptedPassword(password: string) { const info = this._fromStorage() info.password = password this._toStorage(info) this._logService.log('set encrypted password') } verify(username: string, password: string) { const info = this._fromStorage() if (username !== info.username) { return false } if (this._encrypt(password) !== info.password) { return false } return true } } ================================================ FILE: server-shared/src/constants.ts ================================================ export const HEXO_BASE_DIR_KEY = "hexo-basedir" export const HEXO_OPTIONS_KEY = "hexo-options" export const BRIEF_LENGTH = 500 export const HEXON_PORT_KEY = "@hexon/port" export const HEXON_DEFAULT_PORT = 5777 ================================================ FILE: server-shared/src/env-service.ts ================================================ import { inject, injectable, singleton } from "tsyringe" import { AccountService } from "./account-storage-service" import { StorageService } from "./storage-service" import { HEXON_PORT_KEY, HEXO_BASE_DIR_KEY } from "./constants" import { LogService } from "./log-service" @injectable() @singleton() export class EnvService { constructor( @inject(StorageService) private storage: StorageService, @inject(AccountService) private account: AccountService, @inject(LogService) private _logService: LogService ) { this._logService.setScope("env-service") } sync() { this.syncAccount() this.syncHexon() this.syncHexo() } private syncAccount() { const username = process.env.HEXON_USERNAME const password = process.env.HEXON_PASSWORD if (username) { this._logService.log(`sync account from process.env.HEXON_USERNAME`) this.account.setUsername(username) } if (password) { this._logService.log(`sync account from process.env.HEXON_PASSWORD`) this.account.setPassword(password) } } private syncHexon() { const port = process.env.HEXON_PORT if (port) { this._logService.log(`sync hexon port from process.env.HEXON_PORT`) this.storage.set(HEXON_PORT_KEY, port) } } private syncHexo() { const base = process.env.HEXO_BASE if (base) { this._logService.log(`sync hexo base from process.env.HEXO_BASE`) this.storage.set(HEXO_BASE_DIR_KEY, base) } } } ================================================ FILE: server-shared/src/log-service.ts ================================================ import { container } from "tsyringe" import chalk from "chalk" import dayjs from "dayjs" import { DATE_FORMAT } from "@shared/constants" export class LogService { private scope = "" private dateFormat = DATE_FORMAT _prefix(type: "green" | "red") { let prefix = "" this.scope && (prefix += chalk[type].bold(`[${this.scope}]`)) prefix += chalk.blue(`[${dayjs().format(this.dateFormat)}]`) return prefix } private _log(...args: any[]) { console.log(...args) } private _error(...args: any[]) { console.error(...args) } setScope(scope: string) { this.scope = scope } log(...args: any[]) { this._log(this._prefix("green"), ...args) } error(...args: any[]) { this._error(this._prefix("red"), ...args) } logWithUser(user: { username: string; slug: string }, ...args: any[]) { this._log( this._prefix("green") + chalk.yellow.dim(`[${user.username}:${user.slug}]`), ...args ) } static create(scope: string) { const instance = container.resolve(LogService) instance.setScope(scope) return instance } } ================================================ FILE: server-shared/src/storage-service.ts ================================================ import { inject, singleton } from "tsyringe" import { existsSync, mkdirSync } from "fs" import { resolve } from "path" import JSONdb from "simple-json-db" import { LogService } from "./log-service" export const defaultRoot = resolve(__dirname, "../../server/data") export const defaultFilename = "common.db" export interface IStorageService { get(key: string): T set(key: string, value: T): void delete(key: string): void } @singleton() export class StorageService implements IStorageService { private _db: JSONdb private _root: string = defaultRoot private _filename: string = defaultFilename constructor(@inject(LogService) private _logService: LogService) { this._logService.setScope("storage-service") if (!existsSync(this._root)) mkdirSync(this._root) this._logService.log(`init storage service with root: ${this._root}`) this._db = new JSONdb(resolve(this._root, this._filename)) } get(key: string) { return this._db.get(key) as unknown as T } set(key: string, value: T) { this._db.set(key, value as unknown as object) } delete(key: string) { return this._db.delete(key) } } ================================================ FILE: server-shared/src/store.ts ================================================ import { resolve } from "path" import { reactive, computed } from "@vue/reactivity" import { createStoreCreator, JSONdbStorageAdapter, } from "@winwin/server-reactive-store" export const ROOT = resolve(process.cwd(), "data") export const NAME = "database.json" const createStore = createStoreCreator(new JSONdbStorageAdapter(ROOT, NAME)) export const scriptStore = createStore("script", () => { const state = reactive<{ items: Record }>({ items: {}, }) const keys = computed(() => Object.keys(state.items)) const getScript = (key: string) => state.items[key]?.value || "" const hasScript = (key: string) => state.items[key]?.value const setScript = (key: string, script: string) => { state.items[key] = { value: script } } return { state, keys, getScript, setScript, hasScript } }) ================================================ FILE: server-shared/src/utils.ts ================================================ import fs from "fs" import path from "path" export const noop = () => {} export function isBlog(cwd: string): boolean { let file try { // 检查是否有对应文件 file = fs.readFileSync(path.join(cwd, "package.json"), { encoding: "utf-8", }) fs.readFileSync(path.join(cwd, "_config.yml"), { encoding: "utf-8" }) } catch (err) { if (err.code === "ENOENT") { return false } throw err } // 检查是否有hexo依赖 const packageJSON = JSON.parse(file) if (!packageJSON?.dependencies?.hexo) return false return true } export function toRealPath(value: string) { return path.isAbsolute(value) ? value : path.resolve(process.cwd(), "../..", value) } ================================================ FILE: shared/package.json ================================================ { "name": "shared", "private": true, "scripts": { "build": "exit 0" } } ================================================ FILE: shared/src/constants.ts ================================================ export const DATE_FORMAT = "YYYY-MM-DD HH:mm:ss" ================================================ FILE: shared/src/types/api.ts ================================================ export interface ISettings { ui: { editor: { fontFamily: string } } } export interface IFrontmatterTemplateItem { data: string } export interface IFrontmatterTemplate { items: IFrontmatterTemplateItem[] } ================================================ FILE: shared/src/types/hexo.ts ================================================ interface IArticle { _id: string title: string date: string updated?: string | undefined comments: boolean layout: string excerpt?: string | undefined source: string full_source: string path: string permalink: string prev?: string | undefined // _id next?: string | undefined // _id photos?: string[] | undefined link?: string | undefined [key: string]: any } interface IBrief { brief: string } interface IPage { brief: string } interface IPost { slug: string published?: boolean | undefined categories?: string[] | undefined tags: string[] __post: boolean } interface IDetail { _content: string content: string raw?: string | undefined more?: string | undefined } export interface BriefPage extends IArticle, IBrief, IPage {} export interface BriefPost extends IArticle, IBrief, IPost {} export interface Page extends IArticle, IBrief, IPage, IDetail {} export interface Post extends IArticle, IBrief, IPost, IDetail {} export interface Tag { _id: string name: string slug: string path: string permalink: string posts: string[] // _id length: number } export interface Category extends Tag { parent: string } ================================================ FILE: tsconfig.json ================================================ { "include": [ "shared/src/**/*.ts", "server/src/**/*.ts", "server-shared/src/**/*.ts", "server-scripts/src/**/*.ts", ], "compilerOptions": { "baseUrl": ".", "declaration": false, "noImplicitAny": true, "allowJs": true, "target": "es2015", "outDir": "dist", "strict": true, "esModuleInterop": true, "moduleResolution": "node", "experimentalDecorators": true, "emitDecoratorMetadata": true, "paths": { "@server-shared/*": ["server-shared/src/*"], "@server/*": ["server/src/*"], "@shared/*": ["shared/src/*"], } } }