Просмотр исходного кода

refactor: use core vite instead of quasar cli

NGPixel 1 год назад
Родитель
Сommit
166f519c75
99 измененных файлов с 1056 добавлено и 925 удалено
  1. 2 0
      .gitignore
  2. 0 2
      ux/.eslintignore
  3. 0 0
      ux/.eslintrc.cjs
  4. 4 0
      ux/.gitignore
  5. 2 1
      ux/index.html
  6. 59 54
      ux/package.json
  7. 469 192
      ux/pnpm-lock.yaml
  8. 4 2
      ux/postcss.config.js
  9. 0 293
      ux/quasar.config.js
  10. 5 7
      ux/src/App.vue
  11. 3 4
      ux/src/boot/apollo.js
  12. 5 7
      ux/src/boot/components.js
  13. 2 3
      ux/src/boot/eventbus.js
  14. 5 6
      ux/src/boot/externals.js
  15. 3 5
      ux/src/boot/i18n.js
  16. 1 1
      ux/src/components/AccountMenu.vue
  17. 2 2
      ux/src/components/AuthLoginPanel.vue
  18. 1 1
      ux/src/components/ChangePwdDialog.vue
  19. 1 1
      ux/src/components/CheckUpdateDialog.vue
  20. 5 5
      ux/src/components/EditorMarkdown.vue
  21. 3 3
      ux/src/components/EditorMarkdownConfigOverlay.vue
  22. 3 3
      ux/src/components/EditorMarkdownUserSettingsOverlay.vue
  23. 3 3
      ux/src/components/EditorWysiwyg.vue
  24. 12 12
      ux/src/components/FileManager.vue
  25. 1 1
      ux/src/components/FolderCreateDialog.vue
  26. 1 1
      ux/src/components/FolderRenameDialog.vue
  27. 1 1
      ux/src/components/FooterNav.vue
  28. 45 45
      ux/src/components/GroupEditOverlay.vue
  29. 6 6
      ux/src/components/HeaderNav.vue
  30. 3 3
      ux/src/components/HeaderSearch.vue
  31. 2 2
      ux/src/components/LocaleSelectorMenu.vue
  32. 3 3
      ux/src/components/MailTemplateEditorOverlay.vue
  33. 2 2
      ux/src/components/NavEditMenu.vue
  34. 3 3
      ux/src/components/NavEditOverlay.vue
  35. 2 2
      ux/src/components/NavSidebar.vue
  36. 5 5
      ux/src/components/PageActionsCol.vue
  37. 2 2
      ux/src/components/PageDataDialog.vue
  38. 2 2
      ux/src/components/PageDataTemplateDialog.vue
  39. 7 7
      ux/src/components/PageHeader.vue
  40. 4 4
      ux/src/components/PageNewMenu.vue
  41. 3 3
      ux/src/components/PagePropertiesDialog.vue
  42. 2 2
      ux/src/components/PageRelationDialog.vue
  43. 2 2
      ux/src/components/PageScriptsDialog.vue
  44. 2 2
      ux/src/components/PageSourceOverlay.vue
  45. 3 3
      ux/src/components/PageTags.vue
  46. 1 1
      ux/src/components/RerenderPageDialog.vue
  47. 1 1
      ux/src/components/SetupTfaDialog.vue
  48. 7 7
      ux/src/components/SideDialog.vue
  49. 1 1
      ux/src/components/TableEditorOverlay.vue
  50. 4 4
      ux/src/components/TreeBrowserDialog.vue
  51. 3 3
      ux/src/components/UploadPendingAssetsDialog.vue
  52. 1 1
      ux/src/components/UserCreateDialog.vue
  53. 2 2
      ux/src/components/UserDefaultsMenu.vue
  54. 3 3
      ux/src/components/UserEditOverlay.vue
  55. 3 3
      ux/src/components/WelcomeOverlay.vue
  56. 6 6
      ux/src/layouts/AdminLayout.vue
  57. 12 12
      ux/src/layouts/MainLayout.vue
  58. 6 6
      ux/src/layouts/ProfileLayout.vue
  59. 80 0
      ux/src/main.js
  60. 2 2
      ux/src/pages/AdminApi.vue
  61. 2 2
      ux/src/pages/AdminAuth.vue
  62. 3 3
      ux/src/pages/AdminBlocks.vue
  63. 5 5
      ux/src/pages/AdminDashboard.vue
  64. 3 3
      ux/src/pages/AdminEditors.vue
  65. 2 2
      ux/src/pages/AdminExtensions.vue
  66. 3 3
      ux/src/pages/AdminFlags.vue
  67. 2 2
      ux/src/pages/AdminGeneral.vue
  68. 2 2
      ux/src/pages/AdminGroups.vue
  69. 2 2
      ux/src/pages/AdminIcons.vue
  70. 1 1
      ux/src/pages/AdminInstances.vue
  71. 2 2
      ux/src/pages/AdminLocale.vue
  72. 2 2
      ux/src/pages/AdminLogin.vue
  73. 3 3
      ux/src/pages/AdminMail.vue
  74. 2 2
      ux/src/pages/AdminMetrics.vue
  75. 2 2
      ux/src/pages/AdminNavigation.vue
  76. 2 2
      ux/src/pages/AdminRendering.vue
  77. 1 1
      ux/src/pages/AdminScheduler.vue
  78. 3 3
      ux/src/pages/AdminSearch.vue
  79. 2 2
      ux/src/pages/AdminSecurity.vue
  80. 1 1
      ux/src/pages/AdminSites.vue
  81. 2 2
      ux/src/pages/AdminStorage.vue
  82. 1 1
      ux/src/pages/AdminSystem.vue
  83. 1 1
      ux/src/pages/AdminTerminal.vue
  84. 3 3
      ux/src/pages/AdminTheme.vue
  85. 4 4
      ux/src/pages/AdminUsers.vue
  86. 1 1
      ux/src/pages/AdminUtilities.vue
  87. 3 3
      ux/src/pages/AdminWebhooks.vue
  88. 11 11
      ux/src/pages/Index.vue
  89. 2 2
      ux/src/pages/Login.vue
  90. 6 6
      ux/src/pages/ProfileAuth.vue
  91. 2 2
      ux/src/pages/ProfileAvatar.vue
  92. 1 1
      ux/src/pages/ProfileGroups.vue
  93. 2 2
      ux/src/pages/ProfileInfo.vue
  94. 6 6
      ux/src/pages/Search.vue
  95. 8 22
      ux/src/router/index.js
  96. 40 40
      ux/src/router/routes.js
  97. 7 12
      ux/src/stores/index.js
  98. 1 1
      ux/src/stores/user.js
  99. 83 0
      ux/vite.config.js

+ 2 - 0
.gitignore

@@ -44,3 +44,5 @@ test-results/
 # Localization Resources
 /server/locales/*.json
 !/server/locales/en.json
+
+cloudflared.deb

+ 0 - 2
ux/.eslintignore

@@ -1,6 +1,4 @@
 /dist
-/src-capacitor
-/src-cordova
 /.quasar
 /node_modules
 .eslintrc.js

+ 0 - 0
ux/.eslintrc.js → ux/.eslintrc.cjs


+ 4 - 0
ux/.gitignore

@@ -5,6 +5,10 @@ node_modules
 # Quasar core related directories
 .quasar
 /dist
+/quasar.config.*.temporary.compiled*
+
+# local .env files
+.env.local*
 
 # Cordova related directories and files
 /src-cordova/node_modules

+ 2 - 1
ux/index.html

@@ -69,6 +69,7 @@
   </head>
   <body class="wiki-root">
     <div class="init-loading"></div>
-    <div id="app"><!-- quasar:entry-point --></div>
+    <div id="app"></div>
+    <script type="module" src="/src/main.js"></script>
   </body>
 </html>

+ 59 - 54
ux/package.json

@@ -5,41 +5,42 @@
   "productName": "Wiki.js",
   "author": "Nicolas Giard <nick@requarks.io>",
   "private": true,
+  "type": "module",
   "scripts": {
-    "dev": "quasar dev",
-    "build": "NODE_OPTIONS=--max-old-space-size=8192 quasar build"
+    "dev": "vite --force",
+    "build": "NODE_OPTIONS=--max-old-space-size=8192 vite build --emptyOutDir"
   },
   "dependencies": {
-    "@apollo/client": "3.9.2",
+    "@apollo/client": "3.9.9",
     "@lezer/common": "1.2.1",
     "@mdi/font": "7.4.47",
-    "@quasar/extras": "1.16.9",
+    "@quasar/extras": "1.16.11",
     "@simplewebauthn/browser": "9.0.1",
-    "@tiptap/core": "2.2.1",
-    "@tiptap/extension-code-block": "2.2.1",
-    "@tiptap/extension-code-block-lowlight": "2.2.1",
-    "@tiptap/extension-color": "2.2.1",
-    "@tiptap/extension-dropcursor": "2.2.1",
-    "@tiptap/extension-font-family": "2.2.1",
-    "@tiptap/extension-gapcursor": "2.2.1",
-    "@tiptap/extension-hard-break": "2.2.1",
-    "@tiptap/extension-highlight": "2.2.1",
-    "@tiptap/extension-history": "2.2.1",
-    "@tiptap/extension-image": "2.2.1",
-    "@tiptap/extension-mention": "2.2.1",
-    "@tiptap/extension-placeholder": "2.2.1",
-    "@tiptap/extension-table": "2.2.1",
-    "@tiptap/extension-table-cell": "2.2.1",
-    "@tiptap/extension-table-header": "2.2.1",
-    "@tiptap/extension-table-row": "2.2.1",
-    "@tiptap/extension-task-item": "2.2.1",
-    "@tiptap/extension-task-list": "2.2.1",
-    "@tiptap/extension-text-align": "2.2.1",
-    "@tiptap/extension-text-style": "2.2.1",
-    "@tiptap/extension-typography": "2.2.1",
-    "@tiptap/pm": "2.2.1",
-    "@tiptap/starter-kit": "2.2.1",
-    "@tiptap/vue-3": "2.2.1",
+    "@tiptap/core": "2.2.4",
+    "@tiptap/extension-code-block": "2.2.4",
+    "@tiptap/extension-code-block-lowlight": "2.2.4",
+    "@tiptap/extension-color": "2.2.4",
+    "@tiptap/extension-dropcursor": "2.2.4",
+    "@tiptap/extension-font-family": "2.2.4",
+    "@tiptap/extension-gapcursor": "2.2.4",
+    "@tiptap/extension-hard-break": "2.2.4",
+    "@tiptap/extension-highlight": "2.2.4",
+    "@tiptap/extension-history": "2.2.4",
+    "@tiptap/extension-image": "2.2.4",
+    "@tiptap/extension-mention": "2.2.4",
+    "@tiptap/extension-placeholder": "2.2.4",
+    "@tiptap/extension-table": "2.2.4",
+    "@tiptap/extension-table-cell": "2.2.4",
+    "@tiptap/extension-table-header": "2.2.4",
+    "@tiptap/extension-table-row": "2.2.4",
+    "@tiptap/extension-task-item": "2.2.4",
+    "@tiptap/extension-task-list": "2.2.4",
+    "@tiptap/extension-text-align": "2.2.4",
+    "@tiptap/extension-text-style": "2.2.4",
+    "@tiptap/extension-typography": "2.2.4",
+    "@tiptap/pm": "2.2.4",
+    "@tiptap/starter-kit": "2.2.4",
+    "@tiptap/vue-3": "2.2.4",
     "@vue/repl": "3.4.0",
     "apollo-upload-client": "18.0.1",
     "browser-fs-access": "0.35.0",
@@ -47,19 +48,19 @@
     "codemirror": "5.65.11",
     "codemirror-asciidoc": "1.0.4",
     "dependency-graph": "1.0.0",
-    "filesize": "10.1.0",
+    "filesize": "10.1.1",
     "filesize-parser": "1.5.0",
     "fuse.js": "7.0.0",
-    "graphql": "16.6.0",
+    "graphql": "16.8.1",
     "graphql-tag": "2.12.6",
     "highlight.js": "11.9.0",
     "js-cookie": "3.0.5",
     "jwt-decode": "4.0.0",
-    "katex": "0.16.9",
+    "katex": "0.16.10",
     "lodash-es": "4.17.21",
     "lowlight": "3.1.0",
     "luxon": "3.4.4",
-    "markdown-it": "14.0.0",
+    "markdown-it": "14.1.0",
     "markdown-it-abbr": "2.0.0",
     "markdown-it-attrs": "4.1.6",
     "markdown-it-decorate": "1.2.2",
@@ -68,57 +69,61 @@
     "markdown-it-footnote": "4.0.0",
     "markdown-it-imsize": "2.0.1",
     "markdown-it-mark": "4.0.0",
-    "markdown-it-mdc": "0.2.2",
+    "markdown-it-mdc": "0.2.3",
     "markdown-it-multimd-table": "4.2.3",
     "markdown-it-sub": "2.0.0",
     "markdown-it-sup": "2.0.0",
     "markdown-it-task-lists": "2.1.1",
     "mitt": "3.0.1",
-    "monaco-editor": "0.45.0",
+    "monaco-editor": "0.47.0",
     "pako": "2.1.0",
     "pinia": "2.1.7",
     "prosemirror-commands": "1.5.2",
-    "prosemirror-history": "1.3.2",
+    "prosemirror-history": "1.4.0",
     "prosemirror-keymap": "1.2.2",
     "prosemirror-model": "1.19.4",
     "prosemirror-schema-list": "1.3.0",
     "prosemirror-state": "1.4.3",
     "prosemirror-transform": "1.8.0",
-    "prosemirror-view": "1.32.7",
+    "prosemirror-view": "1.33.3",
     "pug": "3.0.2",
-    "quasar": "2.14.3",
+    "quasar": "2.15.2",
     "slugify": "1.6.6",
-    "socket.io-client": "4.7.4",
+    "socket.io-client": "4.7.5",
     "sortablejs": "1.15.2",
     "sortablejs-vue3": "1.2.11",
-    "tabulator-tables": "5.5.4",
+    "tabulator-tables": "6.2.0",
     "tippy.js": "6.3.7",
     "twemoji": "14.0.2",
-    "typescript": "5.3.3",
+    "typescript": "5.4.3",
     "uuid": "9.0.1",
-    "v-network-graph": "0.9.14",
-    "vue": "3.4.15",
-    "vue-i18n": "9.9.1",
-    "vue-router": "4.2.5",
-    "vue3-otp-input": "0.4.1",
+    "v-network-graph": "0.9.15",
+    "vue": "3.4.21",
+    "vue-i18n": "9.10.2",
+    "vue-router": "4.3.0",
+    "vue3-otp-input": "0.4.4",
     "vuedraggable": "4.1.0",
     "xterm": "5.3.0",
     "zxcvbn": "4.4.2"
   },
   "devDependencies": {
-    "@intlify/unplugin-vue-i18n": "2.0.0",
-    "@quasar/app-vite": "1.7.3",
-    "@types/lodash": "4.14.202",
-    "@vue/language-plugin-pug": "1.8.27",
-    "autoprefixer": "10.4.17",
+    "@eslint/js": "8.57.0",
+    "@intlify/unplugin-vue-i18n": "4.0.0",
+    "@quasar/app-vite": "2.0.0-beta.5",
+    "@quasar/vite-plugin": "1.6.0",
+    "@types/lodash": "4.17.0",
+    "@vue/devtools": "7.0.25",
+    "@vue/language-plugin-pug": "2.0.7",
+    "autoprefixer": "10.4.19",
     "browserlist": "latest",
-    "eslint": "8.56.0",
+    "eslint": "8.57.0",
     "eslint-config-standard": "17.1.0",
     "eslint-plugin-import": "2.29.1",
     "eslint-plugin-n": "16.6.2",
     "eslint-plugin-promise": "6.1.1",
-    "eslint-plugin-vue": "9.21.1",
-    "eslint-plugin-vue-pug": "0.6.1"
+    "eslint-plugin-vue": "9.24.0",
+    "eslint-plugin-vue-pug": "0.6.2",
+    "sass": "1.72.0"
   },
   "engines": {
     "node": ">= 18.0",

Разница между файлами не показана из-за своего большого размера
+ 469 - 192
ux/pnpm-lock.yaml


+ 4 - 2
ux/postcss.config.js

@@ -1,10 +1,12 @@
+import autoprefixer from 'autoprefixer'
+
 /* eslint-disable */
 // https://github.com/michael-ciniawsky/postcss-load-config
 
-module.exports = {
+export default {
   plugins: [
     // https://github.com/postcss/autoprefixer
-    require('autoprefixer')({
+    autoprefixer({
       overrideBrowserslist: [
         'last 4 Chrome versions',
         'last 4 Firefox versions',

+ 0 - 293
ux/quasar.config.js

@@ -1,293 +0,0 @@
-/* eslint-env node */
-
-/*
- * This file runs in a Node context (it's NOT transpiled by Babel), so use only
- * the ES6 features that are supported by your Node version. https://node.green/
- */
-
-// Configuration for your app
-// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
-
-const { configure } = require('quasar/wrappers')
-const path = require('path')
-const yaml = require('js-yaml')
-const fs = require('fs')
-
-module.exports = configure(function (ctx) {
-  const userConfig = ctx.dev ? {
-    dev: { port: 3001, hmrClientPort: 3001 },
-    ...yaml.load(fs.readFileSync(path.resolve(__dirname, '../config.yml'), 'utf8'))
-  } : {}
-
-  return {
-    eslint: {
-      fix: true,
-      // include = [],
-      // exclude = [],
-      // rawOptions = {},
-      warnings: true,
-      errors: true
-    },
-
-    // https://v2.quasar.dev/quasar-cli/prefetch-feature
-    preFetch: true,
-
-    // app boot file (/src/boot)
-    // --> boot files are part of "main.js"
-    // https://v2.quasar.dev/quasar-cli/boot-files
-    boot: [
-      'apollo',
-      'components',
-      'externals',
-      'eventbus',
-      'i18n',
-      {
-        server: false,
-        path: 'monaco'
-      }
-    ],
-
-    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
-    css: [
-      'app.scss'
-    ],
-
-    // https://github.com/quasarframework/quasar/tree/dev/extras
-    extras: [
-      // 'ionicons-v4',
-      // 'mdi-v5',
-      // 'mdi-v7',
-      // 'fontawesome-v6',
-      // 'eva-icons',
-      // 'themify',
-      'line-awesome'
-      // 'roboto-font-latin-ext' // this or either 'roboto-font', NEVER both!
-      // 'roboto-font', // optional, you are not bound to it
-      // 'material-icons' // optional, you are not bound to it
-    ],
-
-    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
-    build: {
-      target: {
-        browser: ['es2019', 'edge88', 'firefox78', 'chrome87', 'safari13.1'],
-        node: 'node18'
-      },
-
-      vueRouterMode: 'history', // available values: 'hash', 'history'
-      // vueRouterBase,
-      // vueDevtools,
-      vueOptionsAPI: false,
-
-      rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
-
-      // publicPath: '/',
-      // analyze: true,
-      // env: {},
-      // rawDefine: {}
-      // ignorePublicFolder: true,
-      // minify: false,
-      // polyfillModulePreload: true,
-      distDir: '../assets',
-
-      extendViteConf (viteConf) {
-        if (ctx.prod) {
-          viteConf.build.assetsDir = '_assets'
-          viteConf.build.rollupOptions = {
-            ...viteConf.build.rollupOptions ?? {},
-            output: {
-              manualChunks (id) {
-                if (id.includes('lodash')) {
-                  return 'lodash'
-                // } else if (id.includes('quasar')) {
-                //   return 'quasar'
-                } else if (id.includes('pages/Admin')) {
-                  return 'admin'
-                } else if (id.includes('pages/Profile')) {
-                  return 'profile'
-                }
-              }
-            }
-          }
-          viteConf.build.chunkSizeWarningLimit = 5000
-          viteConf.optimizeDeps.include = [
-            'prosemirror-state',
-            'prosemirror-transform',
-            'prosemirror-model',
-            'prosemirror-view'
-          ]
-        }
-        viteConf.build.dynamicImportVarsOptions = {
-          warnOnError: true,
-          include: ['!/_blocks/**']
-        }
-      },
-      // viteVuePluginOptions: {},
-
-      vitePlugins: [
-        ['@intlify/unplugin-vue-i18n/vite', {
-          // if you want to use Vue I18n Legacy API, you need to set `compositionOnly: false`
-          // compositionOnly: false,
-
-          // you need to set i18n resource including paths !
-          include: path.resolve(__dirname, './src/i18n/locales/**')
-        }]
-      ]
-      // sourcemap: true
-    },
-
-    // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
-    devServer: {
-      // https: true
-      open: false, // opens browser window automatically
-      port: userConfig.dev?.port,
-      proxy: ['_graphql', '_blocks', '_site', '_thumb', '_user'].reduce((result, key) => {
-        result[`/${key}`] = {
-          target: {
-            host: '127.0.0.1',
-            port: userConfig.port
-          }
-        }
-        return result
-      }, {}),
-      hmr: {
-        clientPort: userConfig.dev?.hmrClientPort
-      },
-      vueDevtools: true
-    },
-
-    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
-    framework: {
-      config: {
-        brand: {
-          header: '#000',
-          sidebar: '#1976D2'
-        },
-        loading: {
-          delay: 500,
-          spinner: 'QSpinnerGrid',
-          spinnerSize: 32,
-          spinnerColor: 'white',
-          customClass: 'loading-darker'
-        },
-        loadingBar: {
-          color: 'primary',
-          size: '1px',
-          position: 'top'
-        },
-        notify: {
-          position: 'top',
-          progress: true,
-          color: 'green',
-          icon: 'las la-check',
-          actions: [
-            {
-              icon: 'las la-times',
-              color: 'white',
-              size: 'sm',
-              round: true,
-              handler: () => {}
-            }
-          ]
-        }
-      },
-
-      iconSet: 'mdi-v7', // Quasar icon set
-      lang: 'en-US', // Quasar language pack
-
-      // For special cases outside of where the auto-import strategy can have an impact
-      // (like functional components as one of the examples),
-      // you can manually specify Quasar components/directives to be available everywhere:
-      //
-      // components: [],
-      // directives: [],
-
-      // Quasar plugins
-      plugins: [
-        'Dialog',
-        'Loading',
-        'LoadingBar',
-        'Meta',
-        'Notify'
-      ]
-    },
-
-    // animations: 'all', // --- includes all animations
-    // https://v2.quasar.dev/options/animations
-    animations: [],
-
-    // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
-    sourceFiles: {
-      // rootComponent: 'src/App.vue',
-      // router: 'src/router/index',
-      store: 'src/stores/index'
-      // registerServiceWorker: 'src-pwa/register-service-worker',
-      // serviceWorker: 'src-pwa/custom-service-worker',
-      // pwaManifestFile: 'src-pwa/manifest.json',
-      // electronMain: 'src-electron/electron-main',
-      // electronPreload: 'src-electron/electron-preload'
-    },
-
-    // https://v2.quasar.dev/quasar-cli/developing-ssr/configuring-ssr
-    ssr: {
-      // ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
-      // will mess up SSR
-
-      // extendSSRWebserverConf (esbuildConf) {},
-      // extendPackageJson (json) {},
-
-      pwa: false,
-
-      // manualStoreHydration: true,
-      // manualPostHydrationTrigger: true,
-
-      prodPort: 3000, // The default port that the production server should use
-      // (gets superseded if process.env.PORT is specified at runtime)
-
-      middlewares: [
-        'render' // keep this as last one
-      ]
-    },
-
-    // https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
-    pwa: {
-      workboxMode: 'generateSW', // or 'injectManifest'
-      injectPwaMetaTags: true,
-      swFilename: 'sw.js',
-      manifestFilename: 'manifest.json',
-      useCredentialsForManifestTag: false
-      // extendGenerateSWOptions (cfg) {}
-      // extendInjectManifestOptions (cfg) {},
-      // extendManifestJson (json) {}
-      // extendPWACustomSWConf (esbuildConf) {}
-    },
-
-    // Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
-    electron: {
-      // extendElectronMainConf (esbuildConf)
-      // extendElectronPreloadConf (esbuildConf)
-
-      inspectPort: 5858,
-
-      bundler: 'packager', // 'packager' or 'builder'
-
-      packager: {
-        // https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
-
-        // OS X / Mac App Store
-        // appBundleId: '',
-        // appCategoryType: '',
-        // osxSign: '',
-        // protocol: 'myapp://path',
-
-        // Windows only
-        // win32metadata: { ... }
-      },
-
-      builder: {
-        // https://www.electron.build/configuration/configuration
-
-        appId: 'ux'
-      }
-    }
-  }
-})

+ 5 - 7
ux/src/App.vue

@@ -1,4 +1,4 @@
-<template lang="pug">
+<template lang='pug'>
 router-view
 </template>
 
@@ -8,12 +8,10 @@ import { useRouter, useRoute } from 'vue-router'
 import { setCssVar, useQuasar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 
-import '@mdi/font/css/materialdesignicons.css'
-
 import { useCommonStore } from './stores/common'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 /* global siteConfig */
 
@@ -105,7 +103,7 @@ async function applyTheme () {
 
     const newHljsStyleEl = document.createElement('style')
     newHljsStyleEl.id = 'hljs-theme'
-    newHljsStyleEl.innerHTML = (await import(`../node_modules/highlight.js/styles/${desiredHljsTheme}.css`)).default
+    // newHljsStyleEl.innerHTML = (await import(`../node_modules/highlight.js/styles/${desiredHljsTheme}.css`)).default
     document.head.appendChild(newHljsStyleEl)
   }
 }

+ 3 - 4
ux/src/boot/apollo.js

@@ -1,12 +1,11 @@
-import { boot } from 'quasar/wrappers'
 import { ApolloClient, HttpLink, InMemoryCache, from, split } from '@apollo/client/core'
 import { setContext } from '@apollo/client/link/context'
 import { BatchHttpLink } from '@apollo/client/link/batch-http'
 import createUploadLink from 'apollo-upload-client/createUploadLink.mjs'
 
-import { useUserStore } from 'src/stores/user'
+import { useUserStore } from '@/stores/user'
 
-export default boot(({ app, store }) => {
+export function initializeApollo (store) {
   const userStore = useUserStore(store)
 
   const defaultLinkOptions = {
@@ -102,4 +101,4 @@ export default boot(({ app, store }) => {
   } else {
     window.APOLLO_CLIENT = client
   }
-})
+}

+ 5 - 7
ux/src/boot/components.js

@@ -1,13 +1,11 @@
-import { boot } from 'quasar/wrappers'
-
-import BlueprintIcon from '../components/BlueprintIcon.vue'
-import StatusLight from '../components/StatusLight.vue'
-import LoadingGeneric from '../components/LoadingGeneric.vue'
+import BlueprintIcon from '@/components/BlueprintIcon.vue'
+import StatusLight from '@/components/StatusLight.vue'
+import LoadingGeneric from '@/components/LoadingGeneric.vue'
 import VNetworkGraph from 'v-network-graph'
 
-export default boot(({ app }) => {
+export function initializeComponents (app) {
   app.component('BlueprintIcon', BlueprintIcon)
   app.component('LoadingGeneric', LoadingGeneric)
   app.component('StatusLight', StatusLight)
   app.use(VNetworkGraph)
-})
+}

+ 2 - 3
ux/src/boot/eventbus.js

@@ -1,7 +1,6 @@
-import { boot } from 'quasar/wrappers'
 import mitt from 'mitt'
 
-export default boot(({ app }) => {
+export function initializeEventBus () {
   const emitter = mitt()
 
   if (import.meta.env.SSR) {
@@ -9,4 +8,4 @@ export default boot(({ app }) => {
   } else {
     window.EVENT_BUS = emitter
   }
-})
+}

+ 5 - 6
ux/src/boot/externals.js

@@ -1,9 +1,8 @@
-import { boot } from 'quasar/wrappers'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-export default boot(({ router, store }) => {
+export function initializeExternals (router, store) {
   if (import.meta.env.SSR) {
     global.WIKI_STATE = {
       page: usePageStore(store),
@@ -19,4 +18,4 @@ export default boot(({ router, store }) => {
     }
     window.WIKI_ROUTER = router
   }
-})
+}

+ 3 - 5
ux/src/boot/i18n.js

@@ -1,9 +1,7 @@
-import { boot } from 'quasar/wrappers'
 import { createI18n } from 'vue-i18n'
+import { useCommonStore } from '@/stores/common'
 
-import { useCommonStore } from 'src/stores/common'
-
-export default boot(({ app, store }) => {
+export function initializeI18n (app, store) {
   const commonStore = useCommonStore(store)
 
   const i18n = createI18n({
@@ -16,4 +14,4 @@ export default boot(({ app, store }) => {
 
   // Set i18n instance on app
   app.use(i18n)
-})
+}

+ 1 - 1
ux/src/components/AccountMenu.vue

@@ -37,7 +37,7 @@ q-btn.account-avbtn.q-ml-md(flat, round, dense, color='custom-color')
 <script setup>
 import { useI18n } from 'vue-i18n'
 
-import { useUserStore } from 'src/stores/user'
+import { useUserStore } from '@/stores/user'
 
 // STORES
 

+ 2 - 2
ux/src/components/AuthLoginPanel.vue

@@ -351,8 +351,8 @@ import {
   startAuthentication
 } from '@simplewebauthn/browser'
 
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 import VOtpInput from 'vue3-otp-input'
 

+ 1 - 1
ux/src/components/ChangePwdDialog.vue

@@ -90,7 +90,7 @@ import { useI18n } from 'vue-i18n'
 import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { computed, reactive, ref } from 'vue'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 1 - 1
ux/src/components/CheckUpdateDialog.vue

@@ -48,7 +48,7 @@ import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { computed, onMounted, reactive } from 'vue'
 import { DateTime } from 'luxon'
 
-import { useUserStore } from 'src/stores/user'
+import { useUserStore } from '@/stores/user'
 
 // EMITS
 

+ 5 - 5
ux/src/components/EditorMarkdown.vue

@@ -273,13 +273,13 @@ import { DateTime } from 'luxon'
 import * as monaco from 'monaco-editor'
 import { Position, Range } from 'monaco-editor'
 
-import { useCommonStore } from 'src/stores/common'
-import { useEditorStore } from 'src/stores/editor'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useCommonStore } from '@/stores/common'
+import { useEditorStore } from '@/stores/editor'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // Markdown Renderer
-import { MarkdownRenderer } from 'src/renderers/markdown'
+import { MarkdownRenderer } from '@/renderers/markdown'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/EditorMarkdownConfigOverlay.vue

@@ -259,9 +259,9 @@ import { onMounted, reactive } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useEditorStore } from 'src/stores/editor'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useEditorStore } from '@/stores/editor'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/EditorMarkdownUserSettingsOverlay.vue

@@ -89,9 +89,9 @@ import { onMounted, reactive } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { useEditorStore } from 'src/stores/editor'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useEditorStore } from '@/stores/editor'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/EditorWysiwyg.vue

@@ -116,9 +116,9 @@ import { useMeta, useQuasar, setCssVar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 import { DateTime } from 'luxon'
 
-import { useEditorStore } from 'src/stores/editor'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useEditorStore } from '@/stores/editor'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 const lowlight = createLowlight(common)
 

+ 12 - 12
ux/src/components/FileManager.vue

@@ -331,17 +331,17 @@ import Fuse from 'fuse.js/basic'
 import NewMenu from './PageNewMenu.vue'
 import Tree from './TreeNav.vue'
 
-import fileTypes from '../helpers/fileTypes'
+import fileTypes from '@/helpers/fileTypes'
 
-import { useCommonStore } from 'src/stores/common'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useCommonStore } from '@/stores/common'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
-import FolderCreateDialog from 'src/components/FolderCreateDialog.vue'
-import FolderDeleteDialog from 'src/components/FolderDeleteDialog.vue'
-import FolderRenameDialog from 'src/components/FolderRenameDialog.vue'
-import AssetRenameDialog from 'src/components/AssetRenameDialog.vue'
-import LocaleSelectorMenu from 'src/components/LocaleSelectorMenu.vue'
+import FolderCreateDialog from '@/components/FolderCreateDialog.vue'
+import FolderDeleteDialog from '@/components/FolderDeleteDialog.vue'
+import FolderRenameDialog from '@/components/FolderRenameDialog.vue'
+import AssetRenameDialog from '@/components/AssetRenameDialog.vue'
+import LocaleSelectorMenu from '@/components/LocaleSelectorMenu.vue'
 
 // QUASAR
 
@@ -793,7 +793,7 @@ function reloadFolder (folderId) {
 
 function rerenderPage (item) {
   $q.dialog({
-    component: defineAsyncComponent(() => import('src/components/RerenderPageDialog.vue')),
+    component: defineAsyncComponent(() => import('@/components/RerenderPageDialog.vue')),
     componentProps: {
       id: item.id
     }
@@ -802,7 +802,7 @@ function rerenderPage (item) {
 
 function delPage (pageId, pageName) {
   $q.dialog({
-    component: defineAsyncComponent(() => import('src/components/PageDeleteDialog.vue')),
+    component: defineAsyncComponent(() => import('@/components/PageDeleteDialog.vue')),
     componentProps: {
       pageId,
       pageName
@@ -831,7 +831,7 @@ function renameAsset (assetId) {
 
 function delAsset (assetId, assetName) {
   $q.dialog({
-    component: defineAsyncComponent(() => import('src/components/AssetDeleteDialog.vue')),
+    component: defineAsyncComponent(() => import('@/components/AssetDeleteDialog.vue')),
     componentProps: {
       assetId,
       assetName

+ 1 - 1
ux/src/components/FolderCreateDialog.vue

@@ -63,7 +63,7 @@ import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { reactive, ref, watch } from 'vue'
 import slugify from 'slugify'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 1 - 1
ux/src/components/FolderRenameDialog.vue

@@ -65,7 +65,7 @@ import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { onMounted, reactive, ref, watch } from 'vue'
 import slugify from 'slugify'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 1 - 1
ux/src/components/FooterNav.vue

@@ -28,7 +28,7 @@ q-footer.site-footer
 import { computed } from 'vue'
 import { useI18n } from 'vue-i18n'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 45 - 45
ux/src/components/GroupEditOverlay.vue

@@ -1,10 +1,10 @@
-<template lang="pug">
+<template lang='pug'>
 q-layout(view='hHh lpR fFf', container)
   q-header.card-header.q-px-md.q-py-sm
     q-icon(name='img:/_assets/icons/fluent-people.svg', left, size='md')
     div
-      span {{t(`admin.groups.edit`)}}
-      .text-caption {{state.group.name}}
+      span {{ t(`admin.groups.edit`) }}
+      .text-caption {{ state.group.name }}
     q-space
     q-btn-group(push)
       q-btn(
@@ -15,7 +15,7 @@ q-layout(view='hHh lpR fFf', container)
         icon='las la-redo-alt'
         @click='refresh'
         )
-        q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}}
+        q-tooltip(anchor='center left', self='center right') {{ t(`common.actions.refresh`) }}
       q-btn(
         push
         color='white'
@@ -43,7 +43,7 @@ q-layout(view='hHh lpR fFf', container)
           )
           q-item-section(side)
             q-icon(:name='sc.icon', color='white')
-          q-item-section {{sc.text}}
+          q-item-section {{ sc.text }}
           q-item-section(side, v-if='sc.usersTotal')
             q-badge(color='dark-3', :label='state.usersTotal')
           q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
@@ -59,12 +59,12 @@ q-layout(view='hHh lpR fFf', container)
           .col-12.col-lg-8
             q-card.shadow-1.q-pb-sm
               q-card-section
-                .text-subtitle1 {{t('admin.groups.general')}}
+                .text-subtitle1 {{ t('admin.groups.general') }}
               q-item
                 blueprint-icon(icon='team')
                 q-item-section
-                  q-item-label {{t(`admin.groups.name`)}}
-                  q-item-label(caption) {{t(`admin.groups.nameHint`)}}
+                  q-item-label {{ t(`admin.groups.name`) }}
+                  q-item-label(caption) {{ t(`admin.groups.nameHint`) }}
                 q-item-section
                   q-input(
                     outlined
@@ -78,12 +78,12 @@ q-layout(view='hHh lpR fFf', container)
 
             q-card.shadow-1.q-pb-sm.q-mt-md(v-if='!isGuestGroup')
               q-card-section
-                .text-subtitle1 {{t('admin.groups.authBehaviors')}}
+                .text-subtitle1 {{ t('admin.groups.authBehaviors') }}
               q-item
                 blueprint-icon(icon='double-right')
                 q-item-section
-                  q-item-label {{t(`admin.groups.redirectOnLogin`)}}
-                  q-item-label(caption) {{t(`admin.groups.redirectOnLoginHint`)}}
+                  q-item-label {{ t(`admin.groups.redirectOnLogin`) }}
+                  q-item-label(caption) {{ t(`admin.groups.redirectOnLoginHint`) }}
                 q-item-section
                   q-input(
                     outlined
@@ -95,8 +95,8 @@ q-layout(view='hHh lpR fFf', container)
               q-item
                 blueprint-icon(icon='chevron-right')
                 q-item-section
-                  q-item-label {{t(`admin.groups.redirectOnFirstLogin`)}}
-                  q-item-label(caption) {{t(`admin.groups.redirectOnFirstLoginHint`)}}
+                  q-item-label {{ t(`admin.groups.redirectOnFirstLogin`) }}
+                  q-item-label(caption) {{ t(`admin.groups.redirectOnFirstLoginHint`) }}
                 q-item-section
                   q-input(
                     outlined
@@ -108,8 +108,8 @@ q-layout(view='hHh lpR fFf', container)
               q-item
                 blueprint-icon(icon='exit')
                 q-item-section
-                  q-item-label {{t(`admin.groups.redirectOnLogout`)}}
-                  q-item-label(caption) {{t(`admin.groups.redirectOnLogoutHint`)}}
+                  q-item-label {{ t(`admin.groups.redirectOnLogout`) }}
+                  q-item-label(caption) {{ t(`admin.groups.redirectOnLogoutHint`) }}
                 q-item-section
                   q-input(
                     outlined
@@ -121,23 +121,23 @@ q-layout(view='hHh lpR fFf', container)
           .col-12.col-lg-4
             q-card.shadow-1.q-pb-sm
               q-card-section
-                .text-subtitle1 {{t('admin.groups.info')}}
+                .text-subtitle1 {{ t('admin.groups.info') }}
               q-item
                 blueprint-icon(icon='team', :hue-rotate='-45')
                 q-item-section
-                  q-item-label {{t(`common.field.id`)}}
+                  q-item-label {{ t(`common.field.id`) }}
                   q-item-label: strong {{state.group.id}}
               q-separator.q-my-sm(inset)
               q-item
                 blueprint-icon(icon='calendar-plus', :hue-rotate='-45')
                 q-item-section
-                  q-item-label {{t(`common.field.createdOn`)}}
+                  q-item-label {{ t(`common.field.createdOn`) }}
                   q-item-label: strong {{humanizeDate(state.group.createdAt)}}
               q-separator.q-my-sm(inset)
               q-item
                 blueprint-icon(icon='summertime', :hue-rotate='-45')
                 q-item-section
-                  q-item-label {{t(`common.field.lastUpdated`)}}
+                  q-item-label {{ t(`common.field.lastUpdated`) }}
                   q-item-label: strong {{humanizeDate(state.group.updatedAt)}}
     //- -----------------------------------------------------------------------
     //- RULES
@@ -146,7 +146,7 @@ q-layout(view='hHh lpR fFf', container)
       q-toolbar.q-pl-md(
         :class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
         )
-        .text-subtitle1 {{t('admin.groups.rules')}}
+        .text-subtitle1 {{ t('admin.groups.rules') }}
         q-space
         q-btn.acrylic-btn.q-mr-sm(
           icon='las la-question-circle'
@@ -162,14 +162,14 @@ q-layout(view='hHh lpR fFf', container)
           icon='las la-file-export'
           @click='exportRules'
           )
-          q-tooltip {{t('admin.groups.exportRules')}}
+          q-tooltip {{ t('admin.groups.exportRules') }}
         q-btn.acrylic-btn.q-mr-sm(
           flat
           color='indigo'
           icon='las la-file-import'
           @click='importRules'
           )
-          q-tooltip {{t('admin.groups.importRules')}}
+          q-tooltip {{ t('admin.groups.importRules') }}
         q-btn(
           unelevated
           color='primary'
@@ -183,11 +183,11 @@ q-layout(view='hHh lpR fFf', container)
           v-if='!state.group.rules || state.group.rules.length < 1'
           rounded
           :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
-          ) {{t('admin.groups.rulesNone')}}
+          ) {{ t('admin.groups.rulesNone') }}
         q-card.shadow-1.q-pb-sm(v-else)
           q-card-section
             .admin-groups-rule(
-              v-for='(rule, idx) of state.group.rules'
+              v-for='rule of state.group.rules'
               :key='rule.id'
               )
               .admin-groups-rule-icon(:class='getRuleModeColor(rule.mode)')
@@ -197,7 +197,7 @@ q-layout(view='hHh lpR fFf', container)
                   @click='rule.mode = getNextRuleMode(rule.mode)'
                 )
               .admin-groups-rule-name
-                .admin-groups-rule-name-text: strong(:class='getRuleModeColor(rule.mode)') {{getRuleModeName(rule.mode)}}
+                .admin-groups-rule-name-text: strong(:class='getRuleModeColor(rule.mode)') {{ getRuleModeName(rule.mode) }}
                 q-separator.q-ml-sm.q-mr-xs(vertical)
                 input(
                   type='text'
@@ -222,7 +222,7 @@ q-layout(view='hHh lpR fFf', container)
                     use-chips
                     stack-label
                     )
-                    template(v-slot:selected-item='scope')
+                    template(#selected-item='scope')
                       q-chip(
                         square
                         dense
@@ -231,7 +231,7 @@ q-layout(view='hHh lpR fFf', container)
                         text-color='white'
                         )
                         span.text-caption {{ scope.opt.title }}
-                    template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+                    template(#option='{ itemProps, itemEvents, opt, selected, toggleOption }')
                       q-item(v-bind='itemProps', v-on='itemEvents')
                         q-item-section(side)
                           q-toggle(
@@ -250,7 +250,7 @@ q-layout(view='hHh lpR fFf', container)
                         //-     dense
                         //-   ) {{opt.permission}}
                         q-item-section
-                          q-item-label {{opt.title}}
+                          q-item-label {{ opt.title }}
                           q-item-label(caption) {{opt.hint}}
                   q-btn.acrylic-btn.q-ml-md(
                     flat
@@ -277,10 +277,10 @@ q-layout(view='hHh lpR fFf', container)
                       behavior='dialog'
                       :display-value='t(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
                       )
-                      template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+                      template(#option='{ itemProps, itemEvents, opt, selected, toggleOption }')
                         q-item(v-bind='itemProps', v-on='itemEvents')
                           q-item-section
-                            q-item-label {{opt.title}}
+                            q-item-label {{ opt.title }}
                           q-item-section(side)
                             q-toggle(
                               :model-value='selected'
@@ -304,10 +304,10 @@ q-layout(view='hHh lpR fFf', container)
                       behavior='dialog'
                       :display-value='t(`admin.groups.selectedLocales`, { n: rule.locales.length > 0 ? rule.locales[0].toUpperCase() : rule.locales.length }, rule.locales.length)'
                       )
-                      template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
+                      template(#option='{ itemProps, opt, selected, toggleOption }')
                         q-item(v-bind='itemProps')
                           q-item-section
-                            q-item-label {{opt.name}}
+                            q-item-label {{ opt.name }}
                           q-item-section(side)
                             q-toggle(
                               :model-value='selected'
@@ -353,7 +353,7 @@ q-layout(view='hHh lpR fFf', container)
             q-card.shadow-1.q-pb-sm
               .flex.justify-between
                 q-card-section
-                  .text-subtitle1 {{t(`admin.groups.permissions`)}}
+                  .text-subtitle1 {{ t(`admin.groups.permissions`) }}
                 q-card-section
                   q-btn.acrylic-btn(
                     icon='las la-question-circle'
@@ -372,8 +372,8 @@ q-layout(view='hHh lpR fFf', container)
                       size='sm'
                       )
                   q-item-section
-                    q-item-label {{perm.permission}}
-                    q-item-label(caption) {{perm.hint}}
+                    q-item-label {{ perm.permission }}
+                    q-item-label(caption) {{ perm.hint }}
                   q-item-section(avatar)
                     q-toggle(
                       v-model='state.group.permissions'
@@ -391,7 +391,7 @@ q-layout(view='hHh lpR fFf', container)
       q-toolbar(
         :class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
         )
-        .text-subtitle1 {{t('admin.groups.users')}}
+        .text-subtitle1 {{ t('admin.groups.users') }}
         q-space
         q-btn.acrylic-btn.q-mr-sm(
           icon='las la-question-circle'
@@ -428,7 +428,7 @@ q-layout(view='hHh lpR fFf', container)
           v-if='!state.users || state.users.length < 1'
           rounded
           :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
-          ) {{t('admin.groups.usersNone')}}
+          ) {{ t('admin.groups.usersNone') }}
         q-card.shadow-1
           q-table(
             :rows='state.users'
@@ -440,13 +440,13 @@ q-layout(view='hHh lpR fFf', container)
             :rows-per-page-options='[0]'
             :loading='state.isLoadingUsers'
             )
-            template(v-slot:body-cell-id='props')
+            template(#body-cell-id='props')
               q-td(:props='props')
                 q-icon(name='las la-user', color='primary', size='sm')
-            template(v-slot:body-cell-name='props')
+            template(#body-cell-name='props')
               q-td(:props='props')
                 .flex.items-center
-                  strong {{props.value}}
+                  strong {{ props.value }}
                   q-icon.q-ml-sm(
                     v-if='props.row.isSystem'
                     name='las la-lock'
@@ -457,10 +457,10 @@ q-layout(view='hHh lpR fFf', container)
                     name='las la-ban'
                     color='pink'
                     )
-            template(v-slot:body-cell-email='props')
+            template(#body-cell-email='props')
               q-td(:props='props')
                 em {{ props.value }}
-            template(v-slot:body-cell-date='props')
+            template(#body-cell-date='props')
               q-td(:props='props')
                 i18n-t.text-caption(keypath='admin.users.createdAt', tag='div')
                   template(#date)
@@ -472,7 +472,7 @@ q-layout(view='hHh lpR fFf', container)
                   )
                   template(#date)
                     strong {{ humanizeDate(props.row.lastLoginAt) }}
-            template(v-slot:body-cell-edit='props')
+            template(#body-cell-edit='props')
               q-td(:props='props')
                 q-btn.acrylic-btn.q-mr-sm(
                   v-if='!props.row.isSystem'
@@ -513,8 +513,8 @@ import { exportFile, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 6 - 6
ux/src/components/HeaderNav.vue

@@ -92,13 +92,13 @@ import { useQuasar } from 'quasar'
 import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useCommonStore } from 'src/stores/common'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useCommonStore } from '@/stores/common'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-import AccountMenu from 'src/components/AccountMenu.vue'
-import NewMenu from 'src/components/PageNewMenu.vue'
-import HeaderSearch from 'src/components/HeaderSearch.vue'
+import AccountMenu from '@/components/AccountMenu.vue'
+import NewMenu from '@/components/PageNewMenu.vue'
+import HeaderSearch from '@/components/HeaderSearch.vue'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/HeaderSearch.vue

@@ -89,7 +89,7 @@ import { computed, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { orderBy } from 'lodash-es'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 
@@ -172,7 +172,7 @@ function addTag (tag) {
 // MOUNTED
 
 onMounted(() => {
-  if (process.env.CLIENT) {
+  if (!import.meta.env.SSR) {
     window.addEventListener('keydown', handleKeyPress)
   }
   if (route.path.startsWith('/_search')) {
@@ -180,7 +180,7 @@ onMounted(() => {
   }
 })
 onBeforeUnmount(() => {
-  if (process.env.CLIENT) {
+  if (!import.meta.env.SSR) {
     window.removeEventListener('keydown', handleKeyPress)
   }
 })

+ 2 - 2
ux/src/components/LocaleSelectorMenu.vue

@@ -24,8 +24,8 @@ q-menu.translucent-menu(
 import { useI18n } from 'vue-i18n'
 import { useQuasar } from 'quasar'
 
-import { useCommonStore } from 'src/stores/common'
-import { useSiteStore } from 'src/stores/site'
+import { useCommonStore } from '@/stores/common'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 3 - 3
ux/src/components/MailTemplateEditorOverlay.vue

@@ -55,9 +55,9 @@ import { Repl, ReplStore, File } from '@vue/repl'
 import Monaco from '@vue/repl/monaco-editor'
 import '@vue/repl/style.css'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useEditorStore } from 'src/stores/editor'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useEditorStore } from '@/stores/editor'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/components/NavEditMenu.vue

@@ -85,8 +85,8 @@ import { useI18n } from 'vue-i18n'
 import { useQuasar } from 'quasar'
 import gql from 'graphql-tag'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 3 - 3
ux/src/components/NavEditOverlay.vue

@@ -390,12 +390,12 @@ import { v4 as uuid } from 'uuid'
 import gql from 'graphql-tag'
 import { cloneDeep, last, pick } from 'lodash-es'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 import { Sortable } from 'sortablejs-vue3'
 
-import IconPickerDialog from 'src/components/IconPickerDialog.vue'
+import IconPickerDialog from '@/components/IconPickerDialog.vue'
 
 // QUASAR
 

+ 2 - 2
ux/src/components/NavSidebar.vue

@@ -51,8 +51,8 @@ import { computed, onMounted, reactive, ref, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 5 - 5
ux/src/components/PageActionsCol.vue

@@ -153,11 +153,11 @@ import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { useEditorStore } from 'src/stores/editor'
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useEditorStore } from '@/stores/editor'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 2 - 2
ux/src/components/PageDataDialog.vue

@@ -96,8 +96,8 @@ import { nextTick, onMounted, reactive, ref, watch } from 'vue'
 
 import PageDataTemplateDialog from './PageDataTemplateDialog.vue'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/components/PageDataTemplateDialog.vue

@@ -166,8 +166,8 @@ import { useI18n } from 'vue-i18n'
 import { useQuasar } from 'quasar'
 import { nextTick, onMounted, reactive, ref, watch } from 'vue'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 7 - 7
ux/src/components/PageHeader.vue

@@ -196,14 +196,14 @@ import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { useEditorStore } from 'src/stores/editor'
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useEditorStore } from '@/stores/editor'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-import IconPickerDialog from 'src/components/IconPickerDialog.vue'
-import SocialSharingMenu from 'src/components/SocialSharingMenu.vue'
+import IconPickerDialog from '@/components/IconPickerDialog.vue'
+import SocialSharingMenu from '@/components/SocialSharingMenu.vue'
 
 // QUASAR
 

+ 4 - 4
ux/src/components/PageNewMenu.vue

@@ -73,10 +73,10 @@ q-menu.translucent-menu(
 import { useI18n } from 'vue-i18n'
 import { useQuasar } from 'quasar'
 
-import { useEditorStore } from 'src/stores/editor'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useFlagsStore } from 'src/stores/flags'
+import { useEditorStore } from '@/stores/editor'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useFlagsStore } from '@/stores/flags'
 
 // PROPS
 

+ 3 - 3
ux/src/components/PagePropertiesDialog.vue

@@ -321,9 +321,9 @@ import PageRelationDialog from './PageRelationDialog.vue'
 import PageScriptsDialog from './PageScriptsDialog.vue'
 import PageTags from './PageTags.vue'
 
-import { useEditorStore } from 'src/stores/editor'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useEditorStore } from '@/stores/editor'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/components/PageRelationDialog.vue

@@ -121,8 +121,8 @@ import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
 
 import IconPickerDialog from './IconPickerDialog.vue'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 2 - 2
ux/src/components/PageScriptsDialog.vue

@@ -46,8 +46,8 @@ import { useI18n } from 'vue-i18n'
 
 import UtilCodeEditor from './UtilCodeEditor.vue'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 2 - 2
ux/src/components/PageSourceOverlay.vue

@@ -45,8 +45,8 @@ import { onBeforeUnmount, onMounted, reactive, ref } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/PageTags.vue

@@ -48,9 +48,9 @@ import { reactive, watch } from 'vue'
 import { useI18n } from 'vue-i18n'
 import { DateTime } from 'luxon'
 
-import { useEditorStore } from 'src/stores/editor'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useEditorStore } from '@/stores/editor'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // PROPS
 

+ 1 - 1
ux/src/components/RerenderPageDialog.vue

@@ -10,7 +10,7 @@ import gql from 'graphql-tag'
 import { useI18n } from 'vue-i18n'
 import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { computed, onMounted, reactive } from 'vue'
-import { usePageStore } from 'src/stores/page'
+import { usePageStore } from '@/stores/page'
 
 // PROPS
 

+ 1 - 1
ux/src/components/SetupTfaDialog.vue

@@ -48,7 +48,7 @@ import { useI18n } from 'vue-i18n'
 import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { onMounted, reactive } from 'vue'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 import VOtpInput from 'vue3-otp-input'
 

+ 7 - 7
ux/src/components/SideDialog.vue

@@ -17,22 +17,22 @@ import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { useEditorStore } from 'src/stores/editor'
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useEditorStore } from '@/stores/editor'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // COMPONENTS
 
-import LoadingGeneric from 'src/components/LoadingGeneric.vue'
+import LoadingGeneric from '@/components/LoadingGeneric.vue'
 
 const sideDialogs = {
   PageDataDialog: defineAsyncComponent({
-    loader: () => import('src/components/PageDataDialog.vue'),
+    loader: () => import('@/components/PageDataDialog.vue'),
     loadingComponent: LoadingGeneric
   }),
   PagePropertiesDialog: defineAsyncComponent({
-    loader: () => import('src/components/PagePropertiesDialog.vue'),
+    loader: () => import('@/components/PagePropertiesDialog.vue'),
     loadingComponent: LoadingGeneric
   })
 }

+ 1 - 1
ux/src/components/TableEditorOverlay.vue

@@ -49,7 +49,7 @@ import { Tabulator } from 'tabulator-tables'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 import 'tabulator-tables/dist/css/tabulator.css'
 

+ 4 - 4
ux/src/components/TreeBrowserDialog.vue

@@ -128,11 +128,11 @@ import slugify from 'slugify'
 
 import fileTypes from '../helpers/fileTypes'
 
-import FolderCreateDialog from 'src/components/FolderCreateDialog.vue'
-import Tree from 'src/components/TreeNav.vue'
+import FolderCreateDialog from '@/components/FolderCreateDialog.vue'
+import Tree from '@/components/TreeNav.vue'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 import { dropRight } from 'lodash'
 
 // PROPS

+ 3 - 3
ux/src/components/UploadPendingAssetsDialog.vue

@@ -21,9 +21,9 @@ import { useI18n } from 'vue-i18n'
 import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { computed, onMounted, reactive } from 'vue'
 
-import { useEditorStore } from 'src/stores/editor'
-import { useSiteStore } from 'src/stores/site'
-import { usePageStore } from 'src/stores/page'
+import { useEditorStore } from '@/stores/editor'
+import { useSiteStore } from '@/stores/site'
+import { usePageStore } from '@/stores/page'
 
 // EMITS
 

+ 1 - 1
ux/src/components/UserCreateDialog.vue

@@ -185,7 +185,7 @@ import { useI18n } from 'vue-i18n'
 import { useDialogPluginComponent, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, ref } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
+import { useAdminStore } from '@/stores/admin'
 
 // EMITS
 

+ 2 - 2
ux/src/components/UserDefaultsMenu.vue

@@ -86,8 +86,8 @@ import { onMounted, reactive, ref } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/components/UserEditOverlay.vue

@@ -537,9 +537,9 @@ import { useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useFlagsStore } from 'src/stores/flags'
-import { useUserStore } from 'src/stores/user'
+import { useAdminStore } from '@/stores/admin'
+import { useFlagsStore } from '@/stores/flags'
+import { useUserStore } from '@/stores/user'
 
 import UserChangePwdDialog from './UserChangePwdDialog.vue'
 import UtilCodeEditor from './UtilCodeEditor.vue'

+ 3 - 3
ux/src/components/WelcomeOverlay.vue

@@ -60,9 +60,9 @@ import { useI18n } from 'vue-i18n'
 import { useRouter } from 'vue-router'
 import { useMeta, useQuasar } from 'quasar'
 
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 6 - 6
ux/src/layouts/AdminLayout.vue

@@ -254,16 +254,16 @@ import { computed, defineAsyncComponent, onMounted, reactive, ref, watch } from
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useCommonStore } from 'src/stores/common'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useAdminStore } from '@/stores/admin'
+import { useCommonStore } from '@/stores/common'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // COMPONENTS
 
 import AccountMenu from '../components/AccountMenu.vue'
-import FooterNav from 'src/components/FooterNav.vue'
+import FooterNav from '@/components/FooterNav.vue'
 const overlays = {
   EditorMarkdownConfig: defineAsyncComponent(() => import('../components/EditorMarkdownConfigOverlay.vue')),
   GroupEditOverlay: defineAsyncComponent(() => import('../components/GroupEditOverlay.vue')),

+ 12 - 12
ux/src/layouts/MainLayout.vue

@@ -126,21 +126,21 @@ import { computed, onMounted, reactive, ref, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 
-import { useCommonStore } from 'src/stores/common'
-import { useEditorStore } from 'src/stores/editor'
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useCommonStore } from '@/stores/common'
+import { useEditorStore } from '@/stores/editor'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // COMPONENTS
 
-import FooterNav from 'src/components/FooterNav.vue'
-import HeaderNav from 'src/components/HeaderNav.vue'
-import LocaleSelectorMenu from 'src/components/LocaleSelectorMenu.vue'
-import NavSidebar from 'src/components/NavSidebar.vue'
-import NavEditMenu from 'src/components/NavEditMenu.vue'
-import MainOverlayDialog from 'src/components/MainOverlayDialog.vue'
+import FooterNav from '@/components/FooterNav.vue'
+import HeaderNav from '@/components/HeaderNav.vue'
+import LocaleSelectorMenu from '@/components/LocaleSelectorMenu.vue'
+import NavSidebar from '@/components/NavSidebar.vue'
+import NavEditMenu from '@/components/NavEditMenu.vue'
+import MainOverlayDialog from '@/components/MainOverlayDialog.vue'
 
 // QUASAR
 

+ 6 - 6
ux/src/layouts/ProfileLayout.vue

@@ -49,13 +49,13 @@ import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-import HeaderNav from 'src/components/HeaderNav.vue'
-import FooterNav from 'src/components/FooterNav.vue'
-import MainOverlayDialog from 'src/components/MainOverlayDialog.vue'
+import HeaderNav from '@/components/HeaderNav.vue'
+import FooterNav from '@/components/FooterNav.vue'
+import MainOverlayDialog from '@/components/MainOverlayDialog.vue'
 
 // QUASAR
 

+ 80 - 0
ux/src/main.js

@@ -0,0 +1,80 @@
+import { createApp } from 'vue'
+import { Quasar, Dialog, Loading, LoadingBar, Meta, Notify } from 'quasar'
+import { initializeRouter } from './router'
+import { initializeStore } from './stores'
+import { initializeApollo } from './boot/apollo'
+import { initializeComponents } from './boot/components'
+import { initializeEventBus } from './boot/eventbus'
+import { initializeExternals } from './boot/externals'
+import { initializeI18n } from './boot/i18n'
+import quasarIconSet from 'quasar/icon-set/mdi-v7'
+
+// Import icon libraries
+import '@quasar/extras/roboto-font/roboto-font.css'
+import '@mdi/font/css/materialdesignicons.css'
+import '@quasar/extras/line-awesome/line-awesome.css'
+
+// Import Quasar css
+import 'quasar/src/css/index.sass'
+import './css/app.scss'
+
+import RootApp from './App.vue'
+
+const router = initializeRouter()
+const store = initializeStore(router)
+
+const app = createApp(RootApp)
+app.use(store)
+app.use(router)
+
+initializeApollo(store)
+initializeComponents(app)
+initializeEventBus()
+initializeExternals(router, store)
+initializeI18n(app, store)
+
+app.use(Quasar, {
+  plugins: {
+    Dialog,
+    Loading,
+    LoadingBar,
+    Meta,
+    Notify
+  },
+  iconSet: quasarIconSet,
+  config: {
+    brand: {
+      header: '#000',
+      sidebar: '#1976D2'
+    },
+    loading: {
+      delay: 500,
+      spinner: 'QSpinnerGrid',
+      spinnerSize: 32,
+      spinnerColor: 'white',
+      customClass: 'loading-darker'
+    },
+    loadingBar: {
+      color: 'primary',
+      size: '1px',
+      position: 'top'
+    },
+    notify: {
+      position: 'top',
+      progress: true,
+      color: 'green',
+      icon: 'las la-check',
+      actions: [
+        {
+          icon: 'las la-times',
+          color: 'white',
+          size: 'sm',
+          round: true,
+          handler: () => {}
+        }
+      ]
+    }
+  }
+})
+
+app.mount('#app')

+ 2 - 2
ux/src/pages/AdminApi.vue

@@ -107,8 +107,8 @@ import { DateTime } from 'luxon'
 import ApiKeyCreateDialog from '../components/ApiKeyCreateDialog.vue'
 import ApiKeyRevokeDialog from '../components/ApiKeyRevokeDialog.vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminAuth.vue

@@ -314,8 +314,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch, nextTick } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminBlocks.vue

@@ -89,9 +89,9 @@ import { defineAsyncComponent, onMounted, reactive, watch } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep, pick } from 'lodash-es'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 5 - 5
ux/src/pages/AdminDashboard.vue

@@ -152,14 +152,14 @@ import { useI18n } from 'vue-i18n'
 import { useRouter } from 'vue-router'
 
 import { useAdminStore } from '../stores/admin'
-import { useUserStore } from 'src/stores/user'
+import { useUserStore } from '@/stores/user'
 
 // COMPONENTS
 
-import CheckUpdateDialog from 'src/components/CheckUpdateDialog.vue'
-import SiteCreateDialog from 'src/components/SiteCreateDialog.vue'
-import UserCreateDialog from 'src/components/UserCreateDialog.vue'
-import GroupCreateDialog from 'src/components/GroupCreateDialog.vue'
+import CheckUpdateDialog from '@/components/CheckUpdateDialog.vue'
+import SiteCreateDialog from '@/components/SiteCreateDialog.vue'
+import UserCreateDialog from '@/components/UserCreateDialog.vue'
+import GroupCreateDialog from '@/components/GroupCreateDialog.vue'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminEditors.vue

@@ -80,9 +80,9 @@ import { defineAsyncComponent, onMounted, reactive, watch } from 'vue'
 import gql from 'graphql-tag'
 import { cloneDeep } from 'lodash-es'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminExtensions.vue

@@ -108,8 +108,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminFlags.vue

@@ -133,9 +133,9 @@ import { useMeta, useQuasar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 import ClipboardJS from 'clipboard'
 
-import { useSiteStore } from 'src/stores/site'
-import { useFlagsStore } from 'src/stores/flags'
-import { useUserStore } from 'src/stores/user'
+import { useSiteStore } from '@/stores/site'
+import { useFlagsStore } from '@/stores/flags'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminGeneral.vue

@@ -504,8 +504,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminGroups.vue

@@ -103,8 +103,8 @@ import { useMeta, useQuasar } from 'quasar'
 import { computed, onBeforeUnmount, onMounted, reactive, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 import GroupCreateDialog from '../components/GroupCreateDialog.vue'
 import GroupDeleteDialog from '../components/GroupDeleteDialog.vue'

+ 2 - 2
ux/src/pages/AdminIcons.vue

@@ -103,8 +103,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 1 - 1
ux/src/pages/AdminInstances.vue

@@ -83,7 +83,7 @@ import { useI18n } from 'vue-i18n'
 import gql from 'graphql-tag'
 import { DateTime, Duration, Interval } from 'luxon'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminLocale.vue

@@ -117,8 +117,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminLogin.vue

@@ -188,8 +188,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminMail.vue

@@ -322,9 +322,9 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminMetrics.vue

@@ -83,8 +83,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminNavigation.vue

@@ -326,8 +326,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch, nextTick } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 import draggable from 'vuedraggable'
 

+ 2 - 2
ux/src/pages/AdminRendering.vue

@@ -71,8 +71,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 1 - 1
ux/src/pages/AdminScheduler.vue

@@ -272,7 +272,7 @@ import { useI18n } from 'vue-i18n'
 import gql from 'graphql-tag'
 import { DateTime, Duration, Interval } from 'luxon'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminSearch.vue

@@ -87,10 +87,10 @@ import { cloneDeep, omit } from 'lodash-es'
 import { useMeta, useQuasar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 
-import { useSiteStore } from 'src/stores/site'
-import { useFlagsStore } from 'src/stores/flags'
+import { useSiteStore } from '@/stores/site'
+import { useFlagsStore } from '@/stores/flags'
 
-import UtilCodeEditor from 'src/components/UtilCodeEditor.vue'
+import UtilCodeEditor from '@/components/UtilCodeEditor.vue'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/AdminSecurity.vue

@@ -336,8 +336,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 1 - 1
ux/src/pages/AdminSites.vue

@@ -109,7 +109,7 @@ import { nextTick, onMounted } from 'vue'
 import { useRouter } from 'vue-router'
 
 import { useAdminStore } from '../stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // COMPONENTS
 

+ 2 - 2
ux/src/pages/AdminStorage.vue

@@ -600,8 +600,8 @@ import { useMeta, useQuasar } from 'quasar'
 import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
 
 import GithubSetupInstallDialog from '../components/GithubSetupInstallDialog.vue'
 

+ 1 - 1
ux/src/pages/AdminSystem.vue

@@ -209,7 +209,7 @@ import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive, ref, watch } from 'vue'
 import ClipboardJS from 'clipboard'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 import CheckUpdateDialog from '../components/CheckUpdateDialog.vue'
 

+ 1 - 1
ux/src/pages/AdminTerminal.vue

@@ -58,7 +58,7 @@ import { io } from 'socket.io-client'
 import { Terminal } from 'xterm'
 import 'xterm/css/xterm.css'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminTheme.vue

@@ -309,9 +309,9 @@ import { useI18n } from 'vue-i18n'
 import { setCssVar, useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive, watch } from 'vue'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
+import { useAdminStore } from '@/stores/admin'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
 
 import UtilCodeEditor from '../components/UtilCodeEditor.vue'
 

+ 4 - 4
ux/src/pages/AdminUsers.vue

@@ -133,12 +133,12 @@ import { useMeta, useQuasar } from 'quasar'
 import { onBeforeUnmount, onMounted, reactive, watch } from 'vue'
 import { useRouter, useRoute } from 'vue-router'
 
-import { useAdminStore } from 'src/stores/admin'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useAdminStore } from '@/stores/admin'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 import UserCreateDialog from '../components/UserCreateDialog.vue'
-import UserDefaultsMenu from 'src/components/UserDefaultsMenu.vue'
+import UserDefaultsMenu from '@/components/UserDefaultsMenu.vue'
 
 // QUASAR
 

+ 1 - 1
ux/src/pages/AdminUtilities.vue

@@ -126,7 +126,7 @@ import { useMeta, useQuasar } from 'quasar'
 import { useI18n } from 'vue-i18n'
 import gql from 'graphql-tag'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 3 - 3
ux/src/pages/AdminWebhooks.vue

@@ -103,10 +103,10 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive } from 'vue'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
-import WebhookEditDialog from 'src/components/WebhookEditDialog.vue'
-import WebhookDeleteDialog from 'src/components/WebhookDeleteDialog.vue'
+import WebhookEditDialog from '@/components/WebhookEditDialog.vue'
+import WebhookDeleteDialog from '@/components/WebhookDeleteDialog.vue'
 
 // QUASAR
 

+ 11 - 11
ux/src/pages/Index.vue

@@ -163,20 +163,20 @@ import { useRouter, useRoute } from 'vue-router'
 import { useI18n } from 'vue-i18n'
 import { DateTime } from 'luxon'
 
-import { useCommonStore } from 'src/stores/common'
-import { useEditorStore } from 'src/stores/editor'
-import { useFlagsStore } from 'src/stores/flags'
-import { usePageStore } from 'src/stores/page'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useCommonStore } from '@/stores/common'
+import { useEditorStore } from '@/stores/editor'
+import { useFlagsStore } from '@/stores/flags'
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // COMPONENTS
 
-import LoadingGeneric from 'src/components/LoadingGeneric.vue'
-import PageActionsCol from 'src/components/PageActionsCol.vue'
-import PageHeader from 'src/components/PageHeader.vue'
-import PageTags from 'src/components/PageTags.vue'
-import SideDialog from 'src/components/SideDialog.vue'
+import LoadingGeneric from '@/components/LoadingGeneric.vue'
+import PageActionsCol from '@/components/PageActionsCol.vue'
+import PageHeader from '@/components/PageHeader.vue'
+import PageTags from '@/components/PageTags.vue'
+import SideDialog from '@/components/SideDialog.vue'
 
 const editorComponents = {
   markdown: defineAsyncComponent({

+ 2 - 2
ux/src/pages/Login.vue

@@ -19,9 +19,9 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive, watch } from 'vue'
 
-import AuthLoginPanel from 'src/components/AuthLoginPanel.vue'
+import AuthLoginPanel from '@/components/AuthLoginPanel.vue'
 
-import { useSiteStore } from 'src/stores/site'
+import { useSiteStore } from '@/stores/site'
 
 // QUASAR
 

+ 6 - 6
ux/src/pages/ProfileAuth.vue

@@ -96,15 +96,15 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive } from 'vue'
 import { browserSupportsWebAuthn, startRegistration } from '@simplewebauthn/browser'
-import { localizeError } from 'src/helpers/localization'
+import { localizeError } from '@/helpers/localization'
 import { DateTime } from 'luxon'
 
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-import ChangePwdDialog from 'src/components/ChangePwdDialog.vue'
-import SetupTfaDialog from 'src/components/SetupTfaDialog.vue'
-import PasskeyCreateDialog from 'src/components/PasskeyCreateDialog.vue'
+import ChangePwdDialog from '@/components/ChangePwdDialog.vue'
+import SetupTfaDialog from '@/components/SetupTfaDialog.vue'
+import PasskeyCreateDialog from '@/components/PasskeyCreateDialog.vue'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/ProfileAvatar.vue

@@ -50,8 +50,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, reactive } from 'vue'
 
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 1 - 1
ux/src/pages/ProfileGroups.vue

@@ -35,7 +35,7 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { onMounted, reactive } from 'vue'
 
-import { useUserStore } from 'src/stores/user'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 2 - 2
ux/src/pages/ProfileInfo.vue

@@ -179,8 +179,8 @@ import { useI18n } from 'vue-i18n'
 import { useMeta, useQuasar } from 'quasar'
 import { computed, onMounted, reactive } from 'vue'
 
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
 // QUASAR
 

+ 6 - 6
ux/src/pages/Search.vue

@@ -186,13 +186,13 @@ import gql from 'graphql-tag'
 import { cloneDeep, debounce, difference } from 'lodash-es'
 import { DateTime } from 'luxon'
 
-import { useFlagsStore } from 'src/stores/flags'
-import { useSiteStore } from 'src/stores/site'
-import { useUserStore } from 'src/stores/user'
+import { useFlagsStore } from '@/stores/flags'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
 
-import HeaderNav from 'src/components/HeaderNav.vue'
-import FooterNav from 'src/components/FooterNav.vue'
-import MainOverlayDialog from 'src/components/MainOverlayDialog.vue'
+import HeaderNav from '@/components/HeaderNav.vue'
+import FooterNav from '@/components/FooterNav.vue'
+import MainOverlayDialog from '@/components/MainOverlayDialog.vue'
 
 const tagsInQueryRgx = /#[a-z0-9-\u3400-\u4DBF\u4E00-\u9FFF]+(?=(?:[^"]*(?:")[^"]*(?:"))*[^"]*$)/g
 

+ 8 - 22
ux/src/router/index.js

@@ -1,30 +1,16 @@
-import { route } from 'quasar/wrappers'
-import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
+import { createRouter, createMemoryHistory, createWebHistory } from 'vue-router'
 import routes from './routes'
 
-/*
- * If not building with SSR mode, you can
- * directly export the Router instantiation;
- *
- * The function below can be async too; either use
- * async/await or return a Promise which resolves
- * with the Router instance.
- */
-
-export default route(function (/* { store, ssrContext } */) {
-  const createHistory = process.env.SERVER
+export function initializeRouter () {
+  const createHistory = import.meta.env.SSR
     ? createMemoryHistory
-    : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
+    : createWebHistory
 
-  const Router = createRouter({
+  const router = createRouter({
     scrollBehavior: () => ({ left: 0, top: 0 }),
     routes,
-
-    // Leave this as is and make changes in quasar.conf.js instead!
-    // quasar.conf.js -> build -> vueRouterMode
-    // quasar.conf.js -> build -> publicPath
-    history: createHistory(process.env.VUE_ROUTER_BASE)
+    history: createHistory(import.meta.env.BASE_URL)
   })
 
-  return Router
-})
+  return router
+}

+ 40 - 40
ux/src/router/routes.js

@@ -1,16 +1,16 @@
-import { usePageStore } from 'src/stores/page'
+import { usePageStore } from '@/stores/page'
 
 const routes = [
   {
     path: '/login',
-    component: () => import('layouts/AuthLayout.vue'),
+    component: () => import('@/layouts/AuthLayout.vue'),
     children: [
-      { path: '', component: () => import('pages/Login.vue') }
+      { path: '', component: () => import('@/pages/Login.vue') }
     ]
   },
   {
     path: '/a/:alias',
-    component: () => import('../layouts/MainLayout.vue'),
+    component: () => import('@/layouts/MainLayout.vue'),
     beforeEnter: async (to, from) => {
       const pageStore = usePageStore()
       try {
@@ -23,60 +23,60 @@ const routes = [
   },
   {
     path: '/_profile',
-    component: () => import('layouts/ProfileLayout.vue'),
+    component: () => import('@/layouts/ProfileLayout.vue'),
     children: [
       { path: '', redirect: '/_profile/info' },
-      { path: 'info', component: () => import('src/pages/ProfileInfo.vue') },
-      { path: 'avatar', component: () => import('src/pages/ProfileAvatar.vue') },
-      { path: 'auth', component: () => import('src/pages/ProfileAuth.vue') },
-      { path: 'groups', component: () => import('src/pages/ProfileGroups.vue') }
+      { path: 'info', component: () => import('@/pages/ProfileInfo.vue') },
+      { path: 'avatar', component: () => import('@/pages/ProfileAvatar.vue') },
+      { path: 'auth', component: () => import('@/pages/ProfileAuth.vue') },
+      { path: 'groups', component: () => import('@/pages/ProfileGroups.vue') }
     ]
   },
   {
     path: '/_search',
-    component: () => import('src/pages/Search.vue')
+    component: () => import('@/pages/Search.vue')
   },
   {
     path: '/_admin',
-    component: () => import('layouts/AdminLayout.vue'),
+    component: () => import('@/layouts/AdminLayout.vue'),
     children: [
       { path: '', redirect: '/_admin/dashboard' },
-      { path: 'dashboard', component: () => import('pages/AdminDashboard.vue') },
-      { path: 'sites', component: () => import('pages/AdminSites.vue') },
+      { path: 'dashboard', component: () => import('@/pages/AdminDashboard.vue') },
+      { path: 'sites', component: () => import('@/pages/AdminSites.vue') },
       // -> Site
-      { path: ':siteid/general', component: () => import('pages/AdminGeneral.vue') },
-      { path: ':siteid/blocks', component: () => import('pages/AdminBlocks.vue') },
-      { path: ':siteid/editors', component: () => import('pages/AdminEditors.vue') },
-      { path: ':siteid/locale', component: () => import('pages/AdminLocale.vue') },
-      { path: ':siteid/login', component: () => import('pages/AdminLogin.vue') },
-      { path: ':siteid/navigation', component: () => import('pages/AdminNavigation.vue') },
-      { path: ':siteid/storage/:id?', component: () => import('pages/AdminStorage.vue') },
-      { path: ':siteid/theme', component: () => import('pages/AdminTheme.vue') },
+      { path: ':siteid/general', component: () => import('@/pages/AdminGeneral.vue') },
+      { path: ':siteid/blocks', component: () => import('@/pages/AdminBlocks.vue') },
+      { path: ':siteid/editors', component: () => import('@/pages/AdminEditors.vue') },
+      { path: ':siteid/locale', component: () => import('@/pages/AdminLocale.vue') },
+      { path: ':siteid/login', component: () => import('@/pages/AdminLogin.vue') },
+      { path: ':siteid/navigation', component: () => import('@/pages/AdminNavigation.vue') },
+      { path: ':siteid/storage/:id?', component: () => import('@/pages/AdminStorage.vue') },
+      { path: ':siteid/theme', component: () => import('@/pages/AdminTheme.vue') },
       // -> Users
-      { path: 'auth', component: () => import('pages/AdminAuth.vue') },
-      { path: 'groups/:id?/:section?', component: () => import('pages/AdminGroups.vue') },
-      { path: 'users/:id?/:section?', component: () => import('pages/AdminUsers.vue') },
+      { path: 'auth', component: () => import('@/pages/AdminAuth.vue') },
+      { path: 'groups/:id?/:section?', component: () => import('@/pages/AdminGroups.vue') },
+      { path: 'users/:id?/:section?', component: () => import('@/pages/AdminUsers.vue') },
       // -> System
-      { path: 'api', component: () => import('pages/AdminApi.vue') },
-      { path: 'extensions', component: () => import('pages/AdminExtensions.vue') },
-      { path: 'icons', component: () => import('pages/AdminIcons.vue') },
-      { path: 'instances', component: () => import('pages/AdminInstances.vue') },
-      { path: 'mail', component: () => import('pages/AdminMail.vue') },
-      { path: 'metrics', component: () => import('pages/AdminMetrics.vue') },
-      { path: 'rendering', component: () => import('pages/AdminRendering.vue') },
-      { path: 'scheduler', component: () => import('pages/AdminScheduler.vue') },
-      { path: 'search', component: () => import('pages/AdminSearch.vue') },
-      { path: 'security', component: () => import('pages/AdminSecurity.vue') },
-      { path: 'system', component: () => import('pages/AdminSystem.vue') },
-      { path: 'terminal', component: () => import('pages/AdminTerminal.vue') },
-      { path: 'utilities', component: () => import('pages/AdminUtilities.vue') },
-      { path: 'webhooks', component: () => import('pages/AdminWebhooks.vue') },
-      { path: 'flags', component: () => import('pages/AdminFlags.vue') }
+      { path: 'api', component: () => import('@/pages/AdminApi.vue') },
+      { path: 'extensions', component: () => import('@/pages/AdminExtensions.vue') },
+      { path: 'icons', component: () => import('@/pages/AdminIcons.vue') },
+      { path: 'instances', component: () => import('@/pages/AdminInstances.vue') },
+      { path: 'mail', component: () => import('@/pages/AdminMail.vue') },
+      { path: 'metrics', component: () => import('@/pages/AdminMetrics.vue') },
+      { path: 'rendering', component: () => import('@/pages/AdminRendering.vue') },
+      { path: 'scheduler', component: () => import('@/pages/AdminScheduler.vue') },
+      { path: 'search', component: () => import('@/pages/AdminSearch.vue') },
+      { path: 'security', component: () => import('@/pages/AdminSecurity.vue') },
+      { path: 'system', component: () => import('@/pages/AdminSystem.vue') },
+      { path: 'terminal', component: () => import('@/pages/AdminTerminal.vue') },
+      { path: 'utilities', component: () => import('@/pages/AdminUtilities.vue') },
+      { path: 'webhooks', component: () => import('@/pages/AdminWebhooks.vue') },
+      { path: 'flags', component: () => import('@/pages/AdminFlags.vue') }
     ]
   },
   {
     path: '/_error/:action?',
-    component: () => import('pages/ErrorGeneric.vue')
+    component: () => import('@/pages/ErrorGeneric.vue')
   },
   // {
   //   path: '/_unknown-site',

+ 7 - 12
ux/src/stores/index.js

@@ -1,20 +1,15 @@
-import { store } from 'quasar/wrappers'
 import { createPinia } from 'pinia'
+import { markRaw } from 'vue'
 
-/*
- * If not building with SSR mode, you can
- * directly export the Store instantiation;
- *
- * The function below can be async too; either use
- * async/await or return a Promise which resolves
- * with the Store instance.
- */
-
-export default store((/* { ssrContext } */) => {
+export function initializeStore (router) {
   const pinia = createPinia()
 
   // You can add Pinia plugins here
   // pinia.use(SomePiniaPlugin)
 
+  pinia.use(({ store }) => {
+    store.router = markRaw(router)
+  })
+
   return pinia
-})
+}

+ 1 - 1
ux/src/stores/user.js

@@ -3,7 +3,7 @@ import { jwtDecode } from 'jwt-decode'
 import Cookies from 'js-cookie'
 import gql from 'graphql-tag'
 import { DateTime } from 'luxon'
-import { getAccessibleColor } from 'src/helpers/accessibility'
+import { getAccessibleColor } from '@/helpers/accessibility'
 
 import { useSiteStore } from './site'
 

+ 83 - 0
ux/vite.config.js

@@ -0,0 +1,83 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import yaml from 'js-yaml'
+import fs from 'node:fs'
+import { fileURLToPath } from 'node:url'
+import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
+
+// https://vitejs.dev/config/
+export default defineConfig(({ mode }) => {
+  const userConfig = mode === 'development' ? {
+    dev: { port: 3001, hmrClientPort: 3001 },
+    ...yaml.load(fs.readFileSync(fileURLToPath(new URL('../config.yml', import.meta.url)), 'utf8'))
+  } : {}
+
+  return {
+    build: {
+      assetsDir: '_assets',
+      chunkSizeWarningLimit: 5000,
+      dynamicImportVarsOptions: {
+        warnOnError: true,
+        include: ['!/_blocks/**']
+      },
+      outDir: '../assets',
+      target: 'es2022',
+      ...(mode === 'production') && {
+        rollupOptions: {
+          output: {
+            manualChunks (id) {
+              if (id.includes('lodash')) {
+                return 'lodash'
+              // } else if (id.includes('quasar')) {
+              //   return 'quasar'
+              } else if (id.includes('pages/Admin')) {
+                return 'admin'
+              } else if (id.includes('pages/Profile')) {
+                return 'profile'
+              }
+            }
+          }
+        }
+      }
+    },
+    optimizeDeps: {
+      include: [
+        'prosemirror-state',
+        'prosemirror-transform',
+        'prosemirror-model',
+        'prosemirror-view'
+      ]
+    },
+    plugins: [
+      vue({
+        template: { transformAssetUrls }
+      }),
+      quasar({
+        autoImportComponentCase: 'kebab',
+        sassVariables: 'src/css/quasar.variables.scss'
+      })
+    ],
+    resolve: {
+      alias: {
+        '@': fileURLToPath(new URL('./src', import.meta.url))
+      }
+    },
+    server: {
+      // https: true
+      open: false, // opens browser window automatically
+      port: userConfig.dev?.port,
+      proxy: ['_graphql', '_blocks', '_site', '_thumb', '_user'].reduce((result, key) => {
+        result[`/${key}`] = {
+          target: {
+            host: '127.0.0.1',
+            port: userConfig.port
+          }
+        }
+        return result
+      }, {}),
+      hmr: {
+        clientPort: userConfig.dev?.hmrClientPort
+      }
+    }
+  }
+})

Некоторые файлы не были показаны из-за большого количества измененных файлов