From ec7da12e7b55b8801ad12fb65222f33981f88a40 Mon Sep 17 00:00:00 2001
From: shichao <17568097882@163.com>
Date: Sat, 13 Jan 2024 10:56:48 +0800
Subject: [PATCH] first commit
---
.browserslistrc | 4 +
.commitlintrc.cjs | 107 +++
.dockerignore | 3 +
.editorconfig | 19 +
.env | 2 +
.env.analyze | 23 +
.env.development | 14 +
.env.production | 21 +
.eslintignore | 16 +
.eslintrc.cjs | 7 +
.gitattributes | 11 +
.gitignore | 34 +
.gitpod.yml | 6 +
.npmrc | 7 +
.prettierignore | 12 +
.prettierrc.cjs | 19 +
.stylelintignore | 2 +
.stylelintrc.cjs | 4 +
.vscode/extensions.json | 14 +
.vscode/launch.json | 13 +
.vscode/settings.json | 188 +++++
CNAME | 1 +
README.md | 0
index.html | 158 ++++
internal/eslint-config/.eslintignore | 9 +
internal/eslint-config/.eslintrc.cjs | 4 +
internal/eslint-config/build.config.ts | 10 +
internal/eslint-config/package.json | 50 ++
internal/eslint-config/src/index.ts | 91 ++
internal/eslint-config/src/strict.ts | 57 ++
internal/eslint-config/tsconfig.json | 5 +
internal/stylelint-config/.eslintignore | 9 +
internal/stylelint-config/.eslintrc.cjs | 4 +
internal/stylelint-config/build.config.ts | 10 +
internal/stylelint-config/package.json | 49 ++
internal/stylelint-config/src/index.ts | 92 ++
internal/stylelint-config/tsconfig.json | 5 +
internal/ts-config/base.json | 27 +
internal/ts-config/node-server.json | 18 +
internal/ts-config/node.json | 12 +
internal/ts-config/package.json | 26 +
internal/ts-config/vue-app.json | 10 +
internal/vite-config/.eslintignore | 9 +
internal/vite-config/.eslintrc.cjs | 4 +
internal/vite-config/build.config.ts | 10 +
internal/vite-config/package.json | 59 ++
.../vite-config/src/config/application.ts | 109 +++
internal/vite-config/src/config/common.ts | 22 +
internal/vite-config/src/config/package.ts | 42 +
internal/vite-config/src/index.ts | 2 +
internal/vite-config/src/plugins/appConfig.ts | 104 +++
internal/vite-config/src/plugins/compress.ts | 38 +
internal/vite-config/src/plugins/html.ts | 14 +
internal/vite-config/src/plugins/index.ts | 59 ++
internal/vite-config/src/plugins/mock.ts | 19 +
internal/vite-config/src/plugins/svgSprite.ts | 17 +
.../vite-config/src/plugins/visualizer.ts | 14 +
internal/vite-config/src/utils/env.ts | 49 ++
internal/vite-config/src/utils/hash.ts | 16 +
internal/vite-config/src/utils/modifyVars.ts | 47 ++
internal/vite-config/tsconfig.json | 5 +
mock/_createProductionServer.ts | 34 +
mock/_util.ts | 62 ++
mock/demo/account.ts | 71 ++
mock/demo/api-cascader.ts | 325 +++++++
mock/demo/select-demo.ts | 28 +
mock/demo/system.ts | 203 +++++
mock/demo/table-demo.ts | 55 ++
mock/demo/tree-demo.ts | 38 +
mock/sys/menu.ts | 270 ++++++
mock/sys/user.ts | 122 +++
package.json | 155 ++++
packages/.gitkeep | 0
packages/hooks/.eslintrc.cjs | 4 +
packages/hooks/build.config.ts | 10 +
packages/hooks/package.json | 40 +
packages/hooks/src/index.ts | 7 +
packages/hooks/src/onMountedOrActivated.ts | 25 +
packages/hooks/src/useAttrs.ts | 43 +
packages/hooks/src/useRefs.ts | 24 +
packages/hooks/src/useRequest/Fetch.ts | 147 ++++
packages/hooks/src/useRequest/index.ts | 30 +
.../useRequest/plugins/useAutoRunPlugin.ts | 52 ++
.../src/useRequest/plugins/useCachePlugin.ts | 127 +++
.../useRequest/plugins/useDebouncePlugin.ts | 71 ++
.../plugins/useLoadingDelayPlugin.ts | 45 +
.../useRequest/plugins/usePollingPlugin.ts | 71 ++
.../plugins/useRefreshOnWindowFocusPlugin.ts | 37 +
.../src/useRequest/plugins/useRetryPlugin.ts | 54 ++
.../useRequest/plugins/useThrottlePlugin.ts | 63 ++
packages/hooks/src/useRequest/types.ts | 124 +++
.../src/useRequest/useRequestImplement.ts | 49 ++
packages/hooks/src/useRequest/utils/cache.ts | 48 ++
.../src/useRequest/utils/cachePromise.ts | 23 +
.../src/useRequest/utils/cacheSubscribe.ts | 22 +
.../hooks/src/useRequest/utils/isBrowser.ts | 5 +
.../src/useRequest/utils/isDocumentVisible.ts | 8 +
.../hooks/src/useRequest/utils/isFunction.ts | 2 +
.../hooks/src/useRequest/utils/isOnline.ts | 8 +
packages/hooks/src/useRequest/utils/limit.ts | 12 +
.../src/useRequest/utils/subscribeFocus.ts | 30 +
.../useRequest/utils/subscribeReVisible.ts | 25 +
packages/hooks/src/useScrollTo.ts | 60 ++
packages/hooks/src/useWindowSizeFn.ts | 40 +
packages/hooks/tsconfig.json | 5 +
packages/types/.eslintrc.cjs | 4 +
packages/types/build.config.ts | 10 +
packages/types/package.json | 32 +
packages/types/src/index.ts | 1 +
packages/types/src/utils.ts | 58 ++
packages/types/tsconfig.json | 5 +
pnpm-workspace.yaml | 4 +
public/favicon.ico | Bin 0 -> 894 bytes
public/logo.png | Bin 0 -> 4042 bytes
public/resource/tinymce/langs/en.js | 419 +++++++++
public/resource/tinymce/langs/zh_CN.js | 389 +++++++++
.../ui/oxide-dark/content.inline.min.css | 7 +
.../skins/ui/oxide-dark/content.min.css | 7 +
.../ui/oxide-dark/content.mobile.min.css | 7 +
.../tinymce/skins/ui/oxide-dark/skin.min.css | 7 +
.../skins/ui/oxide-dark/skin.mobile.min.css | 7 +
.../ui/oxide-dark/skin.shadowdom.min.css | 7 +
.../skins/ui/oxide/content.inline.min.css | 7 +
.../tinymce/skins/ui/oxide/content.min.css | 7 +
.../skins/ui/oxide/content.mobile.min.css | 7 +
.../skins/ui/oxide/fonts/tinymce-mobile.woff | Bin 0 -> 4624 bytes
.../tinymce/skins/ui/oxide/skin.min.css | 7 +
.../skins/ui/oxide/skin.mobile.min.css | 7 +
.../skins/ui/oxide/skin.shadowdom.min.css | 7 +
src/App.vue | 40 +
src/api/demo/account.ts | 16 +
src/api/demo/cascader.ts | 9 +
src/api/demo/error.ts | 12 +
src/api/demo/model/accountModel.ts | 7 +
src/api/demo/model/areaModel.ts | 12 +
src/api/demo/model/optionsModel.ts | 15 +
src/api/demo/model/systemModel.ts | 75 ++
src/api/demo/model/tableModel.ts | 20 +
src/api/demo/select.ts | 12 +
src/api/demo/system.ts | 44 +
src/api/model/baseModel.ts | 9 +
src/api/sys/menu.ts | 14 +
src/api/sys/model/menuModel.ts | 17 +
src/api/sys/model/uploadModel.ts | 5 +
src/api/sys/model/userModel.ts | 38 +
src/api/sys/upload.ts | 23 +
src/api/sys/user.ts | 55 ++
src/assets/icons/download-count.svg | 1 +
src/assets/icons/dynamic-avatar-1.svg | 1 +
src/assets/icons/dynamic-avatar-2.svg | 1 +
src/assets/icons/dynamic-avatar-3.svg | 1 +
src/assets/icons/dynamic-avatar-4.svg | 1 +
src/assets/icons/dynamic-avatar-5.svg | 1 +
src/assets/icons/dynamic-avatar-6.svg | 1 +
src/assets/icons/moon.svg | 16 +
src/assets/icons/sun.svg | 42 +
src/assets/icons/test.svg | 21 +
src/assets/icons/total-sales.svg | 1 +
src/assets/icons/transaction.svg | 1 +
src/assets/icons/visit-count.svg | 1 +
src/assets/images/demo.png | Bin 0 -> 33342 bytes
src/assets/images/header.jpg | Bin 0 -> 16880 bytes
src/assets/images/logo.png | Bin 0 -> 28304 bytes
src/assets/svg/illustration.svg | 1 +
src/assets/svg/login-bg-dark.svg | 19 +
src/assets/svg/login-bg.svg | 17 +
src/assets/svg/login-box-bg.svg | 1 +
src/assets/svg/net-error.svg | 1 +
src/assets/svg/no-data.svg | 1 +
src/assets/svg/preview/p-rotate.svg | 1 +
src/assets/svg/preview/resume.svg | 1 +
src/assets/svg/preview/scale.svg | 1 +
src/assets/svg/preview/unrotate.svg | 1 +
src/assets/svg/preview/unscale.svg | 1 +
src/components/Application/index.ts | 15 +
.../Application/src/AppDarkModeToggle.vue | 78 ++
.../Application/src/AppLocalePicker.vue | 76 ++
src/components/Application/src/AppLogo.vue | 93 ++
.../Application/src/AppProvider.vue | 82 ++
.../Application/src/search/AppSearch.vue | 33 +
.../src/search/AppSearchFooter.vue | 59 ++
.../src/search/AppSearchKeyItem.vue | 12 +
.../Application/src/search/AppSearchModal.vue | 266 ++++++
.../Application/src/search/useMenuSearch.ts | 167 ++++
.../Application/src/useAppContext.ts | 17 +
src/components/Authority/index.ts | 4 +
src/components/Authority/src/Authority.vue | 45 +
src/components/Basic/index.ts | 8 +
src/components/Basic/src/BasicArrow.vue | 84 ++
src/components/Basic/src/BasicHelp.vue | 116 +++
src/components/Basic/src/BasicTitle.vue | 76 ++
src/components/Button/index.ts | 9 +
src/components/Button/src/BasicButton.vue | 42 +
.../Button/src/PopConfirmButton.vue | 54 ++
src/components/Button/src/props.ts | 27 +
src/components/CardList/index.ts | 4 +
src/components/CardList/src/CardList.vue | 177 ++++
src/components/CardList/src/data.ts | 25 +
src/components/ClickOutSide/index.ts | 4 +
.../ClickOutSide/src/ClickOutSide.vue | 20 +
src/components/CodeEditor/index.ts | 8 +
src/components/CodeEditor/src/CodeEditor.vue | 56 ++
.../CodeEditor/src/codemirror/CodeMirror.vue | 128 +++
.../CodeEditor/src/codemirror/codeMirror.ts | 21 +
.../CodeEditor/src/codemirror/codemirror.css | 529 ++++++++++++
.../src/json-preview/JsonPreview.vue | 12 +
src/components/CodeEditor/src/typing.ts | 5 +
src/components/Container/index.ts | 8 +
.../Container/src/ScrollContainer.vue | 91 ++
.../src/collapse/CollapseContainer.vue | 118 +++
.../Container/src/collapse/CollapseHeader.vue | 44 +
src/components/Container/src/typing.ts | 17 +
src/components/ContextMenu/index.ts | 3 +
.../ContextMenu/src/ContextMenu.vue | 217 +++++
.../ContextMenu/src/createContextMenu.ts | 77 ++
src/components/ContextMenu/src/typing.ts | 36 +
src/components/CountDown/index.ts | 6 +
src/components/CountDown/src/CountButton.vue | 55 ++
.../CountDown/src/CountdownInput.vue | 46 +
src/components/CountDown/src/useCountdown.ts | 51 ++
src/components/CountTo/index.ts | 4 +
src/components/CountTo/src/CountTo.vue | 107 +++
src/components/Cropper/index.ts | 7 +
src/components/Cropper/src/Cropper.vue | 186 ++++
src/components/Cropper/src/CropperAvatar.vue | 140 ++++
src/components/Cropper/src/CropperModal.vue | 274 ++++++
src/components/Cropper/src/typing.ts | 8 +
src/components/Description/index.ts | 6 +
.../Description/src/Description.vue | 195 +++++
src/components/Description/src/typing.ts | 50 ++
.../Description/src/useDescription.ts | 28 +
src/components/Drawer/index.ts | 6 +
src/components/Drawer/src/BasicDrawer.vue | 231 +++++
.../Drawer/src/components/DrawerFooter.vue | 81 ++
.../Drawer/src/components/DrawerHeader.vue | 68 ++
src/components/Drawer/src/props.ts | 45 +
src/components/Drawer/src/typing.ts | 194 +++++
src/components/Drawer/src/useDrawer.ts | 161 ++++
src/components/Dropdown/index.ts | 5 +
src/components/Dropdown/src/Dropdown.vue | 98 +++
src/components/Dropdown/src/typing.ts | 9 +
src/components/EllipsisText/index.ts | 4 +
.../EllipsisText/src/EllipsisText.vue | 133 +++
src/components/EllipsisText/src/Tooltip.vue | 158 ++++
src/components/EllipsisText/src/_utils.ts | 40 +
src/components/Excel/index.ts | 8 +
src/components/Excel/src/Export2Excel.ts | 148 ++++
src/components/Excel/src/ExportExcelModal.vue | 79 ++
src/components/Excel/src/ImportExcel.vue | 221 +++++
src/components/Excel/src/typing.ts | 41 +
src/components/FlowChart/index.ts | 4 +
src/components/FlowChart/src/FlowChart.vue | 147 ++++
.../FlowChart/src/FlowChartToolbar.vue | 159 ++++
.../FlowChart/src/adpterForTurbo.ts | 75 ++
src/components/FlowChart/src/config.ts | 96 +++
src/components/FlowChart/src/enum.ts | 11 +
src/components/FlowChart/src/types.ts | 14 +
.../FlowChart/src/useFlowContext.ts | 17 +
src/components/Form/index.ts | 17 +
src/components/Form/src/BasicForm.vue | 365 ++++++++
src/components/Form/src/componentMap.ts | 93 ++
.../Form/src/components/ApiCascader.vue | 188 +++++
.../Form/src/components/ApiRadioGroup.vue | 127 +++
.../Form/src/components/ApiSelect.vue | 148 ++++
.../Form/src/components/ApiTransfer.vue | 127 +++
.../Form/src/components/ApiTree.vue | 97 +++
.../Form/src/components/ApiTreeSelect.vue | 110 +++
.../Form/src/components/FormAction.vue | 115 +++
.../Form/src/components/FormItem.vue | 454 ++++++++++
.../Form/src/components/RadioButtonGroup.vue | 56 ++
src/components/Form/src/helper.ts | 103 +++
src/components/Form/src/hooks/useAdvanced.ts | 171 ++++
src/components/Form/src/hooks/useAutoFocus.ts | 44 +
.../Form/src/hooks/useComponentRegister.ts | 19 +
src/components/Form/src/hooks/useForm.ts | 127 +++
.../Form/src/hooks/useFormContext.ts | 17 +
.../Form/src/hooks/useFormEvents.ts | 454 ++++++++++
.../Form/src/hooks/useFormValues.ts | 160 ++++
.../Form/src/hooks/useLabelWidth.ts | 42 +
src/components/Form/src/props.ts | 104 +++
src/components/Form/src/types/form.ts | 268 ++++++
src/components/Form/src/types/formItem.ts | 91 ++
src/components/Form/src/types/hooks.ts | 6 +
src/components/Form/src/types/index.ts | 176 ++++
src/components/Icon/Icon.vue | 108 +++
src/components/Icon/data/icons.data.ts | 793 ++++++++++++++++++
src/components/Icon/index.ts | 4 +
src/components/Icon/src/IconPicker.vue | 209 +++++
src/components/Icon/src/SvgIcon.vue | 62 ++
src/components/Loading/index.ts | 5 +
src/components/Loading/src/Loading.vue | 76 ++
src/components/Loading/src/createLoading.ts | 64 ++
src/components/Loading/src/typing.ts | 10 +
src/components/Loading/src/useLoading.ts | 49 ++
src/components/Markdown/index.ts | 7 +
src/components/Markdown/src/Markdown.vue | 159 ++++
.../Markdown/src/MarkdownViewer.vue | 65 ++
src/components/Markdown/src/adapter.js | 24 +
src/components/Markdown/src/getTheme.ts | 19 +
src/components/Markdown/src/typing.ts | 5 +
src/components/Menu/index.ts | 3 +
src/components/Menu/src/BasicMenu.vue | 149 ++++
.../Menu/src/components/BasicMenuItem.vue | 14 +
.../Menu/src/components/BasicSubMenuItem.vue | 39 +
.../Menu/src/components/MenuItemContent.vue | 25 +
src/components/Menu/src/index.less | 74 ++
src/components/Menu/src/props.ts | 62 ++
src/components/Menu/src/types.ts | 17 +
src/components/Menu/src/useOpenKeys.ts | 81 ++
src/components/Modal/index.ts | 8 +
src/components/Modal/src/BasicModal.vue | 232 +++++
src/components/Modal/src/components/Modal.tsx | 31 +
.../Modal/src/components/ModalClose.vue | 96 +++
.../Modal/src/components/ModalFooter.vue | 36 +
.../Modal/src/components/ModalHeader.vue | 18 +
.../Modal/src/components/ModalWrapper.vue | 154 ++++
src/components/Modal/src/hooks/useModal.ts | 163 ++++
.../Modal/src/hooks/useModalContext.ts | 16 +
.../Modal/src/hooks/useModalDrag.ts | 107 +++
.../Modal/src/hooks/useModalFullScreen.ts | 43 +
src/components/Modal/src/index.less | 142 ++++
src/components/Modal/src/props.ts | 83 ++
src/components/Modal/src/typing.ts | 209 +++++
src/components/Page/index.ts | 7 +
src/components/Page/src/PageFooter.vue | 49 ++
src/components/Page/src/PageWrapper.vue | 203 +++++
src/components/Preview/index.ts | 2 +
src/components/Preview/src/Functional.vue | 551 ++++++++++++
src/components/Preview/src/Preview.vue | 85 ++
src/components/Preview/src/functional.ts | 17 +
src/components/Preview/src/typing.ts | 49 ++
src/components/Prompt/dialog.vue | 49 ++
src/components/Prompt/index.ts | 39 +
src/components/Prompt/state.ts | 69 ++
src/components/Qrcode/index.ts | 5 +
src/components/Qrcode/src/Qrcode.vue | 114 +++
src/components/Qrcode/src/drawCanvas.ts | 37 +
src/components/Qrcode/src/drawLogo.ts | 89 ++
src/components/Qrcode/src/qrcodePlus.ts | 5 +
src/components/Qrcode/src/toCanvas.ts | 11 +
src/components/Qrcode/src/typing.ts | 38 +
src/components/Scrollbar/index.ts | 7 +
src/components/Scrollbar/src/Scrollbar.vue | 199 +++++
src/components/Scrollbar/src/bar.ts | 108 +++
src/components/Scrollbar/src/types.d.ts | 18 +
src/components/Scrollbar/src/util.ts | 59 ++
src/components/SimpleMenu/index.ts | 1 +
src/components/SimpleMenu/src/SimpleMenu.vue | 148 ++++
.../SimpleMenu/src/SimpleMenuTag.vue | 59 ++
.../SimpleMenu/src/SimpleSubMenu.vue | 102 +++
.../SimpleMenu/src/components/Menu.vue | 154 ++++
.../src/components/MenuCollapseTransition.vue | 72 ++
.../SimpleMenu/src/components/MenuItem.vue | 103 +++
.../SimpleMenu/src/components/SubMenuItem.vue | 308 +++++++
.../SimpleMenu/src/components/menu.less | 319 +++++++
.../SimpleMenu/src/components/types.ts | 25 +
.../SimpleMenu/src/components/useMenu.ts | 84 ++
.../src/components/useSimpleMenuContext.ts | 34 +
src/components/SimpleMenu/src/index.less | 77 ++
src/components/SimpleMenu/src/types.ts | 5 +
src/components/SimpleMenu/src/useOpenKeys.ts | 48 ++
src/components/StrengthMeter/index.ts | 4 +
.../StrengthMeter/src/StrengthMeter.vue | 136 +++
src/components/Table/index.ts | 11 +
src/components/Table/src/BasicTable.vue | 434 ++++++++++
src/components/Table/src/componentMap.ts | 40 +
.../src/components/EditTableHeaderIcon.vue | 16 +
.../Table/src/components/HeaderCell.vue | 61 ++
.../Table/src/components/TableAction.vue | 197 +++++
.../Table/src/components/TableFooter.vue | 91 ++
.../Table/src/components/TableHeader.vue | 88 ++
.../Table/src/components/TableImg.vue | 90 ++
.../src/components/TableSelectionBar.vue | 56 ++
.../Table/src/components/TableTitle.vue | 48 ++
.../src/components/editable/CellComponent.ts | 44 +
.../src/components/editable/EditableCell.vue | 556 ++++++++++++
.../Table/src/components/editable/helper.ts | 28 +
.../Table/src/components/editable/index.ts | 68 ++
.../src/components/settings/ColumnSetting.vue | 687 +++++++++++++++
.../components/settings/FullScreenSetting.vue | 22 +
.../src/components/settings/RedoSetting.vue | 23 +
.../src/components/settings/SizeSetting.vue | 61 ++
.../Table/src/components/settings/index.vue | 67 ++
src/components/Table/src/const.ts | 38 +
src/components/Table/src/hooks/useColumns.ts | 335 ++++++++
.../Table/src/hooks/useCustomRow.ts | 102 +++
.../Table/src/hooks/useDataSource.ts | 389 +++++++++
src/components/Table/src/hooks/useLoading.ts | 21 +
.../Table/src/hooks/usePagination.tsx | 84 ++
.../Table/src/hooks/useRowSelection.ts | 130 +++
src/components/Table/src/hooks/useScrollTo.ts | 55 ++
src/components/Table/src/hooks/useTable.ts | 174 ++++
.../Table/src/hooks/useTableContext.ts | 22 +
.../Table/src/hooks/useTableExpand.ts | 72 ++
.../Table/src/hooks/useTableFooter.ts | 56 ++
.../Table/src/hooks/useTableForm.ts | 50 ++
.../Table/src/hooks/useTableHeader.ts | 61 ++
.../Table/src/hooks/useTableScroll.ts | 271 ++++++
.../Table/src/hooks/useTableStyle.ts | 20 +
src/components/Table/src/props.ts | 152 ++++
src/components/Table/src/types/column.ts | 198 +++++
.../Table/src/types/componentType.ts | 14 +
src/components/Table/src/types/pagination.ts | 115 +++
src/components/Table/src/types/table.ts | 512 +++++++++++
src/components/Table/src/types/tableAction.ts | 40 +
src/components/Time/index.ts | 4 +
src/components/Time/src/Time.vue | 104 +++
src/components/Tinymce/index.ts | 4 +
src/components/Tinymce/src/Editor.vue | 323 +++++++
src/components/Tinymce/src/ImgUpload.vue | 86 ++
src/components/Tinymce/src/helper.ts | 81 ++
src/components/Tinymce/src/tinymce.ts | 13 +
src/components/Transition/index.ts | 27 +
.../Transition/src/CollapseTransition.vue | 72 ++
.../Transition/src/CreateTransition.tsx | 73 ++
.../Transition/src/ExpandTransition.ts | 89 ++
src/components/Tree/index.ts | 6 +
src/components/Tree/src/BasicTree.vue | 477 +++++++++++
src/components/Tree/src/TreeIcon.ts | 12 +
.../Tree/src/components/TreeHeader.vue | 176 ++++
src/components/Tree/src/hooks/useTree.ts | 211 +++++
src/components/Tree/src/types/tree.ts | 195 +++++
src/components/Tree/style/index.less | 61 ++
src/components/Tree/style/index.ts | 1 +
src/components/Upload/index.ts | 6 +
src/components/Upload/src/BasicUpload.vue | 109 +++
.../Upload/src/components/FileList.vue | 139 +++
.../Upload/src/components/ImageUpload.vue | 198 +++++
.../Upload/src/components/ThumbUrl.vue | 25 +
.../Upload/src/components/UploadModal.vue | 293 +++++++
.../src/components/UploadPreviewModal.vue | 81 ++
src/components/Upload/src/components/data.tsx | 139 +++
src/components/Upload/src/helper.ts | 27 +
src/components/Upload/src/hooks/useUpload.ts | 61 ++
src/components/Upload/src/props.ts | 118 +++
src/components/Upload/src/types/typing.ts | 46 +
src/components/Verify/index.ts | 7 +
src/components/Verify/src/DragVerify.vue | 371 ++++++++
src/components/Verify/src/ImgRotate.vue | 220 +++++
src/components/Verify/src/props.ts | 87 ++
src/components/Verify/src/typing.ts | 14 +
src/components/VirtualScroll/index.ts | 4 +
.../VirtualScroll/src/VirtualScroll.vue | 191 +++++
src/components/VxeTable/index.ts | 12 +
src/components/VxeTable/src/VxeBasicTable.tsx | 116 +++
src/components/VxeTable/src/componentMap.ts | 59 ++
src/components/VxeTable/src/componentType.ts | 22 +
.../VxeTable/src/components/AApiSelect.tsx | 20 +
.../src/components/AApiTreeSelect.tsx | 20 +
.../VxeTable/src/components/AAutoComplete.tsx | 16 +
.../VxeTable/src/components/AButton.tsx | 120 +++
.../VxeTable/src/components/AButtonGroup.tsx | 59 ++
.../VxeTable/src/components/ACascader.tsx | 42 +
.../src/components/ACheckboxGroup.tsx | 5 +
.../VxeTable/src/components/ADatePicker.tsx | 33 +
.../VxeTable/src/components/AEmpty.tsx | 27 +
.../VxeTable/src/components/AInput.tsx | 16 +
.../VxeTable/src/components/AInputNumber.tsx | 16 +
.../VxeTable/src/components/AInputSearch.tsx | 17 +
.../VxeTable/src/components/AMonthPicker.tsx | 18 +
.../VxeTable/src/components/ARadioGroup.tsx | 5 +
.../VxeTable/src/components/ARangePicker.tsx | 30 +
.../VxeTable/src/components/ARate.tsx | 15 +
.../VxeTable/src/components/ASelect.tsx | 271 ++++++
.../VxeTable/src/components/ASwitch.tsx | 53 ++
.../VxeTable/src/components/ATimePicker.tsx | 18 +
.../VxeTable/src/components/ATreeSelect.tsx | 35 +
.../VxeTable/src/components/AWeekPicker.tsx | 18 +
.../VxeTable/src/components/AYearPicker.tsx | 18 +
.../VxeTable/src/components/common.tsx | 427 ++++++++++
.../VxeTable/src/components/index.tsx | 114 +++
src/components/VxeTable/src/const.ts | 4 +
src/components/VxeTable/src/css/common.scss | 8 +
.../VxeTable/src/css/component.scss | 123 +++
src/components/VxeTable/src/css/index.scss | 5 +
src/components/VxeTable/src/css/toolbar.scss | 26 +
src/components/VxeTable/src/css/variable.scss | 54 ++
src/components/VxeTable/src/emits.ts | 17 +
src/components/VxeTable/src/helper.ts | 19 +
src/components/VxeTable/src/methods.ts | 170 ++++
src/components/VxeTable/src/props.ts | 52 ++
src/components/VxeTable/src/setting.ts | 4 +
src/components/VxeTable/src/types.ts | 7 +
src/components/registerGlobComp.ts | 8 +
src/design/ant/btn.less | 289 +++++++
src/design/ant/index.less | 64 ++
src/design/ant/input.less | 27 +
src/design/ant/pagination.less | 96 +++
src/design/ant/popconfirm.less | 7 +
src/design/ant/table.less | 72 ++
src/design/color.less | 186 ++++
src/design/config.less | 2 +
src/design/entry.css | 168 ++++
src/design/index.less | 54 ++
src/design/public.less | 55 ++
src/design/theme.less | 139 +++
src/design/transition/base.less | 18 +
src/design/transition/fade.less | 97 +++
src/design/transition/index.less | 13 +
src/design/transition/scale.less | 21 +
src/design/transition/scroll.less | 67 ++
src/design/transition/slide.less | 39 +
src/design/transition/zoom.less | 31 +
src/design/var/breakpoint.less | 33 +
src/design/var/easing.less | 18 +
src/design/var/index.less | 39 +
src/directives/clickOutside.ts | 86 ++
src/directives/ellipsis.ts | 42 +
src/directives/index.ts | 13 +
src/directives/loading.ts | 39 +
src/directives/permission.ts | 32 +
src/directives/repeatClick.ts | 31 +
src/directives/ripple/index.less | 21 +
src/directives/ripple/index.ts | 192 +++++
src/enums/appEnum.ts | 52 ++
src/enums/breakpointEnum.ts | 28 +
src/enums/cacheEnum.ts | 35 +
src/enums/exceptionEnum.ts | 27 +
src/enums/httpEnum.ts | 31 +
src/enums/menuEnum.ts | 50 ++
src/enums/pageEnum.ts | 11 +
src/enums/roleEnum.ts | 7 +
src/enums/sizeEnum.ts | 5 +
src/hooks/component/useFormItem.ts | 60 ++
src/hooks/component/usePageContext.ts | 18 +
src/hooks/core/useContext.ts | 43 +
src/hooks/event/useBreakpoint.ts | 93 ++
src/hooks/event/useEventListener.ts | 58 ++
src/hooks/event/useScroll.ts | 65 ++
src/hooks/setting/index.ts | 18 +
src/hooks/setting/useDarkModeTheme.ts | 18 +
src/hooks/setting/useHeaderSetting.ts | 108 +++
src/hooks/setting/useMenuSetting.ts | 168 ++++
src/hooks/setting/useMultipleTabSetting.ts | 31 +
src/hooks/setting/useRootSetting.ts | 95 +++
src/hooks/setting/useTransitionSetting.ts | 31 +
src/hooks/web/useAppInject.ts | 10 +
src/hooks/web/useContentHeight.ts | 189 +++++
src/hooks/web/useContextMenu.ts | 13 +
src/hooks/web/useDesign.ts | 22 +
src/hooks/web/useECharts.ts | 131 +++
src/hooks/web/useFullContent.ts | 28 +
src/hooks/web/useI18n.ts | 59 ++
src/hooks/web/useLockPage.ts | 75 ++
src/hooks/web/useMessage.tsx | 113 +++
src/hooks/web/usePage.ts | 106 +++
src/hooks/web/usePagination.ts | 34 +
src/hooks/web/usePermission.ts | 119 +++
src/hooks/web/useScript.ts | 46 +
src/hooks/web/useSortable.ts | 23 +
src/hooks/web/useTabs.ts | 103 +++
src/hooks/web/useTitle.ts | 34 +
src/hooks/web/useWatermark.ts | 170 ++++
src/layouts/default/content/index.vue | 47 ++
.../default/content/useContentContext.ts | 17 +
.../default/content/useContentViewHeight.ts | 41 +
src/layouts/default/feature/index.vue | 77 ++
src/layouts/default/footer/index.vue | 81 ++
src/layouts/default/header/MultipleHeader.vue | 121 +++
.../default/header/components/Breadcrumb.vue | 204 +++++
.../header/components/ChangeApi/index.vue | 81 ++
.../default/header/components/ErrorAction.vue | 36 +
.../default/header/components/FullScreen.vue | 34 +
.../default/header/components/index.ts | 14 +
.../header/components/lock/LockModal.vue | 115 +++
.../header/components/notify/NoticeList.vue | 176 ++++
.../default/header/components/notify/data.ts | 193 +++++
.../header/components/notify/index.vue | 81 ++
.../components/user-dropdown/DropMenuItem.vue | 26 +
.../header/components/user-dropdown/index.vue | 171 ++++
src/layouts/default/header/index.less | 196 +++++
src/layouts/default/header/index.vue | 153 ++++
src/layouts/default/index.vue | 91 ++
src/layouts/default/menu/index.vue | 197 +++++
src/layouts/default/menu/useLayoutMenu.ts | 109 +++
src/layouts/default/setting/SettingDrawer.tsx | 435 ++++++++++
.../setting/components/InputNumberItem.vue | 49 ++
.../default/setting/components/SelectItem.vue | 67 ++
.../setting/components/SettingFooter.vue | 87 ++
.../default/setting/components/SwitchItem.vue | 58 ++
.../setting/components/ThemeColorPicker.vue | 81 ++
.../default/setting/components/TypePicker.vue | 172 ++++
.../default/setting/components/index.ts | 8 +
src/layouts/default/setting/enum.ts | 157 ++++
src/layouts/default/setting/handler.ts | 182 ++++
src/layouts/default/setting/index.vue | 16 +
src/layouts/default/sider/DragBar.vue | 58 ++
src/layouts/default/sider/LayoutSider.vue | 157 ++++
src/layouts/default/sider/MixSider.vue | 567 +++++++++++++
src/layouts/default/sider/index.vue | 51 ++
src/layouts/default/sider/useLayoutSider.ts | 143 ++++
.../default/tabs/components/FoldButton.vue | 36 +
.../default/tabs/components/SettingButton.vue | 19 +
.../default/tabs/components/TabContent.vue | 63 ++
.../default/tabs/components/TabRedo.vue | 32 +
src/layouts/default/tabs/index.less | 222 +++++
src/layouts/default/tabs/index.vue | 137 +++
src/layouts/default/tabs/types.ts | 25 +
src/layouts/default/tabs/useMultipleTabs.ts | 83 ++
src/layouts/default/tabs/useTabDropdown.ts | 140 ++++
src/layouts/default/trigger/HeaderTrigger.vue | 17 +
src/layouts/default/trigger/SiderTrigger.vue | 12 +
src/layouts/default/trigger/index.vue | 15 +
src/layouts/iframe/index.vue | 23 +
src/layouts/iframe/useFrameKeepAlive.ts | 59 ++
src/layouts/page/index.vue | 57 ++
src/layouts/page/transition.ts | 33 +
src/locales/helper.ts | 37 +
src/locales/lang/en.ts | 12 +
src/locales/lang/en/common.json | 17 +
src/locales/lang/en/component.json | 128 +++
src/locales/lang/en/layout.json | 96 +++
src/locales/lang/en/routes/basic.json | 4 +
src/locales/lang/en/routes/dashboard.json | 6 +
src/locales/lang/en/routes/demo.json | 179 ++++
src/locales/lang/en/sys.json | 92 ++
.../lang/zh-CN/antdLocale/DatePicker.json | 19 +
src/locales/lang/zh-CN/common.json | 20 +
src/locales/lang/zh-CN/component.json | 128 +++
src/locales/lang/zh-CN/layout.json | 96 +++
src/locales/lang/zh-CN/routes/basic.json | 4 +
src/locales/lang/zh-CN/routes/dashboard.json | 6 +
src/locales/lang/zh-CN/routes/demo.json | 178 ++++
src/locales/lang/zh-CN/sys.json | 92 ++
src/locales/lang/zh_CN.ts | 18 +
src/locales/setupI18n.ts | 44 +
src/locales/useLocale.ts | 71 ++
src/logics/error-handle/index.ts | 183 ++++
src/logics/initAppConfig.ts | 77 ++
src/logics/mitt/routeChange.ts | 33 +
src/logics/theme/dark.ts | 26 +
src/logics/theme/index.ts | 1 +
src/logics/theme/updateBackground.ts | 76 ++
src/logics/theme/updateColorWeak.ts | 9 +
src/logics/theme/updateGrayMode.ts | 9 +
src/logics/theme/util.ts | 11 +
src/main.ts | 64 ++
src/router/constant.ts | 24 +
src/router/guard/index.ts | 146 ++++
src/router/guard/paramMenuGuard.ts | 47 ++
src/router/guard/permissionGuard.ts | 119 +++
src/router/guard/stateGuard.ts | 24 +
src/router/helper/menuHelper.ts | 106 +++
src/router/helper/routeHelper.ts | 183 ++++
src/router/index.ts | 42 +
src/router/menus/index.ts | 136 +++
src/router/routes/basic.ts | 73 ++
src/router/routes/index.ts | 49 ++
src/router/routes/mainOut.ts | 22 +
src/router/routes/modules/dashboard.ts | 29 +
src/router/routes/modules/demo/charts.ts | 28 +
src/router/routes/modules/demo/iframe.ts | 49 ++
src/router/routes/modules/demo/level.ts | 68 ++
src/router/routes/modules/demo/permission.ts | 92 ++
src/router/routes/modules/demo/system.ts | 87 ++
src/router/types.ts | 60 ++
src/settings/componentSetting.ts | 97 +++
src/settings/designSetting.ts | 57 ++
src/settings/encryptionSetting.ts | 13 +
src/settings/localeSetting.ts | 29 +
src/settings/projectSetting.ts | 188 +++++
src/settings/siteSetting.ts | 8 +
src/store/index.ts | 12 +
src/store/modules/app.ts | 118 +++
src/store/modules/errorLog.ts | 77 ++
src/store/modules/locale.ts | 55 ++
src/store/modules/lock.ts | 59 ++
src/store/modules/multipleTab.ts | 361 ++++++++
src/store/modules/permission.ts | 260 ++++++
src/store/modules/tableSetting.ts | 109 +++
src/store/modules/user.ts | 177 ++++
src/store/plugin/persist.ts | 75 ++
src/utils/__test__/index.test.ts | 145 ++++
src/utils/auth/index.ts | 25 +
src/utils/bem.ts | 53 ++
src/utils/cache/index.ts | 31 +
src/utils/cache/memory.ts | 107 +++
src/utils/cache/persistent.ts | 134 +++
src/utils/cache/storageCache.ts | 111 +++
src/utils/cipher.ts | 159 ++++
src/utils/color.ts | 151 ++++
src/utils/copyTextToClipboard.ts | 41 +
src/utils/dateUtil.ts | 17 +
src/utils/domUtils.ts | 180 ++++
src/utils/env.ts | 82 ++
src/utils/event/index.ts | 42 +
src/utils/factory/createAsyncComponent.tsx | 70 ++
src/utils/file/base64Conver.ts | 41 +
src/utils/file/download.ts | 96 +++
src/utils/helper/treeHelper.ts | 216 +++++
src/utils/helper/tsxHelper.tsx | 37 +
src/utils/http/axios/Axios.ts | 252 ++++++
src/utils/http/axios/axiosCancel.ts | 60 ++
src/utils/http/axios/axiosRetry.ts | 30 +
src/utils/http/axios/axiosTransform.ts | 57 ++
src/utils/http/axios/checkStatus.ts | 80 ++
src/utils/http/axios/helper.ts | 48 ++
src/utils/http/axios/index.ts | 285 +++++++
src/utils/index.ts | 147 ++++
src/utils/is.ts | 72 ++
src/utils/lib/echarts.ts | 57 ++
src/utils/log.ts | 9 +
src/utils/mitt.ts | 122 +++
src/utils/propTypes.ts | 35 +
src/utils/props.ts | 185 ++++
src/utils/types.ts | 65 ++
src/utils/uuid.ts | 28 +
.../analysis/components/VisitSource.vue | 82 ++
src/views/dashboard/analysis/index.vue | 17 +
src/views/demo/charts/map/Baidu.vue | 42 +
src/views/demo/level/Menu111.vue | 10 +
src/views/demo/level/Menu12.vue | 10 +
src/views/demo/level/Menu2.vue | 10 +
src/views/demo/main-out/index.vue | 6 +
src/views/demo/page/result/fail/index.vue | 47 ++
src/views/demo/page/result/success/index.vue | 51 ++
.../demo/permission/CurrentPermissionMode.vue | 23 +
src/views/demo/permission/back/Btn.vue | 95 +++
src/views/demo/permission/back/index.vue | 57 ++
src/views/demo/permission/front/AuthPageA.vue | 16 +
src/views/demo/permission/front/AuthPageB.vue | 16 +
src/views/demo/permission/front/Btn.vue | 82 ++
src/views/demo/permission/front/index.vue | 47 ++
.../demo/system/account/AccountDetail.vue | 61 ++
.../demo/system/account/AccountModal.vue | 69 ++
src/views/demo/system/account/DeptTree.vue | 38 +
src/views/demo/system/account/account.data.ts | 157 ++++
src/views/demo/system/account/index.vue | 121 +++
src/views/demo/system/dept/DeptModal.vue | 58 ++
src/views/demo/system/dept/dept.data.ts | 111 +++
src/views/demo/system/dept/index.vue | 88 ++
src/views/demo/system/menu/MenuDrawer.vue | 65 ++
src/views/demo/system/menu/index.vue | 96 +++
src/views/demo/system/menu/menu.data.ts | 201 +++++
src/views/demo/system/password/index.vue | 41 +
src/views/demo/system/password/pwd.data.ts | 46 +
src/views/demo/system/role/RoleDrawer.vue | 74 ++
src/views/demo/system/role/index.vue | 85 ++
src/views/demo/system/role/role.data.ts | 123 +++
src/views/demo/system/vxe-account/index.vue | 86 ++
.../system/vxe-account/vxeAccount.data.ts | 84 ++
src/views/sys/error-log/DetailModal.vue | 27 +
src/views/sys/error-log/data.tsx | 67 ++
src/views/sys/error-log/index.vue | 97 +++
src/views/sys/exception/Exception.vue | 148 ++++
src/views/sys/exception/index.ts | 1 +
src/views/sys/iframe/FrameBlank.vue | 6 +
src/views/sys/iframe/index.vue | 120 +++
src/views/sys/lock/LockPage.vue | 237 ++++++
src/views/sys/lock/index.vue | 13 +
src/views/sys/lock/useNow.ts | 60 ++
src/views/sys/login/ForgetPasswordForm.vue | 64 ++
src/views/sys/login/Login.vue | 212 +++++
src/views/sys/login/LoginForm.vue | 159 ++++
src/views/sys/login/LoginFormTitle.vue | 25 +
src/views/sys/login/MobileForm.vue | 63 ++
src/views/sys/login/QrCodeForm.vue | 31 +
src/views/sys/login/RegisterForm.vue | 104 +++
src/views/sys/login/SessionTimeoutLogin.vue | 54 ++
src/views/sys/login/useLogin.ts | 130 +++
src/views/sys/redirect/index.vue | 30 +
tsconfig.json | 27 +
turbo.json | 18 +
types/axios.d.ts | 56 ++
types/config.d.ts | 162 ++++
types/global.d.ts | 98 +++
types/index.d.ts | 27 +
types/module.d.ts | 18 +
types/store.d.ts | 60 ++
types/utils.d.ts | 6 +
types/vue-router.d.ts | 49 ++
uno.config.ts | 5 +
vite.config.ts | 39 +
....timestamp-1705047329667-3f84b63136279.mjs | 41 +
774 files changed, 57320 insertions(+)
create mode 100644 .browserslistrc
create mode 100644 .commitlintrc.cjs
create mode 100644 .dockerignore
create mode 100644 .editorconfig
create mode 100644 .env
create mode 100644 .env.analyze
create mode 100644 .env.development
create mode 100644 .env.production
create mode 100644 .eslintignore
create mode 100644 .eslintrc.cjs
create mode 100644 .gitattributes
create mode 100644 .gitignore
create mode 100644 .gitpod.yml
create mode 100644 .npmrc
create mode 100644 .prettierignore
create mode 100644 .prettierrc.cjs
create mode 100644 .stylelintignore
create mode 100644 .stylelintrc.cjs
create mode 100644 .vscode/extensions.json
create mode 100644 .vscode/launch.json
create mode 100644 .vscode/settings.json
create mode 100644 CNAME
create mode 100644 README.md
create mode 100644 index.html
create mode 100644 internal/eslint-config/.eslintignore
create mode 100644 internal/eslint-config/.eslintrc.cjs
create mode 100644 internal/eslint-config/build.config.ts
create mode 100644 internal/eslint-config/package.json
create mode 100644 internal/eslint-config/src/index.ts
create mode 100644 internal/eslint-config/src/strict.ts
create mode 100644 internal/eslint-config/tsconfig.json
create mode 100644 internal/stylelint-config/.eslintignore
create mode 100644 internal/stylelint-config/.eslintrc.cjs
create mode 100644 internal/stylelint-config/build.config.ts
create mode 100644 internal/stylelint-config/package.json
create mode 100644 internal/stylelint-config/src/index.ts
create mode 100644 internal/stylelint-config/tsconfig.json
create mode 100644 internal/ts-config/base.json
create mode 100644 internal/ts-config/node-server.json
create mode 100644 internal/ts-config/node.json
create mode 100644 internal/ts-config/package.json
create mode 100644 internal/ts-config/vue-app.json
create mode 100644 internal/vite-config/.eslintignore
create mode 100644 internal/vite-config/.eslintrc.cjs
create mode 100644 internal/vite-config/build.config.ts
create mode 100644 internal/vite-config/package.json
create mode 100644 internal/vite-config/src/config/application.ts
create mode 100644 internal/vite-config/src/config/common.ts
create mode 100644 internal/vite-config/src/config/package.ts
create mode 100644 internal/vite-config/src/index.ts
create mode 100644 internal/vite-config/src/plugins/appConfig.ts
create mode 100644 internal/vite-config/src/plugins/compress.ts
create mode 100644 internal/vite-config/src/plugins/html.ts
create mode 100644 internal/vite-config/src/plugins/index.ts
create mode 100644 internal/vite-config/src/plugins/mock.ts
create mode 100644 internal/vite-config/src/plugins/svgSprite.ts
create mode 100644 internal/vite-config/src/plugins/visualizer.ts
create mode 100644 internal/vite-config/src/utils/env.ts
create mode 100644 internal/vite-config/src/utils/hash.ts
create mode 100644 internal/vite-config/src/utils/modifyVars.ts
create mode 100644 internal/vite-config/tsconfig.json
create mode 100644 mock/_createProductionServer.ts
create mode 100644 mock/_util.ts
create mode 100644 mock/demo/account.ts
create mode 100644 mock/demo/api-cascader.ts
create mode 100644 mock/demo/select-demo.ts
create mode 100644 mock/demo/system.ts
create mode 100644 mock/demo/table-demo.ts
create mode 100644 mock/demo/tree-demo.ts
create mode 100644 mock/sys/menu.ts
create mode 100644 mock/sys/user.ts
create mode 100644 package.json
create mode 100644 packages/.gitkeep
create mode 100644 packages/hooks/.eslintrc.cjs
create mode 100644 packages/hooks/build.config.ts
create mode 100644 packages/hooks/package.json
create mode 100644 packages/hooks/src/index.ts
create mode 100644 packages/hooks/src/onMountedOrActivated.ts
create mode 100644 packages/hooks/src/useAttrs.ts
create mode 100644 packages/hooks/src/useRefs.ts
create mode 100644 packages/hooks/src/useRequest/Fetch.ts
create mode 100644 packages/hooks/src/useRequest/index.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useAutoRunPlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useCachePlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useDebouncePlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useLoadingDelayPlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/usePollingPlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useRefreshOnWindowFocusPlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useRetryPlugin.ts
create mode 100644 packages/hooks/src/useRequest/plugins/useThrottlePlugin.ts
create mode 100644 packages/hooks/src/useRequest/types.ts
create mode 100644 packages/hooks/src/useRequest/useRequestImplement.ts
create mode 100644 packages/hooks/src/useRequest/utils/cache.ts
create mode 100644 packages/hooks/src/useRequest/utils/cachePromise.ts
create mode 100644 packages/hooks/src/useRequest/utils/cacheSubscribe.ts
create mode 100644 packages/hooks/src/useRequest/utils/isBrowser.ts
create mode 100644 packages/hooks/src/useRequest/utils/isDocumentVisible.ts
create mode 100644 packages/hooks/src/useRequest/utils/isFunction.ts
create mode 100644 packages/hooks/src/useRequest/utils/isOnline.ts
create mode 100644 packages/hooks/src/useRequest/utils/limit.ts
create mode 100644 packages/hooks/src/useRequest/utils/subscribeFocus.ts
create mode 100644 packages/hooks/src/useRequest/utils/subscribeReVisible.ts
create mode 100644 packages/hooks/src/useScrollTo.ts
create mode 100644 packages/hooks/src/useWindowSizeFn.ts
create mode 100644 packages/hooks/tsconfig.json
create mode 100644 packages/types/.eslintrc.cjs
create mode 100644 packages/types/build.config.ts
create mode 100644 packages/types/package.json
create mode 100644 packages/types/src/index.ts
create mode 100644 packages/types/src/utils.ts
create mode 100644 packages/types/tsconfig.json
create mode 100644 pnpm-workspace.yaml
create mode 100644 public/favicon.ico
create mode 100644 public/logo.png
create mode 100644 public/resource/tinymce/langs/en.js
create mode 100644 public/resource/tinymce/langs/zh_CN.js
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/content.inline.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/content.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/content.mobile.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff
create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
create mode 100644 public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css
create mode 100644 src/App.vue
create mode 100644 src/api/demo/account.ts
create mode 100644 src/api/demo/cascader.ts
create mode 100644 src/api/demo/error.ts
create mode 100644 src/api/demo/model/accountModel.ts
create mode 100644 src/api/demo/model/areaModel.ts
create mode 100644 src/api/demo/model/optionsModel.ts
create mode 100644 src/api/demo/model/systemModel.ts
create mode 100644 src/api/demo/model/tableModel.ts
create mode 100644 src/api/demo/select.ts
create mode 100644 src/api/demo/system.ts
create mode 100644 src/api/model/baseModel.ts
create mode 100644 src/api/sys/menu.ts
create mode 100644 src/api/sys/model/menuModel.ts
create mode 100644 src/api/sys/model/uploadModel.ts
create mode 100644 src/api/sys/model/userModel.ts
create mode 100644 src/api/sys/upload.ts
create mode 100644 src/api/sys/user.ts
create mode 100644 src/assets/icons/download-count.svg
create mode 100644 src/assets/icons/dynamic-avatar-1.svg
create mode 100644 src/assets/icons/dynamic-avatar-2.svg
create mode 100644 src/assets/icons/dynamic-avatar-3.svg
create mode 100644 src/assets/icons/dynamic-avatar-4.svg
create mode 100644 src/assets/icons/dynamic-avatar-5.svg
create mode 100644 src/assets/icons/dynamic-avatar-6.svg
create mode 100644 src/assets/icons/moon.svg
create mode 100644 src/assets/icons/sun.svg
create mode 100644 src/assets/icons/test.svg
create mode 100644 src/assets/icons/total-sales.svg
create mode 100644 src/assets/icons/transaction.svg
create mode 100644 src/assets/icons/visit-count.svg
create mode 100644 src/assets/images/demo.png
create mode 100644 src/assets/images/header.jpg
create mode 100644 src/assets/images/logo.png
create mode 100644 src/assets/svg/illustration.svg
create mode 100644 src/assets/svg/login-bg-dark.svg
create mode 100644 src/assets/svg/login-bg.svg
create mode 100644 src/assets/svg/login-box-bg.svg
create mode 100644 src/assets/svg/net-error.svg
create mode 100644 src/assets/svg/no-data.svg
create mode 100644 src/assets/svg/preview/p-rotate.svg
create mode 100644 src/assets/svg/preview/resume.svg
create mode 100644 src/assets/svg/preview/scale.svg
create mode 100644 src/assets/svg/preview/unrotate.svg
create mode 100644 src/assets/svg/preview/unscale.svg
create mode 100644 src/components/Application/index.ts
create mode 100644 src/components/Application/src/AppDarkModeToggle.vue
create mode 100644 src/components/Application/src/AppLocalePicker.vue
create mode 100644 src/components/Application/src/AppLogo.vue
create mode 100644 src/components/Application/src/AppProvider.vue
create mode 100644 src/components/Application/src/search/AppSearch.vue
create mode 100644 src/components/Application/src/search/AppSearchFooter.vue
create mode 100644 src/components/Application/src/search/AppSearchKeyItem.vue
create mode 100644 src/components/Application/src/search/AppSearchModal.vue
create mode 100644 src/components/Application/src/search/useMenuSearch.ts
create mode 100644 src/components/Application/src/useAppContext.ts
create mode 100644 src/components/Authority/index.ts
create mode 100644 src/components/Authority/src/Authority.vue
create mode 100644 src/components/Basic/index.ts
create mode 100644 src/components/Basic/src/BasicArrow.vue
create mode 100644 src/components/Basic/src/BasicHelp.vue
create mode 100644 src/components/Basic/src/BasicTitle.vue
create mode 100644 src/components/Button/index.ts
create mode 100644 src/components/Button/src/BasicButton.vue
create mode 100644 src/components/Button/src/PopConfirmButton.vue
create mode 100644 src/components/Button/src/props.ts
create mode 100644 src/components/CardList/index.ts
create mode 100644 src/components/CardList/src/CardList.vue
create mode 100644 src/components/CardList/src/data.ts
create mode 100644 src/components/ClickOutSide/index.ts
create mode 100644 src/components/ClickOutSide/src/ClickOutSide.vue
create mode 100644 src/components/CodeEditor/index.ts
create mode 100644 src/components/CodeEditor/src/CodeEditor.vue
create mode 100644 src/components/CodeEditor/src/codemirror/CodeMirror.vue
create mode 100644 src/components/CodeEditor/src/codemirror/codeMirror.ts
create mode 100644 src/components/CodeEditor/src/codemirror/codemirror.css
create mode 100644 src/components/CodeEditor/src/json-preview/JsonPreview.vue
create mode 100644 src/components/CodeEditor/src/typing.ts
create mode 100644 src/components/Container/index.ts
create mode 100644 src/components/Container/src/ScrollContainer.vue
create mode 100644 src/components/Container/src/collapse/CollapseContainer.vue
create mode 100644 src/components/Container/src/collapse/CollapseHeader.vue
create mode 100644 src/components/Container/src/typing.ts
create mode 100644 src/components/ContextMenu/index.ts
create mode 100644 src/components/ContextMenu/src/ContextMenu.vue
create mode 100644 src/components/ContextMenu/src/createContextMenu.ts
create mode 100644 src/components/ContextMenu/src/typing.ts
create mode 100644 src/components/CountDown/index.ts
create mode 100644 src/components/CountDown/src/CountButton.vue
create mode 100644 src/components/CountDown/src/CountdownInput.vue
create mode 100644 src/components/CountDown/src/useCountdown.ts
create mode 100644 src/components/CountTo/index.ts
create mode 100644 src/components/CountTo/src/CountTo.vue
create mode 100644 src/components/Cropper/index.ts
create mode 100644 src/components/Cropper/src/Cropper.vue
create mode 100644 src/components/Cropper/src/CropperAvatar.vue
create mode 100644 src/components/Cropper/src/CropperModal.vue
create mode 100644 src/components/Cropper/src/typing.ts
create mode 100644 src/components/Description/index.ts
create mode 100644 src/components/Description/src/Description.vue
create mode 100644 src/components/Description/src/typing.ts
create mode 100644 src/components/Description/src/useDescription.ts
create mode 100644 src/components/Drawer/index.ts
create mode 100644 src/components/Drawer/src/BasicDrawer.vue
create mode 100644 src/components/Drawer/src/components/DrawerFooter.vue
create mode 100644 src/components/Drawer/src/components/DrawerHeader.vue
create mode 100644 src/components/Drawer/src/props.ts
create mode 100644 src/components/Drawer/src/typing.ts
create mode 100644 src/components/Drawer/src/useDrawer.ts
create mode 100644 src/components/Dropdown/index.ts
create mode 100644 src/components/Dropdown/src/Dropdown.vue
create mode 100644 src/components/Dropdown/src/typing.ts
create mode 100644 src/components/EllipsisText/index.ts
create mode 100644 src/components/EllipsisText/src/EllipsisText.vue
create mode 100644 src/components/EllipsisText/src/Tooltip.vue
create mode 100644 src/components/EllipsisText/src/_utils.ts
create mode 100644 src/components/Excel/index.ts
create mode 100644 src/components/Excel/src/Export2Excel.ts
create mode 100644 src/components/Excel/src/ExportExcelModal.vue
create mode 100644 src/components/Excel/src/ImportExcel.vue
create mode 100644 src/components/Excel/src/typing.ts
create mode 100644 src/components/FlowChart/index.ts
create mode 100644 src/components/FlowChart/src/FlowChart.vue
create mode 100644 src/components/FlowChart/src/FlowChartToolbar.vue
create mode 100644 src/components/FlowChart/src/adpterForTurbo.ts
create mode 100644 src/components/FlowChart/src/config.ts
create mode 100644 src/components/FlowChart/src/enum.ts
create mode 100644 src/components/FlowChart/src/types.ts
create mode 100644 src/components/FlowChart/src/useFlowContext.ts
create mode 100644 src/components/Form/index.ts
create mode 100644 src/components/Form/src/BasicForm.vue
create mode 100644 src/components/Form/src/componentMap.ts
create mode 100644 src/components/Form/src/components/ApiCascader.vue
create mode 100644 src/components/Form/src/components/ApiRadioGroup.vue
create mode 100644 src/components/Form/src/components/ApiSelect.vue
create mode 100644 src/components/Form/src/components/ApiTransfer.vue
create mode 100644 src/components/Form/src/components/ApiTree.vue
create mode 100644 src/components/Form/src/components/ApiTreeSelect.vue
create mode 100644 src/components/Form/src/components/FormAction.vue
create mode 100644 src/components/Form/src/components/FormItem.vue
create mode 100644 src/components/Form/src/components/RadioButtonGroup.vue
create mode 100644 src/components/Form/src/helper.ts
create mode 100644 src/components/Form/src/hooks/useAdvanced.ts
create mode 100644 src/components/Form/src/hooks/useAutoFocus.ts
create mode 100644 src/components/Form/src/hooks/useComponentRegister.ts
create mode 100644 src/components/Form/src/hooks/useForm.ts
create mode 100644 src/components/Form/src/hooks/useFormContext.ts
create mode 100644 src/components/Form/src/hooks/useFormEvents.ts
create mode 100644 src/components/Form/src/hooks/useFormValues.ts
create mode 100644 src/components/Form/src/hooks/useLabelWidth.ts
create mode 100644 src/components/Form/src/props.ts
create mode 100644 src/components/Form/src/types/form.ts
create mode 100644 src/components/Form/src/types/formItem.ts
create mode 100644 src/components/Form/src/types/hooks.ts
create mode 100644 src/components/Form/src/types/index.ts
create mode 100644 src/components/Icon/Icon.vue
create mode 100644 src/components/Icon/data/icons.data.ts
create mode 100644 src/components/Icon/index.ts
create mode 100644 src/components/Icon/src/IconPicker.vue
create mode 100644 src/components/Icon/src/SvgIcon.vue
create mode 100644 src/components/Loading/index.ts
create mode 100644 src/components/Loading/src/Loading.vue
create mode 100644 src/components/Loading/src/createLoading.ts
create mode 100644 src/components/Loading/src/typing.ts
create mode 100644 src/components/Loading/src/useLoading.ts
create mode 100644 src/components/Markdown/index.ts
create mode 100644 src/components/Markdown/src/Markdown.vue
create mode 100644 src/components/Markdown/src/MarkdownViewer.vue
create mode 100644 src/components/Markdown/src/adapter.js
create mode 100644 src/components/Markdown/src/getTheme.ts
create mode 100644 src/components/Markdown/src/typing.ts
create mode 100644 src/components/Menu/index.ts
create mode 100644 src/components/Menu/src/BasicMenu.vue
create mode 100644 src/components/Menu/src/components/BasicMenuItem.vue
create mode 100644 src/components/Menu/src/components/BasicSubMenuItem.vue
create mode 100644 src/components/Menu/src/components/MenuItemContent.vue
create mode 100644 src/components/Menu/src/index.less
create mode 100644 src/components/Menu/src/props.ts
create mode 100644 src/components/Menu/src/types.ts
create mode 100644 src/components/Menu/src/useOpenKeys.ts
create mode 100644 src/components/Modal/index.ts
create mode 100644 src/components/Modal/src/BasicModal.vue
create mode 100644 src/components/Modal/src/components/Modal.tsx
create mode 100644 src/components/Modal/src/components/ModalClose.vue
create mode 100644 src/components/Modal/src/components/ModalFooter.vue
create mode 100644 src/components/Modal/src/components/ModalHeader.vue
create mode 100644 src/components/Modal/src/components/ModalWrapper.vue
create mode 100644 src/components/Modal/src/hooks/useModal.ts
create mode 100644 src/components/Modal/src/hooks/useModalContext.ts
create mode 100644 src/components/Modal/src/hooks/useModalDrag.ts
create mode 100644 src/components/Modal/src/hooks/useModalFullScreen.ts
create mode 100644 src/components/Modal/src/index.less
create mode 100644 src/components/Modal/src/props.ts
create mode 100644 src/components/Modal/src/typing.ts
create mode 100644 src/components/Page/index.ts
create mode 100644 src/components/Page/src/PageFooter.vue
create mode 100644 src/components/Page/src/PageWrapper.vue
create mode 100644 src/components/Preview/index.ts
create mode 100644 src/components/Preview/src/Functional.vue
create mode 100644 src/components/Preview/src/Preview.vue
create mode 100644 src/components/Preview/src/functional.ts
create mode 100644 src/components/Preview/src/typing.ts
create mode 100644 src/components/Prompt/dialog.vue
create mode 100644 src/components/Prompt/index.ts
create mode 100644 src/components/Prompt/state.ts
create mode 100644 src/components/Qrcode/index.ts
create mode 100644 src/components/Qrcode/src/Qrcode.vue
create mode 100644 src/components/Qrcode/src/drawCanvas.ts
create mode 100644 src/components/Qrcode/src/drawLogo.ts
create mode 100644 src/components/Qrcode/src/qrcodePlus.ts
create mode 100644 src/components/Qrcode/src/toCanvas.ts
create mode 100644 src/components/Qrcode/src/typing.ts
create mode 100644 src/components/Scrollbar/index.ts
create mode 100644 src/components/Scrollbar/src/Scrollbar.vue
create mode 100644 src/components/Scrollbar/src/bar.ts
create mode 100644 src/components/Scrollbar/src/types.d.ts
create mode 100644 src/components/Scrollbar/src/util.ts
create mode 100644 src/components/SimpleMenu/index.ts
create mode 100644 src/components/SimpleMenu/src/SimpleMenu.vue
create mode 100644 src/components/SimpleMenu/src/SimpleMenuTag.vue
create mode 100644 src/components/SimpleMenu/src/SimpleSubMenu.vue
create mode 100644 src/components/SimpleMenu/src/components/Menu.vue
create mode 100644 src/components/SimpleMenu/src/components/MenuCollapseTransition.vue
create mode 100644 src/components/SimpleMenu/src/components/MenuItem.vue
create mode 100644 src/components/SimpleMenu/src/components/SubMenuItem.vue
create mode 100644 src/components/SimpleMenu/src/components/menu.less
create mode 100644 src/components/SimpleMenu/src/components/types.ts
create mode 100644 src/components/SimpleMenu/src/components/useMenu.ts
create mode 100644 src/components/SimpleMenu/src/components/useSimpleMenuContext.ts
create mode 100644 src/components/SimpleMenu/src/index.less
create mode 100644 src/components/SimpleMenu/src/types.ts
create mode 100644 src/components/SimpleMenu/src/useOpenKeys.ts
create mode 100644 src/components/StrengthMeter/index.ts
create mode 100644 src/components/StrengthMeter/src/StrengthMeter.vue
create mode 100644 src/components/Table/index.ts
create mode 100644 src/components/Table/src/BasicTable.vue
create mode 100644 src/components/Table/src/componentMap.ts
create mode 100644 src/components/Table/src/components/EditTableHeaderIcon.vue
create mode 100644 src/components/Table/src/components/HeaderCell.vue
create mode 100644 src/components/Table/src/components/TableAction.vue
create mode 100644 src/components/Table/src/components/TableFooter.vue
create mode 100644 src/components/Table/src/components/TableHeader.vue
create mode 100644 src/components/Table/src/components/TableImg.vue
create mode 100644 src/components/Table/src/components/TableSelectionBar.vue
create mode 100644 src/components/Table/src/components/TableTitle.vue
create mode 100644 src/components/Table/src/components/editable/CellComponent.ts
create mode 100644 src/components/Table/src/components/editable/EditableCell.vue
create mode 100644 src/components/Table/src/components/editable/helper.ts
create mode 100644 src/components/Table/src/components/editable/index.ts
create mode 100644 src/components/Table/src/components/settings/ColumnSetting.vue
create mode 100644 src/components/Table/src/components/settings/FullScreenSetting.vue
create mode 100644 src/components/Table/src/components/settings/RedoSetting.vue
create mode 100644 src/components/Table/src/components/settings/SizeSetting.vue
create mode 100644 src/components/Table/src/components/settings/index.vue
create mode 100644 src/components/Table/src/const.ts
create mode 100644 src/components/Table/src/hooks/useColumns.ts
create mode 100644 src/components/Table/src/hooks/useCustomRow.ts
create mode 100644 src/components/Table/src/hooks/useDataSource.ts
create mode 100644 src/components/Table/src/hooks/useLoading.ts
create mode 100644 src/components/Table/src/hooks/usePagination.tsx
create mode 100644 src/components/Table/src/hooks/useRowSelection.ts
create mode 100644 src/components/Table/src/hooks/useScrollTo.ts
create mode 100644 src/components/Table/src/hooks/useTable.ts
create mode 100644 src/components/Table/src/hooks/useTableContext.ts
create mode 100644 src/components/Table/src/hooks/useTableExpand.ts
create mode 100644 src/components/Table/src/hooks/useTableFooter.ts
create mode 100644 src/components/Table/src/hooks/useTableForm.ts
create mode 100644 src/components/Table/src/hooks/useTableHeader.ts
create mode 100644 src/components/Table/src/hooks/useTableScroll.ts
create mode 100644 src/components/Table/src/hooks/useTableStyle.ts
create mode 100644 src/components/Table/src/props.ts
create mode 100644 src/components/Table/src/types/column.ts
create mode 100644 src/components/Table/src/types/componentType.ts
create mode 100644 src/components/Table/src/types/pagination.ts
create mode 100644 src/components/Table/src/types/table.ts
create mode 100644 src/components/Table/src/types/tableAction.ts
create mode 100644 src/components/Time/index.ts
create mode 100644 src/components/Time/src/Time.vue
create mode 100644 src/components/Tinymce/index.ts
create mode 100644 src/components/Tinymce/src/Editor.vue
create mode 100644 src/components/Tinymce/src/ImgUpload.vue
create mode 100644 src/components/Tinymce/src/helper.ts
create mode 100644 src/components/Tinymce/src/tinymce.ts
create mode 100644 src/components/Transition/index.ts
create mode 100644 src/components/Transition/src/CollapseTransition.vue
create mode 100644 src/components/Transition/src/CreateTransition.tsx
create mode 100644 src/components/Transition/src/ExpandTransition.ts
create mode 100644 src/components/Tree/index.ts
create mode 100644 src/components/Tree/src/BasicTree.vue
create mode 100644 src/components/Tree/src/TreeIcon.ts
create mode 100644 src/components/Tree/src/components/TreeHeader.vue
create mode 100644 src/components/Tree/src/hooks/useTree.ts
create mode 100644 src/components/Tree/src/types/tree.ts
create mode 100644 src/components/Tree/style/index.less
create mode 100644 src/components/Tree/style/index.ts
create mode 100644 src/components/Upload/index.ts
create mode 100644 src/components/Upload/src/BasicUpload.vue
create mode 100644 src/components/Upload/src/components/FileList.vue
create mode 100644 src/components/Upload/src/components/ImageUpload.vue
create mode 100644 src/components/Upload/src/components/ThumbUrl.vue
create mode 100644 src/components/Upload/src/components/UploadModal.vue
create mode 100644 src/components/Upload/src/components/UploadPreviewModal.vue
create mode 100644 src/components/Upload/src/components/data.tsx
create mode 100644 src/components/Upload/src/helper.ts
create mode 100644 src/components/Upload/src/hooks/useUpload.ts
create mode 100644 src/components/Upload/src/props.ts
create mode 100644 src/components/Upload/src/types/typing.ts
create mode 100644 src/components/Verify/index.ts
create mode 100644 src/components/Verify/src/DragVerify.vue
create mode 100644 src/components/Verify/src/ImgRotate.vue
create mode 100644 src/components/Verify/src/props.ts
create mode 100644 src/components/Verify/src/typing.ts
create mode 100644 src/components/VirtualScroll/index.ts
create mode 100644 src/components/VirtualScroll/src/VirtualScroll.vue
create mode 100644 src/components/VxeTable/index.ts
create mode 100644 src/components/VxeTable/src/VxeBasicTable.tsx
create mode 100644 src/components/VxeTable/src/componentMap.ts
create mode 100644 src/components/VxeTable/src/componentType.ts
create mode 100644 src/components/VxeTable/src/components/AApiSelect.tsx
create mode 100644 src/components/VxeTable/src/components/AApiTreeSelect.tsx
create mode 100644 src/components/VxeTable/src/components/AAutoComplete.tsx
create mode 100644 src/components/VxeTable/src/components/AButton.tsx
create mode 100644 src/components/VxeTable/src/components/AButtonGroup.tsx
create mode 100644 src/components/VxeTable/src/components/ACascader.tsx
create mode 100644 src/components/VxeTable/src/components/ACheckboxGroup.tsx
create mode 100644 src/components/VxeTable/src/components/ADatePicker.tsx
create mode 100644 src/components/VxeTable/src/components/AEmpty.tsx
create mode 100644 src/components/VxeTable/src/components/AInput.tsx
create mode 100644 src/components/VxeTable/src/components/AInputNumber.tsx
create mode 100644 src/components/VxeTable/src/components/AInputSearch.tsx
create mode 100644 src/components/VxeTable/src/components/AMonthPicker.tsx
create mode 100644 src/components/VxeTable/src/components/ARadioGroup.tsx
create mode 100644 src/components/VxeTable/src/components/ARangePicker.tsx
create mode 100644 src/components/VxeTable/src/components/ARate.tsx
create mode 100644 src/components/VxeTable/src/components/ASelect.tsx
create mode 100644 src/components/VxeTable/src/components/ASwitch.tsx
create mode 100644 src/components/VxeTable/src/components/ATimePicker.tsx
create mode 100644 src/components/VxeTable/src/components/ATreeSelect.tsx
create mode 100644 src/components/VxeTable/src/components/AWeekPicker.tsx
create mode 100644 src/components/VxeTable/src/components/AYearPicker.tsx
create mode 100644 src/components/VxeTable/src/components/common.tsx
create mode 100644 src/components/VxeTable/src/components/index.tsx
create mode 100644 src/components/VxeTable/src/const.ts
create mode 100644 src/components/VxeTable/src/css/common.scss
create mode 100644 src/components/VxeTable/src/css/component.scss
create mode 100644 src/components/VxeTable/src/css/index.scss
create mode 100644 src/components/VxeTable/src/css/toolbar.scss
create mode 100644 src/components/VxeTable/src/css/variable.scss
create mode 100644 src/components/VxeTable/src/emits.ts
create mode 100644 src/components/VxeTable/src/helper.ts
create mode 100644 src/components/VxeTable/src/methods.ts
create mode 100644 src/components/VxeTable/src/props.ts
create mode 100644 src/components/VxeTable/src/setting.ts
create mode 100644 src/components/VxeTable/src/types.ts
create mode 100644 src/components/registerGlobComp.ts
create mode 100644 src/design/ant/btn.less
create mode 100644 src/design/ant/index.less
create mode 100644 src/design/ant/input.less
create mode 100644 src/design/ant/pagination.less
create mode 100644 src/design/ant/popconfirm.less
create mode 100644 src/design/ant/table.less
create mode 100644 src/design/color.less
create mode 100644 src/design/config.less
create mode 100644 src/design/entry.css
create mode 100644 src/design/index.less
create mode 100644 src/design/public.less
create mode 100644 src/design/theme.less
create mode 100644 src/design/transition/base.less
create mode 100644 src/design/transition/fade.less
create mode 100644 src/design/transition/index.less
create mode 100644 src/design/transition/scale.less
create mode 100644 src/design/transition/scroll.less
create mode 100644 src/design/transition/slide.less
create mode 100644 src/design/transition/zoom.less
create mode 100644 src/design/var/breakpoint.less
create mode 100644 src/design/var/easing.less
create mode 100644 src/design/var/index.less
create mode 100644 src/directives/clickOutside.ts
create mode 100644 src/directives/ellipsis.ts
create mode 100644 src/directives/index.ts
create mode 100644 src/directives/loading.ts
create mode 100644 src/directives/permission.ts
create mode 100644 src/directives/repeatClick.ts
create mode 100644 src/directives/ripple/index.less
create mode 100644 src/directives/ripple/index.ts
create mode 100644 src/enums/appEnum.ts
create mode 100644 src/enums/breakpointEnum.ts
create mode 100644 src/enums/cacheEnum.ts
create mode 100644 src/enums/exceptionEnum.ts
create mode 100644 src/enums/httpEnum.ts
create mode 100644 src/enums/menuEnum.ts
create mode 100644 src/enums/pageEnum.ts
create mode 100644 src/enums/roleEnum.ts
create mode 100644 src/enums/sizeEnum.ts
create mode 100644 src/hooks/component/useFormItem.ts
create mode 100644 src/hooks/component/usePageContext.ts
create mode 100644 src/hooks/core/useContext.ts
create mode 100644 src/hooks/event/useBreakpoint.ts
create mode 100644 src/hooks/event/useEventListener.ts
create mode 100644 src/hooks/event/useScroll.ts
create mode 100644 src/hooks/setting/index.ts
create mode 100644 src/hooks/setting/useDarkModeTheme.ts
create mode 100644 src/hooks/setting/useHeaderSetting.ts
create mode 100644 src/hooks/setting/useMenuSetting.ts
create mode 100644 src/hooks/setting/useMultipleTabSetting.ts
create mode 100644 src/hooks/setting/useRootSetting.ts
create mode 100644 src/hooks/setting/useTransitionSetting.ts
create mode 100644 src/hooks/web/useAppInject.ts
create mode 100644 src/hooks/web/useContentHeight.ts
create mode 100644 src/hooks/web/useContextMenu.ts
create mode 100644 src/hooks/web/useDesign.ts
create mode 100644 src/hooks/web/useECharts.ts
create mode 100644 src/hooks/web/useFullContent.ts
create mode 100644 src/hooks/web/useI18n.ts
create mode 100644 src/hooks/web/useLockPage.ts
create mode 100644 src/hooks/web/useMessage.tsx
create mode 100644 src/hooks/web/usePage.ts
create mode 100644 src/hooks/web/usePagination.ts
create mode 100644 src/hooks/web/usePermission.ts
create mode 100644 src/hooks/web/useScript.ts
create mode 100644 src/hooks/web/useSortable.ts
create mode 100644 src/hooks/web/useTabs.ts
create mode 100644 src/hooks/web/useTitle.ts
create mode 100644 src/hooks/web/useWatermark.ts
create mode 100644 src/layouts/default/content/index.vue
create mode 100644 src/layouts/default/content/useContentContext.ts
create mode 100644 src/layouts/default/content/useContentViewHeight.ts
create mode 100644 src/layouts/default/feature/index.vue
create mode 100644 src/layouts/default/footer/index.vue
create mode 100644 src/layouts/default/header/MultipleHeader.vue
create mode 100644 src/layouts/default/header/components/Breadcrumb.vue
create mode 100644 src/layouts/default/header/components/ChangeApi/index.vue
create mode 100644 src/layouts/default/header/components/ErrorAction.vue
create mode 100644 src/layouts/default/header/components/FullScreen.vue
create mode 100644 src/layouts/default/header/components/index.ts
create mode 100644 src/layouts/default/header/components/lock/LockModal.vue
create mode 100644 src/layouts/default/header/components/notify/NoticeList.vue
create mode 100644 src/layouts/default/header/components/notify/data.ts
create mode 100644 src/layouts/default/header/components/notify/index.vue
create mode 100644 src/layouts/default/header/components/user-dropdown/DropMenuItem.vue
create mode 100644 src/layouts/default/header/components/user-dropdown/index.vue
create mode 100644 src/layouts/default/header/index.less
create mode 100644 src/layouts/default/header/index.vue
create mode 100644 src/layouts/default/index.vue
create mode 100644 src/layouts/default/menu/index.vue
create mode 100644 src/layouts/default/menu/useLayoutMenu.ts
create mode 100644 src/layouts/default/setting/SettingDrawer.tsx
create mode 100644 src/layouts/default/setting/components/InputNumberItem.vue
create mode 100644 src/layouts/default/setting/components/SelectItem.vue
create mode 100644 src/layouts/default/setting/components/SettingFooter.vue
create mode 100644 src/layouts/default/setting/components/SwitchItem.vue
create mode 100644 src/layouts/default/setting/components/ThemeColorPicker.vue
create mode 100644 src/layouts/default/setting/components/TypePicker.vue
create mode 100644 src/layouts/default/setting/components/index.ts
create mode 100644 src/layouts/default/setting/enum.ts
create mode 100644 src/layouts/default/setting/handler.ts
create mode 100644 src/layouts/default/setting/index.vue
create mode 100644 src/layouts/default/sider/DragBar.vue
create mode 100644 src/layouts/default/sider/LayoutSider.vue
create mode 100644 src/layouts/default/sider/MixSider.vue
create mode 100644 src/layouts/default/sider/index.vue
create mode 100644 src/layouts/default/sider/useLayoutSider.ts
create mode 100644 src/layouts/default/tabs/components/FoldButton.vue
create mode 100644 src/layouts/default/tabs/components/SettingButton.vue
create mode 100644 src/layouts/default/tabs/components/TabContent.vue
create mode 100644 src/layouts/default/tabs/components/TabRedo.vue
create mode 100644 src/layouts/default/tabs/index.less
create mode 100644 src/layouts/default/tabs/index.vue
create mode 100644 src/layouts/default/tabs/types.ts
create mode 100644 src/layouts/default/tabs/useMultipleTabs.ts
create mode 100644 src/layouts/default/tabs/useTabDropdown.ts
create mode 100644 src/layouts/default/trigger/HeaderTrigger.vue
create mode 100644 src/layouts/default/trigger/SiderTrigger.vue
create mode 100644 src/layouts/default/trigger/index.vue
create mode 100644 src/layouts/iframe/index.vue
create mode 100644 src/layouts/iframe/useFrameKeepAlive.ts
create mode 100644 src/layouts/page/index.vue
create mode 100644 src/layouts/page/transition.ts
create mode 100644 src/locales/helper.ts
create mode 100644 src/locales/lang/en.ts
create mode 100644 src/locales/lang/en/common.json
create mode 100644 src/locales/lang/en/component.json
create mode 100644 src/locales/lang/en/layout.json
create mode 100644 src/locales/lang/en/routes/basic.json
create mode 100644 src/locales/lang/en/routes/dashboard.json
create mode 100644 src/locales/lang/en/routes/demo.json
create mode 100644 src/locales/lang/en/sys.json
create mode 100644 src/locales/lang/zh-CN/antdLocale/DatePicker.json
create mode 100644 src/locales/lang/zh-CN/common.json
create mode 100644 src/locales/lang/zh-CN/component.json
create mode 100644 src/locales/lang/zh-CN/layout.json
create mode 100644 src/locales/lang/zh-CN/routes/basic.json
create mode 100644 src/locales/lang/zh-CN/routes/dashboard.json
create mode 100644 src/locales/lang/zh-CN/routes/demo.json
create mode 100644 src/locales/lang/zh-CN/sys.json
create mode 100644 src/locales/lang/zh_CN.ts
create mode 100644 src/locales/setupI18n.ts
create mode 100644 src/locales/useLocale.ts
create mode 100644 src/logics/error-handle/index.ts
create mode 100644 src/logics/initAppConfig.ts
create mode 100644 src/logics/mitt/routeChange.ts
create mode 100644 src/logics/theme/dark.ts
create mode 100644 src/logics/theme/index.ts
create mode 100644 src/logics/theme/updateBackground.ts
create mode 100644 src/logics/theme/updateColorWeak.ts
create mode 100644 src/logics/theme/updateGrayMode.ts
create mode 100644 src/logics/theme/util.ts
create mode 100644 src/main.ts
create mode 100644 src/router/constant.ts
create mode 100644 src/router/guard/index.ts
create mode 100644 src/router/guard/paramMenuGuard.ts
create mode 100644 src/router/guard/permissionGuard.ts
create mode 100644 src/router/guard/stateGuard.ts
create mode 100644 src/router/helper/menuHelper.ts
create mode 100644 src/router/helper/routeHelper.ts
create mode 100644 src/router/index.ts
create mode 100644 src/router/menus/index.ts
create mode 100644 src/router/routes/basic.ts
create mode 100644 src/router/routes/index.ts
create mode 100644 src/router/routes/mainOut.ts
create mode 100644 src/router/routes/modules/dashboard.ts
create mode 100644 src/router/routes/modules/demo/charts.ts
create mode 100644 src/router/routes/modules/demo/iframe.ts
create mode 100644 src/router/routes/modules/demo/level.ts
create mode 100644 src/router/routes/modules/demo/permission.ts
create mode 100644 src/router/routes/modules/demo/system.ts
create mode 100644 src/router/types.ts
create mode 100644 src/settings/componentSetting.ts
create mode 100644 src/settings/designSetting.ts
create mode 100644 src/settings/encryptionSetting.ts
create mode 100644 src/settings/localeSetting.ts
create mode 100644 src/settings/projectSetting.ts
create mode 100644 src/settings/siteSetting.ts
create mode 100644 src/store/index.ts
create mode 100644 src/store/modules/app.ts
create mode 100644 src/store/modules/errorLog.ts
create mode 100644 src/store/modules/locale.ts
create mode 100644 src/store/modules/lock.ts
create mode 100644 src/store/modules/multipleTab.ts
create mode 100644 src/store/modules/permission.ts
create mode 100644 src/store/modules/tableSetting.ts
create mode 100644 src/store/modules/user.ts
create mode 100644 src/store/plugin/persist.ts
create mode 100644 src/utils/__test__/index.test.ts
create mode 100644 src/utils/auth/index.ts
create mode 100644 src/utils/bem.ts
create mode 100644 src/utils/cache/index.ts
create mode 100644 src/utils/cache/memory.ts
create mode 100644 src/utils/cache/persistent.ts
create mode 100644 src/utils/cache/storageCache.ts
create mode 100644 src/utils/cipher.ts
create mode 100644 src/utils/color.ts
create mode 100644 src/utils/copyTextToClipboard.ts
create mode 100644 src/utils/dateUtil.ts
create mode 100644 src/utils/domUtils.ts
create mode 100644 src/utils/env.ts
create mode 100644 src/utils/event/index.ts
create mode 100644 src/utils/factory/createAsyncComponent.tsx
create mode 100644 src/utils/file/base64Conver.ts
create mode 100644 src/utils/file/download.ts
create mode 100644 src/utils/helper/treeHelper.ts
create mode 100644 src/utils/helper/tsxHelper.tsx
create mode 100644 src/utils/http/axios/Axios.ts
create mode 100644 src/utils/http/axios/axiosCancel.ts
create mode 100644 src/utils/http/axios/axiosRetry.ts
create mode 100644 src/utils/http/axios/axiosTransform.ts
create mode 100644 src/utils/http/axios/checkStatus.ts
create mode 100644 src/utils/http/axios/helper.ts
create mode 100644 src/utils/http/axios/index.ts
create mode 100644 src/utils/index.ts
create mode 100644 src/utils/is.ts
create mode 100644 src/utils/lib/echarts.ts
create mode 100644 src/utils/log.ts
create mode 100644 src/utils/mitt.ts
create mode 100644 src/utils/propTypes.ts
create mode 100644 src/utils/props.ts
create mode 100644 src/utils/types.ts
create mode 100644 src/utils/uuid.ts
create mode 100644 src/views/dashboard/analysis/components/VisitSource.vue
create mode 100644 src/views/dashboard/analysis/index.vue
create mode 100644 src/views/demo/charts/map/Baidu.vue
create mode 100644 src/views/demo/level/Menu111.vue
create mode 100644 src/views/demo/level/Menu12.vue
create mode 100644 src/views/demo/level/Menu2.vue
create mode 100644 src/views/demo/main-out/index.vue
create mode 100644 src/views/demo/page/result/fail/index.vue
create mode 100644 src/views/demo/page/result/success/index.vue
create mode 100644 src/views/demo/permission/CurrentPermissionMode.vue
create mode 100644 src/views/demo/permission/back/Btn.vue
create mode 100644 src/views/demo/permission/back/index.vue
create mode 100644 src/views/demo/permission/front/AuthPageA.vue
create mode 100644 src/views/demo/permission/front/AuthPageB.vue
create mode 100644 src/views/demo/permission/front/Btn.vue
create mode 100644 src/views/demo/permission/front/index.vue
create mode 100644 src/views/demo/system/account/AccountDetail.vue
create mode 100644 src/views/demo/system/account/AccountModal.vue
create mode 100644 src/views/demo/system/account/DeptTree.vue
create mode 100644 src/views/demo/system/account/account.data.ts
create mode 100644 src/views/demo/system/account/index.vue
create mode 100644 src/views/demo/system/dept/DeptModal.vue
create mode 100644 src/views/demo/system/dept/dept.data.ts
create mode 100644 src/views/demo/system/dept/index.vue
create mode 100644 src/views/demo/system/menu/MenuDrawer.vue
create mode 100644 src/views/demo/system/menu/index.vue
create mode 100644 src/views/demo/system/menu/menu.data.ts
create mode 100644 src/views/demo/system/password/index.vue
create mode 100644 src/views/demo/system/password/pwd.data.ts
create mode 100644 src/views/demo/system/role/RoleDrawer.vue
create mode 100644 src/views/demo/system/role/index.vue
create mode 100644 src/views/demo/system/role/role.data.ts
create mode 100644 src/views/demo/system/vxe-account/index.vue
create mode 100644 src/views/demo/system/vxe-account/vxeAccount.data.ts
create mode 100644 src/views/sys/error-log/DetailModal.vue
create mode 100644 src/views/sys/error-log/data.tsx
create mode 100644 src/views/sys/error-log/index.vue
create mode 100644 src/views/sys/exception/Exception.vue
create mode 100644 src/views/sys/exception/index.ts
create mode 100644 src/views/sys/iframe/FrameBlank.vue
create mode 100644 src/views/sys/iframe/index.vue
create mode 100644 src/views/sys/lock/LockPage.vue
create mode 100644 src/views/sys/lock/index.vue
create mode 100644 src/views/sys/lock/useNow.ts
create mode 100644 src/views/sys/login/ForgetPasswordForm.vue
create mode 100644 src/views/sys/login/Login.vue
create mode 100644 src/views/sys/login/LoginForm.vue
create mode 100644 src/views/sys/login/LoginFormTitle.vue
create mode 100644 src/views/sys/login/MobileForm.vue
create mode 100644 src/views/sys/login/QrCodeForm.vue
create mode 100644 src/views/sys/login/RegisterForm.vue
create mode 100644 src/views/sys/login/SessionTimeoutLogin.vue
create mode 100644 src/views/sys/login/useLogin.ts
create mode 100644 src/views/sys/redirect/index.vue
create mode 100644 tsconfig.json
create mode 100644 turbo.json
create mode 100644 types/axios.d.ts
create mode 100644 types/config.d.ts
create mode 100644 types/global.d.ts
create mode 100644 types/index.d.ts
create mode 100644 types/module.d.ts
create mode 100644 types/store.d.ts
create mode 100644 types/utils.d.ts
create mode 100644 types/vue-router.d.ts
create mode 100644 uno.config.ts
create mode 100644 vite.config.ts
create mode 100644 vite.config.ts.timestamp-1705047329667-3f84b63136279.mjs
diff --git a/.browserslistrc b/.browserslistrc
new file mode 100644
index 0000000..dc3bc09
--- /dev/null
+++ b/.browserslistrc
@@ -0,0 +1,4 @@
+> 1%
+last 2 versions
+not dead
+not ie 11
diff --git a/.commitlintrc.cjs b/.commitlintrc.cjs
new file mode 100644
index 0000000..151ead3
--- /dev/null
+++ b/.commitlintrc.cjs
@@ -0,0 +1,107 @@
+const fs = require('fs');
+const path = require('path');
+const { execSync } = require('child_process');
+
+const scopes = fs
+ .readdirSync(path.resolve(__dirname, 'src'), { withFileTypes: true })
+ .filter((dirent) => dirent.isDirectory())
+ .map((dirent) => dirent.name.replace(/s$/, ''));
+
+// precomputed scope
+const scopeComplete = execSync('git status --porcelain || true')
+ .toString()
+ .trim()
+ .split('\n')
+ .find((r) => ~r.indexOf('M src'))
+ ?.replace(/(\/)/g, '%%')
+ ?.match(/src%%((\w|-)*)/)?.[1]
+ ?.replace(/s$/, '');
+
+/** @type {import('cz-git').UserConfig} */
+module.exports = {
+ ignores: [(commit) => commit.includes('init')],
+ extends: ['@commitlint/config-conventional'],
+ rules: {
+ 'body-leading-blank': [2, 'always'],
+ 'footer-leading-blank': [1, 'always'],
+ 'header-max-length': [2, 'always', 108],
+ 'subject-empty': [2, 'never'],
+ 'type-empty': [2, 'never'],
+ 'subject-case': [0],
+ 'type-enum': [
+ 2,
+ 'always',
+ [
+ 'feat',
+ 'fix',
+ 'perf',
+ 'style',
+ 'docs',
+ 'test',
+ 'refactor',
+ 'build',
+ 'ci',
+ 'chore',
+ 'revert',
+ 'wip',
+ 'workflow',
+ 'types',
+ 'release',
+ ],
+ ],
+ },
+ prompt: {
+ /** @use `yarn commit :f` */
+ alias: {
+ f: 'docs: fix typos',
+ r: 'docs: update README',
+ s: 'style: update code format',
+ b: 'build: bump dependencies',
+ c: 'chore: update config',
+ },
+ customScopesAlign: !scopeComplete ? 'top' : 'bottom',
+ defaultScope: scopeComplete,
+ scopes: [...scopes, 'mock'],
+ allowEmptyIssuePrefixs: false,
+ allowCustomIssuePrefixs: false,
+
+ // English
+ typesAppend: [
+ { value: 'wip', name: 'wip: work in process' },
+ { value: 'workflow', name: 'workflow: workflow improvements' },
+ { value: 'types', name: 'types: type definition file changes' },
+ ],
+
+ // 中英文对照版
+ // messages: {
+ // type: '选择你要提交的类型 :',
+ // scope: '选择一个提交范围 (可选):',
+ // customScope: '请输入自定义的提交范围 :',
+ // subject: '填写简短精炼的变更描述 :\n',
+ // body: '填写更加详细的变更描述 (可选)。使用 "|" 换行 :\n',
+ // breaking: '列举非兼容性重大的变更 (可选)。使用 "|" 换行 :\n',
+ // footerPrefixsSelect: '选择关联issue前缀 (可选):',
+ // customFooterPrefixs: '输入自定义issue前缀 :',
+ // footer: '列举关联issue (可选) 例如: #31, #I3244 :\n',
+ // confirmCommit: '是否提交或修改commit ?',
+ // },
+ // types: [
+ // { value: 'feat', name: 'feat: 新增功能' },
+ // { value: 'fix', name: 'fix: 修复缺陷' },
+ // { value: 'docs', name: 'docs: 文档变更' },
+ // { value: 'style', name: 'style: 代码格式' },
+ // { value: 'refactor', name: 'refactor: 代码重构' },
+ // { value: 'perf', name: 'perf: 性能优化' },
+ // { value: 'test', name: 'test: 添加疏漏测试或已有测试改动' },
+ // { value: 'build', name: 'build: 构建流程、外部依赖变更 (如升级 npm 包、修改打包配置等)' },
+ // { value: 'ci', name: 'ci: 修改 CI 配置、脚本' },
+ // { value: 'revert', name: 'revert: 回滚 commit' },
+ // { value: 'chore', name: 'chore: 对构建过程或辅助工具和库的更改 (不影响源文件、测试用例)' },
+ // { value: 'wip', name: 'wip: 正在开发中' },
+ // { value: 'workflow', name: 'workflow: 工作流程改进' },
+ // { value: 'types', name: 'types: 类型定义文件修改' },
+ // ],
+ // emptyScopesAlias: 'empty: 不填写',
+ // customScopesAlias: 'custom: 自定义',
+ },
+};
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..8617652
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,3 @@
+node_modules/
+dist/
+.vscode/
diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..dccf841
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,19 @@
+root = true
+
+[*]
+charset=utf-8
+end_of_line=lf
+insert_final_newline=true
+indent_style=space
+indent_size=2
+max_line_length = 100
+
+[*.{yml,yaml,json}]
+indent_style = space
+indent_size = 2
+
+[*.md]
+trim_trailing_whitespace = false
+
+[Makefile]
+indent_style = tab
diff --git a/.env b/.env
new file mode 100644
index 0000000..c6c030e
--- /dev/null
+++ b/.env
@@ -0,0 +1,2 @@
+# 标题
+VITE_GLOB_APP_TITLE = 费县智慧林业防灭火平台
diff --git a/.env.analyze b/.env.analyze
new file mode 100644
index 0000000..1ff51cf
--- /dev/null
+++ b/.env.analyze
@@ -0,0 +1,23 @@
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Whether to enable gzip or brotli compression
+# Optional: gzip | brotli | none
+# If you need multiple forms, you can use `,` to separate
+VITE_BUILD_COMPRESS = 'none'
+
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/basic-api
+
+# File upload address, optional
+# It can be forwarded by nginx or write the actual address directly
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
+
+VITE_ENABLE_ANALYZE = true
diff --git a/.env.development b/.env.development
new file mode 100644
index 0000000..ed7a6b2
--- /dev/null
+++ b/.env.development
@@ -0,0 +1,14 @@
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/basic-api
+
+# File upload address, optional
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
diff --git a/.env.production b/.env.production
new file mode 100644
index 0000000..c672de6
--- /dev/null
+++ b/.env.production
@@ -0,0 +1,21 @@
+# Whether to open mock
+VITE_USE_MOCK = true
+
+# public path
+VITE_PUBLIC_PATH = /
+
+# Whether to enable gzip or brotli compression
+# Optional: gzip | brotli | none
+# If you need multiple forms, you can use `,` to separate
+VITE_BUILD_COMPRESS = 'none'
+
+
+# Basic interface address SPA
+VITE_GLOB_API_URL=/basic-api
+
+# File upload address, optional
+# It can be forwarded by nginx or write the actual address directly
+VITE_GLOB_UPLOAD_URL=/upload
+
+# Interface prefix
+VITE_GLOB_API_URL_PREFIX=
diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..67ac532
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,16 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.vscode
+.idea
+dist
+/public
+/docs
+.husky
+.local
+/bin
+Dockerfile
+package.json
diff --git a/.eslintrc.cjs b/.eslintrc.cjs
new file mode 100644
index 0000000..98fc3ef
--- /dev/null
+++ b/.eslintrc.cjs
@@ -0,0 +1,7 @@
+module.exports = {
+ root: true,
+ extends: ['@vben'],
+ rules: {
+ 'no-undef': 'off',
+ },
+};
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..d4e5bd3
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,11 @@
+# https://docs.github.com/cn/get-started/getting-started-with-git/configuring-git-to-handle-line-endings
+
+# Automatically normalize line endings (to LF) for all text-based files.
+* text=auto eol=lf
+
+# Declare files that will always have CRLF line endings on checkout.
+*.{cmd,[cC][mM][dD]} text eol=crlf
+*.{bat,[bB][aA][tT]} text eol=crlf
+
+# Denote all files that are truly binary and should not be modified.
+*.{ico,png,jpg,jpeg,gif,webp,svg,woff,woff2} binary
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..aa15905
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+node_modules
+.DS_Store
+dist
+.cache
+.turbo
+
+tests/server/static
+tests/server/static/upload
+
+.local
+# local env files
+.env.local
+.env.*.local
+.eslintcache
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+# .vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+package-lock.json
+pnpm-lock.yaml
+
+.history
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 0000000..866381f
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,6 @@
+ports:
+ - port: 3344
+ onOpen: open-preview
+tasks:
+ - init: pnpm install
+ command: pnpm run dev
diff --git a/.npmrc b/.npmrc
new file mode 100644
index 0000000..29f68cf
--- /dev/null
+++ b/.npmrc
@@ -0,0 +1,7 @@
+public-hoist-pattern[]=husky
+public-hoist-pattern[]=*eslint*
+public-hoist-pattern[]=*prettier*
+public-hoist-pattern[]=lint-staged
+public-hoist-pattern[]=*stylelint*
+public-hoist-pattern[]=@commitlint/cli
+public-hoist-pattern[]=@vben/eslint-config
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..24531e6
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,12 @@
+dist
+.local
+.output.js
+node_modules
+
+**/*.svg
+**/*.sh
+
+public
+.npmrc
+
+*-lock.yaml
diff --git a/.prettierrc.cjs b/.prettierrc.cjs
new file mode 100644
index 0000000..4a24e88
--- /dev/null
+++ b/.prettierrc.cjs
@@ -0,0 +1,19 @@
+module.exports = {
+ printWidth: 100,
+ semi: true,
+ vueIndentScriptAndStyle: true,
+ singleQuote: true,
+ trailingComma: 'all',
+ proseWrap: 'never',
+ htmlWhitespaceSensitivity: 'strict',
+ endOfLine: 'auto',
+ plugins: ['prettier-plugin-packagejson'],
+ overrides: [
+ {
+ files: '.*rc',
+ options: {
+ parser: 'json',
+ },
+ },
+ ],
+};
diff --git a/.stylelintignore b/.stylelintignore
new file mode 100644
index 0000000..6cd69e0
--- /dev/null
+++ b/.stylelintignore
@@ -0,0 +1,2 @@
+dist
+public
diff --git a/.stylelintrc.cjs b/.stylelintrc.cjs
new file mode 100644
index 0000000..65320e7
--- /dev/null
+++ b/.stylelintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/stylelint-config'],
+};
diff --git a/.vscode/extensions.json b/.vscode/extensions.json
new file mode 100644
index 0000000..3f7ad41
--- /dev/null
+++ b/.vscode/extensions.json
@@ -0,0 +1,14 @@
+{
+ "recommendations": [
+ "vue.volar",
+ "dbaeumer.vscode-eslint",
+ "stylelint.vscode-stylelint",
+ "esbenp.prettier-vscode",
+ "mrmlnc.vscode-less",
+ "lokalise.i18n-ally",
+ "antfu.iconify",
+ "antfu.unocss",
+ "mikestead.dotenv",
+ "vue.vscode-typescript-vue-plugin"
+ ]
+}
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..720a6df
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,13 @@
+{
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "type": "chrome",
+ "request": "launch",
+ "name": "Launch Chrome",
+ "url": "http://localhost:3100",
+ "webRoot": "${workspaceFolder}/src",
+ "sourceMaps": true
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..fcbab54
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,188 @@
+{
+ "typescript.tsdk": "./node_modules/typescript/lib",
+ "volar.tsPlugin": true,
+ "volar.tsPluginStatus": false,
+ "npm.packageManager": "pnpm",
+ "editor.tabSize": 2,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "files.eol": "\n",
+ "search.exclude": {
+ "**/node_modules": true,
+ "**/*.log": true,
+ "**/*.log*": true,
+ "**/bower_components": true,
+ "**/dist": true,
+ "**/elehukouben": true,
+ "**/.git": true,
+ "**/.gitignore": true,
+ "**/.svn": true,
+ "**/.DS_Store": true,
+ "**/.idea": true,
+ "**/.vscode": false,
+ "**/yarn.lock": true,
+ "**/tmp": true,
+ "out": true,
+ "dist": true,
+ "node_modules": true,
+ "CHANGELOG.md": true,
+ "examples": true,
+ "res": true,
+ "screenshots": true,
+ "yarn-error.log": true,
+ "**/.yarn": true
+ },
+ "files.exclude": {
+ "**/.cache": true,
+ "**/.editorconfig": true,
+ "**/.eslintcache": true,
+ "**/bower_components": true,
+ "**/.idea": true,
+ "**/tmp": true,
+ "**/.git": true,
+ "**/.svn": true,
+ "**/.hg": true,
+ "**/CVS": true,
+ "**/.DS_Store": true
+ },
+ "files.watcherExclude": {
+ "**/.git/objects/**": true,
+ "**/.git/subtree-cache/**": true,
+ "**/.vscode/**": true,
+ "**/node_modules/**": true,
+ "**/tmp/**": true,
+ "**/bower_components/**": true,
+ "**/dist/**": true,
+ "**/yarn.lock": true
+ },
+ "stylelint.enable": true,
+ "stylelint.validate": [
+ "css",
+ "less",
+ "postcss",
+ "scss",
+ "vue",
+ "sass"
+ ],
+ "path-intellisense.mappings": {
+ "@/": "${workspaceRoot}/src"
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[typescriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[html]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[css]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[less]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[scss]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[markdown]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.fixAll.stylelint": "explicit"
+ },
+ "[vue]": {
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit",
+ "source.fixAll.stylelint": "explicit"
+ },
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "i18n-ally.localesPaths": [
+ "src/locales/lang"
+ ],
+ "i18n-ally.keystyle": "nested",
+ "i18n-ally.sortKeys": true,
+ "i18n-ally.namespace": true,
+ "i18n-ally.pathMatcher": "{locale}/{namespaces}.{ext}",
+ "i18n-ally.enabledParsers": [
+ "json"
+ ],
+ "i18n-ally.sourceLanguage": "en",
+ "i18n-ally.displayLanguage": "zh-CN",
+ "i18n-ally.enabledFrameworks": [
+ "vue",
+ "react"
+ ],
+ "cSpell.words": [
+ "antd",
+ "antv",
+ "brotli",
+ "browserslist",
+ "codemirror",
+ "commitlint",
+ "cropperjs",
+ "echarts",
+ "esnext",
+ "esno",
+ "iconify",
+ "INTLIFY",
+ "lintstagedrc",
+ "logicflow",
+ "mockjs",
+ "nprogress",
+ "pinia",
+ "pnpm",
+ "qrcode",
+ "sider",
+ "sortablejs",
+ "stylelint",
+ "tailwindcss",
+ "tinymce",
+ "unocss",
+ "unref",
+ "vben",
+ "vditor",
+ "Vite",
+ "vitejs",
+ "vueuse",
+ "zxcvbn"
+ ],
+ "vetur.format.scriptInitialIndent": true,
+ "vetur.format.styleInitialIndent": true,
+ "vetur.validation.script": false,
+ "MicroPython.executeButton": [
+ {
+ "text": "▶",
+ "tooltip": "运行",
+ "alignment": "left",
+ "command": "extension.executeFile",
+ "priority": 3.5
+ }
+ ],
+ "MicroPython.syncButton": [
+ {
+ "text": "$(sync)",
+ "tooltip": "同步",
+ "alignment": "left",
+ "command": "extension.execute",
+ "priority": 4
+ }
+ ],
+ // 控制相关文件嵌套展示
+ "explorer.fileNesting.enabled": true,
+ "explorer.fileNesting.expand": false,
+ "explorer.fileNesting.patterns": {
+ "*.ts": "$(capture).test.ts, $(capture).test.tsx",
+ "*.tsx": "$(capture).test.ts, $(capture).test.tsx",
+ "*.env": "$(capture).env.*",
+ "CHANGELOG.md": "CHANGELOG*",
+ "package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,README*,.npmrc,.browserslistrc",
+ ".eslintrc.cjs": ".eslintignore,.prettierignore,.stylelintignore,.commitlintrc.*,.prettierrc.*,.stylelintrc.*"
+ },
+ "terminal.integrated.scrollback": 10000,
+ "nuxt.isNuxtApp": false
+}
diff --git a/CNAME b/CNAME
new file mode 100644
index 0000000..3436928
--- /dev/null
+++ b/CNAME
@@ -0,0 +1 @@
+vben.vvbin.cn
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..e69de29
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..16722a6
--- /dev/null
+++ b/index.html
@@ -0,0 +1,158 @@
+
+
+
+
+
+
+
+ <%= VITE_GLOB_APP_TITLE %>
+
+
+
+
+
+
+
+

+
+
+
+
<%= VITE_GLOB_APP_TITLE %>
+
+
+
+
+
+
diff --git a/internal/eslint-config/.eslintignore b/internal/eslint-config/.eslintignore
new file mode 100644
index 0000000..cef44b3
--- /dev/null
+++ b/internal/eslint-config/.eslintignore
@@ -0,0 +1,9 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.turbo
+dist
+package.json
diff --git a/internal/eslint-config/.eslintrc.cjs b/internal/eslint-config/.eslintrc.cjs
new file mode 100644
index 0000000..cd27a19
--- /dev/null
+++ b/internal/eslint-config/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/eslint-config/strict'],
+};
diff --git a/internal/eslint-config/build.config.ts b/internal/eslint-config/build.config.ts
new file mode 100644
index 0000000..08301e5
--- /dev/null
+++ b/internal/eslint-config/build.config.ts
@@ -0,0 +1,10 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+ clean: true,
+ entries: ['src/index', 'src/strict'],
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+});
diff --git a/internal/eslint-config/package.json b/internal/eslint-config/package.json
new file mode 100644
index 0000000..1288394
--- /dev/null
+++ b/internal/eslint-config/package.json
@@ -0,0 +1,50 @@
+{
+ "name": "@vben/eslint-config",
+ "version": "1.0.0",
+ "private": true,
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "internal/eslint-config"
+ },
+ "license": "MIT",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ },
+ "./strict": {
+ "types": "./dist/strict.d.ts",
+ "import": "./dist/strict.mjs",
+ "require": "./dist/strict.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "clean": "pnpm rimraf .turbo node_modules dist",
+ "lint": "pnpm eslint .",
+ "stub": "pnpm unbuild --stub"
+ },
+ "devDependencies": {
+ "@typescript-eslint/eslint-plugin": "^6.17.0",
+ "@typescript-eslint/parser": "^6.17.0",
+ "eslint": "^8.56.0",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-import": "^2.29.1",
+ "eslint-plugin-prettier": "^5.1.2",
+ "eslint-plugin-simple-import-sort": "^10.0.0",
+ "eslint-plugin-vue": "^9.19.2",
+ "vue-eslint-parser": "^9.3.2"
+ }
+}
diff --git a/internal/eslint-config/src/index.ts b/internal/eslint-config/src/index.ts
new file mode 100644
index 0000000..1138bb3
--- /dev/null
+++ b/internal/eslint-config/src/index.ts
@@ -0,0 +1,91 @@
+export default {
+ env: {
+ browser: true,
+ node: true,
+ es6: true,
+ },
+ parser: 'vue-eslint-parser',
+ parserOptions: {
+ parser: '@typescript-eslint/parser',
+ ecmaVersion: 2020,
+ sourceType: 'module',
+ jsxPragma: 'React',
+ ecmaFeatures: {
+ jsx: true,
+ },
+ project: './tsconfig.*?.json',
+ createDefaultProgram: false,
+ extraFileExtensions: ['.vue'],
+ },
+ plugins: ['vue', '@typescript-eslint', 'import'],
+ extends: [
+ 'eslint:recommended',
+ 'plugin:vue/vue3-recommended',
+ 'plugin:@typescript-eslint/recommended',
+ 'plugin:prettier/recommended',
+ ],
+ rules: {
+ 'no-unused-vars': 'off',
+ 'no-case-declarations': 'off',
+ 'no-use-before-define': 'off',
+ 'space-before-function-paren': 'off',
+
+ 'import/first': 'error',
+ 'import/newline-after-import': 'error',
+ 'import/no-duplicates': 'error',
+
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^_',
+ varsIgnorePattern: '^_',
+ },
+ ],
+ '@typescript-eslint/ban-ts-ignore': 'off',
+ '@typescript-eslint/ban-ts-comment': 'off',
+ '@typescript-eslint/ban-types': 'off',
+ '@typescript-eslint/explicit-function-return-type': 'off',
+ '@typescript-eslint/no-explicit-any': 'off',
+ '@typescript-eslint/no-var-requires': 'off',
+ '@typescript-eslint/no-empty-function': 'off',
+ '@typescript-eslint/no-use-before-define': 'off',
+ '@typescript-eslint/no-non-null-assertion': 'off',
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
+ 'vue/script-setup-uses-vars': 'error',
+ 'vue/no-reserved-component-names': 'off',
+ 'vue/custom-event-name-casing': 'off',
+ 'vue/attributes-order': 'off',
+ 'vue/one-component-per-file': 'off',
+ 'vue/html-closing-bracket-newline': 'off',
+ 'vue/max-attributes-per-line': 'off',
+ 'vue/multiline-html-element-content-newline': 'off',
+ 'vue/singleline-html-element-content-newline': 'off',
+ 'vue/attribute-hyphenation': 'off',
+ 'vue/require-default-prop': 'off',
+ 'vue/require-explicit-emits': 'off',
+ 'vue/html-self-closing': [
+ 'error',
+ {
+ html: {
+ void: 'always',
+ normal: 'never',
+ component: 'always',
+ },
+ svg: 'always',
+ math: 'always',
+ },
+ ],
+ 'vue/multi-word-component-names': 'off',
+ // 'sort-imports': [
+ // 'error',
+ // {
+ // ignoreCase: true,
+ // ignoreDeclarationSort: false,
+ // ignoreMemberSort: false,
+ // memberSyntaxSortOrder: ['none', 'all', 'multiple', 'single'],
+ // allowSeparatedGroups: false,
+ // },
+ // ],
+ },
+ globals: { defineOptions: 'readonly' },
+};
diff --git a/internal/eslint-config/src/strict.ts b/internal/eslint-config/src/strict.ts
new file mode 100644
index 0000000..5dbf5b7
--- /dev/null
+++ b/internal/eslint-config/src/strict.ts
@@ -0,0 +1,57 @@
+export default {
+ extends: ['@vben'],
+ plugins: ['simple-import-sort'],
+ rules: {
+ 'simple-import-sort/imports': 'error',
+ 'simple-import-sort/exports': 'error',
+
+ '@typescript-eslint/ban-ts-comment': [
+ 'error',
+ {
+ 'ts-expect-error': 'allow-with-description',
+ 'ts-ignore': 'allow-with-description',
+ 'ts-nocheck': 'allow-with-description',
+ 'ts-check': false,
+ },
+ ],
+
+ /**
+ * 【强制】关键字前后有一个空格
+ * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/keyword-spacing.md
+ */
+ 'keyword-spacing': 'off',
+ '@typescript-eslint/keyword-spacing': [
+ 'error',
+ {
+ before: true,
+ after: true,
+ overrides: {
+ return: { after: true },
+ throw: { after: true },
+ case: { after: true },
+ },
+ },
+ ],
+
+ /**
+ * 禁止出现空函数,普通函数(非 async/await/generator)、箭头函数、类上的方法除外
+ * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/no-empty-function.md
+ */
+ 'no-empty-function': 'off',
+ '@typescript-eslint/no-empty-function': [
+ 'error',
+ {
+ allow: ['arrowFunctions', 'functions', 'methods'],
+ },
+ ],
+
+ /**
+ * 优先使用 interface 而不是 type 定义对象类型
+ * @link https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/docs/rules/consistent-type-definitions.md
+ */
+ '@typescript-eslint/consistent-type-definitions': ['warn', 'interface'],
+
+ 'vue/attributes-order': 'error',
+ 'vue/require-default-prop': 'error',
+ },
+};
diff --git a/internal/eslint-config/tsconfig.json b/internal/eslint-config/tsconfig.json
new file mode 100644
index 0000000..cd27063
--- /dev/null
+++ b/internal/eslint-config/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/ts-config/node.json",
+ "include": ["src"]
+}
diff --git a/internal/stylelint-config/.eslintignore b/internal/stylelint-config/.eslintignore
new file mode 100644
index 0000000..cef44b3
--- /dev/null
+++ b/internal/stylelint-config/.eslintignore
@@ -0,0 +1,9 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.turbo
+dist
+package.json
diff --git a/internal/stylelint-config/.eslintrc.cjs b/internal/stylelint-config/.eslintrc.cjs
new file mode 100644
index 0000000..cd27a19
--- /dev/null
+++ b/internal/stylelint-config/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/eslint-config/strict'],
+};
diff --git a/internal/stylelint-config/build.config.ts b/internal/stylelint-config/build.config.ts
new file mode 100644
index 0000000..20c8b54
--- /dev/null
+++ b/internal/stylelint-config/build.config.ts
@@ -0,0 +1,10 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+ clean: true,
+ entries: ['src/index'],
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+});
diff --git a/internal/stylelint-config/package.json b/internal/stylelint-config/package.json
new file mode 100644
index 0000000..6a3ba22
--- /dev/null
+++ b/internal/stylelint-config/package.json
@@ -0,0 +1,49 @@
+{
+ "name": "@vben/stylelint-config",
+ "version": "1.0.0",
+ "private": true,
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "internal/stylelint-config"
+ },
+ "license": "MIT",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "clean": "pnpm rimraf .turbo node_modules dist",
+ "lint": "pnpm eslint .",
+ "stub": "pnpm unbuild --stub"
+ },
+ "devDependencies": {
+ "postcss": "^8.4.33",
+ "postcss-html": "^1.5.0",
+ "postcss-less": "^6.0.0",
+ "postcss-scss": "^4.0.9",
+ "prettier": "^3.1.1",
+ "stylelint": "^16.1.0",
+ "stylelint-config-property-sort-order-smacss": "^10.0.0",
+ "stylelint-config-recommended-scss": "^14.0.0",
+ "stylelint-config-recommended-vue": "^1.5.0",
+ "stylelint-config-standard": "^36.0.0",
+ "stylelint-config-standard-scss": "^13.0.0",
+ "stylelint-order": "^6.0.4",
+ "stylelint-prettier": "^5.0.0"
+ }
+}
diff --git a/internal/stylelint-config/src/index.ts b/internal/stylelint-config/src/index.ts
new file mode 100644
index 0000000..8b15456
--- /dev/null
+++ b/internal/stylelint-config/src/index.ts
@@ -0,0 +1,92 @@
+export default {
+ extends: ['stylelint-config-standard', 'stylelint-config-property-sort-order-smacss'],
+ plugins: ['stylelint-order', 'stylelint-prettier'],
+ // customSyntax: 'postcss-html',
+ overrides: [
+ {
+ files: ['**/*.(css|html|vue)'],
+ customSyntax: 'postcss-html',
+ },
+ {
+ files: ['*.less', '**/*.less'],
+ customSyntax: 'postcss-less',
+ extends: ['stylelint-config-standard', 'stylelint-config-recommended-vue'],
+ },
+ {
+ files: ['*.scss', '**/*.scss'],
+ customSyntax: 'postcss-scss',
+ extends: ['stylelint-config-standard-scss', 'stylelint-config-recommended-vue/scss'],
+ rule: {
+ 'scss/percent-placeholder-pattern': null,
+ },
+ },
+ ],
+ rules: {
+ 'prettier/prettier': true,
+ 'media-feature-range-notation': null,
+ 'selector-not-notation': null,
+ 'import-notation': null,
+ 'function-no-unknown': null,
+ 'selector-class-pattern': null,
+ 'selector-pseudo-class-no-unknown': [
+ true,
+ {
+ ignorePseudoClasses: ['global', 'deep'],
+ },
+ ],
+ 'selector-pseudo-element-no-unknown': [
+ true,
+ {
+ ignorePseudoElements: ['v-deep'],
+ },
+ ],
+ 'at-rule-no-unknown': [
+ true,
+ {
+ ignoreAtRules: [
+ 'tailwind',
+ 'apply',
+ 'variants',
+ 'responsive',
+ 'screen',
+ 'function',
+ 'if',
+ 'each',
+ 'include',
+ 'mixin',
+ 'extend',
+ ],
+ },
+ ],
+ 'no-empty-source': null,
+ 'named-grid-areas-no-invalid': null,
+ 'no-descending-specificity': null,
+ 'font-family-no-missing-generic-family-keyword': null,
+ 'rule-empty-line-before': [
+ 'always',
+ {
+ ignore: ['after-comment', 'first-nested'],
+ },
+ ],
+ 'unit-no-unknown': [true, { ignoreUnits: ['rpx'] }],
+ 'order/order': [
+ [
+ 'dollar-variables',
+ 'custom-properties',
+ 'at-rules',
+ 'declarations',
+ {
+ type: 'at-rule',
+ name: 'supports',
+ },
+ {
+ type: 'at-rule',
+ name: 'media',
+ },
+ 'rules',
+ ],
+ { severity: 'error' },
+ ],
+ },
+ ignoreFiles: ['**/*.js', '**/*.jsx', '**/*.tsx', '**/*.ts'],
+};
diff --git a/internal/stylelint-config/tsconfig.json b/internal/stylelint-config/tsconfig.json
new file mode 100644
index 0000000..cd27063
--- /dev/null
+++ b/internal/stylelint-config/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/ts-config/node.json",
+ "include": ["src"]
+}
diff --git a/internal/ts-config/base.json b/internal/ts-config/base.json
new file mode 100644
index 0000000..8b90054
--- /dev/null
+++ b/internal/ts-config/base.json
@@ -0,0 +1,27 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Base",
+ "compilerOptions": {
+ "target": "ESNext",
+ "module": "ESNext",
+ "moduleResolution": "bundler",
+ "strict": true,
+ "declaration": true,
+ "noImplicitOverride": true,
+ "noUnusedLocals": true,
+ "esModuleInterop": true,
+ "useUnknownInCatchVariables": false,
+ "composite": false,
+ "declarationMap": false,
+ "forceConsistentCasingInFileNames": true,
+ "inlineSources": false,
+ "isolatedModules": true,
+ "skipLibCheck": true,
+ "noUnusedParameters": false,
+ "preserveWatchOutput": true,
+ "experimentalDecorators": true,
+ "resolveJsonModule": true,
+ "removeComments": true
+ },
+ "exclude": ["**/node_modules/**", "**/dist/**"]
+}
diff --git a/internal/ts-config/node-server.json b/internal/ts-config/node-server.json
new file mode 100644
index 0000000..e27374a
--- /dev/null
+++ b/internal/ts-config/node-server.json
@@ -0,0 +1,18 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Node Server Config",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "declaration": false,
+ "removeComments": true,
+ "emitDecoratorMetadata": true,
+ "experimentalDecorators": true,
+ "target": "es6",
+ "sourceMap": false,
+ "esModuleInterop": true,
+ "outDir": "./dist",
+ "baseUrl": "./"
+ },
+ "exclude": ["node_modules"]
+}
diff --git a/internal/ts-config/node.json b/internal/ts-config/node.json
new file mode 100644
index 0000000..cdd365f
--- /dev/null
+++ b/internal/ts-config/node.json
@@ -0,0 +1,12 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Node Config",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "lib": ["ESNext"],
+ "noImplicitAny": true,
+ "sourceMap": true,
+ "noEmit": true,
+ "baseUrl": "./"
+ }
+}
diff --git a/internal/ts-config/package.json b/internal/ts-config/package.json
new file mode 100644
index 0000000..9a41a22
--- /dev/null
+++ b/internal/ts-config/package.json
@@ -0,0 +1,26 @@
+{
+ "name": "@vben/ts-config",
+ "version": "1.0.0",
+ "private": true,
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "internal/ts-config"
+ },
+ "license": "MIT",
+ "type": "module",
+ "files": [
+ "base.json",
+ "node.json",
+ "vue-app.json",
+ "node-server.json"
+ ],
+ "dependencies": {
+ "@types/node": "^20.10.6",
+ "vite": "^5.0.10"
+ }
+}
diff --git a/internal/ts-config/vue-app.json b/internal/ts-config/vue-app.json
new file mode 100644
index 0000000..02f3fd2
--- /dev/null
+++ b/internal/ts-config/vue-app.json
@@ -0,0 +1,10 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "display": "Vue Application",
+ "extends": "./base.json",
+ "compilerOptions": {
+ "jsx": "preserve",
+ "lib": ["ESNext", "DOM"],
+ "noImplicitAny": false
+ }
+}
diff --git a/internal/vite-config/.eslintignore b/internal/vite-config/.eslintignore
new file mode 100644
index 0000000..cef44b3
--- /dev/null
+++ b/internal/vite-config/.eslintignore
@@ -0,0 +1,9 @@
+
+*.sh
+node_modules
+*.md
+*.woff
+*.ttf
+.turbo
+dist
+package.json
diff --git a/internal/vite-config/.eslintrc.cjs b/internal/vite-config/.eslintrc.cjs
new file mode 100644
index 0000000..cd27a19
--- /dev/null
+++ b/internal/vite-config/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/eslint-config/strict'],
+};
diff --git a/internal/vite-config/build.config.ts b/internal/vite-config/build.config.ts
new file mode 100644
index 0000000..20c8b54
--- /dev/null
+++ b/internal/vite-config/build.config.ts
@@ -0,0 +1,10 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+ clean: true,
+ entries: ['src/index'],
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+});
diff --git a/internal/vite-config/package.json b/internal/vite-config/package.json
new file mode 100644
index 0000000..35ae44b
--- /dev/null
+++ b/internal/vite-config/package.json
@@ -0,0 +1,59 @@
+{
+ "name": "@vben/vite-config",
+ "version": "1.0.0",
+ "private": true,
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "internal/vite-config"
+ },
+ "license": "MIT",
+ "type": "module",
+ "exports": {
+ ".": {
+ "types": "./dist/index.d.ts",
+ "import": "./dist/index.mjs",
+ "require": "./dist/index.cjs"
+ }
+ },
+ "main": "./dist/index.cjs",
+ "module": "./dist/index.mjs",
+ "types": "./dist/index.d.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "clean": "pnpm rimraf .turbo node_modules dist",
+ "lint": "pnpm eslint .",
+ "stub": "pnpm unbuild --stub"
+ },
+ "dependencies": {
+ "@ant-design/colors": "^7.0.2",
+ "vite": "^5.0.10"
+ },
+ "devDependencies": {
+ "@types/fs-extra": "^11.0.4",
+ "@vitejs/plugin-vue": "^5.0.2",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "ant-design-vue": "^4.0.8",
+ "dayjs": "^1.11.10",
+ "dotenv": "^16.3.1",
+ "fs-extra": "^11.2.0",
+ "less": "^4.2.0",
+ "picocolors": "^1.0.0",
+ "pkg-types": "^1.0.3",
+ "rollup-plugin-visualizer": "^5.12.0",
+ "sass": "^1.69.7",
+ "unocss": "0.58.3",
+ "vite-plugin-compression": "^0.5.1",
+ "vite-plugin-dts": "^3.7.0",
+ "vite-plugin-html": "^3.2.1",
+ "vite-plugin-mock": "^2.9.6",
+ "vite-plugin-purge-icons": "^0.10.0",
+ "vite-plugin-svg-icons": "^2.0.1"
+ }
+}
diff --git a/internal/vite-config/src/config/application.ts b/internal/vite-config/src/config/application.ts
new file mode 100644
index 0000000..bd6d6ca
--- /dev/null
+++ b/internal/vite-config/src/config/application.ts
@@ -0,0 +1,109 @@
+import { resolve } from 'node:path';
+
+import dayjs from 'dayjs';
+import { readPackageJSON } from 'pkg-types';
+import { defineConfig, loadEnv, mergeConfig, type UserConfig } from 'vite';
+
+import { createPlugins } from '../plugins';
+import { generateModifyVars } from '../utils/modifyVars';
+import { commonConfig } from './common';
+
+interface DefineOptions {
+ overrides?: UserConfig;
+ options?: {
+ //
+ };
+}
+
+function defineApplicationConfig(defineOptions: DefineOptions = {}) {
+ const { overrides = {} } = defineOptions;
+
+ return defineConfig(async ({ command, mode }) => {
+ const root = process.cwd();
+ const isBuild = command === 'build';
+ const { VITE_PUBLIC_PATH, VITE_USE_MOCK, VITE_BUILD_COMPRESS, VITE_ENABLE_ANALYZE } = loadEnv(
+ mode,
+ root,
+ );
+
+ const defineData = await createDefineData(root);
+ const plugins = await createPlugins({
+ isBuild,
+ root,
+ enableAnalyze: VITE_ENABLE_ANALYZE === 'true',
+ enableMock: VITE_USE_MOCK === 'true',
+ compress: VITE_BUILD_COMPRESS,
+ });
+
+ const pathResolve = (pathname: string) => resolve(root, '.', pathname);
+ const timestamp = new Date().getTime();
+ const applicationConfig: UserConfig = {
+ base: VITE_PUBLIC_PATH,
+ resolve: {
+ alias: [
+ {
+ find: 'vue-i18n',
+ replacement: 'vue-i18n/dist/vue-i18n.cjs.js',
+ },
+ // @/xxxx => src/xxxx
+ {
+ find: /@\//,
+ replacement: pathResolve('src') + '/',
+ },
+ // #/xxxx => types/xxxx
+ {
+ find: /#\//,
+ replacement: pathResolve('types') + '/',
+ },
+ ],
+ },
+ define: defineData,
+ build: {
+ target: 'es2015',
+ cssTarget: 'chrome80',
+ rollupOptions: {
+ output: {
+ // 入口文件名
+ entryFileNames: `assets/entry/[name]-[hash]-${timestamp}.js`,
+ manualChunks: {
+ vue: ['vue', 'pinia', 'vue-router'],
+ antd: ['ant-design-vue', '@ant-design/icons-vue'],
+ },
+ },
+ },
+ },
+ css: {
+ preprocessorOptions: {
+ less: {
+ modifyVars: generateModifyVars(),
+ javascriptEnabled: true,
+ },
+ },
+ },
+ plugins,
+ };
+
+ const mergedConfig = mergeConfig(commonConfig(mode), applicationConfig);
+
+ return mergeConfig(mergedConfig, overrides);
+ });
+}
+
+async function createDefineData(root: string) {
+ try {
+ const pkgJson = await readPackageJSON(root);
+ const { dependencies, devDependencies, name, version } = pkgJson;
+
+ const __APP_INFO__ = {
+ pkg: { dependencies, devDependencies, name, version },
+ lastBuildTime: dayjs().format('YYYY-MM-DD HH:mm:ss'),
+ };
+ return {
+ __APP_INFO__: JSON.stringify(__APP_INFO__),
+ };
+ } catch (error) {
+ return {};
+ }
+}
+
+export { defineApplicationConfig };
diff --git a/internal/vite-config/src/config/common.ts b/internal/vite-config/src/config/common.ts
new file mode 100644
index 0000000..559896e
--- /dev/null
+++ b/internal/vite-config/src/config/common.ts
@@ -0,0 +1,22 @@
+import UnoCSS from 'unocss/vite';
+import { type UserConfig } from 'vite';
+
+const commonConfig: (mode: string) => UserConfig = (mode) => ({
+ server: {
+ host: true,
+ },
+ esbuild: {
+ drop: mode === 'production' ? ['console', 'debugger'] : [],
+ },
+ build: {
+ reportCompressedSize: false,
+ chunkSizeWarningLimit: 1500,
+ rollupOptions: {
+ // TODO: Prevent memory overflow
+ maxParallelFileOps: 3,
+ },
+ },
+ plugins: [UnoCSS()],
+});
+
+export { commonConfig };
diff --git a/internal/vite-config/src/config/package.ts b/internal/vite-config/src/config/package.ts
new file mode 100644
index 0000000..ab83852
--- /dev/null
+++ b/internal/vite-config/src/config/package.ts
@@ -0,0 +1,42 @@
+import { readPackageJSON } from 'pkg-types';
+import { defineConfig, mergeConfig, type UserConfig } from 'vite';
+import dts from 'vite-plugin-dts';
+
+import { commonConfig } from './common';
+
+interface DefineOptions {
+ overrides?: UserConfig;
+ options?: {
+ //
+ };
+}
+
+function definePackageConfig(defineOptions: DefineOptions = {}) {
+ const { overrides = {} } = defineOptions;
+ const root = process.cwd();
+ return defineConfig(async ({ mode }) => {
+ const { dependencies = {}, peerDependencies = {} } = await readPackageJSON(root);
+ const packageConfig: UserConfig = {
+ build: {
+ lib: {
+ entry: 'src/index.ts',
+ formats: ['es'],
+ fileName: () => 'index.mjs',
+ },
+ rollupOptions: {
+ external: [...Object.keys(dependencies), ...Object.keys(peerDependencies)],
+ },
+ },
+ plugins: [
+ dts({
+ logLevel: 'error',
+ }),
+ ],
+ };
+ const mergedConfig = mergeConfig(commonConfig(mode), packageConfig);
+
+ return mergeConfig(mergedConfig, overrides);
+ });
+}
+
+export { definePackageConfig };
diff --git a/internal/vite-config/src/index.ts b/internal/vite-config/src/index.ts
new file mode 100644
index 0000000..9ef1e80
--- /dev/null
+++ b/internal/vite-config/src/index.ts
@@ -0,0 +1,2 @@
+export * from './config/application';
+export * from './config/package';
diff --git a/internal/vite-config/src/plugins/appConfig.ts b/internal/vite-config/src/plugins/appConfig.ts
new file mode 100644
index 0000000..7d50662
--- /dev/null
+++ b/internal/vite-config/src/plugins/appConfig.ts
@@ -0,0 +1,104 @@
+import colors from 'picocolors';
+import { readPackageJSON } from 'pkg-types';
+import { type PluginOption } from 'vite';
+
+import { getEnvConfig } from '../utils/env';
+import { createContentHash } from '../utils/hash';
+
+const GLOBAL_CONFIG_FILE_NAME = '_app.config.js';
+const PLUGIN_NAME = 'app-config';
+
+async function createAppConfigPlugin({
+ root,
+ isBuild,
+}: {
+ root: string;
+ isBuild: boolean;
+}): Promise {
+ let publicPath: string;
+ let source: string;
+ if (!isBuild) {
+ return {
+ name: PLUGIN_NAME,
+ };
+ }
+ const { version = '' } = await readPackageJSON(root);
+
+ return {
+ name: PLUGIN_NAME,
+ async configResolved(_config) {
+ const appTitle = _config?.env?.VITE_GLOB_APP_TITLE ?? '';
+ // appTitle = appTitle.replace(/\s/g, '_').replace(/-/g, '_');
+ publicPath = _config.base;
+ source = await getConfigSource(appTitle);
+ },
+ async transformIndexHtml(html) {
+ publicPath = publicPath.endsWith('/') ? publicPath : `${publicPath}/`;
+
+ const appConfigSrc = `${
+ publicPath || '/'
+ }${GLOBAL_CONFIG_FILE_NAME}?v=${version}-${createContentHash(source)}`;
+
+ return {
+ html,
+ tags: [
+ {
+ tag: 'script',
+ attrs: {
+ src: appConfigSrc,
+ },
+ },
+ ],
+ };
+ },
+ async generateBundle() {
+ try {
+ this.emitFile({
+ type: 'asset',
+ fileName: GLOBAL_CONFIG_FILE_NAME,
+ source,
+ });
+
+ console.log(colors.cyan(`✨configuration file is build successfully!`));
+ } catch (error) {
+ console.log(
+ colors.red('configuration file configuration file failed to package:\n' + error),
+ );
+ }
+ },
+ };
+}
+
+/**
+ * Get the configuration file variable name
+ * @param env
+ */
+const getVariableName = (title: string) => {
+ function strToHex(str: string) {
+ const result: string[] = [];
+ for (let i = 0; i < str.length; ++i) {
+ const hex = str.charCodeAt(i).toString(16);
+ result.push(('000' + hex).slice(-4));
+ }
+ return result.join('').toUpperCase();
+ }
+ return `__PRODUCTION__${strToHex(title) || '__APP'}__CONF__`.toUpperCase().replace(/\s/g, '');
+};
+
+async function getConfigSource(appTitle: string) {
+ const config = await getEnvConfig();
+ const variableName = getVariableName(appTitle);
+ const windowVariable = `window.${variableName}`;
+ // Ensure that the variable will not be modified
+ let source = `${windowVariable}=${JSON.stringify(config)};`;
+ source += `
+ Object.freeze(${windowVariable});
+ Object.defineProperty(window, "${variableName}", {
+ configurable: false,
+ writable: false,
+ });
+ `.replace(/\s/g, '');
+ return source;
+}
+
+export { createAppConfigPlugin };
diff --git a/internal/vite-config/src/plugins/compress.ts b/internal/vite-config/src/plugins/compress.ts
new file mode 100644
index 0000000..8fc1397
--- /dev/null
+++ b/internal/vite-config/src/plugins/compress.ts
@@ -0,0 +1,38 @@
+/**
+ * Used to package and output gzip. Note that this does not work properly in Vite, the specific reason is still being investigated
+ * https://github.com/anncwb/vite-plugin-compression
+ */
+import type { PluginOption } from 'vite';
+import compressPlugin from 'vite-plugin-compression';
+
+export function configCompressPlugin({
+ compress,
+ deleteOriginFile = false,
+}: {
+ compress: string;
+ deleteOriginFile?: boolean;
+}): PluginOption[] {
+ const compressList = compress.split(',');
+
+ const plugins: PluginOption[] = [];
+
+ if (compressList.includes('gzip')) {
+ plugins.push(
+ compressPlugin({
+ ext: '.gz',
+ deleteOriginFile,
+ }),
+ );
+ }
+
+ if (compressList.includes('brotli')) {
+ plugins.push(
+ compressPlugin({
+ ext: '.br',
+ algorithm: 'brotliCompress',
+ deleteOriginFile,
+ }),
+ );
+ }
+ return plugins;
+}
diff --git a/internal/vite-config/src/plugins/html.ts b/internal/vite-config/src/plugins/html.ts
new file mode 100644
index 0000000..2623a28
--- /dev/null
+++ b/internal/vite-config/src/plugins/html.ts
@@ -0,0 +1,14 @@
+/**
+ * Plugin to minimize and use ejs template syntax in index.html.
+ * https://github.com/anncwb/vite-plugin-html
+ */
+import type { PluginOption } from 'vite';
+import { createHtmlPlugin } from 'vite-plugin-html';
+
+export function configHtmlPlugin({ isBuild }: { isBuild: boolean }) {
+ const htmlPlugin: PluginOption[] = createHtmlPlugin({
+ minify: isBuild,
+ viteNext: true,
+ });
+ return htmlPlugin;
+}
diff --git a/internal/vite-config/src/plugins/index.ts b/internal/vite-config/src/plugins/index.ts
new file mode 100644
index 0000000..b13fb39
--- /dev/null
+++ b/internal/vite-config/src/plugins/index.ts
@@ -0,0 +1,59 @@
+import vue from '@vitejs/plugin-vue';
+import vueJsx from '@vitejs/plugin-vue-jsx';
+import { type PluginOption } from 'vite';
+import purgeIcons from 'vite-plugin-purge-icons';
+
+import { createAppConfigPlugin } from './appConfig';
+import { configCompressPlugin } from './compress';
+import { configHtmlPlugin } from './html';
+import { configMockPlugin } from './mock';
+import { configSvgIconsPlugin } from './svgSprite';
+import { configVisualizerConfig } from './visualizer';
+
+interface Options {
+ isBuild: boolean;
+ root: string;
+ compress: string;
+ enableMock?: boolean;
+ enableAnalyze?: boolean;
+}
+
+async function createPlugins({ isBuild, root, enableMock, compress, enableAnalyze }: Options) {
+ const vitePlugins: (PluginOption | PluginOption[])[] = [vue(), vueJsx()];
+
+ const appConfigPlugin = await createAppConfigPlugin({ root, isBuild });
+ vitePlugins.push(appConfigPlugin);
+
+ // vite-plugin-html
+ vitePlugins.push(configHtmlPlugin({ isBuild }));
+
+ // vite-plugin-svg-icons
+ vitePlugins.push(configSvgIconsPlugin({ isBuild }));
+
+ // vite-plugin-purge-icons
+ vitePlugins.push(purgeIcons());
+
+ // The following plugins only work in the production environment
+ if (isBuild) {
+ // rollup-plugin-gzip
+ vitePlugins.push(
+ configCompressPlugin({
+ compress,
+ }),
+ );
+ }
+
+ // rollup-plugin-visualizer
+ if (enableAnalyze) {
+ vitePlugins.push(configVisualizerConfig());
+ }
+
+ // vite-plugin-mock
+ if (enableMock) {
+ vitePlugins.push(configMockPlugin({ isBuild }));
+ }
+
+ return vitePlugins;
+}
+
+export { createPlugins };
diff --git a/internal/vite-config/src/plugins/mock.ts b/internal/vite-config/src/plugins/mock.ts
new file mode 100644
index 0000000..b47899c
--- /dev/null
+++ b/internal/vite-config/src/plugins/mock.ts
@@ -0,0 +1,19 @@
+/**
+ * Mock plugin for development and production.
+ * https://github.com/anncwb/vite-plugin-mock
+ */
+import { viteMockServe } from 'vite-plugin-mock';
+
+export function configMockPlugin({ isBuild }: { isBuild: boolean }) {
+ return viteMockServe({
+ ignore: /^_/,
+ mockPath: 'mock',
+ localEnabled: !isBuild,
+ prodEnabled: isBuild,
+ injectCode: `
+ import { setupProdMockServer } from '../mock/_createProductionServer';
+
+ setupProdMockServer();
+ `,
+ });
+}
diff --git a/internal/vite-config/src/plugins/svgSprite.ts b/internal/vite-config/src/plugins/svgSprite.ts
new file mode 100644
index 0000000..659e5af
--- /dev/null
+++ b/internal/vite-config/src/plugins/svgSprite.ts
@@ -0,0 +1,17 @@
+/**
+ * Vite Plugin for fast creating SVG sprites.
+ * https://github.com/anncwb/vite-plugin-svg-icons
+ */
+
+import { resolve } from 'node:path';
+
+import type { PluginOption } from 'vite';
+import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
+
+export function configSvgIconsPlugin({ isBuild }: { isBuild: boolean }) {
+ const svgIconsPlugin = createSvgIconsPlugin({
+ iconDirs: [resolve(process.cwd(), 'src/assets/icons')],
+ svgoOptions: isBuild,
+ });
+ return svgIconsPlugin as PluginOption;
+}
diff --git a/internal/vite-config/src/plugins/visualizer.ts b/internal/vite-config/src/plugins/visualizer.ts
new file mode 100644
index 0000000..0b6ba62
--- /dev/null
+++ b/internal/vite-config/src/plugins/visualizer.ts
@@ -0,0 +1,14 @@
+/**
+ * Package file volume analysis
+ */
+import visualizer from 'rollup-plugin-visualizer';
+import { type PluginOption } from 'vite';
+
+export function configVisualizerConfig() {
+ return visualizer({
+ filename: './node_modules/.cache/visualizer/stats.html',
+ open: true,
+ gzipSize: true,
+ brotliSize: true,
+ }) as PluginOption;
+}
diff --git a/internal/vite-config/src/utils/env.ts b/internal/vite-config/src/utils/env.ts
new file mode 100644
index 0000000..c84ea94
--- /dev/null
+++ b/internal/vite-config/src/utils/env.ts
@@ -0,0 +1,49 @@
+import { join } from 'node:path';
+
+import dotenv from 'dotenv';
+import { readFile } from 'fs-extra';
+
+/**
+ * 获取当前环境下生效的配置文件名
+ */
+function getConfFiles() {
+ const script = process.env.npm_lifecycle_script as string;
+ const reg = new RegExp('--mode ([a-z_\\d]+)');
+ const result = reg.exec(script);
+ if (result) {
+ const mode = result[1];
+ return ['.env', `.env.${mode}`];
+ }
+ return ['.env', '.env.production'];
+}
+
+/**
+ * Get the environment variables starting with the specified prefix
+ * @param match prefix
+ * @param confFiles ext
+ */
+export async function getEnvConfig(
+ match = 'VITE_GLOB_',
+ confFiles = getConfFiles(),
+): Promise<{
+ [key: string]: string;
+}> {
+ let envConfig = {};
+
+ for (const confFile of confFiles) {
+ try {
+ const envPath = await readFile(join(process.cwd(), confFile), { encoding: 'utf8' });
+ const env = dotenv.parse(envPath);
+ envConfig = { ...envConfig, ...env };
+ } catch (e) {
+ console.error(`Error in parsing ${confFile}`, e);
+ }
+ }
+ const reg = new RegExp(`^(${match})`);
+ Object.keys(envConfig).forEach((key) => {
+ if (!reg.test(key)) {
+ Reflect.deleteProperty(envConfig, key);
+ }
+ });
+ return envConfig;
+}
diff --git a/internal/vite-config/src/utils/hash.ts b/internal/vite-config/src/utils/hash.ts
new file mode 100644
index 0000000..0b5a7c9
--- /dev/null
+++ b/internal/vite-config/src/utils/hash.ts
@@ -0,0 +1,16 @@
+import { createHash } from 'node:crypto';
+
+function createContentHash(content: string, hashLSize = 12) {
+ const hash = createHash('sha256').update(content);
+ return hash.digest('hex').slice(0, hashLSize);
+}
+function strToHex(str: string) {
+ const result: string[] = [];
+ for (let i = 0; i < str.length; ++i) {
+ const hex = str.charCodeAt(i).toString(16);
+ result.push(('000' + hex).slice(-4));
+ }
+ return result.join('').toUpperCase();
+}
+
+export { createContentHash, strToHex };
diff --git a/internal/vite-config/src/utils/modifyVars.ts b/internal/vite-config/src/utils/modifyVars.ts
new file mode 100644
index 0000000..0554343
--- /dev/null
+++ b/internal/vite-config/src/utils/modifyVars.ts
@@ -0,0 +1,47 @@
+import { resolve } from 'node:path';
+
+import { generate } from '@ant-design/colors';
+// @ts-ignore: typo
+/* import { getThemeVariables } from 'ant-design-vue/dist/theme'; */
+import { theme } from 'ant-design-vue/lib';
+import convertLegacyToken from 'ant-design-vue/lib/theme/convertLegacyToken';
+
+const { defaultAlgorithm, defaultSeed } = theme;
+const primaryColor = '#0960bd';
+
+function generateAntColors(color: string, theme: 'default' | 'dark' = 'default') {
+ return generate(color, {
+ theme,
+ });
+}
+
+/**
+ * less global variable
+ */
+export function generateModifyVars() {
+ const palettes = generateAntColors(primaryColor);
+ const primary = palettes[5];
+ const primaryColorObj: Record = {};
+
+ for (let index = 0; index < 10; index++) {
+ primaryColorObj[`primary-${index + 1}`] = palettes[index];
+ }
+ // const modifyVars = getThemeVariables();
+ const mapToken = defaultAlgorithm(defaultSeed);
+ const v3Token = convertLegacyToken(mapToken);
+ return {
+ ...v3Token,
+ // reference: Avoid repeated references
+ hack: `true; @import (reference) "${resolve('src/design/config.less')}";`,
+ 'primary-color': primary,
+ ...primaryColorObj,
+ 'info-color': primary,
+ 'processing-color': primary,
+ 'success-color': '#55D187', // Success color
+ 'error-color': '#ED6F6F', // False color
+ 'warning-color': '#EFBD47', // Warning color
+ 'font-size-base': '14px', // Main font size
+ 'border-radius-base': '2px', // Component/float fillet
+ 'link-color': primary, // Link color
+ };
+}
diff --git a/internal/vite-config/tsconfig.json b/internal/vite-config/tsconfig.json
new file mode 100644
index 0000000..cd27063
--- /dev/null
+++ b/internal/vite-config/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/ts-config/node.json",
+ "include": ["src"]
+}
diff --git a/mock/_createProductionServer.ts b/mock/_createProductionServer.ts
new file mode 100644
index 0000000..57c2755
--- /dev/null
+++ b/mock/_createProductionServer.ts
@@ -0,0 +1,34 @@
+import { createProdMockServer } from 'vite-plugin-mock/es/createProdMockServer';
+
+// 问题描述
+// 1. `import.meta.globEager` 已被弃用, 需要升级vite版本,有兼容问题
+// 2. `vite-plugin-mock` 插件问题 https://github.com/vbenjs/vite-plugin-mock/issues/56
+
+// const modules: Record = import.meta.glob("./**/*.ts", {
+// import: "default",
+// eager: true,
+// });
+
+// const mockModules = Object.keys(modules).reduce((pre, key) => {
+// if (!key.includes("/_")) {
+// pre.push(...modules[key]);
+// }
+// return pre;
+// }, [] as any[]);
+
+const modules = import.meta.glob('./**/*.ts', { eager: true });
+
+const mockModules: any[] = [];
+Object.keys(modules).forEach((key) => {
+ if (key.includes('/_')) {
+ return;
+ }
+ mockModules.push(...(modules as Recordable)[key].default);
+});
+
+/**
+ * Used in a production environment. Need to manually import all modules
+ */
+export function setupProdMockServer() {
+ createProdMockServer(mockModules);
+}
diff --git a/mock/_util.ts b/mock/_util.ts
new file mode 100644
index 0000000..380c3a3
--- /dev/null
+++ b/mock/_util.ts
@@ -0,0 +1,62 @@
+// Interface data format used to return a unified format
+import { ResultEnum } from '@/enums/httpEnum';
+
+export function resultSuccess(result: T, { message = 'ok' } = {}) {
+ return {
+ code: ResultEnum.SUCCESS,
+ result,
+ message,
+ type: 'success',
+ };
+}
+
+export function resultPageSuccess(
+ page: number,
+ pageSize: number,
+ list: T[],
+ { message = 'ok' } = {},
+) {
+ const pageData = pagination(page, pageSize, list);
+
+ return {
+ ...resultSuccess({
+ items: pageData,
+ total: list.length,
+ }),
+ message,
+ };
+}
+
+export function resultError(
+ message = 'Request failed',
+ { code = ResultEnum.ERROR, result = null } = {},
+) {
+ return {
+ code,
+ result,
+ message,
+ type: 'error',
+ };
+}
+
+export function pagination(pageNo: number, pageSize: number, array: T[]): T[] {
+ const offset = (pageNo - 1) * Number(pageSize);
+ return offset + Number(pageSize) >= array.length
+ ? array.slice(offset, array.length)
+ : array.slice(offset, offset + Number(pageSize));
+}
+
+export interface requestParams {
+ method: string;
+ body: any;
+ headers?: { authorization?: string };
+ query: any;
+}
+
+/**
+ * @description 本函数用于从request数据中获取token,请根据项目的实际情况修改
+ *
+ */
+export function getRequestToken({ headers }: requestParams): string | undefined {
+ return headers?.authorization;
+}
diff --git a/mock/demo/account.ts b/mock/demo/account.ts
new file mode 100644
index 0000000..a392493
--- /dev/null
+++ b/mock/demo/account.ts
@@ -0,0 +1,71 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess, resultError } from '../_util';
+import { ResultEnum } from '../../src/enums/httpEnum';
+
+const userInfo = {
+ name: 'Vben',
+ userid: '00000001',
+ email: 'test@gmail.com',
+ signature: '海纳百川,有容乃大',
+ introduction: '微笑着,努力着,欣赏着',
+ title: '交互专家',
+ group: '某某某事业群-某某平台部-某某技术部-UED',
+ tags: [
+ {
+ key: '0',
+ label: '很有想法的',
+ },
+ {
+ key: '1',
+ label: '专注设计',
+ },
+ {
+ key: '2',
+ label: '辣~',
+ },
+ {
+ key: '3',
+ label: '大长腿',
+ },
+ {
+ key: '4',
+ label: '川妹子',
+ },
+ {
+ key: '5',
+ label: '海纳百川',
+ },
+ ],
+ notifyCount: 12,
+ unreadCount: 11,
+ country: 'China',
+ address: 'Xiamen City 77',
+ phone: '0592-268888888',
+};
+
+export default [
+ {
+ url: '/basic-api/account/getAccountInfo',
+ timeout: 1000,
+ method: 'get',
+ response: () => {
+ return resultSuccess(userInfo);
+ },
+ },
+ {
+ url: '/basic-api/user/sessionTimeout',
+ method: 'post',
+ statusCode: 401,
+ response: () => {
+ return resultError();
+ },
+ },
+ {
+ url: '/basic-api/user/tokenExpired',
+ method: 'post',
+ statusCode: 200,
+ response: () => {
+ return resultError('Token Expired!', { code: ResultEnum.TIMEOUT as number });
+ },
+ },
+] as MockMethod[];
diff --git a/mock/demo/api-cascader.ts b/mock/demo/api-cascader.ts
new file mode 100644
index 0000000..6334ef5
--- /dev/null
+++ b/mock/demo/api-cascader.ts
@@ -0,0 +1,325 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const areaList: any[] = [
+ {
+ id: '530825900854620160',
+ code: '430000',
+ parentCode: '100000',
+ levelType: 1,
+ name: '湖南省',
+ province: '湖南省',
+ city: null,
+ district: null,
+ town: null,
+ village: null,
+ parentPath: '430000',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900883980288',
+ code: '430100',
+ parentCode: '430000',
+ levelType: 2,
+ name: '长沙市',
+ province: '湖南省',
+ city: '长沙市',
+ district: null,
+ town: null,
+ village: null,
+ parentPath: '430000,430100',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900951089152',
+ code: '430102',
+ parentCode: '430100',
+ levelType: 3,
+ name: '芙蓉区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '芙蓉区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430102',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825901014003712',
+ code: '430104',
+ parentCode: '430100',
+ levelType: 3,
+ name: '岳麓区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '岳麓区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430104',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530825900988837888',
+ code: '430103',
+ parentCode: '430100',
+ levelType: 3,
+ name: '天心区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: null,
+ village: null,
+ parentPath: '430000,430100,430103',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 16:33:42',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530826672489115648',
+ code: '430103002',
+ parentCode: '430103',
+ levelType: 4,
+ name: '坡子街街道',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: null,
+ parentPath: '430000,430100,430103,430103002',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-12-14 15:26:43',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241171607552',
+ code: '430103002001',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '八角亭社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '八角亭社区',
+ parentPath: '430000,430100,430103,430103002,430103002001',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2021-01-20 14:07:23',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241200967680',
+ code: '430103002002',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '西牌楼社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '西牌楼社区',
+ parentPath: '430000,430100,430103,430103002,430103002002',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241230327808',
+ code: '430103002003',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '太平街社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '太平街社区',
+ parentPath: '430000,430100,430103,430103002,430103002003',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241259687936',
+ code: '430103002005',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '坡子街社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '坡子街社区',
+ parentPath: '430000,430100,430103,430103002,430103002005',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241284853760',
+ code: '430103002006',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '青山祠社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '青山祠社区',
+ parentPath: '430000,430100,430103,430103002,430103002006',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241310019584',
+ code: '430103002007',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '沙河社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '沙河社区',
+ parentPath: '430000,430100,430103,430103002,430103002007',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241381322752',
+ code: '430103002008',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '碧湘社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '碧湘社区',
+ parentPath: '430000,430100,430103,430103002,430103002008',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241410682880',
+ code: '430103002009',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '创远社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '创远社区',
+ parentPath: '430000,430100,430103,430103002,430103002009',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241431654400',
+ code: '430103002010',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '楚湘社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '楚湘社区',
+ parentPath: '430000,430100,430103,430103002,430103002010',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241465208832',
+ code: '430103002011',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '西湖社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '西湖社区',
+ parentPath: '430000,430100,430103,430103002,430103002011',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241502957568',
+ code: '430103002012',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '登仁桥社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '登仁桥社区',
+ parentPath: '430000,430100,430103,430103002,430103002012',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+ {
+ id: '530840241553289216',
+ code: '430103002013',
+ parentCode: '430103002',
+ levelType: 5,
+ name: '文庙坪社区',
+ province: '湖南省',
+ city: '长沙市',
+ district: '天心区',
+ town: '坡子街街道',
+ village: '文庙坪社区',
+ parentPath: '430000,430100,430103,430103002,430103002013',
+ createTime: '2020-11-30 15:47:31',
+ updateTime: '2020-11-30 17:30:41',
+ customized: false,
+ usable: true,
+ },
+];
+export default [
+ {
+ url: '/basic-api/cascader/getAreaRecord',
+ timeout: 1000,
+ method: 'post',
+ response: ({ body }) => {
+ const { parentCode } = body || {};
+ if (!parentCode) {
+ return resultSuccess(areaList.filter((it) => it.code === '430000'));
+ }
+ return resultSuccess(areaList.filter((it) => it.parentCode === parentCode));
+ },
+ },
+] as MockMethod[];
diff --git a/mock/demo/select-demo.ts b/mock/demo/select-demo.ts
new file mode 100644
index 0000000..631c6bb
--- /dev/null
+++ b/mock/demo/select-demo.ts
@@ -0,0 +1,28 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const demoList = (keyword, count = 20) => {
+ const result = {
+ list: [] as any[],
+ };
+ for (let index = 0; index < count; index++) {
+ result.list.push({
+ name: `${keyword ?? ''}选项${index}`,
+ id: `${index}`,
+ });
+ }
+ return result;
+};
+
+export default [
+ {
+ url: '/basic-api/select/getDemoOptions',
+ timeout: 1000,
+ method: 'get',
+ response: ({ query }) => {
+ const { keyword, count } = query;
+ console.log(keyword);
+ return resultSuccess(demoList(keyword, count));
+ },
+ },
+] as MockMethod[];
diff --git a/mock/demo/system.ts b/mock/demo/system.ts
new file mode 100644
index 0000000..4c5d8de
--- /dev/null
+++ b/mock/demo/system.ts
@@ -0,0 +1,203 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultPageSuccess, resultSuccess } from '../_util';
+
+const accountList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 20; index++) {
+ result.push({
+ id: `${index}`,
+ account: '@first',
+ email: '@email',
+ nickname: '@cname()',
+ role: '@first',
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'dept|0-2': 1,
+ 'status|1': ['0', '1'],
+ });
+ }
+ return result;
+})();
+
+const roleList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 4; index++) {
+ result.push({
+ id: index + 1,
+ orderNo: `${index + 1}`,
+ roleName: ['超级管理员', '管理员', '文章管理员', '普通用户'][index],
+ roleValue: '@first',
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ menu: [['0', '1', '2'], ['0', '1'], ['0', '2'], ['2']][index],
+ 'status|1': ['0', '1'],
+ });
+ }
+ return result;
+})();
+
+const deptList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 3; index++) {
+ result.push({
+ id: `${index}`,
+ deptName: ['华东分部', '华南分部', '西北分部'][index],
+ orderNo: index + 1,
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'status|1': ['0', '0', '1'],
+ children: (() => {
+ const children: any[] = [];
+ for (let j = 0; j < 4; j++) {
+ children.push({
+ id: `${index}-${j}`,
+ deptName: ['研发部', '市场部', '商务部', '财务部'][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ remark: '@cword(10,20)',
+ 'status|1': ['0', '1'],
+ parentDept: `${index}`,
+ children: undefined,
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return result;
+})();
+
+const menuList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 3; index++) {
+ result.push({
+ id: `${index}`,
+ icon: ['ion:layers-outline', 'ion:git-compare-outline', 'ion:tv-outline'][index],
+ component: 'LAYOUT',
+ type: '0',
+ menuName: ['Dashboard', '权限管理', '功能'][index],
+ permission: '',
+ orderNo: index + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '0', '1'],
+ children: (() => {
+ const children: any[] = [];
+ for (let j = 0; j < 4; j++) {
+ children.push({
+ id: `${index}-${j}`,
+ type: '1',
+ menuName: ['菜单1', '菜单2', '菜单3', '菜单4'][j],
+ icon: 'ion:document',
+ permission: ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index],
+ component: [
+ '/dashboard/welcome/index',
+ '/dashboard/analysis/index',
+ '/dashboard/workbench/index',
+ '/dashboard/test/index',
+ ][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '1'],
+ parentMenu: `${index}`,
+ children: (() => {
+ const children: any[] = [];
+ for (let k = 0; k < 4; k++) {
+ children.push({
+ id: `${index}-${j}-${k}`,
+ type: '2',
+ menuName: '按钮' + (j + 1) + '-' + (k + 1),
+ icon: '',
+ permission:
+ ['menu1:view', 'menu2:add', 'menu3:update', 'menu4:del'][index] +
+ ':btn' +
+ (k + 1),
+ component: [
+ '/dashboard/welcome/index',
+ '/dashboard/analysis/index',
+ '/dashboard/workbench/index',
+ '/dashboard/test/index',
+ ][j],
+ orderNo: j + 1,
+ createTime: '@datetime',
+ 'status|1': ['0', '1'],
+ parentMenu: `${index}-${j}`,
+ children: undefined,
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return children;
+ })(),
+ });
+ }
+ return result;
+})();
+
+export default [
+ {
+ url: '/basic-api/system/getAccountList',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, accountList);
+ },
+ },
+ {
+ url: '/basic-api/system/getRoleListByPage',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, roleList);
+ },
+ },
+ {
+ url: '/basic-api/system/setRoleStatus',
+ timeout: 500,
+ method: 'post',
+ response: ({ query }) => {
+ const { id, status } = query;
+ return resultSuccess({ id, status });
+ },
+ },
+ {
+ url: '/basic-api/system/getAllRoleList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(roleList);
+ },
+ },
+ {
+ url: '/basic-api/system/getDeptList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(deptList);
+ },
+ },
+ {
+ url: '/basic-api/system/getMenuList',
+ timeout: 100,
+ method: 'get',
+ response: () => {
+ return resultSuccess(menuList);
+ },
+ },
+ {
+ url: '/basic-api/system/accountExist',
+ timeout: 500,
+ method: 'post',
+ response: ({ body }) => {
+ const { account } = body || {};
+ if (account && account.indexOf('admin') !== -1) {
+ return resultError('该字段不能包含admin');
+ } else {
+ return resultSuccess(`${account} can use`);
+ }
+ },
+ },
+] as MockMethod[];
diff --git a/mock/demo/table-demo.ts b/mock/demo/table-demo.ts
new file mode 100644
index 0000000..b450e3e
--- /dev/null
+++ b/mock/demo/table-demo.ts
@@ -0,0 +1,55 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { Random } from 'mockjs';
+import { resultPageSuccess } from '../_util';
+
+function getRandomPics(count = 10): string[] {
+ const arr: string[] = [];
+ for (let i = 0; i < count; i++) {
+ arr.push(Random.image('800x600', Random.color(), Random.color(), Random.title()));
+ }
+ return arr;
+}
+
+const demoList = (() => {
+ const result: any[] = [];
+ for (let index = 0; index < 200; index++) {
+ result.push({
+ id: `${index}`,
+ beginTime: '@datetime',
+ endTime: '@datetime',
+ address: '@city()',
+ name: '@cname()',
+ name1: '@cname()',
+ name2: '@cname()',
+ name3: '@cname()',
+ name4: '@cname()',
+ name5: '@cname()',
+ name6: '@cname()',
+ name7: '@cname()',
+ name8: '@cname()',
+ radio1: `选项${index + 1}`,
+ radio2: `选项${index + 1}`,
+ radio3: `选项${index + 1}`,
+ avatar: Random.image('400x400', Random.color(), Random.color(), Random.first()),
+ imgArr: getRandomPics(Math.ceil(Math.random() * 3) + 1),
+ imgs: getRandomPics(Math.ceil(Math.random() * 3) + 1),
+ date: `@date('yyyy-MM-dd')`,
+ time: `@time('HH:mm')`,
+ 'no|100000-10000000': 100000,
+ 'status|1': ['normal', 'enable', 'disable'],
+ });
+ }
+ return result;
+})();
+
+export default [
+ {
+ url: '/basic-api/table/getDemoList',
+ timeout: 100,
+ method: 'get',
+ response: ({ query }) => {
+ const { page = 1, pageSize = 20 } = query;
+ return resultPageSuccess(page, pageSize, demoList);
+ },
+ },
+] as MockMethod[];
diff --git a/mock/demo/tree-demo.ts b/mock/demo/tree-demo.ts
new file mode 100644
index 0000000..6fdcb85
--- /dev/null
+++ b/mock/demo/tree-demo.ts
@@ -0,0 +1,38 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultSuccess } from '../_util';
+
+const demoTreeList = (keyword) => {
+ const result = {
+ list: [] as Recordable[],
+ };
+ for (let index = 0; index < 5; index++) {
+ const children: Recordable[] = [];
+ for (let j = 0; j < 3; j++) {
+ children.push({
+ title: `${keyword ?? ''}选项${index}-${j}`,
+ value: `${index}-${j}`,
+ key: `${index}-${j}`,
+ });
+ }
+ result.list.push({
+ title: `${keyword ?? ''}选项${index}`,
+ value: `${index}`,
+ key: `${index}`,
+ children,
+ });
+ }
+ return result;
+};
+
+export default [
+ {
+ url: '/basic-api/tree/getDemoOptions',
+ timeout: 1000,
+ method: 'get',
+ response: ({ query }) => {
+ const { keyword } = query;
+ console.log(keyword);
+ return resultSuccess(demoTreeList(keyword));
+ },
+ },
+] as MockMethod[];
diff --git a/mock/sys/menu.ts b/mock/sys/menu.ts
new file mode 100644
index 0000000..e85af63
--- /dev/null
+++ b/mock/sys/menu.ts
@@ -0,0 +1,270 @@
+import { resultSuccess, resultError, getRequestToken, requestParams } from '../_util';
+import { MockMethod } from 'vite-plugin-mock';
+import { createFakeUserList } from './user';
+
+// single
+const dashboardRoute = {
+ path: '/dashboard',
+ name: 'Dashboard',
+ component: 'LAYOUT',
+ redirect: '/dashboard/analysis',
+ meta: {
+ title: 'routes.dashboard.dashboard',
+ hideChildrenInMenu: true,
+ icon: 'bx:bx-home',
+ },
+ children: [
+ {
+ path: 'analysis',
+ name: 'Analysis',
+ component: '/dashboard/analysis/index',
+ meta: {
+ hideMenu: true,
+ hideBreadcrumb: true,
+ title: 'routes.dashboard.analysis',
+ currentActiveMenu: '/dashboard',
+ icon: 'bx:bx-home',
+ },
+ },
+ {
+ path: 'workbench',
+ name: 'Workbench',
+ component: '/dashboard/workbench/index',
+ meta: {
+ hideMenu: true,
+ hideBreadcrumb: true,
+ title: 'routes.dashboard.workbench',
+ currentActiveMenu: '/dashboard',
+ icon: 'bx:bx-home',
+ },
+ },
+ ],
+};
+
+const backRoute = {
+ path: 'back',
+ name: 'PermissionBackDemo',
+ meta: {
+ title: 'routes.demo.permission.back',
+ },
+
+ children: [
+ {
+ path: 'page',
+ name: 'BackAuthPage',
+ component: '/demo/permission/back/index',
+ meta: {
+ title: 'routes.demo.permission.backPage',
+ },
+ },
+ {
+ path: 'btn',
+ name: 'BackAuthBtn',
+ component: '/demo/permission/back/Btn',
+ meta: {
+ title: 'routes.demo.permission.backBtn',
+ },
+ },
+ ],
+};
+
+const authRoute = {
+ path: '/permission',
+ name: 'Permission',
+ component: 'LAYOUT',
+ redirect: '/permission/front/page',
+ meta: {
+ icon: 'carbon:user-role',
+ title: 'routes.demo.permission.permission',
+ },
+ children: [backRoute],
+};
+
+const levelRoute = {
+ path: '/level',
+ name: 'Level',
+ component: 'LAYOUT',
+ redirect: '/level/menu1/menu1-1',
+ meta: {
+ icon: 'carbon:user-role',
+ title: 'routes.demo.level.level',
+ },
+
+ children: [
+ {
+ path: 'menu1',
+ name: 'Menu1Demo',
+ meta: {
+ title: 'Menu1',
+ },
+ children: [
+ {
+ path: 'menu1-1',
+ name: 'Menu11Demo',
+ meta: {
+ title: 'Menu1-1',
+ },
+ children: [
+ {
+ path: 'menu1-1-1',
+ name: 'Menu111Demo',
+ component: '/demo/level/Menu111',
+ meta: {
+ title: 'Menu111',
+ },
+ },
+ ],
+ },
+ {
+ path: 'menu1-2',
+ name: 'Menu12Demo',
+ component: '/demo/level/Menu12',
+ meta: {
+ title: 'Menu1-2',
+ },
+ },
+ ],
+ },
+ {
+ path: 'menu2',
+ name: 'Menu2Demo',
+ component: '/demo/level/Menu2',
+ meta: {
+ title: 'Menu2',
+ },
+ },
+ ],
+};
+
+const sysRoute = {
+ path: '/system',
+ name: 'System',
+ component: 'LAYOUT',
+ redirect: '/system/account',
+ meta: {
+ icon: 'ion:settings-outline',
+ title: 'routes.demo.system.moduleName',
+ },
+ children: [
+ {
+ path: 'account',
+ name: 'AccountManagement',
+ meta: {
+ title: 'routes.demo.system.account',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/account/index',
+ },
+ {
+ path: 'account_detail/:id',
+ name: 'AccountDetail',
+ meta: {
+ hideMenu: true,
+ title: 'routes.demo.system.account_detail',
+ ignoreKeepAlive: true,
+ showMenu: false,
+ currentActiveMenu: '/system/account',
+ },
+ component: '/demo/system/account/AccountDetail',
+ },
+ {
+ path: 'role',
+ name: 'RoleManagement',
+ meta: {
+ title: 'routes.demo.system.role',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/role/index',
+ },
+
+ {
+ path: 'menu',
+ name: 'MenuManagement',
+ meta: {
+ title: 'routes.demo.system.menu',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/menu/index',
+ },
+ {
+ path: 'dept',
+ name: 'DeptManagement',
+ meta: {
+ title: 'routes.demo.system.dept',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/dept/index',
+ },
+ {
+ path: 'changePassword',
+ name: 'ChangePassword',
+ meta: {
+ title: 'routes.demo.system.password',
+ ignoreKeepAlive: true,
+ },
+ component: '/demo/system/password/index',
+ },
+ ],
+};
+
+const linkRoute = {
+ path: '/link',
+ name: 'Link',
+ component: 'LAYOUT',
+ meta: {
+ icon: 'ion:tv-outline',
+ title: 'routes.demo.iframe.frame',
+ },
+ children: [
+ {
+ path: 'doc',
+ name: 'Doc',
+ meta: {
+ title: 'routes.demo.iframe.doc',
+ frameSrc: 'https://doc.vvbin.cn/',
+ },
+ },
+ {
+ path: 'https://doc.vvbin.cn/',
+ name: 'DocExternal',
+ component: 'LAYOUT',
+ meta: {
+ title: 'routes.demo.iframe.docExternal',
+ },
+ },
+ ],
+};
+
+export default [
+ {
+ url: '/basic-api/getMenuList',
+ timeout: 1000,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) {
+ return resultError('Invalid token!');
+ }
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('Invalid user token!');
+ }
+ const id = checkUser.userId;
+ let menu: Object[];
+ switch (id) {
+ case '1':
+ dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[0].path;
+ menu = [dashboardRoute, authRoute, levelRoute, sysRoute, linkRoute];
+ break;
+ case '2':
+ dashboardRoute.redirect = dashboardRoute.path + '/' + dashboardRoute.children[1].path;
+ menu = [dashboardRoute, authRoute, levelRoute, linkRoute];
+ break;
+ default:
+ menu = [];
+ }
+
+ return resultSuccess(menu);
+ },
+ },
+] as unknown as MockMethod[];
diff --git a/mock/sys/user.ts b/mock/sys/user.ts
new file mode 100644
index 0000000..79e46b9
--- /dev/null
+++ b/mock/sys/user.ts
@@ -0,0 +1,122 @@
+import { MockMethod } from 'vite-plugin-mock';
+import { resultError, resultSuccess, getRequestToken, requestParams } from '../_util';
+
+export function createFakeUserList() {
+ return [
+ {
+ userId: '1',
+ username: 'vben',
+ realName: 'Vben Admin',
+ avatar: '',
+ desc: 'manager',
+ password: '123456',
+ token: 'fakeToken1',
+ homePath: '/dashboard/analysis',
+ roles: [
+ {
+ roleName: 'Super Admin',
+ value: 'super',
+ },
+ ],
+ },
+ {
+ userId: '2',
+ username: 'test',
+ password: '123456',
+ realName: 'test user',
+ avatar: '',
+ desc: 'tester',
+ token: 'fakeToken2',
+ homePath: '/dashboard/workbench',
+ roles: [
+ {
+ roleName: 'Tester',
+ value: 'test',
+ },
+ ],
+ },
+ ];
+}
+
+const fakeCodeList: any = {
+ '1': ['1000', '3000', '5000'],
+
+ '2': ['2000', '4000', '6000'],
+};
+export default [
+ // mock user login
+ {
+ url: '/basic-api/login',
+ timeout: 200,
+ method: 'post',
+ response: ({ body }) => {
+ const { username, password } = body;
+ const checkUser = createFakeUserList().find(
+ (item) => item.username === username && password === item.password,
+ );
+ if (!checkUser) {
+ return resultError('Incorrect account or password!');
+ }
+ const { userId, username: _username, token, realName, desc, roles } = checkUser;
+ return resultSuccess({
+ roles,
+ userId,
+ username: _username,
+ token,
+ realName,
+ desc,
+ });
+ },
+ },
+ {
+ url: '/basic-api/getUserInfo',
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('The corresponding user information was not obtained!');
+ }
+ return resultSuccess(checkUser);
+ },
+ },
+ {
+ url: '/basic-api/getPermCode',
+ timeout: 200,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('Invalid token!');
+ }
+ const codeList = fakeCodeList[checkUser.userId];
+
+ return resultSuccess(codeList);
+ },
+ },
+ {
+ url: '/basic-api/logout',
+ timeout: 200,
+ method: 'get',
+ response: (request: requestParams) => {
+ const token = getRequestToken(request);
+ if (!token) return resultError('Invalid token');
+ const checkUser = createFakeUserList().find((item) => item.token === token);
+ if (!checkUser) {
+ return resultError('Invalid token!');
+ }
+ return resultSuccess(undefined, { message: 'Token has been destroyed' });
+ },
+ },
+ {
+ url: '/basic-api/testRetry',
+ statusCode: 405,
+ method: 'get',
+ response: () => {
+ return resultError('Error!');
+ },
+ },
+] as MockMethod[];
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..789bc84
--- /dev/null
+++ b/package.json
@@ -0,0 +1,155 @@
+{
+ "name": "vben-admin",
+ "version": "2.10.1",
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git"
+ },
+ "license": "MIT",
+ "author": {
+ "name": "vben",
+ "email": "anncwb@126.com",
+ "url": "https://github.com/anncwb"
+ },
+ "type": "module",
+ "scripts": {
+ "bootstrap": "pnpm install",
+ "build": "cross-env NODE_ENV=production NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build",
+ "build:analyze": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode analyze",
+ "build:docker": "vite build --mode docker",
+ "build:no-cache": "pnpm store prune && npm run build",
+ "build:test": "cross-env NODE_OPTIONS=--max-old-space-size=8192 pnpm vite build --mode test",
+ "commit": "czg",
+ "dev": "pnpm vite",
+ "preinstall": "npx only-allow pnpm",
+ "postinstall": "turbo run stub",
+ "lint": "turbo run lint",
+ "lint:eslint": "eslint --cache --max-warnings 0 \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
+ "lint:prettier": "prettier --write .",
+ "lint:stylelint": "stylelint \"**/*.{vue,css,less,scss}\" --fix --cache --cache-location node_modules/.cache/stylelint/",
+ "prepare": "husky install",
+ "preview": "npm run build && vite preview",
+ "reinstall": "rimraf pnpm-lock.yaml && rimraf package.lock.json && rimraf node_modules && npm run bootstrap",
+ "serve": "npm run dev",
+ "test:gzip": "npx http-server dist --cors --gzip -c-1",
+ "type:check": "vue-tsc --noEmit --skipLibCheck"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx}": [
+ "prettier --write",
+ "eslint --fix"
+ ],
+ "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
+ "prettier --write--parser json"
+ ],
+ "package.json": [
+ "prettier --write"
+ ],
+ "*.vue": [
+ "prettier --write",
+ "eslint --fix",
+ "stylelint --fix"
+ ],
+ "*.{scss,less,styl,html}": [
+ "prettier --write",
+ "stylelint --fix"
+ ],
+ "*.md": [
+ "prettier --write"
+ ]
+ },
+ "config": {
+ "commitizen": {
+ "path": "node_modules/cz-git"
+ }
+ },
+ "dependencies": {
+ "@ant-design/icons-vue": "^7.0.1",
+ "@iconify/iconify": "^3.1.1",
+ "@logicflow/core": "^1.2.18",
+ "@logicflow/extension": "^1.2.19",
+ "@vben/hooks": "workspace:*",
+ "@vue/shared": "^3.4.5",
+ "@vueuse/core": "^10.7.1",
+ "@zxcvbn-ts/core": "^3.0.4",
+ "ant-design-vue": "^4.0.8",
+ "axios": "^1.6.4",
+ "codemirror": "^5.65.16",
+ "cropperjs": "^1.6.1",
+ "crypto-js": "^4.2.0",
+ "dayjs": "^1.11.10",
+ "driver.js": "^1.3.1",
+ "echarts": "^5.4.3",
+ "exceljs": "^4.4.0",
+ "lodash-es": "^4.17.21",
+ "mockjs": "^1.1.0",
+ "nprogress": "^0.2.0",
+ "path-to-regexp": "^6.2.1",
+ "pinia": "2.1.7",
+ "pinia-plugin-persistedstate": "^3.2.1",
+ "print-js": "^1.6.0",
+ "qrcode": "^1.5.3",
+ "qs": "^6.11.2",
+ "resize-observer-polyfill": "^1.5.1",
+ "showdown": "^2.1.0",
+ "sortablejs": "^1.15.1",
+ "tinymce": "^5.10.9",
+ "unocss": "0.58.3",
+ "vditor": "^3.9.8",
+ "vue": "3.3.4",
+ "vue-i18n": "^9.8.0",
+ "vue-json-pretty": "^2.3.0",
+ "vue-router": "^4.2.5",
+ "vue-types": "^5.1.1",
+ "vuedraggable": "^4.1.0",
+ "vxe-table": "^4.5.17",
+ "vxe-table-plugin-export-xlsx": "^3.1.0",
+ "xe-utils": "^3.5.14",
+ "xlsx": "^0.18.5"
+ },
+ "devDependencies": {
+ "@commitlint/cli": "^18.4.4",
+ "@commitlint/config-conventional": "^18.4.4",
+ "@iconify/json": "^2.2.164",
+ "@purge-icons/generated": "^0.10.0",
+ "@types/codemirror": "^5.60.15",
+ "@types/crypto-js": "^4.2.1",
+ "@types/lodash-es": "^4.17.12",
+ "@types/mockjs": "^1.0.10",
+ "@types/nprogress": "^0.2.3",
+ "@types/qrcode": "^1.5.5",
+ "@types/qs": "^6.9.11",
+ "@types/showdown": "^2.0.6",
+ "@types/sortablejs": "^1.15.7",
+ "@vben/eslint-config": "workspace:*",
+ "@vben/stylelint-config": "workspace:*",
+ "@vben/ts-config": "workspace:*",
+ "@vben/types": "workspace:*",
+ "@vben/vite-config": "workspace:*",
+ "@vue/compiler-sfc": "^3.4.5",
+ "@vue/test-utils": "^2.4.3",
+ "cross-env": "^7.0.3",
+ "cz-git": "^1.8.0",
+ "czg": "^1.8.0",
+ "husky": "^8.0.3",
+ "lint-staged": "15.2.0",
+ "prettier": "^3.1.1",
+ "prettier-plugin-packagejson": "^2.4.8",
+ "rimraf": "^5.0.5",
+ "turbo": "^1.11.3",
+ "typescript": "^5.3.3",
+ "unbuild": "^2.0.0",
+ "vite": "^5.0.10",
+ "vite-plugin-mock": "^2.9.6",
+ "vue-tsc": "^1.8.27"
+ },
+ "packageManager": "pnpm@8.10.0",
+ "engines": {
+ "node": ">=18.12.0",
+ "pnpm": ">=8.10.0"
+ }
+}
diff --git a/packages/.gitkeep b/packages/.gitkeep
new file mode 100644
index 0000000..e69de29
diff --git a/packages/hooks/.eslintrc.cjs b/packages/hooks/.eslintrc.cjs
new file mode 100644
index 0000000..cd27a19
--- /dev/null
+++ b/packages/hooks/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/eslint-config/strict'],
+};
diff --git a/packages/hooks/build.config.ts b/packages/hooks/build.config.ts
new file mode 100644
index 0000000..20c8b54
--- /dev/null
+++ b/packages/hooks/build.config.ts
@@ -0,0 +1,10 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+ clean: true,
+ entries: ['src/index'],
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+});
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
new file mode 100644
index 0000000..0156169
--- /dev/null
+++ b/packages/hooks/package.json
@@ -0,0 +1,40 @@
+{
+ "name": "@vben/hooks",
+ "version": "1.0.0",
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "packages/hooks"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "type": "module",
+ "exports": {
+ ".": {
+ "default": "./src/index.ts"
+ }
+ },
+ "main": "./src/index.ts",
+ "module": "./src/index.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "//build": "pnpm unbuild",
+ "//stub": "pnpm unbuild --stub",
+ "clean": "pnpm rimraf .turbo node_modules dist",
+ "lint": "pnpm eslint ."
+ },
+ "dependencies": {
+ "@vueuse/core": "^10.7.1",
+ "lodash-es": "^4.17.21",
+ "vue": "3.3.4"
+ },
+ "devDependencies": {
+ "@vben/types": "workspace:*"
+ }
+}
diff --git a/packages/hooks/src/index.ts b/packages/hooks/src/index.ts
new file mode 100644
index 0000000..557a974
--- /dev/null
+++ b/packages/hooks/src/index.ts
@@ -0,0 +1,7 @@
+export * from './onMountedOrActivated';
+export * from './useAttrs';
+export * from './useRefs';
+export * from './useRequest';
+export * from './useScrollTo';
+export * from './useWindowSizeFn';
+export { useTimeoutFn } from '@vueuse/core';
diff --git a/packages/hooks/src/onMountedOrActivated.ts b/packages/hooks/src/onMountedOrActivated.ts
new file mode 100644
index 0000000..53a3c5e
--- /dev/null
+++ b/packages/hooks/src/onMountedOrActivated.ts
@@ -0,0 +1,25 @@
+import { type AnyFunction } from '@vben/types';
+import { nextTick, onActivated, onMounted } from 'vue';
+
+/**
+ * 在 OnMounted 或者 OnActivated 时触发
+ * @param hook 任何函数(包括异步函数)
+ */
+function onMountedOrActivated(hook: AnyFunction) {
+ let mounted: boolean;
+
+ onMounted(() => {
+ hook();
+ nextTick(() => {
+ mounted = true;
+ });
+ });
+
+ onActivated(() => {
+ if (mounted) {
+ hook();
+ }
+ });
+}
+
+export { onMountedOrActivated };
diff --git a/packages/hooks/src/useAttrs.ts b/packages/hooks/src/useAttrs.ts
new file mode 100644
index 0000000..df2118d
--- /dev/null
+++ b/packages/hooks/src/useAttrs.ts
@@ -0,0 +1,43 @@
+import { type Recordable } from '@vben/types';
+import { getCurrentInstance, reactive, shallowRef, watchEffect } from 'vue';
+
+interface UseAttrsOptions {
+ excludeListeners?: boolean;
+ excludeKeys?: string[];
+ excludeDefaultKeys?: boolean;
+}
+
+const DEFAULT_EXCLUDE_KEYS = ['class', 'style'];
+const LISTENER_PREFIX = /^on[A-Z]/;
+
+function entries(obj: Recordable): [string, T][] {
+ return Object.keys(obj).map((key: string) => [key, obj[key]]);
+}
+
+function useAttrs(options: UseAttrsOptions = {}): Recordable {
+ const instance = getCurrentInstance();
+ if (!instance) return {};
+
+ const { excludeListeners = false, excludeKeys = [], excludeDefaultKeys = true } = options;
+ const attrs = shallowRef({});
+ const allExcludeKeys = excludeKeys.concat(excludeDefaultKeys ? DEFAULT_EXCLUDE_KEYS : []);
+
+ // Since attrs are not reactive, make it reactive instead of doing in `onUpdated` hook for better performance
+ instance.attrs = reactive(instance.attrs);
+
+ watchEffect(() => {
+ const res = entries(instance.attrs).reduce((acm, [key, val]) => {
+ if (!allExcludeKeys.includes(key) && !(excludeListeners && LISTENER_PREFIX.test(key))) {
+ acm[key] = val;
+ }
+
+ return acm;
+ }, {} as Recordable);
+
+ attrs.value = res;
+ });
+
+ return attrs;
+}
+
+export { useAttrs, type UseAttrsOptions };
diff --git a/packages/hooks/src/useRefs.ts b/packages/hooks/src/useRefs.ts
new file mode 100644
index 0000000..efbaabd
--- /dev/null
+++ b/packages/hooks/src/useRefs.ts
@@ -0,0 +1,24 @@
+import type { ComponentPublicInstance, Ref } from 'vue';
+import { onBeforeUpdate, shallowRef } from 'vue';
+
+function useRefs(): {
+ refs: Ref;
+ setRefs: (index: number) => (el: Element | ComponentPublicInstance | null) => void;
+} {
+ const refs = shallowRef([]) as Ref;
+
+ onBeforeUpdate(() => {
+ refs.value = [];
+ });
+
+ const setRefs = (index: number) => (el: Element | ComponentPublicInstance | null) => {
+ refs.value[index] = el as T;
+ };
+
+ return {
+ refs,
+ setRefs,
+ };
+}
+
+export { useRefs };
diff --git a/packages/hooks/src/useRequest/Fetch.ts b/packages/hooks/src/useRequest/Fetch.ts
new file mode 100644
index 0000000..5e5b40a
--- /dev/null
+++ b/packages/hooks/src/useRequest/Fetch.ts
@@ -0,0 +1,147 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+import { reactive } from 'vue';
+
+import type { FetchState, PluginReturn, Service, Subscribe, UseRequestOptions } from './types';
+import { isFunction } from './utils/isFunction';
+
+export default class Fetch {
+ pluginImpls: PluginReturn[] = [];
+
+ count: number = 0;
+
+ state: FetchState = reactive({
+ loading: false,
+ params: undefined,
+ data: undefined,
+ error: undefined,
+ });
+
+ constructor(
+ public serviceRef: Service,
+ public options: UseRequestOptions,
+ public subscribe: Subscribe,
+ public initState: Partial> = {},
+ ) {
+ this.setState({ loading: !options.manual, ...initState });
+ }
+
+ setState(s: Partial> = {}) {
+ Object.assign(this.state, s);
+ this.subscribe();
+ }
+
+ runPluginHandler(event: keyof PluginReturn, ...rest: any[]) {
+ // @ts-ignore
+ const r = this.pluginImpls.map((i) => i[event]?.(...rest)).filter(Boolean);
+ return Object.assign({}, ...r);
+ }
+
+ async runAsync(...params: TParams): Promise {
+ this.count += 1;
+ const currentCount = this.count;
+
+ const {
+ stopNow = false,
+ returnNow = false,
+ ...state
+ } = this.runPluginHandler('onBefore', params);
+
+ // stop request
+ if (stopNow) {
+ return new Promise(() => {});
+ }
+
+ this.setState({
+ loading: true,
+ params,
+ ...state,
+ });
+
+ // return now
+ if (returnNow) {
+ return Promise.resolve(state.data);
+ }
+
+ this.options.onBefore?.(params);
+
+ try {
+ // replace service
+ let { servicePromise } = this.runPluginHandler('onRequest', this.serviceRef, params);
+
+ if (!servicePromise) {
+ servicePromise = this.serviceRef(...params);
+ }
+
+ const res = await servicePromise;
+
+ if (currentCount !== this.count) {
+ // prevent run.then when request is canceled
+ return new Promise(() => {});
+ }
+
+ // const formattedResult = this.options.formatResultRef.current ? this.options.formatResultRef.current(res) : res;
+
+ this.setState({ data: res, error: undefined, loading: false });
+
+ this.options.onSuccess?.(res, params);
+ this.runPluginHandler('onSuccess', res, params);
+
+ this.options.onFinally?.(params, res, undefined);
+
+ if (currentCount === this.count) {
+ this.runPluginHandler('onFinally', params, res, undefined);
+ }
+
+ return res;
+ } catch (error) {
+ if (currentCount !== this.count) {
+ // prevent run.then when request is canceled
+ return new Promise(() => {});
+ }
+
+ this.setState({ error, loading: false });
+
+ this.options.onError?.(error, params);
+ this.runPluginHandler('onError', error, params);
+
+ this.options.onFinally?.(params, undefined, error);
+
+ if (currentCount === this.count) {
+ this.runPluginHandler('onFinally', params, undefined, error);
+ }
+
+ throw error;
+ }
+ }
+
+ run(...params: TParams) {
+ this.runAsync(...params).catch((error) => {
+ if (!this.options.onError) {
+ console.error(error);
+ }
+ });
+ }
+
+ cancel() {
+ this.count += 1;
+ this.setState({ loading: false });
+
+ this.runPluginHandler('onCancel');
+ }
+
+ refresh() {
+ // @ts-ignore
+ this.run(...(this.state.params || []));
+ }
+
+ refreshAsync() {
+ // @ts-ignore
+ return this.runAsync(...(this.state.params || []));
+ }
+
+ mutate(data?: TData | ((oldData?: TData) => TData | undefined)) {
+ const targetData = isFunction(data) ? data(this.state.data) : data;
+ this.runPluginHandler('onMutate', targetData);
+ this.setState({ data: targetData });
+ }
+}
diff --git a/packages/hooks/src/useRequest/index.ts b/packages/hooks/src/useRequest/index.ts
new file mode 100644
index 0000000..8b8c887
--- /dev/null
+++ b/packages/hooks/src/useRequest/index.ts
@@ -0,0 +1,30 @@
+import useAutoRunPlugin from './plugins/useAutoRunPlugin';
+import useCachePlugin from './plugins/useCachePlugin';
+import useDebouncePlugin from './plugins/useDebouncePlugin';
+import useLoadingDelayPlugin from './plugins/useLoadingDelayPlugin';
+import usePollingPlugin from './plugins/usePollingPlugin';
+import useRefreshOnWindowFocusPlugin from './plugins/useRefreshOnWindowFocusPlugin';
+import useRetryPlugin from './plugins/useRetryPlugin';
+import useThrottlePlugin from './plugins/useThrottlePlugin';
+import type { Service, UseRequestOptions, UseRequestPlugin } from './types';
+import { useRequestImplement } from './useRequestImplement';
+
+export { clearCache } from './utils/cache';
+
+export function useRequest(
+ service: Service,
+ options?: UseRequestOptions,
+ plugins?: UseRequestPlugin[],
+) {
+ return useRequestImplement(service, options, [
+ ...(plugins || []),
+ useDebouncePlugin,
+ useLoadingDelayPlugin,
+ usePollingPlugin,
+ useRefreshOnWindowFocusPlugin,
+ useThrottlePlugin,
+ useAutoRunPlugin,
+ useCachePlugin,
+ useRetryPlugin,
+ ] as UseRequestPlugin[]);
+}
diff --git a/packages/hooks/src/useRequest/plugins/useAutoRunPlugin.ts b/packages/hooks/src/useRequest/plugins/useAutoRunPlugin.ts
new file mode 100644
index 0000000..0023b9b
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useAutoRunPlugin.ts
@@ -0,0 +1,52 @@
+import { ref, unref, watch } from 'vue';
+
+import type { UseRequestPlugin } from '../types';
+
+// support refreshDeps & ready
+const useAutoRunPlugin: UseRequestPlugin = (
+ fetchInstance,
+ { manual, ready = true, defaultParams = [], refreshDeps = [], refreshDepsAction },
+) => {
+ const hasAutoRun = ref(false);
+
+ watch(
+ () => unref(ready),
+ (readyVal) => {
+ if (!unref(manual) && readyVal) {
+ hasAutoRun.value = true;
+ fetchInstance.run(...defaultParams);
+ }
+ },
+ );
+
+ if (refreshDeps.length) {
+ watch(refreshDeps, () => {
+ if (hasAutoRun.value) {
+ return;
+ }
+ if (!manual) {
+ if (refreshDepsAction) {
+ refreshDepsAction();
+ } else {
+ fetchInstance.refresh();
+ }
+ }
+ });
+ }
+
+ return {
+ onBefore: () => {
+ if (!unref(ready)) {
+ return { stopNow: true };
+ }
+ },
+ };
+};
+
+useAutoRunPlugin.onInit = ({ ready = true, manual }) => {
+ return {
+ loading: !unref(manual) && unref(ready),
+ };
+};
+
+export default useAutoRunPlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useCachePlugin.ts b/packages/hooks/src/useRequest/plugins/useCachePlugin.ts
new file mode 100644
index 0000000..b449b66
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useCachePlugin.ts
@@ -0,0 +1,127 @@
+import { onUnmounted, ref, watchEffect } from 'vue';
+
+import type { UseRequestPlugin } from '../types';
+import type { CachedData } from '../utils/cache';
+import { getCache, setCache } from '../utils/cache';
+import { getCachePromise, setCachePromise } from '../utils/cachePromise';
+import { subscribe, trigger } from '../utils/cacheSubscribe';
+
+const useCachePlugin: UseRequestPlugin = (
+ fetchInstance,
+ {
+ cacheKey,
+ cacheTime = 5 * 60 * 1000,
+ staleTime = 0,
+ setCache: customSetCache,
+ getCache: customGetCache,
+ },
+) => {
+ const unSubscribeRef = ref<() => void>();
+ const currentPromiseRef = ref>();
+
+ const _setCache = (key: string, cachedData: CachedData) => {
+ customSetCache ? customSetCache(cachedData) : setCache(key, cacheTime, cachedData);
+ trigger(key, cachedData.data);
+ };
+
+ const _getCache = (key: string, params: any[] = []) => {
+ return customGetCache ? customGetCache(params) : getCache(key);
+ };
+
+ watchEffect(() => {
+ if (!cacheKey) return;
+
+ // get data from cache when init
+ const cacheData = _getCache(cacheKey);
+ if (cacheData && Object.hasOwnProperty.call(cacheData, 'data')) {
+ fetchInstance.state.data = cacheData.data;
+ fetchInstance.state.params = cacheData.params;
+
+ if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
+ fetchInstance.state.loading = false;
+ }
+ }
+
+ // subscribe same cachekey update, trigger update
+ unSubscribeRef.value = subscribe(cacheKey, (data) => {
+ fetchInstance.setState({ data });
+ });
+ });
+
+ onUnmounted(() => {
+ unSubscribeRef.value?.();
+ });
+
+ if (!cacheKey) {
+ return {};
+ }
+
+ return {
+ onBefore: (params) => {
+ const cacheData = _getCache(cacheKey, params);
+
+ if (!cacheData || !Object.hasOwnProperty.call(cacheData, 'data')) {
+ return {};
+ }
+
+ // If the data is fresh, stop request
+ if (staleTime === -1 || new Date().getTime() - cacheData.time <= staleTime) {
+ return {
+ loading: false,
+ data: cacheData?.data,
+ error: undefined,
+ returnNow: true,
+ };
+ } else {
+ // If the data is stale, return data, and request continue
+ return { data: cacheData?.data, error: undefined };
+ }
+ },
+ onRequest: (service, args) => {
+ let servicePromise = getCachePromise(cacheKey);
+
+ // If has servicePromise, and is not trigger by self, then use it
+ if (servicePromise && servicePromise !== currentPromiseRef.value) {
+ return { servicePromise };
+ }
+
+ servicePromise = service(...args);
+ currentPromiseRef.value = servicePromise;
+ setCachePromise(cacheKey, servicePromise);
+
+ return { servicePromise };
+ },
+ onSuccess: (data, params) => {
+ if (cacheKey) {
+ // cancel subscribe, avoid trgger self
+ unSubscribeRef.value?.();
+
+ _setCache(cacheKey, { data, params, time: new Date().getTime() });
+
+ // resubscribe
+ unSubscribeRef.value = subscribe(cacheKey, (d) => {
+ fetchInstance.setState({ data: d });
+ });
+ }
+ },
+ onMutate: (data) => {
+ if (cacheKey) {
+ // cancel subscribe, avoid trigger self
+ unSubscribeRef.value?.();
+
+ _setCache(cacheKey, {
+ data,
+ params: fetchInstance.state.params,
+ time: new Date().getTime(),
+ });
+
+ // resubscribe
+ unSubscribeRef.value = subscribe(cacheKey, (d) => {
+ fetchInstance.setState({ data: d });
+ });
+ }
+ },
+ };
+};
+
+export default useCachePlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useDebouncePlugin.ts b/packages/hooks/src/useRequest/plugins/useDebouncePlugin.ts
new file mode 100644
index 0000000..6a91ad1
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useDebouncePlugin.ts
@@ -0,0 +1,71 @@
+import type { DebouncedFunc, DebounceSettings } from 'lodash-es';
+import { debounce } from 'lodash-es';
+import { computed, ref, watchEffect } from 'vue';
+
+import type { UseRequestPlugin } from '../types';
+
+const useDebouncePlugin: UseRequestPlugin = (
+ fetchInstance,
+ { debounceWait, debounceLeading, debounceTrailing, debounceMaxWait },
+) => {
+ const debouncedRef = ref>();
+
+ const options = computed(() => {
+ const ret: DebounceSettings = {};
+
+ if (debounceLeading !== undefined) {
+ ret.leading = debounceLeading;
+ }
+ if (debounceTrailing !== undefined) {
+ ret.trailing = debounceTrailing;
+ }
+ if (debounceMaxWait !== undefined) {
+ ret.maxWait = debounceMaxWait;
+ }
+
+ return ret;
+ });
+
+ watchEffect(() => {
+ if (debounceWait) {
+ const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance);
+
+ debouncedRef.value = debounce(
+ (callback) => {
+ callback();
+ },
+ debounceWait,
+ options.value,
+ );
+
+ // debounce runAsync should be promise
+ // https://github.com/lodash/lodash/issues/4400#issuecomment-834800398
+ fetchInstance.runAsync = (...args) => {
+ return new Promise((resolve, reject) => {
+ debouncedRef.value?.(() => {
+ _originRunAsync(...args)
+ .then(resolve)
+ .catch(reject);
+ });
+ });
+ };
+
+ return () => {
+ debouncedRef.value?.cancel();
+ fetchInstance.runAsync = _originRunAsync;
+ };
+ }
+ });
+
+ if (!debounceWait) {
+ return {};
+ }
+
+ return {
+ onCancel: () => {
+ debouncedRef.value?.cancel();
+ },
+ };
+};
+
+export default useDebouncePlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useLoadingDelayPlugin.ts b/packages/hooks/src/useRequest/plugins/useLoadingDelayPlugin.ts
new file mode 100644
index 0000000..89c77f8
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useLoadingDelayPlugin.ts
@@ -0,0 +1,45 @@
+import { ref, unref } from 'vue';
+
+import type { UseRequestPlugin, UseRequestTimeout } from '../types';
+
+const useLoadingDelayPlugin: UseRequestPlugin = (
+ fetchInstance,
+ { loadingDelay, ready },
+) => {
+ const timerRef = ref();
+
+ if (!loadingDelay) {
+ return {};
+ }
+
+ const cancelTimeout = () => {
+ if (timerRef.value) {
+ clearTimeout(timerRef.value);
+ }
+ };
+
+ return {
+ onBefore: () => {
+ cancelTimeout();
+
+ // Two cases:
+ // 1. ready === undefined
+ // 2. ready === true
+ if (unref(ready) !== false) {
+ timerRef.value = setTimeout(() => {
+ fetchInstance.setState({ loading: true });
+ }, loadingDelay);
+ }
+
+ return { loading: false };
+ },
+ onFinally: () => {
+ cancelTimeout();
+ },
+ onCancel: () => {
+ cancelTimeout();
+ },
+ };
+};
+
+export default useLoadingDelayPlugin;
diff --git a/packages/hooks/src/useRequest/plugins/usePollingPlugin.ts b/packages/hooks/src/useRequest/plugins/usePollingPlugin.ts
new file mode 100644
index 0000000..7d076b3
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/usePollingPlugin.ts
@@ -0,0 +1,71 @@
+import { ref, watch } from 'vue';
+
+import type { UseRequestPlugin, UseRequestTimeout } from '../types';
+import { isDocumentVisible } from '../utils/isDocumentVisible';
+import subscribeReVisible from '../utils/subscribeReVisible';
+
+const usePollingPlugin: UseRequestPlugin = (
+ fetchInstance,
+ { pollingInterval, pollingWhenHidden = true, pollingErrorRetryCount = -1 },
+) => {
+ const timerRef = ref();
+ const unsubscribeRef = ref<() => void>();
+ const countRef = ref(0);
+
+ const stopPolling = () => {
+ if (timerRef.value) {
+ clearTimeout(timerRef.value);
+ }
+ unsubscribeRef.value?.();
+ };
+
+ watch(
+ () => pollingInterval,
+ () => {
+ if (!pollingInterval) {
+ stopPolling();
+ }
+ },
+ );
+
+ if (!pollingInterval) {
+ return {};
+ }
+
+ return {
+ onBefore: () => {
+ stopPolling();
+ },
+ onError: () => {
+ countRef.value += 1;
+ },
+ onSuccess: () => {
+ countRef.value = 0;
+ },
+ onFinally: () => {
+ if (
+ pollingErrorRetryCount === -1 ||
+ // When an error occurs, the request is not repeated after pollingErrorRetryCount retries
+ (pollingErrorRetryCount !== -1 && countRef.value <= pollingErrorRetryCount)
+ ) {
+ timerRef.value = setTimeout(() => {
+ // if pollingWhenHidden = false && document is hidden, then stop polling and subscribe revisible
+ if (!pollingWhenHidden && !isDocumentVisible()) {
+ unsubscribeRef.value = subscribeReVisible(() => {
+ fetchInstance.refresh();
+ });
+ } else {
+ fetchInstance.refresh();
+ }
+ }, pollingInterval);
+ } else {
+ countRef.value = 0;
+ }
+ },
+ onCancel: () => {
+ stopPolling();
+ },
+ };
+};
+
+export default usePollingPlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useRefreshOnWindowFocusPlugin.ts b/packages/hooks/src/useRequest/plugins/useRefreshOnWindowFocusPlugin.ts
new file mode 100644
index 0000000..e1f7b3b
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useRefreshOnWindowFocusPlugin.ts
@@ -0,0 +1,37 @@
+import { onUnmounted, ref, watchEffect } from 'vue';
+
+import type { UseRequestPlugin } from '../types';
+import { limit } from '../utils/limit';
+import subscribeFocus from '../utils/subscribeFocus';
+
+const useRefreshOnWindowFocusPlugin: UseRequestPlugin = (
+ fetchInstance,
+ { refreshOnWindowFocus, focusTimespan = 5000 },
+) => {
+ const unsubscribeRef = ref<() => void>();
+
+ const stopSubscribe = () => {
+ unsubscribeRef.value?.();
+ };
+
+ watchEffect(() => {
+ if (refreshOnWindowFocus) {
+ const limitRefresh = limit(fetchInstance.refresh.bind(fetchInstance), focusTimespan);
+ unsubscribeRef.value = subscribeFocus(() => {
+ limitRefresh();
+ });
+ }
+
+ return () => {
+ stopSubscribe();
+ };
+ });
+
+ onUnmounted(() => {
+ stopSubscribe();
+ });
+
+ return {};
+};
+
+export default useRefreshOnWindowFocusPlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useRetryPlugin.ts b/packages/hooks/src/useRequest/plugins/useRetryPlugin.ts
new file mode 100644
index 0000000..b400db3
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useRetryPlugin.ts
@@ -0,0 +1,54 @@
+import { ref } from 'vue';
+
+import type { UseRequestPlugin, UseRequestTimeout } from '../types';
+
+const useRetryPlugin: UseRequestPlugin = (
+ fetchInstance,
+ { retryInterval, retryCount },
+) => {
+ const timerRef = ref();
+ const countRef = ref(0);
+
+ const triggerByRetry = ref(false);
+
+ if (!retryCount) {
+ return {};
+ }
+
+ return {
+ onBefore: () => {
+ if (!triggerByRetry.value) {
+ countRef.value = 0;
+ }
+ triggerByRetry.value = false;
+
+ if (timerRef.value) {
+ clearTimeout(timerRef.value);
+ }
+ },
+ onSuccess: () => {
+ countRef.value = 0;
+ },
+ onError: () => {
+ countRef.value += 1;
+ if (retryCount === -1 || countRef.value <= retryCount) {
+ // Exponential backoff
+ const timeout = retryInterval ?? Math.min(1000 * 2 ** countRef.value, 30000);
+ timerRef.value = setTimeout(() => {
+ triggerByRetry.value = true;
+ fetchInstance.refresh();
+ }, timeout);
+ } else {
+ countRef.value = 0;
+ }
+ },
+ onCancel: () => {
+ countRef.value = 0;
+ if (timerRef.value) {
+ clearTimeout(timerRef.value);
+ }
+ },
+ };
+};
+
+export default useRetryPlugin;
diff --git a/packages/hooks/src/useRequest/plugins/useThrottlePlugin.ts b/packages/hooks/src/useRequest/plugins/useThrottlePlugin.ts
new file mode 100644
index 0000000..adeed05
--- /dev/null
+++ b/packages/hooks/src/useRequest/plugins/useThrottlePlugin.ts
@@ -0,0 +1,63 @@
+import type { DebouncedFunc, ThrottleSettings } from 'lodash-es';
+import { throttle } from 'lodash-es';
+import { ref, watchEffect } from 'vue';
+
+import type { UseRequestPlugin } from '../types';
+
+const useThrottlePlugin: UseRequestPlugin = (
+ fetchInstance,
+ { throttleWait, throttleLeading, throttleTrailing },
+) => {
+ const throttledRef = ref>();
+
+ const options: ThrottleSettings = {};
+ if (throttleLeading !== undefined) {
+ options.leading = throttleLeading;
+ }
+ if (throttleTrailing !== undefined) {
+ options.trailing = throttleTrailing;
+ }
+
+ watchEffect(() => {
+ if (throttleWait) {
+ const _originRunAsync = fetchInstance.runAsync.bind(fetchInstance);
+
+ throttledRef.value = throttle(
+ (callback) => {
+ callback();
+ },
+ throttleWait,
+ options,
+ );
+
+ // throttle runAsync should be promise
+ // https://github.com/lodash/lodash/issues/4400#issuecomment-834800398
+ fetchInstance.runAsync = (...args) => {
+ return new Promise((resolve, reject) => {
+ throttledRef.value?.(() => {
+ _originRunAsync(...args)
+ .then(resolve)
+ .catch(reject);
+ });
+ });
+ };
+
+ return () => {
+ fetchInstance.runAsync = _originRunAsync;
+ throttledRef.value?.cancel();
+ };
+ }
+ });
+
+ if (!throttleWait) {
+ return {};
+ }
+
+ return {
+ onCancel: () => {
+ throttledRef.value?.cancel();
+ },
+ };
+};
+
+export default useThrottlePlugin;
diff --git a/packages/hooks/src/useRequest/types.ts b/packages/hooks/src/useRequest/types.ts
new file mode 100644
index 0000000..60cc8e9
--- /dev/null
+++ b/packages/hooks/src/useRequest/types.ts
@@ -0,0 +1,124 @@
+import type { MaybeRef, Ref, WatchSource } from 'vue';
+
+import type Fetch from './Fetch';
+import type { CachedData } from './utils/cache';
+
+export type Service = (...args: TParams) => Promise;
+export type Subscribe = () => void;
+
+// for Fetch
+export interface FetchState {
+ loading: boolean;
+ params?: TParams;
+ data?: TData;
+ error?: Error;
+}
+
+export interface PluginReturn {
+ onBefore?: (params: TParams) =>
+ | ({
+ stopNow?: boolean;
+ returnNow?: boolean;
+ } & Partial>)
+ | void;
+
+ onRequest?: (
+ service: Service,
+ params: TParams,
+ ) => {
+ servicePromise?: Promise;
+ };
+
+ onSuccess?: (data: TData, params: TParams) => void;
+ onError?: (e: Error, params: TParams) => void;
+ onFinally?: (params: TParams, data?: TData, e?: Error) => void;
+ onCancel?: () => void;
+ onMutate?: (data: TData) => void;
+}
+
+// for useRequestImplement
+export interface UseRequestOptions {
+ manual?: MaybeRef;
+
+ onBefore?: (params: TParams) => void;
+ onSuccess?: (data: TData, params: TParams) => void;
+ onError?: (e: Error, params: TParams) => void;
+ // formatResult?: (res: any) => TData;
+ onFinally?: (params: TParams, data?: TData, e?: Error) => void;
+
+ defaultParams?: TParams;
+
+ // refreshDeps
+ refreshDeps?: WatchSource[];
+ refreshDepsAction?: () => void;
+
+ // loading delay
+ loadingDelay?: number;
+
+ // polling
+ pollingInterval?: number;
+ pollingWhenHidden?: boolean;
+ pollingErrorRetryCount?: number;
+
+ // refresh on window focus
+ refreshOnWindowFocus?: boolean;
+ focusTimespan?: number;
+
+ // debounce
+ debounceWait?: number;
+ debounceLeading?: boolean;
+ debounceTrailing?: boolean;
+ debounceMaxWait?: number;
+
+ // throttle
+ throttleWait?: number;
+ throttleLeading?: boolean;
+ throttleTrailing?: boolean;
+
+ // cache
+ cacheKey?: string;
+ cacheTime?: number;
+ staleTime?: number;
+ setCache?: (data: CachedData) => void;
+ getCache?: (params: TParams) => CachedData | undefined;
+
+ // retry
+ retryCount?: number;
+ retryInterval?: number;
+
+ // ready
+ ready?: MaybeRef;
+
+ // [key: string]: any;
+}
+
+export interface UseRequestPlugin {
+ // eslint-disable-next-line prettier/prettier
+ (
+ fetchInstance: Fetch,
+ options: UseRequestOptions,
+ ): PluginReturn;
+ onInit?: (options: UseRequestOptions) => Partial>;
+}
+
+// for index
+// export type OptionsWithoutFormat = Omit, 'formatResult'>;
+
+// export interface OptionsWithFormat extends Omit, 'formatResult'> {
+// formatResult: (res: TData) => TFormated;
+// };
+
+export interface UseRequestResult {
+ loading: Ref;
+ data: Ref;
+ error: Ref;
+ params: Ref;
+ cancel: Fetch['cancel'];
+ refresh: Fetch['refresh'];
+ refreshAsync: Fetch['refreshAsync'];
+ run: Fetch['run'];
+ runAsync: Fetch['runAsync'];
+ mutate: Fetch['mutate'];
+}
+
+export type UseRequestTimeout = ReturnType;
diff --git a/packages/hooks/src/useRequest/useRequestImplement.ts b/packages/hooks/src/useRequest/useRequestImplement.ts
new file mode 100644
index 0000000..54cf153
--- /dev/null
+++ b/packages/hooks/src/useRequest/useRequestImplement.ts
@@ -0,0 +1,49 @@
+/* eslint-disable @typescript-eslint/ban-ts-comment */
+import { onMounted, onUnmounted, toRefs } from 'vue';
+
+import Fetch from './Fetch';
+import type { Service, UseRequestOptions, UseRequestPlugin, UseRequestResult } from './types';
+
+export function useRequestImplement(
+ service: Service,
+ options: UseRequestOptions = {},
+ plugins: UseRequestPlugin[] = [],
+) {
+ const { manual = false, ...rest } = options;
+ const fetchOptions = { manual, ...rest };
+
+ const initState = plugins.map((p) => p?.onInit?.(fetchOptions)).filter(Boolean);
+
+ const fetchInstance = new Fetch(
+ service,
+ fetchOptions,
+ () => {},
+ Object.assign({}, ...initState),
+ );
+
+ fetchInstance.options = fetchOptions;
+ // run all plugins hooks
+ fetchInstance.pluginImpls = plugins.map((p) => p(fetchInstance, fetchOptions));
+
+ onMounted(() => {
+ if (!manual) {
+ const params = fetchInstance.state.params || options.defaultParams || [];
+ // @ts-ignore
+ fetchInstance.run(...params);
+ }
+ });
+
+ onUnmounted(() => {
+ fetchInstance.cancel();
+ });
+
+ return {
+ ...toRefs(fetchInstance.state),
+ cancel: fetchInstance.cancel.bind(fetchInstance),
+ mutate: fetchInstance.mutate.bind(fetchInstance),
+ refresh: fetchInstance.refresh.bind(fetchInstance),
+ refreshAsync: fetchInstance.refreshAsync.bind(fetchInstance),
+ run: fetchInstance.run.bind(fetchInstance),
+ runAsync: fetchInstance.runAsync.bind(fetchInstance),
+ } as UseRequestResult;
+}
diff --git a/packages/hooks/src/useRequest/utils/cache.ts b/packages/hooks/src/useRequest/utils/cache.ts
new file mode 100644
index 0000000..f89e0a1
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/cache.ts
@@ -0,0 +1,48 @@
+type Timer = ReturnType;
+type CachedKey = string | number;
+
+export interface CachedData {
+ data: TData;
+ params: TParams;
+ time: number;
+}
+
+interface RecordData extends CachedData {
+ timer: Timer | undefined;
+}
+
+const cache = new Map();
+
+export const setCache = (key: CachedKey, cacheTime: number, cachedData: CachedData) => {
+ const currentCache = cache.get(key);
+ if (currentCache?.timer) {
+ clearTimeout(currentCache.timer);
+ }
+
+ let timer: Timer | undefined = undefined;
+
+ if (cacheTime > -1) {
+ // if cache out, clear it
+ timer = setTimeout(() => {
+ cache.delete(key);
+ }, cacheTime);
+ }
+
+ cache.set(key, {
+ ...cachedData,
+ timer,
+ });
+};
+
+export const getCache = (key: CachedKey) => {
+ return cache.get(key);
+};
+
+export const clearCache = (key?: string | string[]) => {
+ if (key) {
+ const cacheKeys = Array.isArray(key) ? key : [key];
+ cacheKeys.forEach((cacheKey) => cache.delete(cacheKey));
+ } else {
+ cache.clear();
+ }
+};
diff --git a/packages/hooks/src/useRequest/utils/cachePromise.ts b/packages/hooks/src/useRequest/utils/cachePromise.ts
new file mode 100644
index 0000000..602a5c2
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/cachePromise.ts
@@ -0,0 +1,23 @@
+type CachedKey = string | number;
+
+const cachePromise = new Map>();
+
+export const getCachePromise = (cacheKey: CachedKey) => {
+ return cachePromise.get(cacheKey);
+};
+
+export const setCachePromise = (cacheKey: CachedKey, promise: Promise) => {
+ // Should cache the same promise, cannot be promise.finally
+ // Because the promise.finally will change the reference of the promise
+ cachePromise.set(cacheKey, promise);
+
+ // no use promise.finally for compatibility
+ promise
+ .then((res) => {
+ cachePromise.delete(cacheKey);
+ return res;
+ })
+ .catch(() => {
+ cachePromise.delete(cacheKey);
+ });
+};
diff --git a/packages/hooks/src/useRequest/utils/cacheSubscribe.ts b/packages/hooks/src/useRequest/utils/cacheSubscribe.ts
new file mode 100644
index 0000000..c66dc0c
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/cacheSubscribe.ts
@@ -0,0 +1,22 @@
+type Listener = (data: any) => void;
+
+const listeners: Record = {};
+
+export const trigger = (key: string, data: any) => {
+ if (listeners[key]) {
+ listeners[key].forEach((item) => item(data));
+ }
+};
+
+export const subscribe = (key: string, listener: Listener) => {
+ if (!listeners[key]) {
+ listeners[key] = [];
+ }
+
+ listeners[key].push(listener);
+
+ return function unsubscribe() {
+ const index = listeners[key].indexOf(listener);
+ listeners[key].splice(index, 1);
+ };
+};
diff --git a/packages/hooks/src/useRequest/utils/isBrowser.ts b/packages/hooks/src/useRequest/utils/isBrowser.ts
new file mode 100644
index 0000000..4a1b91e
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/isBrowser.ts
@@ -0,0 +1,5 @@
+export const isBrowser = !!(
+ typeof window !== 'undefined' &&
+ window.document &&
+ window.document.createElement
+);
diff --git a/packages/hooks/src/useRequest/utils/isDocumentVisible.ts b/packages/hooks/src/useRequest/utils/isDocumentVisible.ts
new file mode 100644
index 0000000..e9d1275
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/isDocumentVisible.ts
@@ -0,0 +1,8 @@
+import { isBrowser } from './isBrowser';
+
+export function isDocumentVisible(): boolean {
+ if (isBrowser) {
+ return document.visibilityState !== 'hidden';
+ }
+ return true;
+}
diff --git a/packages/hooks/src/useRequest/utils/isFunction.ts b/packages/hooks/src/useRequest/utils/isFunction.ts
new file mode 100644
index 0000000..782f37c
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/isFunction.ts
@@ -0,0 +1,2 @@
+export const isFunction = (value: unknown): value is (...args: any) => any =>
+ typeof value === 'function';
diff --git a/packages/hooks/src/useRequest/utils/isOnline.ts b/packages/hooks/src/useRequest/utils/isOnline.ts
new file mode 100644
index 0000000..900f9a3
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/isOnline.ts
@@ -0,0 +1,8 @@
+import { isBrowser } from './isBrowser';
+
+export function isOnline(): boolean {
+ if (isBrowser && typeof navigator.onLine !== 'undefined') {
+ return navigator.onLine;
+ }
+ return true;
+}
diff --git a/packages/hooks/src/useRequest/utils/limit.ts b/packages/hooks/src/useRequest/utils/limit.ts
new file mode 100644
index 0000000..c540e87
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/limit.ts
@@ -0,0 +1,12 @@
+export function limit(fn: any, timespan: number) {
+ let pending = false;
+
+ return (...args: any[]) => {
+ if (pending) return;
+ pending = true;
+ fn(...args);
+ setTimeout(() => {
+ pending = false;
+ }, timespan);
+ };
+}
diff --git a/packages/hooks/src/useRequest/utils/subscribeFocus.ts b/packages/hooks/src/useRequest/utils/subscribeFocus.ts
new file mode 100644
index 0000000..751650f
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/subscribeFocus.ts
@@ -0,0 +1,30 @@
+import { isBrowser } from './isBrowser';
+import { isDocumentVisible } from './isDocumentVisible';
+import { isOnline } from './isOnline';
+
+type Listener = () => void;
+
+const listeners: Listener[] = [];
+
+if (isBrowser) {
+ const revalidate = () => {
+ if (!isDocumentVisible() || !isOnline()) return;
+ for (let i = 0; i < listeners.length; i++) {
+ const listener = listeners[i];
+ listener();
+ }
+ };
+ window.addEventListener('visibilitychange', revalidate, false);
+ window.addEventListener('focus', revalidate, false);
+}
+
+export default function subscribe(listener: Listener) {
+ listeners.push(listener);
+
+ return function unsubscribe() {
+ const index = listeners.indexOf(listener);
+ if (index > -1) {
+ listeners.splice(index, 1);
+ }
+ };
+}
diff --git a/packages/hooks/src/useRequest/utils/subscribeReVisible.ts b/packages/hooks/src/useRequest/utils/subscribeReVisible.ts
new file mode 100644
index 0000000..cf961e9
--- /dev/null
+++ b/packages/hooks/src/useRequest/utils/subscribeReVisible.ts
@@ -0,0 +1,25 @@
+import { isBrowser } from './isBrowser';
+import { isDocumentVisible } from './isDocumentVisible';
+
+type Listener = () => void;
+
+const listeners: Listener[] = [];
+
+if (isBrowser) {
+ const revalidate = () => {
+ if (!isDocumentVisible()) return;
+ for (let i = 0; i < listeners.length; i++) {
+ const listener = listeners[i];
+ listener();
+ }
+ };
+ window.addEventListener('visibilitychange', revalidate, false);
+}
+
+export default function subscribe(listener: Listener) {
+ listeners.push(listener);
+ return function unsubscribe() {
+ const index = listeners.indexOf(listener);
+ listeners.splice(index, 1);
+ };
+}
diff --git a/packages/hooks/src/useScrollTo.ts b/packages/hooks/src/useScrollTo.ts
new file mode 100644
index 0000000..f6a95f4
--- /dev/null
+++ b/packages/hooks/src/useScrollTo.ts
@@ -0,0 +1,60 @@
+import { shallowRef, unref } from 'vue';
+
+interface UseScrollToOptions {
+ el: any;
+ to: number;
+ duration?: number;
+ callback?: () => any;
+}
+
+function easeInOutQuad(t: number, b: number, c: number, d: number) {
+ t /= d / 2;
+ if (t < 1) {
+ return (c / 2) * t * t + b;
+ }
+ t--;
+ return (-c / 2) * (t * (t - 2) - 1) + b;
+}
+
+function move(el: HTMLElement, amount: number) {
+ el.scrollTop = amount;
+}
+
+const position = (el: HTMLElement) => {
+ return el.scrollTop;
+};
+function useScrollTo({ el, to, duration = 500, callback }: UseScrollToOptions) {
+ const isActiveRef = shallowRef(false);
+ const start = position(el);
+ const change = to - start;
+ const increment = 20;
+ let currentTime = 0;
+
+ const animateScroll = function () {
+ if (!unref(isActiveRef)) {
+ return;
+ }
+ currentTime += increment;
+ const val = easeInOutQuad(currentTime, start, change, duration);
+ move(el, val);
+ if (currentTime < duration && unref(isActiveRef)) {
+ requestAnimationFrame(animateScroll);
+ } else {
+ if (callback && typeof callback === 'function') {
+ callback();
+ }
+ }
+ };
+ const run = () => {
+ isActiveRef.value = true;
+ animateScroll();
+ };
+
+ const stop = () => {
+ isActiveRef.value = false;
+ };
+
+ return { start: run, stop };
+}
+
+export { useScrollTo, type UseScrollToOptions };
diff --git a/packages/hooks/src/useWindowSizeFn.ts b/packages/hooks/src/useWindowSizeFn.ts
new file mode 100644
index 0000000..d8e7710
--- /dev/null
+++ b/packages/hooks/src/useWindowSizeFn.ts
@@ -0,0 +1,40 @@
+import { type AnyFunction } from '@vben/types';
+import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core';
+
+interface UseWindowSizeOptions {
+ wait?: number;
+ once?: boolean;
+ immediate?: boolean;
+ listenerOptions?: AddEventListenerOptions | boolean;
+}
+
+function useWindowSizeFn(fn: AnyFunction, options: UseWindowSizeOptions = {}) {
+ const { wait = 150, immediate } = options;
+ let handler = () => {
+ fn();
+ };
+ const handleSize = useDebounceFn(handler, wait);
+ handler = handleSize;
+
+ const start = () => {
+ if (immediate) {
+ handler();
+ }
+ window.addEventListener('resize', handler);
+ };
+
+ const stop = () => {
+ window.removeEventListener('resize', handler);
+ };
+
+ tryOnMounted(() => {
+ start();
+ });
+
+ tryOnUnmounted(() => {
+ stop();
+ });
+ return { start, stop };
+}
+
+export { useWindowSizeFn, type UseWindowSizeOptions };
diff --git a/packages/hooks/tsconfig.json b/packages/hooks/tsconfig.json
new file mode 100644
index 0000000..8508d50
--- /dev/null
+++ b/packages/hooks/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/ts-config/vue-app.json",
+ "include": ["src"]
+}
diff --git a/packages/types/.eslintrc.cjs b/packages/types/.eslintrc.cjs
new file mode 100644
index 0000000..cd27a19
--- /dev/null
+++ b/packages/types/.eslintrc.cjs
@@ -0,0 +1,4 @@
+module.exports = {
+ root: true,
+ extends: ['@vben/eslint-config/strict'],
+};
diff --git a/packages/types/build.config.ts b/packages/types/build.config.ts
new file mode 100644
index 0000000..20c8b54
--- /dev/null
+++ b/packages/types/build.config.ts
@@ -0,0 +1,10 @@
+import { defineBuildConfig } from 'unbuild';
+
+export default defineBuildConfig({
+ clean: true,
+ entries: ['src/index'],
+ declaration: true,
+ rollup: {
+ emitCJS: true,
+ },
+});
diff --git a/packages/types/package.json b/packages/types/package.json
new file mode 100644
index 0000000..38c15a9
--- /dev/null
+++ b/packages/types/package.json
@@ -0,0 +1,32 @@
+{
+ "name": "@vben/types",
+ "version": "1.0.0",
+ "homepage": "https://github.com/vbenjs/vue-vben-admin",
+ "bugs": {
+ "url": "https://github.com/vbenjs/vue-vben-admin/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/vbenjs/vue-vben-admin.git",
+ "directory": "packages/types"
+ },
+ "license": "MIT",
+ "sideEffects": false,
+ "type": "module",
+ "exports": {
+ ".": {
+ "default": "./src/index.ts"
+ }
+ },
+ "main": "./src/index.ts",
+ "module": "./src/index.ts",
+ "files": [
+ "dist"
+ ],
+ "scripts": {
+ "//build": "pnpm unbuild",
+ "//stub": "pnpm unbuild --stub",
+ "clean": "pnpm rimraf .turbo node_modules dist",
+ "lint": "pnpm eslint ."
+ }
+}
diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts
new file mode 100644
index 0000000..04bca77
--- /dev/null
+++ b/packages/types/src/index.ts
@@ -0,0 +1 @@
+export * from './utils';
diff --git a/packages/types/src/utils.ts b/packages/types/src/utils.ts
new file mode 100644
index 0000000..b8ed226
--- /dev/null
+++ b/packages/types/src/utils.ts
@@ -0,0 +1,58 @@
+/**
+ * 任意类型的异步函数
+ */
+type AnyPromiseFunction = (...arg: any[]) => PromiseLike;
+
+/**
+ * 任意类型的普通函数
+ */
+type AnyNormalFunction = (...arg: any[]) => any;
+
+/**
+ * 任意类型的函数
+ */
+type AnyFunction = AnyNormalFunction | AnyPromiseFunction;
+
+/**
+ * T | null 包装
+ */
+type Nullable = T | null;
+
+/**
+ * T | Not null 包装
+ */
+type NonNullable = T extends null | undefined ? never : T;
+
+/**
+ * 字符串类型对象
+ */
+type Recordable = Record;
+
+/**
+ * 字符串类型对象(只读)
+ */
+interface ReadonlyRecordable {
+ readonly [key: string]: T;
+}
+
+/**
+ * setTimeout 返回值类型
+ */
+type TimeoutHandle = ReturnType;
+
+/**
+ * setInterval 返回值类型
+ */
+type IntervalHandle = ReturnType;
+
+export {
+ type AnyFunction,
+ type AnyNormalFunction,
+ type AnyPromiseFunction,
+ type IntervalHandle,
+ type NonNullable,
+ type Nullable,
+ type ReadonlyRecordable,
+ type Recordable,
+ type TimeoutHandle,
+};
diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json
new file mode 100644
index 0000000..8508d50
--- /dev/null
+++ b/packages/types/tsconfig.json
@@ -0,0 +1,5 @@
+{
+ "$schema": "https://json.schemastore.org/tsconfig",
+ "extends": "@vben/ts-config/vue-app.json",
+ "include": ["src"]
+}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..103ba07
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,4 @@
+packages:
+ - 'internal/*'
+ - 'packages/*'
+ - 'apps/*'
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..d92e0b8c4963f1ae81021bbed63e94c2699dea66
GIT binary patch
literal 894
zcmZQzU<5(|0R|u`!H~hsz#zuJz@P!dKp_SNAO?x!2k(<*|Mp73(S^ghaCG}W3micV
zd6}gJRR66~2Cn|nK^;8mfr=prt{%vMt3Q887jFKoe=I;N;Of6uAgs8$R~xS7)?Ze*
z7G(86jaaP!YD8B5HAMw(#m^dPxRwit(0u_9G`M>FR>0MN^(+15GZl&w=KiVL1wn6T
z6kkZ}DT8x=g_XnA|4v>HSO33$3S12u1Fjy#fSC_upeciM
zfBAL8wfs)p0oU^X|9`j|GzMHfvK44*kh#ACs}Nd}*5GjrTo*z;*cWhd@)!pg85kNs
n7^oeNfqano0bpKwAP>a+3=AJYn4N*)2ax^&6#E0jpkM$1?7KMX
literal 0
HcmV?d00001
diff --git a/public/logo.png b/public/logo.png
new file mode 100644
index 0000000000000000000000000000000000000000..cd4c33d8b71f31d32aaed9fefe14f237b81e764f
GIT binary patch
literal 4042
zcmV;*4>jPx#L}ge>W=%~1DgXcg2mk?xX#fNO00031000^Q000001E2u_0{{R30RRC20H6W@
z1ONa40RR91X`llD1ONa40RR91S^xk503#H8v;Y7PG)Y83RCodHU3+j;)fqqM+eRNjTJZ@8SP-oeu(du$YGwKW?M$nVcGODS
znU1Y9bx=VBiz0}M6+#HFKoYWf?e5*Z_w>8b&1{l=+}F3{kM2MB-gCa!`F{8Do$q`n
zA>{wR3GV?qkuALQ`uJ>Dj9!UtqfjmVn3qI
z$nORnAxu4XWHUR=n7pg5i9do3kZyl+Z&HbN_;5vb$KN
zlAXcj<2@TB{g>AhzfRX7()jPoSjkH(X*3^Sq5CJ~lT;(b|AGxrl2SgamU>eTuS;_G
zfUts_Cmcw(wq!hsZu^H(HJ
zP+!A?5=qV}CxNj5cr{MHG^A}E+kUREClp94T9lib<6=FwQ@kM>7b(wpBU~Kk;SE3M
z%dl-QBY(nx#5t#pG5Q22nVn~5P`PNky*xU*i2HKUTr#9Rvh+#r&P7wmz6k@;+C}j#
zRSGS_96X0$p)t(z&JDv!E;sb>=P_+DBYQ%CbbAZ0oDma$Y{qaav}a36UyX|!3hS?k
z@~hWu;4`i0ahhjBfK-)CY%NmBs7TA{fyOA|bE^2bU?H0xAH05NDcaw%^%t`JoP)IN
zd(kVWMTPn3gRZ50KEYhnIA7rn53c98pxc&5!8r%1zMnlWz+IciPFvl*Zl0J|GCt%s
zB6Pl@ZES7jdB0;uWLc#-ry#9b9A90p2$fciH_A6T%#u7y$em9{eS&*SsrFnxI?Voa
z1`^zzu4z%W9&>QI>~23FX!3B69X$)8q^0#C-egCH9o};WlDbcMeo~Ukc8t%*t3t><
zGsJA`DAG73S4h(@Ht-*%WEb*y
zPC%NZ(7(zWLDfAV5_MiV2|DIX2yW)awN0&@quQOx-hwM!b#3w*n7Gc!WQ>z--a0bm
zV$Md5Qxd$WWWDxBC+##MtKcBj4{1*mD!o)64+UY2%QeyRa5FldkLACDT_|7O)ngARsMk
zQRd7XVppX*6yrQuLn8r(8-1M-sB*HLUWZ|SHkkzhsWwht5|Nk0dnKNnSL$HMoyBAC
zy4j-6+lIAgx*yThbV3=+V%mFpD#b`8lJWJt)!cyISe}M(LfRLj{SqLOLbR
zHacZ&HaT5>J~-W%dr3SSuhDjR5%5nXcB4^xC+Sc@y6*$!smcV)Sx;bhFnWHjImBFc
z<>cevtR({;
z!P)%WSe>T?8mld5KOEa$$ZHgkYUA2-3_CyP=J7c}YfIv09Q?kUt^39@8aKn^G_B(x
ztzH~oTZkp`nlR-~J7cW8gD!eVW$Um9(N#kG8a%}Iifb-A
zHHaFIxY_q}(G;rBQINWhD=(BLDQbDH0`JiaY6xmruRDK_#NSHl1Pa#ddrFva?@q>GphFZ@t610b>=#E45UDeZk1uJC6_Y0eG!qv
zk~np4Nj!@|d)(wtOV`j7S-K0}?1)o;x;Wm_922KwTkj9EAqhEZJTB6~n`g2m*kdsx
z-w}|)ak>S2N&K{rN6#1PYze`oKe3^+;v=|G
zW1m2i2@hRt%B}(uTYd@z9{)p^h*vtL`yein=!|k9a0J;Q`~Au@&s=`E@`CLL&~|ue
zZYjEdUX!Ivx~2NG`X!P1s>*j_M|9eG{@q-n7h_^1`QvW~r+<1eBmynputw}V6L!7X
zUFTVzpi}l5p--nV{kqe4i332t#s-K4U3;(rB7`RW&4)NPK$=MRch$KrCq(^??!R;Z
zjGUVWDSUI^4*($C?URfq)oKrFSg$)ruI&0JW$I-{9A%k?Qmtc#3{NR+VM;hj&m@Dd;pkg%cTY9RW|Ext9-AeP`7;B2@*q(k
zG$Y6`NTJ($^#QFgBistDTIW3gcN=OFmAR9jxJvr2vSObZp6*(4Us5EK#`3`UZj*SM
z;TzE~NL(S0!$%Kwt%!TYgG3eK}o
zbW1PXb$p@I&KfIE3whxW0^&|wU8Viom`nNkSlv!vAD<@2U@QPe=AW4$A}>fSFLHq=
z`4gE{IoHZd<(;rW)9@?6ie^e_yr|)539%3co6)EEgdP1Q?1FqeMuUjFAnpEF-)*35
zRz8Kc`WcC874AA)G7K<#LY=QcM4Z=o21Ml`p#w)}$PGCN(z=WV5jjDcc~hiRqkMgu
zRFqS#+P4cr?phS#UkGI)Kjo=7jhemCRrOX%|4F!s;q;P`AR;G7eN@{3uZB`g=Mfh>
z$%7)g^ZZRm_P{ry0(C5uww-DaFEytle-M!sq=sAjXTc=-R&xr?Q)^*w8{7z9^PI*x
zBwu{5=pRH67EUy~yWwx@nO^tG7er(QDUnbiSjZlo73GXN#8nP=M9*kQvWg^Cbqzpw?0`XM43Ul#Of%a*W+|zQ=IMG)$<@g&A
zayL35E2|7fkDq9g9SVFh2N9z{3NIO~f`t0LSq*Zi1M+k4!S}A*A#oYSFqhgcmvtk*
z<_PJWDs^z?T;P)_h!_PD*OZ3|C#X3s;t|@2Pt20j%II%Pjlt%&Pa$KEny6JJ_Fhk=
z^kqv1=3p8dM2vu>FNs681Qzsge~_Q+fm3G(`PH|eD5HCnAr4=tmEIlL
zQ6m&0pY_&uj#&wHcpxC|OvrUm6VMAblklgKhF2ekK|1n8NiU;xo%6%_LZVAJLyAS@
zJCBf}#`fKCBUGb}0|axCeh8I6=z$L<^mht9?|?p*VUYBbDYb#ku-@{qe(v;7t@C^W
z&O?o~1W)3>Qn3FDr)?i44<=o(3?TqirpvNM$;p4w?IqI4Lo))>wpI!?>vUCqesdJm
zCw)xZ0{Wd7n0B}moQJxPc5LO3IZqg;ri48Zq2aSBjuDXbhW4j|ufk*YTB_5DW3twh
z0OyU;6d}
zO_cMk@*ClUr(Y?WdaTR{tsR=48rKJzDu3+A%)|@t(&aC~G$Ork97so>@*jXQ{4ME3
zmd*t-#LMM+OS~qSy!9uy5vnztU@=V!bGw6`QzYakRX2-njnT$&JKo2Eq?1Y>VI8F;
zqMuv+!JKF}e<;h)^*KmxEEnnr2Q8{_&;v$jm&P0-QI!Z99jp-zre6Oigzib;n`fAIYH7HnLt+%
zm}%z{lInwKZaV}EsF4EGdUCUPh3q>=dJW41=`uS2PL}u6sta12o01-4F<4M_MX;
zi_oLS&Z(}WPFDVm^;PN`Lhe7gPM*@~2Kt;YK{{14=C>Kfz7B+~1x
zJdGMP!s`dI@+aLZRAq$TmEL0H+!RQ}*G7+)KK7#Qz9(5NZi1%>%4_M6QDtW7L|Hi@6%97Rc9KrB
wye8-)$EM3QFb+%|;3_^K6VC&%0&4pAKS2^|fB`U?S^xk507*qoM6N<$g6lSzX#fBK
literal 0
HcmV?d00001
diff --git a/public/resource/tinymce/langs/en.js b/public/resource/tinymce/langs/en.js
new file mode 100644
index 0000000..27337c3
--- /dev/null
+++ b/public/resource/tinymce/langs/en.js
@@ -0,0 +1,419 @@
+tinymce.addI18n('es', {
+ Redo: 'Rehacer',
+ Undo: 'Deshacer',
+ Cut: 'Cortar',
+ Copy: 'Copiar',
+ Paste: 'Pegar',
+ 'Select all': 'Seleccionar todo',
+ 'New document': 'Nuevo documento',
+ Ok: 'Ok',
+ Cancel: 'Cancelar',
+ 'Visual aids': 'Ayudas visuales',
+ Bold: 'Negrita',
+ Italic: 'Cursiva',
+ Underline: 'Subrayado',
+ Strikethrough: 'Tachado',
+ Superscript: 'Super\u00edndice',
+ Subscript: 'Sub\u00edndice',
+ 'Clear formatting': 'Limpiar formato',
+ 'Align left': 'Alinear a la izquierda',
+ 'Align center': 'Alinear al centro',
+ 'Align right': 'Alinear a la derecha',
+ Justify: 'Justificar',
+ 'Bullet list': 'Lista de vi\u00f1etas',
+ 'Numbered list': 'Lista numerada',
+ 'Decrease indent': 'Disminuir sangr\u00eda',
+ 'Increase indent': 'Incrementar sangr\u00eda',
+ Close: 'Cerrar',
+ Formats: 'Formatos',
+ "Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": 'Su navegador no es compatible con el acceso directo al portapapeles. Use las teclas Crtl+X\/C\/V de su teclado.',
+ Headers: 'Encabezados',
+ 'Header 1': 'Encabezado 1',
+ 'Header 2': 'Encabezado 2',
+ 'Header 3': 'Encabezado 3',
+ 'Header 4': 'Encabezado 4',
+ 'Header 5': 'Encabezado 5',
+ 'Header 6': 'Encabezado 6',
+ Headings: 'Encabezados',
+ 'Heading 1': 'Encabezado 1',
+ 'Heading 2': 'Encabezado 2',
+ 'Heading 3': 'Encabezado 3',
+ 'Heading 4': 'Encabezado 4',
+ 'Heading 5': 'Encabezado 5',
+ 'Heading 6': 'Encabezado 6',
+ Preformatted: 'Con formato previo',
+ Div: 'Div',
+ Pre: 'Pre',
+ Code: 'C\u00f3digo',
+ Paragraph: 'P\u00e1rrafo',
+ Blockquote: 'Blockquote',
+ Inline: 'Alineado',
+ Blocks: 'Bloques',
+ 'Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.': 'Pegar est\u00e1 ahora en modo de texto plano. El contenido se pegar\u00e1 como texto plano hasta que desactive esta opci\u00f3n.',
+ Fonts: 'Fuentes',
+ 'Font Sizes': 'Tama\u00f1os de fuente',
+ Class: 'Clase',
+ 'Browse for an image': 'Buscar una imagen',
+ OR: 'OR',
+ 'Drop an image here': 'Arrastre una imagen aqu\u00ed',
+ Upload: 'Cargar',
+ Block: 'Bloque',
+ Align: 'Alinear',
+ Default: 'Por defecto',
+ Circle: 'C\u00edrculo',
+ Disc: 'Disco',
+ Square: 'Cuadrado',
+ 'Lower Alpha': 'Inferior Alfa',
+ 'Lower Greek': 'Inferior Griega',
+ 'Lower Roman': 'Inferior Romana',
+ 'Upper Alpha': 'Superior Alfa',
+ 'Upper Roman': 'Superior Romana',
+ 'Anchor...': 'Anclaje...',
+ Name: 'Nombre',
+ Id: 'Id',
+ 'Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.': 'Deber\u00eda comenzar por una letra, seguida solo de letras, n\u00fameros, guiones, puntos, dos puntos o guiones bajos.',
+ 'You have unsaved changes are you sure you want to navigate away?': 'Tiene cambios sin guardar. \u00bfEst\u00e1 seguro de que quiere salir?',
+ 'Restore last draft': 'Restaurar el \u00faltimo borrador',
+ 'Special character...': 'Car\u00e1cter especial...',
+ 'Source code': 'C\u00f3digo fuente',
+ 'Insert\/Edit code sample': 'Insertar\/editar c\u00f3digo de prueba',
+ Language: 'Idioma',
+ 'Code sample...': 'Ejemplo de c\u00f3digo...',
+ 'Color Picker': 'Selector de colores',
+ R: 'R',
+ G: 'V',
+ B: 'A',
+ 'Left to right': 'De izquierda a derecha',
+ 'Right to left': 'De derecha a izquierda',
+ 'Emoticons...': 'Emoticones...',
+ 'Metadata and Document Properties': 'Metadatos y propiedades del documento',
+ Title: 'T\u00edtulo',
+ Keywords: 'Palabras clave',
+ Description: 'Descripci\u00f3n',
+ Robots: 'Robots',
+ Author: 'Autor',
+ Encoding: 'Codificaci\u00f3n',
+ Fullscreen: 'Pantalla completa',
+ Action: 'Acci\u00f3n',
+ Shortcut: 'Atajo',
+ Help: 'Ayuda',
+ Address: 'Direcci\u00f3n',
+ 'Focus to menubar': 'Enfocar la barra del men\u00fa',
+ 'Focus to toolbar': 'Enfocar la barra de herramientas',
+ 'Focus to element path': 'Enfocar la ruta del elemento',
+ 'Focus to contextual toolbar': 'Enfocar la barra de herramientas contextual',
+ 'Insert link (if link plugin activated)': 'Insertar enlace (si el complemento de enlace est\u00e1 activado)',
+ 'Save (if save plugin activated)': 'Guardar (si el componente de salvar est\u00e1 activado)',
+ 'Find (if searchreplace plugin activated)': 'Buscar (si el complemento buscar-remplazar est\u00e1 activado)',
+ 'Plugins installed ({0}):': 'Plugins instalados ({0}):',
+ 'Premium plugins:': 'Complementos premium:',
+ 'Learn more...': 'Aprende m\u00e1s...',
+ 'You are using {0}': 'Estas usando {0}',
+ Plugins: 'Complementos',
+ 'Handy Shortcuts': 'Accesos directos',
+ 'Horizontal line': 'L\u00ednea horizontal',
+ 'Insert\/edit image': 'Insertar\/editar imagen',
+ 'Image description': 'Descripci\u00f3n de la imagen',
+ Source: 'Enlace',
+ Dimensions: 'Dimensiones',
+ 'Constrain proportions': 'Restringir proporciones',
+ General: 'General',
+ Advanced: 'Avanzado',
+ Style: 'Estilo',
+ 'Vertical space': 'Espacio vertical',
+ 'Horizontal space': 'Espacio horizontal',
+ Border: 'Borde',
+ 'Insert image': 'Insertar imagen',
+ 'Image...': 'Imagen...',
+ 'Image list': 'Lista de im\u00e1genes',
+ 'Rotate counterclockwise': 'Girar a la izquierda',
+ 'Rotate clockwise': 'Girar a la derecha',
+ 'Flip vertically': 'Invertir verticalmente',
+ 'Flip horizontally': 'Invertir horizontalmente',
+ 'Edit image': 'Editar imagen',
+ 'Image options': 'Opciones de imagen',
+ 'Zoom in': 'Acercar',
+ 'Zoom out': 'Alejar',
+ Crop: 'Recortar',
+ Resize: 'Redimensionar',
+ Orientation: 'Orientaci\u00f3n',
+ Brightness: 'Brillo',
+ Sharpen: 'Forma',
+ Contrast: 'Contraste',
+ 'Color levels': 'Niveles de color',
+ Gamma: 'Gamma',
+ Invert: 'Invertir',
+ Apply: 'Aplicar',
+ Back: 'Atr\u00e1s',
+ 'Insert date\/time': 'Insertar fecha\/hora',
+ 'Date\/time': 'Fecha\/hora',
+ 'Insert\/Edit Link': 'Insertar\/editar enlace',
+ 'Insert\/edit link': 'Insertar\/editar enlace',
+ 'Text to display': 'Texto para mostrar',
+ Url: 'URL',
+ 'Open link in...': 'Abrir enlace en...',
+ 'Current window': 'Ventana actual',
+ None: 'Ninguno',
+ 'New window': 'Nueva ventana',
+ 'Remove link': 'Quitar enlace',
+ Anchors: 'Anclas',
+ 'Link...': 'Enlace...',
+ 'Paste or type a link': 'Pega o introduce un enlace',
+ 'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?': 'El enlace que has introducido no parece ser una direcci\u00f3n de correo electr\u00f3nico. Quieres a\u00f1adir el prefijo necesario mailto: ?',
+ 'The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?': 'El enlace que has introducido no parece ser una enlace externo. Quieres a\u00f1adir el prefijo necesario http:\/\/ ?',
+ 'Link list': 'Lista de enlaces',
+ 'Insert video': 'Insertar video',
+ 'Insert\/edit video': 'Insertar\/editar video',
+ 'Insert\/edit media': 'Insertar\/editar medio',
+ 'Alternative source': 'Enlace alternativo',
+ 'Alternative source URL': 'Origen de URL alternativo',
+ 'Media poster (Image URL)': 'P\u00f3ster de medio (URL de imagen)',
+ 'Paste your embed code below:': 'Pega tu c\u00f3digo embebido debajo',
+ Embed: 'Incrustado',
+ 'Media...': 'Medios...',
+ 'Nonbreaking space': 'Espacio fijo',
+ 'Page break': 'Salto de p\u00e1gina',
+ 'Paste as text': 'Pegar como texto',
+ Preview: 'Previsualizar',
+ 'Print...': 'Imprimir...',
+ Save: 'Guardar',
+ Find: 'Buscar',
+ 'Replace with': 'Reemplazar con',
+ Replace: 'Reemplazar',
+ 'Replace all': 'Reemplazar todo',
+ Previous: 'Anterior',
+ Next: 'Siguiente',
+ 'Find and replace...': 'Buscar y reemplazar...',
+ 'Could not find the specified string.': 'No se encuentra la cadena de texto especificada',
+ 'Match case': 'Coincidencia exacta',
+ 'Find whole words only': 'Solo palabras completas',
+ 'Spell check': 'Revisar ortograf\u00eda',
+ Ignore: 'Ignorar',
+ 'Ignore all': 'Ignorar todos',
+ Finish: 'Finalizar',
+ 'Add to Dictionary': 'A\u00f1adir al Diccionario',
+ 'Insert table': 'Insertar tabla',
+ 'Table properties': 'Propiedades de la tabla',
+ 'Delete table': 'Eliminar tabla',
+ Cell: 'Celda',
+ Row: 'Fila',
+ Column: 'Columna',
+ 'Cell properties': 'Propiedades de la celda',
+ 'Merge cells': 'Combinar celdas',
+ 'Split cell': 'Dividir celdas',
+ 'Insert row before': 'Insertar fila antes',
+ 'Insert row after': 'Insertar fila despu\u00e9s ',
+ 'Delete row': 'Eliminar fila',
+ 'Row properties': 'Propiedades de la fila',
+ 'Cut row': 'Cortar fila',
+ 'Copy row': 'Copiar fila',
+ 'Paste row before': 'Pegar la fila antes',
+ 'Paste row after': 'Pegar la fila despu\u00e9s',
+ 'Insert column before': 'Insertar columna antes',
+ 'Insert column after': 'Insertar columna despu\u00e9s',
+ 'Delete column': 'Eliminar columna',
+ Cols: 'Columnas',
+ Rows: 'Filas',
+ Width: 'Ancho',
+ Height: 'Alto',
+ 'Cell spacing': 'Espacio entre celdas',
+ 'Cell padding': 'Relleno de celda',
+ 'Show caption': 'Mostrar t\u00edtulo',
+ Left: 'Izquierda',
+ Center: 'Centrado',
+ Right: 'Derecha',
+ 'Cell type': 'Tipo de celda',
+ Scope: '\u00c1mbito',
+ Alignment: 'Alineaci\u00f3n',
+ 'H Align': 'Alineamiento Horizontal',
+ 'V Align': 'Alineamiento Vertical',
+ Top: 'Arriba',
+ Middle: 'Centro',
+ Bottom: 'Abajo',
+ 'Header cell': 'Celda de la cebecera',
+ 'Row group': 'Grupo de filas',
+ 'Column group': 'Grupo de columnas',
+ 'Row type': 'Tipo de fila',
+ Header: 'Cabecera',
+ Body: 'Cuerpo',
+ Footer: 'Pie de p\u00e1gina',
+ 'Border color': 'Color del borde',
+ 'Insert template...': 'Insertar plantilla...',
+ Templates: 'Plantillas',
+ Template: 'Plantilla',
+ 'Text color': 'Color del texto',
+ 'Background color': 'Color de fondo',
+ 'Custom...': 'Personalizar...',
+ 'Custom color': 'Color personalizado',
+ 'No color': 'Sin color',
+ 'Remove color': 'Quitar color',
+ 'Table of Contents': 'Tabla de contenidos',
+ 'Show blocks': 'Mostrar bloques',
+ 'Show invisible characters': 'Mostrar caracteres invisibles',
+ 'Word count': 'Contar palabras',
+ Count: 'Recuento',
+ Document: 'Documento',
+ Selection: 'Selecci\u00f3n',
+ Words: 'Palabras',
+ 'Words: {0}': 'Palabras: {0}',
+ '{0} words': '{0} palabras',
+ File: 'Archivo',
+ Edit: 'Editar',
+ Insert: 'Insertar',
+ View: 'Ver',
+ Format: 'Formato',
+ Table: 'Tabla',
+ Tools: 'Herramientas',
+ 'Powered by {0}': 'Desarrollado por {0}',
+ 'Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help': '\u00c1rea de texto enriquecido. Pulse ALT-F9 para el menu. Pulse ALT-F10 para la barra de herramientas. Pulse ALT-0 para ayuda',
+ 'Image title': 'Titulo de imagen',
+ 'Border width': 'Ancho de borde',
+ 'Border style': 'Estilo de borde',
+ Error: 'Error',
+ Warn: 'Advertencia',
+ Valid: 'V\u00e1lido',
+ 'To open the popup, press Shift+Enter': 'Para abrir el elemento emergente, pulse May\u00fas+Intro',
+ 'Rich Text Area. Press ALT-0 for help.': '\u00c1rea de texto enriquecido. Pulse ALT-0 para abrir la ayuda.',
+ 'System Font': 'Fuente de sistema',
+ 'Failed to upload image: {0}': 'Fallo al cargar imagen: {0}',
+ 'Failed to load plugin: {0} from url {1}': 'Fallo al cargar complemento: {0} desde URL {1}',
+ 'Failed to load plugin url: {0}': 'Fallo al cargar URL del complemento: {0}',
+ 'Failed to initialize plugin: {0}': 'Fallo al iniciar el complemento: {0}',
+ example: 'ejemplo',
+ Search: 'Buscar',
+ All: 'Todo',
+ Currency: 'Divisa',
+ Text: 'Texto',
+ Quotations: 'Comillas',
+ Mathematical: 'S\u00edmbolo matem\u00e1tico',
+ 'Extended Latin': 'Latino extendido A',
+ Symbols: 'S\u00edmbolos',
+ Arrows: 'Flechas',
+ 'User Defined': 'Definido por el usuario',
+ 'dollar sign': 'signo de d\u00f3lar',
+ 'currency sign': 'signo de divisa',
+ 'euro-currency sign': 'signo de euro',
+ 'colon sign': 'signo de dos puntos',
+ 'cruzeiro sign': 'signo de cruceiro',
+ 'french franc sign': 'signo de franco franc\u00e9s',
+ 'lira sign': 'signo de lira',
+ 'mill sign': 'signo de mill',
+ 'naira sign': 'signo de naira',
+ 'peseta sign': 'signo de peseta',
+ 'rupee sign': 'signo de rupia',
+ 'won sign': 'signo de won',
+ 'new sheqel sign': 'signo de nuevo s\u00e9quel',
+ 'dong sign': 'signo de dong',
+ 'kip sign': 'signo de kip',
+ 'tugrik sign': 'signo de tugrik',
+ 'drachma sign': 'signo de dracma',
+ 'german penny symbol': 'signo de penique alem\u00e1n',
+ 'peso sign': 'signo de peso',
+ 'guarani sign': 'signo de guaran\u00ed',
+ 'austral sign': 'signo de austral',
+ 'hryvnia sign': 'signo de grivna',
+ 'cedi sign': 'signo de cedi',
+ 'livre tournois sign': 'signo de libra tornesa',
+ 'spesmilo sign': 'signo de spesmilo',
+ 'tenge sign': 'signo de tenge',
+ 'indian rupee sign': 'signo de rupia india',
+ 'turkish lira sign': 'signo de lira turca',
+ 'nordic mark sign': 'signo de marco n\u00f3rdico',
+ 'manat sign': 'signo de manat',
+ 'ruble sign': 'signo de rublo',
+ 'yen character': 'car\u00e1cter de yen',
+ 'yuan character': 'car\u00e1cter de yuan',
+ 'yuan character, in hong kong and taiwan': 'car\u00e1cter de yuan en Hong Kong y Taiw\u00e1n',
+ 'yen\/yuan character variant one': 'Variante uno de car\u00e1cter de yen\/yuan',
+ 'Loading emoticons...': 'Cargando emoticonos...',
+ 'Could not load emoticons': 'No se han podido cargar los emoticonos',
+ People: 'Personas',
+ 'Animals and Nature': 'Animales y naturaleza',
+ 'Food and Drink': 'Comida y bebida',
+ Activity: 'Actividad',
+ 'Travel and Places': 'Viajes y lugares',
+ Objects: 'Objetos',
+ Flags: 'Banderas',
+ Characters: 'Caracteres',
+ 'Characters (no spaces)': 'Caracteres (sin espacios)',
+ '{0} characters': '{0} caracteres',
+ 'Error: Form submit field collision.': 'Error: Colisi\u00f3n de campo al enviar formulario.',
+ 'Error: No form element found.': 'Error: No se encuentra ning\u00fan elemento de formulario.',
+ Update: 'Actualizar',
+ 'Color swatch': 'Muestrario de colores',
+ Turquoise: 'Turquesa',
+ Green: 'Verde',
+ Blue: 'Azul',
+ Purple: 'P\u00farpura',
+ 'Navy Blue': 'Azul marino',
+ 'Dark Turquoise': 'Turquesa oscuro',
+ 'Dark Green': 'Verde oscuro',
+ 'Medium Blue': 'Azul medio',
+ 'Medium Purple': 'P\u00farpura medio',
+ 'Midnight Blue': 'Azul medio',
+ Yellow: 'Amarillo',
+ Orange: 'Naranja',
+ Red: 'Rojo',
+ 'Light Gray': 'Gris claro',
+ Gray: 'Gris',
+ 'Dark Yellow': 'Amarillo oscuro',
+ 'Dark Orange': 'Naranja oscuro',
+ 'Dark Red': 'Rojo oscuro',
+ 'Medium Gray': 'Gris medio',
+ 'Dark Gray': 'Gris oscuro',
+ 'Light Green': 'Verde claro',
+ 'Light Yellow': 'Amarillo claro',
+ 'Light Red': 'Rojo claro',
+ 'Light Purple': 'Morado claro',
+ 'Light Blue': 'Azul claro',
+ 'Dark Purple': 'Morado oscuro',
+ 'Dark Blue': 'Azul oscuro',
+ Black: 'Negro',
+ White: 'Blanco',
+ 'Switch to or from fullscreen mode': 'Activar o desactivar modo pantalla completa',
+ 'Open help dialog': 'Abrir di\u00e1logo de ayuda',
+ history: 'historial',
+ styles: 'estilos',
+ formatting: 'formato',
+ alignment: 'alineaci\u00f3n',
+ indentation: 'sangr\u00eda',
+ 'permanent pen': 'bol\u00edgrafo permanente',
+ comments: 'comentarios',
+ 'Format Painter': 'Copiar formato',
+ 'Insert\/edit iframe': 'Insertar\/editar iframe',
+ Capitalization: 'Uso de may\u00fasculas',
+ lowercase: 'min\u00fasculas',
+ UPPERCASE: 'MAY\u00daSCULAS',
+ 'Title Case': 'Tipo T\u00edtulo',
+ 'Permanent Pen Properties': 'Propiedades del bol\u00edgrafo permanente',
+ 'Permanent pen properties...': 'Propiedades del bol\u00edgrafo permanente...',
+ Font: 'Fuente',
+ Size: 'Tama\u00f1o',
+ 'More...': 'M\u00e1s...',
+ 'Spellcheck Language': 'Corrector',
+ 'Select...': 'Seleccionar...',
+ Preferences: 'Preferencias',
+ Yes: 'S\u00ed',
+ No: 'No',
+ 'Keyboard Navigation': 'Navegaci\u00f3n con el teclado',
+ Version: 'Versi\u00f3n',
+ Anchor: 'Ancla',
+ 'Special character': 'Car\u00e1cter especial',
+ 'Code sample': 'Ejemplo de c\u00f3digo',
+ Color: 'Color',
+ Emoticons: 'Emoticonos',
+ 'Document properties': 'Propiedades del documento',
+ Image: 'Imagen',
+ 'Insert link': 'Insertar enlace',
+ Target: 'Destino',
+ Link: 'Enlace',
+ Poster: 'Miniatura',
+ Media: 'Media',
+ Print: 'Imprimir',
+ Prev: 'Anterior',
+ 'Find and replace': 'Buscar y reemplazar',
+ 'Whole words': 'Palabras completas',
+ Spellcheck: 'Corrector ortogr\u00e1fico',
+ Caption: 'Subt\u00edtulo',
+ 'Insert template': 'Insertar plantilla'
+})
diff --git a/public/resource/tinymce/langs/zh_CN.js b/public/resource/tinymce/langs/zh_CN.js
new file mode 100644
index 0000000..f9d8b5c
--- /dev/null
+++ b/public/resource/tinymce/langs/zh_CN.js
@@ -0,0 +1,389 @@
+tinymce.addI18n('zh_CN',{
+"Redo": "\u91cd\u505a",
+"Undo": "\u64a4\u9500",
+"Cut": "\u526a\u5207",
+"Copy": "\u590d\u5236",
+"Paste": "\u7c98\u8d34",
+"Select all": "\u5168\u9009",
+"New document": "\u65b0\u6587\u4ef6",
+"Ok": "\u786e\u5b9a",
+"Cancel": "\u53d6\u6d88",
+"Visual aids": "\u7f51\u683c\u7ebf",
+"Bold": "\u7c97\u4f53",
+"Italic": "\u659c\u4f53",
+"Underline": "\u4e0b\u5212\u7ebf",
+"Strikethrough": "\u5220\u9664\u7ebf",
+"Superscript": "\u4e0a\u6807",
+"Subscript": "\u4e0b\u6807",
+"Clear formatting": "\u6e05\u9664\u683c\u5f0f",
+"Align left": "\u5de6\u8fb9\u5bf9\u9f50",
+"Align center": "\u4e2d\u95f4\u5bf9\u9f50",
+"Align right": "\u53f3\u8fb9\u5bf9\u9f50",
+"Justify": "\u4e24\u7aef\u5bf9\u9f50",
+"Bullet list": "\u9879\u76ee\u7b26\u53f7",
+"Numbered list": "\u7f16\u53f7\u5217\u8868",
+"Decrease indent": "\u51cf\u5c11\u7f29\u8fdb",
+"Increase indent": "\u589e\u52a0\u7f29\u8fdb",
+"Close": "\u5173\u95ed",
+"Formats": "\u683c\u5f0f",
+"Your browser doesn't support direct access to the clipboard. Please use the Ctrl+X\/C\/V keyboard shortcuts instead.": "\u4f60\u7684\u6d4f\u89c8\u5668\u4e0d\u652f\u6301\u6253\u5f00\u526a\u8d34\u677f\uff0c\u8bf7\u4f7f\u7528Ctrl+X\/C\/V\u7b49\u5feb\u6377\u952e\u3002",
+"Headers": "\u6807\u9898",
+"Header 1": "\u6807\u98981",
+"Header 2": "\u6807\u98982",
+"Header 3": "\u6807\u98983",
+"Header 4": "\u6807\u98984",
+"Header 5": "\u6807\u98985",
+"Header 6": "\u6807\u98986",
+"Headings": "\u6807\u9898",
+"Heading 1": "\u6807\u98981",
+"Heading 2": "\u6807\u98982",
+"Heading 3": "\u6807\u98983",
+"Heading 4": "\u6807\u98984",
+"Heading 5": "\u6807\u98985",
+"Heading 6": "\u6807\u98986",
+"Preformatted": "\u9884\u5148\u683c\u5f0f\u5316\u7684",
+"Div": "Div",
+"Pre": "Pre",
+"Code": "\u4ee3\u7801",
+"Paragraph": "\u6bb5\u843d",
+"Blockquote": "\u5f15\u6587\u533a\u5757",
+"Inline": "\u6587\u672c",
+"Blocks": "\u57fa\u5757",
+"Paste is now in plain text mode. Contents will now be pasted as plain text until you toggle this option off.": "\u5f53\u524d\u4e3a\u7eaf\u6587\u672c\u7c98\u8d34\u6a21\u5f0f\uff0c\u518d\u6b21\u70b9\u51fb\u53ef\u4ee5\u56de\u5230\u666e\u901a\u7c98\u8d34\u6a21\u5f0f\u3002",
+"Fonts": "\u5b57\u4f53",
+"Font Sizes": "\u5b57\u53f7",
+"Class": "\u7c7b\u578b",
+"Browse for an image": "\u6d4f\u89c8\u56fe\u50cf",
+"OR": "\u6216",
+"Drop an image here": "\u62d6\u653e\u4e00\u5f20\u56fe\u50cf\u81f3\u6b64",
+"Upload": "\u4e0a\u4f20",
+"Block": "\u5757",
+"Align": "\u5bf9\u9f50",
+"Default": "\u9ed8\u8ba4",
+"Circle": "\u7a7a\u5fc3\u5706",
+"Disc": "\u5b9e\u5fc3\u5706",
+"Square": "\u65b9\u5757",
+"Lower Alpha": "\u5c0f\u5199\u82f1\u6587\u5b57\u6bcd",
+"Lower Greek": "\u5c0f\u5199\u5e0c\u814a\u5b57\u6bcd",
+"Lower Roman": "\u5c0f\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Upper Alpha": "\u5927\u5199\u82f1\u6587\u5b57\u6bcd",
+"Upper Roman": "\u5927\u5199\u7f57\u9a6c\u5b57\u6bcd",
+"Anchor...": "\u951a\u70b9...",
+"Name": "\u540d\u79f0",
+"Id": "\u6807\u8bc6\u7b26",
+"Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.": "\u6807\u8bc6\u7b26\u5e94\u8be5\u4ee5\u5b57\u6bcd\u5f00\u5934\uff0c\u540e\u8ddf\u5b57\u6bcd\u3001\u6570\u5b57\u3001\u7834\u6298\u53f7\u3001\u70b9\u3001\u5192\u53f7\u6216\u4e0b\u5212\u7ebf\u3002",
+"You have unsaved changes are you sure you want to navigate away?": "\u4f60\u8fd8\u6709\u6587\u6863\u5c1a\u672a\u4fdd\u5b58\uff0c\u786e\u5b9a\u8981\u79bb\u5f00\uff1f",
+"Restore last draft": "\u6062\u590d\u4e0a\u6b21\u7684\u8349\u7a3f",
+"Special characters...": "\u7279\u6b8a\u5b57\u7b26...",
+"Source code": "\u6e90\u4ee3\u7801",
+"Insert\/Edit code sample": "\u63d2\u5165\/\u7f16\u8f91\u4ee3\u7801\u793a\u4f8b",
+"Language": "\u8bed\u8a00",
+"Code sample...": "\u793a\u4f8b\u4ee3\u7801...",
+"Color Picker": "\u9009\u8272\u5668",
+"R": "R",
+"G": "G",
+"B": "B",
+"Left to right": "\u4ece\u5de6\u5230\u53f3",
+"Right to left": "\u4ece\u53f3\u5230\u5de6",
+"Emoticons...": "\u8868\u60c5\u7b26\u53f7...",
+"Metadata and Document Properties": "\u5143\u6570\u636e\u548c\u6587\u6863\u5c5e\u6027",
+"Title": "\u6807\u9898",
+"Keywords": "\u5173\u952e\u8bcd",
+"Description": "\u63cf\u8ff0",
+"Robots": "\u673a\u5668\u4eba",
+"Author": "\u4f5c\u8005",
+"Encoding": "\u7f16\u7801",
+"Fullscreen": "\u5168\u5c4f",
+"Action": "\u64cd\u4f5c",
+"Shortcut": "\u5feb\u6377\u952e",
+"Help": "\u5e2e\u52a9",
+"Address": "\u5730\u5740",
+"Focus to menubar": "\u79fb\u52a8\u7126\u70b9\u5230\u83dc\u5355\u680f",
+"Focus to toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u5de5\u5177\u680f",
+"Focus to element path": "\u79fb\u52a8\u7126\u70b9\u5230\u5143\u7d20\u8def\u5f84",
+"Focus to contextual toolbar": "\u79fb\u52a8\u7126\u70b9\u5230\u4e0a\u4e0b\u6587\u83dc\u5355",
+"Insert link (if link plugin activated)": "\u63d2\u5165\u94fe\u63a5 (\u5982\u679c\u94fe\u63a5\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Save (if save plugin activated)": "\u4fdd\u5b58(\u5982\u679c\u4fdd\u5b58\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Find (if searchreplace plugin activated)": "\u67e5\u627e(\u5982\u679c\u67e5\u627e\u66ff\u6362\u63d2\u4ef6\u5df2\u6fc0\u6d3b)",
+"Plugins installed ({0}):": "\u5df2\u5b89\u88c5\u63d2\u4ef6 ({0}):",
+"Premium plugins:": "\u4f18\u79c0\u63d2\u4ef6\uff1a",
+"Learn more...": "\u4e86\u89e3\u66f4\u591a...",
+"You are using {0}": "\u4f60\u6b63\u5728\u4f7f\u7528 {0}",
+"Plugins": "\u63d2\u4ef6",
+"Handy Shortcuts": "\u5feb\u6377\u952e",
+"Horizontal line": "\u6c34\u5e73\u5206\u5272\u7ebf",
+"Insert\/edit image": "\u63d2\u5165\/\u7f16\u8f91\u56fe\u7247",
+"Image description": "\u56fe\u7247\u63cf\u8ff0",
+"Source": "\u5730\u5740",
+"Dimensions": "\u5927\u5c0f",
+"Constrain proportions": "\u4fdd\u6301\u7eb5\u6a2a\u6bd4",
+"General": "\u666e\u901a",
+"Advanced": "\u9ad8\u7ea7",
+"Style": "\u6837\u5f0f",
+"Vertical space": "\u5782\u76f4\u8fb9\u8ddd",
+"Horizontal space": "\u6c34\u5e73\u8fb9\u8ddd",
+"Border": "\u8fb9\u6846",
+"Insert image": "\u63d2\u5165\u56fe\u7247",
+"Image...": "\u56fe\u7247...",
+"Image list": "\u56fe\u7247\u5217\u8868",
+"Rotate counterclockwise": "\u9006\u65f6\u9488\u65cb\u8f6c",
+"Rotate clockwise": "\u987a\u65f6\u9488\u65cb\u8f6c",
+"Flip vertically": "\u5782\u76f4\u7ffb\u8f6c",
+"Flip horizontally": "\u6c34\u5e73\u7ffb\u8f6c",
+"Edit image": "\u7f16\u8f91\u56fe\u7247",
+"Image options": "\u56fe\u7247\u9009\u9879",
+"Zoom in": "\u653e\u5927",
+"Zoom out": "\u7f29\u5c0f",
+"Crop": "\u88c1\u526a",
+"Resize": "\u8c03\u6574\u5927\u5c0f",
+"Orientation": "\u65b9\u5411",
+"Brightness": "\u4eae\u5ea6",
+"Sharpen": "\u9510\u5316",
+"Contrast": "\u5bf9\u6bd4\u5ea6",
+"Color levels": "\u989c\u8272\u5c42\u6b21",
+"Gamma": "\u4f3d\u9a6c\u503c",
+"Invert": "\u53cd\u8f6c",
+"Apply": "\u5e94\u7528",
+"Back": "\u540e\u9000",
+"Insert date\/time": "\u63d2\u5165\u65e5\u671f\/\u65f6\u95f4",
+"Date\/time": "\u65e5\u671f\/\u65f6\u95f4",
+"Insert\/Edit Link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Insert\/edit link": "\u63d2\u5165\/\u7f16\u8f91\u94fe\u63a5",
+"Text to display": "\u663e\u793a\u6587\u5b57",
+"Url": "\u5730\u5740",
+"Open link in...": "\u94fe\u63a5\u6253\u5f00\u4f4d\u7f6e...",
+"Current window": "\u5f53\u524d\u7a97\u53e3",
+"None": "\u65e0",
+"New window": "\u5728\u65b0\u7a97\u53e3\u6253\u5f00",
+"Remove link": "\u5220\u9664\u94fe\u63a5",
+"Anchors": "\u951a\u70b9",
+"Link...": "\u94fe\u63a5...",
+"Paste or type a link": "\u7c98\u8d34\u6216\u8f93\u5165\u94fe\u63a5",
+"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u4e3a\u90ae\u4ef6\u5730\u5740\uff0c\u9700\u8981\u52a0\u4e0amailto:\u524d\u7f00\u5417\uff1f",
+"The URL you entered seems to be an external link. Do you want to add the required http:\/\/ prefix?": "\u4f60\u6240\u586b\u5199\u7684URL\u5730\u5740\u5c5e\u4e8e\u5916\u90e8\u94fe\u63a5\uff0c\u9700\u8981\u52a0\u4e0ahttp:\/\/:\u524d\u7f00\u5417\uff1f",
+"Link list": "\u94fe\u63a5\u5217\u8868",
+"Insert video": "\u63d2\u5165\u89c6\u9891",
+"Insert\/edit video": "\u63d2\u5165\/\u7f16\u8f91\u89c6\u9891",
+"Insert\/edit media": "\u63d2\u5165\/\u7f16\u8f91\u5a92\u4f53",
+"Alternative source": "\u955c\u50cf",
+"Alternative source URL": "\u66ff\u4ee3\u6765\u6e90\u7f51\u5740",
+"Media poster (Image URL)": "\u5c01\u9762(\u56fe\u7247\u5730\u5740)",
+"Paste your embed code below:": "\u5c06\u5185\u5d4c\u4ee3\u7801\u7c98\u8d34\u5728\u4e0b\u9762:",
+"Embed": "\u5185\u5d4c",
+"Media...": "\u591a\u5a92\u4f53...",
+"Nonbreaking space": "\u4e0d\u95f4\u65ad\u7a7a\u683c",
+"Page break": "\u5206\u9875\u7b26",
+"Paste as text": "\u7c98\u8d34\u4e3a\u6587\u672c",
+"Preview": "\u9884\u89c8",
+"Print...": "\u6253\u5370...",
+"Save": "\u4fdd\u5b58",
+"Find": "\u67e5\u627e",
+"Replace with": "\u66ff\u6362\u4e3a",
+"Replace": "\u66ff\u6362",
+"Replace all": "\u5168\u90e8\u66ff\u6362",
+"Previous": "\u4e0a\u4e00\u4e2a",
+"Next": "\u4e0b\u4e00\u4e2a",
+"Find and replace...": "\u67e5\u627e\u5e76\u66ff\u6362...",
+"Could not find the specified string.": "\u672a\u627e\u5230\u641c\u7d22\u5185\u5bb9.",
+"Match case": "\u533a\u5206\u5927\u5c0f\u5199",
+"Find whole words only": "\u5168\u5b57\u5339\u914d",
+"Spell check": "\u62fc\u5199\u68c0\u67e5",
+"Ignore": "\u5ffd\u7565",
+"Ignore all": "\u5168\u90e8\u5ffd\u7565",
+"Finish": "\u5b8c\u6210",
+"Add to Dictionary": "\u6dfb\u52a0\u5230\u5b57\u5178",
+"Insert table": "\u63d2\u5165\u8868\u683c",
+"Table properties": "\u8868\u683c\u5c5e\u6027",
+"Delete table": "\u5220\u9664\u8868\u683c",
+"Cell": "\u5355\u5143\u683c",
+"Row": "\u884c",
+"Column": "\u5217",
+"Cell properties": "\u5355\u5143\u683c\u5c5e\u6027",
+"Merge cells": "\u5408\u5e76\u5355\u5143\u683c",
+"Split cell": "\u62c6\u5206\u5355\u5143\u683c",
+"Insert row before": "\u5728\u4e0a\u65b9\u63d2\u5165",
+"Insert row after": "\u5728\u4e0b\u65b9\u63d2\u5165",
+"Delete row": "\u5220\u9664\u884c",
+"Row properties": "\u884c\u5c5e\u6027",
+"Cut row": "\u526a\u5207\u884c",
+"Copy row": "\u590d\u5236\u884c",
+"Paste row before": "\u7c98\u8d34\u5230\u4e0a\u65b9",
+"Paste row after": "\u7c98\u8d34\u5230\u4e0b\u65b9",
+"Insert column before": "\u5728\u5de6\u4fa7\u63d2\u5165",
+"Insert column after": "\u5728\u53f3\u4fa7\u63d2\u5165",
+"Delete column": "\u5220\u9664\u5217",
+"Cols": "\u5217",
+"Rows": "\u884c",
+"Width": "\u5bbd",
+"Height": "\u9ad8",
+"Cell spacing": "\u5355\u5143\u683c\u5916\u95f4\u8ddd",
+"Cell padding": "\u5355\u5143\u683c\u5185\u8fb9\u8ddd",
+"Show caption": "\u663e\u793a\u6807\u9898",
+"Left": "\u5de6\u5bf9\u9f50",
+"Center": "\u5c45\u4e2d",
+"Right": "\u53f3\u5bf9\u9f50",
+"Cell type": "\u5355\u5143\u683c\u7c7b\u578b",
+"Scope": "\u8303\u56f4",
+"Alignment": "\u5bf9\u9f50\u65b9\u5f0f",
+"H Align": "\u6c34\u5e73\u5bf9\u9f50",
+"V Align": "\u5782\u76f4\u5bf9\u9f50",
+"Top": "\u9876\u90e8\u5bf9\u9f50",
+"Middle": "\u5782\u76f4\u5c45\u4e2d",
+"Bottom": "\u5e95\u90e8\u5bf9\u9f50",
+"Header cell": "\u8868\u5934\u5355\u5143\u683c",
+"Row group": "\u884c\u7ec4",
+"Column group": "\u5217\u7ec4",
+"Row type": "\u884c\u7c7b\u578b",
+"Header": "\u8868\u5934",
+"Body": "\u8868\u4f53",
+"Footer": "\u8868\u5c3e",
+"Border color": "\u8fb9\u6846\u989c\u8272",
+"Insert template...": "\u63d2\u5165\u6a21\u677f...",
+"Templates": "\u6a21\u677f",
+"Template": "\u6a21\u677f",
+"Text color": "\u6587\u5b57\u989c\u8272",
+"Background color": "\u80cc\u666f\u8272",
+"Custom...": "\u81ea\u5b9a\u4e49...",
+"Custom color": "\u81ea\u5b9a\u4e49\u989c\u8272",
+"No color": "\u65e0",
+"Remove color": "\u79fb\u9664\u989c\u8272",
+"Table of Contents": "\u5185\u5bb9\u5217\u8868",
+"Show blocks": "\u663e\u793a\u533a\u5757\u8fb9\u6846",
+"Show invisible characters": "\u663e\u793a\u4e0d\u53ef\u89c1\u5b57\u7b26",
+"Word count": "\u5b57\u6570",
+"Words: {0}": "\u5b57\u6570\uff1a{0}",
+"{0} words": "{0} \u5b57",
+"File": "\u6587\u4ef6",
+"Edit": "\u7f16\u8f91",
+"Insert": "\u63d2\u5165",
+"View": "\u89c6\u56fe",
+"Format": "\u683c\u5f0f",
+"Table": "\u8868\u683c",
+"Tools": "\u5de5\u5177",
+"Powered by {0}": "\u7531{0}\u9a71\u52a8",
+"Rich Text Area. Press ALT-F9 for menu. Press ALT-F10 for toolbar. Press ALT-0 for help": "\u5728\u7f16\u8f91\u533a\u6309ALT-F9\u6253\u5f00\u83dc\u5355\uff0c\u6309ALT-F10\u6253\u5f00\u5de5\u5177\u680f\uff0c\u6309ALT-0\u67e5\u770b\u5e2e\u52a9",
+"Image title": "\u56fe\u7247\u6807\u9898",
+"Border width": "\u8fb9\u6846\u5bbd\u5ea6",
+"Border style": "\u8fb9\u6846\u6837\u5f0f",
+"Error": "\u9519\u8bef",
+"Warn": "\u8b66\u544a",
+"Valid": "\u6709\u6548",
+"To open the popup, press Shift+Enter": "\u6309Shitf+Enter\u952e\u6253\u5f00\u5bf9\u8bdd\u6846",
+"Rich Text Area. Press ALT-0 for help.": "\u7f16\u8f91\u533a\u3002\u6309Alt+0\u952e\u6253\u5f00\u5e2e\u52a9\u3002",
+"System Font": "\u7cfb\u7edf\u5b57\u4f53",
+"Failed to upload image: {0}": "\u56fe\u7247\u4e0a\u4f20\u5931\u8d25: {0}",
+"Failed to load plugin: {0} from url {1}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25: {0} \u6765\u81ea\u94fe\u63a5 {1}",
+"Failed to load plugin url: {0}": "\u63d2\u4ef6\u52a0\u8f7d\u5931\u8d25 \u94fe\u63a5: {0}",
+"Failed to initialize plugin: {0}": "\u63d2\u4ef6\u521d\u59cb\u5316\u5931\u8d25: {0}",
+"example": "\u793a\u4f8b",
+"Search": "\u641c\u7d22",
+"All": "\u5168\u90e8",
+"Currency": "\u8d27\u5e01",
+"Text": "\u6587\u5b57",
+"Quotations": "\u5f15\u7528",
+"Mathematical": "\u6570\u5b66",
+"Extended Latin": "\u62c9\u4e01\u8bed\u6269\u5145",
+"Symbols": "\u7b26\u53f7",
+"Arrows": "\u7bad\u5934",
+"User Defined": "\u81ea\u5b9a\u4e49",
+"dollar sign": "\u7f8e\u5143\u7b26\u53f7",
+"currency sign": "\u8d27\u5e01\u7b26\u53f7",
+"euro-currency sign": "\u6b27\u5143\u7b26\u53f7",
+"colon sign": "\u5192\u53f7",
+"cruzeiro sign": "\u514b\u9c81\u8d5b\u7f57\u5e01\u7b26\u53f7",
+"french franc sign": "\u6cd5\u90ce\u7b26\u53f7",
+"lira sign": "\u91cc\u62c9\u7b26\u53f7",
+"mill sign": "\u5bc6\u5c14\u7b26\u53f7",
+"naira sign": "\u5948\u62c9\u7b26\u53f7",
+"peseta sign": "\u6bd4\u585e\u5854\u7b26\u53f7",
+"rupee sign": "\u5362\u6bd4\u7b26\u53f7",
+"won sign": "\u97e9\u5143\u7b26\u53f7",
+"new sheqel sign": "\u65b0\u8c22\u514b\u5c14\u7b26\u53f7",
+"dong sign": "\u8d8a\u5357\u76fe\u7b26\u53f7",
+"kip sign": "\u8001\u631d\u57fa\u666e\u7b26\u53f7",
+"tugrik sign": "\u56fe\u683c\u91cc\u514b\u7b26\u53f7",
+"drachma sign": "\u5fb7\u62c9\u514b\u9a6c\u7b26\u53f7",
+"german penny symbol": "\u5fb7\u56fd\u4fbf\u58eb\u7b26\u53f7",
+"peso sign": "\u6bd4\u7d22\u7b26\u53f7",
+"guarani sign": "\u74dc\u62c9\u5c3c\u7b26\u53f7",
+"austral sign": "\u6fb3\u5143\u7b26\u53f7",
+"hryvnia sign": "\u683c\u91cc\u592b\u5c3c\u4e9a\u7b26\u53f7",
+"cedi sign": "\u585e\u5730\u7b26\u53f7",
+"livre tournois sign": "\u91cc\u5f17\u5f17\u5c14\u7b26\u53f7",
+"spesmilo sign": "spesmilo\u7b26\u53f7",
+"tenge sign": "\u575a\u6208\u7b26\u53f7",
+"indian rupee sign": "\u5370\u5ea6\u5362\u6bd4",
+"turkish lira sign": "\u571f\u8033\u5176\u91cc\u62c9",
+"nordic mark sign": "\u5317\u6b27\u9a6c\u514b",
+"manat sign": "\u9a6c\u7eb3\u7279\u7b26\u53f7",
+"ruble sign": "\u5362\u5e03\u7b26\u53f7",
+"yen character": "\u65e5\u5143\u5b57\u6837",
+"yuan character": "\u4eba\u6c11\u5e01\u5143\u5b57\u6837",
+"yuan character, in hong kong and taiwan": "\u5143\u5b57\u6837\uff08\u6e2f\u53f0\u5730\u533a\uff09",
+"yen\/yuan character variant one": "\u5143\u5b57\u6837\uff08\u5927\u5199\uff09",
+"Loading emoticons...": "\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7...",
+"Could not load emoticons": "\u4e0d\u80fd\u52a0\u8f7d\u8868\u60c5\u7b26\u53f7",
+"People": "\u4eba\u7c7b",
+"Animals and Nature": "\u52a8\u7269\u548c\u81ea\u7136",
+"Food and Drink": "\u98df\u7269\u548c\u996e\u54c1",
+"Activity": "\u6d3b\u52a8",
+"Travel and Places": "\u65c5\u6e38\u548c\u5730\u70b9",
+"Objects": "\u7269\u4ef6",
+"Flags": "\u65d7\u5e1c",
+"Characters": "\u5b57\u7b26",
+"Characters (no spaces)": "\u5b57\u7b26(\u65e0\u7a7a\u683c)",
+"Error: Form submit field collision.": "\u9519\u8bef: \u8868\u5355\u63d0\u4ea4\u5b57\u6bb5\u51b2\u7a81\u3002",
+"Error: No form element found.": "\u9519\u8bef: \u6ca1\u6709\u8868\u5355\u63a7\u4ef6\u3002",
+"Update": "\u66f4\u65b0",
+"Color swatch": "\u989c\u8272\u6837\u672c",
+"Turquoise": "\u9752\u7eff\u8272",
+"Green": "\u7eff\u8272",
+"Blue": "\u84dd\u8272",
+"Purple": "\u7d2b\u8272",
+"Navy Blue": "\u6d77\u519b\u84dd",
+"Dark Turquoise": "\u6df1\u84dd\u7eff\u8272",
+"Dark Green": "\u6df1\u7eff\u8272",
+"Medium Blue": "\u4e2d\u84dd\u8272",
+"Medium Purple": "\u4e2d\u7d2b\u8272",
+"Midnight Blue": "\u6df1\u84dd\u8272",
+"Yellow": "\u9ec4\u8272",
+"Orange": "\u6a59\u8272",
+"Red": "\u7ea2\u8272",
+"Light Gray": "\u6d45\u7070\u8272",
+"Gray": "\u7070\u8272",
+"Dark Yellow": "\u6697\u9ec4\u8272",
+"Dark Orange": "\u6df1\u6a59\u8272",
+"Dark Red": "\u6df1\u7ea2\u8272",
+"Medium Gray": "\u4e2d\u7070\u8272",
+"Dark Gray": "\u6df1\u7070\u8272",
+"Black": "\u9ed1\u8272",
+"White": "\u767d\u8272",
+"Switch to or from fullscreen mode": "\u5207\u6362\u5168\u5c4f\u6a21\u5f0f",
+"Open help dialog": "\u6253\u5f00\u5e2e\u52a9\u5bf9\u8bdd\u6846",
+"history": "\u5386\u53f2",
+"styles": "\u6837\u5f0f",
+"formatting": "\u683c\u5f0f\u5316",
+"alignment": "\u5bf9\u9f50",
+"indentation": "\u7f29\u8fdb",
+"permanent pen": "\u8bb0\u53f7\u7b14",
+"comments": "\u5907\u6ce8",
+"Anchor": "\u951a\u70b9",
+"Special character": "\u7279\u6b8a\u7b26\u53f7",
+"Code sample": "\u4ee3\u7801\u793a\u4f8b",
+"Color": "\u989c\u8272",
+"Emoticons": "\u8868\u60c5",
+"Document properties": "\u6587\u6863\u5c5e\u6027",
+"Image": "\u56fe\u7247",
+"Insert link": "\u63d2\u5165\u94fe\u63a5",
+"Target": "\u6253\u5f00\u65b9\u5f0f",
+"Link": "\u94fe\u63a5",
+"Poster": "\u5c01\u9762",
+"Media": "\u5a92\u4f53",
+"Print": "\u6253\u5370",
+"Prev": "\u4e0a\u4e00\u4e2a",
+"Find and replace": "\u67e5\u627e\u548c\u66ff\u6362",
+"Whole words": "\u5168\u5b57\u5339\u914d",
+"Spellcheck": "\u62fc\u5199\u68c0\u67e5",
+"Caption": "\u6807\u9898",
+"Insert template": "\u63d2\u5165\u6a21\u677f"
+});
\ No newline at end of file
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css b/public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
new file mode 100644
index 0000000..b4ab9a3
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/content.inline.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/content.min.css b/public/resource/tinymce/skins/ui/oxide-dark/content.min.css
new file mode 100644
index 0000000..e27b8a0
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/content.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%236d737b%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#f8f8f2;background:0 0;text-shadow:0 1px rgba(0,0,0,.3);font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto;border-radius:.3em}:not(pre)>code[class*=language-],pre[class*=language-]{background:#282a36}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#6272a4}.token.punctuation{color:#f8f8f2}.namespace{opacity:.7}.token.constant,.token.deleted,.token.property,.token.symbol,.token.tag{color:#ff79c6}.token.boolean,.token.number{color:#bd93f9}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#50fa7b}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url,.token.variable{color:#f8f8f2}.token.atrule,.token.attr-value,.token.class-name,.token.function{color:#f1fa8c}.token.keyword{color:#8be9fd}.token.important,.token.regex{color:#ffb86c}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%20fill%3D%22%23cccccc%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #4099ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #4099ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #4099ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #4099ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #4099ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#4099ff}.mce-content-body .mce-edit-focus{outline:3px solid #4099ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid transparent;bottom:-1px;content:'';left:-1px;mix-blend-mode:lighten;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#4099ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse}
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css b/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css
new file mode 100644
index 0000000..35f7dc0
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/content.mobile.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
new file mode 100644
index 0000000..e71f6f0
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tox{box-shadow:none;box-sizing:content-box;color:#2a3746;cursor:auto;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-style:normal;font-weight:400;line-height:normal;-webkit-tap-highlight-color:transparent;text-decoration:none;text-shadow:none;text-transform:none;vertical-align:initial;white-space:normal}.tox :not(svg):not(rect){box-sizing:inherit;color:inherit;cursor:inherit;direction:inherit;font-family:inherit;font-size:inherit;font-style:inherit;font-weight:inherit;line-height:inherit;-webkit-tap-highlight-color:inherit;text-align:inherit;text-decoration:inherit;text-shadow:inherit;text-transform:inherit;vertical-align:inherit;white-space:inherit}.tox :not(svg):not(rect){background:0 0;border:0;box-shadow:none;float:none;height:auto;margin:0;max-width:none;outline:0;padding:0;position:static;width:auto}.tox:not([dir=rtl]){direction:ltr;text-align:left}.tox[dir=rtl]{direction:rtl;text-align:right}.tox-tinymce{border:1px solid #000;border-radius:0;box-shadow:none;box-sizing:border-box;display:flex;flex-direction:column;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;overflow:hidden;position:relative;visibility:inherit!important}.tox-tinymce-inline{border:none;box-shadow:none}.tox-tinymce-inline .tox-editor-header{background-color:transparent;border:1px solid #000;border-radius:0;box-shadow:none}.tox-tinymce-aux{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;z-index:1300}.tox-tinymce :focus,.tox-tinymce-aux :focus{outline:0}button::-moz-focus-inner{border:0}.tox[dir=rtl] .tox-icon--flip svg{transform:rotateY(180deg)}.tox .accessibility-issue__header{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description{align-items:stretch;border:1px solid #000;border-radius:3px;display:flex;justify-content:space-between}.tox .accessibility-issue__description>div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#000;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.5);border-color:#207ab7;color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.5);border-color:rgba(255,165,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.5);border-color:rgba(204,0,0,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.8)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.5);border-color:rgba(120,171,70,.8);color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.8)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#fff}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#fff}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#fff}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#3d546f;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#3d546f;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#fff;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#3d546f;background-image:none;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:hover:not(:disabled){background-color:#34485f;background-image:none;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--secondary:active:not(:disabled){background-color:#2b3b4e;background-image:none;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#fff}.tox .tox-button--naked[disabled]{background-color:#3d546f;border-color:#3d546f;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:focus:not(:disabled){background-color:#34485f;border-color:#34485f;box-shadow:none;color:#fff}.tox .tox-button--naked:active:not(:disabled){background-color:#2b3b4e;border-color:#2b3b4e;box-shadow:none;color:#fff}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#fff}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(255,255,255,.2)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(255,255,255,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(255,255,255,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#1a1a1a;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#333;color:#fff;cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#fff;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#2b3b4e;color:#fff}.tox .tox-collection--list .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#4a5562}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#757d87;color:#fff}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#4a5562;color:#fff}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#fff}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(255,255,255,.5);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#fff}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg{fill:rgba(255,255,255,.5)}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#2b3b4e;border:1px solid #1a1a1a;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #000}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #000}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#4a5562}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#2b3b4e;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#fff;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(255,255,255,.5);font-size:12px}.tox .tox-comment__body{color:#fff;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(255,255,255,.5);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#2b3b4e;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(43,59,78,0),#2b3b4e);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#2b3b4e;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#fff;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#2b3b4e;box-shadow:0 0 8px 8px #2b3b4e;color:#fff;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#2b3b4e;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(255,255,255,.5)}.tox .tox-user__name{color:rgba(255,255,255,.5);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(34,47,62,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#222f3e}.tox .tox-dialog{background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(42,55,70,.15),0 0 40px 1px rgba(42,55,70,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#2b3b4e;border-bottom:none;color:#fff;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#fff;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(255,255,255,.5);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#fff}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#fff;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#fff;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#2b3b4e;border-top:1px solid #000;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(34,47,62,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #000}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #000;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(255,255,255,.5);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #000}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(42,55,70,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(255,255,255,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(255,255,255,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(255,255,255,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #2b3b4e;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(255,255,255,.5);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#222f3e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#fff}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#fff}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#2b3b4e;border-color:#000;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#19232e;color:rgba(255,255,255,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#2b3b4e;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#000;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:#fff;display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#2b3b4e;border:1px solid #000;border-radius:3px;box-shadow:0 4px 8px 0 rgba(42,55,70,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #000}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn--active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{cursor:pointer;text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#fff}.tox .tox-notification--success p{color:#fff}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#fff}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#fff}.tox .tox-notification--error p{color:#fff}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#fff}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#fff}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#fff}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#fff}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#fff}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#fff}.tox .tox-notification--info p{color:#fff}.tox .tox-notification--info a{color:#fff}.tox .tox-notification--info svg{fill:#fff}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#fff;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar,.tox .tox-pop--resizing .tox-toolbar__group{flex-wrap:nowrap}.tox .tox-pop--transition{transition:.15s ease;transition-property:left,right,top,bottom}.tox .tox-pop--transition::after,.tox .tox-pop--transition::before{transition:all .15s,visibility 0s,opacity 75ms ease 75ms}.tox .tox-pop__dialog{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;opacity:1;position:absolute;width:0}.tox .tox-pop.tox-pop--inset::after,.tox .tox-pop.tox-pop--inset::before{opacity:0;transition:all 0s .15s,visibility 0s,opacity 75ms ease}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#222f3e transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#000 transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #222f3e transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #000 transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #222f3e transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #000 transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #222f3e;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #000;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#222f3e;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #000;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(255,255,255,.5);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#222f3e;border-top:1px solid #000;color:#fff;display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:#fff;text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:#fff}.tox .tox-statusbar__resize-handle:focus svg{background-color:#4a5562;border-radius:1px;box-shadow:0 0 0 2px #4a5562}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(34,47,62,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#fff;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#fff}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#4a5562;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#4a5562;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:hover svg{fill:#fff}.tox .tox-tbtn:active{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn:active svg{fill:#fff}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#757d87;border:0;box-shadow:none;color:#fff}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#fff}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#fff}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #4a5562 inset}.tox .tox-split-button:focus{background:#4a5562;box-shadow:none;color:#fff}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(255,255,255,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(255,255,255,.5)}.tox .tox-toolbar-overlord{background-color:#222f3e}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23000000'/%3E%3C/svg%3E") left 0 top 0 #222f3e;background-color:#222f3e;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #000;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #000}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#222f3e;border:1px solid #000;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #000}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #000}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#3d546f;border-radius:3px;box-shadow:0 2px 4px rgba(42,55,70,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #3d546f;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #3d546f;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #3d546f;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #3d546f;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #000;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #000;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px}
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
new file mode 100644
index 0000000..3a45cac
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.mobile.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}}
diff --git a/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css b/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
new file mode 100644
index 0000000..a0893b9
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide-dark/skin.shadowdom.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
diff --git a/public/resource/tinymce/skins/ui/oxide/content.inline.min.css b/public/resource/tinymce/skins/ui/oxide/content.inline.min.css
new file mode 100644
index 0000000..b4ab9a3
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide/content.inline.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}
diff --git a/public/resource/tinymce/skins/ui/oxide/content.min.css b/public/resource/tinymce/skins/ui/oxide/content.min.css
new file mode 100644
index 0000000..844858d
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide/content.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.mce-content-body .mce-item-anchor{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'8'%20height%3D'12'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20d%3D'M0%200L8%200%208%2012%204.09117821%209%200%2012z'%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;cursor:default;display:inline-block;height:12px!important;padding:0 2px;-webkit-user-modify:read-only;-moz-user-modify:read-only;-webkit-user-select:all;-moz-user-select:all;-ms-user-select:all;user-select:all;width:8px!important}.mce-content-body .mce-item-anchor[data-mce-selected]{outline-offset:1px}.tox-comments-visible .tox-comment{background-color:#fff0b7}.tox-comments-visible .tox-comment--active{background-color:#ffe168}.tox-checklist>li:not(.tox-checklist--hidden){list-style:none;margin:.25em 0}.tox-checklist>li:not(.tox-checklist--hidden)::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-unchecked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2215%22%20height%3D%2215%22%20x%3D%22.5%22%20y%3D%22.5%22%20fill-rule%3D%22nonzero%22%20stroke%3D%22%234C4C4C%22%20rx%3D%222%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A");cursor:pointer;height:1em;margin-left:-1.5em;margin-top:.125em;position:absolute;width:1em}.tox-checklist li:not(.tox-checklist--hidden).tox-checklist--checked::before{content:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2216%22%20height%3D%2216%22%20viewBox%3D%220%200%2016%2016%22%3E%3Cg%20id%3D%22checklist-checked%22%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%3Crect%20id%3D%22Rectangle%22%20width%3D%2216%22%20height%3D%2216%22%20fill%3D%22%234099FF%22%20fill-rule%3D%22nonzero%22%20rx%3D%222%22%2F%3E%3Cpath%20id%3D%22Path%22%20fill%3D%22%23FFF%22%20fill-rule%3D%22nonzero%22%20d%3D%22M11.5703186%2C3.14417309%20C11.8516238%2C2.73724603%2012.4164781%2C2.62829933%2012.83558%2C2.89774797%20C13.260121%2C3.17069355%2013.3759736%2C3.72932262%2013.0909105%2C4.14168582%20L7.7580587%2C11.8560195%20C7.43776896%2C12.3193404%206.76483983%2C12.3852142%206.35607322%2C11.9948725%20L3.02491697%2C8.8138662%20C2.66090143%2C8.46625845%202.65798871%2C7.89594698%203.01850234%2C7.54483354%20C3.373942%2C7.19866177%203.94940006%2C7.19592841%204.30829608%2C7.5386474%20L6.85276923%2C9.9684299%20L11.5703186%2C3.14417309%20Z%22%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E%0A")}[dir=rtl] .tox-checklist>li:not(.tox-checklist--hidden)::before{margin-left:0;margin-right:-1.5em}code[class*=language-],pre[class*=language-]{color:#000;background:0 0;text-shadow:0 1px #fff;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;font-size:1em;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;tab-size:4;-webkit-hyphens:none;-ms-hyphens:none;hyphens:none}code[class*=language-] ::-moz-selection,code[class*=language-]::-moz-selection,pre[class*=language-] ::-moz-selection,pre[class*=language-]::-moz-selection{text-shadow:none;background:#b3d4fc}code[class*=language-] ::selection,code[class*=language-]::selection,pre[class*=language-] ::selection,pre[class*=language-]::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*=language-],pre[class*=language-]{text-shadow:none}}pre[class*=language-]{padding:1em;margin:.5em 0;overflow:auto}:not(pre)>code[class*=language-],pre[class*=language-]{background:#f5f2f0}:not(pre)>code[class*=language-]{padding:.1em;border-radius:.3em;white-space:normal}.token.cdata,.token.comment,.token.doctype,.token.prolog{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.boolean,.token.constant,.token.deleted,.token.number,.token.property,.token.symbol,.token.tag{color:#905}.token.attr-name,.token.builtin,.token.char,.token.inserted,.token.selector,.token.string{color:#690}.language-css .token.string,.style .token.string,.token.entity,.token.operator,.token.url{color:#9a6e3a;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.class-name,.token.function{color:#dd4a68}.token.important,.token.regex,.token.variable{color:#e90}.token.bold,.token.important{font-weight:700}.token.italic{font-style:italic}.token.entity{cursor:help}.mce-content-body{overflow-wrap:break-word;word-wrap:break-word}.mce-content-body .mce-visual-caret{background-color:#000;background-color:currentColor;position:absolute}.mce-content-body .mce-visual-caret-hidden{display:none}.mce-content-body [data-mce-caret]{left:-1000px;margin:0;padding:0;position:absolute;right:auto;top:0}.mce-content-body .mce-offscreen-selection{left:-2000000px;max-width:1000000px;position:absolute}.mce-content-body [contentEditable=false]{cursor:default}.mce-content-body [contentEditable=true]{cursor:text}.tox-cursor-format-painter{cursor:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%3E%0A%20%20%3Cg%20fill%3D%22none%22%20fill-rule%3D%22evenodd%22%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M15%2C6%20C15%2C5.45%2014.55%2C5%2014%2C5%20L6%2C5%20C5.45%2C5%205%2C5.45%205%2C6%20L5%2C10%20C5%2C10.55%205.45%2C11%206%2C11%20L14%2C11%20C14.55%2C11%2015%2C10.55%2015%2C10%20L15%2C9%20L16%2C9%20L16%2C12%20L9%2C12%20L9%2C19%20C9%2C19.55%209.45%2C20%2010%2C20%20L11%2C20%20C11.55%2C20%2012%2C19.55%2012%2C19%20L12%2C14%20L18%2C14%20L18%2C7%20L15%2C7%20L15%2C6%20Z%22%2F%3E%0A%20%20%20%20%3Cpath%20fill%3D%22%23000%22%20fill-rule%3D%22nonzero%22%20d%3D%22M1%2C1%20L8.25%2C1%20C8.66421356%2C1%209%2C1.33578644%209%2C1.75%20L9%2C1.75%20C9%2C2.16421356%208.66421356%2C2.5%208.25%2C2.5%20L2.5%2C2.5%20L2.5%2C8.25%20C2.5%2C8.66421356%202.16421356%2C9%201.75%2C9%20L1.75%2C9%20C1.33578644%2C9%201%2C8.66421356%201%2C8.25%20L1%2C1%20Z%22%2F%3E%0A%20%20%3C%2Fg%3E%0A%3C%2Fsvg%3E%0A"),default}.mce-content-body figure.align-left{float:left}.mce-content-body figure.align-right{float:right}.mce-content-body figure.image.align-center{display:table;margin-left:auto;margin-right:auto}.mce-preview-object{border:1px solid gray;display:inline-block;line-height:0;margin:0 2px 0 2px;position:relative}.mce-preview-object .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.mce-preview-object[data-mce-selected="2"] .mce-shim{display:none}.mce-object{background:transparent url("data:image/svg+xml;charset=UTF-8,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%3E%3Cpath%20d%3D%22M4%203h16a1%201%200%200%201%201%201v16a1%201%200%200%201-1%201H4a1%201%200%200%201-1-1V4a1%201%200%200%201%201-1zm1%202v14h14V5H5zm4.79%202.565l5.64%204.028a.5.5%200%200%201%200%20.814l-5.64%204.028a.5.5%200%200%201-.79-.407V7.972a.5.5%200%200%201%20.79-.407z%22%2F%3E%3C%2Fsvg%3E%0A") no-repeat center;border:1px dashed #aaa}.mce-pagebreak{border:1px dashed #aaa;cursor:default;display:block;height:5px;margin-top:15px;page-break-before:always;width:100%}@media print{.mce-pagebreak{border:0}}.tiny-pageembed .mce-shim{background:url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7);height:100%;left:0;position:absolute;top:0;width:100%}.tiny-pageembed[data-mce-selected="2"] .mce-shim{display:none}.tiny-pageembed{display:inline-block;position:relative}.tiny-pageembed--16by9,.tiny-pageembed--1by1,.tiny-pageembed--21by9,.tiny-pageembed--4by3{display:block;overflow:hidden;padding:0;position:relative;width:100%}.tiny-pageembed--21by9{padding-top:42.857143%}.tiny-pageembed--16by9{padding-top:56.25%}.tiny-pageembed--4by3{padding-top:75%}.tiny-pageembed--1by1{padding-top:100%}.tiny-pageembed--16by9 iframe,.tiny-pageembed--1by1 iframe,.tiny-pageembed--21by9 iframe,.tiny-pageembed--4by3 iframe{border:0;height:100%;left:0;position:absolute;top:0;width:100%}.mce-content-body[data-mce-placeholder]{position:relative}.mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before{color:rgba(34,47,62,.7);content:attr(data-mce-placeholder);position:absolute}.mce-content-body:not([dir=rtl])[data-mce-placeholder]:not(.mce-visualblocks)::before{left:1px}.mce-content-body[dir=rtl][data-mce-placeholder]:not(.mce-visualblocks)::before{right:1px}.mce-content-body div.mce-resizehandle{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;height:10px;position:absolute;width:10px;z-index:1298}.mce-content-body div.mce-resizehandle:hover{background-color:#4099ff}.mce-content-body div.mce-resizehandle:nth-of-type(1){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(2){cursor:nesw-resize}.mce-content-body div.mce-resizehandle:nth-of-type(3){cursor:nwse-resize}.mce-content-body div.mce-resizehandle:nth-of-type(4){cursor:nesw-resize}.mce-content-body .mce-resize-backdrop{z-index:10000}.mce-content-body .mce-clonedresizable{cursor:default;opacity:.5;outline:1px dashed #000;position:absolute;z-index:10001}.mce-content-body .mce-clonedresizable.mce-resizetable-columns td,.mce-content-body .mce-clonedresizable.mce-resizetable-columns th{border:0}.mce-content-body .mce-resize-helper{background:#555;background:rgba(0,0,0,.75);border:1px;border-radius:3px;color:#fff;display:none;font-family:sans-serif;font-size:12px;line-height:14px;margin:5px 10px;padding:5px;position:absolute;white-space:nowrap;z-index:10002}.tox-rtc-user-selection{position:relative}.tox-rtc-user-cursor{bottom:0;cursor:default;position:absolute;top:0;width:2px}.tox-rtc-user-cursor::before{background-color:inherit;border-radius:50%;content:'';display:block;height:8px;position:absolute;right:-3px;top:-3px;width:8px}.tox-rtc-user-cursor:hover::after{background-color:inherit;border-radius:100px;box-sizing:border-box;color:#fff;content:attr(data-user);display:block;font-size:12px;font-weight:700;left:-5px;min-height:8px;min-width:8px;padding:0 12px;position:absolute;top:-11px;white-space:nowrap;z-index:1000}.tox-rtc-user-selection--1 .tox-rtc-user-cursor{background-color:#2dc26b}.tox-rtc-user-selection--2 .tox-rtc-user-cursor{background-color:#e03e2d}.tox-rtc-user-selection--3 .tox-rtc-user-cursor{background-color:#f1c40f}.tox-rtc-user-selection--4 .tox-rtc-user-cursor{background-color:#3598db}.tox-rtc-user-selection--5 .tox-rtc-user-cursor{background-color:#b96ad9}.tox-rtc-user-selection--6 .tox-rtc-user-cursor{background-color:#e67e23}.tox-rtc-user-selection--7 .tox-rtc-user-cursor{background-color:#aaa69d}.tox-rtc-user-selection--8 .tox-rtc-user-cursor{background-color:#f368e0}.tox-rtc-remote-image{background:#eaeaea url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2236%22%20height%3D%2212%22%20viewBox%3D%220%200%2036%2012%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%0A%20%20%3Ccircle%20cx%3D%226%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2218%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.33s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%20%20%3Ccircle%20cx%3D%2230%22%20cy%3D%226%22%20r%3D%223%22%20fill%3D%22rgba(0%2C%200%2C%200%2C%20.2)%22%3E%0A%20%20%20%20%3Canimate%20attributeName%3D%22r%22%20values%3D%223%3B5%3B3%22%20calcMode%3D%22linear%22%20begin%3D%22.66s%22%20dur%3D%221s%22%20repeatCount%3D%22indefinite%22%20%2F%3E%0A%20%20%3C%2Fcircle%3E%0A%3C%2Fsvg%3E%0A") no-repeat center center;border:1px solid #ccc;min-height:240px;min-width:320px}.mce-match-marker{background:#aaa;color:#fff}.mce-match-marker-selected{background:#39f;color:#fff}.mce-match-marker-selected::-moz-selection{background:#39f;color:#fff}.mce-match-marker-selected::selection{background:#39f;color:#fff}.mce-content-body audio[data-mce-selected],.mce-content-body embed[data-mce-selected],.mce-content-body img[data-mce-selected],.mce-content-body object[data-mce-selected],.mce-content-body table[data-mce-selected],.mce-content-body video[data-mce-selected]{outline:3px solid #b4d7ff}.mce-content-body hr[data-mce-selected]{outline:3px solid #b4d7ff;outline-offset:1px}.mce-content-body [contentEditable=false] [contentEditable=true]:focus{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false] [contentEditable=true]:hover{outline:3px solid #b4d7ff}.mce-content-body [contentEditable=false][data-mce-selected]{cursor:not-allowed;outline:3px solid #b4d7ff}.mce-content-body.mce-content-readonly [contentEditable=true]:focus,.mce-content-body.mce-content-readonly [contentEditable=true]:hover{outline:0}.mce-content-body [data-mce-selected=inline-boundary]{background-color:#b4d7ff}.mce-content-body .mce-edit-focus{outline:3px solid #b4d7ff}.mce-content-body td[data-mce-selected],.mce-content-body th[data-mce-selected]{position:relative}.mce-content-body td[data-mce-selected]::-moz-selection,.mce-content-body th[data-mce-selected]::-moz-selection{background:0 0}.mce-content-body td[data-mce-selected]::selection,.mce-content-body th[data-mce-selected]::selection{background:0 0}.mce-content-body td[data-mce-selected] *,.mce-content-body th[data-mce-selected] *{outline:0;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{background-color:rgba(180,215,255,.7);border:1px solid rgba(180,215,255,.7);bottom:-1px;content:'';left:-1px;mix-blend-mode:multiply;position:absolute;right:-1px;top:-1px}@media screen and (-ms-high-contrast:active),(-ms-high-contrast:none){.mce-content-body td[data-mce-selected]::after,.mce-content-body th[data-mce-selected]::after{border-color:rgba(0,84,180,.7)}}.mce-content-body img::-moz-selection{background:0 0}.mce-content-body img::selection{background:0 0}.ephox-snooker-resizer-bar{background-color:#b4d7ff;opacity:0;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.ephox-snooker-resizer-cols{cursor:col-resize}.ephox-snooker-resizer-rows{cursor:row-resize}.ephox-snooker-resizer-bar.ephox-snooker-resizer-bar-dragging{opacity:1}.mce-spellchecker-word{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%23ff0000'%20fill%3D'none'%20stroke-linecap%3D'round'%20stroke-opacity%3D'.75'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default;height:2rem}.mce-spellchecker-grammar{background-image:url("data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D'4'%20height%3D'4'%20xmlns%3D'http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg'%3E%3Cpath%20stroke%3D'%2300A835'%20fill%3D'none'%20stroke-linecap%3D'round'%20d%3D'M0%203L2%201%204%203'%2F%3E%3C%2Fsvg%3E%0A");background-position:0 calc(100% + 1px);background-repeat:repeat-x;background-size:auto 6px;cursor:default}.mce-toc{border:1px solid gray}.mce-toc h2{margin:4px}.mce-toc li{list-style-type:none}.mce-item-table:not([border]),.mce-item-table:not([border]) caption,.mce-item-table:not([border]) td,.mce-item-table:not([border]) th,.mce-item-table[border="0"],.mce-item-table[border="0"] caption,.mce-item-table[border="0"] td,.mce-item-table[border="0"] th,table[style*="border-width: 0px"],table[style*="border-width: 0px"] caption,table[style*="border-width: 0px"] td,table[style*="border-width: 0px"] th{border:1px dashed #bbb}.mce-visualblocks address,.mce-visualblocks article,.mce-visualblocks aside,.mce-visualblocks blockquote,.mce-visualblocks div:not([data-mce-bogus]),.mce-visualblocks dl,.mce-visualblocks figcaption,.mce-visualblocks figure,.mce-visualblocks h1,.mce-visualblocks h2,.mce-visualblocks h3,.mce-visualblocks h4,.mce-visualblocks h5,.mce-visualblocks h6,.mce-visualblocks hgroup,.mce-visualblocks ol,.mce-visualblocks p,.mce-visualblocks pre,.mce-visualblocks section,.mce-visualblocks ul{background-repeat:no-repeat;border:1px dashed #bbb;margin-left:3px;padding-top:10px}.mce-visualblocks p{background-image:url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}.mce-visualblocks h1{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}.mce-visualblocks h2{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}.mce-visualblocks h3{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}.mce-visualblocks h4{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}.mce-visualblocks h5{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}.mce-visualblocks h6{background-image:url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}.mce-visualblocks div:not([data-mce-bogus]){background-image:url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}.mce-visualblocks section{background-image:url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}.mce-visualblocks article{background-image:url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}.mce-visualblocks blockquote{background-image:url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}.mce-visualblocks address{background-image:url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}.mce-visualblocks pre{background-image:url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}.mce-visualblocks figure{background-image:url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}.mce-visualblocks figcaption{border:1px dashed #bbb}.mce-visualblocks hgroup{background-image:url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}.mce-visualblocks aside{background-image:url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}.mce-visualblocks ul{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIAAALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGuYnqUVSjvw26DzzXiqIDlVwAAOw==)}.mce-visualblocks ol{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybH6HHt0qourxC6CvzXieHyeWQAAOw==)}.mce-visualblocks dl{background-image:url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybEOnmOvUoWznTqeuEjNSCqeGRUAOw==)}.mce-visualblocks:not([dir=rtl]) address,.mce-visualblocks:not([dir=rtl]) article,.mce-visualblocks:not([dir=rtl]) aside,.mce-visualblocks:not([dir=rtl]) blockquote,.mce-visualblocks:not([dir=rtl]) div:not([data-mce-bogus]),.mce-visualblocks:not([dir=rtl]) dl,.mce-visualblocks:not([dir=rtl]) figcaption,.mce-visualblocks:not([dir=rtl]) figure,.mce-visualblocks:not([dir=rtl]) h1,.mce-visualblocks:not([dir=rtl]) h2,.mce-visualblocks:not([dir=rtl]) h3,.mce-visualblocks:not([dir=rtl]) h4,.mce-visualblocks:not([dir=rtl]) h5,.mce-visualblocks:not([dir=rtl]) h6,.mce-visualblocks:not([dir=rtl]) hgroup,.mce-visualblocks:not([dir=rtl]) ol,.mce-visualblocks:not([dir=rtl]) p,.mce-visualblocks:not([dir=rtl]) pre,.mce-visualblocks:not([dir=rtl]) section,.mce-visualblocks:not([dir=rtl]) ul{margin-left:3px}.mce-visualblocks[dir=rtl] address,.mce-visualblocks[dir=rtl] article,.mce-visualblocks[dir=rtl] aside,.mce-visualblocks[dir=rtl] blockquote,.mce-visualblocks[dir=rtl] div:not([data-mce-bogus]),.mce-visualblocks[dir=rtl] dl,.mce-visualblocks[dir=rtl] figcaption,.mce-visualblocks[dir=rtl] figure,.mce-visualblocks[dir=rtl] h1,.mce-visualblocks[dir=rtl] h2,.mce-visualblocks[dir=rtl] h3,.mce-visualblocks[dir=rtl] h4,.mce-visualblocks[dir=rtl] h5,.mce-visualblocks[dir=rtl] h6,.mce-visualblocks[dir=rtl] hgroup,.mce-visualblocks[dir=rtl] ol,.mce-visualblocks[dir=rtl] p,.mce-visualblocks[dir=rtl] pre,.mce-visualblocks[dir=rtl] section,.mce-visualblocks[dir=rtl] ul{background-position-x:right;margin-right:3px}.mce-nbsp,.mce-shy{background:#aaa}.mce-shy::after{content:'-'}body{font-family:sans-serif}table{border-collapse:collapse}
diff --git a/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css b/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css
new file mode 100644
index 0000000..35f7dc0
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide/content.mobile.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-unfocused-selections .tinymce-mobile-unfocused-selection{background-color:green;display:inline-block;opacity:.5;position:absolute}body{-webkit-text-size-adjust:none}body img{max-width:96vw}body table img{max-width:95%}body{font-family:sans-serif}table{border-collapse:collapse}
diff --git a/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff b/public/resource/tinymce/skins/ui/oxide/fonts/tinymce-mobile.woff
new file mode 100644
index 0000000000000000000000000000000000000000..1e3be038a607cb7c2544ed8ae3d6621f77bf4c38
GIT binary patch
literal 4624
zcmb7IeQaFC5#QN&AGUL{efE7g{=BM1W-|RaVdWQe^e?BC`eGz4^i8S3PQw?Hhd_eQHxTkckXZB
zdzU((wCVGko!Qyh+1c6InRotvZ%+>+hNrBQtrFOI4t*}DZ$7=>Sr=uD3c$ZlKuKBQ
z8~ervCczs9SOk2!>AAqrz+v$CC}f1JfYPDSqx->|V$6{ekbe8M#Bh3Gkg?)-Fdi3B
zeB$}UFqn*$pv&q7*net~hsUOlfG7Ho2zaowY%JPRytMvu{&xRPm(h_~w##F>vqE&a5-ssH##mlfAk}44^
zXRJKd!Ifw&ce{$Y9BAg5c>e>p_Z;t!=P{izddGWie?aHLdKL3Cn9rG=d2vt;esWqH
zoD}uAoi3Z~4+LABvADt+so4~t%VlyIJ{O3tm$NC+(!yenQD%NVr*btG$T3+_WX=LH
z#1M2ZNEtrO+-x;l2i>M^5o%GQ@s?N+gw*19H@G~vl3Q5Zf*t6jjW0GOTmAmlWYgSS
zJeiEo%~LA-FW|YAd_Em$OE#@dw)y*#@p!UtnWa);V1HY3ZBw!>(3gY{iFFa_c6iW9
zIQ@xck^{xu9_o;UyQH#ba@y?L$xW?8J35?$p1z46ZjIctZ8QCKCa29bMC1-t@pT>S
zTUT1WMjQz-75d)5zJxv~@Yd)bY)ejQBx_XQiaMJ
z>$5`NO3?L*ND{UQeF8%xl)$_>w9tmQpfEebzedazFeh#~d}suN+vzsqLiW~@TLhoe
zk1%xEcxP2ZL)FuoXeYzb-J5goljDxPL2@@#RW)d&X#&6QO5U=04_628@ONSvtgpha
zDqqmoVep`A4<+PK$V>K+T}}{8Rj+Q|UAzCtl!Fh)uXJg{x$}HMJH7LcBLzj-r{h;<
zzote8Id%pcAyE;87D<8glyaFeq#k)OEDB%yA
ze%CeZ!?4TEs#pj+%14DBZHn8jxaF2as6}p3+!6p-&@I>5lbP3&N$svcIF-`0R5(o2
zh7la++|;-euckH44a4BAwB++#-cZ
z)kFyC=eUS-4D0t}H8LdZY!JD^sW@F85io)%=8HU)ouhEeo-K_dJ3BV+8fo0JXIjlP
zZt0H`0=Yv~I|PpRZ)r5_iAYmY9V=wT@BsoN9<3vftB|}TOH;|yNk_e7(2-?y{&cSK
zG=E5Nz^Ko4>KxcbY!Q13!=HBS$lM96_+0y3M1yWTAt2u5C;6MWMXbRN?RI{$eHnAx
z&t=-PSjZ>Qe2V2-YGs1YWemAq
zVHdG{9V$QvsY~Cgq-L*PZqMPGv|px$)K~3<%+fBtG{oIRPL_7ye$-(`C=tS)^xC}%
zue73qiF&{nXJ*>-@668G!`IrAeB;ad09shzt{O?7omLE_X@H|#ozGt&64
zb-&_lLkZI8TzigPZvUr=4g2-8M6M8b9EQLgoPswYg)d)j&%gZHJO!2>(?;I*8d>aG
z#oS295Kcq{uD4R2@VEG($}WWiF-6YK)kjqks%o_U{CIAVX2;tX7o|unkew5?Gn3(|
zOePS^{$(;Xi4ph;`KO#;k+vaLt8n5@doi+OEvH&?*+3(WgqkT9-$b0fTHm;)r=NmR
zJnJ9o>UvNR(JMoIdRBf{%kd}jmZ)b)#4>dnDfq0G(?~S%d
zv50QeMR$Kzd*S$AEXdp5Fhqe0Pz
zZ!oS2e!i-tWEJ2^YoVo}V7S0tV7CujimbVJtVNb#yB&<-f&xpSb@m2=wBZ|qU-_^;
z?C{lk+;tlxk&Sh3Pwh(D7~kNh`O=~TMWuRUu^0=9)`CYEVwhvGWUt4Wd3`6*H)Zs>LLYQcC#*~B78EfTt7RQ*l)b{v
zqntLNsC`h&zZCY{x*}gfPU4at;nfileU3>zeyLdO7;;lFIft~
zsm6#wb5Jjtv;_VxleU0<%cQON-O*ywHt`@C4fn-Y83}=|hJPOpN>1H%C#7)9etg_yG)$div{padding-bottom:4px}.tox .accessibility-issue__description>div>div{align-items:center;display:flex;margin-bottom:4px}.tox .accessibility-issue__description>:last-child:not(:only-child){border-color:#ccc;border-style:solid}.tox .accessibility-issue__repair{margin-top:16px}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description{background-color:rgba(32,122,183,.1);border-color:rgba(32,122,183,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--info .accessibility-issue__description>:last-child{border-color:rgba(32,122,183,.4)}.tox .tox-dialog__body-content .accessibility-issue--info .tox-form__group h2{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info .tox-icon svg{fill:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--info a .tox-icon{color:#207ab7}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description{background-color:rgba(255,165,0,.1);border-color:rgba(255,165,0,.5);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--warn .accessibility-issue__description>:last-child{border-color:rgba(255,165,0,.5)}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-form__group h2{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn .tox-icon svg{fill:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--warn a .tox-icon{color:#cc8500}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description{background-color:rgba(204,0,0,.1);border-color:rgba(204,0,0,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--error .accessibility-issue__description>:last-child{border-color:rgba(204,0,0,.4)}.tox .tox-dialog__body-content .accessibility-issue--error .tox-form__group h2{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--error .tox-icon svg{fill:#c00}.tox .tox-dialog__body-content .accessibility-issue--error a .tox-icon{color:#c00}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description{background-color:rgba(120,171,70,.1);border-color:rgba(120,171,70,.4);color:#222f3e}.tox .tox-dialog__body-content .accessibility-issue--success .accessibility-issue__description>:last-child{border-color:rgba(120,171,70,.4)}.tox .tox-dialog__body-content .accessibility-issue--success .tox-form__group h2{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success .tox-icon svg{fill:#78ab46}.tox .tox-dialog__body-content .accessibility-issue--success a .tox-icon{color:#78ab46}.tox .tox-dialog__body-content .accessibility-issue__header h1,.tox .tox-dialog__body-content .tox-form__group .accessibility-issue__description h2{margin-top:0}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-left:4px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-left:auto}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description{padding:4px 4px 4px 8px}.tox:not([dir=rtl]) .tox-dialog__body-content .accessibility-issue__description>:last-child{border-left-width:1px;padding-left:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header .tox-button{margin-right:4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__header>:nth-last-child(2){margin-right:auto}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description{padding:4px 8px 4px 4px}.tox[dir=rtl] .tox-dialog__body-content .accessibility-issue__description>:last-child{border-right-width:1px;padding-right:4px}.tox .tox-anchorbar{display:flex;flex:0 0 auto}.tox .tox-bar{display:flex;flex:0 0 auto}.tox .tox-button{background-color:#207ab7;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#207ab7;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#fff;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;line-height:24px;margin:0;outline:0;padding:4px 16px;text-align:center;text-decoration:none;text-transform:none;white-space:nowrap}.tox .tox-button[disabled]{background-color:#207ab7;background-image:none;border-color:#207ab7;box-shadow:none;color:rgba(255,255,255,.5);cursor:not-allowed}.tox .tox-button:focus:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:hover:not(:disabled){background-color:#1c6ca1;background-image:none;border-color:#1c6ca1;box-shadow:none;color:#fff}.tox .tox-button:active:not(:disabled){background-color:#185d8c;background-image:none;border-color:#185d8c;box-shadow:none;color:#fff}.tox .tox-button--secondary{background-color:#f0f0f0;background-image:none;background-position:0 0;background-repeat:repeat;border-color:#f0f0f0;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;color:#222f3e;font-size:14px;font-style:normal;font-weight:700;letter-spacing:normal;outline:0;padding:4px 16px;text-decoration:none;text-transform:none}.tox .tox-button--secondary[disabled]{background-color:#f0f0f0;background-image:none;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--secondary:focus:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:hover:not(:disabled){background-color:#e3e3e3;background-image:none;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--secondary:active:not(:disabled){background-color:#d6d6d6;background-image:none;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--icon,.tox .tox-button.tox-button--icon,.tox .tox-button.tox-button--secondary.tox-button--icon{padding:4px}.tox .tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--icon .tox-icon svg,.tox .tox-button.tox-button--secondary.tox-button--icon .tox-icon svg{display:block;fill:currentColor}.tox .tox-button-link{background:0;border:none;box-sizing:border-box;cursor:pointer;display:inline-block;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;font-weight:400;line-height:1.3;margin:0;padding:0;white-space:nowrap}.tox .tox-button-link--sm{font-size:14px}.tox .tox-button--naked{background-color:transparent;border-color:transparent;box-shadow:unset;color:#222f3e}.tox .tox-button--naked[disabled]{background-color:#f0f0f0;border-color:#f0f0f0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-button--naked:hover:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:focus:not(:disabled){background-color:#e3e3e3;border-color:#e3e3e3;box-shadow:none;color:#222f3e}.tox .tox-button--naked:active:not(:disabled){background-color:#d6d6d6;border-color:#d6d6d6;box-shadow:none;color:#222f3e}.tox .tox-button--naked .tox-icon svg{fill:currentColor}.tox .tox-button--naked.tox-button--icon:hover:not(:disabled){color:#222f3e}.tox .tox-checkbox{align-items:center;border-radius:3px;cursor:pointer;display:flex;height:36px;min-width:36px}.tox .tox-checkbox__input{height:1px;overflow:hidden;position:absolute;top:auto;width:1px}.tox .tox-checkbox__icons{align-items:center;border-radius:3px;box-shadow:0 0 0 2px transparent;box-sizing:content-box;display:flex;height:24px;justify-content:center;padding:calc(4px - 1px);width:24px}.tox .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:block;fill:rgba(34,47,62,.3)}.tox .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:none;fill:#207ab7}.tox .tox-checkbox__icons .tox-checkbox-icon__checked svg{display:none;fill:#207ab7}.tox .tox-checkbox--disabled{color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__checked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__unchecked svg{fill:rgba(34,47,62,.5)}.tox .tox-checkbox--disabled .tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{fill:rgba(34,47,62,.5)}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:checked+.tox-checkbox__icons .tox-checkbox-icon__checked svg{display:block}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__unchecked svg{display:none}.tox input.tox-checkbox__input:indeterminate+.tox-checkbox__icons .tox-checkbox-icon__indeterminate svg{display:block}.tox input.tox-checkbox__input:focus+.tox-checkbox__icons{border-radius:3px;box-shadow:inset 0 0 0 1px #207ab7;padding:calc(4px - 1px)}.tox:not([dir=rtl]) .tox-checkbox__label{margin-left:4px}.tox:not([dir=rtl]) .tox-checkbox__input{left:-10000px}.tox:not([dir=rtl]) .tox-bar .tox-checkbox{margin-left:4px}.tox[dir=rtl] .tox-checkbox__label{margin-right:4px}.tox[dir=rtl] .tox-checkbox__input{right:-10000px}.tox[dir=rtl] .tox-bar .tox-checkbox{margin-right:4px}.tox .tox-collection--toolbar .tox-collection__group{display:flex;padding:0}.tox .tox-collection--grid .tox-collection__group{display:flex;flex-wrap:wrap;max-height:208px;overflow-x:hidden;overflow-y:auto;padding:0}.tox .tox-collection--list .tox-collection__group{border-bottom-width:0;border-color:#ccc;border-left-width:0;border-right-width:0;border-style:solid;border-top-width:1px;padding:4px 0}.tox .tox-collection--list .tox-collection__group:first-child{border-top-width:0}.tox .tox-collection__group-heading{background-color:#e6e6e6;color:rgba(34,47,62,.7);cursor:default;font-size:12px;font-style:normal;font-weight:400;margin-bottom:4px;margin-top:-4px;padding:4px 8px;text-transform:none;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection__item{align-items:center;color:#222f3e;cursor:pointer;display:flex;-webkit-touch-callout:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.tox .tox-collection--list .tox-collection__item{padding:4px 8px}.tox .tox-collection--toolbar .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--grid .tox-collection__item{border-radius:3px;padding:4px}.tox .tox-collection--list .tox-collection__item--enabled{background-color:#fff;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--toolbar .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active{background-color:#dee0e2}.tox .tox-collection--grid .tox-collection__item--enabled{background-color:#c8cbcf;color:#222f3e}.tox .tox-collection--grid .tox-collection__item--active:not(.tox-collection__item--state-disabled){background-color:#dee0e2;color:#222f3e}.tox .tox-collection--list .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection--toolbar .tox-collection__item--active:not(.tox-collection__item--state-disabled){color:#222f3e}.tox .tox-collection__item-checkmark,.tox .tox-collection__item-icon{align-items:center;display:flex;height:24px;justify-content:center;width:24px}.tox .tox-collection__item-checkmark svg,.tox .tox-collection__item-icon svg{fill:currentColor}.tox .tox-collection--toolbar-lg .tox-collection__item-icon{height:48px;width:48px}.tox .tox-collection__item-label{color:currentColor;display:inline-block;flex:1;-ms-flex-preferred-size:auto;font-size:14px;font-style:normal;font-weight:400;line-height:24px;text-transform:none;word-break:break-all}.tox .tox-collection__item-accessory{color:rgba(34,47,62,.7);display:inline-block;font-size:14px;height:24px;line-height:24px;text-transform:none}.tox .tox-collection__item-caret{align-items:center;display:flex;min-height:24px}.tox .tox-collection__item-caret::after{content:'';font-size:0;min-height:inherit}.tox .tox-collection__item-caret svg{fill:#222f3e}.tox .tox-collection__item--state-disabled{background-color:transparent;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-collection__item--state-disabled .tox-collection__item-caret svg{fill:rgba(34,47,62,.5)}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-checkmark svg{display:none}.tox .tox-collection--list .tox-collection__item:not(.tox-collection__item--enabled) .tox-collection__item-accessory+.tox-collection__item-checkmark{display:none}.tox .tox-collection--horizontal{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:nowrap;margin-bottom:0;overflow-x:auto;padding:0}.tox .tox-collection--horizontal .tox-collection__group{align-items:center;display:flex;flex-wrap:nowrap;margin:0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item{height:34px;margin:2px 0 3px 0;padding:0 4px}.tox .tox-collection--horizontal .tox-collection__item-label{white-space:nowrap}.tox .tox-collection--horizontal .tox-collection__item-caret{margin-left:4px}.tox .tox-collection__item-container{display:flex}.tox .tox-collection__item-container--row{align-items:center;flex:1 1 auto;flex-direction:row}.tox .tox-collection__item-container--row.tox-collection__item-container--align-left{margin-right:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--align-right{justify-content:flex-end;margin-left:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-top{align-items:flex-start;margin-bottom:auto}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-middle{align-items:center}.tox .tox-collection__item-container--row.tox-collection__item-container--valign-bottom{align-items:flex-end;margin-top:auto}.tox .tox-collection__item-container--column{-ms-grid-row-align:center;align-self:center;flex:1 1 auto;flex-direction:column}.tox .tox-collection__item-container--column.tox-collection__item-container--align-left{align-items:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--align-right{align-items:flex-end}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-top{align-self:flex-start}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-middle{-ms-grid-row-align:center;align-self:center}.tox .tox-collection__item-container--column.tox-collection__item-container--valign-bottom{align-self:flex-end}.tox:not([dir=rtl]) .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-right:1px solid #ccc}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>:not(:first-child){margin-left:8px}.tox:not([dir=rtl]) .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-left:4px}.tox:not([dir=rtl]) .tox-collection__item-accessory{margin-left:16px;text-align:right}.tox:not([dir=rtl]) .tox-collection .tox-collection__item-caret{margin-left:16px}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__group:not(:last-of-type){border-left:1px solid #ccc}.tox[dir=rtl] .tox-collection--list .tox-collection__item>:not(:first-child){margin-right:8px}.tox[dir=rtl] .tox-collection--list .tox-collection__item>.tox-collection__item-label:first-child{margin-right:4px}.tox[dir=rtl] .tox-collection__item-accessory{margin-right:16px;text-align:left}.tox[dir=rtl] .tox-collection .tox-collection__item-caret{margin-right:16px;transform:rotateY(180deg)}.tox[dir=rtl] .tox-collection--horizontal .tox-collection__item-caret{margin-right:4px}.tox .tox-color-picker-container{display:flex;flex-direction:row;height:225px;margin:0}.tox .tox-sv-palette{box-sizing:border-box;display:flex;height:100%}.tox .tox-sv-palette-spectrum{height:100%}.tox .tox-sv-palette,.tox .tox-sv-palette-spectrum{width:225px}.tox .tox-sv-palette-thumb{background:0 0;border:1px solid #000;border-radius:50%;box-sizing:content-box;height:12px;position:absolute;width:12px}.tox .tox-sv-palette-inner-thumb{border:1px solid #fff;border-radius:50%;height:10px;position:absolute;width:10px}.tox .tox-hue-slider{box-sizing:border-box;height:100%;width:25px}.tox .tox-hue-slider-spectrum{background:linear-gradient(to bottom,red,#ff0080,#f0f,#8000ff,#00f,#0080ff,#0ff,#00ff80,#0f0,#80ff00,#ff0,#ff8000,red);height:100%;width:100%}.tox .tox-hue-slider,.tox .tox-hue-slider-spectrum{width:20px}.tox .tox-hue-slider-thumb{background:#fff;border:1px solid #000;box-sizing:content-box;height:4px;width:100%}.tox .tox-rgb-form{display:flex;flex-direction:column;justify-content:space-between}.tox .tox-rgb-form div{align-items:center;display:flex;justify-content:space-between;margin-bottom:5px;width:inherit}.tox .tox-rgb-form input{width:6em}.tox .tox-rgb-form input.tox-invalid{border:1px solid red!important}.tox .tox-rgb-form .tox-rgba-preview{border:1px solid #000;flex-grow:2;margin-bottom:0}.tox:not([dir=rtl]) .tox-sv-palette{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider{margin-right:15px}.tox:not([dir=rtl]) .tox-hue-slider-thumb{margin-left:-1px}.tox:not([dir=rtl]) .tox-rgb-form label{margin-right:.5em}.tox[dir=rtl] .tox-sv-palette{margin-left:15px}.tox[dir=rtl] .tox-hue-slider{margin-left:15px}.tox[dir=rtl] .tox-hue-slider-thumb{margin-right:-1px}.tox[dir=rtl] .tox-rgb-form label{margin-left:.5em}.tox .tox-toolbar .tox-swatches,.tox .tox-toolbar__overflow .tox-swatches,.tox .tox-toolbar__primary .tox-swatches{margin:2px 0 3px 4px}.tox .tox-collection--list .tox-collection__group .tox-swatches-menu{border:0;margin:-4px 0}.tox .tox-swatches__row{display:flex}.tox .tox-swatch{height:30px;transition:transform .15s,box-shadow .15s;width:30px}.tox .tox-swatch:focus,.tox .tox-swatch:hover{box-shadow:0 0 0 1px rgba(127,127,127,.3) inset;transform:scale(.8)}.tox .tox-swatch--remove{align-items:center;display:flex;justify-content:center}.tox .tox-swatch--remove svg path{stroke:#e74c3c}.tox .tox-swatches__picker-btn{align-items:center;background-color:transparent;border:0;cursor:pointer;display:flex;height:30px;justify-content:center;outline:0;padding:0;width:30px}.tox .tox-swatches__picker-btn svg{height:24px;width:24px}.tox .tox-swatches__picker-btn:hover{background:#dee0e2}.tox:not([dir=rtl]) .tox-swatches__picker-btn{margin-left:auto}.tox[dir=rtl] .tox-swatches__picker-btn{margin-right:auto}.tox .tox-comment-thread{background:#fff;position:relative}.tox .tox-comment-thread>:not(:first-child){margin-top:8px}.tox .tox-comment{background:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);padding:8px 8px 16px 8px;position:relative}.tox .tox-comment__header{align-items:center;color:#222f3e;display:flex;justify-content:space-between}.tox .tox-comment__date{color:rgba(34,47,62,.7);font-size:12px}.tox .tox-comment__body{color:#222f3e;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;margin-top:8px;position:relative;text-transform:initial}.tox .tox-comment__body textarea{resize:none;white-space:normal;width:100%}.tox .tox-comment__expander{padding-top:8px}.tox .tox-comment__expander p{color:rgba(34,47,62,.7);font-size:14px;font-style:normal}.tox .tox-comment__body p{margin:0}.tox .tox-comment__buttonspacing{padding-top:16px;text-align:center}.tox .tox-comment-thread__overlay::after{background:#fff;bottom:0;content:"";display:flex;left:0;opacity:.9;position:absolute;right:0;top:0;z-index:5}.tox .tox-comment__reply{display:flex;flex-shrink:0;flex-wrap:wrap;justify-content:flex-end;margin-top:8px}.tox .tox-comment__reply>:first-child{margin-bottom:8px;width:100%}.tox .tox-comment__edit{display:flex;flex-wrap:wrap;justify-content:flex-end;margin-top:16px}.tox .tox-comment__gradient::after{background:linear-gradient(rgba(255,255,255,0),#fff);bottom:0;content:"";display:block;height:5em;margin-top:-40px;position:absolute;width:100%}.tox .tox-comment__overlay{background:#fff;bottom:0;display:flex;flex-direction:column;flex-grow:1;left:0;opacity:.9;position:absolute;right:0;text-align:center;top:0;z-index:5}.tox .tox-comment__loading-text{align-items:center;color:#222f3e;display:flex;flex-direction:column;position:relative}.tox .tox-comment__loading-text>div{padding-bottom:16px}.tox .tox-comment__overlaytext{bottom:0;flex-direction:column;font-size:14px;left:0;padding:1em;position:absolute;right:0;top:0;z-index:10}.tox .tox-comment__overlaytext p{background-color:#fff;box-shadow:0 0 8px 8px #fff;color:#222f3e;text-align:center}.tox .tox-comment__overlaytext div:nth-of-type(2){font-size:.8em}.tox .tox-comment__busy-spinner{align-items:center;background-color:#fff;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:20}.tox .tox-comment__scroll{display:flex;flex-direction:column;flex-shrink:1;overflow:auto}.tox .tox-conversations{margin:8px}.tox:not([dir=rtl]) .tox-comment__edit{margin-left:8px}.tox:not([dir=rtl]) .tox-comment__buttonspacing>:last-child,.tox:not([dir=rtl]) .tox-comment__edit>:last-child,.tox:not([dir=rtl]) .tox-comment__reply>:last-child{margin-left:8px}.tox[dir=rtl] .tox-comment__edit{margin-right:8px}.tox[dir=rtl] .tox-comment__buttonspacing>:last-child,.tox[dir=rtl] .tox-comment__edit>:last-child,.tox[dir=rtl] .tox-comment__reply>:last-child{margin-right:8px}.tox .tox-user{align-items:center;display:flex}.tox .tox-user__avatar svg{fill:rgba(34,47,62,.7)}.tox .tox-user__name{color:rgba(34,47,62,.7);font-size:12px;font-style:normal;font-weight:700;text-transform:uppercase}.tox:not([dir=rtl]) .tox-user__avatar svg{margin-right:8px}.tox:not([dir=rtl]) .tox-user__avatar+.tox-user__name{margin-left:8px}.tox[dir=rtl] .tox-user__avatar svg{margin-left:8px}.tox[dir=rtl] .tox-user__avatar+.tox-user__name{margin-right:8px}.tox .tox-dialog-wrap{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:fixed;right:0;top:0;z-index:1100}.tox .tox-dialog-wrap__backdrop{background-color:rgba(255,255,255,.75);bottom:0;left:0;position:absolute;right:0;top:0;z-index:1}.tox .tox-dialog-wrap__backdrop--opaque{background-color:#fff}.tox .tox-dialog{background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:0 16px 16px -10px rgba(34,47,62,.15),0 0 40px 1px rgba(34,47,62,.15);display:flex;flex-direction:column;max-height:100%;max-width:480px;overflow:hidden;position:relative;width:95vw;z-index:2}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog{align-self:flex-start;margin:8px auto;width:calc(100vw - 16px)}}.tox .tox-dialog-inline{z-index:1100}.tox .tox-dialog__header{align-items:center;background-color:#fff;border-bottom:none;color:#222f3e;display:flex;font-size:16px;justify-content:space-between;padding:8px 16px 0 16px;position:relative}.tox .tox-dialog__header .tox-button{z-index:1}.tox .tox-dialog__draghandle{cursor:grab;height:100%;left:0;position:absolute;top:0;width:100%}.tox .tox-dialog__draghandle:active{cursor:grabbing}.tox .tox-dialog__dismiss{margin-left:auto}.tox .tox-dialog__title{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:20px;font-style:normal;font-weight:400;line-height:1.3;margin:0;text-transform:none}.tox .tox-dialog__body{color:#222f3e;display:flex;flex:1;-ms-flex-preferred-size:auto;font-size:16px;font-style:normal;font-weight:400;line-height:1.3;min-width:0;text-align:left;text-transform:none}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body{flex-direction:column}}.tox .tox-dialog__body-nav{align-items:flex-start;display:flex;flex-direction:column;padding:16px 16px}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox .tox-dialog__body-nav{flex-direction:row;-webkit-overflow-scrolling:touch;overflow-x:auto;padding-bottom:0}}.tox .tox-dialog__body-nav-item{border-bottom:2px solid transparent;color:rgba(34,47,62,.7);display:inline-block;font-size:14px;line-height:1.3;margin-bottom:8px;text-decoration:none;white-space:nowrap}.tox .tox-dialog__body-nav-item:focus{background-color:rgba(32,122,183,.1)}.tox .tox-dialog__body-nav-item--active{border-bottom:2px solid #207ab7;color:#207ab7}.tox .tox-dialog__body-content{box-sizing:border-box;display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto;max-height:650px;overflow:auto;-webkit-overflow-scrolling:touch;padding:16px 16px}.tox .tox-dialog__body-content>*{margin-bottom:0;margin-top:16px}.tox .tox-dialog__body-content>:first-child{margin-top:0}.tox .tox-dialog__body-content>:last-child{margin-bottom:0}.tox .tox-dialog__body-content>:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog__body-content a{color:#207ab7;cursor:pointer;text-decoration:none}.tox .tox-dialog__body-content a:focus,.tox .tox-dialog__body-content a:hover{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content a:active{color:#185d8c;text-decoration:none}.tox .tox-dialog__body-content svg{fill:#222f3e}.tox .tox-dialog__body-content ul{display:block;list-style-type:disc;margin-bottom:16px;-webkit-margin-end:0;margin-inline-end:0;-webkit-margin-start:0;margin-inline-start:0;-webkit-padding-start:2.5rem;padding-inline-start:2.5rem}.tox .tox-dialog__body-content .tox-form__group h1{color:#222f3e;font-size:20px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group h2{color:#222f3e;font-size:16px;font-style:normal;font-weight:700;letter-spacing:normal;margin-bottom:16px;margin-top:2rem;text-transform:none}.tox .tox-dialog__body-content .tox-form__group p{margin-bottom:16px}.tox .tox-dialog__body-content .tox-form__group h1:first-child,.tox .tox-dialog__body-content .tox-form__group h2:first-child,.tox .tox-dialog__body-content .tox-form__group p:first-child{margin-top:0}.tox .tox-dialog__body-content .tox-form__group h1:last-child,.tox .tox-dialog__body-content .tox-form__group h2:last-child,.tox .tox-dialog__body-content .tox-form__group p:last-child{margin-bottom:0}.tox .tox-dialog__body-content .tox-form__group h1:only-child,.tox .tox-dialog__body-content .tox-form__group h2:only-child,.tox .tox-dialog__body-content .tox-form__group p:only-child{margin-bottom:0;margin-top:0}.tox .tox-dialog--width-lg{height:650px;max-width:1200px}.tox .tox-dialog--width-md{max-width:800px}.tox .tox-dialog--width-md .tox-dialog__body-content{overflow:auto}.tox .tox-dialog__body-content--centered{text-align:center}.tox .tox-dialog__footer{align-items:center;background-color:#fff;border-top:1px solid #ccc;display:flex;justify-content:space-between;padding:8px 16px}.tox .tox-dialog__footer-end,.tox .tox-dialog__footer-start{display:flex}.tox .tox-dialog__busy-spinner{align-items:center;background-color:rgba(255,255,255,.75);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0;z-index:3}.tox .tox-dialog__table{border-collapse:collapse;width:100%}.tox .tox-dialog__table thead th{font-weight:700;padding-bottom:8px}.tox .tox-dialog__table tbody tr{border-bottom:1px solid #ccc}.tox .tox-dialog__table tbody tr:last-child{border-bottom:none}.tox .tox-dialog__table td{padding-bottom:8px;padding-top:8px}.tox .tox-dialog__popups{position:absolute;width:100%;z-index:1100}.tox .tox-dialog__body-iframe{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-iframe .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox .tox-dialog-dock-fadeout{opacity:0;visibility:hidden}.tox .tox-dialog-dock-fadein{opacity:1;visibility:visible}.tox .tox-dialog-dock-transition{transition:visibility 0s linear .3s,opacity .3s ease}.tox .tox-dialog-dock-transition.tox-dialog-dock-fadein{transition-delay:0s}.tox.tox-platform-ie .tox-dialog-wrap{position:-ms-device-fixed}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav{margin-right:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox:not([dir=rtl]) .tox-dialog__body-nav-item:not(:first-child){margin-left:8px}}.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-end>*,.tox:not([dir=rtl]) .tox-dialog__footer .tox-dialog__footer-start>*{margin-left:8px}.tox[dir=rtl] .tox-dialog__body{text-align:right}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav{margin-left:0}}@media only screen and (max-width:767px){body:not(.tox-force-desktop) .tox[dir=rtl] .tox-dialog__body-nav-item:not(:first-child){margin-right:8px}}.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-end>*,.tox[dir=rtl] .tox-dialog__footer .tox-dialog__footer-start>*{margin-right:8px}body.tox-dialog__disable-scroll{overflow:hidden}.tox .tox-dropzone-container{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dropzone{align-items:center;background:#fff;border:2px dashed #ccc;box-sizing:border-box;display:flex;flex-direction:column;flex-grow:1;justify-content:center;min-height:100px;padding:10px}.tox .tox-dropzone p{color:rgba(34,47,62,.7);margin:0 0 16px 0}.tox .tox-edit-area{display:flex;flex:1;-ms-flex-preferred-size:auto;overflow:hidden;position:relative}.tox .tox-edit-area__iframe{background-color:#fff;border:0;box-sizing:border-box;flex:1;-ms-flex-preferred-size:auto;height:100%;position:absolute;width:100%}.tox.tox-inline-edit-area{border:1px dotted #ccc}.tox .tox-editor-container{display:flex;flex:1 1 auto;flex-direction:column;overflow:hidden}.tox .tox-editor-header{z-index:1}.tox:not(.tox-tinymce-inline) .tox-editor-header{box-shadow:none;transition:box-shadow .5s}.tox.tox-tinymce--toolbar-bottom .tox-editor-header,.tox.tox-tinymce-inline .tox-editor-header{margin-bottom:-1px}.tox.tox-tinymce--toolbar-sticky-on .tox-editor-header{background-color:transparent;box-shadow:0 4px 4px -3px rgba(0,0,0,.25)}.tox-editor-dock-fadeout{opacity:0;visibility:hidden}.tox-editor-dock-fadein{opacity:1;visibility:visible}.tox-editor-dock-transition{transition:visibility 0s linear .25s,opacity .25s ease}.tox-editor-dock-transition.tox-editor-dock-fadein{transition-delay:0s}.tox .tox-control-wrap{flex:1;position:relative}.tox .tox-control-wrap:not(.tox-control-wrap--status-invalid) .tox-control-wrap__status-icon-invalid,.tox .tox-control-wrap:not(.tox-control-wrap--status-unknown) .tox-control-wrap__status-icon-unknown,.tox .tox-control-wrap:not(.tox-control-wrap--status-valid) .tox-control-wrap__status-icon-valid{display:none}.tox .tox-control-wrap svg{display:block}.tox .tox-control-wrap__status-icon-wrap{position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-control-wrap__status-icon-invalid svg{fill:#c00}.tox .tox-control-wrap__status-icon-unknown svg{fill:orange}.tox .tox-control-wrap__status-icon-valid svg{fill:green}.tox:not([dir=rtl]) .tox-control-wrap--status-invalid .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-unknown .tox-textfield,.tox:not([dir=rtl]) .tox-control-wrap--status-valid .tox-textfield{padding-right:32px}.tox:not([dir=rtl]) .tox-control-wrap__status-icon-wrap{right:4px}.tox[dir=rtl] .tox-control-wrap--status-invalid .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-unknown .tox-textfield,.tox[dir=rtl] .tox-control-wrap--status-valid .tox-textfield{padding-left:32px}.tox[dir=rtl] .tox-control-wrap__status-icon-wrap{left:4px}.tox .tox-autocompleter{max-width:25em}.tox .tox-autocompleter .tox-menu{max-width:25em}.tox .tox-autocompleter .tox-autocompleter-highlight{font-weight:700}.tox .tox-color-input{display:flex;position:relative;z-index:1}.tox .tox-color-input .tox-textfield{z-index:-1}.tox .tox-color-input span{border-color:rgba(34,47,62,.2);border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;height:24px;position:absolute;top:6px;width:24px}.tox .tox-color-input span:focus:not([aria-disabled=true]),.tox .tox-color-input span:hover:not([aria-disabled=true]){border-color:#207ab7;cursor:pointer}.tox .tox-color-input span::before{background-image:linear-gradient(45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(-45deg,rgba(0,0,0,.25) 25%,transparent 25%),linear-gradient(45deg,transparent 75%,rgba(0,0,0,.25) 75%),linear-gradient(-45deg,transparent 75%,rgba(0,0,0,.25) 75%);background-position:0 0,0 6px,6px -6px,-6px 0;background-size:12px 12px;border:1px solid #fff;border-radius:3px;box-sizing:border-box;content:'';height:24px;left:-1px;position:absolute;top:-1px;width:24px;z-index:-1}.tox .tox-color-input span[aria-disabled=true]{cursor:not-allowed}.tox:not([dir=rtl]) .tox-color-input .tox-textfield{padding-left:36px}.tox:not([dir=rtl]) .tox-color-input span{left:6px}.tox[dir=rtl] .tox-color-input .tox-textfield{padding-right:36px}.tox[dir=rtl] .tox-color-input span{right:6px}.tox .tox-label,.tox .tox-toolbar-label{color:rgba(34,47,62,.7);display:block;font-size:14px;font-style:normal;font-weight:400;line-height:1.3;padding:0 8px 0 0;text-transform:none;white-space:nowrap}.tox .tox-toolbar-label{padding:0 8px}.tox[dir=rtl] .tox-label{padding:0 0 0 8px}.tox .tox-form{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group{box-sizing:border-box;margin-bottom:4px}.tox .tox-form-group--maximize{flex:1}.tox .tox-form__group--error{color:#c00}.tox .tox-form__group--collection{display:flex}.tox .tox-form__grid{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between}.tox .tox-form__grid--2col>.tox-form__group{width:calc(50% - (8px / 2))}.tox .tox-form__grid--3col>.tox-form__group{width:calc(100% / 3 - (8px / 2))}.tox .tox-form__grid--4col>.tox-form__group{width:calc(25% - (8px / 2))}.tox .tox-form__controls-h-stack{align-items:center;display:flex}.tox .tox-form__group--inline{align-items:center;display:flex}.tox .tox-form__group--stretched{display:flex;flex:1;flex-direction:column;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-textarea{flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-form__group--stretched .tox-navobj :nth-child(2){flex:1;-ms-flex-preferred-size:auto;height:100%}.tox:not([dir=rtl]) .tox-form__controls-h-stack>:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-form__controls-h-stack>:not(:first-child){margin-right:4px}.tox .tox-lock.tox-locked .tox-lock-icon__unlock,.tox .tox-lock:not(.tox-locked) .tox-lock-icon__lock{display:none}.tox .tox-listboxfield .tox-listbox--select,.tox .tox-textarea,.tox .tox-textfield,.tox .tox-toolbar-textfield{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-textarea[disabled],.tox .tox-textfield[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listboxfield .tox-listbox--select:focus,.tox .tox-textarea:focus,.tox .tox-textfield:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-toolbar-textfield{border-width:0;margin-bottom:3px;margin-top:2px;max-width:250px}.tox .tox-naked-btn{background-color:transparent;border:0;border-color:transparent;box-shadow:unset;color:#207ab7;cursor:pointer;display:block;margin:0;padding:0}.tox .tox-naked-btn svg{display:block;fill:#222f3e}.tox:not([dir=rtl]) .tox-toolbar-textfield+*{margin-left:4px}.tox[dir=rtl] .tox-toolbar-textfield+*{margin-right:4px}.tox .tox-listboxfield{cursor:pointer;position:relative}.tox .tox-listboxfield .tox-listbox--select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-listbox__select-label{cursor:default;flex:1;margin:0 4px}.tox .tox-listbox__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-listbox__select-chevron svg{fill:#222f3e}.tox .tox-listboxfield .tox-listbox--select{align-items:center;display:flex}.tox:not([dir=rtl]) .tox-listboxfield svg{right:8px}.tox[dir=rtl] .tox-listboxfield svg{left:8px}.tox .tox-selectfield{cursor:pointer;position:relative}.tox .tox-selectfield select{-webkit-appearance:none;-moz-appearance:none;appearance:none;background-color:#fff;border-color:#ccc;border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;color:#222f3e;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;font-size:16px;line-height:24px;margin:0;min-height:34px;outline:0;padding:5px 4.75px;resize:none;width:100%}.tox .tox-selectfield select[disabled]{background-color:#f2f2f2;color:rgba(34,47,62,.85);cursor:not-allowed}.tox .tox-selectfield select::-ms-expand{display:none}.tox .tox-selectfield select:focus{background-color:#fff;border-color:#207ab7;box-shadow:none;outline:0}.tox .tox-selectfield svg{pointer-events:none;position:absolute;top:50%;transform:translateY(-50%)}.tox:not([dir=rtl]) .tox-selectfield select[size="0"],.tox:not([dir=rtl]) .tox-selectfield select[size="1"]{padding-right:24px}.tox:not([dir=rtl]) .tox-selectfield svg{right:8px}.tox[dir=rtl] .tox-selectfield select[size="0"],.tox[dir=rtl] .tox-selectfield select[size="1"]{padding-left:24px}.tox[dir=rtl] .tox-selectfield svg{left:8px}.tox .tox-textarea{-webkit-appearance:textarea;-moz-appearance:textarea;appearance:textarea;white-space:pre-wrap}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}.tox .tox-help__more-link{list-style:none;margin-top:1em}.tox .tox-image-tools{width:100%}.tox .tox-image-tools__toolbar{align-items:center;display:flex;justify-content:center}.tox .tox-image-tools__image{background-color:#666;height:380px;overflow:auto;position:relative;width:100%}.tox .tox-image-tools__image,.tox .tox-image-tools__image+.tox-image-tools__toolbar{margin-top:8px}.tox .tox-image-tools__image-bg{background:url(data:image/gif;base64,R0lGODdhDAAMAIABAMzMzP///ywAAAAADAAMAAACFoQfqYeabNyDMkBQb81Uat85nxguUAEAOw==)}.tox .tox-image-tools__toolbar>.tox-spacer{flex:1;-ms-flex-preferred-size:auto}.tox .tox-croprect-block{background:#000;opacity:.5;position:absolute;zoom:1}.tox .tox-croprect-handle{border:2px solid #fff;height:20px;left:0;position:absolute;top:0;width:20px}.tox .tox-croprect-handle-move{border:0;cursor:move;position:absolute}.tox .tox-croprect-handle-nw{border-width:2px 0 0 2px;cursor:nw-resize;left:100px;margin:-2px 0 0 -2px;top:100px}.tox .tox-croprect-handle-ne{border-width:2px 2px 0 0;cursor:ne-resize;left:200px;margin:-2px 0 0 -20px;top:100px}.tox .tox-croprect-handle-sw{border-width:0 0 2px 2px;cursor:sw-resize;left:100px;margin:-20px 2px 0 -2px;top:200px}.tox .tox-croprect-handle-se{border-width:0 2px 2px 0;cursor:se-resize;left:200px;margin:-20px 0 0 -20px;top:200px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-left:8px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-left:32px}.tox:not([dir=rtl]) .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-left:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider:not(:first-of-type){margin-right:8px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-button+.tox-slider{margin-right:32px}.tox[dir=rtl] .tox-image-tools__toolbar>.tox-slider+.tox-button{margin-right:32px}.tox .tox-insert-table-picker{display:flex;flex-wrap:wrap;width:170px}.tox .tox-insert-table-picker>div{border-color:#ccc;border-style:solid;border-width:0 1px 1px 0;box-sizing:border-box;height:17px;width:17px}.tox .tox-collection--list .tox-collection__group .tox-insert-table-picker{margin:-4px 0}.tox .tox-insert-table-picker .tox-insert-table-picker__selected{background-color:rgba(32,122,183,.5);border-color:rgba(32,122,183,.5)}.tox .tox-insert-table-picker__label{color:rgba(34,47,62,.7);display:block;font-size:14px;padding:4px;text-align:center;width:100%}.tox:not([dir=rtl]) .tox-insert-table-picker>div:nth-child(10n){border-right:0}.tox[dir=rtl] .tox-insert-table-picker>div:nth-child(10n+1){border-right:0}.tox .tox-menu{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 4px 8px 0 rgba(34,47,62,.1);display:inline-block;overflow:hidden;vertical-align:top;z-index:1150}.tox .tox-menu.tox-collection.tox-collection--list{padding:0}.tox .tox-menu.tox-collection.tox-collection--toolbar{padding:4px}.tox .tox-menu.tox-collection.tox-collection--grid{padding:4px}.tox .tox-menu__label blockquote,.tox .tox-menu__label code,.tox .tox-menu__label h1,.tox .tox-menu__label h2,.tox .tox-menu__label h3,.tox .tox-menu__label h4,.tox .tox-menu__label h5,.tox .tox-menu__label h6,.tox .tox-menu__label p{margin:0}.tox .tox-menubar{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 4px 0 4px}.tox.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-menubar{border-top:1px solid #ccc}.tox .tox-mbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0 4px;text-transform:none;width:auto}.tox .tox-mbtn[disabled]{background-color:transparent;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-mbtn:focus:not(:disabled){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn--active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn:hover:not(:disabled):not(.tox-mbtn--active){background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-mbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-mbtn[disabled] .tox-mbtn__select-label{cursor:not-allowed}.tox .tox-mbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px;display:none}.tox .tox-notification{border-radius:3px;border-style:solid;border-width:1px;box-shadow:none;box-sizing:border-box;display:-ms-grid;display:grid;font-size:14px;font-weight:400;-ms-grid-columns:minmax(40px,1fr) auto minmax(40px,1fr);grid-template-columns:minmax(40px,1fr) auto minmax(40px,1fr);margin-top:4px;opacity:0;padding:4px;transition:transform .1s ease-in,opacity 150ms ease-in}.tox .tox-notification p{font-size:14px;font-weight:400}.tox .tox-notification a{cursor:pointer;text-decoration:underline}.tox .tox-notification--in{opacity:1}.tox .tox-notification--success{background-color:#e4eeda;border-color:#d7e6c8;color:#222f3e}.tox .tox-notification--success p{color:#222f3e}.tox .tox-notification--success a{color:#547831}.tox .tox-notification--success svg{fill:#222f3e}.tox .tox-notification--error{background-color:#f8dede;border-color:#f2bfbf;color:#222f3e}.tox .tox-notification--error p{color:#222f3e}.tox .tox-notification--error a{color:#c00}.tox .tox-notification--error svg{fill:#222f3e}.tox .tox-notification--warn,.tox .tox-notification--warning{background-color:#fffaea;border-color:#ffe89d;color:#222f3e}.tox .tox-notification--warn p,.tox .tox-notification--warning p{color:#222f3e}.tox .tox-notification--warn a,.tox .tox-notification--warning a{color:#222f3e}.tox .tox-notification--warn svg,.tox .tox-notification--warning svg{fill:#222f3e}.tox .tox-notification--info{background-color:#d9edf7;border-color:#779ecb;color:#222f3e}.tox .tox-notification--info p{color:#222f3e}.tox .tox-notification--info a{color:#222f3e}.tox .tox-notification--info svg{fill:#222f3e}.tox .tox-notification__body{-ms-grid-row-align:center;align-self:center;color:#222f3e;font-size:14px;-ms-grid-column-span:1;grid-column-end:3;-ms-grid-column:2;grid-column-start:2;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;text-align:center;white-space:normal;word-break:break-all;word-break:break-word}.tox .tox-notification__body>*{margin:0}.tox .tox-notification__body>*+*{margin-top:1rem}.tox .tox-notification__icon{-ms-grid-row-align:center;align-self:center;-ms-grid-column-span:1;grid-column-end:2;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification__icon svg{display:block}.tox .tox-notification__dismiss{-ms-grid-row-align:start;align-self:start;-ms-grid-column-span:1;grid-column-end:4;-ms-grid-column:3;grid-column-start:3;-ms-grid-row-span:1;grid-row-end:2;-ms-grid-row:1;grid-row-start:1;-ms-grid-column-align:end;justify-self:end}.tox .tox-notification .tox-progress-bar{-ms-grid-column-span:3;grid-column-end:4;-ms-grid-column:1;grid-column-start:1;-ms-grid-row-span:1;grid-row-end:3;-ms-grid-row:2;grid-row-start:2;-ms-grid-column-align:center;justify-self:center}.tox .tox-pop{display:inline-block;position:relative}.tox .tox-pop--resizing{transition:width .1s ease}.tox .tox-pop--resizing .tox-toolbar,.tox .tox-pop--resizing .tox-toolbar__group{flex-wrap:nowrap}.tox .tox-pop--transition{transition:.15s ease;transition-property:left,right,top,bottom}.tox .tox-pop--transition::after,.tox .tox-pop--transition::before{transition:all .15s,visibility 0s,opacity 75ms ease 75ms}.tox .tox-pop__dialog{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15);min-width:0;overflow:hidden}.tox .tox-pop__dialog>:not(.tox-toolbar){margin:4px 4px 4px 8px}.tox .tox-pop__dialog .tox-toolbar{background-color:transparent;margin-bottom:-1px}.tox .tox-pop::after,.tox .tox-pop::before{border-style:solid;content:'';display:block;height:0;opacity:1;position:absolute;width:0}.tox .tox-pop.tox-pop--inset::after,.tox .tox-pop.tox-pop--inset::before{opacity:0;transition:all 0s .15s,visibility 0s,opacity 75ms ease}.tox .tox-pop.tox-pop--bottom::after,.tox .tox-pop.tox-pop--bottom::before{left:50%;top:100%}.tox .tox-pop.tox-pop--bottom::after{border-color:#fff transparent transparent transparent;border-width:8px;margin-left:-8px;margin-top:-1px}.tox .tox-pop.tox-pop--bottom::before{border-color:#ccc transparent transparent transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--top::after,.tox .tox-pop.tox-pop--top::before{left:50%;top:0;transform:translateY(-100%)}.tox .tox-pop.tox-pop--top::after{border-color:transparent transparent #fff transparent;border-width:8px;margin-left:-8px;margin-top:1px}.tox .tox-pop.tox-pop--top::before{border-color:transparent transparent #ccc transparent;border-width:9px;margin-left:-9px}.tox .tox-pop.tox-pop--left::after,.tox .tox-pop.tox-pop--left::before{left:0;top:calc(50% - 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--left::after{border-color:transparent #fff transparent transparent;border-width:8px;margin-left:-15px}.tox .tox-pop.tox-pop--left::before{border-color:transparent #ccc transparent transparent;border-width:10px;margin-left:-19px}.tox .tox-pop.tox-pop--right::after,.tox .tox-pop.tox-pop--right::before{left:100%;top:calc(50% + 1px);transform:translateY(-50%)}.tox .tox-pop.tox-pop--right::after{border-color:transparent transparent transparent #fff;border-width:8px;margin-left:-1px}.tox .tox-pop.tox-pop--right::before{border-color:transparent transparent transparent #ccc;border-width:10px;margin-left:-1px}.tox .tox-pop.tox-pop--align-left::after,.tox .tox-pop.tox-pop--align-left::before{left:20px}.tox .tox-pop.tox-pop--align-right::after,.tox .tox-pop.tox-pop--align-right::before{left:calc(100% - 20px)}.tox .tox-sidebar-wrap{display:flex;flex-direction:row;flex-grow:1;-ms-flex-preferred-size:0;min-height:0}.tox .tox-sidebar{background-color:#fff;display:flex;flex-direction:row;justify-content:flex-end}.tox .tox-sidebar__slider{display:flex;overflow:hidden}.tox .tox-sidebar__pane-container{display:flex}.tox .tox-sidebar__pane{display:flex}.tox .tox-sidebar--sliding-closed{opacity:0}.tox .tox-sidebar--sliding-open{opacity:1}.tox .tox-sidebar--sliding-growing,.tox .tox-sidebar--sliding-shrinking{transition:width .5s ease,opacity .5s ease}.tox .tox-selector{background-color:#4099ff;border-color:#4099ff;border-style:solid;border-width:1px;box-sizing:border-box;display:inline-block;height:10px;position:absolute;width:10px}.tox.tox-platform-touch .tox-selector{height:12px;width:12px}.tox .tox-slider{align-items:center;display:flex;flex:1;-ms-flex-preferred-size:auto;height:24px;justify-content:center;position:relative}.tox .tox-slider__rail{background-color:transparent;border:1px solid #ccc;border-radius:3px;height:10px;min-width:120px;width:100%}.tox .tox-slider__handle{background-color:#207ab7;border:2px solid #185d8c;border-radius:3px;box-shadow:none;height:24px;left:50%;position:absolute;top:50%;transform:translateX(-50%) translateY(-50%);width:14px}.tox .tox-source-code{overflow:auto}.tox .tox-spinner{display:flex}.tox .tox-spinner>div{animation:tam-bouncing-dots 1.5s ease-in-out 0s infinite both;background-color:rgba(34,47,62,.7);border-radius:100%;height:8px;width:8px}.tox .tox-spinner>div:nth-child(1){animation-delay:-.32s}.tox .tox-spinner>div:nth-child(2){animation-delay:-.16s}@keyframes tam-bouncing-dots{0%,100%,80%{transform:scale(0)}40%{transform:scale(1)}}.tox:not([dir=rtl]) .tox-spinner>div:not(:first-child){margin-left:4px}.tox[dir=rtl] .tox-spinner>div:not(:first-child){margin-right:4px}.tox .tox-statusbar{align-items:center;background-color:#fff;border-top:1px solid #ccc;color:rgba(34,47,62,.7);display:flex;flex:0 0 auto;font-size:12px;font-weight:400;height:18px;overflow:hidden;padding:0 8px;position:relative;text-transform:uppercase}.tox .tox-statusbar__text-container{display:flex;flex:1 1 auto;justify-content:flex-end;overflow:hidden}.tox .tox-statusbar__path{display:flex;flex:1 1 auto;margin-right:auto;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.tox .tox-statusbar__path>*{display:inline;white-space:nowrap}.tox .tox-statusbar__wordcount{flex:0 0 auto;margin-left:1ch}.tox .tox-statusbar a,.tox .tox-statusbar__path-item,.tox .tox-statusbar__wordcount{color:rgba(34,47,62,.7);text-decoration:none}.tox .tox-statusbar a:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar a:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__path-item:hover:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:focus:not(:disabled):not([aria-disabled=true]),.tox .tox-statusbar__wordcount:hover:not(:disabled):not([aria-disabled=true]){cursor:pointer;text-decoration:underline}.tox .tox-statusbar__resize-handle{align-items:flex-end;align-self:stretch;cursor:nwse-resize;display:flex;flex:0 0 auto;justify-content:flex-end;margin-left:auto;margin-right:-8px;padding-left:1ch}.tox .tox-statusbar__resize-handle svg{display:block;fill:rgba(34,47,62,.7)}.tox .tox-statusbar__resize-handle:focus svg{background-color:#dee0e2;border-radius:1px;box-shadow:0 0 0 2px #dee0e2}.tox:not([dir=rtl]) .tox-statusbar__path>*{margin-right:4px}.tox:not([dir=rtl]) .tox-statusbar__branding{margin-left:1ch}.tox[dir=rtl] .tox-statusbar{flex-direction:row-reverse}.tox[dir=rtl] .tox-statusbar__path>*{margin-left:4px}.tox .tox-throbber{z-index:1299}.tox .tox-throbber__busy-spinner{align-items:center;background-color:rgba(255,255,255,.6);bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}.tox .tox-tbtn{align-items:center;background:0 0;border:0;border-radius:3px;box-shadow:none;color:#222f3e;display:flex;flex:0 0 auto;font-size:14px;font-style:normal;font-weight:400;height:34px;justify-content:center;margin:2px 0 3px 0;outline:0;overflow:hidden;padding:0;text-transform:none;width:34px}.tox .tox-tbtn svg{display:block;fill:#222f3e}.tox .tox-tbtn.tox-tbtn-more{padding-left:5px;padding-right:5px;width:inherit}.tox .tox-tbtn:focus{background:#dee0e2;border:0;box-shadow:none}.tox .tox-tbtn:hover{background:#dee0e2;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:hover svg{fill:#222f3e}.tox .tox-tbtn:active{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn:active svg{fill:#222f3e}.tox .tox-tbtn--disabled,.tox .tox-tbtn--disabled:hover,.tox .tox-tbtn:disabled,.tox .tox-tbtn:disabled:hover{background:0 0;border:0;box-shadow:none;color:rgba(34,47,62,.5);cursor:not-allowed}.tox .tox-tbtn--disabled svg,.tox .tox-tbtn--disabled:hover svg,.tox .tox-tbtn:disabled svg,.tox .tox-tbtn:disabled:hover svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--enabled,.tox .tox-tbtn--enabled:hover{background:#c8cbcf;border:0;box-shadow:none;color:#222f3e}.tox .tox-tbtn--enabled:hover>*,.tox .tox-tbtn--enabled>*{transform:none}.tox .tox-tbtn--enabled svg,.tox .tox-tbtn--enabled:hover svg{fill:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled){color:#222f3e}.tox .tox-tbtn:focus:not(.tox-tbtn--disabled) svg{fill:#222f3e}.tox .tox-tbtn:active>*{transform:none}.tox .tox-tbtn--md{height:51px;width:51px}.tox .tox-tbtn--lg{flex-direction:column;height:68px;width:68px}.tox .tox-tbtn--return{-ms-grid-row-align:stretch;align-self:stretch;height:unset;width:16px}.tox .tox-tbtn--labeled{padding:0 4px;width:unset}.tox .tox-tbtn__vlabel{display:block;font-size:10px;font-weight:400;letter-spacing:-.025em;margin-bottom:4px;white-space:nowrap}.tox .tox-tbtn--select{margin:2px 0 3px 0;padding:0 4px;width:auto}.tox .tox-tbtn__select-label{cursor:default;font-weight:400;margin:0 4px}.tox .tox-tbtn__select-chevron{align-items:center;display:flex;justify-content:center;width:16px}.tox .tox-tbtn__select-chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-tbtn--bespoke .tox-tbtn__select-label{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;width:7em}.tox .tox-split-button{border:0;border-radius:3px;box-sizing:border-box;display:flex;margin:2px 0 3px 0;overflow:hidden}.tox .tox-split-button:hover{box-shadow:0 0 0 1px #dee0e2 inset}.tox .tox-split-button:focus{background:#dee0e2;box-shadow:none;color:#222f3e}.tox .tox-split-button>*{border-radius:0}.tox .tox-split-button__chevron{width:16px}.tox .tox-split-button__chevron svg{fill:rgba(34,47,62,.5)}.tox .tox-split-button .tox-tbtn{margin:0}.tox.tox-platform-touch .tox-split-button .tox-tbtn:first-child{width:30px}.tox.tox-platform-touch .tox-split-button__chevron{width:20px}.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:focus,.tox .tox-split-button.tox-tbtn--disabled .tox-tbtn:hover,.tox .tox-split-button.tox-tbtn--disabled:focus,.tox .tox-split-button.tox-tbtn--disabled:hover{background:0 0;box-shadow:none;color:rgba(34,47,62,.5)}.tox .tox-toolbar-overlord{background-color:#fff}.tox .tox-toolbar,.tox .tox-toolbar__overflow,.tox .tox-toolbar__primary{background:url("data:image/svg+xml;charset=utf8,%3Csvg height='39px' viewBox='0 0 40 39px' width='40' xmlns='http://www.w3.org/2000/svg'%3E%3Crect x='0' y='38px' width='100' height='1' fill='%23cccccc'/%3E%3C/svg%3E") left 0 top 0 #fff;background-color:#fff;display:flex;flex:0 0 auto;flex-shrink:0;flex-wrap:wrap;padding:0 0}.tox .tox-toolbar__overflow.tox-toolbar__overflow--closed{height:0;opacity:0;padding-bottom:0;padding-top:0;visibility:hidden}.tox .tox-toolbar__overflow--growing{transition:height .3s ease,opacity .2s linear .1s}.tox .tox-toolbar__overflow--shrinking{transition:opacity .3s ease,height .2s linear .1s,visibility 0s linear .3s}.tox .tox-menubar+.tox-toolbar,.tox .tox-menubar+.tox-toolbar-overlord .tox-toolbar__primary{border-top:1px solid #ccc;margin-top:-1px}.tox .tox-toolbar--scrolling{flex-wrap:nowrap;overflow-x:auto}.tox .tox-pop .tox-toolbar{border-width:0}.tox .tox-toolbar--no-divider{background-image:none}.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar-overlord:first-child .tox-toolbar__primary,.tox-tinymce:not(.tox-tinymce-inline) .tox-editor-header:not(:first-child) .tox-toolbar:first-child{border-top:1px solid #ccc}.tox.tox-tinymce-aux .tox-toolbar__overflow{background-color:#fff;border:1px solid #ccc;border-radius:3px;box-shadow:0 1px 3px rgba(0,0,0,.15)}.tox .tox-toolbar__group{align-items:center;display:flex;flex-wrap:wrap;margin:0 0;padding:0 4px 0 4px}.tox .tox-toolbar__group--pull-right{margin-left:auto}.tox .tox-toolbar--scrolling .tox-toolbar__group{flex-shrink:0;flex-wrap:nowrap}.tox:not([dir=rtl]) .tox-toolbar__group:not(:last-of-type){border-right:1px solid #ccc}.tox[dir=rtl] .tox-toolbar__group:not(:last-of-type){border-left:1px solid #ccc}.tox .tox-tooltip{display:inline-block;padding:8px;position:relative}.tox .tox-tooltip__body{background-color:#222f3e;border-radius:3px;box-shadow:0 2px 4px rgba(34,47,62,.3);color:rgba(255,255,255,.75);font-size:14px;font-style:normal;font-weight:400;padding:4px 8px;text-transform:none}.tox .tox-tooltip__arrow{position:absolute}.tox .tox-tooltip--down .tox-tooltip__arrow{border-left:8px solid transparent;border-right:8px solid transparent;border-top:8px solid #222f3e;bottom:0;left:50%;position:absolute;transform:translateX(-50%)}.tox .tox-tooltip--up .tox-tooltip__arrow{border-bottom:8px solid #222f3e;border-left:8px solid transparent;border-right:8px solid transparent;left:50%;position:absolute;top:0;transform:translateX(-50%)}.tox .tox-tooltip--right .tox-tooltip__arrow{border-bottom:8px solid transparent;border-left:8px solid #222f3e;border-top:8px solid transparent;position:absolute;right:0;top:50%;transform:translateY(-50%)}.tox .tox-tooltip--left .tox-tooltip__arrow{border-bottom:8px solid transparent;border-right:8px solid #222f3e;border-top:8px solid transparent;left:0;position:absolute;top:50%;transform:translateY(-50%)}.tox .tox-well{border:1px solid #ccc;border-radius:3px;padding:8px;width:100%}.tox .tox-well>:first-child{margin-top:0}.tox .tox-well>:last-child{margin-bottom:0}.tox .tox-well>:only-child{margin:0}.tox .tox-custom-editor{border:1px solid #ccc;border-radius:3px;display:flex;flex:1;position:relative}.tox .tox-dialog-loading::before{background-color:rgba(0,0,0,.5);content:"";height:100%;position:absolute;width:100%;z-index:1000}.tox .tox-tab{cursor:pointer}.tox .tox-dialog__content-js{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-dialog__body-content .tox-collection{display:flex;flex:1;-ms-flex-preferred-size:auto}.tox .tox-image-tools-edit-panel{height:60px}.tox .tox-image-tools__sidebar{height:60px}
diff --git a/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css b/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
new file mode 100644
index 0000000..3a45cac
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide/skin.mobile.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+.tinymce-mobile-outer-container{all:initial;display:block}.tinymce-mobile-outer-container *{border:0;box-sizing:initial;cursor:inherit;float:none;line-height:1;margin:0;outline:0;padding:0;-webkit-tap-highlight-color:transparent;text-shadow:none;white-space:nowrap}.tinymce-mobile-icon-arrow-back::before{content:"\e5cd"}.tinymce-mobile-icon-image::before{content:"\e412"}.tinymce-mobile-icon-cancel-circle::before{content:"\e5c9"}.tinymce-mobile-icon-full-dot::before{content:"\e061"}.tinymce-mobile-icon-align-center::before{content:"\e234"}.tinymce-mobile-icon-align-left::before{content:"\e236"}.tinymce-mobile-icon-align-right::before{content:"\e237"}.tinymce-mobile-icon-bold::before{content:"\e238"}.tinymce-mobile-icon-italic::before{content:"\e23f"}.tinymce-mobile-icon-unordered-list::before{content:"\e241"}.tinymce-mobile-icon-ordered-list::before{content:"\e242"}.tinymce-mobile-icon-font-size::before{content:"\e245"}.tinymce-mobile-icon-underline::before{content:"\e249"}.tinymce-mobile-icon-link::before{content:"\e157"}.tinymce-mobile-icon-unlink::before{content:"\eca2"}.tinymce-mobile-icon-color::before{content:"\e891"}.tinymce-mobile-icon-previous::before{content:"\e314"}.tinymce-mobile-icon-next::before{content:"\e315"}.tinymce-mobile-icon-large-font::before,.tinymce-mobile-icon-style-formats::before{content:"\e264"}.tinymce-mobile-icon-undo::before{content:"\e166"}.tinymce-mobile-icon-redo::before{content:"\e15a"}.tinymce-mobile-icon-removeformat::before{content:"\e239"}.tinymce-mobile-icon-small-font::before{content:"\e906"}.tinymce-mobile-format-matches::after,.tinymce-mobile-icon-readonly-back::before{content:"\e5ca"}.tinymce-mobile-icon-small-heading::before{content:"small"}.tinymce-mobile-icon-large-heading::before{content:"large"}.tinymce-mobile-icon-large-heading::before,.tinymce-mobile-icon-small-heading::before{font-family:sans-serif;font-size:80%}.tinymce-mobile-mask-edit-icon::before{content:"\e254"}.tinymce-mobile-icon-back::before{content:"\e5c4"}.tinymce-mobile-icon-heading::before{content:"Headings";font-family:sans-serif;font-size:80%;font-weight:700}.tinymce-mobile-icon-h1::before{content:"H1";font-weight:700}.tinymce-mobile-icon-h2::before{content:"H2";font-weight:700}.tinymce-mobile-icon-h3::before{content:"H3";font-weight:700}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask{align-items:center;display:flex;justify-content:center;background:rgba(51,51,51,.5);height:100%;position:absolute;top:0;width:100%}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container{align-items:center;border-radius:50%;display:flex;flex-direction:column;font-family:sans-serif;font-size:1em;justify-content:space-between}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .mixin-menu-item{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{align-items:center;display:flex;justify-content:center;flex-direction:column;font-size:1em}@media only screen and (min-device-width:700px){.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section{font-size:1.2em}}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon{align-items:center;display:flex;justify-content:center;border-radius:50%;height:2.1em;width:2.1em;background-color:#fff;color:#207ab7}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section .tinymce-mobile-mask-tap-icon::before{content:"\e900";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-outer-container .tinymce-mobile-disabled-mask .tinymce-mobile-content-container .tinymce-mobile-content-tap-section:not(.tinymce-mobile-mask-tap-icon-selected) .tinymce-mobile-mask-tap-icon{z-index:2}.tinymce-mobile-android-container.tinymce-mobile-android-maximized{background:#fff;border:none;bottom:0;display:flex;flex-direction:column;left:0;position:fixed;right:0;top:0}.tinymce-mobile-android-container:not(.tinymce-mobile-android-maximized){position:relative}.tinymce-mobile-android-container .tinymce-mobile-editor-socket{display:flex;flex-grow:1}.tinymce-mobile-android-container .tinymce-mobile-editor-socket iframe{display:flex!important;flex-grow:1;height:auto!important}.tinymce-mobile-android-scroll-reload{overflow:hidden}:not(.tinymce-mobile-readonly-mode)>.tinymce-mobile-android-selection-context-toolbar{margin-top:23px}.tinymce-mobile-toolstrip{background:#fff;display:flex;flex:0 0 auto;z-index:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar{align-items:center;background-color:#fff;border-bottom:1px solid #ccc;display:flex;flex:1;height:2.5em;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex-shrink:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-exit-container{background:#f44336}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group.tinymce-mobile-toolbar-scrollable-group{flex-grow:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button{align-items:center;display:flex;height:80%;margin-left:2px;margin-right:2px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item.tinymce-mobile-toolbar-button.tinymce-mobile-toolbar-button-selected{background:#c8cbcf;color:#ccc}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:first-of-type,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar:not(.tinymce-mobile-context-toolbar) .tinymce-mobile-toolbar-group:last-of-type{background:#207ab7;color:#eceff1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group{align-items:center;display:flex;height:100%;flex:1;padding-bottom:.4em;padding-top:.4em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog{display:flex;min-height:1.5em;overflow:hidden;padding-left:0;padding-right:0;position:relative;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain{display:flex;height:100%;transition:left cubic-bezier(.4,0,1,1) .15s;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen{display:flex;flex:0 0 auto;justify-content:space-between;width:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen input{font-family:Sans-serif}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container{display:flex;flex-grow:1;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container .tinymce-mobile-input-container-x{-ms-grid-row-align:center;align-self:center;background:inherit;border:none;border-radius:50%;color:#888;font-size:.6em;font-weight:700;height:100%;padding-right:2px;position:absolute;right:0}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-input-container.tinymce-mobile-input-container-empty .tinymce-mobile-input-container-x{display:none}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous::before{align-items:center;display:flex;font-weight:700;height:100%;padding-left:.5em;padding-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-next.tinymce-mobile-toolbar-navigation-disabled::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serialised-dialog .tinymce-mobile-serialised-dialog-chain .tinymce-mobile-serialised-dialog-screen .tinymce-mobile-icon-previous.tinymce-mobile-toolbar-navigation-disabled::before{visibility:hidden}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item{color:#ccc;font-size:10px;line-height:10px;margin:0 2px;padding-top:3px}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-item.tinymce-mobile-dot-active{color:#c8cbcf}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-large-heading::before{margin-left:.5em;margin-right:.9em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-font::before,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-icon-small-heading::before{margin-left:.9em;margin-right:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider{display:flex;flex:1;margin-left:0;margin-right:0;padding:.28em 0;position:relative}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-size-container .tinymce-mobile-slider-size-line{background:#ccc;display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container{padding-left:2em;padding-right:2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container{align-items:center;display:flex;flex-grow:1;height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-slider-gradient-container .tinymce-mobile-slider-gradient{background:linear-gradient(to right,red 0,#feff00 17%,#0f0 33%,#00feff 50%,#00f 67%,#ff00fe 83%,red 100%);display:flex;flex:1;height:.2em;margin-bottom:.3em;margin-top:.3em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-black{background:#000;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider.tinymce-mobile-hue-slider-container .tinymce-mobile-hue-slider-white{background:#fff;height:.2em;margin-bottom:.3em;margin-top:.3em;width:1.2em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb{align-items:center;background-clip:padding-box;background-color:#455a64;border:.5em solid rgba(136,136,136,0);border-radius:3em;bottom:0;color:#fff;display:flex;height:.5em;justify-content:center;left:-10px;margin:auto;position:absolute;top:0;transition:border 120ms cubic-bezier(.39,.58,.57,1);width:.5em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-slider .tinymce-mobile-slider-thumb.tinymce-mobile-thumb-active{border:.5em solid rgba(136,136,136,.39)}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper,.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group>div{align-items:center;display:flex;height:100%;flex:1}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-serializer-wrapper{flex-direction:column;justify-content:center}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item{align-items:center;display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-toolbar-group-item:not(.tinymce-mobile-serialised-dialog){height:100%}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group .tinymce-mobile-dot-container{display:flex}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input{background:#fff;border:none;border-radius:0;color:#455a64;flex-grow:1;font-size:.85em;padding-bottom:.1em;padding-left:5px;padding-top:.1em}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::-webkit-input-placeholder{color:#888}.tinymce-mobile-toolstrip .tinymce-mobile-toolbar.tinymce-mobile-context-toolbar .tinymce-mobile-toolbar-group input::placeholder{color:#888}.tinymce-mobile-dropup{background:#fff;display:flex;overflow:hidden;width:100%}.tinymce-mobile-dropup.tinymce-mobile-dropup-shrinking{transition:height .3s ease-out}.tinymce-mobile-dropup.tinymce-mobile-dropup-growing{transition:height .3s ease-in}.tinymce-mobile-dropup.tinymce-mobile-dropup-closed{flex-grow:0}.tinymce-mobile-dropup.tinymce-mobile-dropup-open:not(.tinymce-mobile-dropup-growing){flex-grow:1}.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}@media only screen and (orientation:landscape){.tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:200px}}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-dropup:not(.tinymce-mobile-dropup-closed){min-height:150px}}.tinymce-mobile-styles-menu{font-family:sans-serif;outline:4px solid #000;overflow:hidden;position:relative;width:100%}.tinymce-mobile-styles-menu [role=menu]{display:flex;flex-direction:column;height:100%;position:absolute;width:100%}.tinymce-mobile-styles-menu [role=menu].transitioning{transition:transform .5s ease-in-out}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item{border-bottom:1px solid #ddd;color:#455a64;cursor:pointer;display:flex;padding:1em 1em;position:relative}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser .tinymce-mobile-styles-collapse-icon::before{color:#455a64;content:"\e314";font-family:tinymce-mobile,sans-serif}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-styles-item-is-menu::after{color:#455a64;content:"\e315";font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-item.tinymce-mobile-format-matches::after{font-family:tinymce-mobile,sans-serif;padding-left:1em;padding-right:1em;position:absolute;right:0}.tinymce-mobile-styles-menu .tinymce-mobile-styles-collapser,.tinymce-mobile-styles-menu .tinymce-mobile-styles-separator{align-items:center;background:#fff;border-top:#455a64;color:#455a64;display:flex;min-height:2.5em;padding-left:1em;padding-right:1em}.tinymce-mobile-styles-menu [data-transitioning-destination=before][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=before]{transform:translate(-100%)}.tinymce-mobile-styles-menu [data-transitioning-destination=current][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=current]{transform:translate(0)}.tinymce-mobile-styles-menu [data-transitioning-destination=after][data-transitioning-state],.tinymce-mobile-styles-menu [data-transitioning-state=after]{transform:translate(100%)}@font-face{font-family:tinymce-mobile;font-style:normal;font-weight:400;src:url(fonts/tinymce-mobile.woff?8x92w3) format('woff')}@media (min-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:25px}}@media (max-device-width:700px){.tinymce-mobile-outer-container,.tinymce-mobile-outer-container input{font-size:18px}}.tinymce-mobile-icon{font-family:tinymce-mobile,sans-serif}.mixin-flex-and-centre{align-items:center;display:flex;justify-content:center}.mixin-flex-bar{align-items:center;display:flex;height:100%}.tinymce-mobile-outer-container .tinymce-mobile-editor-socket iframe{background-color:#fff;width:100%}.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{background-color:#207ab7;border-radius:50%;bottom:1em;color:#fff;font-size:1em;height:2.1em;position:fixed;right:2em;width:2.1em;align-items:center;display:flex;justify-content:center}@media only screen and (min-device-width:700px){.tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{font-size:1.2em}}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket{height:300px;overflow:hidden}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-editor-socket iframe{height:100%}.tinymce-mobile-outer-container:not(.tinymce-mobile-fullscreen-maximized) .tinymce-mobile-toolstrip{display:none}input[type=file]::-webkit-file-upload-button{display:none}@media only screen and (min-device-width :320px) and (max-device-width :568px) and (orientation :landscape){.tinymce-mobile-ios-container .tinymce-mobile-editor-socket .tinymce-mobile-mask-edit-icon{bottom:50%}}
diff --git a/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css b/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css
new file mode 100644
index 0000000..a0893b9
--- /dev/null
+++ b/public/resource/tinymce/skins/ui/oxide/skin.shadowdom.min.css
@@ -0,0 +1,7 @@
+/**
+ * Copyright (c) Tiny Technologies, Inc. All rights reserved.
+ * Licensed under the LGPL or a commercial license.
+ * For LGPL see License.txt in the project root for license information.
+ * For commercial licenses see https://www.tiny.cloud/
+ */
+body.tox-dialog__disable-scroll{overflow:hidden}.tox-fullscreen{border:0;height:100%;margin:0;overflow:hidden;-ms-scroll-chaining:none;overscroll-behavior:none;padding:0;touch-action:pinch-zoom;width:100%}.tox.tox-tinymce.tox-fullscreen .tox-statusbar__resize-handle{display:none}.tox-shadowhost.tox-fullscreen,.tox.tox-tinymce.tox-fullscreen{left:0;position:fixed;top:0;z-index:1200}.tox.tox-tinymce.tox-fullscreen{background-color:transparent}.tox-fullscreen .tox.tox-tinymce-aux,.tox-fullscreen~.tox.tox-tinymce-aux{z-index:1201}
diff --git a/src/App.vue b/src/App.vue
new file mode 100644
index 0000000..18bcdbd
--- /dev/null
+++ b/src/App.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
diff --git a/src/api/demo/account.ts b/src/api/demo/account.ts
new file mode 100644
index 0000000..d4d9c27
--- /dev/null
+++ b/src/api/demo/account.ts
@@ -0,0 +1,16 @@
+import { defHttp } from '@/utils/http/axios';
+import { GetAccountInfoModel } from './model/accountModel';
+
+enum Api {
+ ACCOUNT_INFO = '/account/getAccountInfo',
+ SESSION_TIMEOUT = '/user/sessionTimeout',
+ TOKEN_EXPIRED = '/user/tokenExpired',
+}
+
+// Get personal center-basic settings
+
+export const accountInfoApi = () => defHttp.get({ url: Api.ACCOUNT_INFO });
+
+export const sessionTimeoutApi = () => defHttp.post({ url: Api.SESSION_TIMEOUT });
+
+export const tokenExpiredApi = () => defHttp.post({ url: Api.TOKEN_EXPIRED });
diff --git a/src/api/demo/cascader.ts b/src/api/demo/cascader.ts
new file mode 100644
index 0000000..198853d
--- /dev/null
+++ b/src/api/demo/cascader.ts
@@ -0,0 +1,9 @@
+import { defHttp } from '@/utils/http/axios';
+import { AreaModel, AreaParams } from '@/api/demo/model/areaModel';
+
+enum Api {
+ AREA_RECORD = '/cascader/getAreaRecord',
+}
+
+export const areaRecord = (data: AreaParams) =>
+ defHttp.post({ url: Api.AREA_RECORD, data });
diff --git a/src/api/demo/error.ts b/src/api/demo/error.ts
new file mode 100644
index 0000000..0a7f662
--- /dev/null
+++ b/src/api/demo/error.ts
@@ -0,0 +1,12 @@
+import { defHttp } from '@/utils/http/axios';
+
+enum Api {
+ // The address does not exist
+ Error = '/error',
+}
+
+/**
+ * @description: Trigger ajax error
+ */
+
+export const fireErrorApi = () => defHttp.get({ url: Api.Error });
diff --git a/src/api/demo/model/accountModel.ts b/src/api/demo/model/accountModel.ts
new file mode 100644
index 0000000..4594393
--- /dev/null
+++ b/src/api/demo/model/accountModel.ts
@@ -0,0 +1,7 @@
+export interface GetAccountInfoModel {
+ email: string;
+ name: string;
+ introduction: string;
+ phone: string;
+ address: string;
+}
diff --git a/src/api/demo/model/areaModel.ts b/src/api/demo/model/areaModel.ts
new file mode 100644
index 0000000..dfaa481
--- /dev/null
+++ b/src/api/demo/model/areaModel.ts
@@ -0,0 +1,12 @@
+export interface AreaModel {
+ id: string;
+ code: string;
+ parentCode: string;
+ name: string;
+ levelType: number;
+ [key: string]: string | number;
+}
+
+export interface AreaParams {
+ parentCode: string;
+}
diff --git a/src/api/demo/model/optionsModel.ts b/src/api/demo/model/optionsModel.ts
new file mode 100644
index 0000000..871ae9f
--- /dev/null
+++ b/src/api/demo/model/optionsModel.ts
@@ -0,0 +1,15 @@
+import { BasicFetchResult } from '@/api/model/baseModel';
+
+export interface DemoOptionsItem {
+ name: string;
+ id: string;
+}
+
+export interface selectParams {
+ id: number | string;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type DemoOptionsGetResultModel = BasicFetchResult;
diff --git a/src/api/demo/model/systemModel.ts b/src/api/demo/model/systemModel.ts
new file mode 100644
index 0000000..0e5f8fa
--- /dev/null
+++ b/src/api/demo/model/systemModel.ts
@@ -0,0 +1,75 @@
+import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel';
+
+export type AccountParams = BasicPageParams & {
+ account?: string;
+ nickname?: string;
+ [key: string]: any;
+};
+
+export type RoleParams = {
+ roleName?: string;
+ status?: string;
+};
+
+export type RolePageParams = BasicPageParams & RoleParams;
+
+export type DeptParams = {
+ deptName?: string;
+ status?: string;
+};
+
+export type MenuParams = {
+ menuName?: string;
+ status?: string;
+};
+
+export interface AccountListItem {
+ id: string;
+ account: string;
+ email: string;
+ nickname: string;
+ role: number;
+ createTime: string;
+ remark: string;
+ status: number;
+}
+
+export interface DeptListItem {
+ id: string;
+ orderNo: string;
+ createTime: string;
+ remark: string;
+ status: number;
+}
+
+export interface MenuListItem {
+ id: string;
+ orderNo: string;
+ createTime: string;
+ status: number;
+ icon: string;
+ component: string;
+ permission: string;
+}
+
+export interface RoleListItem {
+ id: string;
+ roleName: string;
+ roleValue: string;
+ status: number;
+ orderNo: string;
+ createTime: string;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type AccountListGetResultModel = BasicFetchResult;
+
+export type DeptListGetResultModel = BasicFetchResult;
+
+export type MenuListGetResultModel = BasicFetchResult;
+
+export type RolePageListGetResultModel = BasicFetchResult;
+
+export type RoleListGetResultModel = RoleListItem[];
diff --git a/src/api/demo/model/tableModel.ts b/src/api/demo/model/tableModel.ts
new file mode 100644
index 0000000..8f1eea5
--- /dev/null
+++ b/src/api/demo/model/tableModel.ts
@@ -0,0 +1,20 @@
+import { BasicPageParams, BasicFetchResult } from '@/api/model/baseModel';
+/**
+ * @description: Request list interface parameters
+ */
+export type DemoParams = Partial;
+
+export interface DemoListItem {
+ id: string;
+ beginTime: string;
+ endTime: string;
+ address: string;
+ name: string;
+ no: number;
+ status: number;
+}
+
+/**
+ * @description: Request list return value
+ */
+export type DemoListGetResultModel = BasicFetchResult;
diff --git a/src/api/demo/select.ts b/src/api/demo/select.ts
new file mode 100644
index 0000000..f5f5cf9
--- /dev/null
+++ b/src/api/demo/select.ts
@@ -0,0 +1,12 @@
+import { defHttp } from '@/utils/http/axios';
+import { DemoOptionsItem, selectParams } from './model/optionsModel';
+
+enum Api {
+ OPTIONS_LIST = '/select/getDemoOptions',
+}
+
+/**
+ * @description: Get sample options value
+ */
+export const optionsListApi = (params?: selectParams) =>
+ defHttp.get({ url: Api.OPTIONS_LIST, params });
diff --git a/src/api/demo/system.ts b/src/api/demo/system.ts
new file mode 100644
index 0000000..980e58c
--- /dev/null
+++ b/src/api/demo/system.ts
@@ -0,0 +1,44 @@
+import {
+ AccountParams,
+ DeptListItem,
+ MenuParams,
+ RoleParams,
+ RolePageParams,
+ MenuListGetResultModel,
+ DeptListGetResultModel,
+ AccountListGetResultModel,
+ RolePageListGetResultModel,
+ RoleListGetResultModel,
+} from './model/systemModel';
+import { defHttp } from '@/utils/http/axios';
+
+enum Api {
+ AccountList = '/system/getAccountList',
+ IsAccountExist = '/system/accountExist',
+ DeptList = '/system/getDeptList',
+ setRoleStatus = '/system/setRoleStatus',
+ MenuList = '/system/getMenuList',
+ RolePageList = '/system/getRoleListByPage',
+ GetAllRoleList = '/system/getAllRoleList',
+}
+
+export const getAccountList = (params: AccountParams) =>
+ defHttp.get({ url: Api.AccountList, params });
+
+export const getDeptList = (params?: DeptListItem) =>
+ defHttp.get({ url: Api.DeptList, params });
+
+export const getMenuList = (params?: MenuParams) =>
+ defHttp.get({ url: Api.MenuList, params });
+
+export const getRoleListByPage = (params?: RolePageParams) =>
+ defHttp.get({ url: Api.RolePageList, params });
+
+export const getAllRoleList = (params?: RoleParams) =>
+ defHttp.get({ url: Api.GetAllRoleList, params });
+
+export const setRoleStatus = (id: number, status: string) =>
+ defHttp.post({ url: Api.setRoleStatus, params: { id, status } });
+
+export const isAccountExist = (account: string) =>
+ defHttp.post({ url: Api.IsAccountExist, params: { account } }, { errorMessageMode: 'none' });
diff --git a/src/api/model/baseModel.ts b/src/api/model/baseModel.ts
new file mode 100644
index 0000000..1a36511
--- /dev/null
+++ b/src/api/model/baseModel.ts
@@ -0,0 +1,9 @@
+export interface BasicPageParams {
+ page: number;
+ pageSize: number;
+}
+
+export interface BasicFetchResult {
+ items: T[];
+ total: number;
+}
diff --git a/src/api/sys/menu.ts b/src/api/sys/menu.ts
new file mode 100644
index 0000000..27fb826
--- /dev/null
+++ b/src/api/sys/menu.ts
@@ -0,0 +1,14 @@
+import { defHttp } from '@/utils/http/axios';
+import { getMenuListResultModel } from './model/menuModel';
+
+enum Api {
+ GetMenuList = '/getMenuList',
+}
+
+/**
+ * @description: Get user menu based on id
+ */
+
+export const getMenuList = () => {
+ return defHttp.get({ url: Api.GetMenuList });
+};
diff --git a/src/api/sys/model/menuModel.ts b/src/api/sys/model/menuModel.ts
new file mode 100644
index 0000000..c2064a1
--- /dev/null
+++ b/src/api/sys/model/menuModel.ts
@@ -0,0 +1,17 @@
+import type { RouteMeta } from 'vue-router';
+
+export interface RouteItem {
+ path: string;
+ component: any;
+ meta: RouteMeta;
+ name?: string;
+ alias?: string | string[];
+ redirect?: string;
+ caseSensitive?: boolean;
+ children?: RouteItem[];
+}
+
+/**
+ * @description: Get menu return value
+ */
+export type getMenuListResultModel = RouteItem[];
diff --git a/src/api/sys/model/uploadModel.ts b/src/api/sys/model/uploadModel.ts
new file mode 100644
index 0000000..d770c64
--- /dev/null
+++ b/src/api/sys/model/uploadModel.ts
@@ -0,0 +1,5 @@
+export interface UploadApiResult {
+ message: string;
+ code: number;
+ url: string;
+}
diff --git a/src/api/sys/model/userModel.ts b/src/api/sys/model/userModel.ts
new file mode 100644
index 0000000..3869219
--- /dev/null
+++ b/src/api/sys/model/userModel.ts
@@ -0,0 +1,38 @@
+/**
+ * @description: Login interface parameters
+ */
+export interface LoginParams {
+ username: string;
+ password: string;
+}
+
+export interface RoleInfo {
+ roleName: string;
+ value: string;
+}
+
+/**
+ * @description: Login interface return value
+ */
+export interface LoginResultModel {
+ userId: string | number;
+ token: string;
+ roles: RoleInfo[];
+}
+
+/**
+ * @description: Get user information return value
+ */
+export interface GetUserInfoModel {
+ roles: RoleInfo[];
+ // 用户id
+ userId: string | number;
+ // 用户名
+ username: string;
+ // 真实名字
+ realName: string;
+ // 头像
+ avatar: string;
+ // 介绍
+ desc?: string;
+}
diff --git a/src/api/sys/upload.ts b/src/api/sys/upload.ts
new file mode 100644
index 0000000..2c4e191
--- /dev/null
+++ b/src/api/sys/upload.ts
@@ -0,0 +1,23 @@
+import { UploadApiResult } from './model/uploadModel';
+import { defHttp } from '@/utils/http/axios';
+import { UploadFileParams } from '#/axios';
+import { useGlobSetting } from '@/hooks/setting';
+import { AxiosProgressEvent } from 'axios';
+
+const { uploadUrl = '' } = useGlobSetting();
+
+/**
+ * @description: Upload interface
+ */
+export function uploadApi(
+ params: UploadFileParams,
+ onUploadProgress: (progressEvent: AxiosProgressEvent) => void,
+) {
+ return defHttp.uploadFile(
+ {
+ url: uploadUrl,
+ onUploadProgress,
+ },
+ params,
+ );
+}
diff --git a/src/api/sys/user.ts b/src/api/sys/user.ts
new file mode 100644
index 0000000..511fca2
--- /dev/null
+++ b/src/api/sys/user.ts
@@ -0,0 +1,55 @@
+import { defHttp } from '@/utils/http/axios';
+import { LoginParams, LoginResultModel, GetUserInfoModel } from './model/userModel';
+
+import { ErrorMessageMode } from '#/axios';
+
+enum Api {
+ Login = '/login',
+ Logout = '/logout',
+ GetUserInfo = '/getUserInfo',
+ GetPermCode = '/getPermCode',
+ TestRetry = '/testRetry',
+}
+
+/**
+ * @description: user login api
+ */
+export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal') {
+ return defHttp.post(
+ {
+ url: Api.Login,
+ params,
+ },
+ {
+ errorMessageMode: mode,
+ },
+ );
+}
+
+/**
+ * @description: getUserInfo
+ */
+export function getUserInfo() {
+ return defHttp.get({ url: Api.GetUserInfo }, { errorMessageMode: 'none' });
+}
+
+export function getPermCode() {
+ return defHttp.get({ url: Api.GetPermCode });
+}
+
+export function doLogout() {
+ return defHttp.get({ url: Api.Logout });
+}
+
+export function testRetry() {
+ return defHttp.get(
+ { url: Api.TestRetry },
+ {
+ retryRequest: {
+ isOpenRetry: true,
+ count: 5,
+ waitTime: 1000,
+ },
+ },
+ );
+}
diff --git a/src/assets/icons/download-count.svg b/src/assets/icons/download-count.svg
new file mode 100644
index 0000000..1c95195
--- /dev/null
+++ b/src/assets/icons/download-count.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-1.svg b/src/assets/icons/dynamic-avatar-1.svg
new file mode 100644
index 0000000..e1553e5
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-1.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-2.svg b/src/assets/icons/dynamic-avatar-2.svg
new file mode 100644
index 0000000..c4c1722
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-3.svg b/src/assets/icons/dynamic-avatar-3.svg
new file mode 100644
index 0000000..81145f9
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-4.svg b/src/assets/icons/dynamic-avatar-4.svg
new file mode 100644
index 0000000..e586ed4
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-4.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-5.svg b/src/assets/icons/dynamic-avatar-5.svg
new file mode 100644
index 0000000..746e4b8
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-5.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/dynamic-avatar-6.svg b/src/assets/icons/dynamic-avatar-6.svg
new file mode 100644
index 0000000..b2432f2
--- /dev/null
+++ b/src/assets/icons/dynamic-avatar-6.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/moon.svg b/src/assets/icons/moon.svg
new file mode 100644
index 0000000..e6667f0
--- /dev/null
+++ b/src/assets/icons/moon.svg
@@ -0,0 +1,16 @@
+
+
diff --git a/src/assets/icons/sun.svg b/src/assets/icons/sun.svg
new file mode 100644
index 0000000..a3997cb
--- /dev/null
+++ b/src/assets/icons/sun.svg
@@ -0,0 +1,42 @@
+
+
diff --git a/src/assets/icons/test.svg b/src/assets/icons/test.svg
new file mode 100644
index 0000000..244252d
--- /dev/null
+++ b/src/assets/icons/test.svg
@@ -0,0 +1,21 @@
+
+
\ No newline at end of file
diff --git a/src/assets/icons/total-sales.svg b/src/assets/icons/total-sales.svg
new file mode 100644
index 0000000..eff7964
--- /dev/null
+++ b/src/assets/icons/total-sales.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/transaction.svg b/src/assets/icons/transaction.svg
new file mode 100644
index 0000000..7ba9e2f
--- /dev/null
+++ b/src/assets/icons/transaction.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/icons/visit-count.svg b/src/assets/icons/visit-count.svg
new file mode 100644
index 0000000..ba2a306
--- /dev/null
+++ b/src/assets/icons/visit-count.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/src/assets/images/demo.png b/src/assets/images/demo.png
new file mode 100644
index 0000000000000000000000000000000000000000..1a45c9835b7b2c708c114b04fb445e6ef00d8827
GIT binary patch
literal 33342
zcmV)0K+eC3P)E5{{R30UbFgLy!>9I_gk&^U$6RJtodK5_*RqthH1UyJNogyiSz@ME_7W4ruX
zlJQ}e@no&|S%T|Tc;m6b?Pk0CS)KN1viVo0_hrrfSG4+s!1-CW{O@p-zTf?0mhrCC
z{DYkA=`{EnUSXQS+yuIHGw_ocPw`@Ya_f5h6`vg+D!+1bO_+23xA
z?_!+kVuRY-+@OG~^4i&u;m>GkmE+6M;%AKAgNVBH=Y76bmdNh^+S}5vyX2FRtzVYr
zxx(aHkmA9}cPie~3(3_ss{dAw;(ax#Ec&5+EwXtEL*w=!Dj`f+K+^M6|pPr|G
zt>2Wi%FW2XwXxBpy|BE*ow&rk$j6Ar&eEoRx3;X2xvaPEywdph!|kWR>xrrR@SE1X
z+QiQY=>Px#1awkPQ~jnK0Q?=)ZvX&)+(|@1RCwCFUAeC8L=?P$Eo-=LIiv|30@kEV
zhCnPoz$cIq5V8CO5`qAc6Cn_YZ{+ONi|v`qR^9Gy4~sm0HFfHow&CfuS6_LWUXEo4
z!yTminA6;1d@;6dyE4`{?qj_gHpckxV(vlT@IHn+SavY|OL%(a)z_Y0eHkSdqJtjV
zhG|%KT7lGaVwWCc8u^}e7^&Nk#_?J-!rC?Zd>d)JUdJ~c!AN{?PHf});>MT{8e{g(
zUyTm_p7%MCq}5pV+7ylYze5e8#4FR|Sh&eNL}S|8im@vfNrrQbI>?P1lQ+4Yw=~}9
zrqaPo;16wFM;mZlX@pQbfR#zH8Sw9W_*jX9)B(F{;RF~>JGo+xGNNk9mBNO
z7HZu8IbL~sTqqVD20elkHZI2HPAG!%`1V6f2<_BNVjW3-V%*b>hE0~+m}o)Ms!CDJ
zgv7kKLMZNHl;GjOzw6PN;MXXNz^p?OLa1`2s|ezKt&rA;#iG4xH7kS|{~S**4j#q}
z#E!|St2EAyNG#L_YKPHd^l4P3aP7sfO_87xjy0%J{%YxfP~68_kwX7H>slzmo!Oco
z1wTNFS-;Fx$uOrYeg-v$&Q+ujhYC5Ag4G#Kf21Mf{}?YLgsCPnaV^M5xT2j_?yf;BZ#A<9*EVzIt3C$gj-%1u)U9%!-wAV%y71;$DxOkJr9
zFJRSdN^30@Yp6w|6dQ!%rBWdTGe82?P^JD{hApi|40N}dGT4_Q#zQ5Jr2@aBCFPP5
zU_@mmln$9s{#T_iY0B<5jh`CVf*hIk!Bv7NF)=4TJWV}DOd~a8g-j(ObP>eBp9chd
zQ^%nu;UPXiDr&D8pmpUT`g71EjlkSq{mf0Z(BkA_=z#HBv<-oYkDvv0r$rLwp3b7j
zzfTIdOw+5R$tirr64y=8Z|C6TxCE`r4B8gHA
z;Wo3)gMW=Bxy{7a#R@Me)ud=?(I^8m5#6Yn)Xv~4
zmDV7%PvArkaKhS@(h|)?IJ-LQ?NFpE5+0)h0efO
zGvYd;NMSMo$%xNPCR$&yrfpSHKqy`=70wA@bVY(v1Ti+muO>w@D`6+Wc2z`Y#du5$
zQ3}3;Sw)uzIC&Qe?kFVwk4mvjCHpcmQ@0ezl!k0N0ZOEyG!;A_)+uIcej`DKE*(YY
zfy84qmnlH%@gO=<)U+rJrE5kNLky{1B+r|~HJgK$FDb3Z0HI(jrds=9_Fx5kZ`x
zSTIsdsp56AW%3wu<}C(l{=3R%H^sQ=fV?kJtK-LVz>9x9e3(vHPAom>YT2kFEx~Q;
zgLfMai`>ou;zw1sQWxi1L8wh$aaKBt^(}61&_UdIgT}YtSRb>R5DEYZX)G{e>WZO{S^SfOW24pt!j
zFis4|;X@sT=Wg6*?+Dc^_s#wo@MDGZtAv91z)BG#VS7j_fL&hHrFAd@SQB>w>Mcrk
z9lTgUM-Ol!N)-}8zzft0WoAAY8x|n`fD2xj97BULT}kpyiHy^!AV!6!2nA{9Z4
z9L8dz)~16fY3|9zzaBn{eXzv?7~-0NO@&=PD8bP0Y9jzj%<|iT#5Oi_jr+WgRmW0*
z=)u|`Q=McyE*0at1E@rO5zr9qeH8geS+U?V!bt*xo5lGDyrwG!n+g+VEgKS|aOB%#
zCH`enfO9-r#1d8t8cVT2D)bvz^f)bAY1bASi`$uyci6C^H(BBap>R*ZW`YjxMncDT&=$3B?4OTa<^iQpRzNA_kf31=O5tVAj9Melz<&@vyojoN%%T-2
zQ^ay9ltPEW8dB4tqy)V9=%rv!>fzaZg725!ct4Or0>@iyGCMbU_OMN0J>f+kx{442
zK<%X0oxDk}2t_eqH
z{vS&5pYMI3s;x>9o+8DDxmzD71$IcGgR(eEJwzfr1Y1cs@=)w|Fc8GCEQG>SkGFPe
z*{lW+k>XS>f`rg0IL@m0iaZ3hZW`}}58SSSdrs~$-BM)FKWvnrs^%XVsRxMhUwH~E
zqAmm_hB_Pu@({b~W{0#AieX?ofG(JR2qT1Mu1Y-1_+A$N`Rm@%}B*8efn&)+jar6-m6V$+^
zh^s)sUgC$*f)2M8uvChnl8Pn)fz?D!b6BKL8a)WvDizYufqyq8)kKHKqHe_(Br1V@
z8Yc2&!4JQDe0y`LFS}+{;S0o)mhN
zG-%R}P1qY}s&)EpG^-M;IlHT9;s>~Z*FPj8gA&<)fhHBxA{FmsL^!Qk{o7^k*djHT
zIb;i@Z2c#{sPEuYoh5)HWZwb#_noPbbJ(*+;?C(hU&i65(C
zJJ1R_z=m#9M39URH(3fLypPO&iyVhJsUCwO<&cU99~L|YIj|`uN#cKByaEunxD6A6
zn(;z`t7kax4{F^rH9UYKWROD;5*$ubs8N;=8<)8%7kscYyz?W(Smo2RtaRlD>}iEP
z){QI(SmT2ItAzrFZ?gD-yc8;mtq%h%X3`t2v{0q68lq){mL~4wxm{!Ra#CFUkzL^<
zp6=%}4QdLfMK)Rt6-vEO_%@hSDRklwoEREE>`fpA#JPDI&bPwe1`%w4P(&9mdTKSt
zeW^gs+4>rvQ8h_+-8WYXLy`lz=nPJvH7{JU@M?jA2IhicFuCUAAw27Vd7l#GW
z4U{;Nies-yeBllfv?c=E=QrOJ^Nl>lWQx#XuPjb0Is5h$5(s#R#gH@fi$0h)eTOwuoTxidaN{XqrO@6
zKq352R(evQP#{0H!;uP8ag**`u+b`4{+$`Bs|jk_BC+}|UfQp35t(XZ;2`Gh#@Px2l)&_?5rd#7)Oh)9BUGeTiW5#;GAk1wdC_8bc4kaotbn?yQVb37oe@6CV1
z4w8L%_CN6?sVJV1JR!sf_>7wq1f;OVfv0$W#8SxHX~-gS3@avY+EDn{id
zW><+FQdrdrrh*tjgX%<22k=3e5cuF3m@rN8nG)oQK55BwYA-HyOc%K*Is|mWM_e$_
zX+``N-i?%$5T;z%fI0@D;{#R>cy@(69
zi%}xdQj2gwW-Lk+b_!4%4snGobIDN6&|1)HYK{LGL6SeAa1;ToVJPm$4<`j!(Q~4H
z$F#g8j~70J2{z99U12fj)=un_Vo9EN$fzz~=fn6@By{|A}$j0q5p
z9XKoOqGShz2U-<$=GGz=#7MKU0tE`tqNW5v1~qI~?44p$jAaiP
zBPJig7n(aVAcQaf&Y;Mz&r6XD-xJ~#8J-C;-Jl?m%kI!6Zy%Dl;7RZi5QqbT6PGB=%
zC>6%L8WKB@3KzswM&N3l8M@;E*tbRmNppmc11#KehZWO0L(CLEcu25YvrUawKrP~RD;5rOce48sLh-h#1M7ss__1B#9v^WP
z`=kK+!uEIzHvvsklj+78h%zp`1>X)h%p+Q$x~)ILs2k-5)0>&wl;4zIVagY3JklRy
zCR_+KRP=bl8Mt%N1{UZ5Fe|v#O0hI<^E5Aw`{HU*lVToCfM>N6CKR6Y(K@iD`ah6p
zjG&Ix0{|(G(2(LV0omGIRHaM?JX;FMb0mh1vn`1fe4HtT4k#e|vo!oi`!08aPVeUm
zZ&vLH6KEjW2g9cmrMNUok*WKZ7@wN`xuX+l@Sg?EDXLe!Qg}+5YD3Qll8}`sp-Qou
zHuzNSnQx_eeAHEpkc#tn2oX8xz@;*c@p2fD6d=Xj@Ucj@yNkjVHG)uq+WS1d&nJ3b
zVM2fNpt;4ENGZyg6O+mX*no+ZM_92;%nW3uAOz
zx>AfT5k5mG2yHw7DNeFdnA#RKBW|TwG`R*I~-Ly|A56aH?LQAU;Tcu3UstLdn5rSuL<2%Qf<43oq_E3O?q9v@DnraNAx`
zp{#Y!{%4^=akV{j4I2&!MV(@9#tPdbA?Vaw&0|um^&?<6K9Y1d6h>puO7~fTy>z`P
z_oO0rAZw*Cd0?#|_y9~yg*R;mN2*?tBJxw{#Kn5;EwMJCusyoG2wd0jp8Q$(n1cez
z(8|P&*NY((NyBweVuet=)xjJBp-VA0W(9UiFgH@M*=A@@imrCmD~07BH{aabVMUZS
zACU@V9|J6fDur^uVh9b8ivp8742>do(5cem`GXHCqluDMsT5VYiZNwwwv$f5BU5*^
z$djj|)1yj}(jhBo=OBK15DN2}a|retNe>PuT*U)Yp|E@d_Q%o>
zyYA9ev`WF0(5QN@6wHfk+hM3tAaF3057Ge@3Tcy^8YQw*Vxk68Kq)RN5;C+xL44qr
z^^6i2i5)B$c&F4t2Z|JTlwvU9JW>iHLgDIz84B!GrcMNWng~yNigcV^av|5P$NtFr}3SO94^iV=D8<4v4C%sY#1rR`#9x}jU
z(k#pnp-`Wh!%qwvK_<5p_p=W=kg4yILOcUY!i-eZA6c;o4#nO^2t|onC{_URLpTx5
zI;2ARa6pRP=yAhC6qbHKFwRJEKY+kfNQB0A7?M%Ue7Io1RLCHwdS;*Nls_O8O5rjw
z*&0f5F+KmKH-jqim)<{1JU*SWc6r(sKAwBhu6*AqaUWKQK$92O~ghF5j
zBfte%OC2+8CiPY+Bn_p}r;D|ETNGN6>BO-BrW7Uj_R_-#Hr68OaxfwKq6PlFy
z>Bm=Ez5ChcAAR}tx8MEz>kqcAL@jw&tf3U+TL-|Nc}od*7WJD$D1{bPO+)`m5FXz3
zOM)R*HzeIs>rub3d5gL-<4SP=ygf?j!^gXI*{V-IKnG=k6GP-8_)>-sSP2A>S}HDX
zBxIpJ(}g0770#+^)U4Khf_SIeekWe*b$rfb`18*{e45aZ&g&W|_nH&%5(Va{aKlL8|4~7}LuU16PfCxFB9eY9Qz(=LNIS%6z9~~*
z>GsG0J26r3!$;sE2jCm89xj*=SgNm(rfQM>wRkrdhlr=xai2mq-eop?qaaRv^2JY2
zq6ZKPmJJOk8x2<$ihZ96p+NNLTm?c#q^6OTUX>M{RKR@}d@!GBbl{ctYU88GHwp~Y
z&od7p1@;{?FC6d_dmCa19oED~pvDQ);iN!HIw5LYyqV3oC>NynSRy%{^kPFSmP1C!
zQNJ+dg7NWJ-voaDgz&`|zhvWpbl(qAT^G*)p=dBdD8}`G6d(c)fjz*t$B6(QEWZ^s
zdF{cBR6dFaD^YIs=v_HN9XT?BN2?U?Q@0cFp^*ys3ilS~K&KDh0V!Z3dQ40gj~TU2
zSaX%&0aR8D=a^e0#`jK%-$*8e6kq(n?j0qI+YF%?MJNvNhH&Cq7r5^fu#6wDn){vt
zz|*4YCeX1-Iy>6>1UNy*3GkW+Fgc6@ou7C=Xn~R@_emxjuv>NkC(6AKSPC@bhM|`V
zd*2w9O)t@(lEQ<>P`1QE@p&0Kj1@oq0Hvslnp-*(iuEc$fxAUtzyeUf0%}emHI`{+
zH&GR<++g(eZ3GT!j5Ddg&&(9L^%TfHbmIe30XGS)5L!f$+hWM;<}+b5@qmSUi*5(~
z(!$aYEmwH#;B^rC%u@&@dY$uoSGs
zM#VxzP;5W2@i#i>mM4?R#VEd$%(#l8@yUD6oJ?k4RZO^5NU}*zF|ctbTj6TmC@4bB
zQ$cy6(n?lw0U_}*u=RA)SEIL1;r9V(9J)0oc-H8XJh|ebC~J>nhs+aW)+nQ{1Ca3ZSrwhc>F3kw9ujH1W=rxalP&G`>Os8
zqhRha4+Z%w9STH<%C=bFP${@NO
zO%Ca6kopg
z3Pb0HVM&EDapB}toEoEmiN)|C+W2Z1sNmZv5nu!fOg&;I>KiYQ3s(dV7WU_b#~lL|
zX+EdWXkkZGD$>Ix-PSG+enw-*?b~%uVcq;J1iNSyWH9&e11J`k`s>>tU2PQ~2pts^
zU%tiS3X+OD21UW)G;b8^DFu(k)}R0h?4rhj5(NoG&=Jch>gpxM+<>B6UW>L5R?Ar(`1Hh^(cYdUa5K
z{u3sELaF#+7GaUvpDzv3<0NW~!h}=IY8DDo%_$ZOHW8%yVX>t1Rd@(2kbGJR8`h%~
z15n%p2A_poifAB;@pjcTeuy_%D?ic!D4YvaKP|;38WBUPzyaAnftZyG6BwzbnWPg;
zKZ?%Zcx97aDJ^=46pJOJaI09zDWGC0euyang`bGHCRp%(N_HOb3hk(CZzL2%4%EwO
zDI9MA+5D4SQ{+rYlHfFm7X%pgBk5ECq38q@DAin8M}eXtB7Ttg>$W;9sy2#yzep!)
zr|P`;TXk-nt#0gYSgt
zIwPVJqd=8W0L2OwyezhE6?b7GJu6CwPW^(psK+%eY7ehE^7w1IH2FYV*423>UG-Ku
z1yc_h%_$s-+r~vGf(2w$V)l{b;GA)aq6I8)ir&9ya&Rz{I>$?1aLW#nw=;g2MX9Dfo{!6aohj
zn6zW%6u1Rkgi#bNLUFZ9DtcXCv5SgvRqp}mL<_}tWkNznYZNUMCkJAM3fotA%PN}q
z@o~`m1M!13{bj@{mg^H3mAV{F?ODdAM@ukV^gM}0!q6Z?o
zJO&})QMzCXA9ecf_1C7@vQLR
zdd)75_q)*+-?LoYMH^X^<5EI0L9q_HZc~pM$weHU2LAXTWsk)R4QH@529o~5wE%uMP#s)g8j}G
zbBdgIK;f@y_L9H>gv^##NA6HJn>pxXav8jrP
zT~6WJ%qdE+({X2+g3_K{p||hAVg$u!OY^1K-ik5xK(nj4`SE_8Y%nOi+MR=mmoHyt
zgQ9W@W+9y<6%jvhihr(C^o|8kRF6GeK(SZbiDp>EW?GRb%0o2A;ql?no=OE$FwRO^
z5nKSpdF+y>hOEYXH=k-XNW=jY|85n%G4ZnKj3;5dB*;+BKj!Nc`wJCG4Y{8Brm=S7a}&9H>K62
z!pZpbwMfVSi!W!s#~ek9JVXyPlT+*sA28C+N+p2Y6ex^|9wcyzYVDQ3-2@f{%c5|}
zjqMRpvgqM=AW$&z2>maoAjfnj)`}I3)gD;v$s7Mt?g^l#Pz$T$}or1T`afP)NEM
zNMIEI3yL`K|4W&Oo8tnSAtgo@h^-0CP`!!4n=)G;1dqTAuiXa^S5sc4dvSR5mdrFfaMecLnIY2ob?RYt7jh(JpXXoanVEZ5Uw2wfuh;^jEd$G2uE#l
z?1W-}vKc>0`U%Jf0mDQYrzq+LhrUB`mD!GUmI*m@WRpDUTahu4U{1t)Ag7_$Em(B2
z*~8iOvmPWOZbWq=6yM<<`6bd>$GtgO%}V;5*5;|x&&0f140hr2_+Oj?MXITcAlR_mL5`_#
zicuz(a|?y){%Mg4X)+C24I$Syg_D#&cZeU-D%n!2NGe!Hk%vWB9Y%p5qS@i4DNR_4
zrq{FQ<})ZnJz9a{wly1$@q=JPyZuf973)dGFeVbsdJ>ZL@GF6E3I;T(3$8N3tW?gw
zM&cGwm{s5v$Vp<}a`O=``r%W7aBAuYFE>=FwVy4<7Cla&UghvlMO2sQ4-P6gat<|zoK!q`3Tb9=FJ30_+HNlc3~|1^sxB{KBJ}+}QGh}K!E#aO@d+M-Xf#Zp
zQP57^GnU!}LJ0OQ83jfljDr>~PNCwa4q1!|+LWEDwNqh0w4QR};o+??;e(FA1ID4L
zPn2b=Dlk!bUsW9)H%Td|#Z=%{K12>U1w)}+Y)?c(q1Y%6m=_IW;sjYGJ)DA>020th
z1nCuvxzdrJq=jKf)n-_vZi-_#9zE7)
zfMO!4xI)tm6k-XvO_Vi_n~~NBMS=ndo*piR34cnW|3H&WniU98ZehcxQkC1O9V$6`
z?6?TdF%+uN@FBO0BAYlxn<%H)I0^uyP*s}86cezydf`f}KFccnD5m%7r$#aF6w+bG
zgop!avPAX)B48C7KS+#)sr9olOoVq<*k&?7c3!b+cq-K<$Eo8^NB0F29zF`Eco1_z
zx_C*oy)-A|27Upja9tL@HXH1L#IUk)YDLVJRG{s6&TAH{IW_Z6u_B{5fr_OH7Gsg_
z1QLnv2oz1g!s3TbSshd6Rpu04vg*q;6W?pEZehZqJjE%Xz3WnFSFbV%)Y~SZvuAqd
zoPs1j*-QqIN+zH|TQk*7^$J0u<4#o_5IZ2)s$-E{;c85fQHZ9PD0a+<;TaeHA;gcI
z3e>FzT8L1bX!?R|u+V2jh>%W%v!ewQ%Q*#rpav*RwxVEA!`2p5m{SlFGT#y;v`0jl
z79O%#FoDKpuJ~PP^?`jLZ2>wv;lRQjzUG#Bv!!-yC}?>4Mf}Q
zB)P1C{4t3`2nje}S^#n!cC6mSaT;vQpgRr~^=Y%hZfbqWwsXnCBfaU^_y&s~P5#3Z4QMd!Bg{PGPc~V$>tFZI-zZr!bA?6?jydxA~j`)|;gikx>*-9N`nXI1LI|
z1!704=1`75cxvq?B{vG4Sa}=_IYoVrYbeH;fU0ocWF9NWdzt?v)F;gzVT}4peM5
zHu`{3lwvT5j@jWzNkuvaSqTuSS9C=Hv5%5RV48BHXwPPiB1Pff!iW{VO06@iU?LDc
z%qd8{7Ol`$#s+kk
zHK{dhjW(PTO};h}c9G{4%T_V(6hwt;
zGNpjbC)gWc5^5CEs;m0|1<^oI2o>j&)zeERxam@rIt3w*V!rytSWe+}ZmVLY#*2&s
zP$Us#K$aftf^mT)OuB$`lPv6C=&g&Qy|9jaz1`+T?oPs9HEfoyp6o7)oP#6>Y
zT-~%lYOE5A;1#Id+IpU9))qo!hT%FBc5#jy1+3yMHwvVJ83958Ry~B6X~^1A{8-Dc
zXcY#UJboafXjhsGp~3X#PJGyyJ&CdZ>l9#uCX79>U?4oXh@qgfq@*b(w9|f^jnpa>
zi!rZ2jT<+OmFhhm^UqdvzvY29&tjz`1CqG}$-poO&Rfn3R#6ImWLDvm_(a(3m1796
zMrmJ4DR7i>B1%m{fp;!1Q`!%ks&Cec(X;%&w?a;s1#GOvh6A5}1l
zdY9KUi&F#U6HEn+qDU)-z(fNjBs69S+&9#Te4qbdG1YAJ*SIyT9SSXG_Cg2jA-m4qD3XDEB(@=zeyIatu#4eZ#YDN5j
zJL0L`3I*1hIyrj!jSoV^!HohECZuH190C>Xv4QMXEpM&Bp`92AknuY}=dEI}-(vMk
z+
ztMFe#&3;9As%}=C7irKy)adgUo!v|TJ4+5{A&
z3(CahiSQ8?fZ{_!fPv&0g}$wSJ(7GhqZi#y@&qvLuC3bu+Bm>?m$
zz?C4$oo~HcMa?Y>9!^ELF7qN!ITIt=294)mby$B%)AqtN7RIOZ|R@=DA8lVC}_ai9^K5@BJ%H-vPolw0@4lv
ziS?cLKY#vwGg#S6tO6|PLZn$mFbO!*>H{B%?_v+S{?hHvjiRHbSbzyS4ko)5?3_%@
zgpWNz;Y?8DH#}~;GtV3evq|7U~qI=M?uMr>GxYCp`_Dk9qU$SOC%2_Jx1`L7k#b
zpnz3$LWm?3yVvx`-!L6)6zUbWgj$CJ(ZdRj)EN-*qwBH@A-8M&7@Tfu9d{I
z))g{|m)A)Og|C;VI`Cp^g920}#R;s8Y6~nz5(O}Kra=HEWI4`+
zE5^a0@b{eMeDEy2Z}LLuVcW1jfbc=vi%_7RWmfS(z_|G7x?oUS^pLHO@fBI6QveF*
zLJyP*Q6zz)7aFKT52M&Q6LxoRD^$=s%$?mE-Z%n7t1GQ;YW@+RSQ`}002ERYgfNSy
zfI@`z1{Yd|rM+Guf+ncGcuuF-Mv90CbxfxKRM>11F2b5avZ2YKpjAL|E-@4|2^jAL
zgEtRqL=TE4Th~MKii&2gUH}RinN}>LV&_#o9K9F{+TXLz2Jwo)EQ0caK{JZ@O=^#f
z1a+z-iQoOfzG2FT8?=wR#_k0?2o&oh{{{NzS{eW|6KnPfg2p6;DzOSev1KhmA+O6`
z+m6LNR3v`Uarn6CX9wza2?^wr2;IQ_aj(>
zwj#7$P^rrdJg~2KgP&USyMaj+IVW^Z?JGzz=jK);}8m?)_nnrjygq|3Jn_?Nz@>e3o#0Sg7kK$2xtLFpicz*
z5krw86!zwG)~4wZ6VZGy0u5GCAIB(`ZC#wg$_Jp;*ajxNSpxMw9~}m9iK)*a&NE1b|`*>2$fC
z9%wZ~&qkk!@PjNmGilS4vns+za2m@>cgaRKWmyeKoxRXTqHd@#hD){A6F?0|L
ztTUC0qxi1LHW8GdP@}kgqu!}gIG>1hv=oTfgiv%m!lE_94?B|aM~L5=zB&5Fr;V3Upe(<5>rFQJSEMIQ@G1=BPbNQno*yis1(jDw1tQp
zWl9i?QrkeG^13NGKHV^+0%a+t&*!N{_;EG<*;ccdwEWfK2=dTiBQ@Y=X4K(PNsz
zBJn_WObyq6dg^n-s8d+WguXX0(X=)rTt}WQqu~Hw>$@L8(J)ZNd7e@$oq!4{T5Q(5
zWnpjK9cb@4pC|O>`~hPIRzfB+d~yLX<;?rtIiIWqigtF6v1|gp41uD@>zbd>Yg*T;
zdx0W3?}oIo2;dtz2$~6^q|38}$8?hC(g+I0D4yjrdis!A2!#|rap3^_6(Jc}KRPZq
zb0WYoP#pT3C#n}S6trooPJv_0xB3JF@y@{;L-DqG3Jo9gk;$xP
zL>qev-smk?B`CIIdQ8F8ZD4*rZa~(f{UZMu3SXdzPu=c`S|>A5q&Nvc_?;Rz{)$}v
z^zhKlpc*0Y{Q}EQ00pv>bODouYIUL3DHSLlKFu+MBN~CCP3t
z;Gid2aUx8Vo#MSYl%YoPuB2OC6rQ1zh>K2f_r~}Uu+S-Z@bR=i(D;MK9vk)oFp55X
zA%_$lis2)oMqUFGP1jzcy>G^>H3D)u;nqOW5GW3r??1E`WX-