diff --git a/config.sample.yml b/config.sample.yml
index 47edd8d2..b98f38d1 100644
--- a/config.sample.yml
+++ b/config.sample.yml
@@ -2,7 +2,7 @@
# Wiki.js - CONFIGURATION #
#######################################################################
# Full documentation + examples:
-# https://docs.requarks.io/install
+# https://docs.js.wiki/install
# ---------------------------------------------------------------------
# Port the server should listen to
@@ -13,25 +13,20 @@ port: 3000
# ---------------------------------------------------------------------
# Database
# ---------------------------------------------------------------------
-# Supported Database Engines:
-# - postgres = PostgreSQL 9.5 or later
-# - mysql = MySQL 8.0 or later (5.7.8 partially supported, refer to docs)
-# - mariadb = MariaDB 10.2.7 or later
-# - mssql = MS SQL Server 2012 or later
-# - sqlite = SQLite 3.9 or later
+# PostgreSQL 9.6 or later required
db:
- type: postgres
-
- # PostgreSQL / MySQL / MariaDB / MS SQL Server only:
host: localhost
port: 5432
user: wikijs
pass: wikijsrocks
db: wiki
+ schemas:
+ wiki: wiki
+ scheduler: scheduler
ssl: false
- # Optional - PostgreSQL / MySQL / MariaDB only:
+ # Optional
# -> Uncomment lines you need below and set `auto` to false
# -> Full list of accepted options: https://nodejs.org/api/tls.html#tls_tls_createsecurecontext_options
sslOptions:
@@ -43,12 +38,6 @@ db:
# pfx: path/to/cert.pfx
# passphrase: xyz123
- # Optional - PostgreSQL only:
- schema: public
-
- # SQLite only:
- storage: path/to/database.sqlite
-
#######################################################################
# ADVANCED OPTIONS #
#######################################################################
@@ -102,15 +91,12 @@ pool:
bindIP: 0.0.0.0
# ---------------------------------------------------------------------
-# Log Level
+# Logging
# ---------------------------------------------------------------------
# Possible values: error, warn, info (default), verbose, debug, silly
logLevel: info
-# ---------------------------------------------------------------------
-# Log Format
-# ---------------------------------------------------------------------
# Output format for logging, possible values: default, json
logFormat: default
@@ -123,15 +109,6 @@ logFormat: default
offline: false
-# ---------------------------------------------------------------------
-# High-Availability
-# ---------------------------------------------------------------------
-# Set to true if you have multiple concurrent instances running off the
-# same DB (e.g. Kubernetes pods / load balanced instances). Leave false
-# otherwise. You MUST be using PostgreSQL to use this feature.
-
-ha: false
-
# ---------------------------------------------------------------------
# Data Path
# ---------------------------------------------------------------------
diff --git a/package.json b/package.json
index abf8fefb..9e388f3b 100644
--- a/package.json
+++ b/package.json
@@ -1,15 +1,13 @@
{
"name": "wiki",
- "version": "2.0.0",
- "releaseDate": "2019-01-01T01:01:01.000Z",
- "description": "A modern, lightweight and powerful wiki app built on NodeJS, Git and Markdown",
+ "version": "3.0.0",
+ "releaseDate": "2022-01-01T01:01:01.000Z",
+ "description": "The most powerful and extensible open source Wiki software",
"main": "wiki.js",
"dev": true,
"scripts": {
"start": "node server",
"dev": "node dev",
- "build": "webpack --profile --config dev/webpack/webpack.prod.js",
- "watch": "webpack --config dev/webpack/webpack.dev.js",
"test": "eslint --format codeframe --ext .js,.vue . && pug-lint server/views && jest",
"cypress:open": "cypress open"
},
@@ -33,7 +31,7 @@
},
"homepage": "https://github.com/Requarks/wiki#readme",
"engines": {
- "node": ">=10.12"
+ "node": ">=16.0"
},
"dependencies": {
"@azure/storage-blob": "12.2.1",
diff --git a/server/app/data.yml b/server/app/data.yml
index ca48a5ea..c0dc58f5 100644
--- a/server/app/data.yml
+++ b/server/app/data.yml
@@ -8,16 +8,17 @@ defaults:
# File defaults
port: 80
db:
- type: postgres
host: localhost
port: 5432
user: wikijs
pass: wikijsrocks
db: wiki
ssl: false
- storage: ./db.sqlite
sslOptions:
auto: true
+ schemas:
+ wiki: wiki
+ scheduler: scheduler
ssl:
enabled: false
pool:
@@ -26,7 +27,6 @@ defaults:
logLevel: info
logFormat: default
offline: false
- ha: false
bodyParserLimit: 5mb
# DB defaults
api:
@@ -88,7 +88,7 @@ defaults:
ldapdebug: false
sqllog: false
# System defaults
- channel: STABLE
+ channel: NEXT
setup: false
dataPath: ./data
cors:
diff --git a/server/core/db.js b/server/core/db.js
index 2d614c55..dbd51f7a 100644
--- a/server/core/db.js
+++ b/server/core/db.js
@@ -7,7 +7,7 @@ const fs = require('fs')
const Objection = require('objection')
const migrationSource = require('../db/migrator-source')
-const migrateFromBeta = require('../db/beta')
+const migrateFromLegacy = require('../db/legacy')
/* global WIKI */
@@ -20,15 +20,12 @@ module.exports = {
listener: null,
/**
* Initialize DB
- *
- * @return {Object} DB instance
*/
init() {
let self = this
// Fetch DB Config
- let dbClient = null
let dbConfig = (!_.isEmpty(process.env.DATABASE_URL)) ? process.env.DATABASE_URL : {
host: WIKI.config.db.host.toString(),
user: WIKI.config.db.user.toString(),
@@ -74,84 +71,22 @@ module.exports = {
}
}
- // Engine-specific config
- switch (WIKI.config.db.type) {
- case 'postgres':
- dbClient = 'pg'
-
- if (dbUseSSL && _.isPlainObject(dbConfig)) {
- dbConfig.ssl = (sslOptions === true) ? { rejectUnauthorized: true } : sslOptions
- }
- break
- case 'mariadb':
- case 'mysql':
- dbClient = 'mysql2'
-
- if (dbUseSSL && _.isPlainObject(dbConfig)) {
- dbConfig.ssl = sslOptions
- }
-
- // Fix mysql boolean handling...
- dbConfig.typeCast = (field, next) => {
- if (field.type === 'TINY' && field.length === 1) {
- let value = field.string()
- return value ? (value === '1') : null
- }
- return next()
- }
- break
- case 'mssql':
- dbClient = 'mssql'
-
- if (_.isPlainObject(dbConfig)) {
- dbConfig.appName = 'Wiki.js'
- _.set(dbConfig, 'options.appName', 'Wiki.js')
-
- dbConfig.enableArithAbort = true
- _.set(dbConfig, 'options.enableArithAbort', true)
-
- if (dbUseSSL) {
- dbConfig.encrypt = true
- _.set(dbConfig, 'options.encrypt', true)
- }
- }
- break
- case 'sqlite':
- dbClient = 'sqlite3'
- dbConfig = { filename: WIKI.config.db.storage }
- break
- default:
- WIKI.logger.error('Invalid DB Type')
- process.exit(1)
+ if (dbUseSSL && _.isPlainObject(dbConfig)) {
+ dbConfig.ssl = (sslOptions === true) ? { rejectUnauthorized: true } : sslOptions
}
// Initialize Knex
this.knex = Knex({
- client: dbClient,
+ client: 'pg',
useNullAsDefault: true,
asyncStackTraces: WIKI.IS_DEBUG,
connection: dbConfig,
+ searchPath: [WIKI.config.db.schemas.wiki],
pool: {
...WIKI.config.pool,
async afterCreate(conn, done) {
// -> Set Connection App Name
- switch (WIKI.config.db.type) {
- case 'postgres':
- await conn.query(`set application_name = 'Wiki.js'`)
- // -> Set schema if it's not public
- if (WIKI.config.db.schema && WIKI.config.db.schema !== 'public') {
- await conn.query(`set search_path TO ${WIKI.config.db.schema}, public;`)
- }
- done()
- break
- case 'mysql':
- await conn.promise().query(`set autocommit = 1`)
- done()
- break
- default:
- done()
- break
- }
+ await conn.query(`set application_name = 'Wiki.js'`)
}
},
debug: WIKI.IS_DEBUG
@@ -191,18 +126,19 @@ module.exports = {
async syncSchemas () {
return self.knex.migrate.latest({
tableName: 'migrations',
- migrationSource
+ migrationSource,
+ schemaName: WIKI.config.db.schemas.wiki
})
},
- // -> Migrate DB Schemas from beta
- async migrateFromBeta () {
- return migrateFromBeta.migrate(self.knex)
+ // -> Migrate DB Schemas from 2.x
+ async migrateFromLegacy () {
+ return migrateFromLegacy.migrate(self.knex)
}
}
let initTasksQueue = (WIKI.IS_MASTER) ? [
initTasks.connect,
- initTasks.migrateFromBeta,
+ initTasks.migrateFromLegacy,
initTasks.syncSchemas
] : [
() => { return Promise.resolve() }
@@ -210,7 +146,6 @@ module.exports = {
// Perform init tasks
- WIKI.logger.info(`Using database driver ${dbClient} for ${WIKI.config.db.type} [ OK ]`)
this.onReady = Promise.each(initTasksQueue, t => t()).return(true)
return {
@@ -222,14 +157,6 @@ module.exports = {
* Subscribe to database LISTEN / NOTIFY for multi-instances events
*/
async subscribeToNotifications () {
- const useHA = (WIKI.config.ha === true || WIKI.config.ha === 'true' || WIKI.config.ha === 1 || WIKI.config.ha === '1')
- if (!useHA) {
- return
- } else if (WIKI.config.db.type !== 'postgres') {
- WIKI.logger.warn(`Database engine doesn't support pub/sub. Will not handle concurrent instances: [ DISABLED ]`)
- return
- }
-
const PGPubSub = require('pg-pubsub')
this.listener = new PGPubSub(this.knex.client.connectionSettings, {
@@ -254,7 +181,7 @@ module.exports = {
WIKI.configSvc.subscribeToEvents()
WIKI.models.pages.subscribeToEvents()
- WIKI.logger.info(`High-Availability Listener initialized successfully: [ OK ]`)
+ WIKI.logger.info(`PG PubSub Listener initialized successfully: [ OK ]`)
},
/**
* Unsubscribe from database LISTEN / NOTIFY
diff --git a/server/db/beta/index.js b/server/db/beta/index.js
deleted file mode 100644
index aa3157c3..00000000
--- a/server/db/beta/index.js
+++ /dev/null
@@ -1,115 +0,0 @@
-const _ = require('lodash')
-const path = require('path')
-const fs = require('fs-extra')
-const semver = require('semver')
-
-/* global WIKI */
-
-module.exports = {
- async migrate (knex) {
- const migrationsTableExists = await knex.schema.hasTable('migrations')
- if (!migrationsTableExists) {
- return
- }
-
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
-
- const migrations = await knex('migrations')
- if (_.some(migrations, m => m.name.indexOf('2.0.0-beta') >= 0)) {
- // -> Pre-beta.241 locale field length fix
- const localeColnInfo = await knex('pages').columnInfo('localeCode')
- if (WIKI.config.db.type !== 'sqlite' && localeColnInfo.maxLength === 2) {
- // -> Load locales
- const locales = await knex('locales')
- await knex.schema
- // -> Remove constraints
- .table('users', table => {
- table.dropForeign('localeCode')
- })
- .table('pages', table => {
- table.dropForeign('localeCode')
- })
- .table('pageHistory', table => {
- table.dropForeign('localeCode')
- })
- .table('pageTree', table => {
- table.dropForeign('localeCode')
- })
- // -> Recreate locales table
- .dropTable('locales')
- .createTable('locales', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('code', 5).notNullable().primary()
- table.json('strings')
- table.boolean('isRTL').notNullable().defaultTo(false)
- table.string('name').notNullable()
- table.string('nativeName').notNullable()
- table.integer('availability').notNullable().defaultTo(0)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- await knex('locales').insert(locales)
- // -> Alter columns length
- await knex.schema
- .table('users', table => {
- table.string('localeCode', 5).notNullable().defaultTo('en').alter()
- })
- .table('pages', table => {
- table.string('localeCode', 5).alter()
- })
- .table('pageHistory', table => {
- table.string('localeCode', 5).alter()
- })
- .table('pageTree', table => {
- table.string('localeCode', 5).alter()
- })
- // -> Restore restraints
- .table('users', table => {
- table.foreign('localeCode').references('code').inTable('locales')
- })
- .table('pages', table => {
- table.foreign('localeCode').references('code').inTable('locales')
- })
- .table('pageHistory', table => {
- table.foreign('localeCode').references('code').inTable('locales')
- })
- .table('pageTree', table => {
- table.foreign('localeCode').references('code').inTable('locales')
- })
- }
-
- // -> Advance to latest beta/rc migration state
- const baseMigrationPath = path.join(WIKI.SERVERPATH, (WIKI.config.db.type !== 'sqlite') ? 'db/beta/migrations' : 'db/beta/migrations-sqlite')
- await knex.migrate.latest({
- tableName: 'migrations',
- migrationSource: {
- async getMigrations() {
- const migrationFiles = await fs.readdir(baseMigrationPath)
- return migrationFiles.sort(semver.compare).map(m => ({
- file: m,
- directory: baseMigrationPath
- }))
- },
- getMigrationName(migration) {
- return migration.file
- },
- getMigration(migration) {
- return require(path.join(baseMigrationPath, migration.file))
- }
- }
- })
-
- // -> Cleanup migration table
- await knex('migrations').truncate()
-
- // -> Advance to stable 2.0 migration state
- await knex('migrations').insert({
- name: '2.0.0.js',
- batch: 1,
- migration_time: knex.fn.now()
- })
- }
- }
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.1.js b/server/db/beta/migrations-sqlite/2.0.0-beta.1.js
deleted file mode 100644
index 2b6d7467..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.1.js
+++ /dev/null
@@ -1,259 +0,0 @@
-exports.up = knex => {
- return knex.schema
- // =====================================
- // MODEL TABLES
- // =====================================
- // ASSETS ------------------------------
- .createTable('assets', table => {
- table.increments('id').primary()
- table.string('filename').notNullable()
- table.string('basename').notNullable()
- table.string('ext').notNullable()
- table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
- table.string('mime').notNullable().defaultTo('application/octet-stream')
- table.integer('fileSize').unsigned().comment('In kilobytes')
- table.json('metadata')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.integer('folderId').unsigned().references('id').inTable('assetFolders')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // ASSET FOLDERS -----------------------
- .createTable('assetFolders', table => {
- table.increments('id').primary()
- table.string('name').notNullable()
- table.string('slug').notNullable()
- table.integer('parentId').unsigned().references('id').inTable('assetFolders')
- })
- // AUTHENTICATION ----------------------
- .createTable('authentication', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- table.boolean('selfRegistration').notNullable().defaultTo(false)
- table.json('domainWhitelist').notNullable()
- table.json('autoEnrollGroups').notNullable()
- })
- // COMMENTS ----------------------------
- .createTable('comments', table => {
- table.increments('id').primary()
- table.text('content').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // EDITORS -----------------------------
- .createTable('editors', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // GROUPS ------------------------------
- .createTable('groups', table => {
- table.increments('id').primary()
- table.string('name').notNullable()
- table.json('permissions').notNullable()
- table.json('pageRules').notNullable()
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOCALES -----------------------------
- .createTable('locales', table => {
- table.string('code', 5).notNullable().primary()
- table.json('strings')
- table.boolean('isRTL').notNullable().defaultTo(false)
- table.string('name').notNullable()
- table.string('nativeName').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOGGING ----------------------------
- .createTable('loggers', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('level').notNullable().defaultTo('warn')
- table.json('config')
- })
- // NAVIGATION ----------------------------
- .createTable('navigation', table => {
- table.string('key').notNullable().primary()
- table.json('config')
- })
- // PAGE HISTORY ------------------------
- .createTable('pageHistory', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
-
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // PAGES -------------------------------
- .createTable('pages', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('privateNS')
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.text('render')
- table.json('toc')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- table.integer('creatorId').unsigned().references('id').inTable('users')
- })
- // PAGE TREE ---------------------------
- .createTable('pageTree', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
-
- table.integer('parent').unsigned().references('id').inTable('pageTree')
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
- // RENDERERS ---------------------------
- .createTable('renderers', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SEARCH ------------------------------
- .createTable('searchEngines', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SETTINGS ----------------------------
- .createTable('settings', table => {
- table.string('key').notNullable().primary()
- table.json('value')
- table.string('updatedAt').notNullable()
- })
- // STORAGE -----------------------------
- .createTable('storage', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
- table.json('config')
- })
- // TAGS --------------------------------
- .createTable('tags', table => {
- table.increments('id').primary()
- table.string('tag').notNullable().unique()
- table.string('title')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // USER KEYS ---------------------------
- .createTable('userKeys', table => {
- table.increments('id').primary()
- table.string('kind').notNullable()
- table.string('token').notNullable()
- table.string('createdAt').notNullable()
- table.string('validUntil').notNullable()
-
- table.integer('userId').unsigned().references('id').inTable('users')
- })
- // USERS -------------------------------
- .createTable('users', table => {
- table.increments('id').primary()
- table.string('email').notNullable()
- table.string('name').notNullable()
- table.string('providerId')
- table.string('password')
- table.boolean('tfaIsActive').notNullable().defaultTo(false)
- table.string('tfaSecret')
- table.string('jobTitle').defaultTo('')
- table.string('location').defaultTo('')
- table.string('pictureUrl')
- table.string('timezone').notNullable().defaultTo('America/New_York')
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.boolean('isActive').notNullable().defaultTo(false)
- table.boolean('isVerified').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
- table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
- table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
- })
- // =====================================
- // RELATION TABLES
- // =====================================
- // PAGE HISTORY TAGS ---------------------------
- .createTable('pageHistoryTags', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // PAGE TAGS ---------------------------
- .createTable('pageTags', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // USER GROUPS -------------------------
- .createTable('userGroups', table => {
- table.increments('id').primary()
- table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
- table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
- })
- // =====================================
- // REFERENCES
- // =====================================
- .table('users', table => {
- table.unique(['providerKey', 'email'])
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('userGroups')
- .dropTableIfExists('pageHistoryTags')
- .dropTableIfExists('pageHistory')
- .dropTableIfExists('pageTags')
- .dropTableIfExists('assets')
- .dropTableIfExists('assetFolders')
- .dropTableIfExists('comments')
- .dropTableIfExists('editors')
- .dropTableIfExists('groups')
- .dropTableIfExists('locales')
- .dropTableIfExists('navigation')
- .dropTableIfExists('pages')
- .dropTableIfExists('renderers')
- .dropTableIfExists('settings')
- .dropTableIfExists('storage')
- .dropTableIfExists('tags')
- .dropTableIfExists('userKeys')
- .dropTableIfExists('users')
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.11.js b/server/db/beta/migrations-sqlite/2.0.0-beta.11.js
deleted file mode 100644
index e2b2e2de..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.11.js
+++ /dev/null
@@ -1,52 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .renameTable('pageHistory', 'pageHistory_old')
- .createTable('pageHistory', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('action').defaultTo('updated')
-
- table.integer('pageId').unsigned()
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .raw(`INSERT INTO pageHistory SELECT id,path,hash,title,description,isPrivate,isPublished,publishStartDate,publishEndDate,content,contentType,createdAt,'updated' AS action,pageId,editorKey,localeCode,authorId FROM pageHistory_old;`)
- .dropTable('pageHistory_old')
-}
-
-exports.down = knex => {
- return knex.schema
- .renameTable('pageHistory', 'pageHistory_old')
- .createTable('pageHistory', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
-
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .raw('INSERT INTO pageHistory SELECT id,path,hash,title,description,isPrivate,isPublished,publishStartDate,publishEndDate,content,contentType,createdAt,NULL as pageId,editorKey,localeCode,authorId FROM pageHistory_old;')
- .dropTable('pageHistory_old')
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.127.js b/server/db/beta/migrations-sqlite/2.0.0-beta.127.js
deleted file mode 100644
index bd0e9f43..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.127.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('assets', table => {
- table.dropColumn('basename')
- table.string('hash').notNullable().defaultTo('')
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('assets', table => {
- table.dropColumn('hash')
- table.string('basename').notNullable().defaultTo('')
- })
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.205.js b/server/db/beta/migrations-sqlite/2.0.0-beta.205.js
deleted file mode 100644
index 934abfbc..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.205.js
+++ /dev/null
@@ -1,13 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('analytics', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('analytics')
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.217.js b/server/db/beta/migrations-sqlite/2.0.0-beta.217.js
deleted file mode 100644
index 0051e7fb..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.217.js
+++ /dev/null
@@ -1,13 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('locales', table => {
- table.integer('availability').notNullable().defaultTo(0)
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('locales', table => {
- table.dropColumn('availability')
- })
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.242.js b/server/db/beta/migrations-sqlite/2.0.0-beta.242.js
deleted file mode 100644
index bf26cf05..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.242.js
+++ /dev/null
@@ -1,13 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('users', table => {
- table.boolean('mustChangePwd').notNullable().defaultTo(false)
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('users', table => {
- table.dropColumn('mustChangePwd')
- })
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.293.js b/server/db/beta/migrations-sqlite/2.0.0-beta.293.js
deleted file mode 100644
index 2524b79c..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.293.js
+++ /dev/null
@@ -1,17 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('pageLinks', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('path').notNullable()
- table.string('localeCode', 5).notNullable()
- })
- .table('pageLinks', table => {
- table.index(['path', 'localeCode'])
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('pageLinks')
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.38.js b/server/db/beta/migrations-sqlite/2.0.0-beta.38.js
deleted file mode 100644
index 1c7f5343..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.38.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('storage', table => {
- table.string('syncInterval')
- table.json('state')
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('storage', table => {
- table.dropColumn('syncInterval')
- table.dropColumn('state')
- })
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-beta.99.js b/server/db/beta/migrations-sqlite/2.0.0-beta.99.js
deleted file mode 100644
index 02cfa514..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-beta.99.js
+++ /dev/null
@@ -1,12 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('assetData', table => {
- table.integer('id').primary()
- table.binary('data').notNullable()
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('assetData')
-}
diff --git a/server/db/beta/migrations-sqlite/2.0.0-rc.2.js b/server/db/beta/migrations-sqlite/2.0.0-rc.2.js
deleted file mode 100644
index 4bfd5986..00000000
--- a/server/db/beta/migrations-sqlite/2.0.0-rc.2.js
+++ /dev/null
@@ -1,35 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .dropTable('pageTree')
- .createTable('pageTree', table => {
- table.integer('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
-
- table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTable('pageTree')
- .createTable('pageTree', table => {
- table.integer('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
-
- table.integer('parent').unsigned().references('id').inTable('pageTree')
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.1.js b/server/db/beta/migrations/2.0.0-beta.1.js
deleted file mode 100644
index 7b1e9f84..00000000
--- a/server/db/beta/migrations/2.0.0-beta.1.js
+++ /dev/null
@@ -1,292 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- // =====================================
- // MODEL TABLES
- // =====================================
- // ASSETS ------------------------------
- .createTable('assets', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('filename').notNullable()
- table.string('basename').notNullable()
- table.string('ext').notNullable()
- table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
- table.string('mime').notNullable().defaultTo('application/octet-stream')
- table.integer('fileSize').unsigned().comment('In kilobytes')
- table.json('metadata')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // ASSET FOLDERS -----------------------
- .createTable('assetFolders', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('name').notNullable()
- table.string('slug').notNullable()
- table.integer('parentId').unsigned().references('id').inTable('assetFolders')
- })
- // AUTHENTICATION ----------------------
- .createTable('authentication', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- table.boolean('selfRegistration').notNullable().defaultTo(false)
- table.json('domainWhitelist').notNullable()
- table.json('autoEnrollGroups').notNullable()
- })
- // COMMENTS ----------------------------
- .createTable('comments', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.text('content').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // EDITORS -----------------------------
- .createTable('editors', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // GROUPS ------------------------------
- .createTable('groups', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('name').notNullable()
- table.json('permissions').notNullable()
- table.json('pageRules').notNullable()
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOCALES -----------------------------
- .createTable('locales', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('code', 2).notNullable().primary()
- table.json('strings')
- table.boolean('isRTL').notNullable().defaultTo(false)
- table.string('name').notNullable()
- table.string('nativeName').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOGGING ----------------------------
- .createTable('loggers', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('level').notNullable().defaultTo('warn')
- table.json('config')
- })
- // NAVIGATION ----------------------------
- .createTable('navigation', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.json('config')
- })
- // PAGE HISTORY ------------------------
- .createTable('pageHistory', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- })
- // PAGES -------------------------------
- .createTable('pages', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('privateNS')
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.text('render')
- table.json('toc')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // PAGE TREE ---------------------------
- .createTable('pageTree', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
- })
- // RENDERERS ---------------------------
- .createTable('renderers', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SEARCH ------------------------------
- .createTable('searchEngines', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SETTINGS ----------------------------
- .createTable('settings', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.json('value')
- table.string('updatedAt').notNullable()
- })
- // STORAGE -----------------------------
- .createTable('storage', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
- table.json('config')
- })
- // TAGS --------------------------------
- .createTable('tags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('tag').notNullable().unique()
- table.string('title')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // USER KEYS ---------------------------
- .createTable('userKeys', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('kind').notNullable()
- table.string('token').notNullable()
- table.string('createdAt').notNullable()
- table.string('validUntil').notNullable()
- })
- // USERS -------------------------------
- .createTable('users', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('email').notNullable()
- table.string('name').notNullable()
- table.string('providerId')
- table.string('password')
- table.boolean('tfaIsActive').notNullable().defaultTo(false)
- table.string('tfaSecret')
- table.string('jobTitle').defaultTo('')
- table.string('location').defaultTo('')
- table.string('pictureUrl')
- table.string('timezone').notNullable().defaultTo('America/New_York')
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.boolean('isActive').notNullable().defaultTo(false)
- table.boolean('isVerified').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // =====================================
- // RELATION TABLES
- // =====================================
- // PAGE HISTORY TAGS ---------------------------
- .createTable('pageHistoryTags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // PAGE TAGS ---------------------------
- .createTable('pageTags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // USER GROUPS -------------------------
- .createTable('userGroups', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
- table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
- })
- // =====================================
- // REFERENCES
- // =====================================
- .table('assets', table => {
- table.integer('folderId').unsigned().references('id').inTable('assetFolders')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('comments', table => {
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('pageHistory', table => {
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 2).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('pages', table => {
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 2).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- table.integer('creatorId').unsigned().references('id').inTable('users')
- })
- .table('pageTree', table => {
- table.integer('parent').unsigned().references('id').inTable('pageTree')
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('localeCode', 2).references('code').inTable('locales')
- })
- .table('userKeys', table => {
- table.integer('userId').unsigned().references('id').inTable('users')
- })
- .table('users', table => {
- table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
- table.string('localeCode', 2).references('code').inTable('locales').notNullable().defaultTo('en')
- table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
-
- table.unique(['providerKey', 'email'])
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('userGroups')
- .dropTableIfExists('pageHistoryTags')
- .dropTableIfExists('pageHistory')
- .dropTableIfExists('pageTags')
- .dropTableIfExists('assets')
- .dropTableIfExists('assetFolders')
- .dropTableIfExists('comments')
- .dropTableIfExists('editors')
- .dropTableIfExists('groups')
- .dropTableIfExists('locales')
- .dropTableIfExists('navigation')
- .dropTableIfExists('pages')
- .dropTableIfExists('renderers')
- .dropTableIfExists('settings')
- .dropTableIfExists('storage')
- .dropTableIfExists('tags')
- .dropTableIfExists('userKeys')
- .dropTableIfExists('users')
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.11.js b/server/db/beta/migrations/2.0.0-beta.11.js
deleted file mode 100644
index c1beb64f..00000000
--- a/server/db/beta/migrations/2.0.0-beta.11.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('pageHistory', table => {
- table.string('action').defaultTo('updated')
- table.dropForeign('pageId')
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('pageHistory', table => {
- table.dropColumn('action')
- table.integer('pageId').unsigned().references('id').inTable('pages')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.127.js b/server/db/beta/migrations/2.0.0-beta.127.js
deleted file mode 100644
index 28be9d0c..00000000
--- a/server/db/beta/migrations/2.0.0-beta.127.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('assets', table => {
- table.dropColumn('basename')
- table.string('hash').notNullable()
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('assets', table => {
- table.dropColumn('hash')
- table.string('basename').notNullable()
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.148.js b/server/db/beta/migrations/2.0.0-beta.148.js
deleted file mode 100644
index 5cfcbe43..00000000
--- a/server/db/beta/migrations/2.0.0-beta.148.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- blobLength: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .table('assetData', table => {
- if (dbCompat.blobLength) {
- table.dropColumn('data')
- }
- })
- .table('assetData', table => {
- if (dbCompat.blobLength) {
- table.specificType('data', 'LONGBLOB').notNullable()
- }
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('assetData', table => {})
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.205.js b/server/db/beta/migrations/2.0.0-beta.205.js
deleted file mode 100644
index 7e7bfa84..00000000
--- a/server/db/beta/migrations/2.0.0-beta.205.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('analytics', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('analytics')
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.217.js b/server/db/beta/migrations/2.0.0-beta.217.js
deleted file mode 100644
index 0051e7fb..00000000
--- a/server/db/beta/migrations/2.0.0-beta.217.js
+++ /dev/null
@@ -1,13 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('locales', table => {
- table.integer('availability').notNullable().defaultTo(0)
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('locales', table => {
- table.dropColumn('availability')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.242.js b/server/db/beta/migrations/2.0.0-beta.242.js
deleted file mode 100644
index bf26cf05..00000000
--- a/server/db/beta/migrations/2.0.0-beta.242.js
+++ /dev/null
@@ -1,13 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('users', table => {
- table.boolean('mustChangePwd').notNullable().defaultTo(false)
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('users', table => {
- table.dropColumn('mustChangePwd')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.293.js b/server/db/beta/migrations/2.0.0-beta.293.js
deleted file mode 100644
index 94b0273a..00000000
--- a/server/db/beta/migrations/2.0.0-beta.293.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('pageLinks', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('path').notNullable()
- table.string('localeCode', 5).notNullable()
- })
- .table('pageLinks', table => {
- table.index(['path', 'localeCode'])
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('pageLinks')
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.38.js b/server/db/beta/migrations/2.0.0-beta.38.js
deleted file mode 100644
index 1c7f5343..00000000
--- a/server/db/beta/migrations/2.0.0-beta.38.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .table('storage', table => {
- table.string('syncInterval')
- table.json('state')
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .table('storage', table => {
- table.dropColumn('syncInterval')
- table.dropColumn('state')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-beta.99.js b/server/db/beta/migrations/2.0.0-beta.99.js
deleted file mode 100644
index d761945d..00000000
--- a/server/db/beta/migrations/2.0.0-beta.99.js
+++ /dev/null
@@ -1,18 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('assetData', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').primary()
- table.binary('data').notNullable()
- })
-}
-
-exports.down = knex => {
- return knex.schema
- .dropTableIfExists('assetData')
-}
diff --git a/server/db/beta/migrations/2.0.0-rc.2.js b/server/db/beta/migrations/2.0.0-rc.2.js
deleted file mode 100644
index 22987d5f..00000000
--- a/server/db/beta/migrations/2.0.0-rc.2.js
+++ /dev/null
@@ -1,54 +0,0 @@
-/* global WIKI */
-
-exports.up = async knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
- selfCascadeDelete: WIKI.config.db.type !== 'mssql'
- }
-
- return knex.schema
- .dropTable('pageTree')
- .createTable('pageTree', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').unsigned().primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
- })
- .table('pageTree', table => {
- if (dbCompat.selfCascadeDelete) {
- table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
- } else {
- table.integer('parent').unsigned()
- }
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
-}
-
-exports.down = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
- selfCascadeDelete: WIKI.config.db.type !== 'mssql'
- }
- return knex.schema
- .dropTable('pageTree')
- .createTable('pageTree', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
- })
- .table('pageTree', table => {
- table.integer('parent').unsigned().references('id').inTable('pageTree')
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
-}
diff --git a/server/db/beta/migrations/2.0.0-rc.29.js b/server/db/beta/migrations/2.0.0-rc.29.js
deleted file mode 100644
index fcbe2725..00000000
--- a/server/db/beta/migrations/2.0.0-rc.29.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- return knex.schema
- .table('pages', table => {
- switch (WIKI.config.db.type) {
- case 'mariadb':
- case 'mysql':
- table.specificType('content', 'LONGTEXT').alter()
- table.specificType('render', 'LONGTEXT').alter()
- break
- case 'mssql':
- table.specificType('content', 'VARCHAR(max)').alter()
- table.specificType('render', 'VARCHAR(max)').alter()
- break
- }
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/legacy/index.js b/server/db/legacy/index.js
new file mode 100644
index 00000000..77fd9527
--- /dev/null
+++ b/server/db/legacy/index.js
@@ -0,0 +1,32 @@
+const _ = require('lodash')
+
+/* global WIKI */
+
+module.exports = {
+ async migrate (knex) {
+ const migrationsTableExists = await knex.schema.hasTable('migrations')
+ if (!migrationsTableExists) {
+ return
+ }
+
+ const migrations = await knex('migrations')
+ if (_.some(migrations, m => m.name.indexOf('2.5.128') >= 0)) {
+ // TODO: 2.x MIGRATIONS for 3.0
+ WIKI.logger.error('Upgrading from 2.x is not yet supported. A future release will allow for upgrade from 2.x. Exiting...')
+ process.exit(1)
+
+ // -> Cleanup migration table
+ await knex('migrations').truncate()
+
+ // -> Advance to stable 3.0 migration state
+ await knex('migrations').insert({
+ name: '3.0.0.js',
+ batch: 1,
+ migration_time: knex.fn.now()
+ })
+ } else {
+ console.error('CANNOT UPGRADE FROM OLDER UNSUPPORTED VERSION. UPGRADE TO THE LATEST 2.X VERSION FIRST! Exiting...')
+ process.exit(1)
+ }
+ }
+}
diff --git a/server/db/migrations-sqlite/2.0.0.js b/server/db/migrations-sqlite/2.0.0.js
deleted file mode 100644
index 4428cfc4..00000000
--- a/server/db/migrations-sqlite/2.0.0.js
+++ /dev/null
@@ -1,268 +0,0 @@
-exports.up = knex => {
- return knex.schema
- // =====================================
- // MODEL TABLES
- // =====================================
- // ANALYTICS ---------------------------
- .createTable('analytics', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // ASSETS ------------------------------
- .createTable('assets', table => {
- table.increments('id').primary()
- table.string('filename').notNullable()
- table.string('hash').notNullable().defaultTo('')
- table.string('ext').notNullable()
- table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
- table.string('mime').notNullable().defaultTo('application/octet-stream')
- table.integer('fileSize').unsigned().comment('In kilobytes')
- table.json('metadata')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.integer('folderId').unsigned().references('id').inTable('assetFolders')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // ASSET DATA --------------------------
- .createTable('assetData', table => {
- table.integer('id').primary()
- table.binary('data').notNullable()
- })
- // ASSET FOLDERS -----------------------
- .createTable('assetFolders', table => {
- table.increments('id').primary()
- table.string('name').notNullable()
- table.string('slug').notNullable()
- table.integer('parentId').unsigned().references('id').inTable('assetFolders')
- })
- // AUTHENTICATION ----------------------
- .createTable('authentication', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- table.boolean('selfRegistration').notNullable().defaultTo(false)
- table.json('domainWhitelist').notNullable()
- table.json('autoEnrollGroups').notNullable()
- })
- // COMMENTS ----------------------------
- .createTable('comments', table => {
- table.increments('id').primary()
- table.text('content').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // EDITORS -----------------------------
- .createTable('editors', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // GROUPS ------------------------------
- .createTable('groups', table => {
- table.increments('id').primary()
- table.string('name').notNullable()
- table.json('permissions').notNullable()
- table.json('pageRules').notNullable()
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOCALES -----------------------------
- .createTable('locales', table => {
- table.string('code', 5).notNullable().primary()
- table.json('strings')
- table.boolean('isRTL').notNullable().defaultTo(false)
- table.string('name').notNullable()
- table.string('nativeName').notNullable()
- table.integer('availability').notNullable().defaultTo(0)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOGGING ----------------------------
- .createTable('loggers', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('level').notNullable().defaultTo('warn')
- table.json('config')
- })
- // NAVIGATION ----------------------------
- .createTable('navigation', table => {
- table.string('key').notNullable().primary()
- table.json('config')
- })
- // PAGE HISTORY ------------------------
- .createTable('pageHistory', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('action').defaultTo('updated')
-
- table.integer('pageId').unsigned()
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- // PAGE LINKS --------------------------
- .createTable('pageLinks', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('path').notNullable()
- table.string('localeCode', 5).notNullable()
- })
- // PAGES -------------------------------
- .createTable('pages', table => {
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('privateNS')
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.text('content')
- table.text('render')
- table.json('toc')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- table.integer('creatorId').unsigned().references('id').inTable('users')
- })
- // PAGE TREE ---------------------------
- .createTable('pageTree', table => {
- table.integer('id').primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
-
- table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
- // RENDERERS ---------------------------
- .createTable('renderers', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SEARCH ------------------------------
- .createTable('searchEngines', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SETTINGS ----------------------------
- .createTable('settings', table => {
- table.string('key').notNullable().primary()
- table.json('value')
- table.string('updatedAt').notNullable()
- })
- // STORAGE -----------------------------
- .createTable('storage', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
- table.json('config')
- table.string('syncInterval')
- table.json('state')
- })
- // TAGS --------------------------------
- .createTable('tags', table => {
- table.increments('id').primary()
- table.string('tag').notNullable().unique()
- table.string('title')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // USER KEYS ---------------------------
- .createTable('userKeys', table => {
- table.increments('id').primary()
- table.string('kind').notNullable()
- table.string('token').notNullable()
- table.string('createdAt').notNullable()
- table.string('validUntil').notNullable()
-
- table.integer('userId').unsigned().references('id').inTable('users')
- })
- // USERS -------------------------------
- .createTable('users', table => {
- table.increments('id').primary()
- table.string('email').notNullable()
- table.string('name').notNullable()
- table.string('providerId')
- table.string('password')
- table.boolean('tfaIsActive').notNullable().defaultTo(false)
- table.string('tfaSecret')
- table.string('jobTitle').defaultTo('')
- table.string('location').defaultTo('')
- table.string('pictureUrl')
- table.string('timezone').notNullable().defaultTo('America/New_York')
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.boolean('isActive').notNullable().defaultTo(false)
- table.boolean('isVerified').notNullable().defaultTo(false)
- table.boolean('mustChangePwd').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
-
- table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
- table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
- table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
- })
- // =====================================
- // RELATION TABLES
- // =====================================
- // PAGE HISTORY TAGS ---------------------------
- .createTable('pageHistoryTags', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // PAGE TAGS ---------------------------
- .createTable('pageTags', table => {
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // USER GROUPS -------------------------
- .createTable('userGroups', table => {
- table.increments('id').primary()
- table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
- table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
- })
- // =====================================
- // REFERENCES
- // =====================================
- .table('users', table => {
- table.unique(['providerKey', 'email'])
- })
- // =====================================
- // INDEXES
- // =====================================
- .table('pageLinks', table => {
- table.index(['path', 'localeCode'])
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.2.17.js b/server/db/migrations-sqlite/2.2.17.js
deleted file mode 100644
index 2cd3a62f..00000000
--- a/server/db/migrations-sqlite/2.2.17.js
+++ /dev/null
@@ -1,9 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('pageHistory', table => {
- table.string('versionDate').notNullable().defaultTo('')
- })
- .raw(`UPDATE pageHistory AS h1 SET versionDate = COALESCE((SELECT createdAt FROM pageHistory AS h2 WHERE h2.pageId = h1.pageId AND h2.id < h1.id ORDER BY h2.id DESC LIMIT 1), h1.createdAt, '')`)
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.2.3.js b/server/db/migrations-sqlite/2.2.3.js
deleted file mode 100644
index bb29f975..00000000
--- a/server/db/migrations-sqlite/2.2.3.js
+++ /dev/null
@@ -1,14 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('apiKeys', table => {
- table.increments('id').primary()
- table.string('name').notNullable()
- table.text('key').notNullable()
- table.string('expiration').notNullable()
- table.boolean('isRevoked').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.3.10.js b/server/db/migrations-sqlite/2.3.10.js
deleted file mode 100644
index 7a2f37db..00000000
--- a/server/db/migrations-sqlite/2.3.10.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('users', table => {
- table.string('lastLoginAt')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.3.14.js b/server/db/migrations-sqlite/2.3.14.js
deleted file mode 100644
index c1ecd694..00000000
--- a/server/db/migrations-sqlite/2.3.14.js
+++ /dev/null
@@ -1,10 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('commentProviders', table => {
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.3.23.js b/server/db/migrations-sqlite/2.3.23.js
deleted file mode 100644
index ec68bbce..00000000
--- a/server/db/migrations-sqlite/2.3.23.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('pageTree', table => {
- table.json('ancestors')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.4.13.js b/server/db/migrations-sqlite/2.4.13.js
deleted file mode 100644
index 396a3d37..00000000
--- a/server/db/migrations-sqlite/2.4.13.js
+++ /dev/null
@@ -1,15 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('pages', table => {
- table.json('extra').notNullable().defaultTo('{}')
- })
- .alterTable('pageHistory', table => {
- table.json('extra').notNullable().defaultTo('{}')
- })
- .alterTable('users', table => {
- table.string('dateFormat').notNullable().defaultTo('')
- table.string('appearance').notNullable().defaultTo('')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.4.36.js b/server/db/migrations-sqlite/2.4.36.js
deleted file mode 100644
index b955ff2c..00000000
--- a/server/db/migrations-sqlite/2.4.36.js
+++ /dev/null
@@ -1,11 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('comments', table => {
- table.text('render').notNullable().defaultTo('')
- table.string('name').notNullable().defaultTo('')
- table.string('email').notNullable().defaultTo('')
- table.string('ip').notNullable().defaultTo('')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.4.61.js b/server/db/migrations-sqlite/2.4.61.js
deleted file mode 100644
index 3f89ed5f..00000000
--- a/server/db/migrations-sqlite/2.4.61.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('comments', table => {
- table.integer('replyTo').unsigned().notNullable().defaultTo(0)
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.1.js b/server/db/migrations-sqlite/2.5.1.js
deleted file mode 100644
index ffd70e0c..00000000
--- a/server/db/migrations-sqlite/2.5.1.js
+++ /dev/null
@@ -1,34 +0,0 @@
-exports.up = async knex => {
- // Check for users using disabled strategies
- let protectedStrategies = []
- const disabledStrategies = await knex('authentication').where('isEnabled', false)
- if (disabledStrategies) {
- const incompatibleUsers = await knex('users').distinct('providerKey').whereIn('providerKey', disabledStrategies.map(s => s.key))
- if (incompatibleUsers && incompatibleUsers.length > 0) {
- protectedStrategies = incompatibleUsers.map(u => u.providerKey)
- }
- }
-
- // Delete disabled strategies
- await knex('authentication').whereNotIn('key', protectedStrategies).andWhere('isEnabled', false).del()
-
- // Update table schema
- await knex.schema
- .alterTable('authentication', table => {
- table.integer('order').unsigned().notNullable().defaultTo(0)
- table.string('strategyKey').notNullable().defaultTo('')
- table.string('displayName').notNullable().defaultTo('')
- })
-
- // Fix pre-2.5 strategies
- const strategies = await knex('authentication')
- let idx = 1
- for (const strategy of strategies) {
- await knex('authentication').where('key', strategy.key).update({
- strategyKey: strategy.key,
- order: (strategy.key === 'local') ? 0 : idx++
- })
- }
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.108.js b/server/db/migrations-sqlite/2.5.108.js
deleted file mode 100644
index cfd99fff..00000000
--- a/server/db/migrations-sqlite/2.5.108.js
+++ /dev/null
@@ -1,14 +0,0 @@
-const has = require('lodash/has')
-
-exports.up = async knex => {
- // -> Fix 2.5.1 added isEnabled columns for beta users
- const localStrategy = await knex('authentication').where('key', 'local').first()
- if (localStrategy && !has(localStrategy, 'isEnabled')) {
- await knex.schema
- .alterTable('authentication', table => {
- table.boolean('isEnabled').notNullable().defaultTo(true)
- })
- }
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.118.js b/server/db/migrations-sqlite/2.5.118.js
deleted file mode 100644
index 11c1498e..00000000
--- a/server/db/migrations-sqlite/2.5.118.js
+++ /dev/null
@@ -1,6 +0,0 @@
-exports.up = async knex => {
- // -> Fix 2.5.117 new installations without isEnabled on local auth (#2382)
- await knex('authentication').where('key', 'local').update({ isEnabled: true })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.12.js b/server/db/migrations-sqlite/2.5.12.js
deleted file mode 100644
index 8b8c81b5..00000000
--- a/server/db/migrations-sqlite/2.5.12.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = async knex => {
- await knex.schema
- .alterTable('groups', table => {
- table.string('redirectOnLogin').notNullable().defaultTo('/')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.122.js b/server/db/migrations-sqlite/2.5.122.js
deleted file mode 100644
index 115afe1d..00000000
--- a/server/db/migrations-sqlite/2.5.122.js
+++ /dev/null
@@ -1,9 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .createTable('userAvatars', table => {
- table.integer('id').primary()
- table.binary('data').notNullable()
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations-sqlite/2.5.128.js b/server/db/migrations-sqlite/2.5.128.js
deleted file mode 100644
index e2105c86..00000000
--- a/server/db/migrations-sqlite/2.5.128.js
+++ /dev/null
@@ -1,7 +0,0 @@
-exports.up = async knex => {
- await knex('users').update({
- email: knex.raw('LOWER(email)')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.0.0.js b/server/db/migrations/2.0.0.js
deleted file mode 100644
index 91101890..00000000
--- a/server/db/migrations/2.0.0.js
+++ /dev/null
@@ -1,325 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- blobLength: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
- selfCascadeDelete: WIKI.config.db.type !== 'mssql'
- }
- return knex.schema
- // =====================================
- // MODEL TABLES
- // =====================================
- // ANALYTICS ---------------------------
- .createTable('analytics', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // ASSETS ------------------------------
- .createTable('assets', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('filename').notNullable()
- table.string('hash').notNullable()
- table.string('ext').notNullable()
- table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
- table.string('mime').notNullable().defaultTo('application/octet-stream')
- table.integer('fileSize').unsigned().comment('In kilobytes')
- table.json('metadata')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // ASSET DATA --------------------------
- .createTable('assetData', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').primary()
- if (dbCompat.blobLength) {
- table.specificType('data', 'LONGBLOB').notNullable()
- } else {
- table.binary('data').notNullable()
- }
- })
- // ASSET FOLDERS -----------------------
- .createTable('assetFolders', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('name').notNullable()
- table.string('slug').notNullable()
- table.integer('parentId').unsigned().references('id').inTable('assetFolders')
- })
- // AUTHENTICATION ----------------------
- .createTable('authentication', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- table.boolean('selfRegistration').notNullable().defaultTo(false)
- table.json('domainWhitelist').notNullable()
- table.json('autoEnrollGroups').notNullable()
- })
- // COMMENTS ----------------------------
- .createTable('comments', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.text('content').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // EDITORS -----------------------------
- .createTable('editors', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
- // GROUPS ------------------------------
- .createTable('groups', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('name').notNullable()
- table.json('permissions').notNullable()
- table.json('pageRules').notNullable()
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOCALES -----------------------------
- .createTable('locales', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('code', 5).notNullable().primary()
- table.json('strings')
- table.boolean('isRTL').notNullable().defaultTo(false)
- table.string('name').notNullable()
- table.string('nativeName').notNullable()
- table.integer('availability').notNullable().defaultTo(0)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // LOGGING ----------------------------
- .createTable('loggers', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('level').notNullable().defaultTo('warn')
- table.json('config')
- })
- // NAVIGATION ----------------------------
- .createTable('navigation', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.json('config')
- })
- // PAGE HISTORY ------------------------
- .createTable('pageHistory', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('publishStartDate')
- table.string('publishEndDate')
- table.string('action').defaultTo('updated')
- table.integer('pageId').unsigned()
- table.text('content')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- })
- // PAGE LINKS --------------------------
- .createTable('pageLinks', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('localeCode', 5).notNullable()
- })
- // PAGES -------------------------------
- .createTable('pages', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('path').notNullable()
- table.string('hash').notNullable()
- table.string('title').notNullable()
- table.string('description')
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isPublished').notNullable().defaultTo(false)
- table.string('privateNS')
- table.string('publishStartDate')
- table.string('publishEndDate')
- switch (WIKI.config.db.type) {
- case 'postgres':
- case 'sqlite':
- table.text('content')
- table.text('render')
- break
- case 'mariadb':
- case 'mysql':
- table.specificType('content', 'LONGTEXT')
- table.specificType('render', 'LONGTEXT')
- break
- case 'mssql':
- table.specificType('content', 'VARCHAR(max)')
- table.specificType('render', 'VARCHAR(max)')
- break
- }
- table.json('toc')
- table.string('contentType').notNullable()
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // PAGE TREE ---------------------------
- .createTable('pageTree', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').unsigned().primary()
- table.string('path').notNullable()
- table.integer('depth').unsigned().notNullable()
- table.string('title').notNullable()
- table.boolean('isPrivate').notNullable().defaultTo(false)
- table.boolean('isFolder').notNullable().defaultTo(false)
- table.string('privateNS')
- })
- // RENDERERS ---------------------------
- .createTable('renderers', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SEARCH ------------------------------
- .createTable('searchEngines', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config')
- })
- // SETTINGS ----------------------------
- .createTable('settings', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.json('value')
- table.string('updatedAt').notNullable()
- })
- // STORAGE -----------------------------
- .createTable('storage', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.string('mode', ['sync', 'push', 'pull']).notNullable().defaultTo('push')
- table.json('config')
- table.string('syncInterval')
- table.json('state')
- })
- // TAGS --------------------------------
- .createTable('tags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('tag').notNullable().unique()
- table.string('title')
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // USER KEYS ---------------------------
- .createTable('userKeys', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('kind').notNullable()
- table.string('token').notNullable()
- table.string('createdAt').notNullable()
- table.string('validUntil').notNullable()
- })
- // USERS -------------------------------
- .createTable('users', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('email').notNullable()
- table.string('name').notNullable()
- table.string('providerId')
- table.string('password')
- table.boolean('tfaIsActive').notNullable().defaultTo(false)
- table.string('tfaSecret')
- table.string('jobTitle').defaultTo('')
- table.string('location').defaultTo('')
- table.string('pictureUrl')
- table.string('timezone').notNullable().defaultTo('America/New_York')
- table.boolean('isSystem').notNullable().defaultTo(false)
- table.boolean('isActive').notNullable().defaultTo(false)
- table.boolean('isVerified').notNullable().defaultTo(false)
- table.boolean('mustChangePwd').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
- // =====================================
- // RELATION TABLES
- // =====================================
- // PAGE HISTORY TAGS ---------------------------
- .createTable('pageHistoryTags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pageHistory').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // PAGE TAGS ---------------------------
- .createTable('pageTags', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.integer('tagId').unsigned().references('id').inTable('tags').onDelete('CASCADE')
- })
- // USER GROUPS -------------------------
- .createTable('userGroups', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.integer('userId').unsigned().references('id').inTable('users').onDelete('CASCADE')
- table.integer('groupId').unsigned().references('id').inTable('groups').onDelete('CASCADE')
- })
- // =====================================
- // REFERENCES
- // =====================================
- .table('assets', table => {
- table.integer('folderId').unsigned().references('id').inTable('assetFolders')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('comments', table => {
- table.integer('pageId').unsigned().references('id').inTable('pages')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('pageHistory', table => {
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- })
- .table('pageLinks', table => {
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.index(['path', 'localeCode'])
- })
- .table('pages', table => {
- table.string('editorKey').references('key').inTable('editors')
- table.string('localeCode', 5).references('code').inTable('locales')
- table.integer('authorId').unsigned().references('id').inTable('users')
- table.integer('creatorId').unsigned().references('id').inTable('users')
- })
- .table('pageTree', table => {
- if (dbCompat.selfCascadeDelete) {
- table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
- } else {
- table.integer('parent').unsigned()
- }
- table.integer('pageId').unsigned().references('id').inTable('pages').onDelete('CASCADE')
- table.string('localeCode', 5).references('code').inTable('locales')
- })
- .table('userKeys', table => {
- table.integer('userId').unsigned().references('id').inTable('users')
- })
- .table('users', table => {
- table.string('providerKey').references('key').inTable('authentication').notNullable().defaultTo('local')
- table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
- table.string('defaultEditor').references('key').inTable('editors').notNullable().defaultTo('markdown')
-
- table.unique(['providerKey', 'email'])
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.1.85.js b/server/db/migrations/2.1.85.js
deleted file mode 100644
index 9ab00f94..00000000
--- a/server/db/migrations/2.1.85.js
+++ /dev/null
@@ -1,19 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- return knex.schema
- .alterTable('pageHistory', table => {
- switch (WIKI.config.db.type) {
- // No change needed for PostgreSQL and SQLite
- case 'mariadb':
- case 'mysql':
- table.specificType('content', 'LONGTEXT').alter()
- break
- case 'mssql':
- table.specificType('content', 'VARCHAR(max)').alter()
- break
- }
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.2.17.js b/server/db/migrations/2.2.17.js
deleted file mode 100644
index 8827115c..00000000
--- a/server/db/migrations/2.2.17.js
+++ /dev/null
@@ -1,37 +0,0 @@
-const _ = require('lodash')
-
-/* global WIKI */
-
-exports.up = async knex => {
- let sqlVersionDate = ''
- switch (WIKI.config.db.type) {
- case 'postgres':
- sqlVersionDate = 'UPDATE "pageHistory" h1 SET "versionDate" = COALESCE((SELECT prev."createdAt" FROM "pageHistory" prev WHERE prev."pageId" = h1."pageId" AND prev.id < h1.id ORDER BY prev.id DESC LIMIT 1), h1."createdAt")'
- break
- case 'mssql':
- sqlVersionDate = 'UPDATE h1 SET "versionDate" = COALESCE((SELECT TOP 1 prev."createdAt" FROM "pageHistory" prev WHERE prev."pageId" = h1."pageId" AND prev.id < h1.id ORDER BY prev.id DESC), h1."createdAt") FROM "pageHistory" h1'
- break
- case 'mysql':
- case 'mariadb':
- // -> Fix for 2.2.50 failed migration
- const pageHistoryColumns = await knex.schema.raw('SHOW COLUMNS FROM pageHistory')
- if (_.some(pageHistoryColumns[0], ['Field', 'versionDate'])) {
- console.info('MySQL 2.2.50 Migration Fix - Dropping failed versionDate column...')
- await knex.schema.raw('ALTER TABLE pageHistory DROP COLUMN versionDate')
- console.info('versionDate column dropped successfully.')
- }
-
- sqlVersionDate = `UPDATE pageHistory AS h1 INNER JOIN pageHistory AS h2 ON h2.id = (SELECT prev.id FROM (SELECT * FROM pageHistory) AS prev WHERE prev.pageId = h1.pageId AND prev.id < h1.id ORDER BY prev.id DESC LIMIT 1) SET h1.versionDate = h2.createdAt`
- break
- // case 'mariadb':
- // sqlVersionDate = `UPDATE pageHistory AS h1 INNER JOIN pageHistory AS h2 ON h2.id = (SELECT prev.id FROM pageHistory AS prev WHERE prev.pageId = h1.pageId AND prev.id < h1.id ORDER BY prev.id DESC LIMIT 1) SET h1.versionDate = h2.createdAt`
- // break
- }
- await knex.schema
- .alterTable('pageHistory', table => {
- table.string('versionDate').notNullable().defaultTo('')
- })
- .raw(sqlVersionDate)
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.2.3.js b/server/db/migrations/2.2.3.js
deleted file mode 100644
index 29d87c74..00000000
--- a/server/db/migrations/2.2.3.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('apiKeys', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.increments('id').primary()
- table.string('name').notNullable()
- table.text('key').notNullable()
- table.string('expiration').notNullable()
- table.boolean('isRevoked').notNullable().defaultTo(false)
- table.string('createdAt').notNullable()
- table.string('updatedAt').notNullable()
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.3.10.js b/server/db/migrations/2.3.10.js
deleted file mode 100644
index 7a2f37db..00000000
--- a/server/db/migrations/2.3.10.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('users', table => {
- table.string('lastLoginAt')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.3.23.js b/server/db/migrations/2.3.23.js
deleted file mode 100644
index ec68bbce..00000000
--- a/server/db/migrations/2.3.23.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('pageTree', table => {
- table.json('ancestors')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.4.13.js b/server/db/migrations/2.4.13.js
deleted file mode 100644
index bc679ac8..00000000
--- a/server/db/migrations/2.4.13.js
+++ /dev/null
@@ -1,25 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- return knex.schema
- .alterTable('pages', table => {
- if (WIKI.config.db.type === 'mysql') {
- table.json('extra')
- } else {
- table.json('extra').notNullable().defaultTo('{}')
- }
- })
- .alterTable('pageHistory', table => {
- if (WIKI.config.db.type === 'mysql') {
- table.json('extra')
- } else {
- table.json('extra').notNullable().defaultTo('{}')
- }
- })
- .alterTable('users', table => {
- table.string('dateFormat').notNullable().defaultTo('')
- table.string('appearance').notNullable().defaultTo('')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.4.14.js b/server/db/migrations/2.4.14.js
deleted file mode 100644
index 05706e44..00000000
--- a/server/db/migrations/2.4.14.js
+++ /dev/null
@@ -1,16 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('commentProviders', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.string('key').notNullable().primary()
- table.boolean('isEnabled').notNullable().defaultTo(false)
- table.json('config').notNullable()
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.4.36.js b/server/db/migrations/2.4.36.js
deleted file mode 100644
index b955ff2c..00000000
--- a/server/db/migrations/2.4.36.js
+++ /dev/null
@@ -1,11 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('comments', table => {
- table.text('render').notNullable().defaultTo('')
- table.string('name').notNullable().defaultTo('')
- table.string('email').notNullable().defaultTo('')
- table.string('ip').notNullable().defaultTo('')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.4.61.js b/server/db/migrations/2.4.61.js
deleted file mode 100644
index 3f89ed5f..00000000
--- a/server/db/migrations/2.4.61.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = knex => {
- return knex.schema
- .alterTable('comments', table => {
- table.integer('replyTo').unsigned().notNullable().defaultTo(0)
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.1.js b/server/db/migrations/2.5.1.js
deleted file mode 100644
index ffd70e0c..00000000
--- a/server/db/migrations/2.5.1.js
+++ /dev/null
@@ -1,34 +0,0 @@
-exports.up = async knex => {
- // Check for users using disabled strategies
- let protectedStrategies = []
- const disabledStrategies = await knex('authentication').where('isEnabled', false)
- if (disabledStrategies) {
- const incompatibleUsers = await knex('users').distinct('providerKey').whereIn('providerKey', disabledStrategies.map(s => s.key))
- if (incompatibleUsers && incompatibleUsers.length > 0) {
- protectedStrategies = incompatibleUsers.map(u => u.providerKey)
- }
- }
-
- // Delete disabled strategies
- await knex('authentication').whereNotIn('key', protectedStrategies).andWhere('isEnabled', false).del()
-
- // Update table schema
- await knex.schema
- .alterTable('authentication', table => {
- table.integer('order').unsigned().notNullable().defaultTo(0)
- table.string('strategyKey').notNullable().defaultTo('')
- table.string('displayName').notNullable().defaultTo('')
- })
-
- // Fix pre-2.5 strategies
- const strategies = await knex('authentication')
- let idx = 1
- for (const strategy of strategies) {
- await knex('authentication').where('key', strategy.key).update({
- strategyKey: strategy.key,
- order: (strategy.key === 'local') ? 0 : idx++
- })
- }
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.108.js b/server/db/migrations/2.5.108.js
deleted file mode 100644
index cfd99fff..00000000
--- a/server/db/migrations/2.5.108.js
+++ /dev/null
@@ -1,14 +0,0 @@
-const has = require('lodash/has')
-
-exports.up = async knex => {
- // -> Fix 2.5.1 added isEnabled columns for beta users
- const localStrategy = await knex('authentication').where('key', 'local').first()
- if (localStrategy && !has(localStrategy, 'isEnabled')) {
- await knex.schema
- .alterTable('authentication', table => {
- table.boolean('isEnabled').notNullable().defaultTo(true)
- })
- }
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.118.js b/server/db/migrations/2.5.118.js
deleted file mode 100644
index 11c1498e..00000000
--- a/server/db/migrations/2.5.118.js
+++ /dev/null
@@ -1,6 +0,0 @@
-exports.up = async knex => {
- // -> Fix 2.5.117 new installations without isEnabled on local auth (#2382)
- await knex('authentication').where('key', 'local').update({ isEnabled: true })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.12.js b/server/db/migrations/2.5.12.js
deleted file mode 100644
index 8b8c81b5..00000000
--- a/server/db/migrations/2.5.12.js
+++ /dev/null
@@ -1,8 +0,0 @@
-exports.up = async knex => {
- await knex.schema
- .alterTable('groups', table => {
- table.string('redirectOnLogin').notNullable().defaultTo('/')
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.122.js b/server/db/migrations/2.5.122.js
deleted file mode 100644
index 24c5377c..00000000
--- a/server/db/migrations/2.5.122.js
+++ /dev/null
@@ -1,20 +0,0 @@
-/* global WIKI */
-
-exports.up = knex => {
- const dbCompat = {
- blobLength: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`),
- charset: (WIKI.config.db.type === `mysql` || WIKI.config.db.type === `mariadb`)
- }
- return knex.schema
- .createTable('userAvatars', table => {
- if (dbCompat.charset) { table.charset('utf8mb4') }
- table.integer('id').primary()
- if (dbCompat.blobLength) {
- table.specificType('data', 'LONGBLOB').notNullable()
- } else {
- table.binary('data').notNullable()
- }
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/2.5.128.js b/server/db/migrations/2.5.128.js
deleted file mode 100644
index 8316f6ae..00000000
--- a/server/db/migrations/2.5.128.js
+++ /dev/null
@@ -1,7 +0,0 @@
-exports.up = async knex => {
- await knex('users').update({
- email: knex.raw('LOWER(??)', ['email'])
- })
-}
-
-exports.down = knex => { }
diff --git a/server/db/migrations/3.0.0.js b/server/db/migrations/3.0.0.js
new file mode 100644
index 00000000..70e085e4
--- /dev/null
+++ b/server/db/migrations/3.0.0.js
@@ -0,0 +1,589 @@
+const { v4: uuid } = require('uuid')
+const bcrypt = require('bcryptjs-then')
+const crypto = require('crypto')
+const pem2jwk = require('pem-jwk').pem2jwk
+
+/* global WIKI */
+
+exports.up = async knex => {
+ WIKI.logger.info('Running 3.0.0 database migration...')
+
+ // =====================================
+ // PG EXTENSIONS
+ // =====================================
+ await knex.raw('CREATE EXTENSION IF NOT EXISTS pgcrypto;')
+
+ await knex.schema
+ // =====================================
+ // MODEL TABLES
+ // =====================================
+ // ANALYTICS ---------------------------
+ .createTable('analytics', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('module').notNullable()
+ table.boolean('isEnabled').notNullable().defaultTo(false)
+ table.jsonb('config').notNullable()
+ })
+ // API KEYS ----------------------------
+ .createTable('apiKeys', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('name').notNullable()
+ table.text('key').notNullable()
+ table.string('expiration').notNullable()
+ table.boolean('isRevoked').notNullable().defaultTo(false)
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // ASSETS ------------------------------
+ .createTable('assets', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('filename').notNullable()
+ table.string('hash').notNullable().index()
+ table.string('ext').notNullable()
+ table.enum('kind', ['binary', 'image']).notNullable().defaultTo('binary')
+ table.string('mime').notNullable().defaultTo('application/octet-stream')
+ table.integer('fileSize').unsigned().comment('In kilobytes')
+ table.jsonb('metadata')
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // ASSET DATA --------------------------
+ .createTable('assetData', table => {
+ table.uuid('id').notNullable().index()
+ table.binary('data').notNullable()
+ })
+ // ASSET FOLDERS -----------------------
+ .createTable('assetFolders', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('name').notNullable()
+ table.string('slug').notNullable()
+ })
+ // AUTHENTICATION ----------------------
+ .createTable('authentication', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('module').notNullable()
+ table.boolean('isEnabled').notNullable().defaultTo(false)
+ table.integer('order').unsigned().notNullable().defaultTo(0)
+ table.string('displayName').notNullable().defaultTo('')
+ table.jsonb('config').notNullable().defaultTo('{}')
+ table.boolean('selfRegistration').notNullable().defaultTo(false)
+ table.jsonb('domainWhitelist').notNullable().defaultTo('[]')
+ table.jsonb('autoEnrollGroups').notNullable().defaultTo('[]')
+ table.jsonb('hideOnSites').notNullable().defaultTo('[]')
+ })
+ // COMMENTS ----------------------------
+ .createTable('comments', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.uuid('replyTo')
+ table.text('content').notNullable()
+ table.text('render').notNullable().defaultTo('')
+ table.string('name').notNullable().defaultTo('')
+ table.string('email').notNullable().defaultTo('')
+ table.string('ip').notNullable().defaultTo('')
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // GROUPS ------------------------------
+ .createTable('groups', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('name').notNullable()
+ table.jsonb('permissions').notNullable()
+ table.jsonb('rules').notNullable()
+ table.string('redirectOnLogin').notNullable().defaultTo('')
+ table.string('redirectOnFirstLogin').notNullable().defaultTo('')
+ table.string('redirectOnLogout').notNullable().defaultTo('')
+ table.boolean('isSystem').notNullable().defaultTo(false)
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // HOOKS -------------------------------
+ .createTable('hooks', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('name').notNullable()
+ table.jsonb('events').notNullable().defaultTo('[]')
+ table.string('url').notNullable()
+ table.boolean('includeMetadata').notNullable().defaultTo(false)
+ table.boolean('includeContent').notNullable().defaultTo(false)
+ table.boolean('acceptUntrusted').notNullable().defaultTo(false)
+ table.string('authHeader')
+ table.enum('state', ['pending', 'error', 'success']).notNullable().defaultTo('pending')
+ table.string('lastErrorMessage')
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // LOCALES -----------------------------
+ .createTable('locales', table => {
+ table.string('code', 5).notNullable().primary()
+ table.jsonb('strings')
+ table.boolean('isRTL').notNullable().defaultTo(false)
+ table.string('name').notNullable()
+ table.string('nativeName').notNullable()
+ table.integer('availability').notNullable().defaultTo(0)
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // NAVIGATION ----------------------------
+ .createTable('navigation', table => {
+ table.string('key').notNullable().primary()
+ table.jsonb('config')
+ })
+ // PAGE HISTORY ------------------------
+ .createTable('pageHistory', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.uuid('pageId').notNullable().index()
+ table.string('path').notNullable()
+ table.string('hash').notNullable()
+ table.string('title').notNullable()
+ table.string('description')
+ table.enu('publishState', ['draft', 'published', 'scheduled']).notNullable().defaultTo('draft')
+ table.timestamp('publishStartDate')
+ table.timestamp('publishEndDate')
+ table.string('action').defaultTo('updated')
+ table.text('content')
+ table.string('contentType').notNullable()
+ table.jsonb('extra').notNullable().defaultTo('{}')
+ table.jsonb('tags').defaultTo('[]')
+ table.timestamp('versionDate').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // PAGE LINKS --------------------------
+ .createTable('pageLinks', table => {
+ table.increments('id').primary()
+ table.string('path').notNullable()
+ table.string('localeCode', 5).notNullable()
+ })
+ // PAGES -------------------------------
+ .createTable('pages', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('slug')
+ table.string('path').notNullable()
+ table.string('hash').notNullable()
+ table.string('title').notNullable()
+ table.string('description')
+ table.enu('publishState', ['draft', 'published', 'scheduled']).notNullable().defaultTo('draft')
+ table.timestamp('publishStartDate')
+ table.timestamp('publishEndDate')
+ table.text('content')
+ table.text('render')
+ table.jsonb('toc')
+ table.string('contentType').notNullable()
+ table.jsonb('extra').notNullable().defaultTo('{}')
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // PAGE TREE ---------------------------
+ .createTable('pageTree', table => {
+ table.integer('id').unsigned().primary()
+ table.string('path').notNullable()
+ table.integer('depth').unsigned().notNullable()
+ table.string('title').notNullable()
+ table.boolean('isFolder').notNullable().defaultTo(false)
+ table.jsonb('ancestors')
+ })
+ // RENDERERS ---------------------------
+ .createTable('renderers', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('module').notNullable()
+ table.boolean('isEnabled').notNullable().defaultTo(false)
+ table.jsonb('config')
+ })
+ // SETTINGS ----------------------------
+ .createTable('settings', table => {
+ table.string('key').notNullable().primary()
+ table.jsonb('value')
+ })
+ // SITES -------------------------------
+ .createTable('sites', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('hostname').notNullable()
+ table.boolean('isEnabled').notNullable().defaultTo(false)
+ table.jsonb('config').notNullable()
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // STORAGE -----------------------------
+ .createTable('storage', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('module').notNullable()
+ table.boolean('isEnabled').notNullable().defaultTo(false)
+ table.jsonb('contentTypes')
+ table.jsonb('assetDelivery')
+ table.jsonb('versioning')
+ table.jsonb('schedule')
+ table.jsonb('config')
+ table.jsonb('state')
+ })
+ // TAGS --------------------------------
+ .createTable('tags', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('tag').notNullable()
+ table.jsonb('display').notNullable().defaultTo('{}')
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // USER AVATARS ------------------------
+ .createTable('userAvatars', table => {
+ table.uuid('id').notNullable().primary()
+ table.binary('data').notNullable()
+ })
+ // USER KEYS ---------------------------
+ .createTable('userKeys', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('kind').notNullable()
+ table.string('token').notNullable()
+ table.timestamp('validUntil').notNullable()
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // USERS -------------------------------
+ .createTable('users', table => {
+ table.uuid('id').notNullable().primary().defaultTo(knex.raw('gen_random_uuid()'))
+ table.string('email').notNullable()
+ table.string('name').notNullable()
+ table.jsonb('auth')
+ table.jsonb('tfa')
+ table.jsonb('meta')
+ table.jsonb('prefs')
+ table.string('pictureUrl')
+ table.boolean('isSystem').notNullable().defaultTo(false)
+ table.boolean('isActive').notNullable().defaultTo(false)
+ table.boolean('isVerified').notNullable().defaultTo(false)
+ table.timestamp('lastLoginAt').index()
+ table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
+ table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
+ })
+ // =====================================
+ // RELATION TABLES
+ // =====================================
+ // PAGE TAGS ---------------------------
+ .createTable('pageTags', table => {
+ table.increments('id').primary()
+ table.uuid('pageId').references('id').inTable('pages').onDelete('CASCADE')
+ table.uuid('tagId').references('id').inTable('tags').onDelete('CASCADE')
+ })
+ // USER GROUPS -------------------------
+ .createTable('userGroups', table => {
+ table.increments('id').primary()
+ table.uuid('userId').references('id').inTable('users').onDelete('CASCADE')
+ table.uuid('groupId').references('id').inTable('groups').onDelete('CASCADE')
+ })
+ // =====================================
+ // REFERENCES
+ // =====================================
+ .table('analytics', table => {
+ table.uuid('siteId').notNullable().references('id').inTable('sites')
+ })
+ .table('assets', table => {
+ table.uuid('folderId').notNullable().references('id').inTable('assetFolders').index()
+ table.uuid('authorId').notNullable().references('id').inTable('users')
+ table.uuid('siteId').notNullable().references('id').inTable('sites').index()
+ })
+ .table('assetFolders', table => {
+ table.uuid('parentId').references('id').inTable('assetFolders').index()
+ })
+ .table('comments', table => {
+ table.uuid('pageId').notNullable().references('id').inTable('pages').index()
+ table.uuid('authorId').notNullable().references('id').inTable('users').index()
+ })
+ .table('navigation', table => {
+ table.uuid('siteId').notNullable().references('id').inTable('sites').index()
+ })
+ .table('pageHistory', table => {
+ table.string('localeCode', 5).references('code').inTable('locales')
+ table.uuid('authorId').notNullable().references('id').inTable('users')
+ table.uuid('siteId').notNullable().references('id').inTable('sites').index()
+ })
+ .table('pageLinks', table => {
+ table.uuid('pageId').notNullable().references('id').inTable('pages').onDelete('CASCADE')
+ table.index(['path', 'localeCode'])
+ })
+ .table('pages', table => {
+ table.string('localeCode', 5).references('code').inTable('locales').index()
+ table.uuid('authorId').notNullable().references('id').inTable('users').index()
+ table.uuid('creatorId').notNullable().references('id').inTable('users').index()
+ table.uuid('siteId').notNullable().references('id').inTable('sites').index()
+ })
+ .table('pageTree', table => {
+ table.integer('parent').unsigned().references('id').inTable('pageTree').onDelete('CASCADE')
+ table.uuid('pageId').notNullable().references('id').inTable('pages').onDelete('CASCADE')
+ table.string('localeCode', 5).references('code').inTable('locales')
+ })
+ .table('storage', table => {
+ table.uuid('siteId').notNullable().references('id').inTable('sites')
+ })
+ .table('tags', table => {
+ table.uuid('siteId').notNullable().references('id').inTable('sites')
+ table.unique(['siteId', 'tag'])
+ })
+ .table('userKeys', table => {
+ table.uuid('userId').notNullable().references('id').inTable('users')
+ })
+ .table('users', table => {
+ table.string('localeCode', 5).references('code').inTable('locales').notNullable().defaultTo('en')
+ })
+
+ // =====================================
+ // DEFAULT DATA
+ // =====================================
+
+ // -> SYSTEM CONFIG
+
+ await knex('settings').insert([
+ {
+ key: 'update',
+ value: {
+ locales: true
+ }
+ },
+ {
+ key: 'mail',
+ value: {
+ senderName: '',
+ senderEmail: '',
+ host: '',
+ port: 465,
+ secure: true,
+ verifySSL: true,
+ user: '',
+ pass: '',
+ useDKIM: false,
+ dkimDomainName: '',
+ dkimKeySelector: '',
+ dkimPrivateKey: ''
+ }
+ },
+ {
+ key: 'security',
+ value: {
+ corsConfig: '',
+ corsMode: 'OFF',
+ cspDirectives: '',
+ disallowFloc: true,
+ disallowIframe: true,
+ disallowOpenRedirect: true,
+ enforceCsp: false,
+ enforceHsts: false,
+ enforceSameOriginReferrerPolicy: true,
+ forceAssetDownload: true,
+ hstsDuration: 0,
+ trustProxy: false,
+ authJwtAudience: 'urn:wiki.js',
+ authJwtExpiration: '30m',
+ authJwtRenewablePeriod: '14d',
+ uploadMaxFileSize: 10485760,
+ uploadMaxFiles: 20,
+ uploadScanSVG: true
+ }
+ }
+ ])
+
+ // -> DEFAULT LOCALE
+
+ await knex('locales').insert({
+ code: 'en',
+ strings: {},
+ isRTL: false,
+ name: 'English',
+ nativeName: 'English'
+ })
+
+ // -> DEFAULT SITE
+
+ WIKI.logger.info('Generating certificates...')
+ const secret = crypto.randomBytes(32).toString('hex')
+ const certs = crypto.generateKeyPairSync('rsa', {
+ modulusLength: 2048,
+ publicKeyEncoding: {
+ type: 'pkcs1',
+ format: 'pem'
+ },
+ privateKeyEncoding: {
+ type: 'pkcs1',
+ format: 'pem',
+ cipher: 'aes-256-cbc',
+ passphrase: secret
+ }
+ })
+
+ const siteId = uuid()
+ await knex('sites').insert({
+ id: siteId,
+ hostname: '*',
+ isEnabled: true,
+ config: {
+ auth: {
+ audience: 'urn:wiki.js',
+ tokenExpiration: '30m',
+ tokenRenewal: '14d',
+ certs: {
+ jwk: pem2jwk(certs.publicKey),
+ public: certs.publicKey,
+ private: certs.privateKey
+ },
+ secret
+ },
+ title: 'My Wiki Site',
+ description: '',
+ company: '',
+ contentLicense: '',
+ defaults: {
+ timezone: 'America/New_York',
+ dateFormat: 'YYYY-MM-DD',
+ timeFormat: '12h'
+ },
+ features: {
+ ratings: false,
+ ratingsMode: 'off',
+ comments: false,
+ contributions: false,
+ profile: true,
+ search: true
+ },
+ logoText: true,
+ robots: {
+ index: true,
+ follow: true
+ },
+ locale: 'en',
+ localeNamespacing: false,
+ localeNamespaces: [],
+ theme: {
+ dark: false,
+ colorPrimary: '#1976d2',
+ colorSecondary: '#02c39a',
+ colorAccent: '#f03a47',
+ colorHeader: '#000000',
+ colorSidebar: '#1976d2',
+ injectCSS: '',
+ injectHead: '',
+ injectBody: '',
+ sidebarPosition: 'left',
+ tocPosition: 'right',
+ showSharingMenu: true,
+ showPrintBtn: true
+ }
+ }
+ })
+
+ // -> DEFAULT GROUPS
+
+ const groupAdminId = uuid()
+ const groupGuestId = '10000000-0000-4000-0000-000000000001'
+ await knex('groups').insert([
+ {
+ id: groupAdminId,
+ name: 'Administrators',
+ permissions: JSON.stringify(['manage:system']),
+ rules: JSON.stringify([]),
+ isSystem: true
+ },
+ {
+ id: groupGuestId,
+ name: 'Guests',
+ permissions: JSON.stringify(['read:pages', 'read:assets', 'read:comments']),
+ rules: JSON.stringify([
+ {
+ id: uuid(),
+ name: 'Default Rule',
+ roles: ['read:pages', 'read:assets', 'read:comments'],
+ match: 'START',
+ mode: 'DENY',
+ path: '',
+ locales: [],
+ sites: []
+ }
+ ]),
+ isSystem: true
+ }
+ ])
+
+ // -> AUTHENTICATION MODULE
+
+ const authModuleId = uuid()
+ await knex('authentication').insert({
+ id: authModuleId,
+ module: 'local',
+ isEnabled: true,
+ displayName: 'Local Authentication'
+ })
+
+ // -> USERS
+
+ const userAdminId = uuid()
+ const userGuestId = uuid()
+ await knex('users').insert([
+ {
+ id: userAdminId,
+ email: process.env.ADMIN_EMAIL ?? 'admin@example.com',
+ auth: {
+ [authModuleId]: {
+ password: await bcrypt.hash(process.env.ADMIN_PASS || '12345678', 12),
+ mustChangePwd: !process.env.ADMIN_PASS,
+ restrictLogin: false,
+ tfaRequired: false,
+ tfaSecret: ''
+ }
+ },
+ name: 'Administrator',
+ isSystem: false,
+ isActive: true,
+ isVerified: true,
+ meta: {
+ location: '',
+ jobTitle: '',
+ pronouns: ''
+ },
+ prefs: {
+ timezone: 'America/New_York',
+ dateFormat: 'YYYY-MM-DD',
+ timeFormat: '12h',
+ darkMode: false
+ },
+ localeCode: 'en'
+ },
+ {
+ id: userGuestId,
+ email: 'guest@example.com',
+ name: 'Guest',
+ isSystem: true,
+ isActive: true,
+ isVerified: true,
+ localeCode: 'en'
+ }
+ ])
+
+ await knex('userGroups').insert([
+ {
+ userId: userAdminId,
+ groupId: groupAdminId
+ },
+ {
+ userId: userGuestId,
+ groupId: groupGuestId
+ }
+ ])
+
+ // -> STORAGE MODULE
+
+ await knex('storage').insert({
+ module: 'db',
+ siteId,
+ isEnabled: true,
+ contentTypes: {
+ activeTypes: ['pages', 'images', 'documents', 'others', 'large'],
+ largeThreshold: '5MB'
+ },
+ assetDelivery: {
+ streaming: true,
+ directAccess: false
+ },
+ versioning: {
+ enabled: false
+ },
+ state: {
+ current: 'ok'
+ }
+ })
+
+ WIKI.logger.info('Completed 3.0.0 database migration.')
+}
+
+exports.down = knex => { }
diff --git a/server/db/migrator-source.js b/server/db/migrator-source.js
index 8b086653..eab254fd 100644
--- a/server/db/migrator-source.js
+++ b/server/db/migrator-source.js
@@ -2,7 +2,7 @@ const path = require('path')
const fs = require('fs-extra')
const semver = require('semver')
-const baseMigrationPath = path.join(WIKI.SERVERPATH, (WIKI.config.db.type !== 'sqlite') ? 'db/migrations' : 'db/migrations-sqlite')
+const baseMigrationPath = path.join(WIKI.SERVERPATH, 'db/migrations')
/* global WIKI */
diff --git a/server/graph/resolvers/site.js b/server/graph/resolvers/site.js
index c325706c..6a732cec 100644
--- a/server/graph/resolvers/site.js
+++ b/server/graph/resolvers/site.js
@@ -1,13 +1,185 @@
const graphHelper = require('../../helpers/graph')
const _ = require('lodash')
+const CleanCSS = require('clean-css')
+const path = require('path')
/* global WIKI */
module.exports = {
Query: {
+ async sites () {
+ const sites = await WIKI.models.sites.query()
+ return sites.map(s => ({
+ ...s.config,
+ id: s.id,
+ hostname: s.hostname,
+ isEnabled: s.isEnabled
+ }))
+ },
+ async siteById (obj, args) {
+ const site = await WIKI.models.sites.query().findById(args.id)
+ return site ? {
+ ...site.config,
+ id: site.id,
+ hostname: site.hostname,
+ isEnabled: site.isEnabled
+ } : null
+ },
+ async siteByHostname (obj, args) {
+ let site = await WIKI.models.sites.query().where({
+ hostname: args.hostname
+ }).first()
+ if (!site && !args.exact) {
+ site = await WIKI.models.sites.query().where({
+ hostname: '*'
+ }).first()
+ }
+ return site ? {
+ ...site.config,
+ id: site.id,
+ hostname: site.hostname,
+ isEnabled: site.isEnabled
+ } : null
+ },
+ // LEGACY
async site() { return {} }
},
Mutation: {
+ /**
+ * CREATE SITE
+ */
+ async createSite (obj, args) {
+ try {
+ // -> Validate inputs
+ if (!args.hostname || args.hostname.length < 1 || !/^(\\*)|([a-z0-9\-.:]+)$/.test(args.hostname)) {
+ throw WIKI.ERROR(new Error('Invalid Site Hostname'), 'SiteCreateInvalidHostname')
+ }
+ if (!args.title || args.title.length < 1 || !/^[^<>"]+$/.test(args.title)) {
+ throw WIKI.ERROR(new Error('Invalid Site Title'), 'SiteCreateInvalidTitle')
+ }
+ // -> Check for duplicate catch-all
+ if (args.hostname === '*') {
+ const site = await WIKI.models.sites.query().where({
+ hostname: args.hostname
+ }).first()
+ if (site) {
+ throw WIKI.ERROR(new Error('A site with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.'), 'SiteCreateDuplicateCatchAll')
+ }
+ }
+ // -> Create site
+ const newSite = await WIKI.models.sites.createSite(args.hostname, {
+ title: args.title
+ })
+ return {
+ status: graphHelper.generateSuccess('Site created successfully'),
+ site: newSite
+ }
+ } catch (err) {
+ return graphHelper.generateError(err)
+ }
+ },
+ /**
+ * UPDATE SITE
+ */
+ async updateSite (obj, args) {
+ try {
+ // -> Load site
+ const site = await WIKI.models.sites.query().findById(args.id)
+ if (!site) {
+ throw WIKI.ERROR(new Error('Invalid Site ID'), 'SiteInvalidId')
+ }
+ // -> Check for bad input
+ if (_.has(args.patch, 'hostname') && _.trim(args.patch.hostname).length < 1) {
+ throw WIKI.ERROR(new Error('Hostname is invalid.'), 'SiteInvalidHostname')
+ }
+ // -> Check for duplicate catch-all
+ if (args.patch.hostname === '*' && site.hostname !== '*') {
+ const dupSite = await WIKI.models.sites.query().where({ hostname: '*' }).first()
+ if (dupSite) {
+ throw WIKI.ERROR(new Error(`Site ${dupSite.config.title} with a catch-all hostname already exists! Cannot have 2 catch-all hostnames.`), 'SiteUpdateDuplicateCatchAll')
+ }
+ }
+ // -> Format Code
+ if (args.patch?.theme?.injectCSS) {
+ args.patch.theme.injectCSS = new CleanCSS({ inline: false }).minify(args.patch.theme.injectCSS).styles
+ }
+ // -> Update site
+ await WIKI.models.sites.updateSite(args.id, {
+ hostname: args.patch.hostname ?? site.hostname,
+ isEnabled: args.patch.isEnabled ?? site.isEnabled,
+ config: _.defaultsDeep(_.omit(args.patch, ['hostname', 'isEnabled']), site.config)
+ })
+
+ return {
+ status: graphHelper.generateSuccess('Site updated successfully')
+ }
+ } catch (err) {
+ WIKI.logger.warn(err)
+ return graphHelper.generateError(err)
+ }
+ },
+ /**
+ * DELETE SITE
+ */
+ async deleteSite (obj, args) {
+ try {
+ // -> Ensure site isn't last one
+ const sitesCount = await WIKI.models.sites.query().count('id').first()
+ if (sitesCount?.count && _.toNumber(sitesCount?.count) <= 1) {
+ throw WIKI.ERROR(new Error('Cannot delete the last site. At least 1 site must exists at all times.'), 'SiteDeleteLastSite')
+ }
+ // -> Delete site
+ await WIKI.models.sites.deleteSite(args.id)
+ return {
+ status: graphHelper.generateSuccess('Site deleted successfully')
+ }
+ } catch (err) {
+ WIKI.logger.warn(err)
+ return graphHelper.generateError(err)
+ }
+ },
+ /**
+ * UPLOAD LOGO
+ */
+ async uploadSiteLogo (obj, args) {
+ try {
+ const { filename, mimetype, createReadStream } = await args.image
+ WIKI.logger.info(`Processing site logo ${filename} of type ${mimetype}...`)
+ if (!WIKI.extensions.ext.sharp.isInstalled) {
+ throw new Error('This feature requires the Sharp extension but it is not installed.')
+ }
+ console.info(mimetype)
+ const destFormat = mimetype.startsWith('image/svg') ? 'svg' : 'png'
+ const destPath = path.resolve(
+ process.cwd(),
+ WIKI.config.dataPath,
+ `assets/logo.${destFormat}`
+ )
+ await WIKI.extensions.ext.sharp.resize({
+ format: destFormat,
+ inputStream: createReadStream(),
+ outputPath: destPath,
+ width: 100
+ })
+ WIKI.logger.info('New site logo processed successfully.')
+ return {
+ status: graphHelper.generateSuccess('Site logo uploaded successfully')
+ }
+ } catch (err) {
+ return graphHelper.generateError(err)
+ }
+ },
+ /**
+ * UPLOAD FAVICON
+ */
+ async uploadSiteFavicon (obj, args) {
+ const { filename, mimetype, createReadStream } = await args.image
+ console.info(filename, mimetype)
+ return {
+ status: graphHelper.generateSuccess('Site favicon uploaded successfully')
+ }
+ },
+ // LEGACY
async site() { return {} }
},
SiteQuery: {
diff --git a/server/graph/scalars/json.js b/server/graph/scalars/json.js
new file mode 100644
index 00000000..4ab8aa33
--- /dev/null
+++ b/server/graph/scalars/json.js
@@ -0,0 +1,59 @@
+const { Kind, GraphQLScalarType } = require('graphql')
+
+function ensureObject (value) {
+ if (typeof value !== 'object' || value === null || Array.isArray(value)) {
+ throw new TypeError(`JSONObject cannot represent non-object value: ${value}`)
+ }
+
+ return value
+}
+
+function parseLiteral (typeName, ast, variables) {
+ switch (ast.kind) {
+ case Kind.STRING:
+ case Kind.BOOLEAN:
+ return ast.value
+ case Kind.INT:
+ case Kind.FLOAT:
+ return parseFloat(ast.value)
+ case Kind.OBJECT:
+ return parseObject(typeName, ast, variables)
+ case Kind.LIST:
+ return ast.values.map((n) => parseLiteral(typeName, n, variables))
+ case Kind.NULL:
+ return null
+ case Kind.VARIABLE:
+ return variables ? variables[ast.name.value] : undefined
+ default:
+ throw new TypeError(`${typeName} cannot represent value: ${ast}`)
+ }
+}
+
+function parseObject (typeName, ast, variables) {
+ const value = Object.create(null)
+ ast.fields.forEach((field) => {
+ // eslint-disable-next-line no-use-before-define
+ value[field.name.value] = parseLiteral(typeName, field.value, variables)
+ })
+
+ return value
+}
+
+module.exports = {
+ JSON: new GraphQLScalarType({
+ name: 'JSON',
+ description:
+ 'The `JSON` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).',
+ specifiedByUrl:
+ 'http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf',
+ serialize: ensureObject,
+ parseValue: ensureObject,
+ parseLiteral: (ast, variables) => {
+ if (ast.kind !== Kind.OBJECT) {
+ throw new TypeError(`JSONObject cannot represent non-object value: ${ast}`)
+ }
+
+ return parseObject('JSONObject', ast, variables)
+ }
+ })
+}
diff --git a/server/graph/scalars/uuid.js b/server/graph/scalars/uuid.js
new file mode 100644
index 00000000..3b2de14b
--- /dev/null
+++ b/server/graph/scalars/uuid.js
@@ -0,0 +1,39 @@
+const { Kind, GraphQLScalarType } = require('graphql')
+// const { Kind } = require('graphql/language')
+
+const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i
+const nilUUID = '00000000-0000-0000-0000-000000000000'
+
+function isUUID (value) {
+ return uuidRegex.test(value) || nilUUID === value
+}
+
+module.exports = {
+ UUID: new GraphQLScalarType({
+ name: 'UUID',
+ description: 'The `UUID` scalar type represents UUID values as specified by [RFC 4122](https://tools.ietf.org/html/rfc4122).',
+ serialize: (value) => {
+ if (!isUUID(value)) {
+ throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
+ }
+
+ return value.toLowerCase()
+ },
+ parseValue: (value) => {
+ if (!isUUID(value)) {
+ throw new TypeError(`UUID cannot represent non-UUID value: ${value}`)
+ }
+
+ return value.toLowerCase()
+ },
+ parseLiteral: (ast) => {
+ if (ast.kind === Kind.STRING) {
+ if (isUUID(ast.value)) {
+ return ast.value
+ }
+ }
+
+ return undefined
+ }
+ })
+}
diff --git a/server/graph/schemas/common.graphql b/server/graph/schemas/common.graphql
index 50e9cbdc..8fb6f949 100644
--- a/server/graph/schemas/common.graphql
+++ b/server/graph/schemas/common.graphql
@@ -34,6 +34,11 @@ type ResponseStatus {
message: String
}
+enum OrderByDirection {
+ asc
+ desc
+}
+
# ROOT
# ----
diff --git a/server/graph/schemas/scalars.graphql b/server/graph/schemas/scalars.graphql
index d8d6cfbd..9f6833a7 100644
--- a/server/graph/schemas/scalars.graphql
+++ b/server/graph/schemas/scalars.graphql
@@ -1,3 +1,6 @@
# SCALARS
scalar Date
+scalar JSON
+# scalar Upload
+scalar UUID
diff --git a/server/graph/schemas/site.graphql b/server/graph/schemas/site.graphql
index 5544fffe..069e5184 100644
--- a/server/graph/schemas/site.graphql
+++ b/server/graph/schemas/site.graphql
@@ -1,12 +1,49 @@
# ===============================================
-# SITE
+# SITES
# ===============================================
extend type Query {
+ sites: [Site] @auth(requires: ["manage:system"])
+
+ siteById (
+ id: UUID!
+ ): Site @auth(requires: ["manage:system"])
+
+ siteByHostname (
+ hostname: String!
+ exact: Boolean!
+ ): Site @auth(requires: ["manage:system"])
+
+ # Legacy
site: SiteQuery
}
extend type Mutation {
+ createSite (
+ hostname: String!
+ title: String!
+ ): SiteCreateResponse @auth(requires: ["manage:system"])
+
+ updateSite (
+ id: UUID!
+ patch: SiteUpdateInput!
+ ): DefaultResponse @auth(requires: ["manage:system"])
+
+ uploadSiteLogo (
+ id: UUID!
+ image: Upload!
+ ): DefaultResponse @auth(requires: ["manage:system"])
+
+ uploadSiteFavicon (
+ id: UUID!
+ image: Upload!
+ ): DefaultResponse @auth(requires: ["manage:system"])
+
+ deleteSite (
+ id: UUID!
+ ): DefaultResponse @auth(requires: ["manage:system"])
+
+ # Legacy
site: SiteMutation
}
@@ -64,6 +101,135 @@ type SiteMutation {
# TYPES
# -----------------------------------------------
+type Site {
+ id: UUID
+ hostname: String
+ isEnabled: Boolean
+ title: String
+ description: String
+ company: String
+ contentLicense: String
+ logoText: Boolean
+ robots: SiteRobots
+ features: SiteFeatures
+ defaults: SiteDefaults
+ locale: String
+ localeNamespaces: [String]
+ localeNamespacing: Boolean
+ theme: SiteTheme
+}
+
+type SiteRobots {
+ index: Boolean
+ follow: Boolean
+}
+
+type SiteFeatures {
+ ratings: Boolean
+ ratingsMode: SitePageRatingModes
+ comments: Boolean
+ contributions: Boolean
+ profile: Boolean
+ search: Boolean
+}
+
+type SiteDefaults {
+ timezone: String
+ dateFormat: String
+ timeFormat: String
+}
+
+type SiteLocale {
+ locale: String
+ autoUpdate: Boolean
+ namespacing: Boolean
+ namespaces: [String]
+}
+
+type SiteTheme {
+ dark: Boolean
+ colorPrimary: String
+ colorSecondary: String
+ colorAccent: String
+ colorHeader: String
+ colorSidebar: String
+ injectCSS: String
+ injectHead: String
+ injectBody: String
+ sidebarPosition: SiteThemePosition
+ tocPosition: SiteThemePosition
+ showSharingMenu: Boolean
+ showPrintBtn: Boolean
+}
+
+enum SiteThemePosition {
+ left
+ right
+}
+
+enum SitePageRatingModes {
+ off
+ thumbs
+ stars
+}
+
+type SiteCreateResponse {
+ status: ResponseStatus
+ site: Site
+}
+
+input SiteUpdateInput {
+ hostname: String
+ isEnabled: Boolean
+ title: String
+ description: String
+ company: String
+ contentLicense: String
+ logoText: Boolean
+ robots: SiteRobotsInput
+ features: SiteFeaturesInput
+ defaults: SiteDefaultsInput
+ theme: SiteThemeInput
+}
+
+input SiteRobotsInput {
+ index: Boolean
+ follow: Boolean
+}
+
+input SiteFeaturesInput {
+ ratings: Boolean
+ ratingsMode: SitePageRatingModes
+ comments: Boolean
+ contributions: Boolean
+ profile: Boolean
+ search: Boolean
+}
+
+input SiteDefaultsInput {
+ timezone: String
+ dateFormat: String
+ timeFormat: String
+}
+
+input SiteThemeInput {
+ dark: Boolean
+ colorPrimary: String
+ colorSecondary: String
+ colorAccent: String
+ colorHeader: String
+ colorSidebar: String
+ injectCSS: String
+ injectHead: String
+ injectBody: String
+ sidebarPosition: SiteThemePosition
+ tocPosition: SiteThemePosition
+ showSharingMenu: Boolean
+ showPrintBtn: Boolean
+}
+
+# LEGACY
+
type SiteConfig {
host: String
title: String
diff --git a/ux/.editorconfig b/ux/.editorconfig
new file mode 100644
index 00000000..9d08a1a8
--- /dev/null
+++ b/ux/.editorconfig
@@ -0,0 +1,9 @@
+root = true
+
+[*]
+charset = utf-8
+indent_style = space
+indent_size = 2
+end_of_line = lf
+insert_final_newline = true
+trim_trailing_whitespace = true
diff --git a/ux/.eslintignore b/ux/.eslintignore
new file mode 100644
index 00000000..263e5eb8
--- /dev/null
+++ b/ux/.eslintignore
@@ -0,0 +1,6 @@
+/dist
+/src-capacitor
+/src-cordova
+/.quasar
+/node_modules
+.eslintrc.js
diff --git a/ux/.eslintrc.js b/ux/.eslintrc.js
new file mode 100644
index 00000000..dff451c7
--- /dev/null
+++ b/ux/.eslintrc.js
@@ -0,0 +1,75 @@
+module.exports = {
+ // https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
+ // This option interrupts the configuration hierarchy at this file
+ // Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
+ root: true,
+
+ parserOptions: {
+ ecmaVersion: '2021' // Allows for the parsing of modern ECMAScript features
+ },
+
+ env: {
+ node: true,
+ browser: true,
+ 'vue/setup-compiler-macros': true
+ },
+
+ // Rules order is important, please avoid shuffling them
+ extends: [
+ // Base ESLint recommended rules
+ // 'eslint:recommended',
+
+ // Uncomment any of the lines below to choose desired strictness,
+ // but leave only one uncommented!
+ // See https://eslint.vuejs.org/rules/#available-rules
+ 'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
+ 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
+ // 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
+
+ 'standard'
+ ],
+
+ plugins: [
+ // https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
+ // required to lint *.vue files
+ 'vue'
+ ],
+
+ globals: {
+ ga: 'readonly', // Google Analytics
+ __statics: 'readonly',
+ __QUASAR_SSR__: 'readonly',
+ __QUASAR_SSR_SERVER__: 'readonly',
+ __QUASAR_SSR_CLIENT__: 'readonly',
+ __QUASAR_SSR_PWA__: 'readonly',
+ process: 'readonly',
+ APOLLO_CLIENT: 'readonly'
+ },
+
+ // add your custom rules here
+ rules: {
+ // allow async-await
+ 'generator-star-spacing': 'off',
+ // allow paren-less arrow functions
+ 'arrow-parens': 'off',
+ 'one-var': 'off',
+ 'no-void': 'off',
+ 'multiline-ternary': 'off',
+
+ 'import/first': 'off',
+ 'import/named': 'error',
+ 'import/namespace': 'error',
+ 'import/default': 'error',
+ 'import/export': 'error',
+ 'import/extensions': 'off',
+ 'import/no-unresolved': 'off',
+ 'import/no-extraneous-dependencies': 'off',
+
+ 'prefer-promise-reject-errors': 'off',
+
+ 'no-unused-vars': 'off',
+
+ // allow debugger during development only
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
+ }
+}
diff --git a/ux/.gitignore b/ux/.gitignore
new file mode 100644
index 00000000..06ff1aac
--- /dev/null
+++ b/ux/.gitignore
@@ -0,0 +1,29 @@
+.DS_Store
+.thumbs.db
+node_modules
+
+# Quasar core related directories
+.quasar
+/dist
+
+# Cordova related directories and files
+/src-cordova/node_modules
+/src-cordova/platforms
+/src-cordova/plugins
+/src-cordova/www
+
+# Capacitor related directories and files
+/src-capacitor/www
+/src-capacitor/node_modules
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
diff --git a/ux/.vscode/extensions.json b/ux/.vscode/extensions.json
new file mode 100644
index 00000000..df0976dc
--- /dev/null
+++ b/ux/.vscode/extensions.json
@@ -0,0 +1,14 @@
+{
+ "recommendations": [
+ "dbaeumer.vscode-eslint",
+ "editorconfig.editorconfig",
+ "johnsoncodehk.volar",
+ "wayou.vscode-todo-highlight"
+ ],
+ "unwantedRecommendations": [
+ "octref.vetur",
+ "hookyqr.beautify",
+ "dbaeumer.jshint",
+ "ms-vscode.vscode-typescript-tslint-plugin"
+ ]
+}
\ No newline at end of file
diff --git a/ux/.vscode/settings.json b/ux/.vscode/settings.json
new file mode 100644
index 00000000..f39da49b
--- /dev/null
+++ b/ux/.vscode/settings.json
@@ -0,0 +1,16 @@
+{
+ "editor.bracketPairColorization.enabled": true,
+ "editor.guides.bracketPairs": true,
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "dbaeumer.vscode-eslint",
+ "editor.codeActionsOnSave": [
+ "source.fixAll.eslint"
+ ],
+ "eslint.validate": [
+ "javascript",
+ "javascriptreact",
+ "typescript",
+ "vue"
+ ],
+ "i18n-ally.localesPaths": "src/i18n/locales"
+}
diff --git a/ux/README.md b/ux/README.md
new file mode 100644
index 00000000..f7d3f8ef
--- /dev/null
+++ b/ux/README.md
@@ -0,0 +1,33 @@
+# Wiki.js (ux)
+
+The most powerful and extensible open source Wiki software
+
+## Install the dependencies
+```bash
+yarn
+# or
+npm install
+```
+
+### Start the app in development mode (hot-code reloading, error reporting, etc.)
+```bash
+quasar dev
+```
+
+
+### Lint the files
+```bash
+yarn lint
+# or
+npm run lint
+```
+
+
+
+### Build the app for production
+```bash
+quasar build
+```
+
+### Customize the configuration
+See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
diff --git a/ux/apollo.config.js b/ux/apollo.config.js
new file mode 100644
index 00000000..4a3ed47f
--- /dev/null
+++ b/ux/apollo.config.js
@@ -0,0 +1,14 @@
+module.exports = {
+ client: {
+ service: {
+ name: 'wiki-core',
+ // URL to the GraphQL API
+ url: 'http://localhost:11511'
+ },
+ // Files processed by the extension
+ includes: [
+ 'src/**/*.vue',
+ 'src/**/*.js'
+ ]
+ }
+}
diff --git a/ux/index.html b/ux/index.html
new file mode 100644
index 00000000..c03cd480
--- /dev/null
+++ b/ux/index.html
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+ Wiki.js
+
+
+
+
+
+
+
+
+
+
+
diff --git a/ux/jsconfig.json b/ux/jsconfig.json
new file mode 100644
index 00000000..456944a5
--- /dev/null
+++ b/ux/jsconfig.json
@@ -0,0 +1,39 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "src/*": [
+ "src/*"
+ ],
+ "app/*": [
+ "*"
+ ],
+ "components/*": [
+ "src/components/*"
+ ],
+ "layouts/*": [
+ "src/layouts/*"
+ ],
+ "pages/*": [
+ "src/pages/*"
+ ],
+ "assets/*": [
+ "src/assets/*"
+ ],
+ "boot/*": [
+ "src/boot/*"
+ ],
+ "stores/*": [
+ "src/stores/*"
+ ],
+ "vue$": [
+ "node_modules/vue/dist/vue.runtime.esm-bundler.js"
+ ]
+ }
+ },
+ "exclude": [
+ "dist",
+ ".quasar",
+ "node_modules"
+ ]
+}
\ No newline at end of file
diff --git a/ux/package.json b/ux/package.json
new file mode 100644
index 00000000..b35942e4
--- /dev/null
+++ b/ux/package.json
@@ -0,0 +1,102 @@
+{
+ "name": "ux",
+ "version": "0.0.1",
+ "description": "The most powerful and extensible open source Wiki software",
+ "productName": "Wiki.js",
+ "author": "Nicolas Giard ",
+ "private": true,
+ "scripts": {
+ "dev": "quasar dev",
+ "build": "quasar build",
+ "lint": "eslint --ext .js,.vue ./"
+ },
+ "dependencies": {
+ "@apollo/client": "3.5.10",
+ "@codemirror/autocomplete": "0.19.15",
+ "@codemirror/basic-setup": "0.19.1",
+ "@codemirror/closebrackets": "0.19.1",
+ "@codemirror/commands": "0.19.8",
+ "@codemirror/comment": "0.19.1",
+ "@codemirror/fold": "0.19.3",
+ "@codemirror/gutter": "0.19.9",
+ "@codemirror/highlight": "0.19.8",
+ "@codemirror/history": "0.19.2",
+ "@codemirror/lang-css": "0.19.3",
+ "@codemirror/lang-html": "0.19.4",
+ "@codemirror/lang-javascript": "0.19.7",
+ "@codemirror/lang-json": "0.19.2",
+ "@codemirror/lang-markdown": "0.19.6",
+ "@codemirror/matchbrackets": "0.19.4",
+ "@codemirror/search": "0.19.9",
+ "@codemirror/state": "0.19.9",
+ "@codemirror/tooltip": "0.19.16",
+ "@codemirror/view": "0.19.47",
+ "@lezer/common": "0.15.12",
+ "@quasar/extras": "1.13.5",
+ "@tiptap/core": "2.0.0-beta.174",
+ "@tiptap/extension-code-block": "2.0.0-beta.37",
+ "@tiptap/extension-code-block-lowlight": "2.0.0-beta.68",
+ "@tiptap/extension-color": "2.0.0-beta.9",
+ "@tiptap/extension-dropcursor": "2.0.0-beta.25",
+ "@tiptap/extension-font-family": "2.0.0-beta.21",
+ "@tiptap/extension-gapcursor": "2.0.0-beta.34",
+ "@tiptap/extension-hard-break": "2.0.0-beta.30",
+ "@tiptap/extension-highlight": "2.0.0-beta.33",
+ "@tiptap/extension-history": "2.0.0-beta.21",
+ "@tiptap/extension-image": "2.0.0-beta.27",
+ "@tiptap/extension-mention": "2.0.0-beta.95",
+ "@tiptap/extension-placeholder": "2.0.0-beta.48",
+ "@tiptap/extension-table": "2.0.0-beta.48",
+ "@tiptap/extension-table-cell": "2.0.0-beta.20",
+ "@tiptap/extension-table-header": "2.0.0-beta.22",
+ "@tiptap/extension-table-row": "2.0.0-beta.19",
+ "@tiptap/extension-task-item": "2.0.0-beta.31",
+ "@tiptap/extension-task-list": "2.0.0-beta.26",
+ "@tiptap/extension-text-align": "2.0.0-beta.29",
+ "@tiptap/extension-text-style": "2.0.0-beta.23",
+ "@tiptap/extension-typography": "2.0.0-beta.20",
+ "@tiptap/starter-kit": "2.0.0-beta.183",
+ "@tiptap/vue-3": "2.0.0-beta.90",
+ "@vue/apollo-option": "4.0.0-alpha.16",
+ "apollo-upload-client": "17.0.0",
+ "browser-fs-access": "0.26.1",
+ "clipboard": "2.0.10",
+ "filesize": "8.0.7",
+ "filesize-parser": "1.5.0",
+ "graphql": "16.3.0",
+ "graphql-tag": "2.12.6",
+ "js-cookie": "3.0.1",
+ "jwt-decode": "3.1.2",
+ "lodash": "4.17.21",
+ "luxon": "2.3.1",
+ "pinia": "2.0.13",
+ "pug": "3.0.2",
+ "quasar": "2.6.5",
+ "tippy.js": "6.3.7",
+ "uuid": "8.3.2",
+ "v-network-graph": "0.5.9",
+ "vue": "3.2.31",
+ "vue-i18n": "9.1.9",
+ "vue-router": "4.0.14",
+ "vuedraggable": "4.1.0",
+ "zxcvbn": "4.4.2"
+ },
+ "devDependencies": {
+ "@intlify/vite-plugin-vue-i18n": "3.4.0",
+ "@quasar/app-vite": "1.0.0-beta.13",
+ "@types/lodash": "4.14.181",
+ "autoprefixer": "10.4.4",
+ "eslint": "8.12.0",
+ "eslint-config-standard": "17.0.0-1",
+ "eslint-plugin-import": "2.26.0",
+ "eslint-plugin-n": "15.1.0",
+ "eslint-plugin-promise": "6.0.0",
+ "eslint-plugin-vue": "8.6.0"
+ },
+ "engines": {
+ "node": "^18 || ^16",
+ "npm": ">= 6.13.4",
+ "yarn": ">= 1.21.1"
+ },
+ "eslint.packageManager": "yarn"
+}
diff --git a/ux/postcss.config.js b/ux/postcss.config.js
new file mode 100644
index 00000000..94b7b1c8
--- /dev/null
+++ b/ux/postcss.config.js
@@ -0,0 +1,27 @@
+/* eslint-disable */
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+module.exports = {
+ plugins: [
+ // https://github.com/postcss/autoprefixer
+ require('autoprefixer')({
+ overrideBrowserslist: [
+ 'last 4 Chrome versions',
+ 'last 4 Firefox versions',
+ 'last 4 Edge versions',
+ 'last 4 Safari versions',
+ 'last 4 Android versions',
+ 'last 4 ChromeAndroid versions',
+ 'last 4 FirefoxAndroid versions',
+ 'last 4 iOS versions'
+ ]
+ })
+
+ // https://github.com/elchininet/postcss-rtlcss
+ // If you want to support RTL css, then
+ // 1. yarn/npm install postcss-rtlcss
+ // 2. optionally set quasar.config.js > framework > lang to an RTL language
+ // 3. uncomment the following line:
+ // require('postcss-rtlcss')
+ ]
+}
diff --git a/ux/public/_assets/bg/login-v3.jpg b/ux/public/_assets/bg/login-v3.jpg
new file mode 100644
index 00000000..1b548776
Binary files /dev/null and b/ux/public/_assets/bg/login-v3.jpg differ
diff --git a/ux/public/_assets/bg/login.jpg b/ux/public/_assets/bg/login.jpg
new file mode 100644
index 00000000..16de276c
Binary files /dev/null and b/ux/public/_assets/bg/login.jpg differ
diff --git a/ux/public/_assets/bg/test-1.jpg b/ux/public/_assets/bg/test-1.jpg
new file mode 100644
index 00000000..34d837e3
Binary files /dev/null and b/ux/public/_assets/bg/test-1.jpg differ
diff --git a/ux/public/_assets/bg/test-2.jpg b/ux/public/_assets/bg/test-2.jpg
new file mode 100644
index 00000000..dc1de331
Binary files /dev/null and b/ux/public/_assets/bg/test-2.jpg differ
diff --git a/ux/public/_assets/icons/color-blog.svg b/ux/public/_assets/icons/color-blog.svg
new file mode 100644
index 00000000..49f8cd0a
--- /dev/null
+++ b/ux/public/_assets/icons/color-blog.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/color-nodejs.svg b/ux/public/_assets/icons/color-nodejs.svg
new file mode 100644
index 00000000..d7814ddb
--- /dev/null
+++ b/ux/public/_assets/icons/color-nodejs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/color-postgresql.svg b/ux/public/_assets/icons/color-postgresql.svg
new file mode 100644
index 00000000..1b811574
--- /dev/null
+++ b/ux/public/_assets/icons/color-postgresql.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-account.svg b/ux/public/_assets/icons/fluent-account.svg
new file mode 100644
index 00000000..eb2a574b
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-account.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-advance.svg b/ux/public/_assets/icons/fluent-advance.svg
new file mode 100644
index 00000000..14259474
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-advance.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-api.svg b/ux/public/_assets/icons/fluent-api.svg
new file mode 100644
index 00000000..247028a4
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-apps-tab.svg b/ux/public/_assets/icons/fluent-apps-tab.svg
new file mode 100644
index 00000000..46809947
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-apps-tab.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-bar-chart.svg b/ux/public/_assets/icons/fluent-bar-chart.svg
new file mode 100644
index 00000000..361def4d
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-bar-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-bunch-of-keys.svg b/ux/public/_assets/icons/fluent-bunch-of-keys.svg
new file mode 100644
index 00000000..73356398
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-bunch-of-keys.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-bursts.svg b/ux/public/_assets/icons/fluent-bursts.svg
new file mode 100644
index 00000000..76c96fc0
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-bursts.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-cashbook.svg b/ux/public/_assets/icons/fluent-cashbook.svg
new file mode 100644
index 00000000..e2928a1b
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-cashbook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-change-theme.svg b/ux/public/_assets/icons/fluent-change-theme.svg
new file mode 100644
index 00000000..ae36332b
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-change-theme.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-color-wheel.svg b/ux/public/_assets/icons/fluent-color-wheel.svg
new file mode 100644
index 00000000..87410e46
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-color-wheel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-comments.svg b/ux/public/_assets/icons/fluent-comments.svg
new file mode 100644
index 00000000..77be6513
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-comments.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-database.svg b/ux/public/_assets/icons/fluent-database.svg
new file mode 100644
index 00000000..78bf7453
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-database.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-delete-bin.svg b/ux/public/_assets/icons/fluent-delete-bin.svg
new file mode 100644
index 00000000..252ee303
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-delete-bin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-down.svg b/ux/public/_assets/icons/fluent-down.svg
new file mode 100644
index 00000000..7d4cb5c5
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-event-log.svg b/ux/public/_assets/icons/fluent-event-log.svg
new file mode 100644
index 00000000..bfad419b
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-event-log.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-fantasy.svg b/ux/public/_assets/icons/fluent-fantasy.svg
new file mode 100644
index 00000000..d5e24ebe
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-fantasy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-firewall.svg b/ux/public/_assets/icons/fluent-firewall.svg
new file mode 100644
index 00000000..61a7502d
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-firewall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-inspection.svg b/ux/public/_assets/icons/fluent-inspection.svg
new file mode 100644
index 00000000..214eb812
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-inspection.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-language.svg b/ux/public/_assets/icons/fluent-language.svg
new file mode 100644
index 00000000..05b1f8bf
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-language.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-lightning-bolt.svg b/ux/public/_assets/icons/fluent-lightning-bolt.svg
new file mode 100644
index 00000000..3ee3fcc3
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-lightning-bolt.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/ux/public/_assets/icons/fluent-markdown.svg b/ux/public/_assets/icons/fluent-markdown.svg
new file mode 100644
index 00000000..be0a381a
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-markdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-message-settings.svg b/ux/public/_assets/icons/fluent-message-settings.svg
new file mode 100644
index 00000000..4999d0f5
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-message-settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-module.svg b/ux/public/_assets/icons/fluent-module.svg
new file mode 100644
index 00000000..5d78233e
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-module.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-open-file-under-cursor.svg b/ux/public/_assets/icons/fluent-open-file-under-cursor.svg
new file mode 100644
index 00000000..f9837371
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-open-file-under-cursor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-paint-roller.svg b/ux/public/_assets/icons/fluent-paint-roller.svg
new file mode 100644
index 00000000..82a4ca3c
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-paint-roller.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-password-reset.svg b/ux/public/_assets/icons/fluent-password-reset.svg
new file mode 100644
index 00000000..865f0245
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-password-reset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-pencil-drawing.svg b/ux/public/_assets/icons/fluent-pencil-drawing.svg
new file mode 100644
index 00000000..fdb425c7
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-pencil-drawing.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-people.svg b/ux/public/_assets/icons/fluent-people.svg
new file mode 100644
index 00000000..84563530
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-people.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-plus-plus.svg b/ux/public/_assets/icons/fluent-plus-plus.svg
new file mode 100644
index 00000000..03d530ac
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-plus-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-processor.svg b/ux/public/_assets/icons/fluent-processor.svg
new file mode 100644
index 00000000..64b4becd
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-processor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-protect.svg b/ux/public/_assets/icons/fluent-protect.svg
new file mode 100644
index 00000000..84c0bf67
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-protect.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-rest-api.svg b/ux/public/_assets/icons/fluent-rest-api.svg
new file mode 100644
index 00000000..5afb29d5
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-rest-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-rich-text-converter.svg b/ux/public/_assets/icons/fluent-rich-text-converter.svg
new file mode 100644
index 00000000..73383655
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-rich-text-converter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-security-lock.svg b/ux/public/_assets/icons/fluent-security-lock.svg
new file mode 100644
index 00000000..b45228e4
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-security-lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-security-ssl.svg b/ux/public/_assets/icons/fluent-security-ssl.svg
new file mode 100644
index 00000000..e0c7dcbc
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-security-ssl.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-shutdown.svg b/ux/public/_assets/icons/fluent-shutdown.svg
new file mode 100644
index 00000000..5c384dff
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-shutdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-ssd.svg b/ux/public/_assets/icons/fluent-ssd.svg
new file mode 100644
index 00000000..43e1cb9d
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-ssd.svg
@@ -0,0 +1,76 @@
+
diff --git a/ux/public/_assets/icons/fluent-swiss-army-knife.svg b/ux/public/_assets/icons/fluent-swiss-army-knife.svg
new file mode 100644
index 00000000..1087649b
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-swiss-army-knife.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-tree-structure.svg b/ux/public/_assets/icons/fluent-tree-structure.svg
new file mode 100644
index 00000000..835a1f47
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-tree-structure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-web.svg b/ux/public/_assets/icons/fluent-web.svg
new file mode 100644
index 00000000..5f8ae0f5
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-web.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/fluent-windsock.svg b/ux/public/_assets/icons/fluent-windsock.svg
new file mode 100644
index 00000000..eef7c8db
--- /dev/null
+++ b/ux/public/_assets/icons/fluent-windsock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-3d-touch.svg b/ux/public/_assets/icons/ultraviolet-3d-touch.svg
new file mode 100644
index 00000000..1e530730
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-3d-touch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-access.svg b/ux/public/_assets/icons/ultraviolet-access.svg
new file mode 100644
index 00000000..8dd18aa1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-access.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-add-image.svg b/ux/public/_assets/icons/ultraviolet-add-image.svg
new file mode 100644
index 00000000..47caa6d0
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-add-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-address.svg b/ux/public/_assets/icons/ultraviolet-address.svg
new file mode 100644
index 00000000..1d61c96a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-address.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-administrator-male.svg b/ux/public/_assets/icons/ultraviolet-administrator-male.svg
new file mode 100644
index 00000000..42bd1870
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-administrator-male.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-advance.svg b/ux/public/_assets/icons/ultraviolet-advance.svg
new file mode 100644
index 00000000..8c48405a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-advance.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-amazon-web-services.svg b/ux/public/_assets/icons/ultraviolet-amazon-web-services.svg
new file mode 100644
index 00000000..5ebd14e9
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-amazon-web-services.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-amazon.svg b/ux/public/_assets/icons/ultraviolet-amazon.svg
new file mode 100644
index 00000000..798a682c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-amazon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-api.svg b/ux/public/_assets/icons/ultraviolet-api.svg
new file mode 100644
index 00000000..03ec242e
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-apple-logo.svg b/ux/public/_assets/icons/ultraviolet-apple-logo.svg
new file mode 100644
index 00000000..b243ef49
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-apple-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-apply.svg b/ux/public/_assets/icons/ultraviolet-apply.svg
new file mode 100644
index 00000000..75c8c543
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-apply.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-archive-folder.svg b/ux/public/_assets/icons/ultraviolet-archive-folder.svg
new file mode 100644
index 00000000..4a25278c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-archive-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-automation.svg b/ux/public/_assets/icons/ultraviolet-automation.svg
new file mode 100644
index 00000000..d75f0119
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-automation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-azure.svg b/ux/public/_assets/icons/ultraviolet-azure.svg
new file mode 100644
index 00000000..250e2d79
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-azure.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-bot.svg b/ux/public/_assets/icons/ultraviolet-bot.svg
new file mode 100644
index 00000000..060f66de
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-bot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-breakable.svg b/ux/public/_assets/icons/ultraviolet-breakable.svg
new file mode 100644
index 00000000..4fc06553
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-breakable.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-building.svg b/ux/public/_assets/icons/ultraviolet-building.svg
new file mode 100644
index 00000000..a8fddbc0
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-building.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-butterfly.svg b/ux/public/_assets/icons/ultraviolet-butterfly.svg
new file mode 100644
index 00000000..192912ec
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-butterfly.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-calendar-plus.svg b/ux/public/_assets/icons/ultraviolet-calendar-plus.svg
new file mode 100644
index 00000000..de817219
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-calendar-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-calendar.svg b/ux/public/_assets/icons/ultraviolet-calendar.svg
new file mode 100644
index 00000000..ba53ca89
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-calendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-chat.svg b/ux/public/_assets/icons/ultraviolet-chat.svg
new file mode 100644
index 00000000..3a09f7a7
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-chat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-chevron-right.svg b/ux/public/_assets/icons/ultraviolet-chevron-right.svg
new file mode 100644
index 00000000..c06de194
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-chevron-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-clock.svg b/ux/public/_assets/icons/ultraviolet-clock.svg
new file mode 100644
index 00000000..bb75f4c8
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-clock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-close-pane.svg b/ux/public/_assets/icons/ultraviolet-close-pane.svg
new file mode 100644
index 00000000..72c2f0e0
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-close-pane.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-cloud-checked.svg b/ux/public/_assets/icons/ultraviolet-cloud-checked.svg
new file mode 100644
index 00000000..c8714a69
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-cloud-checked.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-code-file.svg b/ux/public/_assets/icons/ultraviolet-code-file.svg
new file mode 100644
index 00000000..022237dc
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-code-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-code-fork.svg b/ux/public/_assets/icons/ultraviolet-code-fork.svg
new file mode 100644
index 00000000..7b029a67
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-code-fork.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-comments.svg b/ux/public/_assets/icons/ultraviolet-comments.svg
new file mode 100644
index 00000000..af1119f1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-comments.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-computer.svg b/ux/public/_assets/icons/ultraviolet-computer.svg
new file mode 100644
index 00000000..9061692d
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-computer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-contact.svg b/ux/public/_assets/icons/ultraviolet-contact.svg
new file mode 100644
index 00000000..209bec4b
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-contact.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-cookies.svg b/ux/public/_assets/icons/ultraviolet-cookies.svg
new file mode 100644
index 00000000..c4a81e82
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-cookies.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-copyright.svg b/ux/public/_assets/icons/ultraviolet-copyright.svg
new file mode 100644
index 00000000..06796307
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-copyright.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-css.svg b/ux/public/_assets/icons/ultraviolet-css.svg
new file mode 100644
index 00000000..7f5213a3
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-css.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-curly-arrow.svg b/ux/public/_assets/icons/ultraviolet-curly-arrow.svg
new file mode 100644
index 00000000..157530fc
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-curly-arrow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-customer.svg b/ux/public/_assets/icons/ultraviolet-customer.svg
new file mode 100644
index 00000000..11bada10
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-customer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-data-protection.svg b/ux/public/_assets/icons/ultraviolet-data-protection.svg
new file mode 100644
index 00000000..525cbe37
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-data-protection.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-database-daily-export.svg b/ux/public/_assets/icons/ultraviolet-database-daily-export.svg
new file mode 100644
index 00000000..5e3ed815
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-database-daily-export.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-database-daily-import.svg b/ux/public/_assets/icons/ultraviolet-database-daily-import.svg
new file mode 100644
index 00000000..82225d63
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-database-daily-import.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-database.svg b/ux/public/_assets/icons/ultraviolet-database.svg
new file mode 100644
index 00000000..e6183dab
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-database.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-denied.svg b/ux/public/_assets/icons/ultraviolet-denied.svg
new file mode 100644
index 00000000..83e809f4
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-denied.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-discord.svg b/ux/public/_assets/icons/ultraviolet-discord.svg
new file mode 100644
index 00000000..5f123ce8
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-discord.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-discussion-forum.svg b/ux/public/_assets/icons/ultraviolet-discussion-forum.svg
new file mode 100644
index 00000000..59990334
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-discussion-forum.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-dns.svg b/ux/public/_assets/icons/ultraviolet-dns.svg
new file mode 100644
index 00000000..bc20cb42
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-dns.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-do-not-touch.svg b/ux/public/_assets/icons/ultraviolet-do-not-touch.svg
new file mode 100644
index 00000000..aae9b6e3
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-do-not-touch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-docker-container.svg b/ux/public/_assets/icons/ultraviolet-docker-container.svg
new file mode 100644
index 00000000..3820de09
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-docker-container.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg b/ux/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg
new file mode 100644
index 00000000..1b523456
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-double-right.svg b/ux/public/_assets/icons/ultraviolet-double-right.svg
new file mode 100644
index 00000000..57f637d4
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-double-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-download-from-cloud.svg b/ux/public/_assets/icons/ultraviolet-download-from-cloud.svg
new file mode 100644
index 00000000..9d506238
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-download-from-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-downloads.svg b/ux/public/_assets/icons/ultraviolet-downloads.svg
new file mode 100644
index 00000000..e78ac223
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-downloads.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-dropbox.svg b/ux/public/_assets/icons/ultraviolet-dropbox.svg
new file mode 100644
index 00000000..fd9d5205
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-dropbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-email-open.svg b/ux/public/_assets/icons/ultraviolet-email-open.svg
new file mode 100644
index 00000000..6cc6b6db
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-email-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-email.svg b/ux/public/_assets/icons/ultraviolet-email.svg
new file mode 100644
index 00000000..ccb35a50
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-email.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-enter.svg b/ux/public/_assets/icons/ultraviolet-enter.svg
new file mode 100644
index 00000000..d46d59c5
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-enter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-envelope.svg b/ux/public/_assets/icons/ultraviolet-envelope.svg
new file mode 100644
index 00000000..93a65aaa
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-envelope.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-ethernet-off.svg b/ux/public/_assets/icons/ultraviolet-ethernet-off.svg
new file mode 100644
index 00000000..c4dc8023
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-ethernet-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-exit.svg b/ux/public/_assets/icons/ultraviolet-exit.svg
new file mode 100644
index 00000000..e2d259d7
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-exit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-expired.svg b/ux/public/_assets/icons/ultraviolet-expired.svg
new file mode 100644
index 00000000..7088ca8a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-expired.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-explosion.svg b/ux/public/_assets/icons/ultraviolet-explosion.svg
new file mode 100644
index 00000000..db8b58d2
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-explosion.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-facebook.svg b/ux/public/_assets/icons/ultraviolet-facebook.svg
new file mode 100644
index 00000000..d9aad53c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-facebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-fill-color.svg b/ux/public/_assets/icons/ultraviolet-fill-color.svg
new file mode 100644
index 00000000..1a84ac22
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-fill-color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-filtration.svg b/ux/public/_assets/icons/ultraviolet-filtration.svg
new file mode 100644
index 00000000..2f33d9ca
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-filtration.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-fingerprint-scan.svg b/ux/public/_assets/icons/ultraviolet-fingerprint-scan.svg
new file mode 100644
index 00000000..16d33d1b
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-fingerprint-scan.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-firewall.svg b/ux/public/_assets/icons/ultraviolet-firewall.svg
new file mode 100644
index 00000000..fb665978
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-firewall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-flag-filled.svg b/ux/public/_assets/icons/ultraviolet-flag-filled.svg
new file mode 100644
index 00000000..ce3adc90
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-flag-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-full-image.svg b/ux/public/_assets/icons/ultraviolet-full-image.svg
new file mode 100644
index 00000000..c573a148
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-full-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-future.svg b/ux/public/_assets/icons/ultraviolet-future.svg
new file mode 100644
index 00000000..f0b59b7e
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-future.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-gender.svg b/ux/public/_assets/icons/ultraviolet-gender.svg
new file mode 100644
index 00000000..b5b04f0a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-gender.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-geography.svg b/ux/public/_assets/icons/ultraviolet-geography.svg
new file mode 100644
index 00000000..3cee54e1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-geography.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-git.svg b/ux/public/_assets/icons/ultraviolet-git.svg
new file mode 100644
index 00000000..2310ddb1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-git.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-github.svg b/ux/public/_assets/icons/ultraviolet-github.svg
new file mode 100644
index 00000000..25ae025f
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-github.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-google-drive.svg b/ux/public/_assets/icons/ultraviolet-google-drive.svg
new file mode 100644
index 00000000..63dd00a5
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-google-drive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-google-maps.svg b/ux/public/_assets/icons/ultraviolet-google-maps.svg
new file mode 100644
index 00000000..4ed2e247
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-google-maps.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-google-presentation.svg b/ux/public/_assets/icons/ultraviolet-google-presentation.svg
new file mode 100644
index 00000000..5fb85f70
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-google-presentation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-google.svg b/ux/public/_assets/icons/ultraviolet-google.svg
new file mode 100644
index 00000000..bac7ed4b
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-google.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-grand-master-key.svg b/ux/public/_assets/icons/ultraviolet-grand-master-key.svg
new file mode 100644
index 00000000..31bcefdc
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-grand-master-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-hdd.svg b/ux/public/_assets/icons/ultraviolet-hdd.svg
new file mode 100644
index 00000000..9df08564
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-hdd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-heart-outline.svg b/ux/public/_assets/icons/ultraviolet-heart-outline.svg
new file mode 100644
index 00000000..1fddb085
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-heart-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-hips.svg b/ux/public/_assets/icons/ultraviolet-hips.svg
new file mode 100644
index 00000000..b541fe5f
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-hips.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-historical.svg b/ux/public/_assets/icons/ultraviolet-historical.svg
new file mode 100644
index 00000000..48b04f0c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-historical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-home.svg b/ux/public/_assets/icons/ultraviolet-home.svg
new file mode 100644
index 00000000..71f5b710
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-html.svg b/ux/public/_assets/icons/ultraviolet-html.svg
new file mode 100644
index 00000000..5d080f4a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-html.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-inbox.svg b/ux/public/_assets/icons/ultraviolet-inbox.svg
new file mode 100644
index 00000000..de260513
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-inbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-index.svg b/ux/public/_assets/icons/ultraviolet-index.svg
new file mode 100644
index 00000000..3fb06c2f
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-index.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-info-popup.svg b/ux/public/_assets/icons/ultraviolet-info-popup.svg
new file mode 100644
index 00000000..959d4c39
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-info-popup.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-information.svg b/ux/public/_assets/icons/ultraviolet-information.svg
new file mode 100644
index 00000000..9e6920ff
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-information.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-internet.svg b/ux/public/_assets/icons/ultraviolet-internet.svg
new file mode 100644
index 00000000..c31e8b61
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-internet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-join.svg b/ux/public/_assets/icons/ultraviolet-join.svg
new file mode 100644
index 00000000..8f49222b
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-join.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-key.svg b/ux/public/_assets/icons/ultraviolet-key.svg
new file mode 100644
index 00000000..3fa8b038
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-light-on.svg b/ux/public/_assets/icons/ultraviolet-light-on.svg
new file mode 100644
index 00000000..6a9270be
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-light-on.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-lightning-bolt.svg b/ux/public/_assets/icons/ultraviolet-lightning-bolt.svg
new file mode 100644
index 00000000..48591199
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-lightning-bolt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-linux.svg b/ux/public/_assets/icons/ultraviolet-linux.svg
new file mode 100644
index 00000000..b2f09092
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-linux.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-markdown.svg b/ux/public/_assets/icons/ultraviolet-markdown.svg
new file mode 100644
index 00000000..17b1c789
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-markdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-matches.svg b/ux/public/_assets/icons/ultraviolet-matches.svg
new file mode 100644
index 00000000..25a23f83
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-matches.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-maximize-window.svg b/ux/public/_assets/icons/ultraviolet-maximize-window.svg
new file mode 100644
index 00000000..a11efd32
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-maximize-window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-memory-slot.svg b/ux/public/_assets/icons/ultraviolet-memory-slot.svg
new file mode 100644
index 00000000..3f86b754
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-memory-slot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-microchip.svg b/ux/public/_assets/icons/ultraviolet-microchip.svg
new file mode 100644
index 00000000..e687adba
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-microchip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-microsoft.svg b/ux/public/_assets/icons/ultraviolet-microsoft.svg
new file mode 100644
index 00000000..4cd92808
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-microsoft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-module.svg b/ux/public/_assets/icons/ultraviolet-module.svg
new file mode 100644
index 00000000..1c7f58b3
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-module.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-nas.svg b/ux/public/_assets/icons/ultraviolet-nas.svg
new file mode 100644
index 00000000..54ff72bf
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-nas.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg b/ux/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg
new file mode 100644
index 00000000..3361cf63
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-new-job.svg b/ux/public/_assets/icons/ultraviolet-new-job.svg
new file mode 100644
index 00000000..c8466eeb
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-new-job.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-no-access.svg b/ux/public/_assets/icons/ultraviolet-no-access.svg
new file mode 100644
index 00000000..3d6943dc
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-no-access.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-nodejs.svg b/ux/public/_assets/icons/ultraviolet-nodejs.svg
new file mode 100644
index 00000000..537674c2
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-nodejs.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-open-box.svg b/ux/public/_assets/icons/ultraviolet-open-box.svg
new file mode 100644
index 00000000..d1ea0696
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-open-box.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-opened-folder.svg b/ux/public/_assets/icons/ultraviolet-opened-folder.svg
new file mode 100644
index 00000000..87558cb3
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-opened-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-password-reset.svg b/ux/public/_assets/icons/ultraviolet-password-reset.svg
new file mode 100644
index 00000000..b8c862a2
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-password-reset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-password.svg b/ux/public/_assets/icons/ultraviolet-password.svg
new file mode 100644
index 00000000..2e6ba2e9
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-password.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-pen.svg b/ux/public/_assets/icons/ultraviolet-pen.svg
new file mode 100644
index 00000000..d5fca6a2
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-pen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-person.svg b/ux/public/_assets/icons/ultraviolet-person.svg
new file mode 100644
index 00000000..6de77337
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-person.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-polyline.svg b/ux/public/_assets/icons/ultraviolet-polyline.svg
new file mode 100644
index 00000000..aa81c445
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-polyline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-postgresql.svg b/ux/public/_assets/icons/ultraviolet-postgresql.svg
new file mode 100644
index 00000000..f55ba9cd
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-postgresql.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-print.svg b/ux/public/_assets/icons/ultraviolet-print.svg
new file mode 100644
index 00000000..bb9ff078
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-print.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-processor.svg b/ux/public/_assets/icons/ultraviolet-processor.svg
new file mode 100644
index 00000000..3849028c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-processor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-program.svg b/ux/public/_assets/icons/ultraviolet-program.svg
new file mode 100644
index 00000000..a8862c97
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-program.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-received.svg b/ux/public/_assets/icons/ultraviolet-received.svg
new file mode 100644
index 00000000..466ca480
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-received.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-renew.svg b/ux/public/_assets/icons/ultraviolet-renew.svg
new file mode 100644
index 00000000..6478f461
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-renew.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-rescan-document.svg b/ux/public/_assets/icons/ultraviolet-rescan-document.svg
new file mode 100644
index 00000000..050153b5
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-rescan-document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-resume-template.svg b/ux/public/_assets/icons/ultraviolet-resume-template.svg
new file mode 100644
index 00000000..02ca1a48
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-resume-template.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-rfid-signal.svg b/ux/public/_assets/icons/ultraviolet-rfid-signal.svg
new file mode 100644
index 00000000..853576cd
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-rfid-signal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg b/ux/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg
new file mode 100644
index 00000000..7371be28
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-run-command.svg b/ux/public/_assets/icons/ultraviolet-run-command.svg
new file mode 100644
index 00000000..78d1fcb9
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-run-command.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-scan-stock.svg b/ux/public/_assets/icons/ultraviolet-scan-stock.svg
new file mode 100644
index 00000000..9de31b21
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-scan-stock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-search.svg b/ux/public/_assets/icons/ultraviolet-search.svg
new file mode 100644
index 00000000..40e6b346
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-secure.svg b/ux/public/_assets/icons/ultraviolet-secure.svg
new file mode 100644
index 00000000..ebc533df
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-secure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-security-configuration.svg b/ux/public/_assets/icons/ultraviolet-security-configuration.svg
new file mode 100644
index 00000000..df121138
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-security-configuration.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-security-ssl.svg b/ux/public/_assets/icons/ultraviolet-security-ssl.svg
new file mode 100644
index 00000000..fcb674e1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-security-ssl.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-select-all.svg b/ux/public/_assets/icons/ultraviolet-select-all.svg
new file mode 100644
index 00000000..975c028d
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-select-all.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-server.svg b/ux/public/_assets/icons/ultraviolet-server.svg
new file mode 100644
index 00000000..3b152857
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-server.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-share.svg b/ux/public/_assets/icons/ultraviolet-share.svg
new file mode 100644
index 00000000..e6b99c91
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-share.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-shipping-container.svg b/ux/public/_assets/icons/ultraviolet-shipping-container.svg
new file mode 100644
index 00000000..df36ecdd
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-shipping-container.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-skype.svg b/ux/public/_assets/icons/ultraviolet-skype.svg
new file mode 100644
index 00000000..f0574f15
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-skype.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-slack.svg b/ux/public/_assets/icons/ultraviolet-slack.svg
new file mode 100644
index 00000000..a441c3ab
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-slack.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-ssd.svg b/ux/public/_assets/icons/ultraviolet-ssd.svg
new file mode 100644
index 00000000..62f41e23
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-ssd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-star-half-empty.svg b/ux/public/_assets/icons/ultraviolet-star-half-empty.svg
new file mode 100644
index 00000000..adb87e08
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-star-half-empty.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-starfish.svg b/ux/public/_assets/icons/ultraviolet-starfish.svg
new file mode 100644
index 00000000..356306a6
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-starfish.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-summertime.svg b/ux/public/_assets/icons/ultraviolet-summertime.svg
new file mode 100644
index 00000000..1a44be56
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-summertime.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-swipe-right.svg b/ux/public/_assets/icons/ultraviolet-swipe-right.svg
new file mode 100644
index 00000000..85cbab6f
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-swipe-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-symlink-directory.svg b/ux/public/_assets/icons/ultraviolet-symlink-directory.svg
new file mode 100644
index 00000000..a0ca1cec
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-symlink-directory.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-synchronize.svg b/ux/public/_assets/icons/ultraviolet-synchronize.svg
new file mode 100644
index 00000000..8d3dd2db
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-synchronize.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-team.svg b/ux/public/_assets/icons/ultraviolet-team.svg
new file mode 100644
index 00000000..9c4a0b26
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-team.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-test-account.svg b/ux/public/_assets/icons/ultraviolet-test-account.svg
new file mode 100644
index 00000000..a5a30015
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-test-account.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-test-passed.svg b/ux/public/_assets/icons/ultraviolet-test-passed.svg
new file mode 100644
index 00000000..08ec3a5d
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-test-passed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-this-way-up.svg b/ux/public/_assets/icons/ultraviolet-this-way-up.svg
new file mode 100644
index 00000000..c2848e21
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-this-way-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-ticket.svg b/ux/public/_assets/icons/ultraviolet-ticket.svg
new file mode 100644
index 00000000..53c95eb0
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-ticket.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-timer.svg b/ux/public/_assets/icons/ultraviolet-timer.svg
new file mode 100644
index 00000000..6f7d514a
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-timer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-timezone.svg b/ux/public/_assets/icons/ultraviolet-timezone.svg
new file mode 100644
index 00000000..daece1e1
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-timezone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-todo-list.svg b/ux/public/_assets/icons/ultraviolet-todo-list.svg
new file mode 100644
index 00000000..a6ed7200
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-todo-list.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-translation.svg b/ux/public/_assets/icons/ultraviolet-translation.svg
new file mode 100644
index 00000000..80051f10
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-translation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-trash.svg b/ux/public/_assets/icons/ultraviolet-trash.svg
new file mode 100644
index 00000000..9b26a12d
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-tune.svg b/ux/public/_assets/icons/ultraviolet-tune.svg
new file mode 100644
index 00000000..8fd6f1e2
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-tune.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-twitch.svg b/ux/public/_assets/icons/ultraviolet-twitch.svg
new file mode 100644
index 00000000..263ee07b
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-twitch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-typewriter-with-paper.svg b/ux/public/_assets/icons/ultraviolet-typewriter-with-paper.svg
new file mode 100644
index 00000000..6c21db45
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-typewriter-with-paper.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-ubuntu.svg b/ux/public/_assets/icons/ultraviolet-ubuntu.svg
new file mode 100644
index 00000000..5bc519a4
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-ubuntu.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/ux/public/_assets/icons/ultraviolet-unfriend.svg b/ux/public/_assets/icons/ultraviolet-unfriend.svg
new file mode 100644
index 00000000..13c1f223
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-unfriend.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-unit.svg b/ux/public/_assets/icons/ultraviolet-unit.svg
new file mode 100644
index 00000000..20e3c8cb
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-unit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-unknown-status.svg b/ux/public/_assets/icons/ultraviolet-unknown-status.svg
new file mode 100644
index 00000000..56afba76
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-unknown-status.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-upload-to-ftp.svg b/ux/public/_assets/icons/ultraviolet-upload-to-ftp.svg
new file mode 100644
index 00000000..bf5a810f
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-upload-to-ftp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg b/ux/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg
new file mode 100644
index 00000000..3cdfa9c9
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg b/ux/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg
new file mode 100644
index 00000000..289c3a9c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-validation.svg b/ux/public/_assets/icons/ultraviolet-validation.svg
new file mode 100644
index 00000000..bc9f1a2c
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-validation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-washing-machine.svg b/ux/public/_assets/icons/ultraviolet-washing-machine.svg
new file mode 100644
index 00000000..c8cfdaa3
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-washing-machine.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-widescreen.svg b/ux/public/_assets/icons/ultraviolet-widescreen.svg
new file mode 100644
index 00000000..c5650fb9
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-widescreen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/icons/ultraviolet-windows8.svg b/ux/public/_assets/icons/ultraviolet-windows8.svg
new file mode 100644
index 00000000..aea92c65
--- /dev/null
+++ b/ux/public/_assets/icons/ultraviolet-windows8.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/ux/public/_assets/logo-wikijs.svg b/ux/public/_assets/logo-wikijs.svg
new file mode 100644
index 00000000..52c4a790
--- /dev/null
+++ b/ux/public/_assets/logo-wikijs.svg
@@ -0,0 +1,119 @@
+
+
+
diff --git a/ux/public/_assets/storage/azure.jpg b/ux/public/_assets/storage/azure.jpg
new file mode 100644
index 00000000..84c73ea0
Binary files /dev/null and b/ux/public/_assets/storage/azure.jpg differ
diff --git a/ux/public/_assets/storage/database.jpg b/ux/public/_assets/storage/database.jpg
new file mode 100644
index 00000000..e695a144
Binary files /dev/null and b/ux/public/_assets/storage/database.jpg differ
diff --git a/ux/public/_assets/storage/disk.jpg b/ux/public/_assets/storage/disk.jpg
new file mode 100644
index 00000000..09c9b4a0
Binary files /dev/null and b/ux/public/_assets/storage/disk.jpg differ
diff --git a/ux/public/_assets/storage/gcs.jpg b/ux/public/_assets/storage/gcs.jpg
new file mode 100644
index 00000000..7871d509
Binary files /dev/null and b/ux/public/_assets/storage/gcs.jpg differ
diff --git a/ux/public/_assets/storage/git.jpg b/ux/public/_assets/storage/git.jpg
new file mode 100644
index 00000000..214b49c8
Binary files /dev/null and b/ux/public/_assets/storage/git.jpg differ
diff --git a/ux/public/_assets/storage/github.jpg b/ux/public/_assets/storage/github.jpg
new file mode 100644
index 00000000..69754b3f
Binary files /dev/null and b/ux/public/_assets/storage/github.jpg differ
diff --git a/ux/public/_assets/storage/s3.jpg b/ux/public/_assets/storage/s3.jpg
new file mode 100644
index 00000000..3d599e59
Binary files /dev/null and b/ux/public/_assets/storage/s3.jpg differ
diff --git a/ux/public/_assets/storage/ssh.jpg b/ux/public/_assets/storage/ssh.jpg
new file mode 100644
index 00000000..6269ce88
Binary files /dev/null and b/ux/public/_assets/storage/ssh.jpg differ
diff --git a/ux/public/favicon.ico b/ux/public/favicon.ico
new file mode 100644
index 00000000..ae7bbdb7
Binary files /dev/null and b/ux/public/favicon.ico differ
diff --git a/ux/public/icons/favicon-128x128.png b/ux/public/icons/favicon-128x128.png
new file mode 100644
index 00000000..14011761
Binary files /dev/null and b/ux/public/icons/favicon-128x128.png differ
diff --git a/ux/public/icons/favicon-16x16.png b/ux/public/icons/favicon-16x16.png
new file mode 100644
index 00000000..679063a3
Binary files /dev/null and b/ux/public/icons/favicon-16x16.png differ
diff --git a/ux/public/icons/favicon-32x32.png b/ux/public/icons/favicon-32x32.png
new file mode 100644
index 00000000..fd1fbc6f
Binary files /dev/null and b/ux/public/icons/favicon-32x32.png differ
diff --git a/ux/public/icons/favicon-96x96.png b/ux/public/icons/favicon-96x96.png
new file mode 100644
index 00000000..e93b80a0
Binary files /dev/null and b/ux/public/icons/favicon-96x96.png differ
diff --git a/ux/quasar.config.js b/ux/quasar.config.js
new file mode 100644
index 00000000..a811fdc7
--- /dev/null
+++ b/ux/quasar.config.js
@@ -0,0 +1,235 @@
+/* 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')
+
+module.exports = configure(function (/* ctx */) {
+ 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',
+ 'i18n'
+ ],
+
+ // 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',
+ '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: 'node16'
+ },
+
+ vueRouterMode: 'history', // available values: 'hash', 'history'
+ // vueRouterBase,
+ // vueDevtools,
+ vueOptionsAPI: true,
+
+ rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
+
+ // publicPath: '/',
+ // analyze: true,
+ // env: {},
+ // rawDefine: {}
+ // ignorePublicFolder: true,
+ // minify: false,
+ // polyfillModulePreload: true,
+ // distDir
+
+ // extendViteConf (viteConf) {},
+ // viteVuePluginOptions: {},
+
+ vitePlugins: [
+ ['@intlify/vite-plugin-vue-i18n', {
+ // 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/**')
+ }]
+ ]
+ },
+
+ // Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
+ devServer: {
+ // https: true
+ open: false, // opens browser window automatically
+ proxy: {
+ '/_graphql': 'http://localhost:3000/graphql'
+ }
+ },
+
+ // 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'
+ },
+ 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: 'fontawesome-v6', // 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'
+ }
+ }
+ }
+})
diff --git a/ux/src/App.vue b/ux/src/App.vue
new file mode 100644
index 00000000..03f493d9
--- /dev/null
+++ b/ux/src/App.vue
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/ux/src/assets/quasar-logo-vertical.svg b/ux/src/assets/quasar-logo-vertical.svg
new file mode 100644
index 00000000..82108310
--- /dev/null
+++ b/ux/src/assets/quasar-logo-vertical.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/ux/src/boot/.gitkeep b/ux/src/boot/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/ux/src/boot/apollo.js b/ux/src/boot/apollo.js
new file mode 100644
index 00000000..4779377b
--- /dev/null
+++ b/ux/src/boot/apollo.js
@@ -0,0 +1,56 @@
+import { boot } from 'quasar/wrappers'
+import { createApolloProvider } from '@vue/apollo-option'
+import { ApolloClient, InMemoryCache } from '@apollo/client/core'
+import { setContext } from '@apollo/client/link/context'
+import { createUploadLink } from 'apollo-upload-client'
+
+export default boot(({ app }) => {
+ // Authentication Link
+ const authLink = setContext(async (req, { headers }) => {
+ const token = 'test' // await window.auth0Client.getTokenSilently()
+ return {
+ headers: {
+ ...headers,
+ Authorization: token ? `Bearer ${token}` : ''
+ }
+ }
+ })
+
+ // Upload / HTTP Link
+ const uploadLink = createUploadLink({
+ uri () {
+ return '/_graphql'
+ }
+ })
+
+ // Cache
+ const cache = new InMemoryCache()
+
+ if (typeof window !== 'undefined') {
+ const state = window.__APOLLO_STATE__
+ if (state) {
+ cache.restore(state.defaultClient)
+ }
+ }
+
+ // Client
+ const client = new ApolloClient({
+ cache,
+ link: authLink.concat(uploadLink),
+ credentials: 'omit',
+ ssrForceFetchDelay: 100
+ })
+
+ // Init Vue Apollo
+ const apolloProvider = createApolloProvider({
+ defaultClient: client
+ })
+
+ if (import.meta.env.SSR) {
+ global.APOLLO_CLIENT = client
+ } else {
+ window.APOLLO_CLIENT = client
+ }
+
+ app.use(apolloProvider)
+})
diff --git a/ux/src/boot/i18n.js b/ux/src/boot/i18n.js
new file mode 100644
index 00000000..fe708aff
--- /dev/null
+++ b/ux/src/boot/i18n.js
@@ -0,0 +1,15 @@
+import { boot } from 'quasar/wrappers'
+import { createI18n } from 'vue-i18n'
+import messages from 'src/i18n'
+
+export default boot(({ app }) => {
+ const i18n = createI18n({
+ legacy: false,
+ locale: 'en-US',
+ allowComposition: true,
+ messages
+ })
+
+ // Set i18n instance on app
+ app.use(i18n)
+})
diff --git a/ux/src/components/AccountMenu.vue b/ux/src/components/AccountMenu.vue
new file mode 100644
index 00000000..5ee7e1ff
--- /dev/null
+++ b/ux/src/components/AccountMenu.vue
@@ -0,0 +1,44 @@
+
+q-btn.q-ml-md(flat, round, dense, color='grey')
+ q-icon(v-if='!user.picture', name='las la-user-circle')
+ q-avatar(v-else)
+ img(:src='user.picture')
+ q-menu(auto-close)
+ q-card(flat, style='width: 300px;', :dark='false')
+ q-card-section(align='center')
+ .text-subtitle1.text-grey-7 {{user.name}}
+ .text-caption.text-grey-8 {{user.email}}
+ q-separator(:dark='false')
+ q-card-actions(align='center')
+ q-btn(
+ flat
+ label='Profile'
+ icon='las la-user-alt'
+ color='primary'
+ to='/p'
+ no-caps
+ )
+ q-btn(flat
+ label='Logout'
+ icon='las la-sign-out-alt'
+ color='red'
+ href='/logout'
+ no-caps
+ )
+ q-tooltip Account
+
+
+
diff --git a/ux/src/components/BlockVideoPlayer.vue b/ux/src/components/BlockVideoPlayer.vue
new file mode 100644
index 00000000..e3a58c97
--- /dev/null
+++ b/ux/src/components/BlockVideoPlayer.vue
@@ -0,0 +1,25 @@
+
+div(style='max-width: 760px;')
+ Player(controls)
+ Youtube(video-id='DyTCOwB0DVw', :autoplay='0')
+ //- DefaultUi(noControls)
+ //- DefaultControls(
+ //- hideOnMouseLeave
+ //- :activeDuration='2000'
+ //- )
+
+
+
diff --git a/ux/src/components/BlueprintIcon.vue b/ux/src/components/BlueprintIcon.vue
new file mode 100644
index 00000000..882d5116
--- /dev/null
+++ b/ux/src/components/BlueprintIcon.vue
@@ -0,0 +1,69 @@
+
+q-item-section(avatar)
+ q-avatar.blueprint-icon(
+ :color='avatarBgColor'
+ :text-color='avatarTextColor'
+ font-size='14px'
+ rounded
+ :style='hueRotate !== 0 ? `filter: hue-rotate(` + hueRotate + `deg)` : ``'
+ )
+ q-badge(
+ v-if='indicatorDot'
+ rounded
+ :color='indicatorDot'
+ floating
+ )
+ q-tooltip(v-if='indicatorText') {{indicatorText}}
+ q-icon(
+ v-if='!textMode'
+ :name='`img:/_assets/icons/ultraviolet-` + icon + `.svg`'
+ size='sm'
+ )
+ span.text-uppercase(v-else) {{text}}
+
+
+
diff --git a/ux/src/components/EditorChannel.vue b/ux/src/components/EditorChannel.vue
new file mode 100644
index 00000000..67769353
--- /dev/null
+++ b/ux/src/components/EditorChannel.vue
@@ -0,0 +1,81 @@
+
+.channel-container
+ .channel-sidebar
+ q-card.rounded-borders.bg-dark
+ q-list(
+ padding
+ dark
+ )
+ q-item(
+ v-for='ch of channels'
+ :key='ch.id'
+ active-class='bg-primary text-white'
+ :active='selectedChannel === ch.id'
+ @click='selectedChannel = ch.id'
+ clickable
+ )
+ q-item-section(side)
+ q-icon(name='las la-grip-lines')
+ q-item-section
+ q-item-label
+ span #
+ strong {{ch.name}}
+ q-item-label(caption) {{ch.description}}
+ //- q-item-section(side)
+ //- q-badge(color='accent', label='0')
+ q-btn.q-mt-sm.full-width(
+ color='primary'
+ icon='las la-plus'
+ :label='$t(`Add Channel`)'
+ no-caps
+ )
+ .channel-main
+
+
+
+
+
diff --git a/ux/src/components/EditorMarkdown.vue b/ux/src/components/EditorMarkdown.vue
new file mode 100644
index 00000000..91f7f5be
--- /dev/null
+++ b/ux/src/components/EditorMarkdown.vue
@@ -0,0 +1,13 @@
+
+.quill-container
+
+
+
diff --git a/ux/src/components/EditorWysiwyg.vue b/ux/src/components/EditorWysiwyg.vue
new file mode 100644
index 00000000..f87e0cb5
--- /dev/null
+++ b/ux/src/components/EditorWysiwyg.vue
@@ -0,0 +1,865 @@
+
+.wysiwyg-container
+ .wysiwyg-toolbar(v-if='editor')
+ template(v-for='menuItem of menuBar')
+ q-separator.q-mx-xs(
+ v-if='menuItem.type === `divider`'
+ vertical
+ )
+ q-btn(
+ v-else-if='menuItem.type === `dropdown`'
+ :key='menuItem.key'
+ flat
+ :icon='menuItem.icon'
+ padding='xs'
+ :class='{ "is-active": menuItem.isActive && menuItem.isActive() }'
+ :color='menuItem.isActive && menuItem.isActive() ? `primary` : `grey-10`'
+ :aria-label='menuItem.title'
+ split
+ :disabled='menuItem.disabled && menuItem.disabled()'
+ )
+ q-menu
+ q-list(
+ dense
+ padding
+ )
+ template(v-for='child of menuItem.children')
+ q-separator.q-my-sm(v-if='child.type === `divider`')
+ q-item(
+ v-else
+ :key='menuItem.key + `-` + child.key'
+ clickable
+ @click='child.action'
+ :active='child.isActive && child.isActive()'
+ active-class='text-primary'
+ :disabled='child.disabled && child.disabled()'
+ )
+ q-item-section(side)
+ q-icon(
+ :name='child.icon'
+ :color='child.color'
+ )
+ q-item-section
+ q-item-label {{child.title}}
+ q-btn-group(
+ v-else-if='menuItem.type === `btngroup`'
+ :key='menuItem.key'
+ flat
+ )
+ q-btn(
+ v-for='child of menuItem.children'
+ :key='menuItem.key + `-` + child.key'
+ flat
+ :icon='child.icon'
+ padding='xs'
+ :class='{ "is-active": child.isActive && child.isActive() }'
+ :color='child.isActive && child.isActive() ? `primary` : `grey-10`'
+ @click='child.action'
+ :aria-label='child.title'
+ :disabled='menuItem.disabled && menuItem.disabled()'
+ )
+ q-btn(
+ v-else
+ flat
+ :icon='menuItem.icon'
+ padding='xs'
+ :class='{ "is-active": menuItem.isActive && menuItem.isActive() }'
+ :color='menuItem.isActive && menuItem.isActive() ? `primary` : `grey-10`'
+ @click='menuItem.action'
+ :aria-label='menuItem.title'
+ :disabled='menuItem.disabled && menuItem.disabled()'
+ )
+ q-space
+ q-btn(
+ size='sm'
+ unelevated
+ color='red'
+ label='Test'
+ @click='snapshot'
+ )
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ editor-content(:editor='editor')
+
+
+
+
+
diff --git a/ux/src/components/GithubSetupInstallDialog.vue b/ux/src/components/GithubSetupInstallDialog.vue
new file mode 100644
index 00000000..812d69a0
--- /dev/null
+++ b/ux/src/components/GithubSetupInstallDialog.vue
@@ -0,0 +1,46 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide', persistent)
+ q-card(style='min-width: 350px; max-width: 550px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/ultraviolet-github.svg', left, size='sm')
+ span {{$t(`admin.storage.githubSetupInstallApp`)}}
+ q-card-section
+ .text-body2 {{$t(`admin.storage.githubSetupInstallAppInfo`)}}
+ .text-body2.q-mt-md: strong.text-deep-orange {{$t('admin.storage.githubSetupInstallAppSelect')}}
+ .text-body2.q-mt-md {{$t(`admin.storage.githubSetupInstallAppReturn`)}}
+ q-card-actions.card-actions
+ q-space
+ q-btn(
+ unelevated
+ :label='$t(`admin.storage.githubSetupContinue`)'
+ color='positive'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/ux/src/components/GroupCreateDialog.vue b/ux/src/components/GroupCreateDialog.vue
new file mode 100644
index 00000000..cd11b627
--- /dev/null
+++ b/ux/src/components/GroupCreateDialog.vue
@@ -0,0 +1,111 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
+ span {{$t(`admin.groups.create`)}}
+ q-form.q-py-sm(ref='createGroupForm', @submit='create')
+ q-item
+ blueprint-icon(icon='team')
+ q-item-section
+ q-input(
+ outlined
+ v-model='groupName'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.groups.nameMissing'),
+ val => /^[^<>"]+$/.test(val) || $t('admin.groups.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :label='$t(`common.field.name`)'
+ :aria-label='$t(`common.field.name`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='isLoading'
+ )
+
+
+
diff --git a/ux/src/components/GroupDeleteDialog.vue b/ux/src/components/GroupDeleteDialog.vue
new file mode 100644
index 00000000..e4b9f773
--- /dev/null
+++ b/ux/src/components/GroupDeleteDialog.vue
@@ -0,0 +1,93 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{$t(`admin.groups.delete`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='admin.groups.deleteConfirm')
+ template(#groupName)
+ strong {{group.name}}
+ .text-body2.q-mt-md
+ strong.text-negative {{$t(`admin.groups.deleteConfirmWarn`)}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/ux/src/components/GroupEditOverlay.vue b/ux/src/components/GroupEditOverlay.vue
new file mode 100644
index 00000000..b3228b3f
--- /dev/null
+++ b/ux/src/components/GroupEditOverlay.vue
@@ -0,0 +1,1138 @@
+
+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 {{group.name}}
+ q-space
+ q-btn-group(push)
+ q-btn(
+ push
+ color='grey-6'
+ text-color='white'
+ :aria-label='$t(`common.actions.refresh`)'
+ icon='las la-redo-alt'
+ @click='refresh'
+ )
+ q-tooltip(anchor='center left', self='center right') {{$t(`common.actions.refresh`)}}
+ q-btn(
+ push
+ color='white'
+ text-color='grey-7'
+ :label='$t(`common.actions.close`)'
+ icon='las la-times'
+ @click='close'
+ )
+ q-btn(
+ push
+ color='positive'
+ text-color='white'
+ :label='$t(`common.actions.save`)'
+ icon='las la-check'
+ )
+ q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
+ q-list(padding, v-show='!isLoading')
+ q-item(
+ v-for='sc of sections'
+ :key='`section-` + sc.key'
+ clickable
+ :to='{ params: { section: sc.key } }'
+ active-class='bg-primary text-white'
+ :disabled='sc.disabled'
+ )
+ q-item-section(side)
+ q-icon(:name='sc.icon', color='white')
+ q-item-section {{sc.text}}
+ q-item-section(side, v-if='sc.usersTotal')
+ q-badge(color='dark-3', :label='usersTotal')
+ q-item-section(side, v-if='sc.rulesTotal && group.rules')
+ q-badge(color='dark-3', :label='group.rules.length')
+ q-page-container
+ q-page(v-if='isLoading')
+ //- -----------------------------------------------------------------------
+ //- OVERVIEW
+ //- -----------------------------------------------------------------------
+ q-page(v-else-if='$route.params.section === `overview`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-8
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .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-section
+ q-input(
+ outlined
+ v-model='group.name'
+ dense
+ :rules=`[
+ val => /^[^<>"]+$/.test(val) || $t('admin.groups.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.groups.name`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .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-section
+ q-input(
+ outlined
+ v-model='group.redirectOnLogin'
+ dense
+ :aria-label='$t(`admin.groups.redirectOnLogin`)'
+ )
+ q-separator.q-my-sm(inset)
+ 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-section
+ q-input(
+ outlined
+ v-model='group.redirectOnFirstLogin'
+ dense
+ :aria-label='$t(`admin.groups.redirectOnLogin`)'
+ )
+ q-separator.q-my-sm(inset)
+ 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-section
+ q-input(
+ outlined
+ v-model='group.redirectOnLogout'
+ dense
+ :aria-label='$t(`admin.groups.redirectOnLogout`)'
+ )
+
+ .col-12.col-lg-4
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .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: strong {{groupId}}
+ 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: strong {{humanizeDate(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: strong {{humanizeDate(group.updatedAt)}}
+ //- -----------------------------------------------------------------------
+ //- RULES
+ //- -----------------------------------------------------------------------
+ q-page(v-else-if='$route.params.section === `rules`')
+ q-toolbar.q-pl-md(
+ :class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
+ )
+ .text-subtitle1 {{$t('admin.groups.rules')}}
+ q-space
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/groups#rules'
+ target='_blank'
+ )
+ q-btn.acrylic-btn.q-mr-sm(
+ flat
+ color='indigo'
+ icon='las la-file-export'
+ @click='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-btn(
+ unelevated
+ color='primary'
+ icon='las la-plus'
+ label='New Rule'
+ @click='newRule'
+ )
+ q-separator
+ .q-pa-md
+ q-banner(
+ v-if='!group.rules || group.rules.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
+ ) {{$t('admin.groups.rulesNone')}}
+ q-card.shadow-1.q-pb-sm(v-else)
+ q-card-section
+ .admin-groups-rule(
+ v-for='(rule, idx) of group.rules'
+ :key='rule.id'
+ )
+ .admin-groups-rule-icon(:class='getRuleModeColor(rule.mode)')
+ q-icon.cursor-pointer(
+ :name='getRuleModeIcon(rule.mode)'
+ color='white'
+ @click='rule.mode = getNextRuleMode(rule.mode)'
+ )
+ .admin-groups-rule-name
+ .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'
+ v-model='rule.name'
+ placeholder='Rule Name'
+ )
+ q-card.admin-groups-rule-card.q-mt-md(flat)
+ q-card-section.admin-groups-rule-card-permissions(:class='getRuleModeClass(rule.mode)')
+ q-select.q-mt-xs(
+ standout
+ v-model='rule.roles'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.groups.ruleSites`)'
+ :options='rules'
+ placeholder='Select permissions...'
+ option-value='permission'
+ option-label='title'
+ options-dense
+ multiple
+ use-chips
+ stack-label
+ )
+ template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(v-bind='itemProps', v-on='itemEvents')
+ q-item-section(side)
+ q-toggle(
+ :value='selected'
+ @input='toggleOption(opt)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='opt.label'
+ )
+ //- q-item-section(side, style='flex-basis: 150px;')
+ //- q-chip.text-caption(
+ //- square
+ //- color='teal'
+ //- text-color='white'
+ //- dense
+ //- ) {{opt.permission}}
+ q-item-section
+ q-item-label {{opt.title}}
+ q-item-label(caption) {{opt.hint}}
+ q-btn.acrylic-btn.q-ml-md(
+ flat
+ icon='las la-trash'
+ color='negative'
+ padding='sm sm'
+ size='md',
+ @click='deleteRule(rule.id)'
+ )
+ q-card-section(horizontal)
+ q-card-section.admin-groups-rule-card-filters
+ .text-caption Applies to...
+ q-select.q-mt-xs(
+ standout
+ v-model='rule.sites'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.groups.ruleSites`)'
+ :options='sites'
+ option-value='id'
+ option-label='title'
+ multiple
+ behavior='dialog'
+ :display-value='$tc(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
+ )
+ template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(v-bind='itemProps', v-on='itemEvents')
+ q-item-section
+ q-item-label {{opt.title}}
+ q-item-section(side)
+ q-toggle(
+ :value='selected'
+ @input='toggleOption(opt)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='opt.label'
+ )
+ q-select.q-mt-sm(
+ standout
+ v-model='rule.locales'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.groups.ruleLocales`)'
+ :options='locales'
+ option-value='code'
+ option-label='name'
+ multiple
+ behavior='dialog'
+ :display-value='$tc(`admin.groups.selectedLocales`, rule.locales.length, { count: rule.locales.length, locale: rule.locales.length === 1 ? rule.locales[0].toUpperCase() : `` })'
+ )
+ template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
+ q-item(v-bind='itemProps')
+ q-item-section
+ q-item-label {{opt.name}}
+ q-item-section(side)
+ q-toggle(
+ :model-value='selected'
+ @update:model-value='toggleOption(opt)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='opt.name'
+ )
+ q-card-section.admin-groups-rule-card-pattern
+ .text-caption Pattern
+ q-select.q-mt-xs(
+ standout
+ v-model='rule.match'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.groups.ruleMatch`)'
+ :options=`[
+ { label: $t('admin.groups.ruleMatchStart'), value: 'START' },
+ { label: $t('admin.groups.ruleMatchEnd'), value: 'END' },
+ { label: $t('admin.groups.ruleMatchRegex'), value: 'REGEX' },
+ { label: $t('admin.groups.ruleMatchTag'), value: 'TAG' },
+ { label: $t('admin.groups.ruleMatchTagAll'), value: 'TAGALL' },
+ { label: $t('admin.groups.ruleMatchExact'), value: 'EXACT' }
+ ]`
+ )
+ q-input.q-mt-sm(
+ standout
+ v-model='rule.path'
+ dense
+ :prefix='[`START`, `REGEX`, `EXACT`].includes(rule.match) ? `/` : null'
+ :suffix='rule.match === `REGEX` ? `/` : null'
+ :aria-label='$t(`admin.groups.rulePath`)'
+ )
+ //- -----------------------------------------------------------------------
+ //- PERMISSIONS
+ //- -----------------------------------------------------------------------
+ q-page(v-else-if='$route.params.section === `permissions`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-6
+ q-card.shadow-1.q-pb-sm
+ .flex.justify-between
+ q-card-section
+ .text-subtitle1 {{$t(`admin.groups.permissions`)}}
+ q-card-section
+ q-btn.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/groups#permissions'
+ target='_blank'
+ )
+ template(v-for='(perm, idx) of permissions', :key='perm.permission')
+ q-item(tag='label', v-ripple)
+ q-item-section.items-center(style='flex: 0 0 40px;')
+ q-icon(
+ name='las la-comments'
+ color='primary'
+ size='sm'
+ )
+ q-item-section
+ q-item-label {{perm.permission}}
+ q-item-label(caption) {{perm.hint}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='group.permissions'
+ :val='perm.permission'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowComments`)'
+ )
+ q-separator.q-my-sm(inset, v-if='idx < permissions.length - 1')
+ //- -----------------------------------------------------------------------
+ //- USERS
+ //- -----------------------------------------------------------------------
+ q-page(v-else-if='$route.params.section === `users`')
+ q-toolbar(
+ :class='$q.dark.isActive ? `bg-dark-3` : `bg-white`'
+ )
+ .text-subtitle1 {{$t('admin.groups.users')}}
+ q-space
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/groups#users'
+ target='_blank'
+ )
+ q-input.denser.fill-outline.q-mr-sm(
+ outlined
+ v-model='usersFilter'
+ :placeholder='$t(`admin.groups.filterUsers`)'
+ dense
+ )
+ template(#prepend)
+ q-icon(name='las la-search')
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ @click='refreshUsers'
+ )
+ q-btn.q-mr-xs(
+ unelevated
+ icon='las la-user-plus'
+ :label='$t(`admin.groups.assignUser`)'
+ color='primary'
+ @click='assignUser'
+ )
+ q-separator
+ .q-pa-md
+ q-card.shadow-1
+ q-table(
+ :rows='users'
+ :columns='usersHeaders'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='isLoadingUsers'
+ )
+ template(v-slot: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')
+ q-td(:props='props')
+ .flex.items-center
+ strong {{props.value}}
+ q-icon.q-ml-sm(
+ v-if='props.row.isSystem'
+ name='las la-lock'
+ color='pink'
+ )
+ q-icon.q-ml-sm(
+ v-if='!props.row.isActive'
+ name='las la-ban'
+ color='pink'
+ )
+ template(v-slot:body-cell-email='props')
+ q-td(:props='props')
+ em {{ props.value }}
+ template(v-slot:body-cell-date='props')
+ q-td(:props='props')
+ i18n-t.text-caption(keypath='admin.users.createdAt', tag='div')
+ template(#date)
+ strong {{ humanizeDate(props.value) }}
+ i18n-t.text-caption(
+ v-if='props.row.lastLoginAt'
+ keypath='admin.users.lastLoginAt'
+ tag='div'
+ )
+ template(#date)
+ strong {{ humanizeDate(props.row.lastLoginAt) }}
+ template(v-slot:body-cell-edit='props')
+ q-td(:props='props')
+ q-btn.acrylic-btn.q-mr-sm(
+ v-if='!props.row.isSystem'
+ flat
+ :to='`/_admin/users/` + props.row.id'
+ icon='las la-pen'
+ color='indigo'
+ :label='$t(`common.actions.edit`)'
+ no-caps
+ )
+ q-btn.acrylic-btn(
+ v-if='!props.row.isSystem'
+ flat
+ icon='las la-user-minus'
+ color='accent'
+ @click='unassignUser(props.row)'
+ )
+
+ .flex.flex-center.q-mt-md(v-if='usersTotalPages > 1')
+ q-pagination(
+ v-model='usersPage'
+ :max='usersTotalPages'
+ :max-pages='9'
+ boundary-numbers
+ direction-links
+ )
+
+
+
+
+
diff --git a/ux/src/components/HeaderNav.vue b/ux/src/components/HeaderNav.vue
new file mode 100644
index 00000000..e9c657e6
--- /dev/null
+++ b/ux/src/components/HeaderNav.vue
@@ -0,0 +1,123 @@
+
+q-header.bg-header.text-white.site-header(
+ height-hint='64'
+ )
+ .row.no-wrap
+ q-toolbar(
+ style='height: 64px;'
+ dark
+ )
+ q-btn(
+ dense
+ flat
+ to='/'
+ )
+ q-avatar(
+ size='34px'
+ square
+ )
+ img(src='/_assets/logo-wikijs.svg')
+ q-toolbar-title.text-h6.font-poppins {{siteTitle}}
+ q-toolbar.gt-sm(
+ style='height: 64px;'
+ dark
+ )
+ q-input(
+ dark
+ v-model='search'
+ standout='bg-white text-dark'
+ dense
+ rounded
+ ref='searchField'
+ style='width: 100%;'
+ label='Search...'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-search')
+ template(v-slot:append)
+ q-icon.cursor-pointer(
+ name='las la-times'
+ @click='search=``'
+ v-if='search.length > 0'
+ :color='$q.dark.isActive ? `blue` : `grey-4`'
+ )
+ q-btn.q-ml-md(
+ flat
+ round
+ dense
+ icon='las la-tags'
+ color='grey'
+ to='/t'
+ )
+ q-toolbar(
+ style='height: 64px;'
+ dark
+ )
+ q-space
+ transition(name='syncing')
+ q-spinner-rings.q-mr-sm(
+ v-show='isSyncing'
+ color='orange'
+ size='34px'
+ )
+ q-btn.q-ml-md(
+ flat
+ round
+ dense
+ icon='las la-plus-circle'
+ color='blue-4'
+ aria-label='Create New Page'
+ )
+ q-tooltip Create New Page
+ new-menu
+ q-btn.q-ml-md(
+ flat
+ round
+ dense
+ icon='las la-tools'
+ color='secondary'
+ to='/_admin'
+ aria-label='Administration'
+ )
+ q-tooltip Administration
+ account-menu
+
+
+
+
+
diff --git a/ux/src/components/IconPickerDialog.vue b/ux/src/components/IconPickerDialog.vue
new file mode 100644
index 00000000..6e2eab49
--- /dev/null
+++ b/ux/src/components/IconPickerDialog.vue
@@ -0,0 +1,213 @@
+
+q-card.icon-picker(flat, style='width: 400px;')
+ q-tabs.text-primary(
+ v-model='currentTab'
+ no-caps
+ inline-label
+ )
+ q-tab(
+ name='icon'
+ icon='las la-icons'
+ label='Icon'
+ )
+ q-tab(
+ name='img'
+ icon='las la-image'
+ label='Image'
+ )
+ q-separator
+ q-tab-panels(
+ v-model='currentTab'
+ )
+ q-tab-panel(name='icon')
+ q-select(
+ :options='iconPacks'
+ v-model='selPack'
+ emit-value
+ map-options
+ outlined
+ dense
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ )
+ template(v-slot:option='scope')
+ q-item(
+ v-bind='scope.itemProps'
+ v-on='scope.itemEvents'
+ :class='scope.selected ? `bg-primary text-white` : ``'
+ )
+ q-item-section(side)
+ q-icon(
+ name='las la-box'
+ :color='scope.selected ? `white` : `grey`'
+ )
+ q-item-section
+ q-item-label {{scope.opt.name}}
+ q-item-label(caption): strong(:class='scope.selected ? `text-white` : `text-primary`') {{scope.opt.subset}}
+ q-item-section(side, v-if='scope.opt.subset')
+ q-chip(
+ color='primary'
+ text-color='white'
+ rounded
+ size='sm'
+ ) {{scope.opt.subset.toUpperCase()}}
+ q-input.q-mt-md(
+ v-model='selIcon'
+ outlined
+ label='Icon Name'
+ dense
+ )
+ .row.q-gutter-md.q-mt-none
+ .col-auto
+ q-avatar(
+ size='64px'
+ color='primary'
+ rounded
+ )
+ q-icon(
+ :name='iconName'
+ color='white'
+ size='64px'
+ )
+ .col
+ .text-caption Learn how to #[a(href='https://docs.requarks.io') use icons].
+ .text-caption.q-mt-sm View #[a(:href='iconPackRefWebsite', target='_blank') Icon Pack reference] for all possible options.
+ q-tab-panel(name='img')
+ .row.q-gutter-sm
+ q-btn.col(
+ label='Browse...'
+ color='secondary'
+ icon='las la-file-image'
+ unelevated
+ no-caps
+ )
+ q-btn.col(
+ label='Upload...'
+ color='secondary'
+ icon='las la-upload'
+ unelevated
+ no-caps
+ )
+ .q-mt-md.text-center
+ q-avatar(
+ size='64px'
+ rounded
+ )
+ q-img(
+ transition='jump-down'
+ :ratio='1'
+ :src='imgPath'
+ )
+ q-separator
+ q-card-actions
+ q-space
+ q-btn(
+ icon='las la-times'
+ label='Discard'
+ outline
+ color='grey-7'
+ v-close-popup
+ )
+ q-btn(
+ icon='las la-check'
+ label='Apply'
+ unelevated
+ color='secondary'
+ @click='apply'
+ v-close-popup
+ )
+
+
+
+
+
diff --git a/ux/src/components/LocaleInstallDialog.vue b/ux/src/components/LocaleInstallDialog.vue
new file mode 100644
index 00000000..f9e560df
--- /dev/null
+++ b/ux/src/components/LocaleInstallDialog.vue
@@ -0,0 +1,170 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 850px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-down.svg', left, size='sm')
+ span {{$t(`admin.locale.downloadTitle`)}}
+ q-card-section.q-pa-none
+ q-table.no-border-radius(
+ :data='locales'
+ :columns='headers'
+ row-name='code'
+ flat
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='loading > 0'
+ )
+ template(v-slot:body-cell-code='props')
+ q-td(:props='props')
+ q-chip(
+ square
+ color='teal'
+ text-color='white'
+ dense
+ ): span.text-caption {{props.value}}
+ template(v-slot:body-cell-name='props')
+ q-td(:props='props')
+ strong {{props.value}}
+ template(v-slot:body-cell-isRTL='props')
+ q-td(:props='props')
+ q-icon(
+ v-if='props.value'
+ name='las la-check'
+ color='brown'
+ size='xs'
+ )
+ template(v-slot:body-cell-availability='props')
+ q-td(:props='props')
+ q-circular-progress(
+ size='md'
+ show-value
+ :value='props.value'
+ :thickness='0.1'
+ :color='props.value <= 33 ? `negative` : (props.value <= 66) ? `warning` : `positive`'
+ ) {{ props.value }}%
+ template(v-slot:body-cell-isInstalled='props')
+ q-td(:props='props')
+ q-spinner(
+ v-if='props.row.isDownloading'
+ color='primary'
+ size='20px'
+ :thickness='2'
+ )
+ q-btn(
+ v-else-if='props.value && props.row.installDate < props.row.updatedAt'
+ flat
+ round
+ dense
+ @click='download(props.row)'
+ icon='las la-redo-alt'
+ color='accent'
+ )
+ q-btn(
+ v-else-if='props.value'
+ flat
+ round
+ dense
+ @click='download(props.row)'
+ icon='las la-check-circle'
+ color='positive'
+ )
+ q-btn(
+ v-else
+ flat
+ round
+ dense
+ @click='download(props.row)'
+ icon='las la-cloud-download-alt'
+ color='primary'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.close`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+
+ q-inner-loading(:showing='loading')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/ux/src/components/PageBrowser.vue b/ux/src/components/PageBrowser.vue
new file mode 100644
index 00000000..513435fa
--- /dev/null
+++ b/ux/src/components/PageBrowser.vue
@@ -0,0 +1,42 @@
+
+.row
+ .col-auto.bg-grey-1(style='width: 250px;')
+ q-tree(
+ :nodes='tree'
+ default-expand-all
+ node-key='label'
+ @lazy-load='onLazyLoad'
+ )
+ .col Doude
+
+
+
diff --git a/ux/src/components/PageDataDialog.vue b/ux/src/components/PageDataDialog.vue
new file mode 100644
index 00000000..ee4b8777
--- /dev/null
+++ b/ux/src/components/PageDataDialog.vue
@@ -0,0 +1,151 @@
+
+q-card.page-data-dialog(style='width: 750px;')
+ q-toolbar.bg-primary.text-white.flex
+ .text-subtitle2 {{$t('editor.pageData.title')}}
+ q-space
+ q-btn(
+ icon='las la-times'
+ dense
+ flat
+ v-close-popup
+ )
+ q-card-section.page-data-dialog-selector
+ //- .text-overline.text-white {{$t('editor.pageData.template')}}
+ .flex.q-gutter-sm
+ q-select(
+ dark
+ v-model='templateId'
+ :label='$t(`editor.pageData.template`)'
+ :aria-label='$t(`editor.pageData.template`)'
+ :options='templates'
+ option-value='id'
+ map-options
+ emit-value
+ standout
+ dense
+ style='flex: 1 0 auto;'
+ )
+ q-btn.acrylic-btn(
+ dark
+ icon='las la-pen'
+ :label='$t(`common.actions.manage`)'
+ unelevated
+ no-caps
+ color='deep-orange-9'
+ @click='editTemplates'
+ )
+ q-tabs.alt-card(
+ v-model='mode'
+ inline-label
+ no-caps
+ )
+ q-tab(
+ name='visual'
+ label='Visual'
+ )
+ q-tab(
+ name='code'
+ label='YAML'
+ )
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: calc(100% - 50px - 75px - 48px);'
+ )
+ q-card-section(v-if='mode === `visual`')
+ .q-gutter-sm
+ q-input(
+ label='Attribute Text'
+ dense
+ outlined
+ )
+ template(v-slot:before)
+ q-icon(name='las la-font', color='primary')
+ q-input(
+ label='Attribute Number'
+ dense
+ outlined
+ type='number'
+ )
+ template(v-slot:before)
+ q-icon(name='las la-infinity', color='primary')
+ .q-py-xs
+ q-checkbox(
+ label='Attribute Boolean'
+ color='primary'
+ dense
+ size='lg'
+ )
+ q-no-ssr(v-else, :placeholder='$t(`common.loading`)')
+ codemirror.admin-theme-cm(
+ ref='cmData'
+ v-model='content'
+ :options='{ mode: `text/yaml` }'
+ )
+
+ q-dialog(
+ v-model='showDataTemplateDialog'
+ )
+ page-data-template-dialog
+
+
+
+
+
diff --git a/ux/src/components/PageDataTemplateDialog.vue b/ux/src/components/PageDataTemplateDialog.vue
new file mode 100644
index 00000000..a6c089c8
--- /dev/null
+++ b/ux/src/components/PageDataTemplateDialog.vue
@@ -0,0 +1,422 @@
+
+q-card.page-datatmpl-dialog(style='width: 1100px; max-width: 1100px;')
+ q-toolbar.bg-primary.text-white
+ .text-subtitle2 {{$t('editor.pageData.manageTemplates')}}
+ q-space
+ q-btn(
+ icon='las la-times'
+ dense
+ flat
+ v-close-popup
+ )
+ q-card-section.page-datatmpl-selector
+ .flex.q-gutter-md
+ q-select.col(
+ v-model='selectedTemplateId'
+ :options='templates'
+ standout
+ :label='$t(`editor.pageData.template`)'
+ dense
+ dark
+ option-value='id'
+ map-options
+ emit-value
+ )
+ q-btn(
+ icon='las la-plus'
+ :label='$t(`common.actions.new`)'
+ unelevated
+ color='primary'
+ no-caps
+ @click='create'
+ )
+ .row(v-if='tmpl')
+ .col-auto.page-datatmpl-sd
+ .q-pa-md
+ q-btn.acrylic-btn.full-width(
+ :label='$t(`common.actions.howItWorks`)'
+ icon='las la-question-circle'
+ flat
+ color='pink'
+ no-caps
+ )
+ q-item-label(header, style='margin-top: 2px;') {{$t('editor.pageData.templateFullRowTypes')}}
+ .q-px-md
+ draggable(
+ class='q-list rounded-borders'
+ :list='inventoryMisc'
+ :group='{name: `shared`, pull: `clone`, put: false}'
+ :clone='cloneFieldType'
+ :sort='false'
+ :animation='150'
+ @start='dragStarted = true'
+ @end='dragStarted = false'
+ item-key='id'
+ )
+ template(#item='{element}')
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(:name='element.icon', color='primary')
+ q-item-section
+ q-item-label {{element.label}}
+ q-item-label(header) {{$t('editor.pageData.templateKeyValueTypes')}}
+ .q-px-md.q-pb-md
+ draggable(
+ class='q-list rounded-borders'
+ :list='inventoryKV'
+ :group='{name: `shared`, pull: `clone`, put: false}'
+ :clone='cloneFieldType'
+ :sort='false'
+ :animation='150'
+ @start='dragStarted = true'
+ @end='dragStarted = false'
+ item-key='id'
+ )
+ template(#item='{element}')
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(:name='element.icon', color='primary')
+ q-item-section
+ q-item-label {{element.label}}
+ .col.page-datatmpl-content
+ q-scroll-area(
+ ref='scrollArea'
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ .col.page-datatmpl-meta.q-px-md.q-py-md.flex.q-gutter-md
+ q-input.col(
+ ref='tmplTitleIpt'
+ :label='$t(`editor.pageData.templateTitle`)'
+ outlined
+ dense
+ v-model='tmpl.label'
+ )
+ q-btn.acrylic-btn(
+ icon='las la-check'
+ :label='$t(`common.actions.commit`)'
+ no-caps
+ flat
+ color='positive'
+ @click='commit'
+ )
+ q-btn.acrylic-btn(
+ icon='las la-trash'
+ :aria-label='$t(`common.actions.delete`)'
+ flat
+ color='negative'
+ @click='remove'
+ )
+ q-item-label(header) {{$t('editor.pageData.templateStructure')}}
+ .q-px-md.q-pb-md
+ div(:class='(dragStarted || tmpl.data.length < 1 ? `page-datatmpl-box` : ``)')
+ .text-caption.text-primary.q-pa-md(v-if='tmpl.data.length < 1 && !dragStarted'): em {{$t('editor.pageData.dragDropHint')}}
+ draggable(
+ class='q-list rounded-borders'
+ :list='tmpl.data'
+ group='shared'
+ :animation='150'
+ handle='.handle'
+ @end='dragStarted = false'
+ item-key='id'
+ )
+ template(#item='{element}')
+ q-item
+ q-item-section(side)
+ q-icon.handle(name='las la-bars')
+ q-item-section(side)
+ q-icon(:name='element.icon', color='primary')
+ q-item-section
+ q-input(
+ :label='$t(`editor.pageData.label`)'
+ v-model='element.label'
+ outlined
+ dense
+ )
+ q-item-section(v-if='element.type !== `header`')
+ q-input(
+ :label='$t(`editor.pageData.uniqueKey`)'
+ v-model='element.key'
+ outlined
+ dense
+ )
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ color='negative'
+ :aria-label='$t(`common.actions.delete`)'
+ padding='xs'
+ icon='las la-times'
+ flat
+ @click='removeItem(item)'
+ )
+ .page-datatmpl-scrollend(ref='scrollAreaEnd')
+
+ .q-pa-md.text-center(v-else-if='templates.length > 0')
+ em.text-grey-6 {{$t('editor.pageData.selectTemplateAbove')}}
+ .q-pa-md.text-center(v-else)
+ em.text-grey-6 {{$t('editor.pageData.noTemplate')}}
+
+
+
+
+
diff --git a/ux/src/components/PageNewMenu.vue b/ux/src/components/PageNewMenu.vue
new file mode 100644
index 00000000..c056040b
--- /dev/null
+++ b/ux/src/components/PageNewMenu.vue
@@ -0,0 +1,43 @@
+
+q-menu(
+ auto-close
+ anchor='bottom right'
+ self='top right'
+ )
+ q-list(padding)
+ q-item(clickable, @click='create(`wysiwyg`)')
+ blueprint-icon(icon='google-presentation')
+ q-item-section.q-pr-sm New Page
+ q-item(clickable, @click='create(`markdown`)')
+ blueprint-icon(icon='markdown')
+ q-item-section.q-pr-sm New Markdown Page
+ q-item(clickable, @click='create(`channel`)')
+ blueprint-icon(icon='chat')
+ q-item-section.q-pr-sm New Discussion Space
+ q-item(clickable, @click='create(`blog`)')
+ blueprint-icon(icon='typewriter-with-paper')
+ q-item-section.q-pr-sm New Blog Page
+ q-item(clickable, @click='create(`api`)')
+ blueprint-icon(icon='api')
+ q-item-section.q-pr-sm New API Documentation
+ q-item(clickable, @click='create(`redirect`)')
+ blueprint-icon(icon='advance')
+ q-item-section.q-pr-sm New Redirection
+ q-separator.q-my-sm(inset)
+ q-item(clickable, to='/f')
+ blueprint-icon(icon='add-image')
+ q-item-section.q-pr-sm Upload Media Asset
+
+
+
diff --git a/ux/src/components/PagePropertiesDialog.vue b/ux/src/components/PagePropertiesDialog.vue
new file mode 100644
index 00000000..d9fdc4cf
--- /dev/null
+++ b/ux/src/components/PagePropertiesDialog.vue
@@ -0,0 +1,365 @@
+
+q-card.page-properties-dialog
+ .floating-sidepanel-quickaccess.animated.fadeIn(v-if='showQuickAccess', style='right: 486px;')
+ template(v-for='(qa, idx) of quickaccess', :key='`qa-` + qa.key')
+ q-btn(
+ :icon='qa.icon'
+ flat
+ padding='sm xs'
+ size='sm'
+ @click='jumpToSection(qa.key)'
+ )
+ q-tooltip(anchor='center left' self='center right') {{qa.label}}
+ q-separator(dark, v-if='idx < quickaccess.length - 1')
+ q-toolbar.bg-primary.text-white.flex
+ .text-subtitle2 {{$t('editor.props.pageProperties')}}
+ q-space
+ q-btn(
+ icon='las la-times'
+ dense
+ flat
+ v-close-popup
+ )
+ q-scroll-area(
+ ref='scrollArea'
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: calc(100% - 50px);'
+ )
+ q-card-section(ref='card-info')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-info-circle', size='xs')] {{$t('editor.props.info')}}
+ q-form.q-gutter-sm
+ q-input(
+ v-model='title'
+ :label='$t(`editor.props.title`)'
+ outlined
+ dense
+ )
+ q-input(
+ v-model='description'
+ :label='$t(`editor.props.shortDescription`)'
+ outlined
+ dense
+ )
+ q-card-section.alt-card(ref='card-publishstate')
+ .text-overline.q-pb-xs.items-center.flex #[q-icon.q-mr-sm(name='las la-power-off', size='xs')] {{$t('editor.props.publishState')}}
+ q-form.q-gutter-md
+ div
+ q-btn-toggle(
+ v-model='isPublished'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('editor.props.draft'), value: false },
+ { label: $t('editor.props.published'), value: true },
+ { label: $t('editor.props.dateRange'), value: null }
+ ]`
+ )
+ .text-caption(v-if='isPublished'): em {{$t('editor.props.publishedHint')}}
+ .text-caption(v-else-if='isPublished === false'): em {{$t('editor.props.draftHint')}}
+ template(v-else-if='isPublished === null')
+ .text-caption: em {{$t('editor.props.dateRangeHint')}}
+ q-date(
+ v-model='publishingRange'
+ range
+ flat
+ bordered
+ landscape
+ minimal
+ )
+ q-card-section(ref='card-relations')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-sun', size='xs')] {{$t('editor.props.relations')}}
+ q-list.rounded-borders.q-mb-sm.bg-white(
+ v-if='relations.length > 0'
+ separator
+ bordered
+ )
+ q-item(v-for='rel of relations', :key='`rel-id-` + rel.id')
+ q-item-section(side)
+ q-icon(:name='rel.icon')
+ q-item-section
+ q-item-label: strong {{rel.label}}
+ q-item-label(caption) {{rel.caption}}
+ q-item-section(side)
+ q-chip.q-px-sm(
+ dense
+ square
+ color='primary'
+ text-color='white'
+ )
+ .text-caption {{rel.position}}
+ q-item-section(side)
+ q-btn(
+ icon='las la-pen'
+ dense
+ flat
+ padding='none'
+ @click='editRelation(rel)'
+ )
+ q-item-section(side)
+ q-btn(
+ icon='las la-times'
+ dense
+ flat
+ padding='none'
+ @click='removeRelation(rel)'
+ )
+ q-btn.full-width(
+ :label='$t(`editor.props.relationAdd`)'
+ icon='las la-plus'
+ no-caps
+ unelevated
+ color='secondary'
+ @click='newRelation'
+ )
+ q-tooltip {{$t('editor.props.relationAddHint')}}
+ q-card-section.alt-card(ref='card-scripts')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-code', size='xs')] {{$t('editor.props.scripts')}}
+ q-btn.full-width(
+ :label='$t(`editor.props.jsLoad`)'
+ icon='lab la-js-square'
+ no-caps
+ unelevated
+ color='secondary'
+ @click='editScripts(`jsLoad`)'
+ )
+ q-tooltip {{$t('editor.props.jsLoadHint')}}
+ q-btn.full-width.q-mt-sm(
+ :label='$t(`editor.props.jsUnload`)'
+ icon='lab la-js-square'
+ no-caps
+ unelevated
+ color='secondary'
+ @click='editScripts(`jsUnload`)'
+ )
+ q-tooltip {{$t('editor.props.jsUnloadHint')}}
+ q-btn.full-width.q-mt-sm(
+ :label='$t(`editor.props.styles`)'
+ icon='lab la-css3-alt'
+ no-caps
+ unelevated
+ color='secondary'
+ @click='editScripts(`styles`)'
+ )
+ q-tooltip {{$t('editor.props.stylesHint')}}
+ q-card-section.q-pb-lg(ref='card-sidebar')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-ruler-vertical', size='xs')] {{$t('editor.props.sidebar')}}
+ q-form.q-gutter-md.q-pt-sm
+ div
+ q-toggle(
+ v-model='showSidebar'
+ dense
+ :label='$t(`editor.props.showSidebar`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-if='showSidebar'
+ v-model='showToc'
+ dense
+ :label='$t(`editor.props.showToc`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div(
+ v-if='showSidebar && showToc'
+ style='padding-left: 40px;'
+ )
+ .text-caption {{$t('editor.props.tocMinMaxDepth')}} #[strong (H{{tocDepth.min}} → H{{tocDepth.max}})]
+ q-range(
+ v-model='tocDepth'
+ :min='1'
+ :max='6'
+ color='primary'
+ :left-label-value='`H` + tocDepth.min'
+ :right-label-value='`H` + tocDepth.max'
+ snap
+ label
+ markers
+ )
+ div
+ q-toggle(
+ v-if='showSidebar'
+ v-model='showTags'
+ dense
+ :label='$t(`editor.props.showTags`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ q-card-section.alt-card.q-pb-lg(ref='card-social')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-comments', size='xs')] {{$t('editor.props.social')}}
+ q-form.q-gutter-md.q-pt-sm
+ div
+ q-toggle(
+ v-model='allowComments'
+ dense
+ :label='$t(`editor.props.allowComments`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='allowContributions'
+ dense
+ :label='$t(`editor.props.allowContributions`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='allowRatings'
+ dense
+ :label='$t(`editor.props.allowRatings`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ q-card-section.q-pb-lg(ref='card-tags')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-tags', size='xs')] {{$t('editor.props.tags')}}
+ page-tags(edit)
+ q-card-section.alt-card.q-pb-lg(ref='card-visibility')
+ .text-overline.items-center.flex #[q-icon.q-mr-sm(name='las la-eye', size='xs')] {{$t('editor.props.visibility')}}
+ q-form.q-gutter-md.q-pt-sm
+ div
+ q-toggle(
+ v-model='showInTree'
+ dense
+ :label='$t(`editor.props.showInTree`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='requirePassword'
+ dense
+ :label='$t(`editor.props.requirePassword`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div(
+ v-if='requirePassword'
+ style='padding-left: 40px;'
+ )
+ q-input(
+ ref='iptPagePassword'
+ v-model='password'
+ :label='$t(`editor.props.password`)'
+ :hint='$t(`editor.props.passwordHint`)'
+ outlined
+ dense
+ )
+ q-dialog(
+ v-model='showRelationDialog'
+ )
+ page-relation-dialog(:edit-id='editRelationId')
+
+ q-dialog(
+ v-model='showScriptsDialog'
+ )
+ page-scripts-dialog(:mode='pageScriptsMode')
+
+
+
diff --git a/ux/src/components/PageRelationDialog.vue b/ux/src/components/PageRelationDialog.vue
new file mode 100644
index 00000000..4235c6de
--- /dev/null
+++ b/ux/src/components/PageRelationDialog.vue
@@ -0,0 +1,211 @@
+
+q-card.page-relation-dialog(style='width: 500px;')
+ q-toolbar.bg-primary.text-white
+ .text-subtitle2(v-if='isEditMode') {{$t('editor.pageRel.titleEdit')}}
+ .text-subtitle2(v-else) {{$t('editor.pageRel.title')}}
+ q-card-section
+ .text-overline {{$t('editor.pageRel.position')}}
+ q-form.q-gutter-md.q-pt-md
+ div
+ q-btn-toggle(
+ v-model='pos'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('editor.pageRel.left'), value: 'left' },
+ { label: $t('editor.pageRel.center'), value: 'center' },
+ { label: $t('editor.pageRel.right'), value: 'right' }
+ ]`
+ )
+ .text-overline {{$t('editor.pageRel.button')}}
+ q-input(
+ ref='iptRelLabel'
+ outlined
+ dense
+ :label='$t(`editor.pageRel.label`)'
+ v-model='label'
+ )
+ template(v-if='pos !== `center`')
+ q-input(
+ outlined
+ dense
+ :label='$t(`editor.pageRel.caption`)'
+ v-model='caption'
+ )
+ q-btn.rounded-borders(
+ :label='$t(`editor.pageRel.selectIcon`)'
+ color='primary'
+ outline
+ )
+ q-menu(content-class='shadow-7')
+ icon-picker-dialog(v-model='icon')
+ .text-overline {{$t('editor.pageRel.target')}}
+ q-btn.rounded-borders(
+ :label='$t(`editor.pageRel.selectPage`)'
+ color='primary'
+ outline
+ )
+ .text-overline {{$t('editor.pageRel.preview')}}
+ q-btn(
+ v-if='pos === `left`'
+ padding='sm md'
+ outline
+ :icon='icon'
+ no-caps
+ color='primary'
+ )
+ .column.text-left.q-pl-md
+ .text-body2: strong {{label}}
+ .text-caption {{caption}}
+ q-btn.full-width(
+ v-else-if='pos === `center`'
+ :label='label'
+ color='primary'
+ flat
+ no-caps
+ :icon='icon'
+ )
+ q-btn(
+ v-else-if='pos === `right`'
+ padding='sm md'
+ outline
+ :icon-right='icon'
+ no-caps
+ color='primary'
+ )
+ .column.text-left.q-pr-md
+ .text-body2: strong {{label}}
+ .text-caption {{caption}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-times'
+ :label='$t(`common.actions.discard`)'
+ color='grey-7'
+ padding='xs md'
+ v-close-popup
+ flat
+ )
+ q-btn(
+ v-if='isEditMode'
+ :disabled='!canSubmit'
+ icon='las la-check'
+ :label='$t(`common.actions.save`)'
+ unelevated
+ color='primary'
+ padding='xs md'
+ @click='persist'
+ v-close-popup
+ )
+ q-btn(
+ v-else
+ :disabled='!canSubmit'
+ icon='las la-plus'
+ :label='$t(`common.actions.create`)'
+ unelevated
+ color='primary'
+ padding='xs md'
+ @click='create'
+ v-close-popup
+ )
+
+
+
diff --git a/ux/src/components/PageSaveDialog.vue b/ux/src/components/PageSaveDialog.vue
new file mode 100644
index 00000000..103475e0
--- /dev/null
+++ b/ux/src/components/PageSaveDialog.vue
@@ -0,0 +1,57 @@
+
+q-card.page-save-dialog(style='width: 860px; max-width: 90vw;')
+ q-toolbar.bg-primary.text-white
+ .text-subtitle2 {{$t('editor.pageSave.title')}}
+ page-browser
+ q-card-section
+ q-input(
+ v-model='reason'
+ label='Reason for change'
+ dense
+ outlined
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-times'
+ :label='$t(`common.actions.cancel`)'
+ color='grey-7'
+ padding='xs md'
+ v-close-popup
+ flat
+ )
+ q-btn(
+ icon='las la-check'
+ :label='$t(`common.actions.save`)'
+ unelevated
+ color='primary'
+ padding='xs md'
+ @click=''
+ v-close-popup
+ )
+
+
+
+
+
diff --git a/ux/src/components/PageScriptsDialog.vue b/ux/src/components/PageScriptsDialog.vue
new file mode 100644
index 00000000..7c1b133b
--- /dev/null
+++ b/ux/src/components/PageScriptsDialog.vue
@@ -0,0 +1,105 @@
+
+q-card.page-scripts-dialog(style='width: 860px; max-width: 90vw;')
+ q-toolbar.bg-primary.text-white
+ .text-subtitle2 {{$t('editor.pageScripts.title')}} - {{$t('editor.props.' + mode)}}
+ q-space
+ q-chip(
+ square
+ style='background-color: rgba(0,0,0,.1)'
+ text-color='white'
+ )
+ .text-caption {{this.languageLabel}}
+ div(style='min-height: 450px;')
+ q-no-ssr(:placeholder='$t(`common.loading`)')
+ util-code-editor(
+ v-if='showEditor'
+ ref='editor'
+ v-model='content'
+ :language='language'
+ :min-height='450'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-times'
+ :label='$t(`common.actions.discard`)'
+ color='grey-7'
+ padding='xs md'
+ v-close-popup
+ flat
+ )
+ q-btn(
+ icon='las la-check'
+ :label='$t(`common.actions.save`)'
+ unelevated
+ color='primary'
+ padding='xs md'
+ @click='persist'
+ v-close-popup
+ )
+
+
+
+
+
diff --git a/ux/src/components/PageTags.vue b/ux/src/components/PageTags.vue
new file mode 100644
index 00000000..3bcc77ca
--- /dev/null
+++ b/ux/src/components/PageTags.vue
@@ -0,0 +1,59 @@
+
+.q-gutter-xs
+ template(v-if='tags && tags.length > 0')
+ q-chip(
+ square
+ color='secondary'
+ text-color='white'
+ dense
+ clickable
+ :removable='edit'
+ @remove='removeTag(tag)'
+ v-for='tag of tags'
+ :key='`tag-` + tag'
+ )
+ q-icon.q-mr-xs(name='las la-tag', size='14px')
+ span.text-caption {{tag}}
+ q-chip(
+ v-if='!edit && tags.length > 1'
+ square
+ color='secondary'
+ text-color='white'
+ dense
+ clickable
+ )
+ q-icon(name='las la-tags', size='14px')
+ q-input.q-mt-md(
+ v-if='edit'
+ outlined
+ v-model='newTag'
+ dense
+ placeholder='Add new tag...'
+ )
+
+
+
diff --git a/ux/src/components/SiteActivateDialog.vue b/ux/src/components/SiteActivateDialog.vue
new file mode 100644
index 00000000..df48898c
--- /dev/null
+++ b/ux/src/components/SiteActivateDialog.vue
@@ -0,0 +1,114 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-shutdown.svg', left, size='sm')
+ span {{value ? $t(`admin.sites.activate`) : $t(`admin.sites.deactivate`)}}
+ q-card-section
+ .text-body2
+ i18n-t(:keypath='value ? `admin.sites.activateConfirm` : `admin.sites.deactivateConfirm`')
+ template(v-slot:siteTitle)
+ strong {{site.title}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='value ? $t(`common.actions.activate`) : $t(`common.actions.deactivate`)'
+ :color='value ? `positive` : `negative`'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/ux/src/components/SiteCreateDialog.vue b/ux/src/components/SiteCreateDialog.vue
new file mode 100644
index 00000000..754bcb78
--- /dev/null
+++ b/ux/src/components/SiteCreateDialog.vue
@@ -0,0 +1,133 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
+ span {{$t(`admin.sites.new`)}}
+ q-form.q-py-sm(ref='createSiteForm')
+ q-item
+ blueprint-icon(icon='home')
+ q-item-section
+ q-input(
+ outlined
+ v-model='siteName'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.sites.nameMissing'),
+ val => /^[^<>"]+$/.test(val) || $t('admin.sites.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :label='$t(`common.field.name`)'
+ :aria-label='$t(`common.field.name`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ q-item
+ blueprint-icon.self-start(icon='dns')
+ q-item-section
+ q-input(
+ outlined
+ v-model='siteHostname'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.sites.hostnameMissing'),
+ val => /^(\\*)|([a-z0-9\-.:]+)$/.test(val) || $t('admin.sites.hostnameInvalidChars')
+ ]`
+ :hint='$t(`admin.sites.hostnameHint`)'
+ hide-bottom-space
+ :label='$t(`admin.sites.hostname`)'
+ :aria-label='$t(`admin.sites.hostname`)'
+ lazy-rules='ondemand'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='isLoading'
+ )
+
+
+
diff --git a/ux/src/components/SiteDeleteDialog.vue b/ux/src/components/SiteDeleteDialog.vue
new file mode 100644
index 00000000..3262d74e
--- /dev/null
+++ b/ux/src/components/SiteDeleteDialog.vue
@@ -0,0 +1,94 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{$t(`admin.sites.delete`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='admin.sites.deleteConfirm')
+ template(v-slot:siteTitle)
+ strong {{site.title}}
+ .text-body2.q-mt-md
+ strong.text-negative {{$t(`admin.sites.deleteConfirmWarn`)}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/ux/src/components/SocialSharingMenu.vue b/ux/src/components/SocialSharingMenu.vue
new file mode 100644
index 00000000..af9603a4
--- /dev/null
+++ b/ux/src/components/SocialSharingMenu.vue
@@ -0,0 +1,135 @@
+
+q-menu(
+ auto-close
+ anchor='bottom middle'
+ self='top middle'
+ @show='menuShown'
+ @before-hide='menuHidden'
+ )
+ q-list(dense, padding)
+ q-item(clickable, @click='', ref='copyUrlButton')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='las la-clipboard', size='sm')
+ q-item-section.q-pr-md Copy URL
+ q-item(clickable, tag='a', :href='`mailto:?subject=` + encodeURIComponent(title) + `&body=` + encodeURIComponent(urlFormatted) + `%0D%0A%0D%0A` + encodeURIComponent(description)', target='_blank')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='las la-envelope', size='sm')
+ q-item-section.q-pr-md Email
+ q-item(clickable, @click='openSocialPop(`https://www.facebook.com/sharer/sharer.php?u=` + encodeURIComponent(urlFormatted) + `&title=` + encodeURIComponent(title) + `&description=` + encodeURIComponent(description))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-facebook', size='sm')
+ q-item-section.q-pr-md Facebook
+ q-item(clickable, @click='openSocialPop(`https://www.linkedin.com/shareArticle?mini=true&url=` + encodeURIComponent(urlFormatted) + `&title=` + encodeURIComponent(title) + `&summary=` + encodeURIComponent(description))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-linkedin', size='sm')
+ q-item-section.q-pr-md LinkedIn
+ q-item(clickable, @click='openSocialPop(`https://www.reddit.com/submit?url=` + encodeURIComponent(urlFormatted) + `&title=` + encodeURIComponent(title))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-reddit', size='sm')
+ q-item-section.q-pr-md Reddit
+ q-item(clickable, @click='openSocialPop(`https://t.me/share/url?url=` + encodeURIComponent(urlFormatted) + `&text=` + encodeURIComponent(title))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-telegram', size='sm')
+ q-item-section.q-pr-md Telegram
+ q-item(clickable, @click='openSocialPop(`https://twitter.com/intent/tweet?url=` + encodeURIComponent(urlFormatted) + `&text=` + encodeURIComponent(title))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-twitter', size='sm')
+ q-item-section.q-pr-md Twitter
+ q-item(clickable, :href='`viber://forward?text=` + encodeURIComponent(urlFormatted) + ` ` + encodeURIComponent(description)')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-viber', size='sm')
+ q-item-section.q-pr-md Viber
+ q-item(clickable, @click='openSocialPop(`http://service.weibo.com/share/share.php?url=` + encodeURIComponent(urlFormatted) + `&title=` + encodeURIComponent(title))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-weibo', size='sm')
+ q-item-section.q-pr-md Weibo
+ q-item(clickable, @click='openSocialPop(`https://api.whatsapp.com/send?text=` + encodeURIComponent(title) + `%0D%0A` + encodeURIComponent(urlFormatted))')
+ q-item-section.items-center(avatar)
+ q-icon(color='grey', name='lab la-whatsapp', size='sm')
+ q-item-section.q-pr-md Whatsapp
+
+
+
diff --git a/ux/src/components/UserChangePwdDialog.vue b/ux/src/components/UserChangePwdDialog.vue
new file mode 100644
index 00000000..c727b575
--- /dev/null
+++ b/ux/src/components/UserChangePwdDialog.vue
@@ -0,0 +1,199 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-password-reset.svg', left, size='sm')
+ span {{$t(`admin.users.changePassword`)}}
+ q-form.q-py-sm(ref='changeUserPwdForm', @submit='save')
+ q-item
+ blueprint-icon(icon='password')
+ q-item-section
+ q-input(
+ outlined
+ v-model='userPassword'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.users.passwordMissing'),
+ val => val.length >= 8 || $t('admin.users.passwordTooShort')
+ ]`
+ hide-bottom-space
+ :label='$t(`admin.users.password`)'
+ :aria-label='$t(`admin.users.password`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ template(#append)
+ .flex.items-center
+ q-badge(
+ :color='passwordStrength.color'
+ :label='passwordStrength.label'
+ )
+ q-separator.q-mx-sm(vertical)
+ q-btn(
+ flat
+ dense
+ padding='none xs'
+ color='brown'
+ @click='randomizePassword'
+ )
+ q-icon(name='las la-dice-d6')
+ .q-pl-xs.text-caption: strong Generate
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='password-reset')
+ q-item-section
+ q-item-label {{$t(`admin.users.mustChangePwd`)}}
+ q-item-label(caption) {{$t(`admin.users.mustChangePwdHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='userMustChangePassword'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.mustChangePwd`)'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.update`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='isLoading'
+ )
+
+
+
diff --git a/ux/src/components/UserCreateDialog.vue b/ux/src/components/UserCreateDialog.vue
new file mode 100644
index 00000000..ebd65983
--- /dev/null
+++ b/ux/src/components/UserCreateDialog.vue
@@ -0,0 +1,336 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
+ span {{$t(`admin.users.create`)}}
+ q-form.q-py-sm(ref='createUserForm', @submit='create')
+ q-item
+ blueprint-icon(icon='person')
+ q-item-section
+ q-input(
+ outlined
+ v-model='userName'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.users.nameMissing'),
+ val => /^[^<>"]+$/.test(val) || $t('admin.users.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :label='$t(`common.field.name`)'
+ :aria-label='$t(`common.field.name`)'
+ lazy-rules='ondemand'
+ autofocus
+ ref='iptName'
+ )
+ q-item
+ blueprint-icon(icon='email')
+ q-item-section
+ q-input(
+ outlined
+ v-model='userEmail'
+ dense
+ type='email'
+ :rules=`[
+ val => val.length > 0 || $t('admin.users.emailMissing'),
+ val => /^.+\@.+\..+$/.test(val) || $t('admin.users.emailInvalid')
+ ]`
+ hide-bottom-space
+ :label='$t(`admin.users.email`)'
+ :aria-label='$t(`admin.users.email`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ q-item
+ blueprint-icon(icon='password')
+ q-item-section
+ q-input(
+ outlined
+ v-model='userPassword'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.users.passwordMissing'),
+ val => val.length >= 8 || $t('admin.users.passwordTooShort')
+ ]`
+ hide-bottom-space
+ :label='$t(`admin.users.password`)'
+ :aria-label='$t(`admin.users.password`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ template(#append)
+ .flex.items-center
+ q-badge(
+ :color='passwordStrength.color'
+ :label='passwordStrength.label'
+ )
+ q-separator.q-mx-sm(vertical)
+ q-btn(
+ flat
+ dense
+ padding='none xs'
+ color='brown'
+ @click='randomizePassword'
+ )
+ q-icon(name='las la-dice-d6')
+ .q-pl-xs.text-caption: strong Generate
+ q-item
+ blueprint-icon(icon='team')
+ q-item-section
+ q-select(
+ outlined
+ :options='groups'
+ v-model='userGroups'
+ multiple
+ map-options
+ emit-value
+ option-value='id'
+ option-label='name'
+ options-dense
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.users.groupsMissing')
+ ]`
+ hide-bottom-space
+ :label='$t(`admin.users.groups`)'
+ :aria-label='$t(`admin.users.groups`)'
+ lazy-rules='ondemand'
+ :loading='loadingGroups'
+ )
+ template(v-slot:selected)
+ .text-caption(v-if='userGroups.length > 1')
+ i18n-t(keypath='admin.users.groupsSelected')
+ template(#count)
+ strong {{ userGroups.length }}
+ .text-caption(v-else-if='userGroups.length === 1')
+ i18n-t(keypath='admin.users.groupSelected')
+ template(#group)
+ strong {{ selectedGroupName }}
+ span(v-else)
+ template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ v-on='itemEvents'
+ )
+ q-item-section(side)
+ q-checkbox(
+ size='sm'
+ :model-value='selected'
+ @update:model-value='toggleOption(opt)'
+ )
+ q-item-section
+ q-item-label {{opt.name}}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='password-reset')
+ q-item-section
+ q-item-label {{$t(`admin.users.mustChangePwd`)}}
+ q-item-label(caption) {{$t(`admin.users.mustChangePwdHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='userMustChangePassword'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.mustChangePwd`)'
+ )
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='email-open')
+ q-item-section
+ q-item-label {{$t(`admin.users.sendWelcomeEmail`)}}
+ q-item-label(caption) {{$t(`admin.users.sendWelcomeEmailHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='userSendWelcomeEmail'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.sendWelcomeEmail`)'
+ )
+ q-card-actions.card-actions
+ q-checkbox(
+ v-model='keepOpened'
+ color='primary'
+ :label='$t(`admin.users.createKeepOpened`)'
+ size='sm'
+ )
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='loading > 0'
+ )
+
+
+
diff --git a/ux/src/components/UserEditOverlay.vue b/ux/src/components/UserEditOverlay.vue
new file mode 100644
index 00000000..6dfd0248
--- /dev/null
+++ b/ux/src/components/UserEditOverlay.vue
@@ -0,0 +1,777 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/fluent-account.svg', left, size='md')
+ div
+ span {{$t(`admin.users.edit`)}}
+ .text-caption {{user.name}}
+ q-space
+ q-btn-group(push)
+ q-btn(
+ push
+ color='grey-6'
+ text-color='white'
+ :aria-label='$t(`common.actions.refresh`)'
+ icon='las la-redo-alt'
+ @click='load'
+ :loading='loading > 0'
+ )
+ q-tooltip(anchor='center left', self='center right') {{$t(`common.actions.refresh`)}}
+ q-btn(
+ push
+ color='white'
+ text-color='grey-7'
+ :label='$t(`common.actions.close`)'
+ :aria-label='$t(`common.actions.close`)'
+ icon='las la-times'
+ @click='close'
+ )
+ q-btn(
+ push
+ color='positive'
+ text-color='white'
+ :label='$t(`common.actions.save`)'
+ :aria-label='$t(`common.actions.save`)'
+ icon='las la-check'
+ @click='save()'
+ :disabled='loading > 0'
+ )
+ q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
+ q-list(padding, v-if='loading < 1')
+ q-item(
+ v-for='sc of sections'
+ :key='`section-` + sc.key'
+ clickable
+ :to='{ params: { section: sc.key } }'
+ active-class='bg-primary text-white'
+ :disabled='sc.disabled'
+ )
+ q-item-section(side)
+ q-icon(:name='sc.icon', color='white')
+ q-item-section {{sc.text}}
+ q-page-container
+ q-page(v-if='loading > 0')
+ .flex.q-pa-lg.items-center
+ q-spinner-tail(color='primary', size='32px', :thickness='2')
+ .text-caption.text-primary.q-pl-md: strong {{$t('admin.users.loading')}}
+ q-page(v-else-if='$route.params.section === `overview`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-8
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.profile')}}
+ q-item
+ blueprint-icon(icon='contact')
+ q-item-section
+ q-item-label {{$t(`admin.users.name`)}}
+ q-item-label(caption) {{$t(`admin.users.nameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='user.name'
+ dense
+ :rules=`[
+ val => invalidCharsRegex.test(val) || $t('admin.users.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.users.name`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='envelope')
+ q-item-section
+ q-item-label {{$t(`admin.users.email`)}}
+ q-item-label(caption) {{$t(`admin.users.emailHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='user.email'
+ dense
+ :aria-label='$t(`admin.users.email`)'
+ )
+ template(v-if='user.meta')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='address')
+ q-item-section
+ q-item-label {{$t(`admin.users.location`)}}
+ q-item-label(caption) {{$t(`admin.users.locationHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='user.meta.location'
+ dense
+ :aria-label='$t(`admin.users.location`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='new-job')
+ q-item-section
+ q-item-label {{$t(`admin.users.jobTitle`)}}
+ q-item-label(caption) {{$t(`admin.users.jobTitleHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='user.meta.jobTitle'
+ dense
+ :aria-label='$t(`admin.users.jobTitle`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='user.meta')
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.preferences')}}
+ q-item
+ blueprint-icon(icon='timezone')
+ q-item-section
+ q-item-label {{$t(`admin.users.timezone`)}}
+ q-item-label(caption) {{$t(`admin.users.timezoneHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='user.prefs.timezone'
+ :options='timezones'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :aria-label='$t(`admin.users.timezone`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='calendar')
+ q-item-section
+ q-item-label {{$t(`admin.users.dateFormat`)}}
+ q-item-label(caption) {{$t(`admin.users.dateFormatHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='user.prefs.dateFormat'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.users.dateFormat`)'
+ :options=`[
+ { label: $t('profile.localeDefault'), value: '' },
+ { label: 'DD/MM/YYYY', value: 'DD/MM/YYYY' },
+ { label: 'DD.MM.YYYY', value: 'DD.MM.YYYY' },
+ { label: 'MM/DD/YYYY', value: 'MM/DD/YYYY' },
+ { label: 'YYYY-MM-DD', value: 'YYYY-MM-DD' },
+ { label: 'YYYY/MM/DD', value: 'YYYY/MM/DD' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='clock')
+ q-item-section
+ q-item-label {{$t(`admin.users.timeFormat`)}}
+ q-item-label(caption) {{$t(`admin.users.timeFormatHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='user.prefs.timeFormat'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('profile.timeFormat12h'), value: '12h' },
+ { label: $t('profile.timeFormat24h'), value: '24h' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='light-on')
+ q-item-section
+ q-item-label {{$t(`admin.users.darkMode`)}}
+ q-item-label(caption) {{$t(`admin.users.darkModeHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='user.prefs.darkMode'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.darkMode`)'
+ )
+
+ .col-12.col-lg-4
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.info')}}
+ q-item
+ blueprint-icon(icon='person', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t(`common.field.id`)}}
+ q-item-label: strong {{userId}}
+ 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: strong {{humanizeDate(user.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: strong {{humanizeDate(user.updatedAt)}}
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='enter', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t(`admin.users.lastLoginAt`)}}
+ q-item-label: strong {{humanizeDate(user.lastLoginAt)}}
+
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='user.meta')
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.notes')}}
+ q-input.q-mt-sm(
+ outlined
+ v-model='user.meta.notes'
+ type='textarea'
+ :aria-label='$t(`admin.users.notes`)'
+ input-style='min-height: 243px'
+ :hint='$t(`admin.users.noteHint`)'
+ )
+
+ q-page(v-else-if='$route.params.section === `activity`')
+ span ---
+
+ q-page(v-else-if='$route.params.section === `auth`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-7
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.passAuth')}}
+ q-item
+ blueprint-icon(icon='password', :hue-rotate='45')
+ q-item-section
+ q-item-label {{$t(`admin.users.changePassword`)}}
+ q-item-label(caption) {{$t(`admin.users.changePasswordHint`)}}
+ q-item-label(caption): strong(:class='localAuth.password ? `text-positive` : `text-negative`') {{localAuth.password ? $t(`admin.users.pwdSet`) : $t(`admin.users.pwdNotSet`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='changePassword'
+ :label='$t(`common.actions.proceed`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='password-reset')
+ q-item-section
+ q-item-label {{$t(`admin.users.mustChangePwd`)}}
+ q-item-label(caption) {{$t(`admin.users.mustChangePwdHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='localAuth.mustChangePwd'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.mustChangePwd`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='key')
+ q-item-section
+ q-item-label {{$t(`admin.users.pwdAuthRestrict`)}}
+ q-item-label(caption) {{$t(`admin.users.pwdAuthRestrictHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='localAuth.restrictLogin'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.pwdAuthRestrict`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.tfa')}}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='key')
+ q-item-section
+ q-item-label {{$t(`admin.users.tfaRequired`)}}
+ q-item-label(caption) {{$t(`admin.users.tfaRequiredHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='localAuth.tfaRequired'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.users.tfaRequired`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='password', :hue-rotate='45')
+ q-item-section
+ q-item-label {{$t(`admin.users.tfaInvalidate`)}}
+ q-item-label(caption) {{$t(`admin.users.tfaInvalidateHint`)}}
+ q-item-label(caption): strong(:class='localAuth.tfaSecret ? `text-positive` : `text-negative`') {{localAuth.tfaSecret ? $t(`admin.users.tfaSet`) : $t(`admin.users.tfaNotSet`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='invalidateTFA'
+ :label='$t(`common.actions.proceed`)'
+ )
+ .col-12.col-lg-5
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.linkedProviders')}}
+ q-banner.q-mt-md(
+ v-if='linkedAuthProviders.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{$t('admin.users.noLinkedProviders')}}
+ template(
+ v-for='(prv, idx) in linkedAuthProviders'
+ :key='prv._id'
+ )
+ q-separator.q-my-sm(inset, v-if='idx > 0')
+ q-item
+ blueprint-icon(icon='google', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{prv._moduleName}}
+ q-item-label(caption) {{prv.key}}
+
+ q-page(v-else-if='$route.params.section === `groups`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-8
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.groups')}}
+ template(
+ v-for='(grp, idx) of user.groups'
+ :key='grp.id'
+ )
+ q-separator.q-my-sm(inset, v-if='idx > 0')
+ q-item
+ blueprint-icon(icon='team', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{grp.name}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-times'
+ color='accent'
+ @click='unassignGroup(grp.id)'
+ :aria-label='$t(`admin.users.unassignGroup`)'
+ )
+ q-tooltip(anchor='center left' self='center right') {{$t('admin.users.unassignGroup')}}
+ q-card.shadow-1.q-py-sm.q-mt-md
+ q-item
+ blueprint-icon(icon='join')
+ q-item-section
+ q-select(
+ outlined
+ :options='groups'
+ v-model='groupToAdd'
+ map-options
+ emit-value
+ option-value='id'
+ option-label='name'
+ options-dense
+ dense
+ hide-bottom-space
+ :label='$t(`admin.users.groups`)'
+ :aria-label='$t(`admin.users.groups`)'
+ :loading='loading > 0'
+ )
+ q-item-section(side)
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.users.assignGroup`)'
+ color='primary'
+ @click='assignGroup'
+ )
+
+ q-page(v-else-if='$route.params.section === `metadata`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-8
+ q-card.shadow-1.q-pb-sm
+ q-card-section.flex.items-center
+ .text-subtitle1 {{$t('admin.users.metadata')}}
+ q-space
+ q-badge(
+ v-if='metadataInvalidJSON'
+ color='negative'
+ )
+ q-icon.q-mr-xs(name='las la-exclamation-triangle', size='20px')
+ span {{$t('admin.users.invalidJSON')}}
+ q-badge.q-py-xs(
+ v-else
+ label='JSON'
+ color='positive'
+ )
+ q-item
+ q-item-section
+ q-no-ssr(:placeholder='$t(`common.loading`)')
+ util-code-editor.admin-theme-cm(
+ v-model='metadata'
+ language='json'
+ :min-height='500'
+ )
+
+ q-page(v-else-if='$route.params.section === `operations`')
+ .q-pa-md
+ .row.q-col-gutter-md
+ .col-12.col-lg-8
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.users.operations')}}
+ q-item
+ blueprint-icon(icon='email-open', :hue-rotate='45')
+ q-item-section
+ q-item-label {{$t(`admin.users.sendWelcomeEmail`)}}
+ q-item-label(caption) {{$t(`admin.users.sendWelcomeEmailAltHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='sendWelcomeEmail'
+ :label='$t(`common.actions.proceed`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='apply', :hue-rotate='45')
+ q-item-section
+ q-item-label {{user.isVerified ? $t(`admin.users.unverify`) : $t(`admin.users.verify`)}}
+ q-item-label(caption) {{user.isVerified ? $t(`admin.users.unverifyHint`) : $t(`admin.users.verifyHint`)}}
+ q-item-label(caption): strong(:class='user.isVerified ? `text-positive` : `text-negative`') {{user.isVerified ? $t(`admin.users.verified`) : $t(`admin.users.unverified`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='toggleVerified'
+ :label='$t(`common.actions.proceed`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='unfriend', :hue-rotate='45')
+ q-item-section
+ q-item-label {{user.isActive ? $t(`admin.users.ban`) : $t(`admin.users.unban`)}}
+ q-item-label(caption) {{user.isActive ? $t(`admin.users.banHint`) : $t(`admin.users.unbanHint`)}}
+ q-item-label(caption): strong(:class='user.isActive ? `text-positive` : `text-negative`') {{user.isActive ? $t(`admin.users.active`) : $t(`admin.users.banned`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='toggleBan'
+ :label='$t(`common.actions.proceed`)'
+ )
+ q-card.shadow-1.q-py-sm.q-mt-md
+ q-item
+ blueprint-icon(icon='denied', :hue-rotate='140')
+ q-item-section
+ q-item-label {{$t(`admin.users.delete`)}}
+ q-item-label(caption) {{$t(`admin.users.deleteHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='negative'
+ @click='deleteUser'
+ :label='$t(`common.actions.proceed`)'
+ )
+
+
+
diff --git a/ux/src/components/UtilCodeEditor.vue b/ux/src/components/UtilCodeEditor.vue
new file mode 100644
index 00000000..7755a2ba
--- /dev/null
+++ b/ux/src/components/UtilCodeEditor.vue
@@ -0,0 +1,113 @@
+
+.util-code-editor(
+ ref='editor'
+ )
+
+
+
+
+
diff --git a/ux/src/components/WebhookDeleteDialog.vue b/ux/src/components/WebhookDeleteDialog.vue
new file mode 100644
index 00000000..cec1df06
--- /dev/null
+++ b/ux/src/components/WebhookDeleteDialog.vue
@@ -0,0 +1,92 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{$t(`admin.webhooks.delete`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='admin.webhooks.deleteConfirm')
+ template(v-slot:name)
+ strong {{hook.name}}
+ .text-body2.q-mt-md
+ strong.text-negative {{$t(`admin.webhooks.deleteConfirmWarn`)}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ unelevated
+ :label='$t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/ux/src/components/WebhookEditDialog.vue b/ux/src/components/WebhookEditDialog.vue
new file mode 100644
index 00000000..677f3556
--- /dev/null
+++ b/ux/src/components/WebhookEditDialog.vue
@@ -0,0 +1,422 @@
+
+q-dialog(ref='dialog', @hide='onDialogHide')
+ q-card(style='min-width: 850px;')
+ q-card-section.card-header
+ template(v-if='hookId')
+ q-icon(name='img:/_assets/icons/fluent-pencil-drawing.svg', left, size='sm')
+ span {{$t(`admin.webhooks.edit`)}}
+ template(v-else)
+ q-icon(name='img:/_assets/icons/fluent-plus-plus.svg', left, size='sm')
+ span {{$t(`admin.webhooks.new`)}}
+ //- STATE INFO BAR
+ q-card-section.flex.items-center.bg-indigo.text-white(v-if='hookId && hook.state === `pending`')
+ q-spinner-clock.q-mr-sm(
+ color='white'
+ size='xs'
+ )
+ .text-caption {{$t('admin.webhooks.statePendingHint')}}
+ q-card-section.flex.items-center.bg-positive.text-white(v-if='hookId && hook.state === `success`')
+ q-spinner-infinity.q-mr-sm(
+ color='white'
+ size='xs'
+ )
+ .text-caption {{$t('admin.webhooks.stateSuccessHint')}}
+ q-card-section.bg-negative.text-white(v-if='hookId && hook.state === `error`')
+ .flex.items-center
+ q-icon.q-mr-sm(
+ color='white'
+ size='xs'
+ name='las la-exclamation-triangle'
+ )
+ .text-caption {{$t('admin.webhooks.stateErrorExplain')}}
+ .text-caption.q-pl-lg.q-ml-xs.text-red-2 {{hook.lastErrorMessage}}
+ //- FORM
+ q-form.q-py-sm(ref='editWebhookForm')
+ q-item
+ blueprint-icon(icon='info-popup')
+ q-item-section
+ q-input(
+ outlined
+ v-model='hook.name'
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.webhooks.nameMissing'),
+ val => /^[^<>"]+$/.test(val) || $t('admin.webhooks.nameInvalidChars')
+ ]`
+ hide-bottom-space
+ :label='$t(`common.field.name`)'
+ :aria-label='$t(`common.field.name`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ q-item
+ blueprint-icon(icon='lightning-bolt')
+ q-item-section
+ q-select(
+ outlined
+ :options='events'
+ v-model='hook.events'
+ multiple
+ map-options
+ emit-value
+ option-value='key'
+ option-label='name'
+ options-dense
+ dense
+ :rules=`[
+ val => val.length > 0 || $t('admin.webhooks.eventsMissing')
+ ]`
+ hide-bottom-space
+ :label='$t(`admin.webhooks.events`)'
+ :aria-label='$t(`admin.webhooks.events`)'
+ lazy-rules='ondemand'
+ )
+ template(v-slot:selected)
+ .text-caption(v-if='hook.events.length > 0') {{$tc(`admin.webhooks.eventsSelected`, hook.events.length, { count: hook.events.length })}}
+ span(v-else)
+ template(v-slot:option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ v-on='itemEvents'
+ )
+ q-item-section(side)
+ q-checkbox(
+ :model-value='selected'
+ @update:model-value='toggleOption(opt)'
+ size='sm'
+ )
+ q-item-section(side)
+ q-chip.q-mx-none(
+ size='sm'
+ color='positive'
+ text-color='white'
+ square
+ ) {{opt.type}}
+ q-item-section
+ q-item-label {{opt.name}}
+ q-item
+ blueprint-icon.self-start(icon='unknown-status')
+ q-item-section
+ q-item-label {{$t(`admin.webhooks.url`)}}
+ q-item-label(caption) {{$t(`admin.webhooks.urlHint`)}}
+ q-input.q-mt-sm(
+ outlined
+ v-model='hook.url'
+ dense
+ :rules=`[
+ val => (val.length > 0 && val.startsWith('http')) || $t('admin.webhooks.urlMissing'),
+ val => /^[^<>"]+$/.test(val) || $t('admin.webhooks.urlInvalidChars')
+ ]`
+ hide-bottom-space
+ placeholder='https://'
+ :aria-label='$t(`admin.webhooks.url`)'
+ lazy-rules='ondemand'
+ )
+ template(v-slot:prepend)
+ q-chip.q-mx-none(
+ color='positive'
+ text-color='white'
+ square
+ size='sm'
+ ) POST
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='rescan-document')
+ q-item-section
+ q-item-label {{$t(`admin.webhooks.includeMetadata`)}}
+ q-item-label(caption) {{$t(`admin.webhooks.includeMetadataHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='hook.includeMetadata'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.webhooks.includeMetadata`)'
+ )
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='select-all')
+ q-item-section
+ q-item-label {{$t(`admin.webhooks.includeContent`)}}
+ q-item-label(caption) {{$t(`admin.webhooks.includeContentHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='hook.includeContent'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.webhooks.includeContent`)'
+ )
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='security-ssl')
+ q-item-section
+ q-item-label {{$t(`admin.webhooks.acceptUntrusted`)}}
+ q-item-label(caption) {{$t(`admin.webhooks.acceptUntrustedHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='hook.acceptUntrusted'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.webhooks.acceptUntrusted`)'
+ )
+ q-item
+ blueprint-icon.self-start(icon='fingerprint-scan')
+ q-item-section
+ q-item-label {{$t(`admin.webhooks.authHeader`)}}
+ q-item-label(caption) {{$t(`admin.webhooks.authHeaderHint`)}}
+ q-input.q-mt-sm(
+ outlined
+ v-model='hook.authHeader'
+ dense
+ :aria-label='$t(`admin.webhooks.authHeader`)'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='$t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='hide'
+ )
+ q-btn(
+ v-if='hookId'
+ unelevated
+ :label='$t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='loading'
+ )
+ q-btn(
+ v-else
+ unelevated
+ :label='$t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='loading'
+ )
+
+ q-inner-loading(:showing='loading')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/ux/src/css/animation.scss b/ux/src/css/animation.scss
new file mode 100644
index 00000000..36edbf84
--- /dev/null
+++ b/ux/src/css/animation.scss
@@ -0,0 +1,140 @@
+// ------------------------------------------------------------------
+// ANIMATION
+// ------------------------------------------------------------------
+
+:root {
+ --animate-duration: .6s;
+ --animate-delay: 1s;
+ --animate-repeat: 1;
+}
+
+.animated {
+ animation-duration: var(--animate-duration);
+ animation-fill-mode: both;
+
+ &.infinite {
+ animation-iteration-count: infinite;
+ }
+}
+
+@for $i from 1 to 12 {
+ .wait-p#{$i}s {
+ animation-delay: $i * .1s !important;
+ }
+}
+
+// -> Fade Transition
+
+.fade-enter-active,
+.fade-leave-active {
+ transition-duration: 0.3s;
+ transition-property: opacity;
+ transition-timing-function: ease;
+}
+
+.fade-enter,
+.fade-leave-active {
+ opacity: 0
+}
+
+// -> fadeIn
+
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+
+ to {
+ opacity: 1;
+ }
+}
+
+.fadeIn {
+ animation-name: fadeIn;
+}
+
+// -> fadeInDown
+
+@keyframes fadeInDown {
+ from {
+ opacity: 0;
+ transform: translate3d(0, -10px, 0);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.fadeInDown {
+ animation-name: fadeInDown;
+}
+
+// -> fadeInLeft
+
+@keyframes fadeInLeft {
+ from {
+ opacity: 0;
+ transform: translate3d(-10px, 0, 0);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.fadeInLeft {
+ animation-name: fadeInLeft;
+}
+
+// -> fadeInRight
+
+@keyframes fadeInRight {
+ from {
+ opacity: 0;
+ transform: translate3d(10px, 0, 0);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.fadeInRight {
+ animation-name: fadeInRight;
+}
+
+// -> fadeInUp
+
+@keyframes fadeInUp {
+ from {
+ opacity: 0;
+ transform: translate3d(0, 10px, 0);
+ }
+
+ to {
+ opacity: 1;
+ transform: translate3d(0, 0, 0);
+ }
+}
+
+.fadeInUp {
+ animation-name: fadeInUp;
+}
+
+// -> Print + Reduce Motion
+
+@media print, (prefers-reduced-motion: reduce) {
+ .animated {
+ animation-duration: 1ms !important;
+ transition-duration: 1ms !important;
+ animation-iteration-count: 1 !important;
+ }
+
+ .animated[class*='Out'] {
+ opacity: 0;
+ }
+}
diff --git a/ux/src/css/app.scss b/ux/src/css/app.scss
new file mode 100644
index 00000000..fc4d6749
--- /dev/null
+++ b/ux/src/css/app.scss
@@ -0,0 +1,134 @@
+// app global css in SCSS form
+// ------------------------------------------------------------------
+// SCROLLBAR
+// ------------------------------------------------------------------
+
+html {
+ --scrollbarBG: #CCC;
+ --thumbBG: #999;
+}
+body::-webkit-scrollbar {
+ width: 7px;
+}
+body {
+ scrollbar-width: thin;
+ scrollbar-color: var(--thumbBG) var(--scrollbarBG);
+}
+body::-webkit-scrollbar-track {
+ background: var(--scrollbarBG);
+}
+body::-webkit-scrollbar-thumb {
+ background-color: var(--thumbBG);
+ border-radius: 6px;
+ border: 1px solid var(--scrollbarBG);
+}
+
+// ------------------------------------------------------------------
+// FONTS
+// ------------------------------------------------------------------
+
+@font-face {
+ font-family: 'Poppins';
+ src: url(./fonts/poppins-medium.woff2);
+}
+
+.font-poppins {
+ font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+}
+
+@font-face {
+ font-family: 'Roboto Mono';
+ src: url(./fonts/roboto-mono.woff2);
+}
+
+.font-robotomono {
+ font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace;
+}
+
+// ------------------------------------------------------------------
+// THEME COLORS
+// ------------------------------------------------------------------
+
+:root {
+ --q-header: #000;
+ --q-sidebar: #1976D2;
+}
+
+.header, .bg-header {
+ background: #000;
+ background: var(--q-header);
+}
+.sidebar, .bg-sidebar {
+ background: #1976D2;
+ background: var(--q-sidebar);
+}
+
+.bg-dark-6 { background-color: #070a0d; }
+.bg-dark-5 { background-color: #0d1117; }
+.bg-dark-4 { background-color: #161b22; }
+.bg-dark-3 { background-color: #1e232a; }
+
+// ------------------------------------------------------------------
+// FORMS
+// ------------------------------------------------------------------
+
+.v-textarea.is-monospaced textarea {
+ font-family: 'Roboto Mono', 'Courier New', Courier, monospace;
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 1.4;
+}
+
+.q-field.denser .q-field__control {
+ height: 36px;
+
+ .q-field__prepend {
+ height: 36px;
+ }
+}
+
+.q-field.fill-outline .q-field__control {
+ background-color: #FFF;
+
+ @at-root .body--light & {
+ background-color: #FFF;
+ }
+ @at-root .body--dark & {
+ background-color: $dark;
+ }
+}
+
+// ------------------------------------------------------------------
+// BUTTONS
+// ------------------------------------------------------------------
+
+body.desktop .acrylic-btn {
+ .q-focus-helper {
+ background-color: currentColor;
+ opacity: .1;
+ }
+
+ &:hover .q-focus-helper {
+ opacity: .3;
+ }
+}
+
+// ------------------------------------------------------------------
+// ICONS
+// ------------------------------------------------------------------
+
+.blueprint-icon {
+ &-alt {
+ filter: hue-rotate(100);
+ }
+}
+
+// ------------------------------------------------------------------
+// IMPORTS
+// ------------------------------------------------------------------
+
+@import './animation.scss';
+
+// @import '~codemirror/lib/codemirror.css';
+// @import '~codemirror/theme/elegant.css';
+// @import '~codemirror/theme/material-ocean.css';
diff --git a/ux/src/css/fonts/poppins-medium.woff2 b/ux/src/css/fonts/poppins-medium.woff2
new file mode 100644
index 00000000..eaca9a9b
Binary files /dev/null and b/ux/src/css/fonts/poppins-medium.woff2 differ
diff --git a/ux/src/css/fonts/raleway-medium.woff2 b/ux/src/css/fonts/raleway-medium.woff2
new file mode 100644
index 00000000..8c136296
Binary files /dev/null and b/ux/src/css/fonts/raleway-medium.woff2 differ
diff --git a/ux/src/css/fonts/roboto-mono.woff2 b/ux/src/css/fonts/roboto-mono.woff2
new file mode 100644
index 00000000..213e0cc2
Binary files /dev/null and b/ux/src/css/fonts/roboto-mono.woff2 differ
diff --git a/ux/src/css/quasar.variables.scss b/ux/src/css/quasar.variables.scss
new file mode 100644
index 00000000..4028dfad
--- /dev/null
+++ b/ux/src/css/quasar.variables.scss
@@ -0,0 +1,32 @@
+// Quasar SCSS (& Sass) Variables
+// --------------------------------------------------
+// To customize the look and feel of this app, you can override
+// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
+
+// Check documentation for full list of Quasar variables
+
+// Your own variables (that are declared here) and Quasar's own
+// ones will be available out of the box in your .vue/.scss/.sass files
+
+// It's highly recommended to change the default colors
+// to match your app's branding.
+// Tip: Use the "Theme Builder" on Quasar's documentation website.
+
+$primary : #1976D2;
+$secondary : #02C39A;
+$accent : #f03a47;
+
+$dark : #0d1117;
+
+$positive : #02C39A;
+$negative : #f03a47;
+$info : #3e6990;
+$warning : #f99d4d;
+
+$header : #000;
+$sidebar: $primary;
+
+$dark-6: #070a0d;
+$dark-5: #0d1117;
+$dark-4: #161b22;
+$dark-3: #1e232a;
diff --git a/ux/src/i18n/index.js b/ux/src/i18n/index.js
new file mode 100644
index 00000000..1e05841a
--- /dev/null
+++ b/ux/src/i18n/index.js
@@ -0,0 +1,5 @@
+import en from './locales/en.json'
+
+export default {
+ 'en-US': en
+}
diff --git a/ux/src/i18n/locales/en.json b/ux/src/i18n/locales/en.json
new file mode 100644
index 00000000..8d6c5099
--- /dev/null
+++ b/ux/src/i18n/locales/en.json
@@ -0,0 +1,1423 @@
+{
+ "admin.adminArea": "Administration Area",
+ "admin.analytics.providerConfiguration": "Provider Configuration",
+ "admin.analytics.providerNoConfiguration": "This provider has no configuration options you can modify.",
+ "admin.analytics.providers": "Providers",
+ "admin.analytics.refreshSuccess": "List of providers refreshed successfully.",
+ "admin.analytics.saveSuccess": "Analytics configuration saved successfully",
+ "admin.analytics.subtitle": "Add analytics and tracking tools to your wiki",
+ "admin.analytics.title": "Analytics",
+ "admin.api.disableButton": "Disable API",
+ "admin.api.disabled": "API Disabled",
+ "admin.api.enableButton": "Enable API",
+ "admin.api.enabled": "API Enabled",
+ "admin.api.expiration180d": "180 days",
+ "admin.api.expiration1y": "1 year",
+ "admin.api.expiration30d": "30 days",
+ "admin.api.expiration3y": "3 years",
+ "admin.api.expiration90d": "90 days",
+ "admin.api.headerCreated": "Created",
+ "admin.api.headerExpiration": "Expiration",
+ "admin.api.headerKeyEnding": "Key Ending",
+ "admin.api.headerLastUpdated": "Last Updated",
+ "admin.api.headerName": "Name",
+ "admin.api.headerRevoke": "Revoke",
+ "admin.api.newKeyButton": "New API Key",
+ "admin.api.newKeyCopyWarn": "Copy the key shown below as {bold}",
+ "admin.api.newKeyCopyWarnBold": "it will NOT be shown again",
+ "admin.api.newKeyExpiration": "Expiration",
+ "admin.api.newKeyExpirationHint": "You can still revoke a key anytime regardless of the expiration.",
+ "admin.api.newKeyFullAccess": "Full Access",
+ "admin.api.newKeyGroup": "Group",
+ "admin.api.newKeyGroupError": "You must select a group.",
+ "admin.api.newKeyGroupHint": "The API key will have the same permissions as the selected group.",
+ "admin.api.newKeyGroupPermissions": "or use group permissions...",
+ "admin.api.newKeyGuestGroupError": "The guests group cannot be used for API keys.",
+ "admin.api.newKeyName": "Name",
+ "admin.api.newKeyNameError": "Name is missing or invalid.",
+ "admin.api.newKeyNameHint": "Purpose of this key",
+ "admin.api.newKeyPermissionScopes": "Permission Scopes",
+ "admin.api.newKeySuccess": "API key created successfully.",
+ "admin.api.newKeyTitle": "New API Key",
+ "admin.api.noKeyInfo": "No API keys have been generated yet.",
+ "admin.api.refreshSuccess": "List of API keys has been refreshed.",
+ "admin.api.revoke": "Revoke",
+ "admin.api.revokeConfirm": "Revoke API Key?",
+ "admin.api.revokeConfirmText": "Are you sure you want to revoke key {name}? This action cannot be undone!",
+ "admin.api.revokeSuccess": "The key has been revoked successfully.",
+ "admin.api.subtitle": "Manage keys to access the API",
+ "admin.api.title": "API Access",
+ "admin.api.toggleStateDisabledSuccess": "API has been disabled successfully.",
+ "admin.api.toggleStateEnabledSuccess": "API has been enabled successfully.",
+ "admin.approval.title": "Approvals",
+ "admin.audit.title": "Audit Log",
+ "admin.auth.activeStrategies": "Active Strategies",
+ "admin.auth.addStrategy": "Add Strategy",
+ "admin.auth.allowedWebOrigins": "Allowed Web Origins",
+ "admin.auth.autoEnrollGroups": "Assign to group",
+ "admin.auth.autoEnrollGroupsHint": "Automatically assign new users to these groups.",
+ "admin.auth.callbackUrl": "Callback URL / Redirect URI",
+ "admin.auth.configReference": "Configuration Reference",
+ "admin.auth.configReferenceSubtitle": "Some strategies may require some configuration values to be set on your provider. These are provided for reference only and may not be needed by the current strategy.",
+ "admin.auth.displayName": "Display Name",
+ "admin.auth.displayNameHint": "The title shown to the end user for this authentication strategy.",
+ "admin.auth.domainsWhitelist": "Limit to specific email domains",
+ "admin.auth.domainsWhitelistHint": "A list of domains authorized to register. The user email address domain must match one of these to gain access.",
+ "admin.auth.force2fa": "Force all users to use Two-Factor Authentication (2FA)",
+ "admin.auth.force2faHint": "Users will be required to setup 2FA the first time they login and cannot be disabled by the user.",
+ "admin.auth.globalAdvSettings": "Global Advanced Settings",
+ "admin.auth.loginUrl": "Login URL",
+ "admin.auth.logoutUrl": "Logout URL",
+ "admin.auth.refreshSuccess": "List of strategies has been refreshed.",
+ "admin.auth.registration": "Registration",
+ "admin.auth.saveSuccess": "Authentication configuration saved successfully.",
+ "admin.auth.security": "Security",
+ "admin.auth.selfRegistration": "Allow self-registration",
+ "admin.auth.selfRegistrationHint": "Allow any user successfully authorized by the strategy to access the wiki.",
+ "admin.auth.siteUrlNotSetup": "You must set a valid {siteUrl} first! Click on {general} in the left sidebar.",
+ "admin.auth.strategies": "Strategies",
+ "admin.auth.strategyConfiguration": "Strategy Configuration",
+ "admin.auth.strategyIsEnabled": "Active",
+ "admin.auth.strategyIsEnabledHint": "Are users able to login using this strategy?",
+ "admin.auth.strategyNoConfiguration": "This strategy has no configuration options you can modify.",
+ "admin.auth.strategyState": "This strategy is {state} {locked}",
+ "admin.auth.strategyStateActive": "active",
+ "admin.auth.strategyStateInactive": "not active",
+ "admin.auth.strategyStateLocked": "and cannot be disabled.",
+ "admin.auth.subtitle": "Configure the authentication settings of your wiki",
+ "admin.auth.title": "Authentication",
+ "admin.comments.provider": "Provider",
+ "admin.comments.providerConfig": "Provider Configuration",
+ "admin.comments.providerNoConfig": "This provider has no configuration options you can modify.",
+ "admin.comments.subtitle": "Add discussions to your wiki pages",
+ "admin.comments.title": "Comments",
+ "admin.contribute.title": "Donate",
+ "admin.dashboard.contributeHelp": "We need your help!",
+ "admin.dashboard.contributeLearnMore": "Learn More",
+ "admin.dashboard.contributeSubtitle": "Wiki.js is a free and open source project. There are several ways you can contribute to the project.",
+ "admin.dashboard.groups": "Groups",
+ "admin.dashboard.lastLogins": "Last Logins",
+ "admin.dashboard.mostPopularPages": "Most Popular Pages",
+ "admin.dashboard.pages": "Pages",
+ "admin.dashboard.recentPages": "Recent Pages",
+ "admin.dashboard.subtitle": "Wiki.js",
+ "admin.dashboard.title": "Dashboard",
+ "admin.dashboard.users": "Users",
+ "admin.dashboard.versionLatest": "You are running the latest version.",
+ "admin.dashboard.versionNew": "A new version is available: {version}",
+ "admin.dev.flags.title": "Flags",
+ "admin.dev.graphiql.title": "GraphiQL",
+ "admin.dev.title": "Developer Tools",
+ "admin.dev.voyager.title": "Voyager",
+ "admin.editors.apiDescription": "Document your REST / GraphQL APIs.",
+ "admin.editors.apiName": "API Docs Editor",
+ "admin.editors.blogDescription": "Write a series of posts over time.",
+ "admin.editors.blogName": "Blog Editor",
+ "admin.editors.channelDescription": "Create discussion channels to collaborate in real-time with your team.",
+ "admin.editors.channelName": "Discussion Channels",
+ "admin.editors.configuration": "Configuration",
+ "admin.editors.markdownDescription": "Use the Markdown syntax to write content. Includes real-time preview and code completion features.",
+ "admin.editors.markdownName": "Markdown Editor",
+ "admin.editors.redirectDescription": "Create redirections to other pages / external links.",
+ "admin.editors.redirectName": "Redirection",
+ "admin.editors.subtitle": "Manage editors and their configuration",
+ "admin.editors.title": "Editors",
+ "admin.editors.wysiwygDescription": "A visual WYSIWYG editor. The recommended editor for non-technical users.",
+ "admin.editors.wysiwygName": "Visual Editor",
+ "admin.extensions.incompatible": "not compatible",
+ "admin.extensions.install": "Install",
+ "admin.extensions.installFailed": "Failed to install extension.",
+ "admin.extensions.installSuccess": "Extension installed successfully.",
+ "admin.extensions.installed": "Installed",
+ "admin.extensions.installing": "Installing extension...",
+ "admin.extensions.installingHint": "This may take a while depending on your server.",
+ "admin.extensions.instructions": "Instructions",
+ "admin.extensions.instructionsHint": "Must be installed manually",
+ "admin.extensions.reinstall": "Reinstall",
+ "admin.extensions.requiresSharp": "Requires Sharp extension",
+ "admin.extensions.subtitle": "Install extensions for extra functionality",
+ "admin.extensions.title": "Extensions",
+ "admin.flags.hidedonatebtn.hint": "You have already donated to this project (thank you!) and want to hide the button from the administration area.",
+ "admin.flags.hidedonatebtn.label": "Hide Donate Button",
+ "admin.flags.ldapdebug.hint": "Log detailed debug info on LDAP/AD login attempts.",
+ "admin.flags.ldapdebug.label": "LDAP Debug",
+ "admin.flags.sqllog.hint": "Log all queries made to the database to console.",
+ "admin.flags.sqllog.label": "SQL Query Logging",
+ "admin.flags.subtitle": "Low-level system flags for debugging or experimental purposes",
+ "admin.flags.title": "Flags",
+ "admin.flags.warn.hint": "Doing so may result in data loss or broken installation!",
+ "admin.flags.warn.label": "Do NOT enable these flags unless you know what you're doing!",
+ "admin.general.allowComments": "Allow Comments",
+ "admin.general.allowCommentsHint": "Can users leave comments on pages? Can be restricted using Page Rules.",
+ "admin.general.allowContributions": "Allow Contributions",
+ "admin.general.allowContributionsHint": "Can users with read access permissions propose changes for pages? Can be restricted using Page Rules.",
+ "admin.general.allowProfile": "Allow Profile Editing",
+ "admin.general.allowProfileHint": "Can users edit their own profile? If profile data is managed by an external identity provider, you should turn this off.",
+ "admin.general.allowRatings": "Allow Ratings",
+ "admin.general.allowRatingsHint": "Can users leave ratings on pages? Can be restricted using Page Rules.",
+ "admin.general.allowSearch": "Allow Search",
+ "admin.general.allowSearchHint": "Can users search for content they have read access to?",
+ "admin.general.companyName": "Company / Organization Name",
+ "admin.general.companyNameHint": "Name to use when displaying copyright notice in the footer. Leave empty to hide.",
+ "admin.general.contentLicense": "Content License",
+ "admin.general.contentLicenseHint": "License shown in the footer of all content pages.",
+ "admin.general.defaultDateFormat": "Default Date Format",
+ "admin.general.defaultDateFormatHint": "The default date format for new users.",
+ "admin.general.defaultTimeFormat": "Default Time Format",
+ "admin.general.defaultTimeFormat12h": "12 hour",
+ "admin.general.defaultTimeFormat24h": "24 hour",
+ "admin.general.defaultTimeFormatHint": "The default time format for new users.",
+ "admin.general.defaultTimezone": "Default Timezone",
+ "admin.general.defaultTimezoneHint": "The default timezone for new users.",
+ "admin.general.defaults": "Site Defaults",
+ "admin.general.displaySiteTitle": "Display Site Title",
+ "admin.general.displaySiteTitleHint": "Should the site title be displayed next to the logo? If your logo isn't square and contain your brand name, turn this option off.",
+ "admin.general.favicon": "Favicon",
+ "admin.general.faviconHint": "Favicon image file, in SVG, PNG, ICO or GIF format. Must be a square image.",
+ "admin.general.faviconUploadSuccess": "Site Favicon uploaded successfully.",
+ "admin.general.features": "Features",
+ "admin.general.footerCopyright": "Footer / Copyright",
+ "admin.general.general": "General",
+ "admin.general.logo": "Logo",
+ "admin.general.logoUpl": "Site Logo",
+ "admin.general.logoUplHint": "Logo image file, in SVG, PNG, JPG or GIF format.",
+ "admin.general.logoUploadSuccess": "Site logo uploaded successfully.",
+ "admin.general.ratingsOff": "Off",
+ "admin.general.ratingsStars": "Stars",
+ "admin.general.ratingsThumbs": "Thumbs",
+ "admin.general.saveSuccess": "Site configuration saved successfully.",
+ "admin.general.searchAllowFollow": "Allow Search Engines to Follow Links",
+ "admin.general.searchAllowFollowHint": "This sets the meta-robots property to follow or nofollow.",
+ "admin.general.searchAllowIndexing": "Allow Indexing by Search Engines",
+ "admin.general.searchAllowIndexingHint": "This sets the meta-robots property to index or noindex.",
+ "admin.general.senderEmailHint": "Email address of the sender.",
+ "admin.general.senderNameHint": "Name of the sender.",
+ "admin.general.siteBranding": "Site Branding",
+ "admin.general.siteDescription": "Site Description",
+ "admin.general.siteDescriptionHint": "Default description when none is provided for a page.",
+ "admin.general.siteHostname": "Site Hostname",
+ "admin.general.siteHostnameHint": "Hostname this site should respond to. Set * for catch-all / fallback domain.",
+ "admin.general.siteHostnameInvalid": "Invalid Hostname",
+ "admin.general.siteInfo": "Site Info",
+ "admin.general.siteTitle": "Site Title",
+ "admin.general.siteTitleHint": "Displayed in the top bar and appended to all pages meta title.",
+ "admin.general.siteTitleInvalidChars": "Site Title contains invalid characters.",
+ "admin.general.subtitle": "Main settings of your wiki",
+ "admin.general.title": "General",
+ "admin.general.uploadClear": "Clear",
+ "admin.general.uploadLogo": "Upload Logo",
+ "admin.general.uploadSizeHint": "An image of {size} pixels is recommended for best results.",
+ "admin.general.uploadTypesHint": "{typeList} or {lastType} files only",
+ "admin.groups.assignUser": "Assign User",
+ "admin.groups.authBehaviors": "Authentication Behaviors",
+ "admin.groups.create": "New Group",
+ "admin.groups.createSuccess": "Group created successfully.",
+ "admin.groups.delete": "Delete Group",
+ "admin.groups.deleteConfirm": "Are you sure you want delete group {groupName}? Any user currently assigned to this group will be unassigned from it.",
+ "admin.groups.deleteConfirmWarn": "This action cannot be undone!",
+ "admin.groups.deleteSuccess": "Group was deleted successfully.",
+ "admin.groups.edit": "Edit Group",
+ "admin.groups.exportRules": "Export Rules",
+ "admin.groups.exportRulesNoneError": "This group has no rule to export!",
+ "admin.groups.filterUsers": "Filter...",
+ "admin.groups.general": "General",
+ "admin.groups.importFailed": "Something went wrong while importing this file. Making sure it is a valid rules JSON file.",
+ "admin.groups.importModeAdd": "Add to Existing",
+ "admin.groups.importModeReplace": "Replace All",
+ "admin.groups.importModeText": "Do you want to replace all existing rules with these ones or add these rules to the existing ones?",
+ "admin.groups.importModeTitle": "Add or replace?",
+ "admin.groups.importRules": "Import Rules",
+ "admin.groups.importSuccess": "Rules imported successfully!",
+ "admin.groups.info": "Group Info",
+ "admin.groups.name": "Group Name",
+ "admin.groups.nameHint": "Name of the group",
+ "admin.groups.overview": "Overview",
+ "admin.groups.permissions": "Permissions",
+ "admin.groups.redirectOnFirstLogin": "First-time Login Redirect",
+ "admin.groups.redirectOnFirstLoginHint": "Optionally redirect the user to a specific page when he/she login for the first time. Leave empty to use the site-defined value.",
+ "admin.groups.redirectOnLogin": "Redirect on Login",
+ "admin.groups.redirectOnLoginHint": "The path / URL where the user will be redirected upon successful login. Leave empty to use the site-defined value.",
+ "admin.groups.redirectOnLogout": "Redirect on Logout",
+ "admin.groups.redirectOnLogoutHint": "The path / URL where the user will be redirected upon logout. Leave empty to use the site-defined value.",
+ "admin.groups.refreshSuccess": "Groups refreshed successfully.",
+ "admin.groups.ruleAllow": "Allow",
+ "admin.groups.ruleDeny": "Deny",
+ "admin.groups.ruleForceAllow": "Force Allow",
+ "admin.groups.ruleLocales": "Locale(s)",
+ "admin.groups.ruleMatchEnd": "Path Ends With...",
+ "admin.groups.ruleMatchExact": "Path Is Exactly...",
+ "admin.groups.ruleMatchRegex": "Path Matches Regex...",
+ "admin.groups.ruleMatchStart": "Path Starts With...",
+ "admin.groups.ruleMatchTag": "Has Any Tag...",
+ "admin.groups.ruleMatchTagAll": "Has All Tags...",
+ "admin.groups.ruleSites": "Site(s)",
+ "admin.groups.ruleUntitled": "Untitled Rule",
+ "admin.groups.rules": "Rules",
+ "admin.groups.rulesNone": "This group doesn't have any rules yet.",
+ "admin.groups.selectedLocales": "Any Locale | {locale} locale only | {count} locales selected",
+ "admin.groups.selectedSites": "Any Site | 1 site selected | {count} sites selected",
+ "admin.groups.subtitle": "Manage user groups and permissions",
+ "admin.groups.title": "Groups",
+ "admin.groups.userCount": "User Count",
+ "admin.groups.users": "Users",
+ "admin.groups.usersCount": "0 user | 1 user | {count} users",
+ "admin.locale.activeNamespaces": "Active Namespaces",
+ "admin.locale.autoUpdate.hint": "Automatically download updates to this locale as they become available.",
+ "admin.locale.autoUpdate.hintWithNS": "Automatically download updates to all namespaced locales enabled below.",
+ "admin.locale.autoUpdate.label": "Update Automatically",
+ "admin.locale.availability": "Availability",
+ "admin.locale.base.hint": "All UI text elements will be displayed in selected language.",
+ "admin.locale.base.label": "Site Locale",
+ "admin.locale.base.labelWithNS": "Base Locale",
+ "admin.locale.code": "Code",
+ "admin.locale.download": "Download",
+ "admin.locale.downloadNew": "Install New Locale",
+ "admin.locale.downloadTitle": "Download Locale",
+ "admin.locale.name": "Name",
+ "admin.locale.namespaces.hint": "Enables multiple language versions of the same page.",
+ "admin.locale.namespaces.label": "Multilingual Namespaces",
+ "admin.locale.namespacing": "Multilingual Namespacing",
+ "admin.locale.namespacingPrefixWarning.subtitle": "Paths without a locale code will be automatically redirected to the base locale defined above.",
+ "admin.locale.namespacingPrefixWarning.title": "The locale code will be prefixed to all paths. (e.g. /{langCode}/page-name)",
+ "admin.locale.nativeName": "Native Name",
+ "admin.locale.rtl": "RTL",
+ "admin.locale.settings": "Locale Settings",
+ "admin.locale.sideload": "Sideload Locale Package",
+ "admin.locale.sideloadHelp": "If you are not connected to the internet or cannot download locale files using the method above, you can instead sideload packages manually by uploading them below.",
+ "admin.locale.subtitle": "Set localization options for your wiki",
+ "admin.locale.title": "Locale",
+ "admin.logging.title": "Logging",
+ "admin.login.background": "Background Image",
+ "admin.login.backgroundHint": "Specify an image to use as the login background. PNG and JPG are supported, 1920x1080 recommended. Leave empty for default.",
+ "admin.login.bypassScreen": "Bypass Login Screen",
+ "admin.login.bypassScreenHint": "Should the user be redirected automatically to the first authentication provider. Has no effect if the first provider is a username/password provider type.",
+ "admin.login.bypassUnauthorized": "Bypass Unauthorized Screen",
+ "admin.login.bypassUnauthorizedHint": "Always redirect the user to the login screen instead of showing an unauthorized error page when the user is not logged in.",
+ "admin.login.experience": "User Experience",
+ "admin.login.loginRedirect": "Login Redirect",
+ "admin.login.loginRedirectHint": "Optionally redirect the user to a specific page when he/she logins (except if first time login which is defined below). This can be overridden at the group level.",
+ "admin.login.logoutRedirect": "Logout Redirect",
+ "admin.login.logoutRedirectHint": "Optionally redirect the user to a specific page when he/she logouts. This can be overridden at the group level.",
+ "admin.login.providers": "Login Providers",
+ "admin.login.providersVisbleWarning": "Note that you can always temporarily show all hidden providers by adding ?all to the url. This is useful to login as local admin while hiding it from normal users.",
+ "admin.login.subtitle": "Configure the login user experience of your wiki site",
+ "admin.login.title": "Login",
+ "admin.login.welcomeRedirect": "First-time Login Redirect",
+ "admin.login.welcomeRedirectHint": "Optionally redirect the user to a specific page when he/she login for the first time. This can be overridden at the group level.",
+ "admin.mail.configuration": "Configuration",
+ "admin.mail.dkim": "DKIM (optional)",
+ "admin.mail.dkimDomainName": "Domain Name",
+ "admin.mail.dkimDomainNameHint": "Domain name used for DKIM validation.",
+ "admin.mail.dkimHint": "DKIM (DomainKeys Identified Mail) provides a layer of security on all emails sent from Wiki.js by providing the means for recipients to validate the domain name and ensure the message authenticity.",
+ "admin.mail.dkimKeySelector": "Key Selector",
+ "admin.mail.dkimKeySelectorHint": "Determines which key to use for DKIM in your DNS records.",
+ "admin.mail.dkimPrivateKey": "Private Key",
+ "admin.mail.dkimPrivateKeyHint": "Private key for the selector in PEM format",
+ "admin.mail.dkimUse": "Use DKIM",
+ "admin.mail.dkimUseHint": "Should DKIM be used when sending emails.",
+ "admin.mail.saveSuccess": "Configuration saved successfully.",
+ "admin.mail.sendTestSuccess": "A test email was sent successfully.",
+ "admin.mail.sender": "Sender",
+ "admin.mail.senderEmail": "Sender Email",
+ "admin.mail.senderName": "Sender Name",
+ "admin.mail.smtp": "SMTP Settings",
+ "admin.mail.smtpHost": "Host",
+ "admin.mail.smtpHostHint": "Hostname or IP address of the SMTP server.",
+ "admin.mail.smtpPort": "Port",
+ "admin.mail.smtpPortHint": "Usually 465 (recommended), 587 or 25.",
+ "admin.mail.smtpPwd": "Password",
+ "admin.mail.smtpPwdHint": "Password used for authenticating to the SMTP server.",
+ "admin.mail.smtpTLS": "Secure (TLS)",
+ "admin.mail.smtpTLSHint": "Should be enabled when using port 465, otherwise turned off (587 or 25).",
+ "admin.mail.smtpUser": "Username",
+ "admin.mail.smtpUserHint": "Username used for authenticating to the SMTP server.",
+ "admin.mail.smtpVerifySSL": "Verify SSL Certificate",
+ "admin.mail.smtpVerifySSLHint": "Some hosts requires SSL certificate checking to be disabled. Leave enabled for proper security.",
+ "admin.mail.subtitle": "Configure mail settings",
+ "admin.mail.templateResetPwd": "Password Reset Email",
+ "admin.mail.templateWelcome": "Welcome Email",
+ "admin.mail.templates": "Mail Templates",
+ "admin.mail.test": "Send a test email",
+ "admin.mail.testHint": "Send a test email to ensure your SMTP configuration is working.",
+ "admin.mail.testRecipient": "Recipient Email Address",
+ "admin.mail.testRecipientHint": "Email address that should receive the test email.",
+ "admin.mail.testSend": "Send Email",
+ "admin.mail.title": "Mail",
+ "admin.nav.modules": "Modules",
+ "admin.nav.site": "Site",
+ "admin.nav.system": "System",
+ "admin.nav.users": "Users",
+ "admin.navigation.copyFromLocale": "Copy from locale...",
+ "admin.navigation.copyFromLocaleInfoText": "Select the locale from which items will be copied from. Items will be appended to the current list of items in the active locale.",
+ "admin.navigation.delete": "Delete {kind}",
+ "admin.navigation.divider": "Divider",
+ "admin.navigation.edit": "Edit {kind}",
+ "admin.navigation.emptyList": "Navigation is empty",
+ "admin.navigation.header": "Header",
+ "admin.navigation.icon": "Icon",
+ "admin.navigation.label": "Label",
+ "admin.navigation.link": "Link",
+ "admin.navigation.mode": "Navigation Mode",
+ "admin.navigation.modeCustom.description": "Static Navigation Menu + Site Tree Button",
+ "admin.navigation.modeCustom.title": "Custom Navigation",
+ "admin.navigation.modeNone.description": "Disable Site Navigation",
+ "admin.navigation.modeNone.title": "None",
+ "admin.navigation.modeSiteTree.description": "Classic Tree-based Navigation",
+ "admin.navigation.modeSiteTree.title": "Site Tree",
+ "admin.navigation.modeStatic.description": "Static Navigation Menu Only",
+ "admin.navigation.modeStatic.title": "Static Navigation",
+ "admin.navigation.navType.external": "External Link",
+ "admin.navigation.navType.externalblank": "External Link (New Window)",
+ "admin.navigation.navType.home": "Home",
+ "admin.navigation.navType.page": "Page",
+ "admin.navigation.navType.searchQuery": "Search Query",
+ "admin.navigation.noItemsText": "Click the Add button to add your first navigation item.",
+ "admin.navigation.noSelectionText": "Select a navigation item on the left.",
+ "admin.navigation.saveSuccess": "Navigation saved successfully.",
+ "admin.navigation.selectPageButton": "Select Page...",
+ "admin.navigation.sourceLocale": "Source Locale",
+ "admin.navigation.sourceLocaleHint": "The locale from which navigation items will be copied from.",
+ "admin.navigation.subtitle": "Manage the site navigation",
+ "admin.navigation.target": "Target",
+ "admin.navigation.targetType": "Target Type",
+ "admin.navigation.title": "Navigation",
+ "admin.navigation.untitled": "Untitled {kind}",
+ "admin.navigation.visibilityMode.all": "Visible to everyone",
+ "admin.navigation.visibilityMode.restricted": "Visible to select groups...",
+ "admin.pages.title": "Pages",
+ "admin.rendering.subtitle": "Configure the page rendering pipeline",
+ "admin.rendering.title": "Rendering",
+ "admin.search.configSaveSuccess": "Search engine configuration saved successfully.",
+ "admin.search.engineConfig": "Engine Configuration",
+ "admin.search.engineNoConfig": "This engine has no configuration options you can modify.",
+ "admin.search.indexRebuildSuccess": "Index rebuilt successfully.",
+ "admin.search.listRefreshSuccess": "List of search engines has been refreshed.",
+ "admin.search.rebuildIndex": "Rebuild Index",
+ "admin.search.searchEngine": "Search Engine",
+ "admin.search.subtitle": "Configure the search capabilities of your wiki",
+ "admin.search.title": "Search Engine",
+ "admin.security.cors": "CORS (Cross-Origin Resource Sharing)",
+ "admin.security.corsHostnames": "Hostnames Whitelist",
+ "admin.security.corsHostnamesHint": "Enter one hostname per line",
+ "admin.security.corsMode": "CORS Mode",
+ "admin.security.corsModeHint": "How the GraphQL server should handle preflight requests?",
+ "admin.security.corsRegex": "Regex Pattern",
+ "admin.security.corsRegexHint": "Pattern against which the request hostname is matched.",
+ "admin.security.disallowFloc": "Disallow Google FLoC",
+ "admin.security.disallowFlocHint": "Broadcast that this website should be opted-out of Google Federed Learning of Cohorts (FLoC). Recommended for privacy.",
+ "admin.security.disallowIframe": "Disallow iFrame Embedding",
+ "admin.security.disallowIframeHint": "Prevents other websites from embedding your wiki in an iframe. This provides clickjacking protection.",
+ "admin.security.disallowOpenRedirect": "Block Open Redirect",
+ "admin.security.disallowOpenRedirectHint": "Prevents user controlled URLs from directing to websites outside of your wiki. This provides Open Redirect protection.",
+ "admin.security.enforce2fa": "Enforce 2FA",
+ "admin.security.enforce2faHint": "Force all users to use Two-Factor Authentication when using an authentication provider with a user / password form.",
+ "admin.security.enforceHsts": "Enforce HSTS",
+ "admin.security.enforceHstsHint": "This ensures the connection cannot be established through an insecure HTTP connection.",
+ "admin.security.enforceSameOriginReferrerPolicy": "Enforce Same-Origin Referrer Policy",
+ "admin.security.enforceSameOriginReferrerPolicyHint": "Whether the referrer information should be included to requests to external endpoints.",
+ "admin.security.forceAssetDownload": "Force Asset Download for Unsafe Extensions",
+ "admin.security.forceAssetDownloadHint": "Should non-image files be forced as downloads when accessed directly. This prevents potential XSS attacks.",
+ "admin.security.hsts": "HSTS (HTTP Strict Transport Security)",
+ "admin.security.hstsDuration": "HSTS Max Age",
+ "admin.security.hstsDurationHint": "Defines the duration for which the server should only deliver content through HTTPS. It's a good idea to start with small values and make sure that nothing breaks on your wiki before moving to longer values.",
+ "admin.security.jwt": "JWT Configuration",
+ "admin.security.jwtAudience": "JWT Audience",
+ "admin.security.jwtAudienceHint": "Audience URN used in JWT issued upon login. Usually your domain name. (e.g. urn:your.domain.com)",
+ "admin.security.loginScreen": "Login Screen",
+ "admin.security.maxUploadBatch": "Max Files per Upload",
+ "admin.security.maxUploadBatchHint": "How many files can be uploaded in a single batch?",
+ "admin.security.maxUploadBatchSuffix": "files",
+ "admin.security.maxUploadSize": "Max Upload Size",
+ "admin.security.maxUploadSizeHint": "The maximum size for a single file. Final value in base 2.",
+ "admin.security.maxUploadSizeSuffix": "bytes",
+ "admin.security.saveSuccess": "Security configuration updated successfully.",
+ "admin.security.scanSVG": "Scan and Sanitize SVG Uploads",
+ "admin.security.scanSVGHint": "Should SVG uploads be scanned for vulnerabilities and stripped of any potentially unsafe content.",
+ "admin.security.subtitle": "Configure security settings",
+ "admin.security.title": "Security",
+ "admin.security.tokenEndpointAuthMethod": "Token Endpoint Authentication Method",
+ "admin.security.tokenExpiration": "Token Expiration",
+ "admin.security.tokenExpirationHint": "The expiration period of a token until it must be renewed. (default: 30m)",
+ "admin.security.tokenRenewalPeriod": "Token Renewal Period",
+ "admin.security.tokenRenewalPeriodHint": "The maximum period a token can be renewed when expired. (default: 14d)",
+ "admin.security.trustProxy": "Trust X-Forwarded-* Proxy Headers",
+ "admin.security.trustProxyHint": "Should be enabled when using a reverse-proxy like nginx, apache, CloudFlare, etc in front of Wiki.js. Turn off otherwise.",
+ "admin.security.uploads": "Uploads",
+ "admin.security.uploadsInfo": "These settings only affect Wiki.js. If you're using a reverse-proxy (e.g. nginx, Apache, Cloudflare), you must also change its settings to match.",
+ "admin.security.warn": "Make sure to understand the implications before turning on / off a security feature.",
+ "admin.sites.activate": "Activate Site",
+ "admin.sites.activateConfirm": "Are you sure you want activate site {siteTitle}? The site will become accessible to users with read access.",
+ "admin.sites.createSuccess": "Site created successfully.",
+ "admin.sites.deactivate": "Deactivate Site",
+ "admin.sites.deactivateConfirm": "Are you sure you want deactivate site {siteTitle}? The site will no longer be accessible to users.",
+ "admin.sites.delete": "Delete Site",
+ "admin.sites.deleteConfirm": "Are you sure you want delete site {siteTitle}? This will permanently delete all site content (including pages, history, comments and assets) and configuration!",
+ "admin.sites.deleteConfirmWarn": "This action cannot be undone!",
+ "admin.sites.deleteSuccess": "Site was deleted successfully.",
+ "admin.sites.edit": "Edit Site",
+ "admin.sites.hostname": "Hostname",
+ "admin.sites.hostnameHint": "Must be a fully-qualified domain name (e.g. wiki.example.com) or * for a catch-all site. Note that there can only be 1 catch-all site.",
+ "admin.sites.isActive": "Active",
+ "admin.sites.new": "New Site",
+ "admin.sites.refreshSuccess": "List of sites refreshed successfully.",
+ "admin.sites.subtitle": "Manage your wiki sites",
+ "admin.sites.title": "Sites",
+ "admin.ssl.currentState": "Current State",
+ "admin.ssl.domain": "Domain",
+ "admin.ssl.domainHint": "Enter the fully qualified domain pointing to your wiki. (e.g. wiki.example.com)",
+ "admin.ssl.expiration": "Certificate Expiration",
+ "admin.ssl.httpPort": "HTTP Port",
+ "admin.ssl.httpPortHint": "Non-SSL port the server will listen to for HTTP requests. Usually 80 or 3000.",
+ "admin.ssl.httpPortRedirect": "Redirect HTTP requests to HTTPS",
+ "admin.ssl.httpPortRedirectHint": "Will automatically redirect any requests on the HTTP port to HTTPS.",
+ "admin.ssl.httpPortRedirectSaveSuccess": "HTTP Redirection changed successfully.",
+ "admin.ssl.httpPortRedirectTurnOff": "Turn Off",
+ "admin.ssl.httpPortRedirectTurnOn": "Turn On",
+ "admin.ssl.httpsPort": "HTTPS Port",
+ "admin.ssl.httpsPortHint": "SSL port the server will listen to for HTTPS requests. Usually 443.",
+ "admin.ssl.ports": "Ports",
+ "admin.ssl.provider": "Provider",
+ "admin.ssl.providerCustomCertificate": "Custom Certificate",
+ "admin.ssl.providerDisabled": "Disabled",
+ "admin.ssl.providerHint": "Select Custom Certificate if you have your own certificate already.",
+ "admin.ssl.providerLetsEncrypt": "Let's Encrypt",
+ "admin.ssl.providerOptions": "Provider Options",
+ "admin.ssl.renewCertificate": "Renew Certificate",
+ "admin.ssl.renewCertificateLoadingSubtitle": "Do not leave this page.",
+ "admin.ssl.renewCertificateLoadingTitle": "Renewing Certificate...",
+ "admin.ssl.renewCertificateSuccess": "Certificate renewed successfully.",
+ "admin.ssl.status": "Certificate Status",
+ "admin.ssl.subscriberEmail": "Subscriber Email",
+ "admin.ssl.subtitle": "Manage SSL configuration",
+ "admin.ssl.title": "SSL",
+ "admin.ssl.writableConfigFileWarning": "Note that your config file must be writable in order to persist ports configuration.",
+ "admin.stats.title": "Statistics",
+ "admin.storage.actionRun": "Run",
+ "admin.storage.actions": "Actions",
+ "admin.storage.actionsInactiveWarn": "You must enable this storage target and apply changes before you can run actions.",
+ "admin.storage.addTarget": "Add Storage Target",
+ "admin.storage.assetDelivery": "Asset Delivery",
+ "admin.storage.assetDeliveryHint": "Select how uploaded assets should be delivered to the user. Note that not all storage origins support asset delivery and can only be used for backup purposes.",
+ "admin.storage.assetDirectAccess": "Direct Access",
+ "admin.storage.assetDirectAccessHint": "Assets are accessed directly by the user using a secure / signed link. When enabled, takes priority over file streaming.",
+ "admin.storage.assetDirectAccessNotSupported": "Not supported by this storage target.",
+ "admin.storage.assetStreaming": "File Streaming",
+ "admin.storage.assetStreamingHint": "Assets will be streamed from the storage target, through the server, to the user.",
+ "admin.storage.assetStreamingNotSupported": "Not supported by this storage target.",
+ "admin.storage.assetsOnly": "Assets Only",
+ "admin.storage.cancelSetup": "Cancel",
+ "admin.storage.config": "Configuration",
+ "admin.storage.contentTypeDocuments": "Documents",
+ "admin.storage.contentTypeDocumentsHint": "Text or presentation documents in PDF, TXT, Word, Excel and Powerpoint formats.",
+ "admin.storage.contentTypeImages": "Images",
+ "admin.storage.contentTypeImagesHint": "Image Assets in JPG, PNG, GIF, WebP and SVG formats.",
+ "admin.storage.contentTypeLargeFiles": "Large Files",
+ "admin.storage.contentTypeLargeFilesDBWarn": "For performance reasons, large files should not be stored in the database. Consider using another storage type for these files.",
+ "admin.storage.contentTypeLargeFilesHint": "Large files such as videos, zip archives and binaries. Pages never fall into this category, irrespective of their size.",
+ "admin.storage.contentTypeLargeFilesThreshold": "Size Threshold",
+ "admin.storage.contentTypeOthers": "Others",
+ "admin.storage.contentTypeOthersHint": "Any other file types that don't match the other categories.",
+ "admin.storage.contentTypePages": "Pages",
+ "admin.storage.contentTypePagesHint": "Page content source, in Markdown, HTML or JSON format depending on the editor.",
+ "admin.storage.contentTypes": "Content Types",
+ "admin.storage.contentTypesHint": "Select the type of content that should be stored to this storage target:",
+ "admin.storage.currentState": "Current State",
+ "admin.storage.deliveryPaths": "Delivery Paths",
+ "admin.storage.deliveryPathsLegend": "Legend:",
+ "admin.storage.deliveryPathsPushToOrigin": "Push to Origin",
+ "admin.storage.deliveryPathsUser": "User",
+ "admin.storage.deliveryPathsUserRequest": "User Request",
+ "admin.storage.destroyConfirm": "Confirm Setup Reset",
+ "admin.storage.destroyConfirmInfo": "Are you sure you want to reset the storage target setup configuration? Note that this action cannot be undone and you will need to start the setup process over.",
+ "admin.storage.destroyingSetup": "Resetting storage target setup configuration...",
+ "admin.storage.enabled": "Enabled",
+ "admin.storage.enabledForced": "Cannot be disabled on the database target.",
+ "admin.storage.enabledHint": "Should this storage target be used for storing and accessing assets.",
+ "admin.storage.errorMsg": "Error Message",
+ "admin.storage.finishSetup": "Finish Setup",
+ "admin.storage.githubAccTypeOrg": "Organization",
+ "admin.storage.githubAccTypePersonal": "Personal",
+ "admin.storage.githubFinish": "Once you have installed the application on the GitHub repository of your choice, click the Finish Setup button below to validate the installation and start using it. Otherwise, click Destroy to delete any pending configuration and start over.",
+ "admin.storage.githubInstallApp": "Setup GitHub Connection - Step 2",
+ "admin.storage.githubInstallAppHint": "On the next screen, you will be prompted to install the app you just created onto one or more repositories. You may select a single one or all repositories.",
+ "admin.storage.githubOrg": "GitHub Organization",
+ "admin.storage.githubOrgHint": "Enter the GitHub organization account to be used.",
+ "admin.storage.githubPreparingManifest": "Preparing manifest...",
+ "admin.storage.githubPublicUrl": "Wiki Public URL",
+ "admin.storage.githubPublicUrlHint": "Enter the full URL to your wiki (e.g. https://wiki.example.com). Note that your wiki MUST be accessible from the internet!",
+ "admin.storage.githubRedirecting": "Redirecting to GitHub...",
+ "admin.storage.githubRepo": "GitHub Repository",
+ "admin.storage.githubRepoCreating": "Creating GitHub Repository...",
+ "admin.storage.githubRepoHint": "Enter the name of the repository to create on GitHub and use for this wiki:",
+ "admin.storage.githubSetupContinue": "Continue Setup",
+ "admin.storage.githubSetupDestroyFailed": "Failed to destroy setup configuration.",
+ "admin.storage.githubSetupDestroySuccess": "Setup configuration has been reset successfully.",
+ "admin.storage.githubSetupFailed": "GitHub Setup failed.",
+ "admin.storage.githubSetupInstallApp": "GitHub Connection Setup - Step 2",
+ "admin.storage.githubSetupInstallAppInfo": "On the next screen, you'll be prompted to install the app you just created onto one or more GitHub repositories.",
+ "admin.storage.githubSetupInstallAppReturn": "Once the installation on GitHub is completed, you will need to manually return to this page to finish the setup.",
+ "admin.storage.githubSetupInstallAppSelect": "IMPORTANT: Select only the repository that will be used to sync with this wiki.",
+ "admin.storage.githubSetupSuccess": "Success! Wiki.js is now connected to GitHub.",
+ "admin.storage.githubVerifying": "Verifying GitHub configuration...",
+ "admin.storage.inactiveTarget": "Inactive",
+ "admin.storage.lastSync": "Last synchronization {time}",
+ "admin.storage.lastSyncAttempt": "Last attempt was {time}",
+ "admin.storage.missingOrigin": "Missing Origin",
+ "admin.storage.noActions": "This storage target has no actions that you can execute.",
+ "admin.storage.noConfigOption": "This storage target has no configuration options you can modify.",
+ "admin.storage.noSyncModes": "This storage target has no synchronization options you can modify.",
+ "admin.storage.noTarget": "You don't have any active storage target.",
+ "admin.storage.notConfigured": "Not Configured",
+ "admin.storage.pagesAndAssets": "Pages and Assets",
+ "admin.storage.pagesOnly": "Pages Only",
+ "admin.storage.saveFailed": "Failed to save storage configuration.",
+ "admin.storage.saveSuccess": "Storage configuration saved successfully.",
+ "admin.storage.setup": "Setup",
+ "admin.storage.setupConfiguredHint": "This module is already configured. You can uninstall this module to start over.",
+ "admin.storage.setupHint": "This module requires a setup process to be completed in order to use it. Follow the instructions below to get started.",
+ "admin.storage.startSetup": "Start Setup",
+ "admin.storage.status": "Status",
+ "admin.storage.subtitle": "Set backup and sync targets for your content",
+ "admin.storage.sync": "Synchronization",
+ "admin.storage.syncDirBi": "Bi-directional",
+ "admin.storage.syncDirBiHint": "In bi-directional mode, content is first pulled from the storage target. Any newer content overwrites local content. New content since last sync is then pushed to the storage target, overwriting any content on target if present.",
+ "admin.storage.syncDirPull": "Pull from target",
+ "admin.storage.syncDirPullHint": "Content is always pulled from the storage target, overwriting any local content which already exists. This choice is usually reserved for single-use content import. Caution with this option as any local content will always be overwritten!",
+ "admin.storage.syncDirPush": "Push to target",
+ "admin.storage.syncDirPushHint": "Content is always pushed to the storage target, overwriting any existing content. This is safest choice for backup scenarios.",
+ "admin.storage.syncDirection": "Sync Direction",
+ "admin.storage.syncDirectionSubtitle": "Choose how content synchronization is handled for this storage target.",
+ "admin.storage.syncSchedule": "Sync Schedule",
+ "admin.storage.syncScheduleCurrent": "Currently set to every {schedule}.",
+ "admin.storage.syncScheduleDefault": "The default is every {schedule}.",
+ "admin.storage.syncScheduleHint": "For performance reasons, this storage target synchronize changes on an interval-based schedule, instead of on every change. Define at which interval should the synchronization occur.",
+ "admin.storage.targetConfig": "Target Configuration",
+ "admin.storage.targetState": "This storage target is {state}",
+ "admin.storage.targetStateActive": "active",
+ "admin.storage.targetStateInactive": "inactive",
+ "admin.storage.targets": "Targets",
+ "admin.storage.title": "Storage",
+ "admin.storage.uninstall": "Uninstall",
+ "admin.storage.unsupported": "Unsupported",
+ "admin.storage.useVersioning": "Use Versioning",
+ "admin.storage.useVersioningHint": "Should previous versions of assets be retained on the storage target.",
+ "admin.storage.vendor": "Vendor",
+ "admin.storage.vendorWebsite": "Website",
+ "admin.storage.versioning": "Asset Versioning",
+ "admin.storage.versioningForceEnabled": "Cannot be disabled on this storage target.",
+ "admin.storage.versioningHint": "Asset versioning allows for storage of all previous versions of the same file. Unless you have a requirement to store all versions of uploaded assets, it's recommended to leave this oftion off as it can consume significant storage space over time.",
+ "admin.storage.versioningNotSupported": "Not supported by this storage target.",
+ "admin.system.browser": "Browser",
+ "admin.system.browserHint": "The browser name and version.",
+ "admin.system.client": "Client",
+ "admin.system.clientCookies": "Cookies Support",
+ "admin.system.clientCookiesHint": "Whether cookies are enabled on this browser.",
+ "admin.system.clientLanguage": "Primary Language",
+ "admin.system.clientLanguageHint": "The main language advertised by the browser.",
+ "admin.system.clientPlatform": "Platform",
+ "admin.system.clientPlatformHint": "The OS platform the browser is running on.",
+ "admin.system.clientViewport": "Viewport",
+ "admin.system.clientViewportHint": "The viewport dimensions available to the website.",
+ "admin.system.configFile": "Configuration File",
+ "admin.system.configFileHint": "The path to the Wiki.js configuration file.",
+ "admin.system.cpuCores": "CPU Cores",
+ "admin.system.cpuCoresHint": "The number of CPU cores available to Wiki.js.",
+ "admin.system.currentVersion": "Current Version",
+ "admin.system.currentVersionHint": "The currently installed version.",
+ "admin.system.database": "Database",
+ "admin.system.databaseHint": "The version of the database in use.",
+ "admin.system.databaseHost": "Database Host",
+ "admin.system.databaseHostHint": "The hostname used to access the database.",
+ "admin.system.dbPartialSupport": "Your database version is not fully supported. Some functionality may be limited or not work as expected.",
+ "admin.system.engines": "Server Engines",
+ "admin.system.hostInfo": "Server Host Information",
+ "admin.system.hostname": "Hostname",
+ "admin.system.hostnameHint": "The hostname of the server / container.",
+ "admin.system.latestVersion": "Latest Version",
+ "admin.system.latestVersionHint": "The latest version available to install.",
+ "admin.system.nodejsHint": "The version of Node.js installed.",
+ "admin.system.os": "Operating System",
+ "admin.system.osHint": "The OS Wiki.js is running on.",
+ "admin.system.published": "Published",
+ "admin.system.ramUsage": "RAM Usage: {used} / {total}",
+ "admin.system.refreshSuccess": "System Info has been refreshed.",
+ "admin.system.subtitle": "Information about your server / client",
+ "admin.system.title": "System Info",
+ "admin.system.totalRAM": "Total RAM",
+ "admin.system.totalRAMHint": "The total amount of RAM available to Wiki.js.",
+ "admin.system.workingDirectory": "Working Directory",
+ "admin.system.workingDirectoryHint": "The directory path where Wiki.js is currently running from.",
+ "admin.tags.date": "Created {created} and last updated {updated}.",
+ "admin.tags.delete": "Delete this tag",
+ "admin.tags.deleteConfirm": "Delete Tag?",
+ "admin.tags.deleteConfirmText": "Are you sure you want to delete tag {tag}? The tag will also be unlinked from all pages.",
+ "admin.tags.deleteSuccess": "Tag deleted successfully.",
+ "admin.tags.edit": "Edit Tag",
+ "admin.tags.emptyList": "No tags to display.",
+ "admin.tags.filter": "Filter...",
+ "admin.tags.label": "Label",
+ "admin.tags.noItemsText": "Add a tag to a page to get started.",
+ "admin.tags.noSelectionText": "Select a tag from the list on the left.",
+ "admin.tags.refreshSuccess": "Tags have been refreshed.",
+ "admin.tags.saveSuccess": "Tag has been saved successfully.",
+ "admin.tags.subtitle": "Manage page tags",
+ "admin.tags.tag": "Tag",
+ "admin.tags.title": "Tags",
+ "admin.tags.viewLinkedPages": "View Linked Pages",
+ "admin.theme.accentColor": "Accent Color",
+ "admin.theme.accentColorHint": "The accent color for elements that need to stand out or grab the user attention.",
+ "admin.theme.bodyHtmlInjection": "Body HTML Injection",
+ "admin.theme.bodyHtmlInjectionHint": "HTML code to be injected just before the closing body tag.",
+ "admin.theme.codeInjection": "Code Injection",
+ "admin.theme.cssOverride": "CSS Override",
+ "admin.theme.cssOverrideHint": "CSS code to inject after system default CSS. Consider using custom themes if you have a large amount of css code. Injecting too much CSS code will result in poor page load performance! CSS will automatically be minified.",
+ "admin.theme.cssOverrideWarning": "{caution} When adding styles for page content, you must scope them to the {cssClass} class. Omitting this could break the layout of the editor!",
+ "admin.theme.cssOverrideWarningCaution": "CAUTION:",
+ "admin.theme.darkMode": "Dark Mode",
+ "admin.theme.darkModeHint": "Not recommended for accessibility. Can always be turned off by the user.",
+ "admin.theme.downloadAuthor": "Author",
+ "admin.theme.downloadDownload": "Download",
+ "admin.theme.downloadName": "Name",
+ "admin.theme.downloadThemes": "Download Themes",
+ "admin.theme.headHtmlInjection": "Head HTML Injection",
+ "admin.theme.headHtmlInjectionHint": "HTML code to be injected just before the closing head tag. Usually for script tags.",
+ "admin.theme.headerColor": "Header Color",
+ "admin.theme.headerColorHint": "The background color for the site top header.",
+ "admin.theme.iconset": "Icon Set",
+ "admin.theme.iconsetHint": "Set of icons to use for the sidebar navigation.",
+ "admin.theme.layout": "Theme Layout",
+ "admin.theme.options": "Theme Options",
+ "admin.theme.primaryColor": "Primary Color",
+ "admin.theme.primaryColorHint": "The main color for primary action buttons and most form elements.",
+ "admin.theme.reduceMotion": "Reduce Motion",
+ "admin.theme.reduceMotionHint": "Disable most site animations. This setting is automatically enforced when the reduced motion flag is enabled on the user OS.",
+ "admin.theme.resetDefaults": "Reset Defaults",
+ "admin.theme.saveSuccess": "Theme configuration saved successfully!",
+ "admin.theme.secondaryColor": "Secondary Color",
+ "admin.theme.secondaryColorHint": "The alternate color for secondary action buttons and for some other elements.",
+ "admin.theme.showPrintBtn": "Show Print Button",
+ "admin.theme.showPrintBtnHint": "Should the print button be displayed on all pages. Note that this doesn't prevent the user from printing the page using the system dialog.",
+ "admin.theme.showSharingMenu": "Show Sharing Menu",
+ "admin.theme.showSharingMenuHint": "Should the sharing menu be displayed on all pages.",
+ "admin.theme.sidebarColor": "Sidebar Color",
+ "admin.theme.sidebarColorHint": "The background color for the side navigation menu on content pages.",
+ "admin.theme.sidebarPosition": "Sidebar Position",
+ "admin.theme.sidebarPositionHint": "On which side should the main site navigation sidebar be displayed.",
+ "admin.theme.siteTheme": "Site Theme",
+ "admin.theme.siteThemeHint": "Themes affect how content pages are displayed. Other site sections (such as the editor or admin area) are not affected.",
+ "admin.theme.subtitle": "Modify the look & feel of your wiki",
+ "admin.theme.title": "Theme",
+ "admin.theme.tocPosition": "TOC Position",
+ "admin.theme.tocPositionHint": "On which side should the Table of Contents sidebar be displayed for content pages.",
+ "admin.users.active": "Active",
+ "admin.users.activity": "Activity",
+ "admin.users.assignGroup": "Assign Group",
+ "admin.users.auth": "Authentication",
+ "admin.users.authProvider": "Provider",
+ "admin.users.authProviderId": "Provider Id",
+ "admin.users.authentication": "Authentication",
+ "admin.users.ban": "Ban User",
+ "admin.users.banHint": "Block the user from signing in and invalidate any active sessions.",
+ "admin.users.banned": "Banned",
+ "admin.users.basicInfo": "Basic Info",
+ "admin.users.changePassword": "Change Password",
+ "admin.users.changePasswordHint": "Change the user password. Note that the current password cannot be recovered.",
+ "admin.users.create": "Create User",
+ "admin.users.createInvalidData": "Cannot create user as some fields are invalid or missing.",
+ "admin.users.createKeepOpened": "Keep dialog opened after create",
+ "admin.users.createSuccess": "User created successfully!",
+ "admin.users.createdAt": "Created {date}",
+ "admin.users.darkMode": "Dark Mode",
+ "admin.users.darkModeHint": "Display the user interface using dark mode.",
+ "admin.users.dateFormat": "Date Format",
+ "admin.users.dateFormatHint": "How dates should be formatted when displayed to the user.",
+ "admin.users.delete": "Delete User",
+ "admin.users.deleteConfirmForeignNotice": "Note that you cannot delete a user that already created content. You must instead either deactivate the user or delete all content that was created by that user.",
+ "admin.users.deleteConfirmReplaceWarn": "Any content (pages, uploads, comments, etc.) that was created by this user will be reassigned to the user selected below. It is recommended to create a dummy target user (e.g. Deleted User) if you don't want the content to be reassigned to any current active user.",
+ "admin.users.deleteConfirmText": "Are you sure you want to delete user {username}?",
+ "admin.users.deleteConfirmTitle": "Delete User?",
+ "admin.users.deleteHint": "Permanently remove the user from the database. This action cannot be undone!",
+ "admin.users.displayName": "Display Name",
+ "admin.users.edit": "Edit User",
+ "admin.users.email": "Email",
+ "admin.users.emailHint": "Email address of the user.",
+ "admin.users.emailInvalid": "Email address is invalid.",
+ "admin.users.emailMissing": "Email address is missing.",
+ "admin.users.extendedMetadata": "Extended Metadata",
+ "admin.users.groupAlreadyAssigned": "User is already assigned to this group.",
+ "admin.users.groupAssign": "Assign",
+ "admin.users.groupAssignNotice": "Note that you cannot assign users to the Administrators or Guests groups from this panel.",
+ "admin.users.groupSelected": "Assign to {group}",
+ "admin.users.groups": "Groups",
+ "admin.users.groupsMissing": "You must assign the user to at least 1 group.",
+ "admin.users.groupsSelected": "Assign to {count} groups",
+ "admin.users.id": "ID",
+ "admin.users.inactive": "Inactive",
+ "admin.users.info": "User Info",
+ "admin.users.invalidJSON": "Invalid JSON",
+ "admin.users.jobTitle": "Job Title",
+ "admin.users.jobTitleHint": "The job title of the user.",
+ "admin.users.joined": "Joined",
+ "admin.users.lastLoginAt": "Last login",
+ "admin.users.lastUpdated": "Last Updated",
+ "admin.users.linkedAccounts": "Linked Accounts",
+ "admin.users.linkedProviders": "Linked Providers",
+ "admin.users.loading": "Loading User...",
+ "admin.users.location": "Location",
+ "admin.users.locationHint": "The city / country of the user or the office location.",
+ "admin.users.metadata": "Metadata",
+ "admin.users.minimumGroupRequired": "Cannot unassign because user must be assigned to at least 1 group.",
+ "admin.users.mustChangePwd": "Must Change Password",
+ "admin.users.mustChangePwdHint": "User will be prompted to choose a new password upon login.",
+ "admin.users.name": "Display Name",
+ "admin.users.nameHint": "Usually the full name or nickname of the user.",
+ "admin.users.nameInvalidChars": "Name has invalid characters.",
+ "admin.users.nameMissing": "Name is missing.",
+ "admin.users.newPassword": "New Password",
+ "admin.users.noGroupAssigned": "This user is not assigned to any group yet. You must assign at least 1 group to a user.",
+ "admin.users.noGroupSelected": "You must select a group first.",
+ "admin.users.noLinkedProviders": "This user isn't linked to any authentication providers.",
+ "admin.users.noteHint": "Notes are not shown to the user and can only be edited here.",
+ "admin.users.notes": "Notes",
+ "admin.users.operations": "Operations",
+ "admin.users.overview": "Overview",
+ "admin.users.passAuth": "Password Authentication",
+ "admin.users.password": "Password",
+ "admin.users.passwordMissing": "Password is missing.",
+ "admin.users.passwordTooShort": "Password is too short.",
+ "admin.users.preferences": "User Preferences",
+ "admin.users.profile": "User Profile",
+ "admin.users.pwdAuthActive": "Can Use Password Authentication",
+ "admin.users.pwdAuthActiveHint": "Whether the user can login using the password authentication.",
+ "admin.users.pwdAuthRestrict": "Restrict Password Authentication",
+ "admin.users.pwdAuthRestrictHint": "Prevent the user from using password authentication for login.",
+ "admin.users.pwdNotSet": "Password Not Set",
+ "admin.users.pwdSet": "Password is set",
+ "admin.users.pwdStrengthGood": "Good",
+ "admin.users.pwdStrengthMedium": "Medium",
+ "admin.users.pwdStrengthPoor": "Poor",
+ "admin.users.pwdStrengthStrong": "Strong",
+ "admin.users.pwdStrengthWeak": "Weak",
+ "admin.users.refreshSuccess": "Users refreshed successfully.",
+ "admin.users.saveSuccess": "User saved successfully.",
+ "admin.users.selectGroup": "Select Group...",
+ "admin.users.sendWelcomeEmail": "Send Welcome Email",
+ "admin.users.sendWelcomeEmailAltHint": "An email will be sent to the user with link(s) to the wiki(s) the user has read access to.",
+ "admin.users.sendWelcomeEmailHint": "An email will be sent to the user with his login details.",
+ "admin.users.subtitle": "Manage Users",
+ "admin.users.tfa": "Two Factor Authentication (2FA)",
+ "admin.users.tfaInvalidate": "Invalidate 2FA",
+ "admin.users.tfaInvalidateConfirm": "Are you sure you want to invalidate the user current 2FA configuration? This action cannot be undone.",
+ "admin.users.tfaInvalidateHint": "Force the user to setup 2FA again. Any active configuration will no longer work.",
+ "admin.users.tfaInvalidateSuccess": "User TFA configuration has been invalidated.",
+ "admin.users.tfaNotSet": "2FA is awaiting setup",
+ "admin.users.tfaRequired": "Require 2FA",
+ "admin.users.tfaRequiredHint": "User will be forced to use 2FA during the next login. This setting will have no effect if 2FA is already enforced by the login provider.",
+ "admin.users.tfaSet": "2FA configured",
+ "admin.users.timeFormat": "Time Format",
+ "admin.users.timeFormatHint": "How time should be formatted when displayed to the user.",
+ "admin.users.timezone": "Timezone",
+ "admin.users.timezoneHint": "Used to adjust date and time displayed to the user.",
+ "admin.users.title": "Users",
+ "admin.users.toggle2FA": "Toggle 2FA",
+ "admin.users.unassignGroup": "Unassign from Group",
+ "admin.users.unban": "Unban User",
+ "admin.users.unbanHint": "Allow the user to sign in.",
+ "admin.users.unverified": "Unverified",
+ "admin.users.unverify": "Unverify User",
+ "admin.users.unverifyHint": "Set the user as unverified (state where the email has not been validated).",
+ "admin.users.updateUser": "Update User",
+ "admin.users.userActivateSuccess": "User has been activated successfully.",
+ "admin.users.userAlreadyAssignedToGroup": "User is already assigned to this group!",
+ "admin.users.userDeactivateSuccess": "User deactivated successfully.",
+ "admin.users.userTFADisableSuccess": "2FA was disabled successfully.",
+ "admin.users.userTFAEnableSuccess": "2FA was enabled successfully.",
+ "admin.users.userUpdateSuccess": "User updated successfully.",
+ "admin.users.userVerifySuccess": "User has been verified successfully.",
+ "admin.users.verified": "Verified",
+ "admin.users.verify": "Verify User",
+ "admin.users.verifyHint": "Set the user as verified (state where the email has been validated).",
+ "admin.utilities.authSubtitle": "Various tools for authentication / users",
+ "admin.utilities.authTitle": "Authentication",
+ "admin.utilities.cacheSubtitle": "Flush cache of various components",
+ "admin.utilities.cacheTitle": "Flush Cache",
+ "admin.utilities.contentSubtitle": "Various tools for pages",
+ "admin.utilities.contentTitle": "Content",
+ "admin.utilities.graphEndpointSubtitle": "Change the GraphQL endpoint for Wiki.js",
+ "admin.utilities.graphEndpointTitle": "GraphQL Endpoint",
+ "admin.utilities.importv1Subtitle": "Migrate data from a previous 1.x installation",
+ "admin.utilities.importv1Title": "Import from Wiki.js 1.x",
+ "admin.utilities.invalidAuthCertificates": "Invalidate Authentication Certificates",
+ "admin.utilities.invalidAuthCertificatesHint": "Regenerate the public and private keys used for authentication. This will instantly log everyone out.",
+ "admin.utilities.purgeHistory": "Purge History",
+ "admin.utilities.purgeHistoryHint": "Delete history (content versioning) older than the selected timeframe.",
+ "admin.utilities.purgeHistoryTimeframe": "Delete older than...",
+ "admin.utilities.subtitle": "Maintenance and miscellaneous tools",
+ "admin.utilities.telemetrySubtitle": "Enable/Disable telemetry or reset the client ID",
+ "admin.utilities.telemetryTitle": "Telemetry",
+ "admin.utilities.title": "Utilities",
+ "admin.utilities.tools": "Tools",
+ "admin.utitilies.purgeHistoryMonth": "1 Month | {count} Months",
+ "admin.utitilies.purgeHistoryToday": "Today",
+ "admin.utitilies.purgeHistoryYear": "1 Year | {count} Years",
+ "admin.webhooks.acceptUntrusted": "Accept untrusted SSL certificates",
+ "admin.webhooks.acceptUntrustedHint": "It is recommended that you leave this off for proper security.",
+ "admin.webhooks.authHeader": "Authentication Header",
+ "admin.webhooks.authHeaderHint": "(Optional) The value of the Authorization header to send along the request.",
+ "admin.webhooks.createInvalidData": "The webhook has some invalid or missing data.",
+ "admin.webhooks.createSuccess": "Webhook created successfully!",
+ "admin.webhooks.delete": "Delete Webhook",
+ "admin.webhooks.deleteConfirm": "Are you sure you want to delete webhook {name}?",
+ "admin.webhooks.deleteConfirmWarn": "This action cannot be undone!",
+ "admin.webhooks.deleteSuccess": "Webhook deleted successfully.",
+ "admin.webhooks.edit": "Edit Webhook",
+ "admin.webhooks.eventCreatePage": "Create a new page",
+ "admin.webhooks.eventDeleteAsset": "Delete an asset",
+ "admin.webhooks.eventDeleteComment": "Delete a comment",
+ "admin.webhooks.eventDeletePage": "Delete a page",
+ "admin.webhooks.eventEditAsset": "Edit an existing asset",
+ "admin.webhooks.eventEditComment": "Edit an existing comment",
+ "admin.webhooks.eventEditPage": "Update an existing page",
+ "admin.webhooks.eventNewComment": "Post a new comment",
+ "admin.webhooks.eventRenameAsset": "Rename / move an asset",
+ "admin.webhooks.eventRenamePage": "Rename / move a page",
+ "admin.webhooks.eventUploadAsset": "Upload a new asset",
+ "admin.webhooks.eventUserJoin": "Create / register a new user",
+ "admin.webhooks.eventUserLogin": "User logins",
+ "admin.webhooks.eventUserLogout": "User logouts",
+ "admin.webhooks.events": "Events",
+ "admin.webhooks.eventsMissing": "You must select at least 1 event.",
+ "admin.webhooks.eventsSelected": "No event selected | 1 event selected | {count} events selected",
+ "admin.webhooks.includeContent": "Include Content",
+ "admin.webhooks.includeContentHint": "Should the payload include content (e.g. the full page body). Make sure that your remote endpoint can accept large payloads!",
+ "admin.webhooks.includeMetadata": "Include Metadata",
+ "admin.webhooks.includeMetadataHint": "Should the payload include metadata such as title, description, author, etc.",
+ "admin.webhooks.nameInvalidChars": "The name contains invalid characters.",
+ "admin.webhooks.nameMissing": "A name for this webhook is required.",
+ "admin.webhooks.new": "New Webhook",
+ "admin.webhooks.none": "There are no webhooks yet.",
+ "admin.webhooks.stateError": "Failed",
+ "admin.webhooks.stateErrorExplain": "The last trigger failed to call the endpoint.",
+ "admin.webhooks.stateErrorHint": "The last event failed to call your endpoint. Click Edit for more details.",
+ "admin.webhooks.statePending": "Pending",
+ "admin.webhooks.statePendingHint": "Waiting for an event to trigger this webhook for the first time.",
+ "admin.webhooks.stateSuccess": "Healthy",
+ "admin.webhooks.stateSuccessHint": "The last webhook trigger completed successfully.",
+ "admin.webhooks.subtitle": "Manage webhooks to external services",
+ "admin.webhooks.title": "Webhooks",
+ "admin.webhooks.typeAsset": "asset",
+ "admin.webhooks.typeComment": "comment",
+ "admin.webhooks.typePage": "page",
+ "admin.webhooks.typeUser": "user",
+ "admin.webhooks.updateSuccess": "Webhook updated successfully.",
+ "admin.webhooks.url": "URL",
+ "admin.webhooks.urlHint": "Enter the remote endpoint URL",
+ "admin.webhooks.urlInvalidChars": "The URL contains invalid characters.",
+ "admin.webhooks.urlMissing": "The URL is missing or is not valid.",
+ "auth.actions.login": "Log In",
+ "auth.actions.register": "Register",
+ "auth.changePwd.instructions": "You must choose a new password:",
+ "auth.changePwd.loading": "Changing password...",
+ "auth.changePwd.newPasswordPlaceholder": "New Password",
+ "auth.changePwd.newPasswordVerifyPlaceholder": "Verify New Password",
+ "auth.changePwd.proceed": "Change Password",
+ "auth.changePwd.subtitle": "Choose a new password",
+ "auth.enterCredentials": "Enter your credentials",
+ "auth.errors.invalidLogin": "Invalid Login",
+ "auth.errors.invalidLoginMsg": "The email or password is invalid.",
+ "auth.errors.invalidUserEmail": "Invalid User Email",
+ "auth.errors.loginError": "Login error",
+ "auth.errors.notYetAuthorized": "You have not been authorized to login to this site yet.",
+ "auth.errors.tooManyAttempts": "Too many attempts!",
+ "auth.errors.tooManyAttemptsMsg": "You've made too many failed attempts in a short period of time, please try again {time}.",
+ "auth.errors.userNotFound": "User not found",
+ "auth.fields.email": "Email Address",
+ "auth.fields.emailUser": "Email / Username",
+ "auth.fields.name": "Name",
+ "auth.fields.password": "Password",
+ "auth.fields.username": "Username",
+ "auth.fields.verifyPassword": "Verify Password",
+ "auth.forgotPasswordCancel": "Cancel",
+ "auth.forgotPasswordLink": "Forgot your password?",
+ "auth.forgotPasswordLoading": "Requesting password reset...",
+ "auth.forgotPasswordSubtitle": "Enter your email address to receive the instructions to reset your password:",
+ "auth.forgotPasswordSuccess": "Check your emails for password reset instructions!",
+ "auth.forgotPasswordTitle": "Forgot your password",
+ "auth.genericError": "Authentication is unavailable.",
+ "auth.invalidEmail": "Email address is invalid.",
+ "auth.invalidEmailUsername": "Enter a valid email / username.",
+ "auth.invalidPassword": "Enter a valid password.",
+ "auth.loginRequired": "Login required",
+ "auth.loginSuccess": "Login Successful! Redirecting...",
+ "auth.loginUsingStrategy": "Login using {strategy}",
+ "auth.missingEmail": "Missing email address.",
+ "auth.missingName": "Name is missing.",
+ "auth.missingPassword": "Missing password.",
+ "auth.nameTooLong": "Name is too long.",
+ "auth.nameTooShort": "Name is too short.",
+ "auth.orLoginUsingStrategy": "or login using...",
+ "auth.passwordNotMatch": "Both passwords do not match.",
+ "auth.passwordTooShort": "Password is too short.",
+ "auth.pleaseWait": "Please wait",
+ "auth.providers.azure": "Azure Active Directory",
+ "auth.providers.facebook": "Facebook",
+ "auth.providers.github": "GitHub",
+ "auth.providers.google": "Google ID",
+ "auth.providers.ldap": "LDAP / Active Directory",
+ "auth.providers.local": "Local",
+ "auth.providers.slack": "Slack",
+ "auth.providers.windowslive": "Microsoft Account",
+ "auth.registerCheckEmail": "Check your emails to activate your account.",
+ "auth.registerSubTitle": "Fill-in the form below to create your account.",
+ "auth.registerSuccess": "Account created successfully!",
+ "auth.registerTitle": "Create an account",
+ "auth.registering": "Creating account...",
+ "auth.selectAuthProvider": "Select Authentication Provider",
+ "auth.sendResetPassword": "Reset Password",
+ "auth.signingIn": "Signing In...",
+ "auth.switchToLogin.link": "Login instead",
+ "auth.switchToLogin.text": "Already have an account? {link}",
+ "auth.switchToRegister.link": "Create an account",
+ "auth.switchToRegister.text": "Don't have an account yet? {link}",
+ "auth.tfa.placeholder": "XXXXXX",
+ "auth.tfa.subtitle": "Security code required:",
+ "auth.tfa.title": "Two Factor Authentication",
+ "auth.tfa.verifyToken": "Verify",
+ "auth.tfaFormTitle": "Enter the security code generated from your trusted device:",
+ "auth.tfaSetupInstrFirst": "1) Scan the QR code below from your mobile 2FA application:",
+ "auth.tfaSetupInstrSecond": "2) Enter the security code generated from your trusted device:",
+ "auth.tfaSetupTitle": "Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account.",
+ "common.actions.activate": "Activate",
+ "common.actions.add": "Add",
+ "common.actions.apply": "Apply",
+ "common.actions.browse": "Browse...",
+ "common.actions.cancel": "Cancel",
+ "common.actions.clear": "Clear",
+ "common.actions.close": "Close",
+ "common.actions.commit": "Commit",
+ "common.actions.confirm": "Confirm",
+ "common.actions.copy": "Copy",
+ "common.actions.create": "Create",
+ "common.actions.deactivate": "Deactivate",
+ "common.actions.delete": "Delete",
+ "common.actions.discard": "Discard",
+ "common.actions.discardChanges": "Discard Changes",
+ "common.actions.download": "Download",
+ "common.actions.edit": "Edit",
+ "common.actions.exit": "Exit",
+ "common.actions.fetch": "Fetch",
+ "common.actions.filter": "Filter",
+ "common.actions.generate": "Generate",
+ "common.actions.howItWorks": "How it works",
+ "common.actions.insert": "Insert",
+ "common.actions.manage": "Manage",
+ "common.actions.move": "Move",
+ "common.actions.new": "New",
+ "common.actions.ok": "OK",
+ "common.actions.optimize": "Optimize",
+ "common.actions.page": "Page",
+ "common.actions.preview": "Preview",
+ "common.actions.proceed": "Proceed",
+ "common.actions.properties": "Properties",
+ "common.actions.refresh": "Refresh",
+ "common.actions.rename": "Rename",
+ "common.actions.returnToTop": "Return to top",
+ "common.actions.save": "Save",
+ "common.actions.saveChanges": "Save Changes",
+ "common.actions.select": "Select",
+ "common.actions.update": "Update",
+ "common.actions.upload": "Upload",
+ "common.clipboard.failure": "Failed to copy to clipboard.",
+ "common.clipboard.success": "Copied to clipboard successfully.",
+ "common.clipboard.uuid": "Copy UUID to clipboard.",
+ "common.clipboard.uuidFailure": "Failed to copy UUID to clipboard.",
+ "common.clipboard.uuidSuccess": "Copied UUID to clipboard successfully.",
+ "common.comments.beFirst": "Be the first to comment.",
+ "common.comments.contentMissingError": "Comment is empty or too short!",
+ "common.comments.deleteConfirmTitle": "Confirm Delete",
+ "common.comments.deletePermanentWarn": "This action cannot be undone!",
+ "common.comments.deleteSuccess": "Comment was deleted successfully.",
+ "common.comments.deleteWarn": "Are you sure you want to permanently delete this comment?",
+ "common.comments.fieldContent": "Comment Content",
+ "common.comments.fieldEmail": "Your Email Address",
+ "common.comments.fieldName": "Your Name",
+ "common.comments.loading": "Loading comments...",
+ "common.comments.markdownFormat": "Markdown Format",
+ "common.comments.modified": "modified {reldate}",
+ "common.comments.newComment": "New Comment",
+ "common.comments.newPlaceholder": "Write a new comment...",
+ "common.comments.none": "No comments yet.",
+ "common.comments.postComment": "Post Comment",
+ "common.comments.postSuccess": "New comment posted successfully.",
+ "common.comments.postingAs": "Posting as {name}",
+ "common.comments.sdTitle": "Talk",
+ "common.comments.title": "Comments",
+ "common.comments.updateComment": "Update Comment",
+ "common.comments.updateSuccess": "Comment was updated successfully.",
+ "common.comments.viewDiscussion": "View Discussion",
+ "common.duration.days": "Day(s)",
+ "common.duration.every": "Every",
+ "common.duration.hours": "Hour(s)",
+ "common.duration.minutes": "Minute(s)",
+ "common.duration.months": "Month(s)",
+ "common.duration.years": "Year(s)",
+ "common.error.unexpected": "An unexpected error occurred.",
+ "common.field.createdOn": "Created On",
+ "common.field.id": "ID",
+ "common.field.lastUpdated": "Last Updated",
+ "common.field.name": "Name",
+ "common.footer.copyright": "© {year} {company}. All rights reserved.",
+ "common.footer.license": "Content is available under the {license}, by {company}.",
+ "common.footer.poweredBy": "Powered by",
+ "common.header.account": "Account",
+ "common.header.admin": "Administration",
+ "common.header.assets": "Assets",
+ "common.header.browseTags": "Browse by Tags",
+ "common.header.currentPage": "Current Page",
+ "common.header.delete": "Delete",
+ "common.header.duplicate": "Duplicate",
+ "common.header.edit": "Edit",
+ "common.header.history": "History",
+ "common.header.home": "Home",
+ "common.header.imagesFiles": "Images & Files",
+ "common.header.language": "Language",
+ "common.header.login": "Login",
+ "common.header.logout": "Logout",
+ "common.header.move": "Move / Rename",
+ "common.header.myWiki": "My Wiki",
+ "common.header.newPage": "New Page",
+ "common.header.pageActions": "Page Actions",
+ "common.header.profile": "Profile",
+ "common.header.search": "Search...",
+ "common.header.searchClose": "Close",
+ "common.header.searchCopyLink": "Copy Search Link",
+ "common.header.searchDidYouMean": "Did you mean...",
+ "common.header.searchHint": "Type at least 2 characters to start searching...",
+ "common.header.searchLoading": "Searching...",
+ "common.header.searchNoResult": "No pages matching your query.",
+ "common.header.searchResultsCount": "Found {total} results",
+ "common.header.siteMap": "Site Map",
+ "common.header.view": "View",
+ "common.header.viewSource": "View Source",
+ "common.license.alr": "All Rights Reserved",
+ "common.license.cc0": "Public Domain",
+ "common.license.ccby": " Creative Commons Attribution License",
+ "common.license.ccbync": "Creative Commons Attribution-NonCommercial License",
+ "common.license.ccbyncnd": "Creative Commons Attribution-NonCommercial-NoDerivs License",
+ "common.license.ccbyncsa": "Creative Commons Attribution-NonCommercial-ShareAlike License",
+ "common.license.ccbynd": "Creative Commons Attribution-NoDerivs License",
+ "common.license.ccbysa": "Creative Commons Attribution-ShareAlike License",
+ "common.license.none": "None",
+ "common.loading": "Loading...",
+ "common.modernBrowser": "modern browser",
+ "common.newpage.create": "Create Page",
+ "common.newpage.goback": "Go back",
+ "common.newpage.subtitle": "Would you like to create it now?",
+ "common.newpage.title": "This page does not exist yet.",
+ "common.notfound.gohome": "Home",
+ "common.notfound.subtitle": "This page does not exist.",
+ "common.notfound.title": "Not Found",
+ "common.outdatedBrowserWarning": "Your browser is outdated. Upgrade to a {modernBrowser}.",
+ "common.page.bookmark": "Bookmark",
+ "common.page.delete": "Delete Page",
+ "common.page.deleteSubtitle": "The page can be restored from the administration area.",
+ "common.page.deleteTitle": "Are you sure you want to delete page {title}?",
+ "common.page.editPage": "Edit Page",
+ "common.page.global": "Global",
+ "common.page.id": "ID {id}",
+ "common.page.lastEditedBy": "Last edited by",
+ "common.page.loading": "Loading Page...",
+ "common.page.printFormat": "Print Format",
+ "common.page.private": "Private",
+ "common.page.published": "Published",
+ "common.page.returnNormalView": "Return to Normal View",
+ "common.page.share": "Share",
+ "common.page.tags": "Tags",
+ "common.page.tagsMatching": "Pages matching tags",
+ "common.page.toc": "Table of Contents",
+ "common.page.unpublished": "Unpublished",
+ "common.page.unpublishedWarning": "This page is not published.",
+ "common.page.versionId": "Version ID {id}",
+ "common.page.viewingSource": "Viewing source of page {path}",
+ "common.page.viewingSourceVersion": "Viewing source as of {date} of page {path}",
+ "common.pageSelector.createTitle": "Select New Page Location",
+ "common.pageSelector.folderEmptyWarning": "This folder is empty.",
+ "common.pageSelector.moveTitle": "Move / Rename Page Location",
+ "common.pageSelector.pages": "Pages",
+ "common.pageSelector.selectTitle": "Select a Page",
+ "common.pageSelector.virtualFolders": "Virtual Folders",
+ "common.password.average": "Average",
+ "common.password.strong": "Strong",
+ "common.password.veryStrong": "Very Strong",
+ "common.password.veryWeak": "Very Weak",
+ "common.password.weak": "Weak",
+ "common.sidebar.browse": "Browse",
+ "common.sidebar.currentDirectory": "Current Directory",
+ "common.sidebar.mainMenu": "Main Menu",
+ "common.sidebar.root": "(root)",
+ "common.unauthorized.action.create": "You cannot create the page.",
+ "common.unauthorized.action.download": "You cannot download the page content.",
+ "common.unauthorized.action.downloadVersion": "You cannot download the content for this page version.",
+ "common.unauthorized.action.edit": "You cannot edit the page.",
+ "common.unauthorized.action.history": "You cannot view the page history.",
+ "common.unauthorized.action.source": "You cannot view the page source.",
+ "common.unauthorized.action.sourceVersion": "You cannot view the source of this version of the page.",
+ "common.unauthorized.action.view": "You cannot view this page.",
+ "common.unauthorized.goback": "Go Back",
+ "common.unauthorized.login": "Login As...",
+ "common.unauthorized.title": "Unauthorized",
+ "common.user.search": "Search User",
+ "common.user.searchPlaceholder": "Search Users...",
+ "common.welcome.createhome": "Create Home Page",
+ "common.welcome.subtitle": "Let's get started and create the home page.",
+ "common.welcome.title": "Welcome to your wiki!",
+ "editor.assets.deleteAsset": "Delete Asset",
+ "editor.assets.deleteAssetConfirm": "Are you sure you want to delete asset",
+ "editor.assets.deleteAssetWarn": "This action cannot be undone!",
+ "editor.assets.deleteSuccess": "Asset deleted successfully.",
+ "editor.assets.fetchImage": "Fetch Remote Image",
+ "editor.assets.fileCount": "{count} files",
+ "editor.assets.folderCreateSuccess": "Asset folder created successfully.",
+ "editor.assets.folderEmpty": "This asset folder is empty.",
+ "editor.assets.folderName": "Folder Name",
+ "editor.assets.folderNameNamingRules": "Must follow the asset folder {namingRules}.",
+ "editor.assets.folderNameNamingRulesLink": "naming rules",
+ "editor.assets.headerActions": "Actions",
+ "editor.assets.headerAdded": "Added",
+ "editor.assets.headerFileSize": "File Size",
+ "editor.assets.headerFilename": "Filename",
+ "editor.assets.headerId": "ID",
+ "editor.assets.headerType": "Type",
+ "editor.assets.imageAlign": "Image Alignment",
+ "editor.assets.newFolder": "New Folder",
+ "editor.assets.noUploadError": "You must choose a file to upload first!",
+ "editor.assets.refreshSuccess": "List of assets refreshed successfully.",
+ "editor.assets.renameAsset": "Rename Asset",
+ "editor.assets.renameAssetSubtitle": "Enter the new name for this asset:",
+ "editor.assets.renameSuccess": "Asset renamed successfully.",
+ "editor.assets.title": "Assets",
+ "editor.assets.uploadAssets": "Upload Assets",
+ "editor.assets.uploadAssetsDropZone": "Browse or Drop files here...",
+ "editor.assets.uploadFailed": "File upload failed.",
+ "editor.backToEditor": "Back to Editor",
+ "editor.ckeditor.stats": "{chars} chars, {words} words",
+ "editor.conflict.editable": "(editable)",
+ "editor.conflict.infoGeneric": "A more recent version of this page was saved by {authorName}, {date}",
+ "editor.conflict.leftPanelInfo": "Your current edit, based on page version from {date}",
+ "editor.conflict.localVersion": "Local Version {refEditable}",
+ "editor.conflict.overwrite.description": "Are you sure you want to replace your current version with the latest remote content? {refEditsLost}",
+ "editor.conflict.overwrite.editsLost": "Your current edits will be lost.",
+ "editor.conflict.overwrite.title": "Overwrite with Remote Version?",
+ "editor.conflict.pageDescription": "Description:",
+ "editor.conflict.pageTitle": "Title:",
+ "editor.conflict.readonly": "(read-only)",
+ "editor.conflict.remoteVersion": "Remote Version {refReadOnly}",
+ "editor.conflict.rightPanelInfo": "Last edited by {authorName}, {date}",
+ "editor.conflict.title": "Resolve Save Conflict",
+ "editor.conflict.useLocal": "Use Local",
+ "editor.conflict.useLocalHint": "Use content in the left panel",
+ "editor.conflict.useRemote": "Use Remote",
+ "editor.conflict.useRemoteHint": "Discard local changes and use latest version",
+ "editor.conflict.viewLatestVersion": "View Latest Version",
+ "editor.conflict.warning": "Save conflict! Another user has already modified this page.",
+ "editor.conflict.whatToDo": "What do you want to do?",
+ "editor.conflict.whatToDoLocal": "Use your current local version and ignore the latest changes.",
+ "editor.conflict.whatToDoRemote": "Use the remote version (latest) and discard your changes.",
+ "editor.markup.blockquote": "Blockquote",
+ "editor.markup.blockquoteError": "Error Blockquote",
+ "editor.markup.blockquoteInfo": "Info Blockquote",
+ "editor.markup.blockquoteSuccess": "Success Blockquote",
+ "editor.markup.blockquoteWarning": "Warning Blockquote",
+ "editor.markup.bold": "Bold",
+ "editor.markup.distractionFreeMode": "Distraction Free Mode",
+ "editor.markup.heading": "Heading {level}",
+ "editor.markup.horizontalBar": "Horizontal Bar",
+ "editor.markup.inlineCode": "Inline Code",
+ "editor.markup.insertAssets": "Insert Assets",
+ "editor.markup.insertBlock": "Insert Block",
+ "editor.markup.insertCodeBlock": "Insert Code Block",
+ "editor.markup.insertDiagram": "Insert Diagram",
+ "editor.markup.insertLink": "Insert Link",
+ "editor.markup.insertMathExpression": "Insert Math Expression",
+ "editor.markup.insertVideoAudio": "Insert Video / Audio",
+ "editor.markup.italic": "Italic",
+ "editor.markup.keyboardKey": "Keyboard Key",
+ "editor.markup.markdownFormattingHelp": "Markdown Formatting Help",
+ "editor.markup.noSelectionError": "Text must be selected first!",
+ "editor.markup.orderedList": "Ordered List",
+ "editor.markup.strikethrough": "Strikethrough",
+ "editor.markup.subscript": "Subscript",
+ "editor.markup.superscript": "Superscript",
+ "editor.markup.tableHelper": "Table Helper",
+ "editor.markup.togglePreviewPane": "Hide / Show Preview Pane",
+ "editor.markup.toggleSpellcheck": "Toggle Spellcheck",
+ "editor.markup.unorderedList": "Unordered List",
+ "editor.page": "Page",
+ "editor.pageData.data": "Data",
+ "editor.pageData.dragDropHint": "Drag-n-drop fields from the left onto this area to build your template structure.",
+ "editor.pageData.duplicateTemplateKeys": "One or more fields have duplicate unique keys!",
+ "editor.pageData.emptyTemplateStructure": "Template Structure is empty!",
+ "editor.pageData.fieldTypeBoolean": "Boolean",
+ "editor.pageData.fieldTypeHeader": "Header",
+ "editor.pageData.fieldTypeImage": "Image",
+ "editor.pageData.fieldTypeLink": "Link",
+ "editor.pageData.fieldTypeNumber": "Number",
+ "editor.pageData.fieldTypeText": "Text",
+ "editor.pageData.invalidTemplateKeys": "One or more fields have missing or invalid unique keys!",
+ "editor.pageData.invalidTemplateLabels": "One or more fields have missing or invalid labels!",
+ "editor.pageData.invalidTemplateName": "Template name is missing or invalid!",
+ "editor.pageData.label": "Label",
+ "editor.pageData.manageTemplates": "Manage Templates",
+ "editor.pageData.noTemplate": "You don't have any template yet. Create one to get started.",
+ "editor.pageData.selectTemplateAbove": "Select a template to edit from the dropdown menu above.",
+ "editor.pageData.template": "Template",
+ "editor.pageData.templateDeleteConfirmText": "Any page currently using this template will revert to basic template rendering.",
+ "editor.pageData.templateDeleteConfirmTitle": "Are you sure?",
+ "editor.pageData.templateFullRowTypes": "Full Row Types",
+ "editor.pageData.templateKeyValueTypes": "Key-Value Types",
+ "editor.pageData.templateStructure": "Structure",
+ "editor.pageData.templateTitle": "Template Title",
+ "editor.pageData.templateUntitled": "Untitled Template",
+ "editor.pageData.title": "Page Data",
+ "editor.pageData.uniqueKey": "Unique Key",
+ "editor.pageRel.button": "Button Appearance",
+ "editor.pageRel.caption": "Caption (optional)",
+ "editor.pageRel.center": "Center",
+ "editor.pageRel.label": "Label",
+ "editor.pageRel.left": "Left",
+ "editor.pageRel.position": "Button Position",
+ "editor.pageRel.preview": "Preview",
+ "editor.pageRel.right": "Right",
+ "editor.pageRel.selectIcon": "Select Icon...",
+ "editor.pageRel.selectPage": "Select Page...",
+ "editor.pageRel.target": "Target",
+ "editor.pageRel.title": "Add Page Relation",
+ "editor.pageRel.titleEdit": "Edit Page Relation",
+ "editor.pageScripts.title": "Page Scripts",
+ "editor.props.allowComments": "Allow Comments",
+ "editor.props.allowCommentsHint": "Enable commenting abilities on this page.",
+ "editor.props.allowContributions": "Allow Contributions",
+ "editor.props.allowRatings": "Allow Ratings",
+ "editor.props.allowRatingsHint": "Enable rating capabilities on this page.",
+ "editor.props.dateRange": "Date Range",
+ "editor.props.dateRangeHint": "Select the start and end date for this page publication. The page will only be accessible to users with read access within the selected date range.",
+ "editor.props.draft": "Draft",
+ "editor.props.draftHint": "Visible to users with write access only.",
+ "editor.props.info": "Info",
+ "editor.props.jsLoad": "Javascript - On Load",
+ "editor.props.jsLoadHint": "Execute javascript once the page is loaded",
+ "editor.props.jsUnload": "Javascript - On Unload",
+ "editor.props.jsUnloadHint": "Execute javascript before the page content is destroyed",
+ "editor.props.pageProperties": "Page Properties",
+ "editor.props.password": "Password",
+ "editor.props.passwordHint": "The page must be published and the user must have read access rights.",
+ "editor.props.publishState": "Publishing State",
+ "editor.props.published": "Published",
+ "editor.props.publishedHint": "Visible to all users with read access.",
+ "editor.props.relationAdd": "Add Relation...",
+ "editor.props.relationAddHint": "Add links to other pages in the footer (e.g. as part of a series of articles)",
+ "editor.props.relations": "Relations",
+ "editor.props.requirePassword": "Require Password",
+ "editor.props.scripts": "Scripts",
+ "editor.props.shortDescription": "Short Description",
+ "editor.props.showInTree": "Show in Site Navigation",
+ "editor.props.showSidebar": "Show Sidebar",
+ "editor.props.showTags": "Show Tags",
+ "editor.props.showToc": "Show Table of Contents",
+ "editor.props.sidebar": "Sidebar",
+ "editor.props.social": "Social",
+ "editor.props.styles": "CSS Styles",
+ "editor.props.stylesHint": "CSS Rules to add to the page",
+ "editor.props.tags": "Tags",
+ "editor.props.tagsHint": "Use tags to categorize your pages and make them easier to find.",
+ "editor.props.title": "Title",
+ "editor.props.tocMinMaxDepth": "Min/Max Depth",
+ "editor.props.visibility": "Visibility",
+ "editor.save.createSuccess": "Page created successfully.",
+ "editor.save.error": "An error occurred while creating the page",
+ "editor.save.pleaseWait": "Please wait...",
+ "editor.save.processing": "Rendering",
+ "editor.save.saved": "Saved",
+ "editor.save.updateSuccess": "Page updated successfully.",
+ "editor.select.cannotChange": "This cannot be changed once the page is created.",
+ "editor.select.customView": "or create a custom view?",
+ "editor.select.title": "Which editor do you want to use for this page?",
+ "editor.unsaved.body": "You have unsaved changes. Are you sure you want to leave the editor and discard any modifications you made since the last save?",
+ "editor.unsaved.title": "Discard Unsaved Changes?",
+ "editor.unsavedWarning": "You have unsaved edits. Are you sure you want to leave the editor?",
+ "history.restore.confirmButton": "Restore",
+ "history.restore.confirmText": "Are you sure you want to restore this page content as it was on {date}? This version will be copied on top of the current history. As such, newer versions will still be preserved.",
+ "history.restore.confirmTitle": "Restore page version?",
+ "history.restore.success": "Page version restored succesfully!",
+ "profile.activity.commentsPosted": "Comments posted",
+ "profile.activity.joinedOn": "Joined on",
+ "profile.activity.lastLoginOn": "Last login on",
+ "profile.activity.lastUpdatedOn": "Profile last updated on",
+ "profile.activity.pagesCreated": "Pages created",
+ "profile.activity.title": "Activity",
+ "profile.appearance": "Appearance",
+ "profile.appearanceDark": "Dark",
+ "profile.appearanceDefault": "Site Default",
+ "profile.appearanceLight": "Light",
+ "profile.auth.changePassSuccess": "Password changed successfully.",
+ "profile.auth.changePassword": "Change Password",
+ "profile.auth.currentPassword": "Current Password",
+ "profile.auth.newPassword": "New Password",
+ "profile.auth.provider": "Provider",
+ "profile.auth.title": "Authentication",
+ "profile.auth.verifyPassword": "Confirm New Password",
+ "profile.comments.title": "Comments",
+ "profile.darkMode": "Dark Mode",
+ "profile.darkModeHint": "Change the appareance of the site to a dark theme.",
+ "profile.dateFormat": "Date Format",
+ "profile.dateFormatHint": "Set your preferred format to display dates.",
+ "profile.displayName": "Display Name",
+ "profile.displayNameHint": "Your full name; shown when authoring content (e.g. pages, comments, etc.).",
+ "profile.email": "Email Address",
+ "profile.emailHint": "The email address used for login.",
+ "profile.groups.title": "Groups",
+ "profile.jobTitle": "Job Title",
+ "profile.jobTitleHint": "Your position in your organization; shown on your profile page.",
+ "profile.localeDefault": "Locale Default",
+ "profile.location": "Location",
+ "profile.locationHint": "Your city and country; shown on your profile page.",
+ "profile.myInfo": "My Info",
+ "profile.pages.emptyList": "No pages to display.",
+ "profile.pages.headerCreatedAt": "Created",
+ "profile.pages.headerPath": "Path",
+ "profile.pages.headerTitle": "Title",
+ "profile.pages.headerUpdatedAt": "Last Updated",
+ "profile.pages.refreshSuccess": "Page list has been refreshed.",
+ "profile.pages.subtitle": "List of pages I created or last modified",
+ "profile.pages.title": "Pages",
+ "profile.preferences": "Preferences",
+ "profile.pronouns": "Pronouns",
+ "profile.pronounsHint": "Let people know which pronouns should they use when referring to you.",
+ "profile.save.success": "Profile saved successfully.",
+ "profile.subtitle": "My personal info",
+ "profile.timeFormat": "Time Format",
+ "profile.timeFormat12h": "12 hour",
+ "profile.timeFormat24h": "24 hour",
+ "profile.timeFormatHint": "Set your preferred format to display time.",
+ "profile.timezone": "Timezone",
+ "profile.timezoneHint": "Set your timezone to display local time correctly.",
+ "profile.title": "Profile",
+ "profile.viewPublicProfile": "View Public Profile",
+ "tags.clearSelection": "Clear Selection",
+ "tags.currentSelection": "Current Selection",
+ "tags.locale": "Locale",
+ "tags.localeAny": "Any",
+ "tags.noResults": "Couldn't find any page with the selected tags.",
+ "tags.noResultsWithFilter": "Couldn't find any page matching the current filtering options.",
+ "tags.orderBy": "Order By",
+ "tags.orderByField.ID": "ID",
+ "tags.orderByField.creationDate": "Creation Date",
+ "tags.orderByField.lastModified": "Last Modified",
+ "tags.orderByField.path": "Path",
+ "tags.orderByField.title": "Title",
+ "tags.pageLastUpdated": "Last Updated {date}",
+ "tags.retrievingResultsLoading": "Retrieving page results...",
+ "tags.searchWithinResultsPlaceholder": "Search within results...",
+ "tags.selectOneMoreTags": "Select one or more tags",
+ "tags.selectOneMoreTagsHint": "Select one or more tags on the left."
+}
diff --git a/ux/src/layouts/AdminLayout.vue b/ux/src/layouts/AdminLayout.vue
new file mode 100644
index 00000000..d6ac5d07
--- /dev/null
+++ b/ux/src/layouts/AdminLayout.vue
@@ -0,0 +1,359 @@
+
+q-layout.admin(view='hHh Lpr lff')
+ q-header.bg-black.text-white
+ .row.no-wrap
+ q-toolbar(style='height: 64px;', dark)
+ q-btn(dense, flat, to='/')
+ q-avatar(size='34px', square)
+ img(src='/_assets/logo-wikijs.svg')
+ q-toolbar-title.text-h6.font-poppins Wiki.js
+ q-toolbar.gt-sm.justify-center(style='height: 64px;', dark)
+ .text-overline.text-uppercase.text-grey {{t('admin.adminArea')}}
+ q-toolbar(style='height: 64px;', dark)
+ q-space
+ q-spinner-tail(
+ v-show='siteStore.routerLoading'
+ color='blue'
+ size='sm'
+ )
+ q-btn.q-ml-md(flat, dense, icon='las la-times-circle', label='Exit' color='pink', to='/')
+ account-menu
+ q-drawer.admin-sidebar(v-model='leftDrawerOpen', show-if-above, bordered)
+ q-scroll-area.admin-nav(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ )
+ q-list.text-white(padding, dense)
+ q-item.q-mb-sm
+ q-item-section
+ q-btn.acrylic-btn(
+ flat
+ color='pink'
+ icon='las la-heart'
+ :label='t(`admin.contribute.title`)'
+ no-caps
+ href='https://js.wiki/donate'
+ target='_blank'
+ type='a'
+ )
+ q-item(to='/_admin/dashboard', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-apps-tab.svg')
+ q-item-section {{ t('admin.dashboard.title') }}
+ q-item(to='/_admin/sites', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-change-theme.svg')
+ q-item-section {{ t('admin.sites.title') }}
+ q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4 {{ t('admin.nav.site') }}
+ q-item.q-mb-md
+ q-item-section
+ q-select(
+ dark
+ standout
+ dense
+ v-model='adminStore.currentSiteId'
+ :options='adminStore.sites'
+ option-value='id'
+ option-label='title'
+ emit-value
+ map-options
+ )
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/general`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-web.svg')
+ q-item-section {{ t('admin.general.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/analytics`', v-ripple, active-class='bg-primary text-white', disabled)
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-bar-chart.svg')
+ q-item-section {{ t('admin.analytics.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/approvals`', v-ripple, active-class='bg-primary text-white', disabled)
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-inspection.svg')
+ q-item-section {{ t('admin.approval.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/comments`', v-ripple, active-class='bg-primary text-white', disabled)
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-comments.svg')
+ q-item-section {{ t('admin.comments.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-cashbook.svg')
+ q-item-section {{ t('admin.editors.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/locale`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-language.svg')
+ q-item-section {{ t('admin.locale.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/login`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-bunch-of-keys.svg')
+ q-item-section {{ t('admin.login.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/navigation`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-tree-structure.svg')
+ q-item-section {{ t('admin.navigation.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/rendering`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-rich-text-converter.svg')
+ q-item-section {{ t('admin.rendering.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/storage`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-ssd.svg')
+ q-item-section {{ t('admin.storage.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/theme`', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-paint-roller.svg')
+ q-item-section {{ t('admin.theme.title') }}
+ q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4 {{ t('admin.nav.users') }}
+ q-item(to='/_admin/auth', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-security-lock.svg')
+ q-item-section {{ t('admin.auth.title') }}
+ q-item(to='/_admin/groups', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-people.svg')
+ q-item-section {{ t('admin.groups.title') }}
+ q-item-section(side)
+ q-badge(color='dark-3', :label='info.groupsTotal')
+ q-item(to='/_admin/users', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-account.svg')
+ q-item-section {{ t('admin.users.title') }}
+ q-item-section(side)
+ q-badge(color='dark-3', :label='info.usersTotal')
+ q-item-label.q-mt-sm(header).text-caption.text-blue-grey-4 {{ t('admin.nav.system') }}
+ q-item(to='/_admin/api', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-rest-api.svg')
+ q-item-section {{ t('admin.api.title') }}
+ q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled)
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-event-log.svg')
+ q-item-section {{ t('admin.audit.title') }}
+ q-item(to='/_admin/extensions', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-module.svg')
+ q-item-section {{ t('admin.extensions.title') }}
+ q-item(to='/_admin/mail', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-message-settings.svg')
+ q-item-section {{ t('admin.mail.title') }}
+ q-item(to='/_admin/security', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-protect.svg')
+ q-item-section {{ t('admin.security.title') }}
+ q-item(to='/_admin/ssl', v-ripple, active-class='bg-primary text-white', disabled)
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-security-ssl.svg')
+ q-item-section {{ t('admin.ssl.title') }}
+ q-item(to='/_admin/system', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-processor.svg')
+ q-item-section {{ t('admin.system.title') }}
+ q-item(to='/_admin/utilities', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-swiss-army-knife.svg')
+ q-item-section {{ t('admin.utilities.title') }}
+ q-item(to='/_admin/webhooks', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-lightning-bolt.svg')
+ q-item-section {{ t('admin.webhooks.title') }}
+ q-item(to='/_admin/flags', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-windsock.svg')
+ q-item-section {{ t('admin.dev.flags.title') }}
+ q-page-container.admin-container
+ router-view(v-slot='{ Component }')
+ transition(name='fade')
+ component(:is='Component')
+ q-dialog.admin-overlay(
+ v-model='overlayIsShown'
+ persistent
+ full-width
+ full-height
+ no-shake
+ transition-show='jump-up'
+ transition-hide='jump-down'
+ )
+ component(:is='adminStore.overlay')
+ q-footer.admin-footer
+ q-bar.justify-center(dense)
+ span(style='font-size: 11px;') Powered by #[a(href='https://js.wiki', target='_blank'): strong Wiki.js], an open source project.
+
+
+
+
+
diff --git a/ux/src/layouts/AuthLayout.vue b/ux/src/layouts/AuthLayout.vue
new file mode 100644
index 00000000..170c3db2
--- /dev/null
+++ b/ux/src/layouts/AuthLayout.vue
@@ -0,0 +1,49 @@
+
+q-layout(view='hHh lpr lff')
+ q-page-container
+ router-view
+
+
+
+
+
diff --git a/ux/src/layouts/MainLayout.vue b/ux/src/layouts/MainLayout.vue
new file mode 100644
index 00000000..25f7f5dc
--- /dev/null
+++ b/ux/src/layouts/MainLayout.vue
@@ -0,0 +1,168 @@
+
+q-layout(view='hHh Lpr lff')
+ header-nav
+ q-drawer.bg-sidebar(
+ v-model='showSideNav'
+ show-if-above
+ :width='255'
+ )
+ .sidebar-actions.flex.items-stretch
+ q-btn.q-px-sm.col(
+ flat
+ dense
+ icon='las la-globe'
+ color='blue-7'
+ text-color='blue-2'
+ label='EN'
+ aria-label='EN'
+ size='sm'
+ )
+ q-separator(vertical)
+ q-btn.q-px-sm.col(
+ flat
+ dense
+ icon='las la-sitemap'
+ color='blue-7'
+ text-color='blue-2'
+ label='Browse'
+ aria-label='Browse'
+ size='sm'
+ )
+ q-scroll-area.sidebar-nav(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ )
+ q-list(
+ clickable
+ dense
+ dark
+ )
+ q-item-label.text-blue-2.text-caption(header) Getting Started
+ q-item(to='/install')
+ q-item-section(side)
+ q-icon(name='las la-dog', color='white')
+ q-item-section Requirements
+ q-item(to='/install')
+ q-item-section(side)
+ q-icon(name='las la-cat', color='white')
+ q-item-section Installation
+ q-separator.q-my-sm(dark)
+ q-item(to='/install')
+ q-item-section(side)
+ q-icon(name='las la-cat', color='white')
+ q-item-section Installation
+ q-bar.bg-blue-9.text-white(dense)
+ q-btn.col(
+ icon='las la-dharmachakra'
+ label='History'
+ flat
+ )
+ q-separator(vertical)
+ q-btn.col(
+ icon='las la-bookmark'
+ label='Bookmarks'
+ flat
+ )
+ q-page-container
+ router-view
+ q-page-scroller(
+ position='bottom-right'
+ :scroll-offset='150'
+ :offset='[15, 15]'
+ )
+ q-btn(
+ icon='las la-arrow-up'
+ color='primary'
+ round
+ size='md'
+ )
+ q-footer
+ q-bar.justify-center(dense)
+ span(style='font-size: 11px;') © Cyberdyne Systems Corp. 2020 | Powered by #[strong Wiki.js]
+
+
+
+
+
diff --git a/ux/src/layouts/ProfileLayout.vue b/ux/src/layouts/ProfileLayout.vue
new file mode 100644
index 00000000..33a84a5e
--- /dev/null
+++ b/ux/src/layouts/ProfileLayout.vue
@@ -0,0 +1,301 @@
+
+q-layout(view='hHh Lpr lff')
+ header-nav
+ q-page-container.layout-profile
+ .layout-profile-card
+ .layout-profile-sd
+ q-list
+ q-item(
+ v-for='navItem of sidenav'
+ :key='navItem.key'
+ clickable
+ :to='`/p/` + navItem.key'
+ active-class='is-active'
+ v-ripple
+ )
+ q-item-section(side)
+ q-icon(:name='navItem.icon')
+ q-item-section
+ q-item-label {{navItem.label}}
+ q-separator.q-my-sm(inset)
+ q-item(
+ clickable
+ v-ripple
+ to='/p/me'
+ )
+ q-item-section(side)
+ q-icon(name='las la-id-card')
+ q-item-section
+ q-item-label View Public Profile
+ q-separator.q-my-sm(inset)
+ q-item(
+ clickable
+ v-ripple
+ )
+ q-item-section(side)
+ q-icon(name='las la-sign-out-alt', color='negative')
+ q-item-section
+ q-item-label.text-negative Logout
+ router-view
+ q-footer
+ q-bar.justify-center(dense)
+ span(style='font-size: 11px;') © Cyberdyne Systems Corp. 2020 | Powered by #[strong Wiki.js]
+
+
+
+
+
diff --git a/ux/src/pages/AdminAnalytics.vue b/ux/src/pages/AdminAnalytics.vue
new file mode 100644
index 00000000..337960a6
--- /dev/null
+++ b/ux/src/pages/AdminAnalytics.vue
@@ -0,0 +1,181 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row, wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-line-chart.svg', alt='Analytics', style='width: 80px;')
+ .admin-header-title
+ .headline.primary--text.animated.fadeInLeft {{ $t('admin.analytics.title') }}
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin.analytics.subtitle') }}
+ v-spacer
+ v-btn.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
+ v-icon mdi-refresh
+ v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
+ v-icon(left) mdi-check
+ span {{$t('common.actions.apply')}}
+
+ v-flex(lg3, xs12)
+ v-card.animated.fadeInUp
+ v-toolbar(flat, color='primary', dark, dense)
+ .subtitle-1 {{$t('admin.analytics.providers')}}
+ v-list(two-line, dense).py-0
+ template(v-for='(str, idx) in providers')
+ v-list-item(:key='str.key', @click='selectedProvider = str.key', :disabled='!str.isAvailable')
+ v-list-item-avatar(size='24')
+ v-icon(color='grey', v-if='!str.isAvailable') mdi-minus-box-outline
+ v-icon(color='primary', v-else-if='str.isEnabled', v-ripple, @click='str.isEnabled = false') mdi-checkbox-marked-outline
+ v-icon(color='grey', v-else, v-ripple, @click='str.isEnabled = true') mdi-checkbox-blank-outline
+ v-list-item-content
+ v-list-item-title.body-2(:class='!str.isAvailable ? `grey--text` : (selectedProvider === str.key ? `primary--text` : ``)') {{ str.title }}
+ v-list-item-subtitle: .caption(:class='!str.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === str.key ? `blue--text ` : ``)') {{ str.description }}
+ v-list-item-avatar(v-if='selectedProvider === str.key', size='24')
+ v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
+ v-divider(v-if='idx < providers.length - 1')
+
+ v-flex(xs12, lg9)
+
+ v-card.animated.fadeInUp.wait-p2s
+ v-toolbar(color='primary', dense, flat, dark)
+ .subtitle-1 {{provider.title}}
+ v-spacer
+ v-switch(
+ dark
+ color='blue lighten-5'
+ label='Active'
+ v-model='provider.isEnabled'
+ hide-details
+ inset
+ )
+ v-card-info(color='blue')
+ div
+ div {{provider.description}}
+ span.caption: a(:href='provider.website') {{provider.website}}
+ v-spacer
+ .admin-providerlogo
+ img(:src='provider.logo', :alt='provider.title')
+ v-card-text
+ v-form
+ .overline.pb-5 {{$t('admin.analytics.providerConfiguration')}}
+ .body-1.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin.analytics.providerNoConfiguration')}}
+ template(v-else, v-for='cfg in provider.config')
+ v-select(
+ v-if='cfg.value.type === "string" && cfg.value.enum'
+ outlined
+ :items='cfg.value.enum'
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ )
+ v-switch.mb-3(
+ v-else-if='cfg.value.type === "boolean"'
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ color='primary'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ inset
+ )
+ v-textarea(
+ v-else-if='cfg.value.type === "string" && cfg.value.multiline'
+ outlined
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ )
+ v-text-field(
+ v-else
+ outlined
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ )
+
+
+
+
diff --git a/ux/src/pages/AdminApi.vue b/ux/src/pages/AdminApi.vue
new file mode 100644
index 00000000..5c89c440
--- /dev/null
+++ b/ux/src/pages/AdminApi.vue
@@ -0,0 +1,280 @@
+
+q-page.admin-api
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-rest-api.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.api.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.api.subtitle') }}
+ .col
+ .flex.items-center
+ template(v-if='enabled')
+ q-spinner-rings.q-mr-sm(color='green')
+ .text-caption.text-green {{$t('admin.api.enabled')}}
+ template(v-else)
+ q-spinner-rings.q-mr-sm(color='red', size='md')
+ .text-caption.text-red {{$t('admin.api.disabled')}}
+ .col-auto
+ q-btn.q-mr-sm.q-ml-md.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/api'
+ target='_blank'
+ type='a'
+ )
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn.q-mr-sm(
+ unelevated
+ icon='las la-power-off'
+ :label='!enabled ? $t(`admin.api.enableButton`) : $t(`admin.api.disableButton`)'
+ :color='!enabled ? `positive` : `negative`'
+ @click='globalSwitch'
+ :disabled='loading > 0'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.api.newKeyButton`)'
+ color='primary'
+ @click='newKey'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ q-card.shadow-1
+
+//- v-container(fluid, grid-list-lg)
+//- v-layout(row, wrap)
+//- v-flex(xs12)
+//- .admin-header
+//- img.animated.fadeInUp(src='/_assets/svg/icon-rest-api.svg', alt='API', style='width: 80px;')
+//- .admin-header-title
+//- .headline.primary--text.animated.fadeInLeft {{$t('admin.api.title')}}
+//- .subtitle-1.grey--text.animated.fadeInLeft {{$t('admin.api.subtitle')}}
+//- v-spacer
+//- template(v-if='enabled')
+//- status-indicator.mr-3(positive, pulse)
+//- .caption.green--text.animated.fadeInLeft {{$t('admin.api.enabled')}}
+//- template(v-else)
+//- status-indicator.mr-3(negative, pulse)
+//- .caption.red--text.animated.fadeInLeft {{$t('admin.api.disabled')}}
+//- v-spacer
+//- v-btn.mr-3.animated.fadeInDown.wait-p2s(outlined, color='grey', icon, @click='refresh')
+//- v-icon mdi-refresh
+//- v-btn.mr-3.animated.fadeInDown.wait-p1s(:color='enabled ? `red` : `green`', depressed, @click='globalSwitch', dark, :loading='isToggleLoading')
+//- v-icon(left) mdi-power
+//- span(v-if='!enabled') {{$t('admin.api.enableButton')}}
+//- span(v-else) {{$t('admin.api.disableButton')}}
+//- v-btn.animated.fadeInDown(color='primary', depressed, large, @click='newKey', dark)
+//- v-icon(left) mdi-plus
+//- span {{$t('admin.api.newKeyButton')}}
+//- v-card.mt-3.animated.fadeInUp
+//- v-simple-table(v-if='keys && keys.length > 0')
+//- template(v-slot:default)
+//- thead
+//- tr.grey(:class='$vuetify.theme.dark ? `darken-4-d5` : `lighten-5`')
+//- th {{$t('admin.api.headerName')}}
+//- th {{$t('admin.api.headerKeyEnding')}}
+//- th {{$t('admin.api.headerExpiration')}}
+//- th {{$t('admin.api.headerCreated')}}
+//- th {{$t('admin.api.headerLastUpdated')}}
+//- th(width='100') {{$t('admin.api.headerRevoke')}}
+//- tbody
+//- tr(v-for='key of keys', :key='`key-` + key.id')
+//- td
+//- strong(:class='key.isRevoked ? `red--text` : ``') {{ key.name }}
+//- em.caption.ml-1.red--text(v-if='key.isRevoked') (revoked)
+//- td.caption {{ key.keyShort }}
+//- td(:style='key.isRevoked ? `text-decoration: line-through;` : ``') {{ key.expiration | moment('LL') }}
+//- td {{ key.createdAt | moment('calendar') }}
+//- td {{ key.updatedAt | moment('calendar') }}
+//- td: v-btn(icon, @click='revoke(key)', :disabled='key.isRevoked'): v-icon(color='error') mdi-cancel
+//- v-card-text(v-else)
+//- v-alert.mb-0(icon='mdi-information', :value='true', outlined, color='info') {{$t('admin.api.noKeyInfo')}}
+
+//- create-api-key(v-model='isCreateDialogShown', @refresh='refresh(false)')
+
+//- v-dialog(v-model='isRevokeConfirmDialogShown', max-width='500', persistent)
+//- v-card
+//- .dialog-header.is-red {{$t('admin.api.revokeConfirm')}}
+//- v-card-text.pa-4
+//- i18next(tag='span', path='admin.api.revokeConfirmText')
+//- strong(place='name') {{ current.name }}
+//- v-card-actions
+//- v-spacer
+//- v-btn(text, @click='isRevokeConfirmDialogShown = false', :disabled='revokeLoading') {{$t('common.actions.cancel')}}
+//- v-btn(color='red', dark, @click='revokeConfirm', :loading='revokeLoading') {{$t('admin.api.revoke')}}
+
+
+
+
+
diff --git a/ux/src/pages/AdminApiCreate.vue b/ux/src/pages/AdminApiCreate.vue
new file mode 100644
index 00000000..a691ccea
--- /dev/null
+++ b/ux/src/pages/AdminApiCreate.vue
@@ -0,0 +1,236 @@
+
+ div
+ v-dialog(v-model='isShown', max-width='650', persistent)
+ v-card
+ .dialog-header.is-short
+ v-icon.mr-3(color='white') mdi-plus
+ span {{$t('admin.api.newKeyTitle')}}
+ v-card-text.pt-5
+ v-text-field(
+ outlined
+ prepend-icon='mdi-format-title'
+ v-model='name'
+ :label='$t(`admin.api.newKeyName`)'
+ persistent-hint
+ ref='keyNameInput'
+ :hint='$t(`admin.api.newKeyNameHint`)'
+ counter='255'
+ )
+ v-select.mt-3(
+ :items='expirations'
+ outlined
+ prepend-icon='mdi-clock'
+ v-model='expiration'
+ :label='$t(`admin.api.newKeyExpiration`)'
+ :hint='$t(`admin.api.newKeyExpirationHint`)'
+ persistent-hint
+ )
+ v-divider.mt-4
+ v-subheader.pl-2: strong.indigo--text {{$t('admin.api.newKeyPermissionScopes')}}
+ v-list.pl-8(nav)
+ v-list-item-group(v-model='fullAccess')
+ v-list-item(
+ :value='true'
+ active-class='indigo--text'
+ )
+ template(v-slot:default='{ active, toggle }')
+ v-list-item-action
+ v-checkbox(
+ :input-value='active'
+ :true-value='true'
+ color='indigo'
+ @click='toggle'
+ )
+ v-list-item-content
+ v-list-item-title {{$t('admin.api.newKeyFullAccess')}}
+ v-divider.mt-3
+ v-subheader.caption.indigo--text {{$t('admin.api.newKeyGroupPermissions')}}
+ v-list-item
+ v-select(
+ :disabled='fullAccess'
+ :items='groups'
+ item-text='name'
+ item-value='id'
+ outlined
+ color='indigo'
+ v-model='group'
+ :label='$t(`admin.api.newKeyGroup`)'
+ :hint='$t(`admin.api.newKeyGroupHint`)'
+ persistent-hint
+ )
+ v-card-chin
+ v-spacer
+ v-btn(text, @click='isShown = false', :disabled='loading') {{$t('common.actions.cancel')}}
+ v-btn.px-3(depressed, color='primary', @click='generate', :loading='loading')
+ v-icon(left) mdi-chevron-right
+ span {{$t('common.actions.generate')}}
+
+ v-dialog(
+ v-model='isCopyKeyDialogShown'
+ max-width='750'
+ persistent
+ overlay-color='blue darken-5'
+ overlay-opacity='.9'
+ )
+ v-card
+ v-toolbar(dense, flat, color='primary', dark) {{$t('admin.api.newKeyTitle')}}
+ v-card-text.pt-5
+ .body-2.text-center
+ i18next(tag='span', path='admin.api.newKeyCopyWarn')
+ strong(place='bold') {{$t('admin.api.newKeyCopyWarnBold')}}
+ v-textarea.mt-3(
+ ref='keyContentsIpt'
+ filled
+ no-resize
+ readonly
+ v-model='key'
+ :rows='10'
+ hide-details
+ )
+ v-card-chin
+ v-spacer
+ v-btn.px-3(depressed, dark, color='primary', @click='isCopyKeyDialogShown = false') {{$t('common.actions.close')}}
+
+
+
diff --git a/ux/src/pages/AdminAuth.vue b/ux/src/pages/AdminAuth.vue
new file mode 100644
index 00000000..0673c34f
--- /dev/null
+++ b/ux/src/pages/AdminAuth.vue
@@ -0,0 +1,535 @@
+
+q-page.admin-mail
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-security-lock.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.auth.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.auth.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/auth'
+ target='_blank'
+ type='a'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='loading'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-auto
+ q-card.rounded-borders.bg-dark
+ q-list(
+ style='min-width: 350px;'
+ padding
+ dark
+ )
+ q-item(
+ v-for='str of activeStrategies'
+ :key='str.key'
+ active-class='bg-primary text-white'
+ :active='selectedStrategy === str.key'
+ @click='selectedStrategy = str.key'
+ clickable
+ )
+ q-item-section(side)
+ q-icon(
+ :name='`img:` + str.strategy.icon'
+ )
+ q-item-section
+ q-item-label {{str.displayName}}
+ q-item-label(caption) {{str.strategy.title}}
+ q-item-section(side)
+ q-spinner-rings(:color='str.isEnabled ? `positive` : `negative`', size='sm')
+ q-btn.q-mt-sm.full-width(
+ color='primary'
+ icon='las la-plus'
+ :label='$t(`admin.auth.addStrategy`)'
+ )
+ q-menu(auto-close)
+ q-list(style='min-width: 350px;')
+ q-item(clickable)
+ .col Doude
+
+ //- v-flex(lg3, xs12)
+ //- v-card.animated.fadeInUp
+ //- v-toolbar(flat, color='teal', dark, dense)
+ //- .subtitle-1 {{$t('admin.auth.activeStrategies')}}
+ //- v-list(two-line, dense).py-0
+ //- draggable(
+ //- v-model='activeStrategies'
+ //- handle='.is-handle'
+ //- direction='vertical'
+ //- )
+ //- transition-group
+ //- v-list-item(
+ //- v-for='(str, idx) in activeStrategies'
+ //- :key='str.key'
+ //- @click='selectedStrategy = str.key'
+ //- :class='selectedStrategy === str.key ? ($vuetify.theme.dark ? `grey darken-5` : `teal lighten-5`) : ``'
+ //- )
+ //- v-list-item-avatar.is-handle(size='24')
+ //- v-icon(:color='selectedStrategy === str.key ? `teal` : `grey`') mdi-drag-horizontal
+ //- v-list-item-content
+ //- v-list-item-title.body-2(:class='selectedStrategy === str.key ? `teal--text` : ``') {{ str.displayName }}
+ //- v-list-item-subtitle: .caption(:class='selectedStrategy === str.key ? `teal--text ` : ``') {{ str.strategy.title }}
+ //- v-list-item-avatar(v-if='selectedStrategy === str.key', size='24')
+ //- v-icon.animated.fadeInLeft(color='teal', large) mdi-chevron-right
+ //- v-card-chin
+ //- v-menu(offset-y, bottom, min-width='250px', max-width='550px', max-height='50vh', style='flex: 1 1;', center)
+ //- template(v-slot:activator='{ on }')
+ //- v-btn(v-on='on', color='primary', depressed, block)
+ //- v-icon(left) mdi-plus
+ //- span {{$t('admin.auth.addStrategy')}}
+ //- v-list(dense)
+ //- template(v-for='(str, idx) of strategies')
+ //- v-list-item(
+ //- :key='str.key'
+ //- :disabled='str.isDisabled'
+ //- @click='addStrategy(str)'
+ //- )
+ //- v-list-item-avatar(height='24', width='48', tile)
+ //- v-img(:src='str.logo', width='48px', height='24px', contain, :style='str.isDisabled ? `opacity: .25;` : ``')
+ //- v-list-item-content
+ //- v-list-item-title {{str.title}}
+ //- v-list-item-subtitle: .caption(:style='str.isDisabled ? `opacity: .4;` : ``') {{str.description}}
+ //- v-divider(v-if='idx < strategies.length - 1')
+
+ //- v-flex(xs12, lg9)
+ //- v-card.animated.fadeInUp.wait-p2s
+ //- v-toolbar(color='primary', dense, flat, dark)
+ //- .subtitle-1 {{strategy.displayName}} #[em ({{strategy.strategy.title}})]
+ //- v-spacer
+ //- v-btn(small, outlined, dark, color='white', :disabled='strategy.key === `local`', @click='deleteStrategy()')
+ //- v-icon(left) mdi-close
+ //- span {{$t('common.actions.delete')}}
+ //- v-card-info(color='blue')
+ //- div
+ //- span {{strategy.strategy.description}}
+ //- .caption: a(:href='strategy.strategy.website') {{strategy.strategy.website}}
+ //- v-spacer
+ //- .admin-providerlogo
+ //- img(:src='strategy.strategy.logo', :alt='strategy.strategy.title')
+ //- v-card-text
+ //- .row
+ //- .col-8
+ //- v-text-field(
+ //- outlined
+ //- :label='$t(`admin.auth.displayName`)'
+ //- v-model='strategy.displayName'
+ //- prepend-icon='mdi-format-title'
+ //- :hint='$t(`admin.auth.displayNameHint`)'
+ //- persistent-hint
+ //- )
+ //- .col-4
+ //- v-switch.mt-1(
+ //- :label='$t(`admin.auth.strategyIsEnabled`)'
+ //- v-model='strategy.isEnabled'
+ //- color='primary'
+ //- prepend-icon='mdi-power'
+ //- :hint='$t(`admin.auth.strategyIsEnabledHint`)'
+ //- persistent-hint
+ //- inset
+ //- :disabled='strategy.key === `local`'
+ //- )
+ //- template(v-if='strategy.config && Object.keys(strategy.config).length > 0')
+ //- v-divider
+ //- .overline.my-5 {{$t('admin.auth.strategyConfiguration')}}
+ //- .pr-3
+ //- template(v-for='cfg in strategy.config')
+ //- v-select.mb-3(
+ //- v-if='cfg.value.type === "string" && cfg.value.enum'
+ //- outlined
+ //- :items='cfg.value.enum'
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- prepend-icon='mdi-cog-box'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- :class='cfg.value.hint ? "mb-2" : ""'
+ //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
+ //- )
+ //- v-switch.mb-6(
+ //- v-else-if='cfg.value.type === "boolean"'
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- color='primary'
+ //- prepend-icon='mdi-cog-box'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- inset
+ //- )
+ //- v-textarea.mb-3(
+ //- v-else-if='cfg.value.type === "string" && cfg.value.multiline'
+ //- outlined
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- prepend-icon='mdi-cog-box'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- :class='cfg.value.hint ? "mb-2" : ""'
+ //- )
+ //- v-text-field.mb-3(
+ //- v-else
+ //- outlined
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- prepend-icon='mdi-cog-box'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- :class='cfg.value.hint ? "mb-2" : ""'
+ //- :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
+ //- )
+ //- v-divider
+ //- .overline.my-5 {{$t('admin.auth.registration')}}
+ //- .pr-3
+ //- v-switch.ml-3(
+ //- v-model='strategy.selfRegistration'
+ //- :label='$t(`admin.auth.selfRegistration`)'
+ //- color='primary'
+ //- :hint='$t(`admin.auth.selfRegistrationHint`)'
+ //- persistent-hint
+ //- inset
+ //- )
+ //- v-combobox.ml-3.mt-5(
+ //- :label='$t(`admin.auth.domainsWhitelist`)'
+ //- v-model='strategy.domainWhitelist'
+ //- prepend-icon='mdi-email-check-outline'
+ //- outlined
+ //- :disabled='!strategy.selfRegistration'
+ //- :hint='$t(`admin.auth.domainsWhitelistHint`)'
+ //- persistent-hint
+ //- small-chips
+ //- deletable-chips
+ //- clearable
+ //- multiple
+ //- chips
+ //- )
+ //- v-autocomplete.mt-3.ml-3(
+ //- outlined
+ //- :disabled='!strategy.selfRegistration'
+ //- :items='groups'
+ //- item-text='name'
+ //- item-value='id'
+ //- :label='$t(`admin.auth.autoEnrollGroups`)'
+ //- v-model='strategy.autoEnrollGroups'
+ //- prepend-icon='mdi-account-group'
+ //- :hint='$t(`admin.auth.autoEnrollGroupsHint`)'
+ //- small-chips
+ //- persistent-hint
+ //- deletable-chips
+ //- clearable
+ //- multiple
+ //- chips
+ //- )
+
+ //- v-card.mt-4.wiki-form.animated.fadeInUp.wait-p4s(v-if='selectedStrategy !== `local`')
+ //- v-toolbar(color='primary', dense, flat, dark)
+ //- .subtitle-1 {{$t('admin.auth.configReference')}}
+ //- v-card-text
+ //- .body-2 {{$t('admin.auth.configReferenceSubtitle')}}
+ //- v-alert.mt-3.radius-7(v-if='host.length < 8', color='red', outlined, :value='true', icon='mdi-alert')
+ //- i18next(path='admin.auth.siteUrlNotSetup', tag='span')
+ //- strong(place='siteUrl') {{$t('admin.general.siteUrl')}}
+ //- strong(place='general') {{$t('admin.general.title')}}
+ //- .pa-3.mt-3.radius-7.grey(v-else, :class='$vuetify.theme.dark ? `darken-3-d5` : `lighten-3`')
+ //- .body-2: strong {{$t('admin.auth.allowedWebOrigins')}}
+ //- .body-2 {{host}}
+ //- v-divider.my-3
+ //- .body-2: strong {{$t('admin.auth.callbackUrl')}}
+ //- .body-2 {{host}}/login/{{strategy.key}}/callback
+ //- v-divider.my-3
+ //- .body-2: strong {{$t('admin.auth.loginUrl')}}
+ //- .body-2 {{host}}/login
+ //- v-divider.my-3
+ //- .body-2: strong {{$t('admin.auth.logoutUrl')}}
+ //- .body-2 {{host}}
+ //- v-divider.my-3
+ //- .body-2: strong {{$t('admin.auth.tokenEndpointAuthMethod')}}
+ //- .body-2 HTTP-POST
+
+
+
diff --git a/ux/src/pages/AdminComments.vue b/ux/src/pages/AdminComments.vue
new file mode 100644
index 00000000..e5cd139e
--- /dev/null
+++ b/ux/src/pages/AdminComments.vue
@@ -0,0 +1,206 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row, wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-chat-bubble.svg', alt='Comments', style='width: 80px;')
+ .admin-header-title
+ .headline.primary--text.animated.fadeInLeft {{$t('admin.comments.title')}}
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{$t('admin.comments.subtitle')}}
+ v-spacer
+ v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/comments', target='_blank')
+ v-icon mdi-help-circle
+ v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
+ v-icon mdi-refresh
+ v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
+ v-icon(left) mdi-check
+ span {{$t('common.actions.apply')}}
+
+ v-flex(lg3, xs12)
+ v-card.animated.fadeInUp
+ v-toolbar(flat, color='primary', dark, dense)
+ .subtitle-1 {{$t('admin.comments.provider')}}
+ v-list.py-0(two-line, dense)
+ template(v-for='(provider, idx) in providers')
+ v-list-item(:key='provider.key', @click='selectedProvider = provider.key', :disabled='!provider.isAvailable')
+ v-list-item-avatar(size='24')
+ v-icon(color='grey', v-if='!provider.isAvailable') mdi-minus-box-outline
+ v-icon(color='primary', v-else-if='provider.key === selectedProvider') mdi-checkbox-marked-circle-outline
+ v-icon(color='grey', v-else) mdi-checkbox-blank-circle-outline
+ v-list-item-content
+ v-list-item-title.body-2(:class='!provider.isAvailable ? `grey--text` : (selectedProvider === provider.key ? `primary--text` : ``)') {{ provider.title }}
+ v-list-item-subtitle: .caption(:class='!provider.isAvailable ? `grey--text text--lighten-1` : (selectedProvider === provider.key ? `blue--text ` : ``)') {{ provider.description }}
+ v-list-item-avatar(v-if='selectedProvider === provider.key', size='24')
+ v-icon.animated.fadeInLeft(color='primary', large) mdi-chevron-right
+ v-divider(v-if='idx < providers.length - 1')
+
+ v-flex(lg9, xs12)
+ v-card.animated.fadeInUp.wait-p2s
+ v-toolbar(color='primary', dense, flat, dark)
+ .subtitle-1 {{provider.title}}
+ v-card-info(color='blue')
+ div
+ div {{provider.description}}
+ span.caption: a(:href='provider.website') {{provider.website}}
+ v-spacer
+ .admin-providerlogo
+ img(:src='provider.logo', :alt='provider.title')
+ v-card-text
+ .overline.my-5 {{$t('admin.comments.providerConfig')}}
+ .body-2.ml-3(v-if='!provider.config || provider.config.length < 1'): em {{$t('admin.comments.providerNoConfig')}}
+ template(v-else, v-for='cfg in provider.config')
+ v-select.mb-3(
+ v-if='cfg.value.type === "string" && cfg.value.enum'
+ outlined
+ :items='cfg.value.enum'
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
+ )
+ v-switch.mb-6(
+ v-else-if='cfg.value.type === "boolean"'
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ color='primary'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ inset
+ )
+ v-textarea.mb-3(
+ v-else-if='cfg.value.type === "string" && cfg.value.multiline'
+ outlined
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ )
+ v-text-field.mb-3(
+ v-else
+ outlined
+ :key='cfg.key'
+ :label='cfg.value.title'
+ v-model='cfg.value.value'
+ prepend-icon='mdi-cog-box'
+ :hint='cfg.value.hint ? cfg.value.hint : ""'
+ persistent-hint
+ :class='cfg.value.hint ? "mb-2" : ""'
+ :style='cfg.value.maxWidth > 0 ? `max-width:` + cfg.value.maxWidth + `px;` : ``'
+ )
+
+
+
diff --git a/ux/src/pages/AdminDashboard.vue b/ux/src/pages/AdminDashboard.vue
new file mode 100644
index 00000000..7c4f17ed
--- /dev/null
+++ b/ux/src/pages/AdminDashboard.vue
@@ -0,0 +1,181 @@
+
+q-page.admin-dashboard
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-apps-tab.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.dashboard.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.dashboard.subtitle') }}
+ .row.q-px-md.q-col-gutter-md
+ .col-12 ---
+
+//- v-container(fluid, grid-list-lg)
+//- v-layout(row, wrap)
+//- v-flex(xs12)
+//- .admin-header
+//- img.animated.fadeInUp(src='/_assets/svg/icon-browse-page.svg', alt='Dashboard', style='width: 80px;')
+//- .admin-header-title
+//- .headline.primary--text.animated.fadeInLeft {{ $t('admin.dashboard.title') }}
+//- .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s {{ $t('admin.dashboard.subtitle') }}
+//- v-flex(xs12 md6 lg4 xl3 d-flex)
+//- v-card.primary.dashboard-card.animated.fadeInUp(dark)
+//- v-card-text
+//- v-icon.dashboard-icon mdi-file-document-outline
+//- .overline {{$t('admin.dashboard.pages')}}
+//- animated-number.display-1(
+//- :value='info.pagesTotal'
+//- :duration='2000'
+//- :formatValue='round'
+//- easing='easeOutQuint'
+//- )
+//- v-flex(xs12 md6 lg4 xl3 d-flex)
+//- v-card.blue.darken-3.dashboard-card.animated.fadeInUp.wait-p2s(dark)
+//- v-card-text
+//- v-icon.dashboard-icon mdi-account
+//- .overline {{$t('admin.dashboard.users')}}
+//- animated-number.display-1(
+//- :value='info.usersTotal'
+//- :duration='2000'
+//- :formatValue='round'
+//- easing='easeOutQuint'
+//- )
+//- v-flex(xs12 md6 lg4 xl3 d-flex)
+//- v-card.blue.darken-4.dashboard-card.animated.fadeInUp.wait-p4s(dark)
+//- v-card-text
+//- v-icon.dashboard-icon mdi-account-group
+//- .overline {{$t('admin.dashboard.groups')}}
+//- animated-number.display-1(
+//- :value='info.groupsTotal'
+//- :duration='2000'
+//- :formatValue='round'
+//- easing='easeOutQuint'
+//- )
+//- v-flex(xs12 md6 lg12 xl3 d-flex)
+//- v-card.dashboard-card.animated.fadeInUp.wait-p6s(
+//- :class='isLatestVersion ? "green" : "red lighten-2"'
+//- dark
+//- )
+//- v-btn.btn-animate-wrench(fab, absolute, :right='!$vuetify.rtl', :left='$vuetify.rtl', top, small, light, to='system', v-if='hasPermission(`manage:system`)')
+//- v-icon(:color='isLatestVersion ? `green` : `red darken-4`', small) mdi-wrench
+//- v-card-text
+//- v-icon.dashboard-icon mdi-blur
+//- .subtitle-1 Wiki.js {{info.currentVersion}}
+//- .body-2(v-if='isLatestVersion') {{$t('admin.dashboard.versionLatest')}}
+//- .body-2(v-else) {{$t('admin.dashboard.versionNew', { version: info.latestVersion })}}
+//- v-flex(xs12, xl6)
+//- v-card.radius-7.animated.fadeInUp.wait-p2s
+//- v-toolbar(:color='$q.dark.isActive ? `grey darken-2` : `grey lighten-5`', dense, flat)
+//- v-spacer
+//- .overline {{$t('admin.dashboard.recentPages')}}
+//- v-spacer
+//- v-data-table.pb-2(
+//- :items='recentPages'
+//- :headers='recentPagesHeaders'
+//- :loading='recentPagesLoading'
+//- hide-default-footer
+//- hide-default-header
+//- )
+//- template(slot='item', slot-scope='props')
+//- tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
+//- td
+//- .body-2: strong {{ props.item.title }}
+//- td.admin-pages-path
+//- v-chip(label, small, :color='$q.dark.isActive ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
+//- span.ml-2.grey--text(:class='$q.dark.isActive ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
+//- td.text-right.caption(width='250') {{ props.item.updatedAt | moment('calendar') }}
+//- v-flex(xs12, xl6)
+//- v-card.radius-7.animated.fadeInUp.wait-p4s
+//- v-toolbar(:color='$q.dark.isActive ? `grey darken-2` : `grey lighten-5`', dense, flat)
+//- v-spacer
+//- .overline {{$t('admin.dashboard.lastLogins')}}
+//- v-spacer
+//- v-data-table.pb-2(
+//- :items='lastLogins'
+//- :headers='lastLoginsHeaders'
+//- :loading='lastLoginsLoading'
+//- hide-default-footer
+//- hide-default-header
+//- )
+//- template(slot='item', slot-scope='props')
+//- tr.is-clickable(:active='props.selected', @click='$router.push(`/users/` + props.item.id)')
+//- td
+//- .body-2: strong {{ props.item.name }}
+//- td.text-right.caption(width='250') {{ props.item.lastLoginAt | moment('calendar') }}
+
+//- v-flex(xs12)
+//- v-card.dashboard-contribute.animated.fadeInUp.wait-p4s
+//- v-card-text
+//- img(src='/_assets/svg/icon-heart-health.svg', alt='Contribute', style='height: 80px;')
+//- .pl-5
+//- .subtitle-1 {{$t('admin.contribute.title')}}
+//- .body-2.mt-3: strong {{$t('admin.dashboard.contributeSubtitle')}}
+//- .body-2 {{$t('admin.dashboard.contributeHelp')}}
+//- v-btn.mx-0.mt-4(:color='$q.dark.isActive ? `indigo lighten-3` : `indigo`', outlined, small, to='/contribute')
+//- .caption: strong {{$t('admin.dashboard.contributeLearnMore')}}
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminEditors.vue b/ux/src/pages/AdminEditors.vue
new file mode 100644
index 00000000..5c1b69c3
--- /dev/null
+++ b/ux/src/pages/AdminEditors.vue
@@ -0,0 +1,129 @@
+
+q-page.admin-flags
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-cashbook.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.editors.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.editors.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/editors'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card.shadow-1
+ q-list(separator)
+ q-item(v-for='editor of editors', :key='editor.id')
+ blueprint-icon(:icon='editor.icon')
+ q-item-section
+ q-item-label: strong {{$t(`admin.editors.` + editor.id + `Name`)}}
+ q-item-label.flex.items-center(caption)
+ span {{$t(`admin.editors.` + editor.id + `Description`)}}
+ template(v-if='editor.config')
+ q-item-section(
+ side
+ )
+ q-btn(
+ icon='las la-cog'
+ :label='$t(`admin.editors.configuration`)'
+ :color='$q.dark.isActive ? `blue-grey-3` : `blue-grey-8`'
+ outline
+ no-caps
+ padding='xs md'
+ )
+ q-separator.q-ml-md(vertical)
+ q-item-section(side)
+ q-toggle.q-pr-sm(
+ v-model='editor.isActive'
+ :color='editor.isDisabled ? `grey` : `primary`'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :label='$t(`admin.sites.isActive`)'
+ :aria-label='$t(`admin.sites.isActive`)'
+ :disabled='editor.isDisabled'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminExtensions.vue b/ux/src/pages/AdminExtensions.vue
new file mode 100644
index 00000000..d7da5df1
--- /dev/null
+++ b/ux/src/pages/AdminExtensions.vue
@@ -0,0 +1,204 @@
+
+q-page.admin-extensions
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-module.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.extensions.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.extensions.subtitle') }}
+ .col-auto
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/extensions'
+ target='_blank'
+ type='a'
+ )
+ q-btn.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card.shadow-1
+ q-list(separator)
+ q-item(
+ v-for='(ext, idx) of extensions'
+ :key='`ext-` + ext.key'
+ )
+ blueprint-icon(icon='module')
+ q-item-section
+ q-item-label {{ext.title}}
+ q-item-label(caption) {{ext.description}}
+ q-item-section(side)
+ .row
+ q-btn-group(unelevated)
+ q-btn(
+ icon='las la-check'
+ size='sm'
+ color='positive'
+ padding='xs sm'
+ v-if='ext.isInstalled'
+ :ripple='false'
+ )
+ q-tooltip(
+ anchor='center left'
+ self='center right'
+ ) {{$t('admin.extensions.installed')}}
+ q-btn(
+ :label='$t(`admin.extensions.install`)'
+ color='blue-7'
+ v-if='ext.isCompatible && !ext.isInstalled && ext.isInstallable'
+ @click='install(ext)'
+ no-caps
+ )
+ q-btn(
+ v-else-if='ext.isCompatible && ext.isInstalled && ext.isInstallable'
+ :label='$t(`admin.extensions.reinstall`)'
+ color='blue-7'
+ @click='install(ext)'
+ no-caps
+ )
+ q-btn(
+ v-else-if='ext.isCompatible && ext.isInstalled && !ext.isInstallable'
+ :label='$t(`admin.extensions.installed`)'
+ color='positive'
+ no-caps
+ :ripple='false'
+ )
+ q-btn(
+ v-else-if='ext.isCompatible'
+ :label='$t(`admin.extensions.instructions`)'
+ icon='las la-info-circle'
+ color='indigo'
+ outline
+ type='a'
+ :href='`https://docs.js.wiki/admin/extensions/` + ext.key'
+ target='_blank'
+ no-caps
+ )
+ q-tooltip(
+ anchor='center left'
+ self='center right'
+ ) {{$t('admin.extensions.instructionsHint')}}
+ q-btn(
+ v-else
+ color='negative'
+ outline
+ :label='$t(`admin.extensions.incompatible`)'
+ no-caps
+ :ripple='false'
+ )
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminFlags.vue b/ux/src/pages/AdminFlags.vue
new file mode 100644
index 00000000..517c4eec
--- /dev/null
+++ b/ux/src/pages/AdminFlags.vue
@@ -0,0 +1,160 @@
+
+q-page.admin-flags
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-windsock.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.flags.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.flags.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.requarks.io/admin/flags'
+ target='_blank'
+ type='a'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='loading'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ q-card.shadow-1.q-py-sm
+ q-item
+ q-item-section
+ q-card.bg-negative.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-exclamation-triangle', size='sm')
+ q-card-section
+ span {{ $t('admin.flags.warn.label') }}
+ .text-caption.text-red-1 {{ $t('admin.flags.warn.hint') }}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='flag-filled')
+ q-item-section
+ q-item-label {{$t(`admin.flags.ldapdebug.label`)}}
+ q-item-label(caption) {{$t(`admin.flags.ldapdebug.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='flags.ldapdebug'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.flags.ldapdebug.label`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='flag-filled')
+ q-item-section
+ q-item-label {{$t(`admin.flags.sqllog.label`)}}
+ q-item-label(caption) {{$t(`admin.flags.sqllog.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='flags.sqllog'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.flags.sqllog.label`)'
+ )
+ q-card.shadow-1.q-py-sm.q-mt-md
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='heart-outline')
+ q-item-section
+ q-item-label {{$t(`admin.flags.hidedonatebtn.label`)}}
+ q-item-label(caption) {{$t(`admin.flags.hidedonatebtn.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='flags.hidedonatebtn'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.flags.hidedonatebtn.label`)'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminGeneral.vue b/ux/src/pages/AdminGeneral.vue
new file mode 100644
index 00000000..1c945a88
--- /dev/null
+++ b/ux/src/pages/AdminGeneral.vue
@@ -0,0 +1,720 @@
+
+q-page.admin-general
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-web.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.general.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.general.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/general'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ //- -----------------------
+ //- Site Info
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.general.siteInfo')}}
+ q-item
+ blueprint-icon(icon='home')
+ q-item-section
+ q-item-label {{$t(`admin.general.siteTitle`)}}
+ q-item-label(caption) {{$t(`admin.general.siteTitleHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.title'
+ dense
+ :rules=`[
+ val => /^[^<>"]+$/.test(val) || $t('admin.general.siteTitleInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.general.siteTitle`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='select-all')
+ q-item-section
+ q-item-label {{$t(`admin.general.siteDescription`)}}
+ q-item-label(caption) {{$t(`admin.general.siteDescriptionHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.description'
+ dense
+ :aria-label='$t(`admin.general.siteDescription`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='dns')
+ q-item-section
+ q-item-label {{$t(`admin.general.siteHostname`)}}
+ q-item-label(caption) {{$t(`admin.general.siteHostnameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.hostname'
+ dense
+ :rules=`[
+ val => /^(([a-z0-9.-]+)|([*]{1}))$/.test(val) || $t('admin.general.siteHostnameInvalid')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.general.siteHostname`)'
+ )
+
+ //- -----------------------
+ //- Footer / Copyright
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.general.footerCopyright')}}
+ q-item
+ blueprint-icon(icon='building')
+ q-item-section
+ q-item-label {{$t(`admin.general.companyName`)}}
+ q-item-label(caption) {{$t(`admin.general.companyNameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.company'
+ dense
+ :aria-label='$t(`admin.general.companyName`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='copyright')
+ q-item-section
+ q-item-label {{$t(`admin.general.contentLicense`)}}
+ q-item-label(caption) {{$t(`admin.general.contentLicenseHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.contentLicense'
+ :options='contentLicenses'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.general.contentLicense`)'
+ )
+
+ //- -----------------------
+ //- FEATURES
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.general.features')}}
+ q-item(tag='label')
+ blueprint-icon(icon='discussion-forum')
+ q-item-section
+ q-item-label {{$t(`admin.general.allowComments`)}}
+ q-item-label(caption) {{$t(`admin.general.allowCommentsHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.features.comments'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowComments`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='pen')
+ q-item-section
+ q-item-label {{$t(`admin.general.allowContributions`)}}
+ q-item-label(caption) {{$t(`admin.general.allowContributionsHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.features.contributions'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowContributions`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='administrator-male')
+ q-item-section
+ q-item-label {{$t(`admin.general.allowProfile`)}}
+ q-item-label(caption) {{$t(`admin.general.allowProfileHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.features.profile'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowProfile`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='star-half-empty')
+ q-item-section
+ q-item-label {{$t(`admin.general.allowRatings`)}}
+ q-item-label(caption) {{$t(`admin.general.allowRatingsHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='config.features.ratingsMode'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('admin.general.ratingsOff'), value: 'off' },
+ { label: $t('admin.general.ratingsThumbs'), value: 'thumbs' },
+ { label: $t('admin.general.ratingsStars'), value: 'stars' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='search')
+ q-item-section
+ q-item-label {{$t(`admin.general.allowSearch`)}}
+ q-item-label(caption) {{$t(`admin.general.allowSearchHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.features.search'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowSearch`)'
+ )
+
+ .col-12.col-lg-5
+ //- -----------------------
+ //- Logo
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.general.logo')}}
+ q-item
+ blueprint-icon.self-start(icon='butterfly', indicator, :indicator-text='$t(`admin.extensions.requiresSharp`)')
+ q-item-section
+ .flex
+ q-item-section
+ q-item-label {{$t(`admin.general.logoUpl`)}}
+ q-item-label(caption) {{$t(`admin.general.logoUplHint`)}}
+ q-item-section.col-auto
+ q-btn(
+ label='Upload'
+ unelevated
+ icon='las la-upload'
+ color='primary'
+ text-color='white'
+ @click='uploadLogo'
+ )
+ q-toolbar.bg-header.q-mt-md.rounded-borders.text-white(
+ dark
+ style='height: 64px;'
+ )
+ q-btn(dense, flat, to='/')
+ q-avatar(
+ v-if='config.logoText'
+ size='34px'
+ square
+ )
+ img(src='/_assets/logo-wikijs.svg')
+ img(
+ v-else
+ src='https://m.media-amazon.com/images/G/01/audibleweb/arya/navigation/audible_logo._V517446980_.svg'
+ style='height: 34px;'
+ )
+ q-toolbar-title.text-h6.font-poppins(v-if='config.logoText') {{config.title}}
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='information')
+ q-item-section
+ q-item-label {{$t(`admin.general.displaySiteTitle`)}}
+ q-item-label(caption) {{$t(`admin.general.displaySiteTitleHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.logoText'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.displaySiteTitle`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon.self-start(icon='starfish', indicator, :indicator-text='$t(`admin.extensions.requiresSharp`)')
+ q-item-section
+ .flex
+ q-item-section
+ q-item-label {{$t(`admin.general.favicon`)}}
+ q-item-label(caption) {{$t(`admin.general.faviconHint`)}}
+ q-item-section.col-auto
+ q-btn(
+ label='Upload'
+ unelevated
+ icon='las la-upload'
+ color='primary'
+ text-color='white'
+ @click='uploadFavicon'
+ )
+ .admin-general-favicontabs.q-mt-md
+ div
+ q-avatar(
+ size='24px'
+ square
+ )
+ img(src='/_assets/logo-wikijs.svg')
+ .text-caption.q-ml-sm {{config.title}}
+ div
+ q-icon(name='las la-otter', size='24px', color='grey')
+ .text-caption.q-ml-sm Lorem ipsum
+ div
+ q-icon(name='las la-mountain', size='24px', color='grey')
+ .text-caption.q-ml-sm Dolor sit amet...
+
+ //- -----------------------
+ //- Defaults
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='config.defaults')
+ q-card-section
+ .text-subtitle1 {{$t('admin.general.defaults')}}
+ q-item
+ blueprint-icon(icon='timezone')
+ q-item-section
+ q-item-label {{$t(`admin.general.defaultTimezone`)}}
+ q-item-label(caption) {{$t(`admin.general.defaultTimezoneHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.defaults.timezone'
+ :options='timezones'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :virtual-scroll-slice-size='1000'
+ :aria-label='$t(`admin.general.defaultTimezone`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='calendar')
+ q-item-section
+ q-item-label {{$t(`admin.general.defaultDateFormat`)}}
+ q-item-label(caption) {{$t(`admin.general.defaultDateFormatHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.defaults.dateFormat'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.general.defaultDateFormat`)'
+ :options=`[
+ { label: $t('profile.localeDefault'), value: '' },
+ { label: 'DD/MM/YYYY', value: 'DD/MM/YYYY' },
+ { label: 'DD.MM.YYYY', value: 'DD.MM.YYYY' },
+ { label: 'MM/DD/YYYY', value: 'MM/DD/YYYY' },
+ { label: 'YYYY-MM-DD', value: 'YYYY-MM-DD' },
+ { label: 'YYYY/MM/DD', value: 'YYYY/MM/DD' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='clock')
+ q-item-section
+ q-item-label {{$t(`admin.general.defaultTimeFormat`)}}
+ q-item-label(caption) {{$t(`admin.general.defaultTimeFormatHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='config.defaults.timeFormat'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('admin.general.defaultTimeFormat12h'), value: '12h' },
+ { label: $t('admin.general.defaultTimeFormat24h'), value: '24h' }
+ ]`
+ )
+
+ //- -----------------------
+ //- SEO
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='config.robots')
+ q-card-section
+ .text-subtitle1 SEO
+ q-item(tag='label')
+ blueprint-icon(icon='bot')
+ q-item-section
+ q-item-label {{$t(`admin.general.searchAllowIndexing`)}}
+ q-item-label(caption) {{$t(`admin.general.searchAllowIndexingHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.robots.index'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.searchAllowIndexing`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='polyline')
+ q-item-section
+ q-item-label {{$t(`admin.general.searchAllowFollow`)}}
+ q-item-label(caption) {{$t(`admin.general.searchAllowFollowHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.robots.follow'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.searchAllowFollow`)'
+ )
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminGroups.vue b/ux/src/pages/AdminGroups.vue
new file mode 100644
index 00000000..dd304e07
--- /dev/null
+++ b/ux/src/pages/AdminGroups.vue
@@ -0,0 +1,228 @@
+
+q-page.admin-groups
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-people.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.groups.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.groups.subtitle') }}
+ .col-auto.flex.items-center
+ q-input.denser.q-mr-sm(
+ outlined
+ v-model='search'
+ dense
+ :class='$q.dark.isActive ? `bg-dark` : `bg-white`'
+ )
+ template(#prepend)
+ q-icon(name='las la-search')
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/groups'
+ target='_blank'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ @click='load'
+ :loading='loading > 0'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.groups.create`)'
+ color='primary'
+ @click='createGroup'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card.shadow-1
+ q-table(
+ :rows='groups'
+ :columns='headers'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='loading > 0'
+ :filter='search'
+ )
+ template(v-slot:body-cell-id='props')
+ q-td(:props='props')
+ q-icon(name='las la-users', color='primary', size='sm')
+ template(v-slot:body-cell-name='props')
+ q-td(:props='props')
+ .flex.items-center
+ strong {{props.value}}
+ q-icon.q-ml-sm(
+ v-if='props.row.isSystem'
+ name='las la-lock'
+ color='pink'
+ )
+ template(v-slot:body-cell-usercount='props')
+ q-td(:props='props')
+ q-chip.text-caption(
+ square
+ :color='$q.dark.isActive ? `dark-6` : `grey-2`'
+ :text-color='$q.dark.isActive ? `white` : `grey-8`'
+ dense
+ ) {{$t('admin.groups.usersCount', { count: props.value })}}
+ template(v-slot:body-cell-edit='props')
+ q-td(:props='props')
+ q-btn.acrylic-btn.q-mr-sm(
+ flat
+ :to='`/_admin/groups/` + props.row.id'
+ icon='las la-pen'
+ color='indigo'
+ :label='$t(`common.actions.edit`)'
+ no-caps
+ )
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash'
+ :color='props.row.isSystem ? `grey` : `accent`'
+ :disabled='props.row.isSystem'
+ @click='deleteGroup(props.row)'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminLocale.vue b/ux/src/pages/AdminLocale.vue
new file mode 100644
index 00000000..c977605b
--- /dev/null
+++ b/ux/src/pages/AdminLocale.vue
@@ -0,0 +1,339 @@
+
+q-page.admin-locale
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-language.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.locale.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.locale.subtitle') }}
+ .col-auto.flex
+ q-btn.q-mr-md(
+ icon='las la-download'
+ :label='$t(`admin.locale.downloadNew`)'
+ unelevated
+ color='primary'
+ :disabled='loading > 0'
+ @click='installNewLocale'
+ )
+ q-separator.q-mr-md(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.requarks.io/admin/locale'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-7
+ //- -----------------------
+ //- Locale Options
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.locale.settings')}}
+ q-item
+ blueprint-icon(icon='translation')
+ q-item-section
+ q-item-label {{namespacing ? $t(`admin.locale.base.labelWithNS`) : $t(`admin.locale.base.label`)}}
+ q-item-label(caption) {{$t(`admin.locale.base.hint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='selectedLocale'
+ :options='installedLocales'
+ option-value='code'
+ option-label='name'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.locale.base.label`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='unit')
+ q-item-section
+ q-item-label {{$t(`admin.locale.namespaces.label`)}}
+ q-item-label(caption) {{$t(`admin.locale.namespaces.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='namespacing'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.locale.namespaces.label`)'
+ )
+ q-item
+ q-item-section
+ q-card.bg-info.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-info-circle', size='sm')
+ q-card-section
+ span {{ $t('admin.locale.namespacingPrefixWarning.title', { langCode: selectedLocale }) }}
+ .text-caption.text-yellow-1 {{ $t('admin.locale.namespacingPrefixWarning.subtitle') }}
+
+ .col-5
+ //- -----------------------
+ //- Namespacing
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm(v-if='namespacing')
+ q-card-section
+ .text-subtitle1 {{$t('admin.locale.activeNamespaces')}}
+
+ q-item(
+ v-for='(lc, idx) of installedLocales'
+ :key='lc.code'
+ :tag='lc.code !== selectedLocale ? `label` : null'
+ )
+ blueprint-icon(:text='lc.code')
+ q-item-section
+ q-item-label {{lc.name}}
+ q-item-label(caption) {{lc.nativeName}}
+ q-item-section(avatar)
+ q-toggle(
+ :disable='lc.code === selectedLocale'
+ v-model='namespaces'
+ :val='lc.code'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='lc.name'
+ )
+
+ //- q-separator.q-my-sm(inset)
+ //- q-item
+ //- blueprint-icon(icon='test-passed')
+ //- q-item-section
+ //- q-item-label {{$t(`admin.locale.activeNamespaces.label`)}}
+ //- q-item-label(caption) {{$t(`admin.locale.activeNamespaces.hint`)}}
+ //- q-item-section
+ //- q-select(
+ //- outlined
+ //- :disable='!namespacing'
+ //- v-model='namespaces'
+ //- :options='installedLocales'
+ //- multiple
+ //- use-chips
+ //- option-value='code'
+ //- option-label='name'
+ //- emit-value
+ //- map-options
+ //- dense
+ //- :aria-label='$t(`admin.locale.activeNamespaces.label`)'
+ //- )
+
+
+
+
diff --git a/ux/src/pages/AdminLogin.vue b/ux/src/pages/AdminLogin.vue
new file mode 100644
index 00000000..0904a37b
--- /dev/null
+++ b/ux/src/pages/AdminLogin.vue
@@ -0,0 +1,308 @@
+
+q-page.admin-login
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-bunch-of-keys.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.login.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.login.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/login'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Experience
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.login.experience')}}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='full-image', indicator, :indicator-text='$t(`admin.extensions.requiresSharp`)')
+ q-item-section
+ q-item-label {{$t(`admin.login.background`)}}
+ q-item-label(caption) {{$t(`admin.login.backgroundHint`)}}
+ q-item-section.col-auto
+ q-btn(
+ label='Upload'
+ unelevated
+ icon='las la-upload'
+ color='primary'
+ text-color='white'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='close-pane')
+ q-item-section
+ q-item-label {{$t(`admin.login.bypassScreen`)}}
+ q-item-label(caption) {{$t(`admin.login.bypassScreenHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.authAutoLogin'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.login.bypassScreen`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='no-access')
+ q-item-section
+ q-item-label {{$t(`admin.login.bypassUnauthorized`)}}
+ q-item-label(caption) {{$t(`admin.login.bypassUnauthorizedHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.authBypassUnauthorized'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.login.bypassUnauthorized`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='double-right')
+ q-item-section
+ q-item-label {{$t(`admin.login.loginRedirect`)}}
+ q-item-label(caption) {{$t(`admin.login.loginRedirectHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.loginRedirect'
+ dense
+ :rules=`[
+ val => invalidCharsRegex.test(val) || $t('admin.login.loginRedirectInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.login.loginRedirect`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='chevron-right')
+ q-item-section
+ q-item-label {{$t(`admin.login.welcomeRedirect`)}}
+ q-item-label(caption) {{$t(`admin.login.welcomeRedirectHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.welcomeRedirect'
+ dense
+ :rules=`[
+ val => invalidCharsRegex.test(val) || $t('admin.login.welcomeRedirectInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.login.welcomeRedirect`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='exit')
+ q-item-section
+ q-item-label {{$t(`admin.login.logoutRedirect`)}}
+ q-item-label(caption) {{$t(`admin.login.logoutRedirectHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.logoutRedirect'
+ dense
+ :rules=`[
+ val => invalidCharsRegex.test(val) || $t('admin.login.logoutRedirectInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='$t(`admin.login.logoutRedirect`)'
+ )
+
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Providers
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.login.providers')}}
+ q-card-section.admin-login-providers.q-pt-none
+ draggable(
+ class='q-list rounded-borders'
+ :list='providers'
+ :animation='150'
+ handle='.handle'
+ @end='dragStarted = false'
+ item-key='id'
+ )
+ template(#item='{element}')
+ q-item
+ q-item-section(side)
+ q-icon.handle(name='las la-bars')
+ blueprint-icon(:icon='element.icon')
+ q-item-section
+ q-item-label {{element.label}}
+ q-item-label(caption) {{element.provider}}
+ q-item-section(side)
+ q-toggle(
+ v-model='element.isActive'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ label='Visible'
+ :aria-label='element.label'
+ )
+ q-item.q-pt-none
+ q-item-section
+ q-card.bg-info.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-info-circle', size='sm')
+ q-card-section.text-caption {{ $t('admin.login.providersVisbleWarning') }}
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminMail.vue b/ux/src/pages/AdminMail.vue
new file mode 100644
index 00000000..38bdfe4d
--- /dev/null
+++ b/ux/src/pages/AdminMail.vue
@@ -0,0 +1,473 @@
+
+q-page.admin-mail
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-message-settings.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.mail.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.mail.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/mail'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ //- -----------------------
+ //- Configuration
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.mail.configuration')}}
+ q-item
+ blueprint-icon(icon='contact')
+ q-item-section
+ q-item-label {{$t(`admin.mail.senderName`)}}
+ q-item-label(caption) {{$t(`admin.general.senderNameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.senderName'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`admin.mail.senderName`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='envelope')
+ q-item-section
+ q-item-label {{$t(`admin.mail.senderEmail`)}}
+ q-item-label(caption) {{$t(`admin.general.senderEmailHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.senderEmail'
+ dense
+ :aria-label='$t(`admin.mail.senderEmail`)'
+ )
+ //- -----------------------
+ //- SMTP
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.mail.smtp')}}
+ q-item
+ blueprint-icon(icon='dns')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpHost`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpHostHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.host'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`admin.mail.smtpHost`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='ethernet-off')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpPort`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpPortHint`)}}
+ q-item-section(style='flex: 0 0 120px;')
+ q-input(
+ outlined
+ v-model='config.port'
+ dense
+ :aria-label='$t(`admin.mail.smtpPort`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='secure')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpTLS`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpTLSHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.secure'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.mail.smtpTLS`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='security-ssl')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpVerifySSL`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpVerifySSLHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.verifySSL'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.mail.smtpVerifySSL`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='test-account')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpUser`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpUserHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.user'
+ dense
+ :aria-label='$t(`admin.mail.smtpUser`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='password')
+ q-item-section
+ q-item-label {{$t(`admin.mail.smtpPwd`)}}
+ q-item-label(caption) {{$t(`admin.mail.smtpPwdHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.pass'
+ dense
+ :aria-label='$t(`admin.mail.smtpPwd`)'
+ )
+ //- -----------------------
+ //- DKIM
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.mail.dkim')}}
+ q-item.q-pt-none
+ q-item-section
+ q-card.bg-info.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-info-circle', size='sm')
+ q-card-section.text-caption {{ $t('admin.mail.dkimHint') }}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='received')
+ q-item-section
+ q-item-label {{$t(`admin.mail.dkimUse`)}}
+ q-item-label(caption) {{$t(`admin.mail.dkimUseHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.useDKIM'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.mail.dkimUse`)'
+ )
+ template(v-if='config.useDKIM')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='dns')
+ q-item-section
+ q-item-label {{$t(`admin.mail.dkimDomainName`)}}
+ q-item-label(caption) {{$t(`admin.mail.dkimDomainNameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.dkimDomainName'
+ dense
+ :aria-label='$t(`admin.mail.dkimDomainName`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='access')
+ q-item-section
+ q-item-label {{$t(`admin.mail.dkimKeySelector`)}}
+ q-item-label(caption) {{$t(`admin.mail.dkimKeySelectorHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.dkimKeySelector'
+ dense
+ :aria-label='$t(`admin.mail.dkimKeySelector`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='grand-master-key')
+ q-item-section
+ q-item-label {{$t(`admin.mail.dkimPrivateKey`)}}
+ q-item-label(caption) {{$t(`admin.mail.dkimPrivateKeyHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.dkimPrivateKey'
+ dense
+ :aria-label='$t(`admin.mail.dkimPrivateKey`)'
+ type='textarea'
+ )
+
+ .col-12.col-lg-5
+ //- -----------------------
+ //- MAIL TEMPLATES
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.mail.templates')}}
+ q-list
+ q-item
+ blueprint-icon(icon='resume-template')
+ q-item-section
+ q-item-label {{$t(`admin.mail.templateWelcome`)}}
+ q-item-section(side)
+ q-btn(
+ outline
+ no-caps
+ icon='las la-edit'
+ color='primary'
+ @click=''
+ :label='$t(`common.actions.edit`)'
+ )
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='resume-template')
+ q-item-section
+ q-item-label {{$t(`admin.mail.templateResetPwd`)}}
+ q-item-section(side)
+ q-btn(
+ outline
+ no-caps
+ icon='las la-edit'
+ color='primary'
+ @click=''
+ :label='$t(`common.actions.edit`)'
+ )
+ //- -----------------------
+ //- SMTP TEST
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.mail.test')}}
+ q-item
+ blueprint-icon.self-start(icon='email')
+ q-item-section
+ q-item-label {{$t(`admin.mail.testRecipient`)}}
+ q-item-label(caption) {{$t(`admin.mail.testRecipientHint`)}}
+ q-input.q-mt-md(
+ outlined
+ v-model='testEmail'
+ dense
+ :aria-label='$t(`admin.mail.testRecipient`)'
+ )
+ .flex.justify-end.q-pr-md.q-py-sm
+ q-btn(
+ unelevated
+ color='primary'
+ icon='las la-paper-plane'
+ :label='$t(`admin.mail.testSend`)'
+ @click='sendTest'
+ :loading='testLoading'
+ )
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminNavigation.vue b/ux/src/pages/AdminNavigation.vue
new file mode 100644
index 00000000..acae2e9d
--- /dev/null
+++ b/ux/src/pages/AdminNavigation.vue
@@ -0,0 +1,589 @@
+
+q-page.admin-navigation
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-tree-structure.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.navigation.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.navigation.subtitle') }}
+ .col-auto
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.requarks.io/admin/navigation'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-6
+//- v-container(fluid, grid-list-lg)
+//- v-layout(row wrap)
+//- v-flex(xs12)
+//- .admin-header
+//- img.animated.fadeInUp(src='/_assets/svg/icon-triangle-arrow.svg', alt='Navigation', style='width: 80px;')
+//- .admin-header-title
+//- .headline.primary--text.animated.fadeInLeft {{$t('navigation.title')}}
+//- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('navigation.subtitle')}}
+//- v-spacer
+//- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/navigation', target='_blank')
+//- v-icon mdi-help-circle
+//- v-btn.mx-3.animated.fadeInDown.wait-p2s.mr-3(icon, outlined, color='grey', @click='refresh')
+//- v-icon mdi-refresh
+//- v-btn.animated.fadeInDown(color='success', depressed, @click='save', large)
+//- v-icon(left) mdi-check
+//- span {{$t('common.actions.apply')}}
+//- v-container.pa-0.mt-3(fluid, grid-list-lg)
+//- v-row(dense)
+//- v-col(cols='3')
+//- v-card.animated.fadeInUp
+//- v-toolbar(color='teal', dark, dense, flat, height='56')
+//- v-toolbar-title.subtitle-1 {{$t('admin.navigation.mode')}}
+//- v-list(nav, two-line)
+//- v-list-item-group(v-model='config.mode', mandatory, :color='$vuetify.theme.dark ? `teal lighten-3` : `teal`')
+//- v-list-item(value='TREE')
+//- v-list-item-avatar
+//- img(src='/_assets/svg/icon-tree-structure-dotted.svg', alt='Site Tree')
+//- v-list-item-content
+//- v-list-item-title {{$t('admin.navigation.modeSiteTree.title')}}
+//- v-list-item-subtitle {{$t('admin.navigation.modeSiteTree.description')}}
+//- v-list-item-avatar
+//- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `TREE` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
+//- v-icon(v-else, :color='config.mode === `TREE` ? `teal` : `grey lighten-3`') mdi-check-circle
+//- v-list-item(value='STATIC')
+//- v-list-item-avatar
+//- img(src='/_assets/svg/icon-features-list.svg', alt='Static Navigation')
+//- v-list-item-content
+//- v-list-item-title {{$t('admin.navigation.modeStatic.title')}}
+//- v-list-item-subtitle {{$t('admin.navigation.modeStatic.description')}}
+//- v-list-item-avatar
+//- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `STATIC` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
+//- v-icon(v-else, :color='config.mode === `STATIC` ? `teal` : `grey lighten-3`') mdi-check-circle
+//- v-list-item(value='MIXED')
+//- v-list-item-avatar
+//- img(src='/_assets/svg/icon-user-menu-male-dotted.svg', alt='Custom Navigation')
+//- v-list-item-content
+//- v-list-item-title {{$t('admin.navigation.modeCustom.title')}}
+//- v-list-item-subtitle {{$t('admin.navigation.modeCustom.description')}}
+//- v-list-item-avatar
+//- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `MIXED` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
+//- v-icon(v-else, :color='config.mode === `MIXED` ? `teal` : `grey lighten-3`') mdi-check-circle
+//- v-list-item(value='NONE')
+//- v-list-item-avatar
+//- img(src='/_assets/svg/icon-cancel-dotted.svg', alt='None')
+//- v-list-item-content
+//- v-list-item-title {{$t('admin.navigation.modeNone.title')}}
+//- v-list-item-subtitle {{$t('admin.navigation.modeNone.description')}}
+//- v-list-item-avatar
+//- v-icon(v-if='$vuetify.theme.dark', :color='config.mode === `none` ? `teal lighten-3` : `grey darken-2`') mdi-check-circle
+//- v-icon(v-else, :color='config.mode === `none` ? `teal` : `grey lighten-3`') mdi-check-circle
+//- v-col(cols='9', v-if='config.mode === `MIXED` || config.mode === `STATIC`')
+//- v-card.animated.fadeInUp.wait-p2s
+//- v-row(no-gutters, align='stretch')
+//- v-col(style='flex: 0 0 350px;')
+//- v-card.grey(flat, style='height: 100%; border-radius: 4px 0 0 4px;', :class='$vuetify.theme.dark ? `darken-4-l5` : `lighten-3`')
+//- .teal.lighten-1.pa-2.d-flex(style='margin-bottom: 1px; height:56px;')
+//- v-select(
+//- :disabled='locales.length < 2'
+//- label='Locale'
+//- hide-details
+//- solo
+//- flat
+//- background-color='teal darken-2'
+//- dark
+//- dense
+//- v-model='currentLang'
+//- :items='locales'
+//- item-text='nativeName'
+//- item-value='code'
+//- )
+//- v-tooltip(top)
+//- template(v-slot:activator='{ on }')
+//- v-btn.ml-2(icon, tile, color='white', v-on='on', @click='copyFromLocaleDialogIsShown = true')
+//- v-icon mdi-arrange-send-backward
+//- span {{$t('admin.navigation.copyFromLocale')}}
+//- v-list.py-2(dense, nav, dark, class='blue darken-2', style='border-radius: 0;')
+//- v-list-item(v-if='currentTree.length < 1')
+//- v-list-item-avatar(size='24'): v-icon(color='blue lighten-3') mdi-alert
+//- v-list-item-content
+//- em.caption.blue--text.text--lighten-4 {{$t('navigation.emptyList')}}
+//- draggable(v-model='currentTree')
+//- template(v-for='navItem in currentTree')
+//- v-list-item(
+//- v-if='navItem.kind === "link"'
+//- :key='navItem.id'
+//- :class='(navItem === current) ? "blue" : ""'
+//- @click='selectItem(navItem)'
+//- )
+//- v-list-item-avatar(size='24', tile)
+//- v-icon(v-if='navItem.icon.match(/fa[a-z] fa-/)', size='19') {{ navItem.icon }}
+//- v-icon(v-else) {{ navItem.icon }}
+//- v-list-item-title {{navItem.label}}
+//- .py-2.clickable(
+//- v-else-if='navItem.kind === "divider"'
+//- :key='navItem.id'
+//- :class='(navItem === current) ? "blue" : ""'
+//- @click='selectItem(navItem)'
+//- )
+//- v-divider
+//- v-subheader.pl-4.clickable(
+//- v-else-if='navItem.kind === "header"'
+//- :key='navItem.id'
+//- :class='(navItem === current) ? "blue" : ""'
+//- @click='selectItem(navItem)'
+//- ) {{navItem.label}}
+//- v-card-chin
+//- v-menu(offset-y, bottom, min-width='200px', style='flex: 1 1;')
+//- template(v-slot:activator='{ on }')
+//- v-btn(v-on='on', color='primary', depressed, block)
+//- v-icon(left) mdi-plus
+//- span {{$t('common.actions.add')}}
+//- v-list
+//- v-list-item(@click='addItem("link")')
+//- v-list-item-avatar(size='24'): v-icon mdi-link
+//- v-list-item-title {{$t('navigation.link')}}
+//- v-list-item(@click='addItem("header")')
+//- v-list-item-avatar(size='24'): v-icon mdi-format-title
+//- v-list-item-title {{$t('navigation.header')}}
+//- v-list-item(@click='addItem("divider")')
+//- v-list-item-avatar(size='24'): v-icon mdi-minus
+//- v-list-item-title {{$t('navigation.divider')}}
+//- v-col
+//- v-card(flat, style='border-radius: 0 4px 4px 0;')
+//- template(v-if='current.kind === "link"')
+//- v-toolbar(height='56', color='teal lighten-1', flat, dark)
+//- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.link') })}}
+//- v-spacer
+//- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
+//- v-icon(left) mdi-delete
+//- span {{$t('navigation.delete', { kind: $t('navigation.link') })}}
+//- v-card-text
+//- v-text-field(
+//- outlined
+//- :label='$t("navigation.label")'
+//- prepend-icon='mdi-format-title'
+//- v-model='current.label'
+//- counter='255'
+//- )
+//- v-text-field(
+//- outlined
+//- :label='$t("navigation.icon")'
+//- prepend-icon='mdi-dice-5'
+//- v-model='current.icon'
+//- hide-details
+//- )
+//- .caption.pt-3.pl-5 The default icon set is #[strong Material Design Icons]. In order to use another icon set, you must first select it in the Theme administration section.
+//- .caption.pt-3.pl-5: strong Material Design Icons
+//- .caption.pl-5 Refer to the #[a(href='https://materialdesignicons.com/', target='_blank') Material Design Icons Reference] for the list of all possible values. You must prefix all values with #[code mdi-], e.g. #[code mdi-home]
+//- .caption.pt-3.pl-5: strong Font Awesome 5
+//- .caption.pl-5 Refer to the #[a(href='https://fontawesome.com/icons?d=gallery&m=free', target='_blank') Font Awesome 5 Reference] for the list of all possible values. You must prefix all values with #[code fas fa-], e.g. #[code fas fa-home]. Note that some icons use different prefixes (e.g. #[code fab], #[code fad], #[code fal], #[code far]).
+//- .caption.pt-3.pl-5: strong Font Awesome 4
+//- .caption.pl-5 Refer to the #[a(href='https://fontawesome.com/v4.7.0/icons/', target='_blank') Font Awesome 4 Reference] for the list of all possible values. You must prefix all values with #[code fa fa-], e.g. #[code fa fa-home]
+//- v-divider
+//- v-card-text
+//- v-select(
+//- outlined
+//- :label='$t("navigation.targetType")'
+//- prepend-icon='mdi-near-me'
+//- :items='navTypes'
+//- v-model='current.targetType'
+//- hide-details
+//- )
+//- v-text-field.mt-4(
+//- v-if='current.targetType === `external` || current.targetType === `externalblank`'
+//- outlined
+//- :label='$t("navigation.target")'
+//- prepend-icon='mdi-near-me'
+//- v-model='current.target'
+//- hide-details
+//- )
+//- .d-flex.align-center.mt-4(v-else-if='current.targetType === "page"')
+//- v-btn.ml-8(
+//- color='primary'
+//- dark
+//- @click='selectPage'
+//- )
+//- v-icon(left) mdi-magnify
+//- span {{$t('admin.navigation.selectPageButton')}}
+//- .caption.ml-4.primary--text {{current.target}}
+//- v-text-field(
+//- v-else-if='current.targetType === `search`'
+//- outlined
+//- :label='$t("navigation.navType.searchQuery")'
+//- prepend-icon='search'
+//- v-model='current.target'
+//- )
+//- v-divider
+
+//- template(v-else-if='current.kind === "header"')
+//- v-toolbar(height='56', color='teal lighten-1', flat, dark)
+//- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.header') })}}
+//- v-spacer
+//- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
+//- v-icon(left) mdi-delete
+//- span {{$t('navigation.delete', { kind: $t('navigation.header') })}}
+//- v-card-text
+//- v-text-field(
+//- outlined
+//- :label='$t("navigation.label")'
+//- prepend-icon='mdi-format-title'
+//- v-model='current.label'
+//- )
+//- v-divider
+
+//- div(v-else-if='current.kind === "divider"')
+//- v-toolbar(height='56', color='teal lighten-1', flat, dark)
+//- .subtitle-1 {{$t('navigation.edit', { kind: $t('navigation.divider') })}}
+//- v-spacer
+//- v-btn.px-5(color='white', outlined, @click='deleteItem(current)')
+//- v-icon(left) mdi-delete
+//- span {{$t('navigation.delete', { kind: $t('navigation.divider') })}}
+
+//- v-card-text(v-if='current.kind')
+//- v-radio-group.pl-8(v-model='current.visibilityMode', mandatory, hide-details)
+//- v-radio(:label='$t("admin.navigation.visibilityMode.all")', value='all', color='primary')
+//- v-radio.mt-3(:label='$t("admin.navigation.visibilityMode.restricted")', value='restricted', color='primary')
+//- .pl-8
+//- v-select.pl-8.mt-3(
+//- item-text='name'
+//- item-value='id'
+//- outlined
+//- prepend-icon='mdi-account-group'
+//- label='Groups'
+//- :disabled='current.visibilityMode !== `restricted`'
+//- v-model='current.visibilityGroups'
+//- :items='groups'
+//- persistent-hint
+//- clearable
+//- multiple
+//- )
+//- template(v-else)
+//- v-toolbar(height='56', color='teal lighten-1', flat, dark)
+//- v-card-text.grey--text(v-if='currentTree.length > 0') {{$t('navigation.noSelectionText')}}
+//- v-card-text.grey--text(v-else) {{$t('navigation.noItemsText')}}
+
+//- v-dialog(v-model='copyFromLocaleDialogIsShown', max-width='650', persistent)
+//- v-card
+//- .dialog-header.is-short.is-teal
+//- v-icon.mr-3(color='white') mdi-arrange-send-backward
+//- span {{$t('admin.navigation.copyFromLocale')}}
+//- v-card-text.pt-5
+//- .body-2 {{$t('admin.navigation.copyFromLocaleInfoText')}}
+//- v-select.mt-3(
+//- :items='locales'
+//- item-text='nativeName'
+//- item-value='code'
+//- outlined
+//- prepend-icon='mdi-web'
+//- v-model='copyFromLocaleCode'
+//- :label='$t(`admin.navigation.sourceLocale`)'
+//- :hint='$t(`admin.navigation.sourceLocaleHint`)'
+//- persistent-hint
+//- )
+//- v-card-chin
+//- v-spacer
+//- v-btn(text, @click='copyFromLocaleDialogIsShown = false') {{$t('common.actions.cancel')}}
+//- v-btn.px-3(depressed, color='primary', @click='copyFromLocale')
+//- v-icon(left) mdi-chevron-right
+//- span {{$t('common.actions.copy')}}
+
+//- page-selector(mode='select', v-model='selectPageModal', :open-handler='selectPageHandle', path='home', :locale='currentLang')
+
+
+
+
+
diff --git a/ux/src/pages/AdminPages.vue b/ux/src/pages/AdminPages.vue
new file mode 100644
index 00000000..4bd5958c
--- /dev/null
+++ b/ux/src/pages/AdminPages.vue
@@ -0,0 +1,170 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-file.svg', alt='Page', style='width: 80px;')
+ .admin-header-title
+ .headline.blue--text.text--darken-2.animated.fadeInLeft Pages
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s Manage pages
+ v-spacer
+ v-btn.animated.fadeInDown.wait-p1s(icon, color='grey', outlined, @click='refresh')
+ v-icon.grey--text mdi-refresh
+ v-btn.animated.fadeInDown.mx-3(color='primary', outlined, @click='recyclebin', disabled)
+ v-icon(left) mdi-delete-outline
+ span Recycle Bin
+ v-btn.animated.fadeInDown(color='primary', depressed, large, to='pages/visualize')
+ v-icon(left) mdi-graph
+ span Visualize
+ v-card.mt-3.animated.fadeInUp
+ .pa-2.d-flex.align-center(:class='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-3`')
+ v-text-field(
+ solo
+ flat
+ v-model='search'
+ prepend-inner-icon='mdi-file-search-outline'
+ label='Search Pages...'
+ hide-details
+ dense
+ style='max-width: 400px;'
+ )
+ v-spacer
+ v-select.ml-2(
+ solo
+ flat
+ hide-details
+ dense
+ label='Locale'
+ :items='langs'
+ v-model='selectedLang'
+ style='max-width: 250px;'
+ )
+ v-select.ml-2(
+ solo
+ flat
+ hide-details
+ dense
+ label='Publish State'
+ :items='states'
+ v-model='selectedState'
+ style='max-width: 250px;'
+ )
+ v-divider
+ v-data-table(
+ :items='filteredPages'
+ :headers='headers'
+ :search='search'
+ :page.sync='pagination'
+ :items-per-page='15'
+ :loading='loading'
+ must-sort,
+ sort-by='updatedAt',
+ sort-desc,
+ hide-default-footer
+ )
+ template(slot='item', slot-scope='props')
+ tr.is-clickable(:active='props.selected', @click='$router.push(`/pages/` + props.item.id)')
+ td.text-xs-right {{ props.item.id }}
+ td
+ .body-2: strong {{ props.item.title }}
+ .caption {{ props.item.description }}
+ td.admin-pages-path
+ v-chip(label, small, :color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-4`') {{ props.item.locale }}
+ span.ml-2.grey--text(:class='$vuetify.theme.dark ? `text--lighten-1` : `text--darken-2`') / {{ props.item.path }}
+ td {{ props.item.createdAt | moment('calendar') }}
+ td {{ props.item.updatedAt | moment('calendar') }}
+ template(slot='no-data')
+ v-alert.ma-3(icon='mdi-alert', :value='true', outlined) No pages to display.
+ .text-center.py-2.animated.fadeInDown(v-if='this.pageTotal > 1')
+ v-pagination(v-model='pagination', :length='pageTotal')
+
+
+
+
+
diff --git a/ux/src/pages/AdminPagesEdit.vue b/ux/src/pages/AdminPagesEdit.vue
new file mode 100644
index 00000000..ef29082c
--- /dev/null
+++ b/ux/src/pages/AdminPagesEdit.vue
@@ -0,0 +1,235 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row, wrap, v-if='page.id')
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-view-details.svg', alt='Edit Page', style='width: 80px;')
+ .admin-header-title
+ .headline.blue--text.text--darken-2.animated.fadeInLeft Page Details
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s
+ v-chip.ml-0.mr-2(label, small).caption ID {{page.id}}
+ span /{{page.locale}}/{{page.path}}
+ v-spacer
+ template(v-if='page.isPublished')
+ status-indicator.mr-3(positive, pulse)
+ .caption.green--text {{$t('common.page.published')}}
+ template(v-else)
+ status-indicator.mr-3(negative, pulse)
+ .caption.red--text {{$t('common.page.unpublished')}}
+ template(v-if='page.isPrivate')
+ status-indicator.mr-3.ml-4(intermediary, pulse)
+ .caption.deep-orange--text {{$t('common.page.private')}}
+ template(v-else)
+ status-indicator.mr-3.ml-4(active, pulse)
+ .caption.blue--text {{$t('common.page.global')}}
+ v-spacer
+ v-btn.animated.fadeInDown.wait-p3s(color='grey', icon, outlined, to='/pages')
+ v-icon mdi-arrow-left
+ v-menu(offset-y, origin='top right')
+ template(v-slot:activator='{ on }')
+ v-btn.mx-3.animated.fadeInDown.wait-p2s(color='black', v-on='on', depressed, dark)
+ span Actions
+ v-icon(right) mdi-chevron-down
+ v-list(dense, nav)
+ v-list-item(:href='`/` + page.locale + `/` + page.path')
+ v-list-item-icon
+ v-icon(color='indigo') mdi-text-subject
+ v-list-item-title View
+ v-list-item(:href='`/e/` + page.locale + `/` + page.path')
+ v-list-item-icon
+ v-icon(color='indigo') mdi-pencil
+ v-list-item-title Edit
+ v-list-item(@click='', disabled)
+ v-list-item-icon
+ v-icon(color='grey') mdi-cube-scan
+ v-list-item-title Re-Render
+ v-list-item(@click='', disabled)
+ v-list-item-icon
+ v-icon(color='grey') mdi-earth-remove
+ v-list-item-title Unpublish
+ v-list-item(:href='`/s/` + page.locale + `/` + page.path')
+ v-list-item-icon
+ v-icon(color='indigo') mdi-code-tags
+ v-list-item-title View Source
+ v-list-item(:href='`/h/` + page.locale + `/` + page.path')
+ v-list-item-icon
+ v-icon(color='indigo') mdi-history
+ v-list-item-title View History
+ v-list-item(@click='', disabled)
+ v-list-item-icon
+ v-icon(color='grey') mdi-content-duplicate
+ v-list-item-title Duplicate
+ v-list-item(@click='', disabled)
+ v-list-item-icon
+ v-icon(color='grey') mdi-content-save-move-outline
+ v-list-item-title Move / Rename
+ v-dialog(v-model='deletePageDialog', max-width='500')
+ template(v-slot:activator='{ on }')
+ v-list-item(v-on='on')
+ v-list-item-icon
+ v-icon(color='red') mdi-trash-can-outline
+ v-list-item-title Delete
+ v-card
+ .dialog-header.is-short.is-red
+ v-icon.mr-2(color='white') mdi-file-document-box-remove-outline
+ span {{$t('common.page.delete')}}
+ v-card-text.pt-5
+ i18next.body-2(path='common.page.deleteTitle', tag='div')
+ span.red--text.text--darken-2(place='title') {{page.title}}
+ .caption {{$t('common.page.deleteSubtitle')}}
+ v-chip.mt-3.ml-0.mr-1(label, color='red lighten-4', disabled, small)
+ .caption.red--text.text--darken-2 {{page.locale.toUpperCase()}}
+ v-chip.mt-3.mx-0(label, color='red lighten-5', disabled, small)
+ span.red--text.text--darken-2 /{{page.path}}
+ v-card-chin
+ v-spacer
+ v-btn(text, @click='deletePageDialog = false', :disabled='loading') {{$t('common.actions.cancel')}}
+ v-btn(color='red darken-2', @click='deletePage', :loading='loading').white--text {{$t('common.actions.delete')}}
+ v-btn.animated.fadeInDown(color='success', large, depressed, disabled)
+ v-icon(left) mdi-check
+ span Save Changes
+ v-flex(xs12, lg6)
+ v-card.animated.fadeInUp
+ v-toolbar(color='primary', dense, dark, flat)
+ v-icon.mr-2 mdi-text-subject
+ span Properties
+ v-list.py-0(two-line, dense)
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Title
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.title }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Description
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.description || '-' }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Locale
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.locale }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Path
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.path }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Editor
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.editor || '?' }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Content Type
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.contentType || '?' }}
+ v-divider
+ v-list-item
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Page Hash
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.hash }}
+
+ v-flex(xs12, lg6)
+ v-card.animated.fadeInUp.wait-p2s
+ v-toolbar(color='primary', dense, dark, flat)
+ v-icon.mr-2 mdi-account-multiple
+ span Users
+ v-list.py-0(two-line, dense)
+ v-list-item
+ v-list-item-avatar(size='24')
+ v-btn(icon, :to='`/users/` + page.creatorId')
+ v-icon(color='grey') mdi-account
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Creator
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.creatorName }} #[em.caption ({{ page.creatorEmail }})]
+ v-list-item-action
+ v-list-item-action-text {{ page.createdAt | moment('calendar') }}
+ v-divider
+ v-list-item
+ v-list-item-avatar(size='24')
+ v-btn(icon, :to='`/users/` + page.authorId')
+ v-icon(color='grey') mdi-account
+ v-list-item-content
+ v-list-item-title: .overline.grey--text Last Editor
+ v-list-item-subtitle.body-2(:class='$vuetify.theme.dark ? `grey--text text--lighten-2` : `grey--text text--darken-3`') {{ page.authorName }} #[em.caption ({{ page.authorEmail }})]
+ v-list-item-action
+ v-list-item-action-text {{ page.updatedAt | moment('calendar') }}
+
+ v-layout(row, align-center, v-else)
+ v-progress-circular(indeterminate, width='2', color='grey')
+ .body-2.pl-3.grey--text {{ $t('common.page.loading') }}
+
+
+
+
+
diff --git a/ux/src/pages/AdminPagesVisualize.vue b/ux/src/pages/AdminPagesVisualize.vue
new file mode 100644
index 00000000..116b38a9
--- /dev/null
+++ b/ux/src/pages/AdminPagesVisualize.vue
@@ -0,0 +1,405 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-venn-diagram.svg', alt='Visualize Pages', style='width: 80px;')
+ .admin-header-title
+ .headline.blue--text.text--darken-2.animated.fadeInLeft Visualize Pages
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p2s Dendrogram representation of your pages
+ v-spacer
+ v-select.mx-5.animated.fadeInDown.wait-p1s(
+ v-if='locales.length > 0'
+ v-model='currentLocale'
+ :items='locales'
+ style='flex: 0 1 120px;'
+ solo
+ dense
+ hide-details
+ item-value='code'
+ item-text='name'
+ )
+ v-btn-toggle.animated.fadeInDown(v-model='graphMode', color='primary', dense, rounded)
+ v-btn.px-5(value='htree')
+ v-icon(left, :color='graphMode === `htree` ? `primary` : `grey darken-3`') mdi-sitemap
+ span.text-none Hierarchical Tree
+ v-btn.px-5(value='hradial')
+ v-icon(left, :color='graphMode === `hradial` ? `primary` : `grey darken-3`') mdi-chart-donut-variant
+ span.text-none Hierarchical Radial
+ v-btn.px-5(value='rradial')
+ v-icon(left, :color='graphMode === `rradial` ? `primary` : `grey darken-3`') mdi-blur-radial
+ span.text-none Relational Radial
+ .admin-pages-visualize-svg(ref='svgContainer', v-show='pages.length >= 1')
+ v-alert(v-if='pages.length < 1', outlined, type='warning', style='max-width: 650px; margin: 0 auto;') Looks like there's no data yet to graph!
+
+
+
+
+
diff --git a/ux/src/pages/AdminRendering.vue b/ux/src/pages/AdminRendering.vue
new file mode 100644
index 00000000..5e9004de
--- /dev/null
+++ b/ux/src/pages/AdminRendering.vue
@@ -0,0 +1,318 @@
+
+q-page.admin-mail
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-rich-text-converter.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.rendering.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.rendering.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/rendering'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ //- v-container(fluid, grid-list-lg)
+ //- v-layout(row, wrap)
+ //- v-flex(xs12)
+ //- .admin-header
+ //- img.animated.fadeInUp(src='/_assets/svg/icon-process.svg', alt='Rendering', style='width: 80px;')
+ //- .admin-header-title
+ //- .headline.primary--text.animated.fadeInLeft {{ $t('admin.rendering.title') }}
+ //- .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{ $t('admin.rendering.subtitle') }}
+ //- v-spacer
+ //- v-btn.animated.fadeInDown.wait-p3s(icon, outlined, color='grey', href='https://docs.requarks.io/rendering', target='_blank')
+ //- v-icon mdi-help-circle
+ //- v-btn.mx-3.animated.fadeInDown.wait-p2s(icon, outlined, color='grey', @click='refresh')
+ //- v-icon mdi-refresh
+ //- v-btn.animated.fadeInDown(color='success', @click='save', depressed, large)
+ //- v-icon(left) mdi-check
+ //- span {{$t('common.actions.apply')}}
+
+ //- v-flex.animated.fadeInUp(lg3, xs12)
+ //- v-toolbar(
+ //- color='blue darken-2'
+ //- dense
+ //- flat
+ //- dark
+ //- )
+ //- .subtitle-1 Pipeline
+ //- v-expansion-panels.adm-rendering-pipeline(
+ //- v-model='selectedCore'
+ //- accordion
+ //- mandatory
+ //- )
+ //- v-expansion-panel(
+ //- v-for='core in renderers'
+ //- :key='core.key'
+ //- )
+ //- v-expansion-panel-header(
+ //- hide-actions
+ //- ripple
+ //- )
+ //- v-toolbar(
+ //- color='blue'
+ //- dense
+ //- dark
+ //- flat
+ //- )
+ //- v-spacer
+ //- .body-2 {{core.input}}
+ //- v-icon.mx-2 mdi-arrow-right-circle
+ //- .caption {{core.output}}
+ //- v-spacer
+ //- v-expansion-panel-content
+ //- v-list.py-0(two-line, dense)
+ //- template(v-for='(rdr, n) in core.children')
+ //- v-list-item(
+ //- :key='rdr.key'
+ //- @click='selectRenderer(rdr.key)'
+ //- :class='currentRenderer.key === rdr.key ? ($vuetify.theme.dark ? `grey darken-4-l4` : `blue lighten-5`) : ``'
+ //- )
+ //- v-list-item-avatar(size='24', tile)
+ //- v-icon(:color='currentRenderer.key === rdr.key ? "primary" : "grey"') {{rdr.icon}}
+ //- v-list-item-content
+ //- v-list-item-title {{rdr.title}}
+ //- v-list-item-subtitle: .caption {{rdr.description}}
+ //- v-list-item-avatar(size='24')
+ //- status-indicator(v-if='rdr.isEnabled', positive, pulse)
+ //- status-indicator(v-else, negative, pulse)
+ //- v-divider.my-0(v-if='n < core.children.length - 1')
+
+ //- v-flex(lg9, xs12)
+ //- v-card.wiki-form.animated.fadeInUp
+ //- v-toolbar(
+ //- color='indigo'
+ //- dark
+ //- flat
+ //- dense
+ //- )
+ //- v-icon.mr-2 {{currentRenderer.icon}}
+ //- .subtitle-1 {{currentRenderer.title}}
+ //- v-spacer
+ //- v-switch(
+ //- dark
+ //- color='white'
+ //- label='Enabled'
+ //- v-model='currentRenderer.isEnabled'
+ //- hide-details
+ //- inset
+ //- )
+ //- v-card-info(color='blue')
+ //- div
+ //- div {{currentRenderer.description}}
+ //- span.caption: a(href='https://docs.requarks.io/en/rendering', target='_blank') Documentation
+ //- v-card-text.pb-4.pl-4
+ //- .overline.mb-5 Rendering Module Configuration
+ //- .body-2.ml-3(v-if='!currentRenderer.config || currentRenderer.config.length < 1'): em This rendering module has no configuration options you can modify.
+ //- template(v-else, v-for='(cfg, idx) in currentRenderer.config')
+ //- v-select(
+ //- v-if='cfg.value.type === "string" && cfg.value.enum'
+ //- outlined
+ //- :items='cfg.value.enum'
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- :class='cfg.value.hint ? "mb-2" : ""'
+ //- color='indigo'
+ //- )
+ //- v-switch(
+ //- v-else-if='cfg.value.type === "boolean"'
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- color='indigo'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- inset
+ //- )
+ //- v-text-field(
+ //- v-else
+ //- outlined
+ //- :key='cfg.key'
+ //- :label='cfg.value.title'
+ //- v-model='cfg.value.value'
+ //- :hint='cfg.value.hint ? cfg.value.hint : ""'
+ //- persistent-hint
+ //- :class='cfg.value.hint ? "mb-2" : ""'
+ //- color='indigo'
+ //- )
+ //- v-divider.my-5(v-if='idx < currentRenderer.config.length - 1')
+ //- v-card-chin
+ //- v-spacer
+ //- .caption.pr-3.grey--text Module: {{ currentRenderer.key }}
+
+
+
+
+
diff --git a/ux/src/pages/AdminSecurity.vue b/ux/src/pages/AdminSecurity.vue
new file mode 100644
index 00000000..52656fa7
--- /dev/null
+++ b/ux/src/pages/AdminSecurity.vue
@@ -0,0 +1,502 @@
+
+q-page.admin-mail
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-protect.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.security.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.security.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/security'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Security
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.security.title')}}
+ q-item.q-pt-none
+ q-item-section
+ q-card.bg-negative.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-exclamation-triangle', size='sm')
+ q-card-section.text-caption {{ $t('admin.security.warn') }}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='rfid-signal')
+ q-item-section
+ q-item-label {{$t(`admin.security.disallowFloc`)}}
+ q-item-label(caption) {{$t(`admin.security.disallowFlocHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.disallowFloc'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.disallowFloc`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='maximize-window')
+ q-item-section
+ q-item-label {{$t(`admin.security.disallowIframe`)}}
+ q-item-label(caption) {{$t(`admin.security.disallowIframeHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.disallowIframe'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.disallowIframe`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='do-not-touch')
+ q-item-section
+ q-item-label {{$t(`admin.security.enforceSameOriginReferrerPolicy`)}}
+ q-item-label(caption) {{$t(`admin.security.enforceSameOriginReferrerPolicyHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.enforceSameOriginReferrerPolicy'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.enforceSameOriginReferrerPolicy`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='curly-arrow')
+ q-item-section
+ q-item-label {{$t(`admin.security.disallowOpenRedirect`)}}
+ q-item-label(caption) {{$t(`admin.security.disallowOpenRedirectHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.disallowOpenRedirect'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.disallowOpenRedirect`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='download-from-cloud')
+ q-item-section
+ q-item-label {{$t(`admin.security.forceAssetDownload`)}}
+ q-item-label(caption) {{$t(`admin.security.forceAssetDownloadHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.forceAssetDownload'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.forceAssetDownload`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='door-sensor-alarmed')
+ q-item-section
+ q-item-label {{$t(`admin.security.trustProxy`)}}
+ q-item-label(caption) {{$t(`admin.security.trustProxyHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.trustProxy'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.trustProxy`)'
+ )
+ //- -----------------------
+ //- HSTS
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.security.hsts')}}
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='hips')
+ q-item-section
+ q-item-label {{$t(`admin.security.enforceHsts`)}}
+ q-item-label(caption) {{$t(`admin.security.enforceHstsHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.enforceHsts'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.enforceHsts`)'
+ )
+ template(v-if='config.enforceHsts')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='timer')
+ q-item-section
+ q-item-label {{$t(`admin.security.hstsDuration`)}}
+ q-item-label(caption) {{$t(`admin.security.hstsDurationHint`)}}
+ q-item-section(style='flex: 0 0 200px;')
+ q-select(
+ outlined
+ v-model='config.hstsDuration'
+ :options='hstsDurations'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.security.hstsDuration`)'
+ )
+
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Uploads
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.security.uploads')}}
+ q-item.q-pt-none
+ q-item-section
+ q-card.bg-info.text-white.rounded-borders(flat)
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-info-circle', size='sm')
+ q-card-section.text-caption {{ $t('admin.security.uploadsInfo') }}
+ q-item
+ blueprint-icon(icon='upload-to-the-cloud')
+ q-item-section
+ q-item-label {{$t(`admin.security.maxUploadSize`)}}
+ q-item-label(caption) {{$t(`admin.security.maxUploadSizeHint`)}}
+ q-item-section(style='flex: 0 0 200px;')
+ q-input(
+ outlined
+ v-model.number='humanUploadMaxFileSize'
+ dense
+ :aria-label='$t(`admin.security.maxUploadSize`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='upload-to-ftp')
+ q-item-section
+ q-item-label {{$t(`admin.security.maxUploadBatch`)}}
+ q-item-label(caption) {{$t(`admin.security.maxUploadBatchHint`)}}
+ q-item-section(style='flex: 0 0 200px;')
+ q-input(
+ outlined
+ v-model.number='config.uploadMaxFiles'
+ dense
+ :suffix='$t(`admin.security.maxUploadBatchSuffix`)'
+ :aria-label='$t(`admin.security.maxUploadBatch`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='scan-stock')
+ q-item-section
+ q-item-label {{$t(`admin.security.scanSVG`)}}
+ q-item-label(caption) {{$t(`admin.security.scanSVGHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.uploadScanSVG'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.security.scanSVG`)'
+ )
+
+ //- -----------------------
+ //- CORS
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.security.cors')}}
+ q-item
+ blueprint-icon(icon='firewall')
+ q-item-section
+ q-item-label {{$t(`admin.security.corsMode`)}}
+ q-item-label(caption) {{$t(`admin.security.corsModeHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.corsMode'
+ :options='corsModes'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.security.corsMode`)'
+ )
+ template(v-if='config.corsMode === `HOSTNAMES`')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='todo-list', key='corsHostnames')
+ q-item-section
+ q-item-label {{$t(`admin.security.corsHostnames`)}}
+ q-item-label(caption) {{$t(`admin.security.corsHostnamesHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.corsConfig'
+ dense
+ type='textarea'
+ :aria-label='$t(`admin.security.corsHostnames`)'
+ )
+ template(v-else-if='config.corsMode === `REGEX`')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='validation', key='corsRegex')
+ q-item-section
+ q-item-label {{$t(`admin.security.corsRegex`)}}
+ q-item-label(caption) {{$t(`admin.security.corsRegexHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.corsConfig'
+ dense
+ :aria-label='$t(`admin.security.corsRegex`)'
+ )
+
+ //- -----------------------
+ //- JWT
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.security.jwt')}}
+ q-item
+ blueprint-icon(icon='ticket')
+ q-item-section
+ q-item-label {{$t(`admin.security.jwtAudience`)}}
+ q-item-label(caption) {{$t(`admin.security.jwtAudienceHint`)}}
+ q-item-section(style='flex: 0 0 250px;')
+ q-input(
+ outlined
+ v-model='config.authJwtAudience'
+ dense
+ :aria-label='$t(`admin.security.jwtAudience`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='expired')
+ q-item-section
+ q-item-label {{$t(`admin.security.tokenExpiration`)}}
+ q-item-label(caption) {{$t(`admin.security.tokenExpirationHint`)}}
+ q-item-section(style='flex: 0 0 140px;')
+ q-input(
+ outlined
+ v-model='config.authJwtExpiration'
+ dense
+ :aria-label='$t(`admin.security.tokenExpiration`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='future')
+ q-item-section
+ q-item-label {{$t(`admin.security.tokenRenewalPeriod`)}}
+ q-item-label(caption) {{$t(`admin.security.tokenRenewalPeriodHint`)}}
+ q-item-section(style='flex: 0 0 140px;')
+ q-input(
+ outlined
+ v-model='config.authJwtRenewablePeriod'
+ dense
+ :aria-label='$t(`admin.security.tokenRenewalPeriod`)'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminSites.vue b/ux/src/pages/AdminSites.vue
new file mode 100644
index 00000000..63370e03
--- /dev/null
+++ b/ux/src/pages/AdminSites.vue
@@ -0,0 +1,178 @@
+
+q-page.admin-locale
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-change-theme.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.sites.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.sites.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/sites'
+ target='_blank'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ @click='refresh'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.sites.new`)'
+ color='primary'
+ @click='createSite'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card.shadow-1
+ q-list(separator)
+ q-item(
+ v-for='site of sites'
+ :key='site.id'
+ )
+ q-item-section(side)
+ q-icon(name='las la-chalkboard', color='primary')
+ q-item-section
+ strong {{site.title}}
+ q-item-section
+ div
+ q-chip.q-mx-none(
+ v-if='site.hostname !== `*`'
+ square
+ color='blue-7'
+ text-color='white'
+ size='sm'
+ )
+ q-avatar(
+ icon='las la-angle-right'
+ color='blue-5'
+ text-color='white'
+ )
+ span {{site.hostname}}
+ q-chip.q-mx-none(
+ v-else
+ square
+ color='indigo-7'
+ text-color='white'
+ size='sm'
+ )
+ q-avatar(
+ icon='las la-asterisk'
+ color='indigo-5'
+ text-color='white'
+ )
+ span catch-all
+ q-item-section(side)
+ q-toggle(
+ :model-value='site.isEnabled'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :label='$t(`admin.sites.isActive`)'
+ :aria-label='$t(`admin.sites.isActive`)'
+ @update:model-value ='(val) => { toggleSiteState(site, val) }'
+ )
+ q-separator.q-ml-md(vertical)
+ q-item-section(side, style='flex-direction: row; align-items: center;')
+ q-btn.acrylic-btn.q-mr-sm(
+ flat
+ @click='editSite(site)'
+ icon='las la-pen'
+ color='indigo'
+ :label='$t(`common.actions.edit`)'
+ no-caps
+ )
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash'
+ color='accent'
+ @click='deleteSite(site)'
+ )
+
+
+
diff --git a/ux/src/pages/AdminSsl.vue b/ux/src/pages/AdminSsl.vue
new file mode 100644
index 00000000..7da9443a
--- /dev/null
+++ b/ux/src/pages/AdminSsl.vue
@@ -0,0 +1,269 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-validation.svg', alt='SSL', style='width: 80px;')
+ .admin-header-title
+ .headline.primary--text.animated.fadeInLeft {{ $t('admin.ssl.title') }}
+ .subtitle-1.grey--text.animated.fadeInLeft {{ $t('admin.ssl.subtitle') }}
+ v-spacer
+ v-btn.animated.fadeInDown(
+ v-if='info.sslProvider === `letsencrypt` && info.httpsPort > 0'
+ color='black'
+ dark
+ depressed
+ @click='renewCertificate'
+ large
+ :loading='loadingRenew'
+ )
+ v-icon(left) mdi-cached
+ span {{$t('admin.ssl.renewCertificate')}}
+ v-form.pt-3
+ v-layout(row wrap)
+ v-flex(lg6 xs12)
+ v-card.animated.fadeInUp
+ v-subheader {{ $t('admin.ssl.currentState') }}
+ v-list(two-line, dense)
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-handshake
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.provider`) }}
+ v-list-item-subtitle {{ providerTitle }}
+ template(v-if='info.sslProvider === `letsencrypt` && info.httpsPort > 0')
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-application
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.domain`) }}
+ v-list-item-subtitle {{ info.sslDomain }}
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-at
+ v-list-item-content
+ v-list-item-title {{ $t('admin.ssl.subscriberEmail') }}
+ v-list-item-subtitle {{ info.sslSubscriberEmail }}
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-calendar-remove-outline
+ v-list-item-content
+ v-list-item-title {{ $t('admin.ssl.expiration') }}
+ v-list-item-subtitle {{ info.sslExpirationDate | moment('calendar') }}
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-traffic-light
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.status`) }}
+ v-list-item-subtitle {{ info.sslStatus }}
+
+ v-flex(lg6 xs12)
+ v-card.animated.fadeInUp.wait-p2s
+ v-subheader {{ $t('admin.ssl.ports') }}
+ v-list(two-line, dense)
+ v-list-item
+ v-list-item-avatar
+ v-icon.blue.white--text mdi-lock-open-variant
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.httpPort`) }}
+ v-list-item-subtitle {{ info.httpPort }}
+ template(v-if='info.httpsPort > 0')
+ v-divider
+ v-list-item
+ v-list-item-avatar
+ v-icon.green.white--text mdi-lock
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.httpsPort`) }}
+ v-list-item-subtitle {{ info.httpsPort }}
+ v-divider
+ v-list-item
+ v-list-item-avatar
+ v-icon.indigo.white--text mdi-sign-direction
+ v-list-item-content
+ v-list-item-title {{ $t(`admin.ssl.httpPortRedirect`) }}
+ v-list-item-subtitle {{ info.httpRedirection }}
+ v-list-item-action
+ v-btn.red--text(
+ v-if='info.httpRedirection'
+ depressed
+ :color='$vuetify.theme.dark ? `red darken-4` : `red lighten-5`'
+ :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
+ @click='toggleRedir'
+ :loading='loadingRedir'
+ )
+ v-icon(left) mdi-power
+ span {{$t('admin.ssl.httpPortRedirectTurnOff')}}
+ v-btn.green--text(
+ v-else
+ depressed
+ :color='$vuetify.theme.dark ? `green darken-4` : `green lighten-5`'
+ :class='$vuetify.theme.dark ? `text--lighten-5` : `text--darken-2`'
+ @click='toggleRedir'
+ :loading='loadingRedir'
+ )
+ v-icon(left) mdi-power
+ span {{$t('admin.ssl.httpPortRedirectTurnOn')}}
+
+ v-dialog(
+ v-model='loadingRenew'
+ persistent
+ max-width='450'
+ )
+ v-card(color='black', dark)
+ v-card-text.pa-10.text-center
+ semipolar-spinner.animated.fadeIn(
+ :animation-duration='1500'
+ :size='65'
+ color='#FFF'
+ style='margin: 0 auto;'
+ )
+ .mt-5.body-1.white--text {{$t('admin.ssl.renewCertificateLoadingTitle')}}
+ .caption.mt-4 {{$t('admin.ssl.renewCertificateLoadingSubtitle')}}
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminStats.vue b/ux/src/pages/AdminStats.vue
new file mode 100644
index 00000000..cbfd09a3
--- /dev/null
+++ b/ux/src/pages/AdminStats.vue
@@ -0,0 +1,32 @@
+
+ v-container(fluid, fill-height)
+ v-layout(row wrap)
+ v-flex(xs12)
+ .admin-header-icon: v-icon(size='80', color='grey lighten-2') show_chart
+ .headline.primary--text Statistics
+ .subtitle-1.grey--text Useful information about your wiki
+ .pa-3
+ fingerprint-spinner(
+ :animation-duration='1500'
+ :size='128'
+ color='#e91e63'
+ )
+ .caption.pink--text.mt-3 Compiling latest data...
+
+
+
+
+
diff --git a/ux/src/pages/AdminStorage.vue b/ux/src/pages/AdminStorage.vue
new file mode 100644
index 00000000..adcc1846
--- /dev/null
+++ b/ux/src/pages/AdminStorage.vue
@@ -0,0 +1,1191 @@
+
+q-page.admin-storage
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-ssd.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.storage.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.storage.subtitle') }}
+ .col-auto.flex
+ q-spinner-tail.q-mr-md(
+ v-show='loading > 0'
+ color='accent'
+ size='sm'
+ )
+ q-btn-toggle.q-mr-md(
+ v-model='displayMode'
+ push
+ no-caps
+ toggle-color='black'
+ :options=`[
+ { label: $t('admin.storage.targets'), value: 'targets' },
+ { label: $t('admin.storage.deliveryPaths'), value: 'delivery' }
+ ]`
+ )
+ q-separator.q-mr-md(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/storage'
+ target='_blank'
+ type='a'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='loading > 0'
+ )
+ q-separator(inset)
+
+ //- ==========================================
+ //- TARGETS
+ //- ==========================================
+
+ .row.q-pa-md.q-col-gutter-md(v-if='displayMode === `targets`')
+ .col-auto
+ q-card.rounded-borders.bg-dark
+ q-list(
+ style='min-width: 350px;'
+ padding
+ dark
+ )
+ q-item(
+ v-for='tgt of targets'
+ :key='tgt.key'
+ active-class='bg-primary text-white'
+ :active='selectedTarget === tgt.id'
+ :to='`/_admin/` + currentSiteId + `/storage/` + tgt.id'
+ clickable
+ )
+ q-item-section(side)
+ q-icon(
+ :name='`img:` + tgt.icon'
+ )
+ q-item-section
+ q-item-label {{tgt.title}}
+ q-item-label(caption, :class='getTargetSubtitleColor(tgt)') {{getTargetSubtitle(tgt)}}
+ q-item-section(side)
+ q-spinner-rings(:color='tgt.isEnabled ? `positive` : `negative`', size='sm')
+ .col(v-if='target')
+ //- -----------------------
+ //- Content Types
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.contentTypes')}}
+ .text-body2.text-grey {{ $t('admin.storage.contentTypesHint') }}
+ q-item(tag='label')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.contentTypes.activeTypes'
+ :color='target.module === `db` ? `grey` : `primary`'
+ val='pages'
+ :aria-label='$t(`admin.storage.contentTypePages`)'
+ :disable='target.module === `db`'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.contentTypePages`)}}
+ q-item-label(caption) {{$t(`admin.storage.contentTypePagesHint`)}}
+ q-item(tag='label')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.contentTypes.activeTypes'
+ color='primary'
+ val='images'
+ :aria-label='$t(`admin.storage.contentTypeImages`)'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.contentTypeImages`)}}
+ q-item-label(caption) {{$t(`admin.storage.contentTypeImagesHint`)}}
+ q-item(tag='label')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.contentTypes.activeTypes'
+ color='primary'
+ val='documents'
+ :aria-label='$t(`admin.storage.contentTypeDocuments`)'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.contentTypeDocuments`)}}
+ q-item-label(caption) {{$t(`admin.storage.contentTypeDocumentsHint`)}}
+ q-item(tag='label')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.contentTypes.activeTypes'
+ color='primary'
+ val='others'
+ :aria-label='$t(`admin.storage.contentTypeOthers`)'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.contentTypeOthers`)}}
+ q-item-label(caption) {{$t(`admin.storage.contentTypeOthersHint`)}}
+ q-item(tag='label')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.contentTypes.activeTypes'
+ color='primary'
+ val='large'
+ :aria-label='$t(`admin.storage.contentTypeLargeFiles`)'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.contentTypeLargeFiles`)}}
+ q-item-label(caption) {{$t(`admin.storage.contentTypeLargeFilesHint`)}}
+ q-item-label.text-deep-orange(v-if='target.module === `db`', caption) {{$t(`admin.storage.contentTypeLargeFilesDBWarn`)}}
+ q-item-section(side)
+ q-input(
+ outlined
+ :label='$t(`admin.storage.contentTypeLargeFilesThreshold`)'
+ v-model='target.contentTypes.largeThreshold'
+ style='min-width: 150px;'
+ dense
+ )
+
+ //- -----------------------
+ //- Content Delivery
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.assetDelivery')}}
+ .text-body2.text-grey {{ $t('admin.storage.assetDeliveryHint') }}
+ q-item(:tag='target.assetDelivery.isStreamingSupported ? `label` : null')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.assetDelivery.streaming'
+ :color='target.module === `db` || !target.assetDelivery.isStreamingSupported ? `grey` : `primary`'
+ :aria-label='$t(`admin.storage.contentTypePages`)'
+ :disable='target.module === `db` || !target.assetDelivery.isStreamingSupported'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.assetStreaming`)}}
+ q-item-label(caption) {{$t(`admin.storage.assetStreamingHint`)}}
+ q-item-label.text-deep-orange(v-if='!target.assetDelivery.isStreamingSupported', caption) {{$t(`admin.storage.assetStreamingNotSupported`)}}
+ q-item(:tag='target.assetDelivery.isDirectAccessSupported ? `label` : null')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='target.assetDelivery.directAccess'
+ :color='!target.assetDelivery.isDirectAccessSupported ? `grey` : `primary`'
+ :aria-label='$t(`admin.storage.contentTypePages`)'
+ :disable='!target.assetDelivery.isDirectAccessSupported'
+ )
+ q-item-section
+ q-item-label {{$t(`admin.storage.assetDirectAccess`)}}
+ q-item-label(caption) {{$t(`admin.storage.assetDirectAccessHint`)}}
+ q-item-label.text-deep-orange(v-if='!target.assetDelivery.isDirectAccessSupported', caption) {{$t(`admin.storage.assetDirectAccessNotSupported`)}}
+
+ //- -----------------------
+ //- Setup
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='target.setup && target.setup.handler && target.setup.state !== `configured`')
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.setup')}}
+ .text-body2.text-grey {{ $t('admin.storage.setupHint') }}
+ template(v-if='target.setup.handler === `github` && target.setup.state === `notconfigured`')
+ q-item
+ blueprint-icon(icon='test-account')
+ q-item-section
+ q-item-label GitHub Account Type
+ q-item-label(caption) Whether to use an organization or personal GitHub account during setup.
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='target.setup.values.accountType'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('admin.storage.githubAccTypeOrg'), value: 'org' },
+ { label: $t('admin.storage.githubAccTypePersonal'), value: 'personal' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ template(v-if='target.setup.values.accountType === `org`')
+ q-item
+ blueprint-icon(icon='github')
+ q-item-section
+ q-item-label {{ $t('admin.storage.githubOrg') }}
+ q-item-label(caption) {{ $t('admin.storage.githubOrgHint') }}
+ q-item-section
+ q-input(
+ outlined
+ v-model='target.setup.values.org'
+ dense
+ :aria-label='$t(`admin.storage.githubOrg`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='dns')
+ q-item-section
+ q-item-label {{ $t('admin.storage.githubPublicUrl') }}
+ q-item-label(caption) {{ $t('admin.storage.githubPublicUrlHint') }}
+ q-item-section
+ q-input(
+ outlined
+ v-model='target.setup.values.publicUrl'
+ dense
+ :aria-label='$t(`admin.storage.githubPublicUrl`)'
+ )
+ q-card-section.q-pt-sm.text-right
+ form(
+ ref='githubSetupForm'
+ method='POST'
+ :action='setupCfg.action'
+ )
+ input(
+ type='hidden'
+ name='manifest'
+ :value='setupCfg.manifest'
+ )
+ q-btn(
+ unelevated
+ icon='las la-angle-double-right'
+ :label='$t(`admin.storage.startSetup`)'
+ color='secondary'
+ @click='setupGitHub'
+ :loading='setupCfg.loading'
+ )
+ template(v-else-if='target.setup.handler === `github` && target.setup.state === `pendinginstall`')
+ q-card-section.q-py-none
+ q-banner(
+ rounded
+ :class='$q.dark.isActive ? `bg-teal-9 text-white` : `bg-teal-1 text-teal-9`'
+ ) {{$t('admin.storage.githubFinish')}}
+ q-card-section.q-pt-sm.text-right
+ q-btn.q-mr-sm(
+ unelevated
+ icon='las la-times-circle'
+ :label='$t(`admin.storage.cancelSetup`)'
+ color='negative'
+ @click='setupDestroy'
+ )
+ q-btn(
+ unelevated
+ icon='las la-angle-double-right'
+ :label='$t(`admin.storage.finishSetup`)'
+ color='secondary'
+ @click='setupGitHubStep(`verify`)'
+ :loading='setupCfg.loading'
+ )
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='target.setup && target.setup.handler && target.setup.state === `configured`')
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.setup')}}
+ .text-body2.text-grey {{ $t('admin.storage.setupConfiguredHint') }}
+ q-item
+ blueprint-icon.self-start(icon='matches', :hue-rotate='140')
+ q-item-section
+ q-item-label Uninstall
+ q-item-label(caption) Delete the active configuration and start over the setup process.
+ q-item-label.text-red(caption): strong This action cannot be undone!
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='negative'
+ @click='setupDestroy'
+ :label='$t(`admin.storage.uninstall`)'
+ )
+
+ //- -----------------------
+ //- Configuration
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.config')}}
+ q-banner.q-mt-md(
+ v-if='!target.config || Object.keys(target.config).length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{$t('admin.storage.noConfigOption')}}
+ template(
+ v-for='(cfg, cfgKey, idx) in target.config'
+ )
+ template(
+ v-if='configIfCheck(cfg.if)'
+ )
+ q-separator.q-my-sm(inset, v-if='idx > 0')
+ q-item(v-if='cfg.type === `Boolean`', tag='label')
+ blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
+ q-item-section
+ q-item-label {{cfg.title}}
+ q-item-label(caption) {{cfg.hint}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='cfg.value'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowComments`)'
+ :disable='cfg.readOnly'
+ )
+ q-item(v-else)
+ blueprint-icon(:icon='cfg.icon', :hue-rotate='cfg.readOnly ? -45 : 0')
+ q-item-section
+ q-item-label {{cfg.title}}
+ q-item-label(caption) {{cfg.hint}}
+ q-item-section(
+ :style='cfg.type === `Number` ? `flex: 0 0 150px;` : ``'
+ :class='{ "col-auto": cfg.enum && cfg.enumDisplay === `buttons` }'
+ )
+ q-btn-toggle(
+ v-if='cfg.enum && cfg.enumDisplay === `buttons`'
+ v-model='cfg.value'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`cfg.enum`
+ :disable='cfg.readOnly'
+ )
+ q-select(
+ v-else-if='cfg.enum'
+ outlined
+ v-model='cfg.value'
+ :options='cfg.enum'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :aria-label='cfg.title'
+ :disable='cfg.readOnly'
+ )
+ q-input(
+ v-else
+ outlined
+ v-model='cfg.value'
+ dense
+ :type='cfg.multiline ? `textarea` : `input`'
+ :aria-label='cfg.title'
+ :disable='cfg.readOnly'
+ )
+
+ //- -----------------------
+ //- Sync
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='target.sync && Object.keys(target.sync).length > 0')
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.sync')}}
+ q-banner.q-mt-md(
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{$t('admin.storage.noSyncModes')}}
+
+ //- -----------------------
+ //- Actions
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.storage.actions')}}
+ q-banner.q-mt-md(
+ v-if='!target.actions || target.actions.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{$t('admin.storage.noActions')}}
+ q-banner.q-mt-md(
+ v-else-if='!target.isEnabled'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{$t('admin.storage.actionsInactiveWarn')}}
+
+ template(
+ v-if='target.isEnabled'
+ v-for='(act, idx) in target.actions'
+ )
+ q-separator.q-my-sm(inset, v-if='idx > 0')
+ q-item
+ blueprint-icon.self-start(:icon='act.icon', :hue-rotate='45')
+ q-item-section
+ q-item-label {{act.label}}
+ q-item-label(caption) {{act.hint}}
+ q-item-label.text-red(v-if='act.warn', caption): strong {{act.warn}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click=''
+ :label='$t(`common.actions.proceed`)'
+ )
+
+ .col-auto(v-if='target')
+ //- -----------------------
+ //- Infobox
+ //- -----------------------
+ q-card.rounded-borders.q-pb-md(style='width: 350px;')
+ q-card-section
+ .text-subtitle1 {{target.title}}
+ q-img.q-mt-sm.rounded-borders(
+ :src='target.banner'
+ fit='cover'
+ no-spinner
+ )
+ .text-body2.q-mt-md {{target.description}}
+ q-separator.q-mb-sm(inset)
+ q-item
+ q-item-section
+ q-item-label.text-grey {{$t(`admin.storage.vendor`)}}
+ q-item-label {{target.vendor}}
+ q-separator.q-my-sm(inset)
+ q-item
+ q-item-section
+ q-item-label.text-grey {{$t(`admin.storage.vendorWebsite`)}}
+ q-item-label: a(:href='target.website', target='_blank', rel='noreferrer') {{target.website}}
+
+ //- -----------------------
+ //- Status
+ //- -----------------------
+ q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 350px;')
+ q-card-section
+ .text-subtitle1 Status
+ template(v-if='target.module !== `db` && !(target.setup && target.setup.handler && target.setup.state !== `configured`)')
+ q-item(tag='label')
+ q-item-section
+ q-item-label {{$t(`admin.storage.enabled`)}}
+ q-item-label(caption) {{$t(`admin.storage.enabledHint`)}}
+ q-item-label.text-deep-orange(v-if='target.module === `db`', caption) {{$t(`admin.storage.enabledForced`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='target.isEnabled'
+ :disable='target.module === `db` || (target.setup && target.setup.handler && target.setup.state !== `configured`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.general.allowSearch`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ q-item-section
+ q-item-label.text-grey {{$t(`admin.storage.currentState`)}}
+ q-item-label.text-positive No issues detected.
+
+ //- -----------------------
+ //- Versioning
+ //- -----------------------
+ q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 350px;')
+ q-card-section
+ .text-subtitle1 {{$t(`admin.storage.versioning`)}}
+ .text-body2.text-grey {{$t(`admin.storage.versioningHint`)}}
+ q-item(:tag='target.versioning.isSupported ? `label` : null')
+ q-item-section
+ q-item-label {{$t(`admin.storage.useVersioning`)}}
+ q-item-label(caption) {{$t(`admin.storage.useVersioningHint`)}}
+ q-item-label.text-deep-orange(v-if='!target.versioning.isSupported', caption) {{$t(`admin.storage.versioningNotSupported`)}}
+ q-item-label.text-deep-orange(v-if='target.versioning.isForceEnabled', caption) {{$t(`admin.storage.versioningForceEnabled`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='target.versioning.enabled'
+ :disable='!target.versioning.isSupported || target.versioning.isForceEnabled'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.storage.useVersioning`)'
+ )
+
+ //- ==========================================
+ //- DELIVERY PATHS
+ //- ==========================================
+
+ .row.q-pa-md.q-col-gutter-md(v-if='displayMode === `delivery`')
+ .col
+ q-card.rounded-borders
+ q-card-section.flex.items-center
+ .text-caption.q-mr-sm {{ $t('admin.storage.deliveryPathsLegend') }}
+ q-chip(square, dense, color='blue-1', text-color='blue-8')
+ q-avatar(icon='las la-ellipsis-h', color='blue', text-color='white')
+ span.text-caption.q-px-sm {{ $t('admin.storage.deliveryPathsUserRequest') }}
+ q-chip(square, dense, color='teal-1', text-color='teal-8')
+ q-avatar(icon='las la-ellipsis-h', color='positive', text-color='white')
+ span.text-caption.q-px-sm {{ $t('admin.storage.deliveryPathsPushToOrigin') }}
+ q-chip(square, dense, color='red-1', text-color='red-8')
+ q-avatar(icon='las la-minus', color='negative', text-color='white')
+ span.text-caption.q-px-sm {{ $t('admin.storage.missingOrigin') }}
+ q-separator
+ v-network-graph(
+ :zoom-level='2'
+ :configs='deliveryConfig'
+ :nodes='deliveryNodes'
+ :edges='deliveryEdges'
+ :paths='deliveryPaths'
+ :layouts='deliveryLayouts'
+ style='height: 600px;'
+ )
+ template(#override-node='{ nodeId, scale, config, ...slotProps }')
+ rect(
+ :rx='config.borderRadius * scale'
+ :x='-config.radius * scale'
+ :y='-config.radius * scale'
+ :width='config.radius * scale * 2'
+ :height='config.radius * scale * 2'
+ :fill='config.color'
+ v-bind='slotProps'
+ )
+ image(
+ v-if='deliveryNodes[nodeId].icon && deliveryNodes[nodeId].icon.endsWith(`.svg`)'
+ :x='(-config.radius + 5) * scale'
+ :y='(-config.radius + 5) * scale'
+ :width='(config.radius - 5) * scale * 2'
+ :height='(config.radius - 5) * scale * 2'
+ :xlink:href='deliveryNodes[nodeId].icon'
+ )
+ text(
+ v-if='deliveryNodes[nodeId].icon && deliveryNodes[nodeId].iconText'
+ :class='deliveryNodes[nodeId].icon'
+ :font-size='22 * scale'
+ fill='#ffffff'
+ text-anchor='middle'
+ dominant-baseline='central'
+ v-html='deliveryNodes[nodeId].iconText'
+ )
+
+ //- .overline.my-5 {{$t('admin.storage.syncDirection')}}
+ //- .body-2.ml-3 {{$t('admin.storage.syncDirectionSubtitle')}}
+ //- .pr-3.pt-3
+ //- v-radio-group.ml-3.py-0(v-model='target.mode')
+ //- v-radio(
+ //- :label='$t(`admin.storage.syncDirBi`)'
+ //- color='primary'
+ //- value='sync'
+ //- :disabled='target.supportedModes.indexOf(`sync`) < 0'
+ //- )
+ //- v-radio(
+ //- :label='$t(`admin.storage.syncDirPush`)'
+ //- color='primary'
+ //- value='push'
+ //- :disabled='target.supportedModes.indexOf(`push`) < 0'
+ //- )
+ //- v-radio(
+ //- :label='$t(`admin.storage.syncDirPull`)'
+ //- color='primary'
+ //- value='pull'
+ //- :disabled='target.supportedModes.indexOf(`pull`) < 0'
+ //- )
+ //- .body-2.ml-3
+ //- strong {{$t('admin.storage.syncDirBi')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`sync`) < 0') {{$t('admin.storage.unsupported')}}]
+ //- .pb-3 {{$t('admin.storage.syncDirBiHint')}}
+ //- strong {{$t('admin.storage.syncDirPush')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`push`) < 0') {{$t('admin.storage.unsupported')}}]
+ //- .pb-3 {{$t('admin.storage.syncDirPushHint')}}
+ //- strong {{$t('admin.storage.syncDirPull')}} #[em.red--text.text--lighten-2(v-if='target.supportedModes.indexOf(`pull`) < 0') {{$t('admin.storage.unsupported')}}]
+ //- .pb-3 {{$t('admin.storage.syncDirPullHint')}}
+
+ //- template(v-if='target.hasSchedule')
+ //- v-divider.mt-3
+ //- .overline.my-5 {{$t('admin.storage.syncSchedule')}}
+ //- .body-2.ml-3 {{$t('admin.storage.syncScheduleHint')}}
+ //- .pa-3
+ //- duration-picker(v-model='target.syncInterval')
+ //- i18next.caption.mt-3(path='admin.storage.syncScheduleCurrent', tag='div')
+ //- strong(place='schedule') {{getDefaultSchedule(target.syncInterval)}}
+ //- i18next.caption(path='admin.storage.syncScheduleDefault', tag='div')
+ //- strong(place='schedule') {{getDefaultSchedule(target.syncIntervalDefault)}}
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminSystem.vue b/ux/src/pages/AdminSystem.vue
new file mode 100644
index 00000000..a4fb7cc6
--- /dev/null
+++ b/ux/src/pages/AdminSystem.vue
@@ -0,0 +1,419 @@
+
+q-page.admin-system
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-processor.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.system.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.system.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/system'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn.acrylic-btn(
+ ref='copySysInfoBtn'
+ flat
+ icon='las la-clipboard'
+ label='Copy System Info'
+ color='primary'
+ @click=''
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-6
+ //- -----------------------
+ //- WIKI.JS
+ //- -----------------------
+ q-card.q-pb-sm.shadow-1
+ q-card-section
+ .text-subtitle1 Wiki.js
+ q-item
+ blueprint-icon(icon='breakable', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.currentVersion') }}
+ q-item-label(caption) {{$t('admin.system.currentVersionHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.currentVersion }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='cloud-checked', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.latestVersion') }}
+ q-item-label(caption) {{$t('admin.system.latestVersionHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.latestVersion }}
+
+ //- -----------------------
+ //- CLIENT
+ //- -----------------------
+ q-no-ssr
+ q-card.q-mt-md.q-pb-sm.shadow-1
+ q-card-section
+ .text-subtitle1 {{$t('admin.system.client')}}
+ q-item
+ blueprint-icon(icon='navigation-toolbar-top', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.browser')}}
+ q-item-label(caption) {{$t('admin.system.browserHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ clientBrowser }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='computer', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.clientPlatform')}}
+ q-item-label(caption) {{$t('admin.system.clientPlatformHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ clientPlatform }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='translation', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.clientLanguage')}}
+ q-item-label(caption) {{$t('admin.system.clientLanguageHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ clientLanguage }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='cookies', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.clientCookies')}}
+ q-item-label(caption) {{$t('admin.system.clientCookiesHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ clientCookies }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='widescreen', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.clientViewport')}}
+ q-item-label(caption) {{$t('admin.system.clientViewportHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ clientViewport }}
+
+ .col-6
+ //- -----------------------
+ //- ENGINES
+ //- -----------------------
+ q-card.q-pb-sm.shadow-1
+ q-card-section
+ .text-subtitle1 {{$t('admin.system.engines')}}
+ q-item
+ blueprint-icon(icon='nodejs', :hue-rotate='-45')
+ q-item-section
+ q-item-label Node.js
+ q-item-label(caption) {{$t('admin.system.nodejsHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.nodeVersion }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='postgresql', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.database')}}
+ q-item-label(caption) {{$t('admin.system.databaseHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) PostgreSQL {{dbVersion}}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='database', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{$t('admin.system.databaseHost')}}
+ q-item-label(caption) {{$t('admin.system.databaseHostHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.dbHost }}
+
+ //- -----------------------
+ //- HOST INFORMATION
+ //- -----------------------
+ q-card.q-mt-md.q-pb-sm.shadow-1
+ q-card-section
+ .text-subtitle1 {{ $t('admin.system.hostInfo') }}
+ q-item
+ blueprint-icon(:icon='platformLogo', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.os') }}
+ q-item-label(caption) {{$t('admin.system.osHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ (info.platform === 'docker') ? 'Docker Container (Linux)' : info.operatingSystem }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='server', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.hostname') }}
+ q-item-label(caption) {{$t('admin.system.hostnameHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.hostname }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='processor', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.cpuCores') }}
+ q-item-label(caption) {{$t('admin.system.cpuCoresHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.cpuCores }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='memory-slot', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.totalRAM') }}
+ q-item-label(caption) {{$t('admin.system.totalRAMHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.ramTotal }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='program', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.workingDirectory') }}
+ q-item-label(caption) {{$t('admin.system.workingDirectoryHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.workingDirectory }}
+ q-separator(inset)
+ q-item
+ blueprint-icon(icon='automation', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ $t('admin.system.configFile') }}
+ q-item-label(caption) {{$t('admin.system.configFileHint')}}
+ q-item-section
+ q-item-label.dark-value(caption) {{ info.configFile }}
+
+ //- v-list-item-action-text {{ $t('admin.system.published') }} {{ info.latestVersionReleaseDate | moment('from') }}
+ //- v-card-actions(v-if='info.upgradeCapable && !isLatestVersion && info.platform === `docker`', :class='$vuetify.theme.dark ? `grey darken-3-d5` : `indigo lighten-5`')
+ //- .caption.indigo--text.pl-3(:class='$vuetify.theme.dark ? `text--lighten-4` : ``') Wiki.js can perform the upgrade to the latest version for you.
+ //- v-spacer
+ //- v-btn.px-3(
+ //- color='indigo'
+ //- dark
+ //- @click='performUpgrade'
+ //- )
+ //- v-icon(left) mdi-upload
+ //- span Perform Upgrade
+
+ //- v-dialog(
+ //- v-model='isUpgrading'
+ //- persistent
+ //- width='450'
+ //- )
+ //- v-card.blue.darken-5(dark)
+ //- v-card-text.text-center.pa-10
+ //- self-building-square-spinner(
+ //- :animation-duration='4000'
+ //- :size='40'
+ //- color='#FFF'
+ //- style='margin: 0 auto;'
+ //- )
+ //- .body-2.mt-5.blue--text.text--lighten-4 Your Wiki.js container is being upgraded...
+ //- .caption.blue--text.text--lighten-2 Please wait
+ //- v-progress-linear.mt-5(
+ //- color='blue lighten-2'
+ //- :value='upgradeProgress'
+ //- :buffer-value='upgradeProgress'
+ //- rounded
+ //- :stream='isUpgradingStarted'
+ //- query
+ //- :indeterminate='!isUpgradingStarted'
+ //- )
+
+
+
+
+
diff --git a/ux/src/pages/AdminTags.vue b/ux/src/pages/AdminTags.vue
new file mode 100644
index 00000000..35742d89
--- /dev/null
+++ b/ux/src/pages/AdminTags.vue
@@ -0,0 +1,248 @@
+
+ v-container(fluid, grid-list-lg)
+ v-layout(row wrap)
+ v-flex(xs12)
+ .admin-header
+ img.animated.fadeInUp(src='/_assets/svg/icon-tags.svg', alt='Tags', style='width: 80px;')
+ .admin-header-title
+ .headline.primary--text.animated.fadeInLeft {{$t('tags.title')}}
+ .subtitle-1.grey--text.animated.fadeInLeft.wait-p4s {{$t('tags.subtitle')}}
+ v-spacer
+ v-btn.animated.fadeInDown(outlined, color='grey', @click='refresh', icon)
+ v-icon mdi-refresh
+ v-container.pa-0.mt-3(fluid, grid-list-lg)
+ v-layout(row)
+ v-flex(style='flex: 0 0 350px;')
+ v-card.animated.fadeInUp
+ v-toolbar(:color='$vuetify.theme.dark ? `grey darken-3-d5` : `grey lighten-4`', flat)
+ v-text-field(
+ v-model='filter'
+ :label='$t(`admin.tags.filter`)'
+ hide-details
+ single-line
+ solo
+ flat
+ dense
+ color='teal'
+ :background-color='$vuetify.theme.dark ? `grey darken-4` : `grey lighten-2`'
+ prepend-inner-icon='mdi-magnify'
+ )
+ v-divider
+ v-list.py-2(dense, nav)
+ v-list-item(v-if='tags.length < 1')
+ v-list-item-avatar(size='24'): v-icon(color='grey') mdi-compass-off
+ v-list-item-content
+ .caption.grey--text {{$t('tags.emptyList')}}
+ v-list-item(
+ v-for='tag of filteredTags'
+ :key='tag.id'
+ :class='(tag.id === current.id) ? "teal" : ""'
+ @click='selectTag(tag)'
+ )
+ v-list-item-avatar(size='24', tile): v-icon(size='18', :color='tag.id === current.id ? `white` : `teal`') mdi-tag
+ v-list-item-title(:class='tag.id === current.id ? `white--text` : ``') {{tag.tag}}
+ v-flex.animated.fadeInUp.wait-p2s
+ template(v-if='current.id')
+ v-card
+ v-toolbar(dense, color='teal', flat, dark)
+ .subtitle-1 {{$t('tags.edit')}}
+ v-spacer
+ v-btn.pl-4(
+ color='white'
+ dark
+ outlined
+ small
+ :href='`/t/` + current.tag'
+ )
+ span.text-none {{$t('admin.tags.viewLinkedPages')}}
+ v-icon(right) mdi-chevron-right
+ v-card-text
+ v-text-field(
+ outlined
+ :label='$t("tags.tag")'
+ prepend-icon='mdi-tag'
+ v-model='current.tag'
+ counter='255'
+ )
+ v-text-field(
+ outlined
+ :label='$t("tags.label")'
+ prepend-icon='mdi-format-title'
+ v-model='current.title'
+ hide-details
+ )
+ v-card-chin
+ i18next.caption.pl-3(path='admin.tags.date', tag='div')
+ strong(place='created') {{current.createdAt | moment('from')}}
+ strong(place='updated') {{current.updatedAt | moment('from')}}
+ v-spacer
+ v-dialog(v-model='deleteTagDialog', max-width='500')
+ template(v-slot:activator='{ on }')
+ v-btn(color='red', outlined, v-on='on')
+ v-icon(color='red') mdi-trash-can-outline
+ v-card
+ .dialog-header.is-red {{$t('admin.tags.deleteConfirm')}}
+ v-card-text.pa-4
+ i18next(tag='span', path='admin.tags.deleteConfirmText')
+ strong(place='tag') {{ current.tag }}
+ v-card-actions
+ v-spacer
+ v-btn(text, @click='deleteTagDialog = false') {{$t('common.actions.cancel')}}
+ v-btn(color='red', dark, @click='deleteTag(current)') {{$t('common.actions.delete')}}
+ v-btn.px-5.mr-2(color='success', depressed, dark, @click='saveTag(current)')
+ v-icon(left) mdi-content-save
+ span {{$t('common.actions.save')}}
+ v-card(v-else)
+ v-card-text.grey--text(v-if='tags.length > 0') {{$t('tags.noSelectionText')}}
+ v-card-text.grey--text(v-else) {{$t('tags.noItemsText')}}
+
+
+
+
+
diff --git a/ux/src/pages/AdminTheme.vue b/ux/src/pages/AdminTheme.vue
new file mode 100644
index 00000000..5108e202
--- /dev/null
+++ b/ux/src/pages/AdminTheme.vue
@@ -0,0 +1,406 @@
+
+q-page.admin-theme
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-paint-roller.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.theme.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.theme.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/theme'
+ target='_blank'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-6
+ //- -----------------------
+ //- Theme Options
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section.flex.items-center
+ .text-subtitle1 {{$t('admin.theme.options')}}
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-redo-alt'
+ :label='$t(`admin.theme.resetDefaults`)'
+ flat
+ size='sm'
+ color='pink'
+ @click='resetColors'
+ )
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='light-on')
+ q-item-section
+ q-item-label {{$t(`admin.theme.darkMode`)}}
+ q-item-label(caption) {{$t(`admin.theme.darkModeHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.dark'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.theme.darkMode`)'
+ )
+ template(v-for='(cl, idx) of colorKeys', :key='cl')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='fill-color')
+ q-item-section
+ q-item-label {{$t(`admin.theme.` + cl + `Color`)}}
+ q-item-label(caption) {{$t(`admin.theme.` + cl + `ColorHint`)}}
+ q-item-section(side)
+ .text-caption.text-grey-6 {{config[`color` + startCase(cl)]}}
+ q-item-section(side)
+ q-btn.q-mr-sm(
+ :key='`btnpick-` + cl'
+ glossy
+ padding='xs md'
+ no-caps
+ size='sm'
+ :style='`background-color: ` + config[`color` + startCase(cl)] + `;`'
+ text-color='white'
+ )
+ q-icon(name='las la-fill', size='xs', left)
+ span Pick...
+ q-menu
+ q-color(
+ v-model='config[`color` + startCase(cl)]'
+ )
+
+ //- -----------------------
+ //- Theme Layout
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{$t('admin.theme.layout')}}
+ q-item
+ blueprint-icon(icon='right-navigation-toolbar')
+ q-item-section
+ q-item-label {{$t(`admin.theme.sidebarPosition`)}}
+ q-item-label(caption) {{$t(`admin.theme.sidebarPositionHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='config.sidebarPosition'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='index')
+ q-item-section
+ q-item-label {{$t(`admin.theme.tocPosition`)}}
+ q-item-label(caption) {{$t(`admin.theme.tocPositionHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='config.tocPosition'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: 'Left', value: 'left' },
+ { label: 'Right', value: 'right' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='share')
+ q-item-section
+ q-item-label {{$t(`admin.theme.showSharingMenu`)}}
+ q-item-label(caption) {{$t(`admin.theme.showSharingMenuHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.showSharingMenu'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.theme.showSharingMenu`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='print')
+ q-item-section
+ q-item-label {{$t(`admin.theme.showPrintBtn`)}}
+ q-item-label(caption) {{$t(`admin.theme.showPrintBtnHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.showPrintBtn'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`admin.theme.showPrintBtn`)'
+ )
+
+ .col-6
+ //- -----------------------
+ //- Code Injection
+ //- -----------------------
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{$t('admin.theme.codeInjection')}}
+ q-item
+ blueprint-icon(icon='css')
+ q-item-section
+ q-item-label {{$t(`admin.theme.cssOverride`)}}
+ q-item-label(caption) {{$t(`admin.theme.cssOverrideHint`)}}
+ q-item
+ q-item-section
+ q-no-ssr(:placeholder='$t(`common.loading`)')
+ util-code-editor.admin-theme-cm(
+ ref='cmCSS'
+ v-model='config.injectCSS'
+ language='css'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='html')
+ q-item-section
+ q-item-label {{$t(`admin.theme.headHtmlInjection`)}}
+ q-item-label(caption) {{$t(`admin.theme.headHtmlInjectionHint`)}}
+ q-item
+ q-item-section
+ q-no-ssr(:placeholder='$t(`common.loading`)')
+ util-code-editor.admin-theme-cm(
+ ref='cmHead'
+ v-model='config.injectHead'
+ language='html'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='html')
+ q-item-section
+ q-item-label {{$t(`admin.theme.bodyHtmlInjection`)}}
+ q-item-label(caption) {{$t(`admin.theme.bodyHtmlInjectionHint`)}}
+ q-item
+ q-item-section
+ q-no-ssr(:placeholder='$t(`common.loading`)')
+ util-code-editor.admin-theme-cm(
+ ref='cmBody'
+ v-model='config.injectBody'
+ language='html'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminUsers.vue b/ux/src/pages/AdminUsers.vue
new file mode 100644
index 00000000..f2c7e569
--- /dev/null
+++ b/ux/src/pages/AdminUsers.vue
@@ -0,0 +1,247 @@
+
+q-page.admin-groups
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-account.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.users.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.users.subtitle') }}
+ .col-auto.flex.items-center
+ q-input.denser.q-mr-sm(
+ outlined
+ v-model='search'
+ dense
+ :class='$q.dark.isActive ? `bg-dark` : `bg-white`'
+ )
+ template(#prepend)
+ q-icon(name='las la-search')
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ type='a'
+ href='https://docs.js.wiki/admin/users'
+ target='_blank'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ @click='load'
+ :loading='loading > 0'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.users.create`)'
+ color='primary'
+ @click='createUser'
+ :disabled='loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card.shadow-1
+ q-table(
+ :rows='users'
+ :columns='headers'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='loading > 0'
+ )
+ template(v-slot: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')
+ q-td(:props='props')
+ .flex.items-center
+ strong {{props.value}}
+ q-icon.q-ml-sm(
+ v-if='props.row.isSystem'
+ name='las la-lock'
+ color='pink'
+ )
+ q-icon.q-ml-sm(
+ v-if='!props.row.isActive'
+ name='las la-ban'
+ color='pink'
+ )
+ template(v-slot:body-cell-email='props')
+ q-td(:props='props')
+ em {{ props.value }}
+ template(v-slot:body-cell-date='props')
+ q-td(:props='props')
+ i18n-t.text-caption(keypath='admin.users.createdAt', tag='div')
+ template(#date)
+ strong {{ humanizeDate(props.value) }}
+ i18n-t.text-caption(
+ v-if='props.row.lastLoginAt'
+ keypath='admin.users.lastLoginAt'
+ tag='div'
+ )
+ template(#date)
+ strong {{ humanizeDate(props.row.lastLoginAt) }}
+ template(v-slot:body-cell-edit='props')
+ q-td(:props='props')
+ q-btn.acrylic-btn.q-mr-sm(
+ v-if='!props.row.isSystem'
+ flat
+ :to='`/_admin/users/` + props.row.id'
+ icon='las la-pen'
+ color='indigo'
+ :label='$t(`common.actions.edit`)'
+ no-caps
+ )
+ q-btn.acrylic-btn(
+ v-if='!props.row.isSystem'
+ flat
+ icon='las la-trash'
+ color='accent'
+ @click='deleteGroup(props.row)'
+ )
+
+
+
+
+
diff --git a/ux/src/pages/AdminUtilities.vue b/ux/src/pages/AdminUtilities.vue
new file mode 100644
index 00000000..cfb06670
--- /dev/null
+++ b/ux/src/pages/AdminUtilities.vue
@@ -0,0 +1,83 @@
+
+q-page.admin-utilities
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-swiss-army-knife.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.utilities.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.utilities.subtitle') }}
+ .col-auto
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.requarks.io/admin/utilities'
+ target='_blank'
+ type='a'
+ )
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card.shadow-1
+ q-list(separator)
+ q-item
+ blueprint-icon(icon='matches', :hue-rotate='45')
+ q-item-section
+ q-item-label {{$t(`admin.utilities.invalidAuthCertificates`)}}
+ q-item-label(caption) {{$t(`admin.utilities.invalidAuthCertificatesHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click=''
+ :label='$t(`common.actions.proceed`)'
+ )
+ q-item
+ blueprint-icon(icon='historical', :hue-rotate='45')
+ q-item-section
+ q-item-label {{$t(`admin.utilities.purgeHistory`)}}
+ q-item-label(caption) {{$t(`admin.utilities.purgeHistoryHint`)}}
+ q-item-section(side)
+ q-select(
+ outlined
+ :label='$t(`admin.utilities.purgeHistoryTimeframe`)'
+ v-model='purgeHistoryTimeframe'
+ style='min-width: 175px;'
+ emit-value
+ map-options
+ dense
+ :options=`[
+ { value: '24h', label: $t('admin.utitilies.purgeHistoryToday') },
+ { value: '1m', label: $tc('admin.utitilies.purgeHistoryMonth', 1, { count: 1 }) },
+ { value: '3m', label: $tc('admin.utitilies.purgeHistoryMonth', 3, { count: 3 }) },
+ { value: '6m', label: $tc('admin.utitilies.purgeHistoryMonth', 6, { count: 6 }) },
+ { value: '1y', label: $tc('admin.utitilies.purgeHistoryYear', 1, { count: 1 }) },
+ { value: '2y', label: $tc('admin.utitilies.purgeHistoryYear', 2, { count: 2 }) }
+ ]`
+ )
+ q-separator.q-ml-sm(vertical)
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click=''
+ :label='$t(`common.actions.proceed`)'
+ )
+
+
+
+
+
+
diff --git a/ux/src/pages/AdminWebhooks.vue b/ux/src/pages/AdminWebhooks.vue
new file mode 100644
index 00000000..1526fc07
--- /dev/null
+++ b/ux/src/pages/AdminWebhooks.vue
@@ -0,0 +1,185 @@
+
+q-page.admin-webhooks
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-lightning-bolt.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ $t('admin.webhooks.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ $t('admin.webhooks.subtitle') }}
+ .col-auto
+ q-spinner-tail.q-mr-md(
+ v-show='loading'
+ color='accent'
+ size='sm'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ href='https://docs.js.wiki/admin/webhooks'
+ target='_blank'
+ type='a'
+ )
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='loading > 0'
+ @click='load'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='$t(`admin.webhooks.new`)'
+ color='primary'
+ @click='createHook'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12(v-if='hooks.length < 1')
+ q-card.rounded-borders(
+ flat
+ :class='$q.dark.isActive ? `bg-dark-5 text-white` : `bg-grey-3 text-dark`'
+ )
+ q-card-section.items-center(horizontal)
+ q-card-section.col-auto.q-pr-none
+ q-icon(name='las la-info-circle', size='sm')
+ q-card-section.text-caption {{ $t('admin.webhooks.none') }}
+ .col-12(v-else)
+ q-card
+ q-list(separator)
+ q-item(v-for='hook of hooks', :key='hook.id')
+ q-item-section(side)
+ q-icon(name='las la-bolt', color='primary')
+ q-item-section
+ q-item-label {{hook.name}}
+ q-item-label(caption) {{hook.url}}
+ q-item-section(side, style='flex-direction: row; align-items: center;')
+ template(v-if='hook.state === `pending`')
+ q-spinner-clock.q-mr-sm(
+ color='indigo'
+ size='xs'
+ )
+ .text-caption.text-indigo {{$t('admin.webhooks.statePending')}}
+ q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.statePendingHint')}}
+ template(v-else-if='hook.state === `success`')
+ q-spinner-infinity.q-mr-sm(
+ color='positive'
+ size='xs'
+ )
+ .text-caption.text-positive {{$t('admin.webhooks.stateSuccess')}}
+ q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.stateSuccessHint')}}
+ template(v-else-if='hook.state === `error`')
+ q-icon.q-mr-sm(
+ color='negative'
+ size='xs'
+ name='las la-exclamation-triangle'
+ )
+ .text-caption.text-negative {{$t('admin.webhooks.stateError')}}
+ q-tooltip(anchor='center left', self='center right') {{$t('admin.webhooks.stateErrorHint')}}
+ q-separator.q-ml-md(vertical)
+ q-item-section(side, style='flex-direction: row; align-items: center;')
+ q-btn.acrylic-btn.q-mr-sm(
+ color='indigo'
+ icon='las la-pen'
+ label='Edit'
+ flat
+ no-caps
+ @click='editHook(hook.id)'
+ )
+ q-btn.acrylic-btn(
+ color='red'
+ icon='las la-trash'
+ flat
+ @click='deleteHook(hook)'
+ )
+
+
+
+
+
+
diff --git a/ux/src/pages/Error404.vue b/ux/src/pages/Error404.vue
new file mode 100644
index 00000000..ab4b5b86
--- /dev/null
+++ b/ux/src/pages/Error404.vue
@@ -0,0 +1,29 @@
+
+
+
+
+ 404
+
+
+
+ Oops. Nothing here...
+
+
+
+
+
+
+
+
diff --git a/ux/src/pages/ErrorNotFound.vue b/ux/src/pages/ErrorNotFound.vue
new file mode 100644
index 00000000..c1c178b3
--- /dev/null
+++ b/ux/src/pages/ErrorNotFound.vue
@@ -0,0 +1,31 @@
+
+
+
+
+ 404
+
+
+
+ Oops. Nothing here...
+
+
+
+
+
+
+
+
diff --git a/ux/src/pages/Index.vue b/ux/src/pages/Index.vue
new file mode 100644
index 00000000..a7f98522
--- /dev/null
+++ b/ux/src/pages/Index.vue
@@ -0,0 +1,723 @@
+
+q-page.column
+ .page-breadcrumbs.q-py-sm.q-px-md.row
+ .col
+ q-breadcrumbs(
+ active-color='grey-7'
+ separator-color='grey'
+ )
+ template(v-slot:separator)
+ q-icon(
+ name='las la-angle-right'
+ )
+ q-breadcrumbs-el(icon='las la-home', to='/', aria-label='Home')
+ q-tooltip Home
+ q-breadcrumbs-el(
+ v-for='brd of breadcrumbs'
+ :key='brd.id'
+ :icon='brd.icon'
+ :label='brd.title'
+ :aria-label='brd.title'
+ :to='$pageHelpers.getFullPath(brd)'
+ )
+ q-breadcrumbs-el(
+ v-if='editCreateMode'
+ :icon='pageIcon'
+ :label='title || `Untitled Page`'
+ :aria-label='title || `Untitled Page`'
+ )
+ .col-auto.flex.items-center.justify-end
+ template(v-if='!isPublished')
+ .text-caption.text-accent: strong Unpublished
+ q-separator.q-mx-sm(vertical)
+ .text-caption.text-grey-6(v-if='editCreateMode') New Page
+ .text-caption.text-grey-6(v-if='!editCreateMode') Last modified on #[strong September 5th, 2020]
+ .page-header.row
+ //- PAGE ICON
+ .col-auto.q-pl-md.flex.items-center(v-if='editMode')
+ q-btn.rounded-borders(
+ padding='none'
+ size='37px'
+ :icon='pageIcon'
+ color='primary'
+ flat
+ )
+ q-menu(content-class='shadow-7')
+ icon-picker-dialog(v-model='pageIcon')
+ .col-auto.q-pl-md.flex.items-center(v-else)
+ q-icon.rounded-borders(
+ :name='pageIcon'
+ size='64px'
+ color='primary'
+ )
+ //- PAGE HEADER
+ .col.q-pa-md(v-if='editMode')
+ q-input.no-height(
+ borderless
+ v-model='title'
+ input-class='font-poppins text-h4 text-grey-9'
+ input-style='padding: 0;'
+ placeholder='Untitled Page'
+ hide-hint
+ )
+ q-input.no-height(
+ borderless
+ v-model='description'
+ input-class='font-poppins text-subtitle2 text-grey-7'
+ input-style='padding: 0;'
+ placeholder='Enter a short description'
+ hide-hint
+ )
+ .col.q-pa-md.font-poppins(v-else)
+ .text-h4.page-header-title {{title}}
+ .text-subtitle2.page-header-subtitle {{description}}
+
+ //- PAGE ACTIONS
+ .col-auto.q-pa-md.flex.items-center.justify-end(v-if='editMode')
+ q-btn.q-mr-sm.acrylic-btn(
+ flat
+ icon='las la-times'
+ color='grey-7'
+ label='Discard'
+ aria-label='Discard'
+ no-caps
+ @click='mode = `view`'
+ )
+ q-btn(
+ v-if='editorMode === `edit`'
+ unelevated
+ icon='las la-check'
+ color='secondary'
+ label='Save'
+ aria-label='Save'
+ no-caps
+ @click='mode = `view`'
+ )
+ q-btn(
+ v-else
+ unelevated
+ icon='las la-check'
+ color='secondary'
+ label='Create'
+ aria-label='Create'
+ no-caps
+ @click='mode = `view`'
+ )
+ .col-auto.q-pa-md.flex.items-center.justify-end(v-else)
+ q-btn.q-mr-md(
+ flat
+ dense
+ icon='las la-bell'
+ color='grey'
+ aria-label='Watch Page'
+ )
+ q-tooltip Watch Page
+ q-btn.q-mr-md(
+ flat
+ dense
+ icon='las la-bookmark'
+ color='grey'
+ aria-label='Bookmark Page'
+ )
+ q-tooltip Bookmark Page
+ q-btn.q-mr-md(
+ flat
+ dense
+ icon='las la-share-alt'
+ color='grey'
+ aria-label='Share'
+ )
+ q-tooltip Share
+ social-sharing-menu
+ q-btn.q-mr-md(
+ flat
+ dense
+ icon='las la-print'
+ color='grey'
+ aria-label='Print'
+ )
+ q-tooltip Print
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-edit'
+ color='deep-orange-9'
+ label='Edit'
+ aria-label='Edit'
+ no-caps
+ @click='mode = `edit`'
+ )
+ .page-container.row.no-wrap.items-stretch(style='flex: 1 1 100%;')
+ .col(style='order: 1;')
+ q-no-ssr(v-if='editMode')
+ component(:is='editorComponent')
+ //- editor-wysiwyg
+ //- editor-markdown
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ v-else
+ )
+ .q-pa-md
+ div(v-html='render')
+ template(v-if='relations && relations.length > 0')
+ q-separator.q-my-lg
+ .row.align-center
+ .col.text-left(v-if='relationsLeft.length > 0')
+ q-btn.q-mr-sm.q-mb-sm(
+ padding='sm md'
+ outline
+ :icon='rel.icon'
+ no-caps
+ color='primary'
+ v-for='rel of relationsLeft'
+ :key='`rel-id-` + rel.id'
+ )
+ .column.text-left.q-pl-md
+ .text-body2: strong {{rel.label}}
+ .text-caption {{rel.caption}}
+ .col.text-center(v-if='relationsCenter.length > 0')
+ .column
+ q-btn(
+ :label='rel.label'
+ color='primary'
+ flat
+ no-caps
+ :icon='rel.icon'
+ v-for='rel of relationsCenter'
+ :key='`rel-id-` + rel.id'
+ )
+ .col.text-right(v-if='relationsRight.length > 0')
+ q-btn.q-ml-sm.q-mb-sm(
+ padding='sm md'
+ outline
+ :icon-right='rel.icon'
+ no-caps
+ color='primary'
+ v-for='rel of relationsRight'
+ :key='`rel-id-` + rel.id'
+ )
+ .column.text-left.q-pr-md
+ .text-body2: strong {{rel.label}}
+ .text-caption {{rel.caption}}
+ .page-sidebar(
+ v-if='showSidebar'
+ style='order: 2;'
+ )
+ template(v-if='showToc')
+ //- TOC
+ .q-pa-md.flex.items-center
+ q-icon.q-mr-sm(name='las la-stream', color='grey')
+ .text-caption.text-grey-7 Contents
+ .q-px-md.q-pb-sm
+ q-tree(
+ :nodes='toc'
+ node-key='key'
+ v-model:expanded='tocExpanded'
+ v-model:selected='tocSelected'
+ )
+ //- Tags
+ template(v-if='showTags')
+ q-separator(v-if='showToc')
+ .q-pa-md(
+ @mouseover='showTagsEditBtn = true'
+ @mouseleave='showTagsEditBtn = false'
+ )
+ .flex.items-center
+ q-icon.q-mr-sm(name='las la-tags', color='grey')
+ .text-caption.text-grey-7 Tags
+ q-space
+ transition(name='fade')
+ q-btn(
+ v-show='showTagsEditBtn'
+ size='sm'
+ padding='none xs'
+ icon='las la-pen'
+ color='deep-orange-9'
+ flat
+ label='Edit'
+ no-caps
+ @click='tagEditMode = !tagEditMode'
+ )
+ page-tags.q-mt-sm(:edit='tagEditMode')
+ template(v-if='allowRatings && ratingsMode !== `off`')
+ q-separator(v-if='showToc || showTags')
+ //- Rating
+ .q-pa-md.flex.items-center
+ q-icon.q-mr-sm(name='las la-star-half-alt', color='grey')
+ .text-caption.text-grey-7 Rate this page
+ .q-px-md
+ q-rating(
+ v-if='ratingsMode === `stars`'
+ v-model='currentRating'
+ icon='las la-star'
+ color='secondary'
+ size='sm'
+ )
+ .flex.items-center(v-else-if='ratingsMode === `thumbs`')
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-thumbs-down'
+ color='secondary'
+ )
+ q-btn.acrylic-btn.q-ml-sm(
+ flat
+ icon='las la-thumbs-up'
+ color='secondary'
+ )
+ .page-actions.column.items-stretch.order-last
+ q-btn.q-py-md(
+ flat
+ icon='las la-pen-nib'
+ color='deep-orange-9'
+ aria-label='Page Properties'
+ @click='togglePageProperties'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Properties
+ q-btn.q-py-md(
+ flat
+ icon='las la-project-diagram'
+ color='deep-orange-9'
+ aria-label='Page Data'
+ @click='togglePageData'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Data
+ q-separator.q-my-sm(inset)
+ q-btn.q-py-sm(
+ flat
+ icon='las la-history'
+ color='grey'
+ aria-label='Page History'
+ )
+ q-tooltip(anchor='center left' self='center right') Page History
+ q-btn.q-py-sm(
+ flat
+ icon='las la-code'
+ color='grey'
+ aria-label='Page Source'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Source
+ q-btn.q-py-sm(
+ flat
+ icon='las la-ellipsis-h'
+ color='grey'
+ aria-label='Page Actions'
+ )
+ q-menu(
+ anchor='top left'
+ self='top right'
+ auto-close
+ transition-show='jump-left'
+ )
+ q-list(padding, style='min-width: 225px;')
+ q-item(clickable)
+ q-item-section.items-center(avatar)
+ q-icon(color='deep-orange-9', name='las la-atom', size='sm')
+ q-item-section
+ q-item-label Convert Page
+ q-item(clickable)
+ q-item-section.items-center(avatar)
+ q-icon(color='deep-orange-9', name='las la-magic', size='sm')
+ q-item-section
+ q-item-label Re-render Page
+ q-item(clickable)
+ q-item-section.items-center(avatar)
+ q-icon(color='deep-orange-9', name='las la-sun', size='sm')
+ q-item-section
+ q-item-label View Backlinks
+ q-space
+ q-btn.q-py-sm(
+ flat
+ icon='las la-copy'
+ color='grey'
+ aria-label='Duplicate Page'
+ )
+ q-tooltip(anchor='center left' self='center right') Duplicate Page
+ q-btn.q-py-sm(
+ flat
+ icon='las la-share'
+ color='grey'
+ aria-label='Rename / Move Page'
+ )
+ q-tooltip(anchor='center left' self='center right') Rename / Move Page
+ q-btn.q-py-sm(
+ flat
+ icon='las la-trash'
+ color='grey'
+ aria-label='Delete Page'
+ @click='savePage'
+ )
+ q-tooltip(anchor='center left' self='center right') Delete Page
+
+ q-dialog(
+ v-model='showSideDialog'
+ position='right'
+ full-height
+ transition-show='jump-left'
+ transition-hide='jump-right'
+ class='floating-sidepanel'
+ )
+ component(:is='sideDialogComponent')
+
+ q-dialog(
+ v-model='showGlobalDialog'
+ transition-show='jump-up'
+ transition-hide='jump-down'
+ )
+ component(:is='globalDialogComponent')
+
+
+
+
+
diff --git a/ux/src/pages/Login.vue b/ux/src/pages/Login.vue
new file mode 100644
index 00000000..4675f97b
--- /dev/null
+++ b/ux/src/pages/Login.vue
@@ -0,0 +1,604 @@
+
+.auth(:style='`background-image: url(` + bgUrl + `);`')
+ .auth-box
+ .flex.mb-5
+ .auth-login-logo
+ q-avatar(square, size='34px')
+ q-img(:src='logoUrl')
+ .auth-login-title
+ .text-h6.text-grey-9 {{ siteTitle }}
+
+ q-banner.bg-red-7.text-white.q-mt-md(
+ v-if='errorShown'
+ transition='slide-y-reverse-transition'
+ dense
+ )
+ template(v-slot:avatar)
+ q-icon.q-pl-sm(name='las la-exclamation-triangle', size='sm')
+ span {{errorMessage}}
+ //-------------------------------------------------
+ //- PROVIDERS LIST
+ //-------------------------------------------------
+ template(v-if='screen === `login` && strategies.length > 1')
+ .auth-login-subtitle
+ .text-subtitle1 {{$t('auth.selectAuthProvider')}}
+ .auth-login-list
+ q-list.bg-white.shadow-2.rounded-borders.q-pa-sm(separator)
+ q-item.rounded-borders(
+ clickable
+ v-ripple
+ v-for='(stg, idx) of filteredStrategies'
+ :key='stg.key'
+ @click='selectedStrategyKey = stg.key'
+ :class='stg.key === selectedStrategyKey ? `bg-primary text-white` : ``'
+ )
+ q-item-section(avatar)
+ q-avatar.mr-3(:color='stg.strategy.color', rounded, size='32px')
+ div(v-html='stg.strategy.icon')
+ q-item-section
+ span.text-none {{stg.displayName}}
+ //-------------------------------------------------
+ //- LOGIN FORM
+ //-------------------------------------------------
+ template(v-if='screen === `login` && selectedStrategy.strategy.useForm')
+ .auth-login-subtitle
+ .text-subtitle1 {{$t('auth.enterCredentials')}}
+ .auth-login-form
+ q-input.text-black(
+ outlined
+ bg-color='white'
+ ref='iptEmail'
+ v-model='username'
+ :label='isUsernameEmail ? $t(`auth.fields.email`) : $t(`auth.fields.username`)'
+ :type='isUsernameEmail ? `email` : `text`'
+ :autocomplete='isUsernameEmail ? `email` : `username`'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-user-circle', color='primary')
+ q-input.q-mt-sm(
+ outlined
+ bg-color='white'
+ ref='iptPassword'
+ v-model='password'
+ :type='hidePassword ? "password" : "text"'
+ :label='$t("auth:fields.password")'
+ autocomplete='current-password'
+ @keyup.enter='login'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-key', color='primary')
+ template(v-slot:append)
+ q-icon.cursor-pointer(
+ :name='hidePassword ? "las la-eye-slash" : "las la-eye"'
+ @click='() => (hidePassword = !hidePassword)'
+ )
+ q-btn.q-mt-sm.q-py-xs.full-width(
+ no-caps
+ color='blue-7'
+ push
+ @click='login'
+ :loading='isLoading'
+ :label='$t(`auth.actions.login`)'
+ icon='las la-arrow-right'
+ )
+ .text-center.q-mt-lg
+ q-btn(
+ flat
+ no-caps
+ rounded
+ color='grey-8'
+ @click.stop.prevent='forgotPassword'
+ href='#forgot'
+ ): .text-caption {{ $t('auth.forgotPasswordLink') }}
+ q-btn(
+ v-if='selectedStrategyKey === `local` && selectedStrategy.selfRegistration'
+ color='indigo darken-2'
+ flat
+ no-caps
+ rounded
+ href='/register'
+ ): .text-caption {{ $t('auth.switchToRegister.link') }}
+
+
+
+
+
diff --git a/ux/src/pages/Profile.vue b/ux/src/pages/Profile.vue
new file mode 100644
index 00000000..d12a3aba
--- /dev/null
+++ b/ux/src/pages/Profile.vue
@@ -0,0 +1,190 @@
+
+q-page.q-py-md(:style-fn='pageStyle')
+ .text-header {{$t('profile.myInfo')}}
+ q-item
+ blueprint-icon(icon='contact')
+ q-item-section
+ q-item-label {{$t(`profile.displayName`)}}
+ q-item-label(caption) {{$t(`profile.displayNameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.name'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`profile.displayName`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='envelope')
+ q-item-section
+ q-item-label {{$t(`profile.email`)}}
+ q-item-label(caption) {{$t(`profile.emailHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.email'
+ dense
+ :aria-label='$t(`profile.email`)'
+ readonly
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='address')
+ q-item-section
+ q-item-label {{$t(`profile.location`)}}
+ q-item-label(caption) {{$t(`profile.locationHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.location'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`profile.location`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='new-job')
+ q-item-section
+ q-item-label {{$t(`profile.jobTitle`)}}
+ q-item-label(caption) {{$t(`profile.jobTitleHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.jobTitle'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`profile.jobTitle`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='gender')
+ q-item-section
+ q-item-label {{$t(`profile.pronouns`)}}
+ q-item-label(caption) {{$t(`profile.pronounsHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='config.pronouns'
+ dense
+ hide-bottom-space
+ :aria-label='$t(`profile.pronouns`)'
+ )
+ .text-header.q-mt-lg {{$t('profile.preferences')}}
+ q-item
+ blueprint-icon(icon='timezone')
+ q-item-section
+ q-item-label {{$t(`profile.timezone`)}}
+ q-item-label(caption) {{$t(`profile.timezoneHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.timezone'
+ :options='timezones'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :aria-label='$t(`admin.general.defaultTimezone`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='calendar')
+ q-item-section
+ q-item-label {{$t(`profile.dateFormat`)}}
+ q-item-label(caption) {{$t(`profile.dateFormatHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='config.dateFormat'
+ emit-value
+ map-options
+ dense
+ :aria-label='$t(`admin.general.defaultDateFormat`)'
+ :options=`[
+ { label: $t('profile.localeDefault'), value: '' },
+ { label: 'DD/MM/YYYY', value: 'DD/MM/YYYY' },
+ { label: 'DD.MM.YYYY', value: 'DD.MM.YYYY' },
+ { label: 'MM/DD/YYYY', value: 'MM/DD/YYYY' },
+ { label: 'YYYY-MM-DD', value: 'YYYY-MM-DD' },
+ { label: 'YYYY/MM/DD', value: 'YYYY/MM/DD' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='clock')
+ q-item-section
+ q-item-label {{$t(`profile.timeFormat`)}}
+ q-item-label(caption) {{$t(`profile.timeFormatHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='config.timeFormat'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: $t('admin.general.defaultTimeFormat12h'), value: '12h' },
+ { label: $t('admin.general.defaultTimeFormat24h'), value: '24h' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label', v-ripple)
+ blueprint-icon(icon='light-on')
+ q-item-section
+ q-item-label {{$t(`profile.darkMode`)}}
+ q-item-label(caption) {{$t(`profile.darkModeHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='config.darkMode'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='$t(`profile.darkMode`)'
+ )
+ .actions-bar.q-mt-lg
+ q-btn(
+ icon='mdi-check'
+ unelevated
+ label='Save Changes'
+ color='secondary'
+ )
+
+
+
diff --git a/ux/src/pages/UnknownSite.vue b/ux/src/pages/UnknownSite.vue
new file mode 100644
index 00000000..bb301b94
--- /dev/null
+++ b/ux/src/pages/UnknownSite.vue
@@ -0,0 +1,21 @@
+
+.fullscreen.bg-blue.text-white.text-center.q-pa-md.flex.flex-center
+ div
+ .text-h1 Unknown Site
+ .text-h2(style="opacity:.4") Oops. Nothing here...
+ q-btn(
+ class="q-mt-xl"
+ color="white"
+ text-color="blue"
+ unelevated
+ to="/"
+ label="Go Home"
+ no-caps
+ )
+
+
+
diff --git a/ux/src/router/index.js b/ux/src/router/index.js
new file mode 100644
index 00000000..ca3cd61c
--- /dev/null
+++ b/ux/src/router/index.js
@@ -0,0 +1,30 @@
+import { route } from 'quasar/wrappers'
+import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } 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
+ ? createMemoryHistory
+ : (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
+
+ 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)
+ })
+
+ return Router
+})
diff --git a/ux/src/router/routes.js b/ux/src/router/routes.js
new file mode 100644
index 00000000..19f57788
--- /dev/null
+++ b/ux/src/router/routes.js
@@ -0,0 +1,70 @@
+
+const routes = [
+ // {
+ // path: '/',
+ // component: () => import('../layouts/MainLayout.vue'),
+ // children: [
+ // { path: '', component: () => import('../pages/Index.vue') },
+ // { path: 'n/:editor?', component: () => import('../pages/Index.vue') }
+ // ]
+ // },
+ // {
+ // path: '/login',
+ // component: () => import('../layouts/AuthLayout.vue'),
+ // children: [
+ // { path: '', component: () => import('../pages/Login.vue') }
+ // ]
+ // },
+ // {
+ // path: '/p',
+ // component: () => import('../layouts/ProfileLayout.vue'),
+ // children: [
+ // { path: '', redirect: '/p/profile' },
+ // { path: 'profile', component: () => import('../pages/Profile.vue') }
+ // ]
+ // },
+ {
+ path: '/_admin',
+ component: () => import('../layouts/AdminLayout.vue'),
+ children: [
+ { path: '', redirect: '/_admin/dashboard' },
+ { 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/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/rendering', component: () => import('../pages/AdminRendering.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') },
+ // // -> System
+ // { path: 'api', component: () => import('../pages/AdminApi.vue') },
+ // { path: 'extensions', component: () => import('../pages/AdminExtensions.vue') },
+ // { path: 'mail', component: () => import('../pages/AdminMail.vue') },
+ // { path: 'security', component: () => import('../pages/AdminSecurity.vue') },
+ // { path: 'system', component: () => import('../pages/AdminSystem.vue') },
+ // { path: 'utilities', component: () => import('../pages/AdminUtilities.vue') },
+ // { path: 'webhooks', component: () => import('../pages/AdminWebhooks.vue') },
+ // { path: 'flags', component: () => import('../pages/AdminFlags.vue') }
+ ]
+ },
+ // {
+ // path: '/_unknown-site',
+ // component: () => import('../pages/UnknownSite.vue')
+ // },
+
+ // Always leave this as last one,
+ // but you can also remove it
+ {
+ path: '/:catchAll(.*)*',
+ component: () => import('pages/ErrorNotFound.vue')
+ }
+]
+
+export default routes
diff --git a/ux/src/stores/admin.js b/ux/src/stores/admin.js
new file mode 100644
index 00000000..0cb54581
--- /dev/null
+++ b/ux/src/stores/admin.js
@@ -0,0 +1,43 @@
+import { defineStore } from 'pinia'
+import gql from 'graphql-tag'
+import cloneDeep from 'lodash/cloneDeep'
+
+/* global APOLLO_CLIENT */
+
+export const useAdminStore = defineStore('admin', {
+ state: () => ({
+ currentSiteId: null,
+ info: {
+ currentVersion: 'n/a',
+ latestVersion: 'n/a',
+ groupsTotal: 0,
+ pagesTotal: 0,
+ usersTotal: 0
+ },
+ overlay: null,
+ overlayOpts: {},
+ sites: [],
+ locales: [
+ { code: 'en', name: 'English' }
+ ]
+ }),
+ getters: {},
+ actions: {
+ async fetchSites () {
+ const resp = await APOLLO_CLIENT.query({
+ query: gql`
+ query getSites {
+ sites {
+ id
+ hostname
+ isEnabled
+ title
+ }
+ }
+ `,
+ fetchPolicy: 'network-only'
+ })
+ this.sites = cloneDeep(resp?.data?.sites ?? [])
+ }
+ }
+})
diff --git a/ux/src/stores/data.js b/ux/src/stores/data.js
new file mode 100644
index 00000000..f5a55f9f
--- /dev/null
+++ b/ux/src/stores/data.js
@@ -0,0 +1,260 @@
+import { defineStore } from 'pinia'
+
+export const useDataStore = defineStore('data', {
+ state: () => ({
+ timezones: [
+ { text: '(GMT-11:00) Niue', value: 'Pacific/Niue' },
+ { text: '(GMT-11:00) Pago Pago', value: 'Pacific/Pago_Pago' },
+ { text: '(GMT-10:00) Hawaii Time', value: 'Pacific/Honolulu' },
+ { text: '(GMT-10:00) Rarotonga', value: 'Pacific/Rarotonga' },
+ { text: '(GMT-10:00) Tahiti', value: 'Pacific/Tahiti' },
+ { text: '(GMT-09:30) Marquesas', value: 'Pacific/Marquesas' },
+ { text: '(GMT-09:00) Alaska Time', value: 'America/Anchorage' },
+ { text: '(GMT-09:00) Gambier', value: 'Pacific/Gambier' },
+ { text: '(GMT-08:00) Pacific Time', value: 'America/Los_Angeles' },
+ { text: '(GMT-08:00) Pacific Time - Tijuana', value: 'America/Tijuana' },
+ { text: '(GMT-08:00) Pacific Time - Vancouver', value: 'America/Vancouver' },
+ { text: '(GMT-08:00) Pacific Time - Whitehorse', value: 'America/Whitehorse' },
+ { text: '(GMT-08:00) Pitcairn', value: 'Pacific/Pitcairn' },
+ { text: '(GMT-07:00) Mountain Time', value: 'America/Denver' },
+ { text: '(GMT-07:00) Mountain Time - Arizona', value: 'America/Phoenix' },
+ { text: '(GMT-07:00) Mountain Time - Chihuahua, Mazatlan', value: 'America/Mazatlan' },
+ { text: '(GMT-07:00) Mountain Time - Dawson Creek', value: 'America/Dawson_Creek' },
+ { text: '(GMT-07:00) Mountain Time - Edmonton', value: 'America/Edmonton' },
+ { text: '(GMT-07:00) Mountain Time - Hermosillo', value: 'America/Hermosillo' },
+ { text: '(GMT-07:00) Mountain Time - Yellowknife', value: 'America/Yellowknife' },
+ { text: '(GMT-06:00) Belize', value: 'America/Belize' },
+ { text: '(GMT-06:00) Central Time', value: 'America/Chicago' },
+ { text: '(GMT-06:00) Central Time - Mexico City', value: 'America/Mexico_City' },
+ { text: '(GMT-06:00) Central Time - Regina', value: 'America/Regina' },
+ { text: '(GMT-06:00) Central Time - Tegucigalpa', value: 'America/Tegucigalpa' },
+ { text: '(GMT-06:00) Central Time - Winnipeg', value: 'America/Winnipeg' },
+ { text: '(GMT-06:00) Costa Rica', value: 'America/Costa_Rica' },
+ { text: '(GMT-06:00) El Salvador', value: 'America/El_Salvador' },
+ { text: '(GMT-06:00) Galapagos', value: 'Pacific/Galapagos' },
+ { text: '(GMT-06:00) Guatemala', value: 'America/Guatemala' },
+ { text: '(GMT-06:00) Managua', value: 'America/Managua' },
+ { text: '(GMT-05:00) America Cancun', value: 'America/Cancun' },
+ { text: '(GMT-05:00) Bogota', value: 'America/Bogota' },
+ { text: '(GMT-05:00) Easter Island', value: 'Pacific/Easter' },
+ { text: '(GMT-05:00) Eastern Time', value: 'America/New_York' },
+ { text: '(GMT-05:00) Eastern Time - Iqaluit', value: 'America/Iqaluit' },
+ { text: '(GMT-05:00) Eastern Time - Toronto', value: 'America/Toronto' },
+ { text: '(GMT-05:00) Guayaquil', value: 'America/Guayaquil' },
+ { text: '(GMT-05:00) Havana', value: 'America/Havana' },
+ { text: '(GMT-05:00) Jamaica', value: 'America/Jamaica' },
+ { text: '(GMT-05:00) Lima', value: 'America/Lima' },
+ { text: '(GMT-05:00) Nassau', value: 'America/Nassau' },
+ { text: '(GMT-05:00) Panama', value: 'America/Panama' },
+ { text: '(GMT-05:00) Port-au-Prince', value: 'America/Port-au-Prince' },
+ { text: '(GMT-05:00) Rio Branco', value: 'America/Rio_Branco' },
+ { text: '(GMT-04:00) Atlantic Time - Halifax', value: 'America/Halifax' },
+ { text: '(GMT-04:00) Barbados', value: 'America/Barbados' },
+ { text: '(GMT-04:00) Bermuda', value: 'Atlantic/Bermuda' },
+ { text: '(GMT-04:00) Boa Vista', value: 'America/Boa_Vista' },
+ { text: '(GMT-04:00) Caracas', value: 'America/Caracas' },
+ { text: '(GMT-04:00) Curacao', value: 'America/Curacao' },
+ { text: '(GMT-04:00) Grand Turk', value: 'America/Grand_Turk' },
+ { text: '(GMT-04:00) Guyana', value: 'America/Guyana' },
+ { text: '(GMT-04:00) La Paz', value: 'America/La_Paz' },
+ { text: '(GMT-04:00) Manaus', value: 'America/Manaus' },
+ { text: '(GMT-04:00) Martinique', value: 'America/Martinique' },
+ { text: '(GMT-04:00) Port of Spain', value: 'America/Port_of_Spain' },
+ { text: '(GMT-04:00) Porto Velho', value: 'America/Porto_Velho' },
+ { text: '(GMT-04:00) Puerto Rico', value: 'America/Puerto_Rico' },
+ { text: '(GMT-04:00) Santo Domingo', value: 'America/Santo_Domingo' },
+ { text: '(GMT-04:00) Thule', value: 'America/Thule' },
+ { text: '(GMT-03:30) Newfoundland Time - St. Johns', value: 'America/St_Johns' },
+ { text: '(GMT-03:00) Araguaina', value: 'America/Araguaina' },
+ { text: '(GMT-03:00) Asuncion', value: 'America/Asuncion' },
+ { text: '(GMT-03:00) Belem', value: 'America/Belem' },
+ { text: '(GMT-03:00) Buenos Aires', value: 'America/Argentina/Buenos_Aires' },
+ { text: '(GMT-03:00) Campo Grande', value: 'America/Campo_Grande' },
+ { text: '(GMT-03:00) Cayenne', value: 'America/Cayenne' },
+ { text: '(GMT-03:00) Cuiaba', value: 'America/Cuiaba' },
+ { text: '(GMT-03:00) Fortaleza', value: 'America/Fortaleza' },
+ { text: '(GMT-03:00) Godthab', value: 'America/Godthab' },
+ { text: '(GMT-03:00) Maceio', value: 'America/Maceio' },
+ { text: '(GMT-03:00) Miquelon', value: 'America/Miquelon' },
+ { text: '(GMT-03:00) Montevideo', value: 'America/Montevideo' },
+ { text: '(GMT-03:00) Palmer', value: 'Antarctica/Palmer' },
+ { text: '(GMT-03:00) Paramaribo', value: 'America/Paramaribo' },
+ { text: '(GMT-03:00) Punta Arenas', value: 'America/Punta_Arenas' },
+ { text: '(GMT-03:00) Recife', value: 'America/Recife' },
+ { text: '(GMT-03:00) Rothera', value: 'Antarctica/Rothera' },
+ { text: '(GMT-03:00) Salvador', value: 'America/Bahia' },
+ { text: '(GMT-03:00) Santiago', value: 'America/Santiago' },
+ { text: '(GMT-03:00) Stanley', value: 'Atlantic/Stanley' },
+ { text: '(GMT-02:00) Noronha', value: 'America/Noronha' },
+ { text: '(GMT-02:00) Sao Paulo', value: 'America/Sao_Paulo' },
+ { text: '(GMT-02:00) South Georgia', value: 'Atlantic/South_Georgia' },
+ { text: '(GMT-01:00) Azores', value: 'Atlantic/Azores' },
+ { text: '(GMT-01:00) Cape Verde', value: 'Atlantic/Cape_Verde' },
+ { text: '(GMT-01:00) Scoresbysund', value: 'America/Scoresbysund' },
+ { text: '(GMT+00:00) Abidjan', value: 'Africa/Abidjan' },
+ { text: '(GMT+00:00) Accra', value: 'Africa/Accra' },
+ { text: '(GMT+00:00) Bissau', value: 'Africa/Bissau' },
+ { text: '(GMT+00:00) Canary Islands', value: 'Atlantic/Canary' },
+ { text: '(GMT+00:00) Casablanca', value: 'Africa/Casablanca' },
+ { text: '(GMT+00:00) Danmarkshavn', value: 'America/Danmarkshavn' },
+ { text: '(GMT+00:00) Dublin', value: 'Europe/Dublin' },
+ { text: '(GMT+00:00) El Aaiun', value: 'Africa/El_Aaiun' },
+ { text: '(GMT+00:00) Faeroe', value: 'Atlantic/Faroe' },
+ { text: '(GMT+00:00) GMT (no daylight saving)', value: 'Etc/GMT' },
+ { text: '(GMT+00:00) Lisbon', value: 'Europe/Lisbon' },
+ { text: '(GMT+00:00) London', value: 'Europe/London' },
+ { text: '(GMT+00:00) Monrovia', value: 'Africa/Monrovia' },
+ { text: '(GMT+00:00) Reykjavik', value: 'Atlantic/Reykjavik' },
+ { text: '(GMT+01:00) Algiers', value: 'Africa/Algiers' },
+ { text: '(GMT+01:00) Amsterdam', value: 'Europe/Amsterdam' },
+ { text: '(GMT+01:00) Andorra', value: 'Europe/Andorra' },
+ { text: '(GMT+01:00) Berlin', value: 'Europe/Berlin' },
+ { text: '(GMT+01:00) Brussels', value: 'Europe/Brussels' },
+ { text: '(GMT+01:00) Budapest', value: 'Europe/Budapest' },
+ { text: '(GMT+01:00) Central European Time - Belgrade', value: 'Europe/Belgrade' },
+ { text: '(GMT+01:00) Central European Time - Prague', value: 'Europe/Prague' },
+ { text: '(GMT+01:00) Ceuta', value: 'Africa/Ceuta' },
+ { text: '(GMT+01:00) Copenhagen', value: 'Europe/Copenhagen' },
+ { text: '(GMT+01:00) Gibraltar', value: 'Europe/Gibraltar' },
+ { text: '(GMT+01:00) Lagos', value: 'Africa/Lagos' },
+ { text: '(GMT+01:00) Luxembourg', value: 'Europe/Luxembourg' },
+ { text: '(GMT+01:00) Madrid', value: 'Europe/Madrid' },
+ { text: '(GMT+01:00) Malta', value: 'Europe/Malta' },
+ { text: '(GMT+01:00) Monaco', value: 'Europe/Monaco' },
+ { text: '(GMT+01:00) Ndjamena', value: 'Africa/Ndjamena' },
+ { text: '(GMT+01:00) Oslo', value: 'Europe/Oslo' },
+ { text: '(GMT+01:00) Paris', value: 'Europe/Paris' },
+ { text: '(GMT+01:00) Rome', value: 'Europe/Rome' },
+ { text: '(GMT+01:00) Stockholm', value: 'Europe/Stockholm' },
+ { text: '(GMT+01:00) Tirane', value: 'Europe/Tirane' },
+ { text: '(GMT+01:00) Tunis', value: 'Africa/Tunis' },
+ { text: '(GMT+01:00) Vienna', value: 'Europe/Vienna' },
+ { text: '(GMT+01:00) Warsaw', value: 'Europe/Warsaw' },
+ { text: '(GMT+01:00) Zurich', value: 'Europe/Zurich' },
+ { text: '(GMT+02:00) Amman', value: 'Asia/Amman' },
+ { text: '(GMT+02:00) Athens', value: 'Europe/Athens' },
+ { text: '(GMT+02:00) Beirut', value: 'Asia/Beirut' },
+ { text: '(GMT+02:00) Bucharest', value: 'Europe/Bucharest' },
+ { text: '(GMT+02:00) Cairo', value: 'Africa/Cairo' },
+ { text: '(GMT+02:00) Chisinau', value: 'Europe/Chisinau' },
+ { text: '(GMT+02:00) Damascus', value: 'Asia/Damascus' },
+ { text: '(GMT+02:00) Gaza', value: 'Asia/Gaza' },
+ { text: '(GMT+02:00) Helsinki', value: 'Europe/Helsinki' },
+ { text: '(GMT+02:00) Jerusalem', value: 'Asia/Jerusalem' },
+ { text: '(GMT+02:00) Johannesburg', value: 'Africa/Johannesburg' },
+ { text: '(GMT+02:00) Khartoum', value: 'Africa/Khartoum' },
+ { text: '(GMT+02:00) Kiev', value: 'Europe/Kiev' },
+ { text: '(GMT+02:00) Maputo', value: 'Africa/Maputo' },
+ { text: '(GMT+02:00) Moscow-01 - Kaliningrad', value: 'Europe/Kaliningrad' },
+ { text: '(GMT+02:00) Nicosia', value: 'Asia/Nicosia' },
+ { text: '(GMT+02:00) Riga', value: 'Europe/Riga' },
+ { text: '(GMT+02:00) Sofia', value: 'Europe/Sofia' },
+ { text: '(GMT+02:00) Tallinn', value: 'Europe/Tallinn' },
+ { text: '(GMT+02:00) Tripoli', value: 'Africa/Tripoli' },
+ { text: '(GMT+02:00) Vilnius', value: 'Europe/Vilnius' },
+ { text: '(GMT+02:00) Windhoek', value: 'Africa/Windhoek' },
+ { text: '(GMT+03:00) Baghdad', value: 'Asia/Baghdad' },
+ { text: '(GMT+03:00) Istanbul', value: 'Europe/Istanbul' },
+ { text: '(GMT+03:00) Minsk', value: 'Europe/Minsk' },
+ { text: '(GMT+03:00) Moscow+00 - Moscow', value: 'Europe/Moscow' },
+ { text: '(GMT+03:00) Nairobi', value: 'Africa/Nairobi' },
+ { text: '(GMT+03:00) Qatar', value: 'Asia/Qatar' },
+ { text: '(GMT+03:00) Riyadh', value: 'Asia/Riyadh' },
+ { text: '(GMT+03:00) Syowa', value: 'Antarctica/Syowa' },
+ { text: '(GMT+03:30) Tehran', value: 'Asia/Tehran' },
+ { text: '(GMT+04:00) Baku', value: 'Asia/Baku' },
+ { text: '(GMT+04:00) Dubai', value: 'Asia/Dubai' },
+ { text: '(GMT+04:00) Mahe', value: 'Indian/Mahe' },
+ { text: '(GMT+04:00) Mauritius', value: 'Indian/Mauritius' },
+ { text: '(GMT+04:00) Moscow+01 - Samara', value: 'Europe/Samara' },
+ { text: '(GMT+04:00) Reunion', value: 'Indian/Reunion' },
+ { text: '(GMT+04:00) Tbilisi', value: 'Asia/Tbilisi' },
+ { text: '(GMT+04:00) Yerevan', value: 'Asia/Yerevan' },
+ { text: '(GMT+04:30) Kabul', value: 'Asia/Kabul' },
+ { text: '(GMT+05:00) Aqtau', value: 'Asia/Aqtau' },
+ { text: '(GMT+05:00) Aqtobe', value: 'Asia/Aqtobe' },
+ { text: '(GMT+05:00) Ashgabat', value: 'Asia/Ashgabat' },
+ { text: '(GMT+05:00) Dushanbe', value: 'Asia/Dushanbe' },
+ { text: '(GMT+05:00) Karachi', value: 'Asia/Karachi' },
+ { text: '(GMT+05:00) Kerguelen', value: 'Indian/Kerguelen' },
+ { text: '(GMT+05:00) Maldives', value: 'Indian/Maldives' },
+ { text: '(GMT+05:00) Mawson', value: 'Antarctica/Mawson' },
+ { text: '(GMT+05:00) Moscow+02 - Yekaterinburg', value: 'Asia/Yekaterinburg' },
+ { text: '(GMT+05:00) Tashkent', value: 'Asia/Tashkent' },
+ { text: '(GMT+05:30) Colombo', value: 'Asia/Colombo' },
+ { text: '(GMT+05:30) India Standard Time', value: 'Asia/Kolkata' },
+ { text: '(GMT+05:45) Kathmandu', value: 'Asia/Kathmandu' },
+ { text: '(GMT+06:00) Almaty', value: 'Asia/Almaty' },
+ { text: '(GMT+06:00) Bishkek', value: 'Asia/Bishkek' },
+ { text: '(GMT+06:00) Chagos', value: 'Indian/Chagos' },
+ { text: '(GMT+06:00) Dhaka', value: 'Asia/Dhaka' },
+ { text: '(GMT+06:00) Moscow+03 - Omsk', value: 'Asia/Omsk' },
+ { text: '(GMT+06:00) Thimphu', value: 'Asia/Thimphu' },
+ { text: '(GMT+06:00) Vostok', value: 'Antarctica/Vostok' },
+ { text: '(GMT+06:30) Cocos', value: 'Indian/Cocos' },
+ { text: '(GMT+06:30) Rangoon', value: 'Asia/Yangon' },
+ { text: '(GMT+07:00) Bangkok', value: 'Asia/Bangkok' },
+ { text: '(GMT+07:00) Christmas', value: 'Indian/Christmas' },
+ { text: '(GMT+07:00) Davis', value: 'Antarctica/Davis' },
+ { text: '(GMT+07:00) Hanoi', value: 'Asia/Saigon' },
+ { text: '(GMT+07:00) Hovd', value: 'Asia/Hovd' },
+ { text: '(GMT+07:00) Jakarta', value: 'Asia/Jakarta' },
+ { text: '(GMT+07:00) Moscow+04 - Krasnoyarsk', value: 'Asia/Krasnoyarsk' },
+ { text: '(GMT+08:00) Brunei', value: 'Asia/Brunei' },
+ { text: '(GMT+08:00) China Time - Beijing', value: 'Asia/Shanghai' },
+ { text: '(GMT+08:00) Choibalsan', value: 'Asia/Choibalsan' },
+ { text: '(GMT+08:00) Hong Kong', value: 'Asia/Hong_Kong' },
+ { text: '(GMT+08:00) Kuala Lumpur', value: 'Asia/Kuala_Lumpur' },
+ { text: '(GMT+08:00) Macau', value: 'Asia/Macau' },
+ { text: '(GMT+08:00) Makassar', value: 'Asia/Makassar' },
+ { text: '(GMT+08:00) Manila', value: 'Asia/Manila' },
+ { text: '(GMT+08:00) Moscow+05 - Irkutsk', value: 'Asia/Irkutsk' },
+ { text: '(GMT+08:00) Singapore', value: 'Asia/Singapore' },
+ { text: '(GMT+08:00) Taipei', value: 'Asia/Taipei' },
+ { text: '(GMT+08:00) Ulaanbaatar', value: 'Asia/Ulaanbaatar' },
+ { text: '(GMT+08:00) Western Time - Perth', value: 'Australia/Perth' },
+ { text: '(GMT+08:30) Pyongyang', value: 'Asia/Pyongyang' },
+ { text: '(GMT+09:00) Dili', value: 'Asia/Dili' },
+ { text: '(GMT+09:00) Jayapura', value: 'Asia/Jayapura' },
+ { text: '(GMT+09:00) Moscow+06 - Yakutsk', value: 'Asia/Yakutsk' },
+ { text: '(GMT+09:00) Palau', value: 'Pacific/Palau' },
+ { text: '(GMT+09:00) Seoul', value: 'Asia/Seoul' },
+ { text: '(GMT+09:00) Tokyo', value: 'Asia/Tokyo' },
+ { text: '(GMT+09:30) Central Time - Darwin', value: 'Australia/Darwin' },
+ { text: '(GMT+10:00) Dumont D\'Urville', value: 'Antarctica/DumontDUrville' },
+ { text: '(GMT+10:00) Eastern Time - Brisbane', value: 'Australia/Brisbane' },
+ { text: '(GMT+10:00) Guam', value: 'Pacific/Guam' },
+ { text: '(GMT+10:00) Moscow+07 - Vladivostok', value: 'Asia/Vladivostok' },
+ { text: '(GMT+10:00) Port Moresby', value: 'Pacific/Port_Moresby' },
+ { text: '(GMT+10:00) Truk', value: 'Pacific/Chuuk' },
+ { text: '(GMT+10:30) Central Time - Adelaide', value: 'Australia/Adelaide' },
+ { text: '(GMT+11:00) Casey', value: 'Antarctica/Casey' },
+ { text: '(GMT+11:00) Eastern Time - Hobart', value: 'Australia/Hobart' },
+ { text: '(GMT+11:00) Eastern Time - Melbourne, Sydney', value: 'Australia/Sydney' },
+ { text: '(GMT+11:00) Efate', value: 'Pacific/Efate' },
+ { text: '(GMT+11:00) Guadalcanal', value: 'Pacific/Guadalcanal' },
+ { text: '(GMT+11:00) Kosrae', value: 'Pacific/Kosrae' },
+ { text: '(GMT+11:00) Moscow+08 - Magadan', value: 'Asia/Magadan' },
+ { text: '(GMT+11:00) Norfolk', value: 'Pacific/Norfolk' },
+ { text: '(GMT+11:00) Noumea', value: 'Pacific/Noumea' },
+ { text: '(GMT+11:00) Ponape', value: 'Pacific/Pohnpei' },
+ { text: '(GMT+12:00) Funafuti', value: 'Pacific/Funafuti' },
+ { text: '(GMT+12:00) Kwajalein', value: 'Pacific/Kwajalein' },
+ { text: '(GMT+12:00) Majuro', value: 'Pacific/Majuro' },
+ { text: '(GMT+12:00) Moscow+09 - Petropavlovsk-Kamchatskiy', value: 'Asia/Kamchatka' },
+ { text: '(GMT+12:00) Nauru', value: 'Pacific/Nauru' },
+ { text: '(GMT+12:00) Tarawa', value: 'Pacific/Tarawa' },
+ { text: '(GMT+12:00) Wake', value: 'Pacific/Wake' },
+ { text: '(GMT+12:00) Wallis', value: 'Pacific/Wallis' },
+ { text: '(GMT+13:00) Auckland', value: 'Pacific/Auckland' },
+ { text: '(GMT+13:00) Enderbury', value: 'Pacific/Enderbury' },
+ { text: '(GMT+13:00) Fakaofo', value: 'Pacific/Fakaofo' },
+ { text: '(GMT+13:00) Fiji', value: 'Pacific/Fiji' },
+ { text: '(GMT+13:00) Tongatapu', value: 'Pacific/Tongatapu' },
+ { text: '(GMT+14:00) Apia', value: 'Pacific/Apia' },
+ { text: '(GMT+14:00) Kiritimati', value: 'Pacific/Kiritimati' }
+ ]
+ }),
+ getters: {},
+ actions: {}
+})
diff --git a/ux/src/stores/editor.js b/ux/src/stores/editor.js
new file mode 100644
index 00000000..008ee71c
--- /dev/null
+++ b/ux/src/stores/editor.js
@@ -0,0 +1,20 @@
+import { defineStore } from 'pinia'
+
+export const useEditorStore = defineStore('editor', {
+ state: () => ({
+ editor: '',
+ content: '',
+ mode: 'create',
+ activeModal: '',
+ activeModalData: null,
+ media: {
+ folderTree: [],
+ currentFolderId: 0,
+ currentFileId: null
+ },
+ checkoutDateActive: '',
+ editors: {}
+ }),
+ getters: {},
+ actions: {}
+})
diff --git a/ux/src/stores/index.js b/ux/src/stores/index.js
new file mode 100644
index 00000000..ca5bee55
--- /dev/null
+++ b/ux/src/stores/index.js
@@ -0,0 +1,20 @@
+import { store } from 'quasar/wrappers'
+import { createPinia } from 'pinia'
+
+/*
+ * 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 } */) => {
+ const pinia = createPinia()
+
+ // You can add Pinia plugins here
+ // pinia.use(SomePiniaPlugin)
+
+ return pinia
+})
diff --git a/ux/src/stores/page.js b/ux/src/stores/page.js
new file mode 100644
index 00000000..c3f7bd19
--- /dev/null
+++ b/ux/src/stores/page.js
@@ -0,0 +1,128 @@
+import { defineStore } from 'pinia'
+
+export const usePageStore = defineStore('page', {
+ state: () => ({
+ mode: 'view',
+ editor: 'wysiwyg',
+ editorMode: 'edit',
+ id: 0,
+ authorId: 0,
+ authorName: 'Unknown',
+ createdAt: '',
+ description: 'How to install Wiki.js on Ubuntu 18.04 / 20.04',
+ isPublished: true,
+ showInTree: true,
+ locale: 'en',
+ path: '',
+ publishEndDate: '',
+ publishStartDate: '',
+ tags: ['cities', 'canada'],
+ title: 'Ubuntu',
+ icon: 'lab la-empire',
+ updatedAt: '',
+ relations: [],
+ scriptJsLoad: '',
+ scriptJsUnload: '',
+ scriptStyles: '',
+ allowComments: false,
+ allowContributions: true,
+ allowRatings: true,
+ showSidebar: true,
+ showToc: true,
+ showTags: true,
+ tocDepth: {
+ min: 1,
+ max: 2
+ },
+ breadcrumbs: [
+ {
+ id: 1,
+ title: 'Installation',
+ icon: 'las la-file-alt',
+ locale: 'en',
+ path: 'installation'
+ },
+ {
+ id: 2,
+ title: 'Ubuntu',
+ icon: 'lab la-ubuntu',
+ locale: 'en',
+ path: 'installation/ubuntu'
+ }
+ ],
+ effectivePermissions: {
+ comments: {
+ read: false,
+ write: false,
+ manage: false
+ },
+ history: {
+ read: false
+ },
+ source: {
+ read: false
+ },
+ pages: {
+ write: false,
+ manage: false,
+ delete: false,
+ script: false,
+ style: false
+ },
+ system: {
+ manage: false
+ }
+ },
+ commentsCount: 0,
+ content: '',
+ render: 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.'
+ }),
+ getters: {},
+ actions: {
+ /**
+ * PAGE - CREATE
+ */
+ pageCreate ({ editor, locale, path }) {
+ // -> Editor View
+ this.editor = editor
+ this.editorMode = 'create'
+
+ // if (['markdown', 'api'].includes(editor)) {
+ // commit('site/SET_SHOW_SIDE_NAV', false, { root: true })
+ // } else {
+ // commit('site/SET_SHOW_SIDE_NAV', true, { root: true })
+ // }
+
+ // if (['markdown', 'channel', 'api'].includes(editor)) {
+ // commit('site/SET_SHOW_SIDEBAR', false, { root: true })
+ // } else {
+ // commit('site/SET_SHOW_SIDEBAR', true, { root: true })
+ // }
+
+ // -> Page Data
+ this.id = 0
+ this.locale = locale || this.locale
+ if (path) {
+ this.path = path
+ } else {
+ this.path = this.path.length < 2 ? 'new-page' : `${this.path}/new-page`
+ }
+ this.title = ''
+ this.description = ''
+ this.icon = 'las la-file-alt'
+ this.isPublished = false
+ this.relations = []
+ this.tags = []
+ this.breadcrumbs = []
+
+ this.content = ''
+ this.render = ''
+
+ // -> View Mode
+ this.mode = 'edit'
+ },
+ generateToc () {
+
+ }
+ }
+})
diff --git a/ux/src/stores/site.js b/ux/src/stores/site.js
new file mode 100644
index 00000000..03fc733a
--- /dev/null
+++ b/ux/src/stores/site.js
@@ -0,0 +1,98 @@
+import { defineStore } from 'pinia'
+import gql from 'graphql-tag'
+import clone from 'lodash/clone'
+
+export const useSiteStore = defineStore('site', {
+ state: () => ({
+ routerLoading: false,
+ id: null,
+ useLocales: false,
+ hostname: '',
+ company: '',
+ contentLicense: '',
+ dark: false,
+ title: '',
+ description: '',
+ logoUrl: '',
+ search: '',
+ searchIsFocused: false,
+ searchIsLoading: false,
+ searchRestrictLocale: false,
+ searchRestrictPath: false,
+ printView: false,
+ ratingsMode: 'thumbs',
+ pageDataTemplates: [],
+ showSideNav: true,
+ showSidebar: true,
+ theme: {
+ dark: false,
+ injectCSS: '',
+ injectHead: '',
+ injectBody: '',
+ colorPrimary: '#1976D2',
+ colorSecondary: '#02C39A',
+ colorAccent: '#f03a47',
+ colorHeader: '#000',
+ colorSidebar: '#1976D2',
+ sidebarPosition: 'left',
+ tocPosition: 'right',
+ showSharingMenu: true,
+ showPrintBtn: true
+ },
+ thumbStyle: {
+ right: '2px',
+ borderRadius: '5px',
+ backgroundColor: '#000',
+ width: '5px',
+ opacity: 0.15
+ },
+ barStyle: {
+ backgroundColor: '#FAFAFA',
+ width: '9px',
+ opacity: 1
+ }
+ }),
+ getters: {},
+ actions: {
+ async loadSite (hostname) {
+ try {
+ const resp = await APOLLO_CLIENT.query({
+ query: gql`
+ query getSiteInfo ($hostname: String!) {
+ siteByHostname (
+ hostname: $hostname
+ exact: false
+ ) {
+ id
+ hostname
+ title
+ description
+ logoText
+ company
+ contentLicense
+ }
+ }
+ `,
+ variables: {
+ hostname
+ }
+ })
+ const siteInfo = resp.data.siteByHostname
+ if (siteInfo) {
+ this.id = clone(siteInfo.id)
+ this.hostname = clone(siteInfo.hostname)
+ this.title = clone(siteInfo.title)
+ this.description = clone(siteInfo.description)
+ this.logoUrl = clone(siteInfo.logoUrl)
+ this.company = clone(siteInfo.company)
+ this.contentLicense = clone(siteInfo.contentLicense)
+ } else {
+ throw new Error('Invalid Site')
+ }
+ } catch (err) {
+ console.warn(err.networkError?.result ?? err.message)
+ throw err
+ }
+ }
+ }
+})
diff --git a/ux/src/stores/store-flag.d.ts b/ux/src/stores/store-flag.d.ts
new file mode 100644
index 00000000..7677175b
--- /dev/null
+++ b/ux/src/stores/store-flag.d.ts
@@ -0,0 +1,10 @@
+/* eslint-disable */
+// THIS FEATURE-FLAG FILE IS AUTOGENERATED,
+// REMOVAL OR CHANGES WILL CAUSE RELATED TYPES TO STOP WORKING
+import "quasar/dist/types/feature-flag";
+
+declare module "quasar/dist/types/feature-flag" {
+ interface QuasarFeatureFlags {
+ store: true;
+ }
+}
diff --git a/ux/src/stores/user.js b/ux/src/stores/user.js
new file mode 100644
index 00000000..bdadff5f
--- /dev/null
+++ b/ux/src/stores/user.js
@@ -0,0 +1,47 @@
+import { defineStore } from 'pinia'
+import jwtDecode from 'jwt-decode'
+import Cookies from 'js-cookie'
+
+export const useUserStore = defineStore('user', {
+ state: () => ({
+ id: 0,
+ email: '',
+ name: '',
+ pictureUrl: '',
+ localeCode: '',
+ defaultEditor: '',
+ timezone: '',
+ dateFormat: '',
+ appearance: '',
+ permissions: [],
+ iat: 0,
+ exp: 0,
+ authenticated: false
+ }),
+ getters: {},
+ actions: {
+ refreshAuth () {
+ const jwtCookie = Cookies.get('jwt')
+ if (jwtCookie) {
+ try {
+ const jwtData = jwtDecode(jwtCookie)
+ this.id = jwtData.id
+ this.email = jwtData.email
+ this.name = jwtData.name
+ this.pictureUrl = jwtData.av
+ this.localeCode = jwtData.lc
+ this.timezone = jwtData.tz || Intl.DateTimeFormat().resolvedOptions().timeZone || ''
+ this.dateFormat = jwtData.df || ''
+ this.appearance = jwtData.ap || ''
+ // this.defaultEditor = jwtData.defaultEditor
+ this.permissions = jwtData.permissions
+ this.iat = jwtData.iat
+ this.exp = jwtData.exp
+ this.authenticated = true
+ } catch (err) {
+ console.debug('Invalid JWT. Silent authentication skipped.')
+ }
+ }
+ }
+ }
+})
diff --git a/ux/yarn.lock b/ux/yarn.lock
new file mode 100644
index 00000000..8f85efe6
--- /dev/null
+++ b/ux/yarn.lock
@@ -0,0 +1,4561 @@
+# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
+# yarn lockfile v1
+
+
+"@apollo/client@3.5.10":
+ version "3.5.10"
+ resolved "https://registry.yarnpkg.com/@apollo/client/-/client-3.5.10.tgz#43463108a6e07ae602cca0afc420805a19339a71"
+ integrity sha512-tL3iSpFe9Oldq7gYikZK1dcYxp1c01nlSwtsMz75382HcI6fvQXyFXUCJTTK3wgO2/ckaBvRGw7VqjFREdVoRw==
+ dependencies:
+ "@graphql-typed-document-node/core" "^3.0.0"
+ "@wry/context" "^0.6.0"
+ "@wry/equality" "^0.5.0"
+ "@wry/trie" "^0.3.0"
+ graphql-tag "^2.12.3"
+ hoist-non-react-statics "^3.3.2"
+ optimism "^0.16.1"
+ prop-types "^15.7.2"
+ symbol-observable "^4.0.0"
+ ts-invariant "^0.9.4"
+ tslib "^2.3.0"
+ zen-observable-ts "^1.2.0"
+
+"@babel/helper-validator-identifier@^7.16.7":
+ version "7.16.7"
+ resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.16.7.tgz#e8c602438c4a8195751243da9031d1607d247cad"
+ integrity sha512-hsEnFemeiW4D08A5gUAZxLBTXpZ39P+a+DGDsHw1yxqyQ/jzFEnxf5uTEGp+3bzAbNOxU1paTgYS4ECU/IgfDw==
+
+"@babel/parser@^7.16.4", "@babel/parser@^7.6.0", "@babel/parser@^7.9.6":
+ version "7.17.9"
+ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.17.9.tgz#9c94189a6062f0291418ca021077983058e171ef"
+ integrity sha512-vqUSBLP8dQHFPdPi9bc5GK9vRkYHJ49fsZdtoJ8EQ8ibpwk5rPKfvNIwChB0KVXcIjcepEBBd2VHC5r9Gy8ueg==
+
+"@babel/types@^7.6.1", "@babel/types@^7.9.6":
+ version "7.17.0"
+ resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.17.0.tgz#a826e368bccb6b3d84acd76acad5c0d87342390b"
+ integrity sha512-TmKSNO4D5rzhL5bjWFcVHHLETzfQ/AmbKpKPOSjlP0WoHZ6L911fgoOKY4Alp/emzG4cHJdyN49zpgkbXFEHHw==
+ dependencies:
+ "@babel/helper-validator-identifier" "^7.16.7"
+ to-fast-properties "^2.0.0"
+
+"@codemirror/autocomplete@0.19.15", "@codemirror/autocomplete@^0.19.0":
+ version "0.19.15"
+ resolved "https://registry.yarnpkg.com/@codemirror/autocomplete/-/autocomplete-0.19.15.tgz#061f09063dc2a68668d85d7ac8430c7bc6df1a82"
+ integrity sha512-GQWzvvuXxNUyaEk+5gawbAD8s51/v2Chb++nx0e2eGWrphWk42isBtzOMdc3DxrxrZtPZ55q2ldNp+6G8KJLIQ==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/state" "^0.19.4"
+ "@codemirror/text" "^0.19.2"
+ "@codemirror/tooltip" "^0.19.12"
+ "@codemirror/view" "^0.19.0"
+ "@lezer/common" "^0.15.0"
+
+"@codemirror/basic-setup@0.19.1":
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/@codemirror/basic-setup/-/basic-setup-0.19.1.tgz#17b27d02f15c628eb62a85d01e3e1b1958933eb4"
+ integrity sha512-gLjD7YgZU/we6BzS/ecCmD3viw83dsgv5ZUaSydYbYx9X4w4w9RqYnckcJ+0GDyHfNr5Jtfv2Z5ZtFQnBj0UDA==
+ dependencies:
+ "@codemirror/autocomplete" "^0.19.0"
+ "@codemirror/closebrackets" "^0.19.0"
+ "@codemirror/commands" "^0.19.0"
+ "@codemirror/comment" "^0.19.0"
+ "@codemirror/fold" "^0.19.0"
+ "@codemirror/gutter" "^0.19.0"
+ "@codemirror/highlight" "^0.19.0"
+ "@codemirror/history" "^0.19.0"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/lint" "^0.19.0"
+ "@codemirror/matchbrackets" "^0.19.0"
+ "@codemirror/rectangular-selection" "^0.19.0"
+ "@codemirror/search" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.31"
+
+"@codemirror/closebrackets@0.19.1":
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/@codemirror/closebrackets/-/closebrackets-0.19.1.tgz#c93219d6800c27a880e569135b468cf4a05d903f"
+ integrity sha512-ZiLXT6u+VuBK5QnfBbt/Vmfd9Pg6449wn1DIOWFZHUOldg5eFn3VGGjYY2XWuHQz5WuK+7dXamV2KE885O1gyA==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.2"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.44"
+
+"@codemirror/closebrackets@^0.19.0":
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/@codemirror/closebrackets/-/closebrackets-0.19.2.tgz#ff74dd78218cee57172623eb9ebf7b669fa6f4d4"
+ integrity sha512-ClMPzPcPP0eQiDcVjtVPl6OLxgdtZSYDazsvT0AKl70V1OJva0eHgl4/6kCW3RZ0pb2n34i9nJz4eXCmK+TYDA==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.2"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.44"
+
+"@codemirror/commands@0.19.8", "@codemirror/commands@^0.19.0":
+ version "0.19.8"
+ resolved "https://registry.yarnpkg.com/@codemirror/commands/-/commands-0.19.8.tgz#1f99c1a8bf200d17c4d6997379099459f3678107"
+ integrity sha512-65LIMSGUGGpY3oH6mzV46YWRrgao6NmfJ+AuC7jNz3K5NPnH6GCV1H5I6SwOFyVbkiygGyd0EFwrWqywTBD1aw==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/matchbrackets" "^0.19.0"
+ "@codemirror/state" "^0.19.2"
+ "@codemirror/text" "^0.19.6"
+ "@codemirror/view" "^0.19.22"
+ "@lezer/common" "^0.15.0"
+
+"@codemirror/comment@0.19.1", "@codemirror/comment@^0.19.0":
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/@codemirror/comment/-/comment-0.19.1.tgz#7def8345eeb9095ef1ef33676fbde1ab4fe33fad"
+ integrity sha512-uGKteBuVWAC6fW+Yt8u27DOnXMT/xV4Ekk2Z5mRsiADCZDqYvryrJd6PLL5+8t64BVyocwQwNfz1UswYS2CtFQ==
+ dependencies:
+ "@codemirror/state" "^0.19.9"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+
+"@codemirror/fold@0.19.3", "@codemirror/fold@^0.19.0":
+ version "0.19.3"
+ resolved "https://registry.yarnpkg.com/@codemirror/fold/-/fold-0.19.3.tgz#de55d44a7313f2a8920aefb6ebf9eff34715d8d4"
+ integrity sha512-8hT+Eq2G68mL0yPRvSD2ewhnLQAX6sbUJmtGVKFcj8oAXtfpYCX8LIcfXsuI19Qs7gZkOSpqZvn+KKj8IhZoAw==
+ dependencies:
+ "@codemirror/gutter" "^0.19.0"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.22"
+
+"@codemirror/gutter@0.19.9", "@codemirror/gutter@^0.19.0", "@codemirror/gutter@^0.19.4":
+ version "0.19.9"
+ resolved "https://registry.yarnpkg.com/@codemirror/gutter/-/gutter-0.19.9.tgz#bbb69f4d49570d9c1b3f3df5d134980c516cd42b"
+ integrity sha512-PFrtmilahin1g6uL27aG5tM/rqR9DZzZYZsIrCXA5Uc2OFTFqx4owuhoU9hqfYxHp5ovfvBwQ+txFzqS4vog6Q==
+ dependencies:
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.23"
+
+"@codemirror/highlight@0.19.8", "@codemirror/highlight@^0.19.0", "@codemirror/highlight@^0.19.6", "@codemirror/highlight@^0.19.7":
+ version "0.19.8"
+ resolved "https://registry.yarnpkg.com/@codemirror/highlight/-/highlight-0.19.8.tgz#a95aee8ae4389b01f820aa79c48f7b4388087d92"
+ integrity sha512-v/lzuHjrYR8MN2mEJcUD6fHSTXXli9C1XGYpr+ElV6fLBIUhMTNKR3qThp611xuWfXfwDxeL7ppcbkM/MzPV3A==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/view" "^0.19.39"
+ "@lezer/common" "^0.15.0"
+ style-mod "^4.0.0"
+
+"@codemirror/history@0.19.2", "@codemirror/history@^0.19.0":
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/@codemirror/history/-/history-0.19.2.tgz#25e3fda755f77ac1223a6ae6e9d7899f5919265e"
+ integrity sha512-unhP4t3N2smzmHoo/Yio6ueWi+il8gm9VKrvi6wlcdGH5fOfVDNkmjHQ495SiR+EdOG35+3iNebSPYww0vN7ow==
+ dependencies:
+ "@codemirror/state" "^0.19.2"
+ "@codemirror/view" "^0.19.0"
+
+"@codemirror/lang-css@0.19.3", "@codemirror/lang-css@^0.19.0":
+ version "0.19.3"
+ resolved "https://registry.yarnpkg.com/@codemirror/lang-css/-/lang-css-0.19.3.tgz#7a17adf78c6fcdab4ad5ee4e360631c41e949e4a"
+ integrity sha512-tyCUJR42/UlfOPLb94/p7dN+IPsYSIzHbAHP2KQHANj0I+Orqp+IyIOS++M8TuCX4zkWh9dvi8s92yy/Tn8Ifg==
+ dependencies:
+ "@codemirror/autocomplete" "^0.19.0"
+ "@codemirror/highlight" "^0.19.6"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@lezer/css" "^0.15.2"
+
+"@codemirror/lang-html@0.19.4", "@codemirror/lang-html@^0.19.0":
+ version "0.19.4"
+ resolved "https://registry.yarnpkg.com/@codemirror/lang-html/-/lang-html-0.19.4.tgz#e6eec28462f18842a0e108732a214a7416b5e333"
+ integrity sha512-GpiEikNuCBeFnS+/TJSeanwqaOfNm8Kkp9WpVNEPZCLyW1mAMCuFJu/3xlWYeWc778Hc3vJqGn3bn+cLNubgCA==
+ dependencies:
+ "@codemirror/autocomplete" "^0.19.0"
+ "@codemirror/highlight" "^0.19.6"
+ "@codemirror/lang-css" "^0.19.0"
+ "@codemirror/lang-javascript" "^0.19.0"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@lezer/common" "^0.15.0"
+ "@lezer/html" "^0.15.0"
+
+"@codemirror/lang-javascript@0.19.7", "@codemirror/lang-javascript@^0.19.0":
+ version "0.19.7"
+ resolved "https://registry.yarnpkg.com/@codemirror/lang-javascript/-/lang-javascript-0.19.7.tgz#84581ef6abf2a16d78f017ffc96c2d6227de5eb5"
+ integrity sha512-DL9f3JLqOEHH9cIwEqqjnP5bkjdVXeECksLtV+/MbPm+l4H+AG+PkwZaJQ2oR1GfPZKh8MVSIE94aGWNkJP8WQ==
+ dependencies:
+ "@codemirror/autocomplete" "^0.19.0"
+ "@codemirror/highlight" "^0.19.7"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/lint" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+ "@lezer/javascript" "^0.15.1"
+
+"@codemirror/lang-json@0.19.2":
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/@codemirror/lang-json/-/lang-json-0.19.2.tgz#b311a0c16382343261fdc3cbda72f09a61ade7db"
+ integrity sha512-fgUWR58Is59P5D/tiazX6oTczioOCDYqjFT5PEBAmLBFMSsRqcnJE0xNO1snrhg7pWEFDq5wR/oN0eZhkeR6Gg==
+ dependencies:
+ "@codemirror/highlight" "^0.19.0"
+ "@codemirror/language" "^0.19.0"
+ "@lezer/json" "^0.15.0"
+
+"@codemirror/lang-markdown@0.19.6":
+ version "0.19.6"
+ resolved "https://registry.yarnpkg.com/@codemirror/lang-markdown/-/lang-markdown-0.19.6.tgz#761301d276fcfbdf88440f0333785efd71c2a4f5"
+ integrity sha512-ojoHeLgv1Rfu0GNGsU0bCtXAIp5dy4VKjndHScITQdlCkS/+SAIfuoeowEx+nMAQwTxI+/9fQZ3xdZVznGFYug==
+ dependencies:
+ "@codemirror/highlight" "^0.19.0"
+ "@codemirror/lang-html" "^0.19.0"
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/view" "^0.19.0"
+ "@lezer/common" "^0.15.0"
+ "@lezer/markdown" "^0.15.0"
+
+"@codemirror/language@^0.19.0":
+ version "0.19.10"
+ resolved "https://registry.yarnpkg.com/@codemirror/language/-/language-0.19.10.tgz#c3d1330fa5de778c6b6b5177af5572a3d9d596b5"
+ integrity sha512-yA0DZ3RYn2CqAAGW62VrU8c4YxscMQn45y/I9sjBlqB1e2OTQLg4CCkMBuMSLXk4xaqjlsgazeOQWaJQOKfV8Q==
+ dependencies:
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+ "@lezer/common" "^0.15.5"
+ "@lezer/lr" "^0.15.0"
+
+"@codemirror/lint@^0.19.0":
+ version "0.19.6"
+ resolved "https://registry.yarnpkg.com/@codemirror/lint/-/lint-0.19.6.tgz#0379688da3e16739db4a6304c73db857ca85d7ec"
+ integrity sha512-Pbw1Y5kHVs2J+itQ0uez3dI4qY9ApYVap7eNfV81x1/3/BXgBkKfadaw0gqJ4h4FDG7OnJwb0VbPsjJQllHjaA==
+ dependencies:
+ "@codemirror/gutter" "^0.19.4"
+ "@codemirror/panel" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.1"
+ "@codemirror/state" "^0.19.4"
+ "@codemirror/tooltip" "^0.19.16"
+ "@codemirror/view" "^0.19.22"
+ crelt "^1.0.5"
+
+"@codemirror/matchbrackets@0.19.4", "@codemirror/matchbrackets@^0.19.0":
+ version "0.19.4"
+ resolved "https://registry.yarnpkg.com/@codemirror/matchbrackets/-/matchbrackets-0.19.4.tgz#50b5188eb2d53f32598dca906bf5fd66626a9ebc"
+ integrity sha512-VFkaOKPNudAA5sGP1zikRHCEKU0hjYmkKpr04pybUpQvfTvNJXlReCyP0rvH/1iEwAGPL990ZTT+QrLdu4MeEA==
+ dependencies:
+ "@codemirror/language" "^0.19.0"
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+ "@lezer/common" "^0.15.0"
+
+"@codemirror/panel@^0.19.0":
+ version "0.19.1"
+ resolved "https://registry.yarnpkg.com/@codemirror/panel/-/panel-0.19.1.tgz#bf77d27b962cf16357139e50864d0eb69d634441"
+ integrity sha512-sYeOCMA3KRYxZYJYn5PNlt9yNsjy3zTNTrbYSfVgjgL9QomIVgOJWPO5hZ2sTN8lufO6lw0vTBsIPL9MSidmBg==
+ dependencies:
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+
+"@codemirror/rangeset@^0.19.0", "@codemirror/rangeset@^0.19.1", "@codemirror/rangeset@^0.19.5":
+ version "0.19.9"
+ resolved "https://registry.yarnpkg.com/@codemirror/rangeset/-/rangeset-0.19.9.tgz#e80895de93c39dc7899f5be31d368c9d88aa4efc"
+ integrity sha512-V8YUuOvK+ew87Xem+71nKcqu1SXd5QROMRLMS/ljT5/3MCxtgrRie1Cvild0G/Z2f1fpWxzX78V0U4jjXBorBQ==
+ dependencies:
+ "@codemirror/state" "^0.19.0"
+
+"@codemirror/rectangular-selection@^0.19.0":
+ version "0.19.2"
+ resolved "https://registry.yarnpkg.com/@codemirror/rectangular-selection/-/rectangular-selection-0.19.2.tgz#caa60114421b5c43244494c94203b65f666edb53"
+ integrity sha512-AXK/p5eGwFJ9GJcLfntqN4dgY+XiIF7eHfXNQJX5HhQLSped2wJE6WuC1rMEaOlcpOqlb9mrNi/ZdUjSIj9mbA==
+ dependencies:
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/text" "^0.19.4"
+ "@codemirror/view" "^0.19.48"
+
+"@codemirror/search@0.19.9":
+ version "0.19.9"
+ resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-0.19.9.tgz#b9548dfeb2028e6d5ecd1965f8f0ee50e2925603"
+ integrity sha512-h3MuwbUbiyOp6Np3IB5r4LH0w4inZvbtLO1Ipmz8RhElcGRiYr11Q6Bim8ocLfe08RmZT6B5EkTj1E8eNlugQQ==
+ dependencies:
+ "@codemirror/panel" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.34"
+ crelt "^1.0.5"
+
+"@codemirror/search@^0.19.0":
+ version "0.19.10"
+ resolved "https://registry.yarnpkg.com/@codemirror/search/-/search-0.19.10.tgz#4b0d91c53278db05088624ae04f164d66fd581cd"
+ integrity sha512-qjubm69HJixPBWzI6HeEghTWOOD8NXiHOTRNvdizqs8xWRuFChq9zkjD3XiAJ7GXSTzCuQJnAP9DBBGCLq4ZIA==
+ dependencies:
+ "@codemirror/panel" "^0.19.0"
+ "@codemirror/rangeset" "^0.19.0"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/text" "^0.19.0"
+ "@codemirror/view" "^0.19.34"
+ crelt "^1.0.5"
+
+"@codemirror/state@0.19.9", "@codemirror/state@^0.19.0", "@codemirror/state@^0.19.2", "@codemirror/state@^0.19.3", "@codemirror/state@^0.19.4", "@codemirror/state@^0.19.9":
+ version "0.19.9"
+ resolved "https://registry.yarnpkg.com/@codemirror/state/-/state-0.19.9.tgz#b797f9fbc204d6dc7975485e231693c09001b0dd"
+ integrity sha512-psOzDolKTZkx4CgUqhBQ8T8gBc0xN5z4gzed109aF6x7D7umpDRoimacI/O6d9UGuyl4eYuDCZmDFr2Rq7aGOw==
+ dependencies:
+ "@codemirror/text" "^0.19.0"
+
+"@codemirror/text@^0.19.0", "@codemirror/text@^0.19.2", "@codemirror/text@^0.19.4", "@codemirror/text@^0.19.6":
+ version "0.19.6"
+ resolved "https://registry.yarnpkg.com/@codemirror/text/-/text-0.19.6.tgz#9adcbd8137f69b75518eacd30ddb16fd67bbac45"
+ integrity sha512-T9jnREMIygx+TPC1bOuepz18maGq/92q2a+n4qTqObKwvNMg+8cMTslb8yxeEDEq7S3kpgGWxgO1UWbQRij0dA==
+
+"@codemirror/tooltip@0.19.16", "@codemirror/tooltip@^0.19.12", "@codemirror/tooltip@^0.19.16":
+ version "0.19.16"
+ resolved "https://registry.yarnpkg.com/@codemirror/tooltip/-/tooltip-0.19.16.tgz#6ba2c43f9d8e3d943d9d7bbae22bf800f7726a22"
+ integrity sha512-zxKDHryUV5/RS45AQL+wOeN+i7/l81wK56OMnUPoTSzCWNITfxHn7BToDsjtrRKbzHqUxKYmBnn/4hPjpZ4WJQ==
+ dependencies:
+ "@codemirror/state" "^0.19.0"
+ "@codemirror/view" "^0.19.0"
+
+"@codemirror/view@0.19.47":
+ version "0.19.47"
+ resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.19.47.tgz#2163c3016d7680bf50dd695c0e3abdaf80f45363"
+ integrity sha512-SfbagKvJQl5dtt+9wYpo9sa3ZkMgUxTq+/hXDf0KVwIx+zu3cJIqfEm9xSx6yXkq7it7RsPGHaPasApNffF/8g==
+ dependencies:
+ "@codemirror/rangeset" "^0.19.5"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/text" "^0.19.0"
+ style-mod "^4.0.0"
+ w3c-keyname "^2.2.4"
+
+"@codemirror/view@^0.19.0", "@codemirror/view@^0.19.22", "@codemirror/view@^0.19.23", "@codemirror/view@^0.19.31", "@codemirror/view@^0.19.34", "@codemirror/view@^0.19.39", "@codemirror/view@^0.19.44", "@codemirror/view@^0.19.48":
+ version "0.19.48"
+ resolved "https://registry.yarnpkg.com/@codemirror/view/-/view-0.19.48.tgz#1c657e2b0f8ed896ac6448d6e2215ab115e2a0fc"
+ integrity sha512-0eg7D2Nz4S8/caetCTz61rK0tkHI17V/d15Jy0kLOT8dTLGGNJUponDnW28h2B6bERmPlVHKh8MJIr5OCp1nGw==
+ dependencies:
+ "@codemirror/rangeset" "^0.19.5"
+ "@codemirror/state" "^0.19.3"
+ "@codemirror/text" "^0.19.0"
+ style-mod "^4.0.0"
+ w3c-keyname "^2.2.4"
+
+"@dash14/svg-pan-zoom@^3.6.6":
+ version "3.6.6"
+ resolved "https://registry.yarnpkg.com/@dash14/svg-pan-zoom/-/svg-pan-zoom-3.6.6.tgz#77f01e999a3280fb6bfa837aec235f7f1a693189"
+ integrity sha512-HXzIuzvlrNzBw9ENlj3cWFHzhZyNmsIrvutUfZA8cT3Og3WP6rC22eUlVIzezfEC9FRRCrFNKMfumSfx8jd3/A==
+
+"@eslint/eslintrc@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-1.2.1.tgz#8b5e1c49f4077235516bc9ec7d41378c0f69b8c6"
+ integrity sha512-bxvbYnBPN1Gibwyp6NrpnFzA3YtRL3BBAyEAFVIpNTm2Rn4Vy87GA5M4aSn3InRrlsbX5N0GW7XIx+U4SAEKdQ==
+ dependencies:
+ ajv "^6.12.4"
+ debug "^4.3.2"
+ espree "^9.3.1"
+ globals "^13.9.0"
+ ignore "^5.2.0"
+ import-fresh "^3.2.1"
+ js-yaml "^4.1.0"
+ minimatch "^3.0.4"
+ strip-json-comments "^3.1.1"
+
+"@graphql-typed-document-node/core@^3.0.0":
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/@graphql-typed-document-node/core/-/core-3.1.1.tgz#076d78ce99822258cf813ecc1e7fa460fa74d052"
+ integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==
+
+"@humanwhocodes/config-array@^0.9.2":
+ version "0.9.5"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.9.5.tgz#2cbaf9a89460da24b5ca6531b8bbfc23e1df50c7"
+ integrity sha512-ObyMyWxZiCu/yTisA7uzx81s40xR2fD5Cg/2Kq7G02ajkNubJf6BopgDTmDyc3U7sXpNKM8cYOw7s7Tyr+DnCw==
+ dependencies:
+ "@humanwhocodes/object-schema" "^1.2.1"
+ debug "^4.1.1"
+ minimatch "^3.0.4"
+
+"@humanwhocodes/object-schema@^1.2.1":
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
+ integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
+
+"@intlify/bundle-utils@^2.2.2":
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/@intlify/bundle-utils/-/bundle-utils-2.2.2.tgz#fe65ce2549a73b99b75f3e66209d741b4f4d61fd"
+ integrity sha512-vngkvlIVV8ZJoyC5VqMvqJd2nvsx+qMN7pQjPiPjOrVndeiR7Dlue0k86Q8FsFUzyksW3HJZZi833ldxwbFzTA==
+ dependencies:
+ "@intlify/message-compiler" "^9.1.0"
+ "@intlify/shared" "^9.1.0"
+ jsonc-eslint-parser "^1.0.1"
+ source-map "^0.6.1"
+ yaml-eslint-parser "^0.3.2"
+
+"@intlify/core-base@9.1.9":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/core-base/-/core-base-9.1.9.tgz#e4e8c951010728e4af3a0d13d74cf3f9e7add7f6"
+ integrity sha512-x5T0p/Ja0S8hs5xs+ImKyYckVkL4CzcEXykVYYV6rcbXxJTe2o58IquSqX9bdncVKbRZP7GlBU1EcRaQEEJ+vw==
+ dependencies:
+ "@intlify/devtools-if" "9.1.9"
+ "@intlify/message-compiler" "9.1.9"
+ "@intlify/message-resolver" "9.1.9"
+ "@intlify/runtime" "9.1.9"
+ "@intlify/shared" "9.1.9"
+ "@intlify/vue-devtools" "9.1.9"
+
+"@intlify/devtools-if@9.1.9":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/devtools-if/-/devtools-if-9.1.9.tgz#a30e1dd1256ff2c5c98d8d75d075384fba898e5d"
+ integrity sha512-oKSMKjttG3Ut/1UGEZjSdghuP3fwA15zpDPcjkf/1FjlOIm6uIBGMNS5jXzsZy593u+P/YcnrZD6cD3IVFz9vQ==
+ dependencies:
+ "@intlify/shared" "9.1.9"
+
+"@intlify/message-compiler@9.1.9", "@intlify/message-compiler@^9.1.0":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/message-compiler/-/message-compiler-9.1.9.tgz#1193cbd224a71c2fb981455b8534a3c766d2948d"
+ integrity sha512-6YgCMF46Xd0IH2hMRLCssZI3gFG4aywidoWQ3QP4RGYQXQYYfFC54DxhSgfIPpVoPLQ+4AD29eoYmhiHZ+qLFQ==
+ dependencies:
+ "@intlify/message-resolver" "9.1.9"
+ "@intlify/shared" "9.1.9"
+ source-map "0.6.1"
+
+"@intlify/message-resolver@9.1.9":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/message-resolver/-/message-resolver-9.1.9.tgz#3155ccd2f5e6d0dc16cad8b7f1d8e97fcda05bfc"
+ integrity sha512-Lx/DBpigeK0sz2BBbzv5mu9/dAlt98HxwbG7xLawC3O2xMF9MNWU5FtOziwYG6TDIjNq0O/3ZbOJAxwITIWXEA==
+
+"@intlify/runtime@9.1.9":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/runtime/-/runtime-9.1.9.tgz#2c12ce29518a075629efed0a8ed293ee740cb285"
+ integrity sha512-XgPw8+UlHCiie3fI41HPVa/VDJb3/aSH7bLhY1hJvlvNV713PFtb4p4Jo+rlE0gAoMsMCGcsiT982fImolSltg==
+ dependencies:
+ "@intlify/message-compiler" "9.1.9"
+ "@intlify/message-resolver" "9.1.9"
+ "@intlify/shared" "9.1.9"
+
+"@intlify/shared@9.1.9", "@intlify/shared@^9.1.0":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/shared/-/shared-9.1.9.tgz#0baaf96128b85560666bec784ffb01f6623cc17a"
+ integrity sha512-xKGM1d0EAxdDFCWedcYXOm6V5Pfw/TMudd6/qCdEb4tv0hk9EKeg7lwQF1azE0dP2phvx0yXxrt7UQK+IZjNdw==
+
+"@intlify/vite-plugin-vue-i18n@3.4.0":
+ version "3.4.0"
+ resolved "https://registry.yarnpkg.com/@intlify/vite-plugin-vue-i18n/-/vite-plugin-vue-i18n-3.4.0.tgz#cf15b0d207a843227a5da0ac713f1a5b9d96e40b"
+ integrity sha512-XXcZBgwJ+3FRu11c4ARoY9N00kElPii0/jNZ49qR045Ka7/YGCwb1Ku14BBlMSEHiHDSjLQknLwrJKSQGVZLyA==
+ dependencies:
+ "@intlify/bundle-utils" "^2.2.2"
+ "@intlify/shared" "^9.1.0"
+ "@rollup/pluginutils" "^4.1.0"
+ debug "^4.3.1"
+ fast-glob "^3.2.5"
+ source-map "0.6.1"
+
+"@intlify/vue-devtools@9.1.9":
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/@intlify/vue-devtools/-/vue-devtools-9.1.9.tgz#2be8f4dbe7f7ed4115676eb32348141d411e426b"
+ integrity sha512-YPehH9uL4vZcGXky4Ev5qQIITnHKIvsD2GKGXgqf+05osMUI6WSEQHaN9USRa318Rs8RyyPCiDfmA0hRu3k7og==
+ dependencies:
+ "@intlify/message-resolver" "9.1.9"
+ "@intlify/runtime" "9.1.9"
+ "@intlify/shared" "9.1.9"
+
+"@lezer/common@0.15.12", "@lezer/common@^0.15.0", "@lezer/common@^0.15.5":
+ version "0.15.12"
+ resolved "https://registry.yarnpkg.com/@lezer/common/-/common-0.15.12.tgz#2f21aec551dd5fd7d24eb069f90f54d5bc6ee5e9"
+ integrity sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==
+
+"@lezer/css@^0.15.2":
+ version "0.15.2"
+ resolved "https://registry.yarnpkg.com/@lezer/css/-/css-0.15.2.tgz#e96995da67df90bb4b191aaa8a486349cca5d8e7"
+ integrity sha512-tnMOMZY0Zs6JQeVjqfmREYMV0GnmZR1NitndLWioZMD6mA7VQF/PPKPmJX1f+ZgVZQc5Am0df9mX3aiJnNJlKQ==
+ dependencies:
+ "@lezer/lr" "^0.15.0"
+
+"@lezer/html@^0.15.0":
+ version "0.15.1"
+ resolved "https://registry.yarnpkg.com/@lezer/html/-/html-0.15.1.tgz#973a5a179560d0789bf8737c06e6d143cc211406"
+ integrity sha512-0ZYVhu+RwN6ZMM0gNnTxenRAdoycKc2wvpLfMjP0JkKR0vMxhtuLaIpsq9KW2Mv6l7ux5vdjq8CQ7fKDvia8KA==
+ dependencies:
+ "@lezer/lr" "^0.15.0"
+
+"@lezer/javascript@^0.15.1":
+ version "0.15.3"
+ resolved "https://registry.yarnpkg.com/@lezer/javascript/-/javascript-0.15.3.tgz#833a4c5650bae07805b9af88de6706368844dc55"
+ integrity sha512-8jA2NpOfpWwSPZxRhd9BxK2ZPvGd7nLE3LFTJ5AbMhXAzMHeMjneV6GEVd7dAIee85dtap0jdb6bgOSO0+lfwA==
+ dependencies:
+ "@lezer/lr" "^0.15.0"
+
+"@lezer/json@^0.15.0":
+ version "0.15.0"
+ resolved "https://registry.yarnpkg.com/@lezer/json/-/json-0.15.0.tgz#b96c1161eb8514e05f4eaaec95c68376e76e539f"
+ integrity sha512-OsMjjBkTkeQ15iMCu5U1OiBubRC4V9Wm03zdIlUgNZ20aUPx5DWDRqUc5wG41JXVSj7Lxmo+idlFCfBBdxB8sw==
+ dependencies:
+ "@lezer/lr" "^0.15.0"
+
+"@lezer/lr@^0.15.0":
+ version "0.15.8"
+ resolved "https://registry.yarnpkg.com/@lezer/lr/-/lr-0.15.8.tgz#1564a911e62b0a0f75ca63794a6aa8c5dc63db21"
+ integrity sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==
+ dependencies:
+ "@lezer/common" "^0.15.0"
+
+"@lezer/markdown@^0.15.0":
+ version "0.15.6"
+ resolved "https://registry.yarnpkg.com/@lezer/markdown/-/markdown-0.15.6.tgz#2a826a507399b32176efdc35554397f05227d2aa"
+ integrity sha512-1XXLa4q0ZthryUEfO47ipvZHxNb+sCKoQIMM9dKs5vXZOBbgF2Vah/GL3g26BFIAEc2uCv4VQnI+lSrv58BT3g==
+ dependencies:
+ "@lezer/common" "^0.15.0"
+
+"@nodelib/fs.scandir@2.1.5":
+ version "2.1.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
+ integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==
+ dependencies:
+ "@nodelib/fs.stat" "2.0.5"
+ run-parallel "^1.1.9"
+
+"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2":
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b"
+ integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==
+
+"@nodelib/fs.walk@^1.2.3":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a"
+ integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==
+ dependencies:
+ "@nodelib/fs.scandir" "2.1.5"
+ fastq "^1.6.0"
+
+"@popperjs/core@^2.9.0":
+ version "2.11.5"
+ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.5.tgz#db5a11bf66bdab39569719555b0f76e138d7bd64"
+ integrity sha512-9X2obfABZuDVLCgPK9aX0a/x4jaOEweTTWE2+9sr0Qqqevj2Uv5XorvusThmc9XGYpS9yI+fhh8RTafBtGposw==
+
+"@positron/stack-trace@1.0.0":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@positron/stack-trace/-/stack-trace-1.0.0.tgz#14fcc712a530038ef9be1ce6952315a839f466a8"
+ integrity sha1-FPzHEqUwA475vhzmlSMVqDn0Zqg=
+
+"@quasar/app-vite@1.0.0-beta.13":
+ version "1.0.0-beta.13"
+ resolved "https://registry.yarnpkg.com/@quasar/app-vite/-/app-vite-1.0.0-beta.13.tgz#d71d229002588b5236c12d79b4cc2f52d013e8d1"
+ integrity sha512-l4NMmGiqgGkHDonumORFJlxZ/K1iDFbDdxrxWsmjFvTc4/peN3GdRd+cYEoP9xApp/O+BKno1+XVUVIuQp0Evg==
+ dependencies:
+ "@quasar/fastclick" "1.1.5"
+ "@quasar/vite-plugin" "^1.0.9"
+ "@rollup/pluginutils" "^4.1.2"
+ "@types/cordova" "0.0.34"
+ "@types/express" "^4.17.13"
+ "@vitejs/plugin-vue" "^2.2.0"
+ archiver "^5.3.0"
+ chokidar "^3.5.3"
+ ci-info "^3.3.0"
+ compression "^1.7.4"
+ cross-spawn "^7.0.3"
+ dot-prop "6.0.1"
+ elementtree "0.1.7"
+ esbuild "0.14.29"
+ express "^4.17.3"
+ fast-glob "3.2.11"
+ fs-extra "^10.0.1"
+ html-minifier "^4.0.0"
+ inquirer "^8.2.1"
+ isbinaryfile "^4.0.8"
+ kolorist "^1.5.1"
+ lodash.debounce "^4.0.8"
+ lodash.template "^4.5.0"
+ minimist "^1.2.6"
+ open "^8.4.0"
+ ouch "^2.0.0"
+ postcss "^8.4.4"
+ register-service-worker "^1.7.2"
+ rollup-plugin-visualizer "^5.5.4"
+ sass "1.32.12"
+ semver "^7.3.5"
+ serialize-javascript "^6.0.0"
+ table "^6.8.0"
+ vite "2.9.1"
+ webpack-merge "^5.8.0"
+
+"@quasar/extras@1.13.5":
+ version "1.13.5"
+ resolved "https://registry.yarnpkg.com/@quasar/extras/-/extras-1.13.5.tgz#5be677a800566192ea1bac8b9a796b5b66acd0f5"
+ integrity sha512-H4X3jwqOJpDrn1Pof1mJIpoHdBrZQSR9ZpnQ7RCkJsJccpTyQzs8Q6ehVXfIZrco2kd3UPitRQqEiW7k45ckLg==
+
+"@quasar/fastclick@1.1.5":
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/@quasar/fastclick/-/fastclick-1.1.5.tgz#948e79c44098cced6c3d1645315683ebc29ed834"
+ integrity sha512-p3JKgTjRlJ1YQXbqTw3Bsa4j0mQdt5dq+WfYvyb7MgKGdephHCKdR/kxA5PCTAmJanGJuDKqRdyGYX/hYN4KGw==
+
+"@quasar/vite-plugin@^1.0.9":
+ version "1.0.9"
+ resolved "https://registry.yarnpkg.com/@quasar/vite-plugin/-/vite-plugin-1.0.9.tgz#130c277bf7ef10c964d6ac9fd1cbdf88670fcb3a"
+ integrity sha512-i3tOXLu9SvBG/u/VH/vlPpwzU3QHVtogoSl23Y3i9Clgo6XCtm9mHB0XnQvsLQgNfUTkAKJPKnGwAPQd9uBu1A==
+
+"@rollup/pluginutils@^4.1.0", "@rollup/pluginutils@^4.1.2":
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.0.tgz#a14bbd058fdbba0a5647143b16ed0d86fb60bd08"
+ integrity sha512-2WUyJNRkyH5p487pGnn4tWAsxhEFKN/pT8CMgHshd5H+IXkOnKvKZwsz5ZWz+YCXkleZRAU5kwbfgF8CPfDRqA==
+ dependencies:
+ estree-walker "^2.0.1"
+ picomatch "^2.2.2"
+
+"@tiptap/core@2.0.0-beta.174", "@tiptap/core@^2.0.0-beta.174":
+ version "2.0.0-beta.174"
+ resolved "https://registry.yarnpkg.com/@tiptap/core/-/core-2.0.0-beta.174.tgz#cfdf16b7d7401e4b255dc69147d784f5f537b942"
+ integrity sha512-APQDto40PdvagG1HTwkKlieQS4Vp6GXNe7qgV1Qo2QCgJCLyxc/fXCTghtrOx0CQb+9JT7fjSLZxbSyUFXjx7Q==
+ dependencies:
+ "@types/prosemirror-commands" "^1.0.4"
+ "@types/prosemirror-keymap" "^1.0.4"
+ "@types/prosemirror-model" "^1.16.0"
+ "@types/prosemirror-schema-list" "^1.0.3"
+ "@types/prosemirror-state" "^1.2.8"
+ "@types/prosemirror-transform" "^1.1.5"
+ "@types/prosemirror-view" "^1.23.1"
+ prosemirror-commands "^1.2.1"
+ prosemirror-keymap "^1.1.5"
+ prosemirror-model "^1.16.1"
+ prosemirror-schema-list "^1.1.6"
+ prosemirror-state "^1.3.4"
+ prosemirror-transform "^1.3.3"
+ prosemirror-view "^1.23.6"
+
+"@tiptap/extension-blockquote@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-blockquote/-/extension-blockquote-2.0.0-beta.26.tgz#e5ae4b7bd9376db37407a23e22080c7b11287f3b"
+ integrity sha512-A6yjcYovONJfOjQFk6vDYXswaCdCtCwjL7w9VTB0R2DLTuJvvRt9DWN0IDcMrj5G+aMgDq4GUUTitv+2Y8krDg==
+
+"@tiptap/extension-bold@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-bold/-/extension-bold-2.0.0-beta.26.tgz#aa1c7850df28cec8e0614fde437183bd4ae3e66b"
+ integrity sha512-pnO0I5sEQM3pmowjMGQ74adLzvc6HqGyLyqMizaGMicPu9uTYlSdId+qckYEEgPwPMaEShtv2Vg+ZHs7KVqfcg==
+
+"@tiptap/extension-bubble-menu@^2.0.0-beta.55":
+ version "2.0.0-beta.55"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.0.0-beta.55.tgz#a26ad892cea6af9eeada22235701b06d0921af48"
+ integrity sha512-v32/QnwwRbepdbrho8mTYru1/XNW/rJi3Mjrgo3rrIs67R86aEPmhmdzD3QEQUJhAJkduuwdw8zElmVWqIJQ9w==
+ dependencies:
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+ tippy.js "^6.3.7"
+
+"@tiptap/extension-bullet-list@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-bullet-list/-/extension-bullet-list-2.0.0-beta.26.tgz#b42126d2d984c04041b14037e8d3ec1bcf16e7ec"
+ integrity sha512-1n5HV8gY1tLjPk4x48nva6SZlFHoPlRfF6pqSu9JcJxPO7FUSPxUokuz4swYNe0LRrtykfyNz44dUcxKVhoFow==
+
+"@tiptap/extension-code-block-lowlight@2.0.0-beta.68":
+ version "2.0.0-beta.68"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block-lowlight/-/extension-code-block-lowlight-2.0.0-beta.68.tgz#c0da8c327eb2b49d808e3ead303f6ff6b0203e05"
+ integrity sha512-927vw4DJ5pq3lYOdJbBrCjJZ/YsbsEzWkb7SwRsaztDXcgsF2z8gt6dy7onXMaqm5rDx6eNofLPdhUyLCswW8Q==
+ dependencies:
+ "@tiptap/extension-code-block" "^2.0.0-beta.37"
+ "@types/lowlight" "^0.0.3"
+ lowlight "^1.20.0"
+ prosemirror-model "^1.16.1"
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+
+"@tiptap/extension-code-block@2.0.0-beta.37", "@tiptap/extension-code-block@^2.0.0-beta.37":
+ version "2.0.0-beta.37"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.37.tgz#c07c007248a21d9e0434458fd05c363b7078227f"
+ integrity sha512-mJAM+PHaNoKRYwM3D36lZ51/aoPxxvZNQn3UBnZ6G7l0ZJSgB3JvBEzqK6S8nNFeYIIxGwv4QF6vXe4MG9ie2g==
+ dependencies:
+ prosemirror-state "^1.3.4"
+
+"@tiptap/extension-code@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-code/-/extension-code-2.0.0-beta.26.tgz#bbfa600a252ee2cded6947b56b6c4c33d998e53a"
+ integrity sha512-QcFWdEFfbJ1n5UFFBD17QPPAJ3J5p/b7XV484u0shCzywO7aNPV32QeHy1z0eMoyZtCbOWf6hg/a7Ugv8IwpHw==
+
+"@tiptap/extension-color@2.0.0-beta.9":
+ version "2.0.0-beta.9"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-color/-/extension-color-2.0.0-beta.9.tgz#72045f4dcea34865f42a068b87053da81cccc0b2"
+ integrity sha512-c8zcaNCdwUwbgrutfsG7LD9KH7ZvDVwKOZHbOL4gMSwdH9s+6r1ThRFLEbKgHIJlTa2jd96qoo+lVfj1Qwp7ww==
+
+"@tiptap/extension-document@^2.0.0-beta.15":
+ version "2.0.0-beta.15"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-document/-/extension-document-2.0.0-beta.15.tgz#5d17a0289244a913ab2ef08e8495a1e46950711e"
+ integrity sha512-ypENC+xUYD5m2t+KOKNYqyXnanXd5fxyIyhR1qeEEwwQwMXGNrO3kCH6O4mIDCpy+/WqHvVay2tV5dVsXnvY8w==
+
+"@tiptap/extension-dropcursor@2.0.0-beta.25", "@tiptap/extension-dropcursor@^2.0.0-beta.25":
+ version "2.0.0-beta.25"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-dropcursor/-/extension-dropcursor-2.0.0-beta.25.tgz#962f290a200259533a26194daca5a4b4a53e72d3"
+ integrity sha512-GYf5s6dkZtsDy+TEkrQK6kLbfbitG4qnk02D+FlhlJMI/Nnx8rYCRJbwEHDdqrfX7XwZzULMqqqHvzxZYrEeNg==
+ dependencies:
+ "@types/prosemirror-dropcursor" "^1.0.3"
+ prosemirror-dropcursor "^1.4.0"
+
+"@tiptap/extension-floating-menu@^2.0.0-beta.50":
+ version "2.0.0-beta.50"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-floating-menu/-/extension-floating-menu-2.0.0-beta.50.tgz#e8785d5f051a848ae053ce139581dce96b951a35"
+ integrity sha512-aQu1HtthMIYEPylr6kzioLxMiObLbcgwx9xZzF03KwNnkjQLbjZOeJX2RwSYVpiVgtfPBGOm3N/br6NSYec4yQ==
+ dependencies:
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+ tippy.js "^6.3.7"
+
+"@tiptap/extension-font-family@2.0.0-beta.21":
+ version "2.0.0-beta.21"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-font-family/-/extension-font-family-2.0.0-beta.21.tgz#9e2e863a36a7c9ef881f6a3f984d25dcfc2a2c2e"
+ integrity sha512-5KVCtuEBf1QyZxs/IOL0CPDtB5X3rk8QdDB8fB+UlASa6c/Dq59Uo2aObGOgAWNDdY0Vd9MmuDTvnJKP2LI2Ng==
+
+"@tiptap/extension-gapcursor@2.0.0-beta.34", "@tiptap/extension-gapcursor@^2.0.0-beta.34":
+ version "2.0.0-beta.34"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-2.0.0-beta.34.tgz#0e4971affb1621934422dd5fc4bf2dd7a84f70f7"
+ integrity sha512-Vm8vMWWQ2kJcUOLfB5CEo5pYgyudI7JeeiZvX9ScPmUmgKVYhEpt3EAICY9pUYJ41aAVH35gZLXkUtsz2f9GHw==
+ dependencies:
+ "@types/prosemirror-gapcursor" "^1.0.4"
+ prosemirror-gapcursor "^1.2.1"
+
+"@tiptap/extension-hard-break@2.0.0-beta.30", "@tiptap/extension-hard-break@^2.0.0-beta.30":
+ version "2.0.0-beta.30"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.30.tgz#165494f1194a7bad08907e6d64d349dd15851b72"
+ integrity sha512-X9xj/S+CikrbIE7ccUFVwit5QHEbflnKVxod+4zPwr1cxogFbE9AyLZE2MpYdx3z9LcnTYYi9leBqFrP4T/Olw==
+
+"@tiptap/extension-heading@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-heading/-/extension-heading-2.0.0-beta.26.tgz#112b14b4d488772bda36abbf7cb2bc8aba7c42f5"
+ integrity sha512-nR6W/3rjnZH1Swo7tGBoYsmO6xMvu9MGq6jlm3WVHCB7B3CsrRvCkTwGjVIbKTaZC4bQfx5gvAUpQFvwuU+M5w==
+
+"@tiptap/extension-highlight@2.0.0-beta.33":
+ version "2.0.0-beta.33"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-highlight/-/extension-highlight-2.0.0-beta.33.tgz#530b2f9bee8cb4f03cb587f1cf5ca11a23ebfcf8"
+ integrity sha512-TXyMiCcY5a0w5UFax350xU+T79GKJw2XwJ6Punc6sY2RRRgKaEbN1ZF0JCdQhQvD1ooKImHzCRYR8Pldb0xgfg==
+
+"@tiptap/extension-history@2.0.0-beta.21", "@tiptap/extension-history@^2.0.0-beta.21":
+ version "2.0.0-beta.21"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-2.0.0-beta.21.tgz#5d96a17a83a7130744f0757a3275dd5b11eb1bf7"
+ integrity sha512-0v8Cl30V4dsabdpspLdk+f+lMoIvLFlJN5WRxtc7RRZ5gfJVxPHwooIKdvC51brfh/oJtWFCNMRjhoz0fRaF9A==
+ dependencies:
+ "@types/prosemirror-history" "^1.0.3"
+ prosemirror-history "^1.2.0"
+
+"@tiptap/extension-horizontal-rule@^2.0.0-beta.31":
+ version "2.0.0-beta.31"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.0.0-beta.31.tgz#efb383a6cedbbf4f2175d7d207eaeeba626faab0"
+ integrity sha512-MNc4retfjRgkv3qxqGya0+/BEd1Kmn+oMsCRvE+8x3sXyKIse+vdqMuG5qUcA6np0ZD/9hh1riiQ1GQdgc23Ng==
+ dependencies:
+ prosemirror-state "^1.3.4"
+
+"@tiptap/extension-image@2.0.0-beta.27":
+ version "2.0.0-beta.27"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-image/-/extension-image-2.0.0-beta.27.tgz#62152240cfa7ead03080c38485c1ebda4a603d18"
+ integrity sha512-kdJ7V39yNdVWUco/RBe7WgvFevd81l+pU6+Je9HpelqBBP953wDttzLMuAWQB4AeLv9WhKSlORHiFv2SKsV5NA==
+
+"@tiptap/extension-italic@^2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-italic/-/extension-italic-2.0.0-beta.26.tgz#b00c9e32b81b1bd94eaed24bb2a22e44d5dc54a3"
+ integrity sha512-vejGe2ra4K5ipFOn1U9viqF9X9nPTX8WSJpSOux+9UbKjHpANy7bz69tp66OIi/Wh5L/MMDc+luH/04qfVnpZw==
+
+"@tiptap/extension-list-item@^2.0.0-beta.20":
+ version "2.0.0-beta.20"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-list-item/-/extension-list-item-2.0.0-beta.20.tgz#7169528b226dee4590e013bdf6e5fc6d83729b0f"
+ integrity sha512-5IPEspJt38t9ROj4xLUesOVEYlTT/R9Skd9meHRxJQZX1qrzBICs5PC/WRIsnexrvTBhdxpYgCYjpvpsJBlKuQ==
+
+"@tiptap/extension-mention@2.0.0-beta.95":
+ version "2.0.0-beta.95"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-mention/-/extension-mention-2.0.0-beta.95.tgz#b9e2302afa5ddd0d643bb76b93fba0c391dd3ae6"
+ integrity sha512-AiikYJa33APtMI7c6a4EpPhqAYxHHnub5b9hd62zM1peBp2D2aO1dEIL7cB7O2P8EBZ2pu2QN2mFcCbVlSi0Xw==
+ dependencies:
+ "@tiptap/suggestion" "^2.0.0-beta.90"
+ prosemirror-model "^1.16.1"
+ prosemirror-state "^1.3.4"
+
+"@tiptap/extension-ordered-list@^2.0.0-beta.27":
+ version "2.0.0-beta.27"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.27.tgz#ed48a53a9b012d578613b68375db31e8664bfdc9"
+ integrity sha512-apFDeignxdZb3cA3p1HJu0zw1JgJdBYUBz1r7f99qdNybYuk3I/1MPUvlOuOgvIrBB/wydoyVDP+v9F7QN3tfQ==
+
+"@tiptap/extension-paragraph@^2.0.0-beta.23":
+ version "2.0.0-beta.23"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-paragraph/-/extension-paragraph-2.0.0-beta.23.tgz#2ab77308519494994d7a9e5a4acd14042f45f28c"
+ integrity sha512-VWAxyzecErYWk97Kv/Gkghh97zAQTcaVOisEnYYArZAlyYDaYM48qVssAC/vnRRynP2eQxb1EkppbAxE+bMHAA==
+
+"@tiptap/extension-placeholder@2.0.0-beta.48":
+ version "2.0.0-beta.48"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-placeholder/-/extension-placeholder-2.0.0-beta.48.tgz#aff02fbdcd27772ff503b5f84a2f1d83da846006"
+ integrity sha512-TZNGAHocPoV5DtB8Q5BwQU2uf5vDiwLxbgVHRAIme9P4VsVqa/U1i1TkyN5A5BVdfOzc+E4EOU7cKuyjy7DNyA==
+ dependencies:
+ prosemirror-model "^1.16.1"
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+
+"@tiptap/extension-strike@^2.0.0-beta.27":
+ version "2.0.0-beta.27"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-strike/-/extension-strike-2.0.0-beta.27.tgz#c5187bf3c28837f95a5c0c0617d0dd31c318353d"
+ integrity sha512-2dmCgtesuDdivM/54Q+Y6Tc3JbGz1SkHP6c62piuqBiYLWg3xa16zChZOhfN8szbbQlBgLT6XRTDt3c2Ux+Dug==
+
+"@tiptap/extension-table-cell@2.0.0-beta.20":
+ version "2.0.0-beta.20"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-table-cell/-/extension-table-cell-2.0.0-beta.20.tgz#859456fe8d7276a87161006a3f9b3642b306524c"
+ integrity sha512-IllQyxLQvgm1FAewz3U+DkgNHRthmuVrtUQnG6la45qdUOLCOrpFbRRaQ1LJ/BpbvZ2Xs1o2yAa97BqZOPwovQ==
+
+"@tiptap/extension-table-header@2.0.0-beta.22":
+ version "2.0.0-beta.22"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-table-header/-/extension-table-header-2.0.0-beta.22.tgz#a1672970d8985c383411bb21c18e71fc7e53e262"
+ integrity sha512-nMrghrfl+ZS4EDixs3lgXnHw1Q+ECyTugpRvS36rP7b8GFp3GXm9DfbIAUzwGGfcq1D7DwRnJUDM6ARdWXyw0w==
+
+"@tiptap/extension-table-row@2.0.0-beta.19":
+ version "2.0.0-beta.19"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-table-row/-/extension-table-row-2.0.0-beta.19.tgz#b45e82f29dfcc7405440ba237b069dbb93d1a94a"
+ integrity sha512-ldEVDpIUX7ZqbViTy4c/RfyNGRv++O/r3A/Ivuon1PykaDDTbPlp5JM89FunAD39cLAbo2HKtweqdmzCMlZsqA==
+
+"@tiptap/extension-table@2.0.0-beta.48":
+ version "2.0.0-beta.48"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-table/-/extension-table-2.0.0-beta.48.tgz#4b6adfe358fd38199ef5b29dafc9425fd461d1b3"
+ integrity sha512-Hcx3kOBQyazQ3dV0Oq4zKIl1og4EqUuZ5nEWxwcb8HgxSUYIhAJQ7pujPZiRLfkoFy92oVwmh9KhBRfQqRkUpQ==
+ dependencies:
+ prosemirror-tables "^1.1.1"
+ prosemirror-view "^1.23.6"
+
+"@tiptap/extension-task-item@2.0.0-beta.31":
+ version "2.0.0-beta.31"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-task-item/-/extension-task-item-2.0.0-beta.31.tgz#e73f2f1e264d64ac5902ddd13ad3b63cb043b2c9"
+ integrity sha512-9MCInLAf/l/wDD1N3GgOImemloFARi1l9AJ5acfo+sDjN52yfvaLb//lvLJ6IGz4xGepeAyCME8Qns8UGqG4RQ==
+
+"@tiptap/extension-task-list@2.0.0-beta.26":
+ version "2.0.0-beta.26"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-task-list/-/extension-task-list-2.0.0-beta.26.tgz#75a20795429c40c3b12544483ea63014487d71c7"
+ integrity sha512-7zPpz9eOUCnFyWNDFYPCUJ39gjID+mCI5BuXyXrjJjDfm8wxg/xTgg9+KC6xakczos7DypnhzlRKSs4EFczeUg==
+
+"@tiptap/extension-text-align@2.0.0-beta.29":
+ version "2.0.0-beta.29"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-text-align/-/extension-text-align-2.0.0-beta.29.tgz#31df3d23e7c66caf401a1850a80b88f5cedab0a8"
+ integrity sha512-FNGpl0tVtgG7AK9kVWF/+CGYHta05NpoME4/j6+vhNlZLBNXRA+AKg7W5T8UxmtaC9yGoJsBs2X8M9eCxWVaEQ==
+
+"@tiptap/extension-text-style@2.0.0-beta.23":
+ version "2.0.0-beta.23"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-text-style/-/extension-text-style-2.0.0-beta.23.tgz#7f6ef918c6c21c3dd01b7a8d0f4f04be9960c080"
+ integrity sha512-h/7o//RB4WRrLKWV7E5eFk7tZnfjH0Wt9klixOmvTmus6dm00a7r6wTuaT1GNjfPOgClP3K185lTA5rrdgrxRA==
+
+"@tiptap/extension-text@^2.0.0-beta.15":
+ version "2.0.0-beta.15"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-text/-/extension-text-2.0.0-beta.15.tgz#f08cff1b78f1c6996464dfba1fef8ec1e107617f"
+ integrity sha512-S3j2+HyV2gsXZP8Wg/HA+YVXQsZ3nrXgBM9HmGAxB0ESOO50l7LWfip0f3qcw1oRlh5H3iLPkA6/f7clD2/TFA==
+
+"@tiptap/extension-typography@2.0.0-beta.20":
+ version "2.0.0-beta.20"
+ resolved "https://registry.yarnpkg.com/@tiptap/extension-typography/-/extension-typography-2.0.0-beta.20.tgz#6a721d5222e250aff6d4996acd0f6bc0e183eecc"
+ integrity sha512-rDgl5ssuM/UKTZHHN1QgF5lZTqNI/q7hxpsk7SZhGjuTSbo2mJ38shaKWEsLmofer/xE6+lWtYo8IVkuv998QA==
+
+"@tiptap/starter-kit@2.0.0-beta.183":
+ version "2.0.0-beta.183"
+ resolved "https://registry.yarnpkg.com/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.183.tgz#35ff9f4b236bd321ffdd44c5061514959c1b4b9d"
+ integrity sha512-Lcms6lEfFfdL1oHoATcNKfu1C8+yhuZnI5Pq+U6o2zSslfnUSDf3jgmy6nSoZrrkqvFoXjQk4dxMDFg3giw2Kg==
+ dependencies:
+ "@tiptap/core" "^2.0.0-beta.174"
+ "@tiptap/extension-blockquote" "^2.0.0-beta.26"
+ "@tiptap/extension-bold" "^2.0.0-beta.26"
+ "@tiptap/extension-bullet-list" "^2.0.0-beta.26"
+ "@tiptap/extension-code" "^2.0.0-beta.26"
+ "@tiptap/extension-code-block" "^2.0.0-beta.37"
+ "@tiptap/extension-document" "^2.0.0-beta.15"
+ "@tiptap/extension-dropcursor" "^2.0.0-beta.25"
+ "@tiptap/extension-gapcursor" "^2.0.0-beta.34"
+ "@tiptap/extension-hard-break" "^2.0.0-beta.30"
+ "@tiptap/extension-heading" "^2.0.0-beta.26"
+ "@tiptap/extension-history" "^2.0.0-beta.21"
+ "@tiptap/extension-horizontal-rule" "^2.0.0-beta.31"
+ "@tiptap/extension-italic" "^2.0.0-beta.26"
+ "@tiptap/extension-list-item" "^2.0.0-beta.20"
+ "@tiptap/extension-ordered-list" "^2.0.0-beta.27"
+ "@tiptap/extension-paragraph" "^2.0.0-beta.23"
+ "@tiptap/extension-strike" "^2.0.0-beta.27"
+ "@tiptap/extension-text" "^2.0.0-beta.15"
+
+"@tiptap/suggestion@^2.0.0-beta.90":
+ version "2.0.0-beta.90"
+ resolved "https://registry.yarnpkg.com/@tiptap/suggestion/-/suggestion-2.0.0-beta.90.tgz#16b0cf8732c08ac57d5e7f6cc98b70b0a581bdc6"
+ integrity sha512-L5PPYRatY/75uJJRQx2o/Ce+gzcOkmd81TwLjio9sADV3bRf4DO4WYcQy0AtGe6uNSz78DTL0SUVw4204VjoBw==
+ dependencies:
+ prosemirror-model "^1.16.1"
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+
+"@tiptap/vue-3@2.0.0-beta.90":
+ version "2.0.0-beta.90"
+ resolved "https://registry.yarnpkg.com/@tiptap/vue-3/-/vue-3-2.0.0-beta.90.tgz#139bfc26ce95a47fae88f9d076876e6c3ecbd878"
+ integrity sha512-5QwYpwqomqD1DmEL9DVS0wXuVouzp+2CThXl9QzPxXXsE+pm03yjjk+VhNMiFkA+DaY0hhnrvtWU7n0o00ExRQ==
+ dependencies:
+ "@tiptap/extension-bubble-menu" "^2.0.0-beta.55"
+ "@tiptap/extension-floating-menu" "^2.0.0-beta.50"
+ prosemirror-state "^1.3.4"
+ prosemirror-view "^1.23.6"
+
+"@types/body-parser@*":
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
+ integrity sha512-ALYone6pm6QmwZoAgeyNksccT9Q4AWZQ6PvfwR37GT6r6FWUPguq6sUmNGSMV2Wr761oQoBxwGGa6DR5o1DC9g==
+ dependencies:
+ "@types/connect" "*"
+ "@types/node" "*"
+
+"@types/connect@*":
+ version "3.4.35"
+ resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1"
+ integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==
+ dependencies:
+ "@types/node" "*"
+
+"@types/cordova@0.0.34":
+ version "0.0.34"
+ resolved "https://registry.yarnpkg.com/@types/cordova/-/cordova-0.0.34.tgz#ea7addf74ecec3d7629827a0c39e2c9addc73d04"
+ integrity sha1-6nrd907Ow9dimCegw54smt3HPQQ=
+
+"@types/express-serve-static-core@^4.17.18":
+ version "4.17.28"
+ resolved "https://registry.yarnpkg.com/@types/express-serve-static-core/-/express-serve-static-core-4.17.28.tgz#c47def9f34ec81dc6328d0b1b5303d1ec98d86b8"
+ integrity sha512-P1BJAEAW3E2DJUlkgq4tOL3RyMunoWXqbSCygWo5ZIWTjUgN1YnaXWW4VWl/oc8vs/XoYibEGBKP0uZyF4AHig==
+ dependencies:
+ "@types/node" "*"
+ "@types/qs" "*"
+ "@types/range-parser" "*"
+
+"@types/express@^4.17.13":
+ version "4.17.13"
+ resolved "https://registry.yarnpkg.com/@types/express/-/express-4.17.13.tgz#a76e2995728999bab51a33fabce1d705a3709034"
+ integrity sha512-6bSZTPaTIACxn48l50SR+axgrqm6qXFIxrdAKaG6PaJk3+zuUr35hBlgT7vOmJcum+OEaIBLtHV/qloEAFITeA==
+ dependencies:
+ "@types/body-parser" "*"
+ "@types/express-serve-static-core" "^4.17.18"
+ "@types/qs" "*"
+ "@types/serve-static" "*"
+
+"@types/json5@^0.0.29":
+ version "0.0.29"
+ resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"
+ integrity sha1-7ihweulOEdK4J7y+UnC86n8+ce4=
+
+"@types/lodash@4.14.181":
+ version "4.14.181"
+ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.181.tgz#d1d3740c379fda17ab175165ba04e2d03389385d"
+ integrity sha512-n3tyKthHJbkiWhDZs3DkhkCzt2MexYHXlX0td5iMplyfwketaOeKboEVBqzceH7juqvEg3q5oUoBFxSLu7zFag==
+
+"@types/lowlight@^0.0.3":
+ version "0.0.3"
+ resolved "https://registry.yarnpkg.com/@types/lowlight/-/lowlight-0.0.3.tgz#433b03dd63894dde17860063f4c90a688431194b"
+ integrity sha512-R83q/yPX2nIlo9D3WtSjyUDd57t8s+GVLaL8YIv3k7zMMWpYpOXqjJgrWp80qXUJB/a1t76nTyBpxrv0JNYaEg==
+
+"@types/mime@^1":
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a"
+ integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw==
+
+"@types/node@*":
+ version "17.0.23"
+ resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.23.tgz#3b41a6e643589ac6442bdbd7a4a3ded62f33f7da"
+ integrity sha512-UxDxWn7dl97rKVeVS61vErvw086aCYhDLyvRQZ5Rk65rZKepaFdm53GeqXaKBuOhED4e9uWq34IC3TdSdJJ2Gw==
+
+"@types/orderedmap@*":
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/@types/orderedmap/-/orderedmap-1.0.0.tgz#807455a192bba52cbbb4517044bc82bdbfa8c596"
+ integrity sha512-dxKo80TqYx3YtBipHwA/SdFmMMyLCnP+5mkEqN0eMjcTBzHkiiX0ES118DsjDBjvD+zeSsSU9jULTZ+frog+Gw==
+
+"@types/prosemirror-commands@*", "@types/prosemirror-commands@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-commands/-/prosemirror-commands-1.0.4.tgz#d08551415127d93ae62e7239d30db0b5e7208e22"
+ integrity sha512-utDNYB3EXLjAfYIcRWJe6pn3kcQ5kG4RijbT/0Y/TFOm6yhvYS/D9eJVnijdg9LDjykapcezchxGRqFD5LcyaQ==
+ dependencies:
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+ "@types/prosemirror-view" "*"
+
+"@types/prosemirror-dropcursor@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-dropcursor/-/prosemirror-dropcursor-1.0.3.tgz#49250849b8a0b86e8c29eb1ba70a463e53e46947"
+ integrity sha512-b0/8njnJ4lwyHKcGuCMf3x7r1KjxyugB1R/c2iMCjplsJHSC7UY9+OysqgJR5uUXRekUSGniiLgBtac/lvH6wg==
+ dependencies:
+ "@types/prosemirror-state" "*"
+
+"@types/prosemirror-gapcursor@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-gapcursor/-/prosemirror-gapcursor-1.0.4.tgz#7df7d373edb33ea8da12084bfd462cf84cd69761"
+ integrity sha512-9xKjFIG5947dzerFvkLWp6F53JwrUYoYwh3SgcTFEp8SbSfNNrez/PFYVZKPnoqPoaK5WtTdQTaMwpCV9rXQIg==
+ dependencies:
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+
+"@types/prosemirror-history@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-history/-/prosemirror-history-1.0.3.tgz#f1110efbe758129b5475e466ff077f0a8d9b964f"
+ integrity sha512-5TloMDRavgLjOAKXp1Li8u0xcsspzbT1Cm9F2pwHOkgvQOz1jWQb2VIXO7RVNsFjLBZdIXlyfSLivro3DuMWXg==
+ dependencies:
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+
+"@types/prosemirror-keymap@^1.0.4":
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-keymap/-/prosemirror-keymap-1.0.4.tgz#f73c79810e8d0e0a20d153d84f998f02e5afbc0c"
+ integrity sha512-ycevwkqUh+jEQtPwqO7sWGcm+Sybmhu8MpBsM8DlO3+YTKnXbKA6SDz/+q14q1wK3UA8lHJyfR+v+GPxfUSemg==
+ dependencies:
+ "@types/prosemirror-commands" "*"
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+ "@types/prosemirror-view" "*"
+
+"@types/prosemirror-model@*", "@types/prosemirror-model@^1.16.0":
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-model/-/prosemirror-model-1.16.1.tgz#0ce6c80cd81b398b8a11b1bf7cf695bff3160c9a"
+ integrity sha512-SrrCe2cHlYrQ9o55e2i/c3wt1yRajTTpRLvzfmB+2DWjWEbBLTByVWyjrdpKtQTxAaTeU2aeDGo1iuwl/jF27w==
+ dependencies:
+ "@types/orderedmap" "*"
+
+"@types/prosemirror-schema-list@^1.0.3":
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-schema-list/-/prosemirror-schema-list-1.0.3.tgz#bdf1893a7915fbdc5c49b3cac9368e96213d70de"
+ integrity sha512-uWybOf+M2Ea7rlbs0yLsS4YJYNGXYtn4N+w8HCw3Vvfl6wBAROzlMt0gV/D/VW/7J/LlAjwMezuGe8xi24HzXA==
+ dependencies:
+ "@types/orderedmap" "*"
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+
+"@types/prosemirror-state@*", "@types/prosemirror-state@^1.2.8":
+ version "1.2.8"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-state/-/prosemirror-state-1.2.8.tgz#65080eeec52f63c50bf7034377f07773b4f6b2ac"
+ integrity sha512-mq9uyQWcpu8jeamO6Callrdvf/e1H/aRLR2kZWSpZrPHctEsxWHBbluD/wqVjXBRIOoMHLf6ZvOkrkmGLoCHVA==
+ dependencies:
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-transform" "*"
+ "@types/prosemirror-view" "*"
+
+"@types/prosemirror-transform@*", "@types/prosemirror-transform@^1.1.5":
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-transform/-/prosemirror-transform-1.1.6.tgz#4a06979f656331c46c2725039a57360cc35853af"
+ integrity sha512-7HwXOWc5vZQqIfEUUVAz13lPgBqAWJTv89qEpzAtBFB6hOszFmhsvQ02Jqe2LvKauAoJDa3Qpv/dbJAmgyiTuQ==
+ dependencies:
+ "@types/prosemirror-model" "*"
+
+"@types/prosemirror-view@*", "@types/prosemirror-view@^1.23.1":
+ version "1.23.1"
+ resolved "https://registry.yarnpkg.com/@types/prosemirror-view/-/prosemirror-view-1.23.1.tgz#a9a926bb6b6e6873e3a9d8caa61c32f3402629eb"
+ integrity sha512-6e1B2oKUnhmZPUrsVvYjDqeVjE6jGezygjtoHsAK4ZENAxHzHqy5NT4jUvdPTWjCYeH0t2Y7pSfRPNrPIyQX4A==
+ dependencies:
+ "@types/prosemirror-model" "*"
+ "@types/prosemirror-state" "*"
+ "@types/prosemirror-transform" "*"
+
+"@types/qs@*":
+ version "6.9.7"
+ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
+ integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
+
+"@types/range-parser@*":
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
+ integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
+
+"@types/serve-static@*":
+ version "1.13.10"
+ resolved "https://registry.yarnpkg.com/@types/serve-static/-/serve-static-1.13.10.tgz#f5e0ce8797d2d7cc5ebeda48a52c96c4fa47a8d9"
+ integrity sha512-nCkHGI4w7ZgAdNkrEu0bv+4xNV/XDqW+DydknebMOQwkpDGx8G+HTlj7R7ABI8i8nKxVw0wtKPi1D+lPOkh4YQ==
+ dependencies:
+ "@types/mime" "^1"
+ "@types/node" "*"
+
+"@vitejs/plugin-vue@^2.2.0":
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-2.3.1.tgz#5f286b8d3515381c6d5c8fa8eee5e6335f727e14"
+ integrity sha512-YNzBt8+jt6bSwpt7LP890U1UcTOIZZxfpE5WOJ638PNxSEKOqAi0+FSKS0nVeukfdZ0Ai/H7AFd6k3hayfGZqQ==
+
+"@vue/apollo-option@4.0.0-alpha.16":
+ version "4.0.0-alpha.16"
+ resolved "https://registry.yarnpkg.com/@vue/apollo-option/-/apollo-option-4.0.0-alpha.16.tgz#bc1596da3095b263d738642a8fde86b5c5120d4a"
+ integrity sha512-MkNBzTZfVSV9qj/+EHD+CKxRLh1CwkCn3d6Ww4Ju8tQjkfsIac+im2do6qPeijrrd7z3CYV/SqPghVrz4QzNlA==
+ dependencies:
+ throttle-debounce "^3.0.1"
+
+"@vue/compiler-core@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.31.tgz#d38f06c2cf845742403b523ab4596a3fda152e89"
+ integrity sha512-aKno00qoA4o+V/kR6i/pE+aP+esng5siNAVQ422TkBNM6qA4veXiZbSe8OTXHXquEi/f6Akc+nLfB4JGfe4/WQ==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/shared" "3.2.31"
+ estree-walker "^2.0.2"
+ source-map "^0.6.1"
+
+"@vue/compiler-dom@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.31.tgz#b1b7dfad55c96c8cc2b919cd7eb5fd7e4ddbf00e"
+ integrity sha512-60zIlFfzIDf3u91cqfqy9KhCKIJgPeqxgveH2L+87RcGU/alT6BRrk5JtUso0OibH3O7NXuNOQ0cDc9beT0wrg==
+ dependencies:
+ "@vue/compiler-core" "3.2.31"
+ "@vue/shared" "3.2.31"
+
+"@vue/compiler-sfc@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.31.tgz#d02b29c3fe34d599a52c5ae1c6937b4d69f11c2f"
+ integrity sha512-748adc9msSPGzXgibHiO6T7RWgfnDcVQD+VVwYgSsyyY8Ans64tALHZANrKtOzvkwznV/F4H7OAod/jIlp/dkQ==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.31"
+ "@vue/compiler-dom" "3.2.31"
+ "@vue/compiler-ssr" "3.2.31"
+ "@vue/reactivity-transform" "3.2.31"
+ "@vue/shared" "3.2.31"
+ estree-walker "^2.0.2"
+ magic-string "^0.25.7"
+ postcss "^8.1.10"
+ source-map "^0.6.1"
+
+"@vue/compiler-ssr@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.31.tgz#4fa00f486c9c4580b40a4177871ebbd650ecb99c"
+ integrity sha512-mjN0rqig+A8TVDnsGPYJM5dpbjlXeHUm2oZHZwGyMYiGT/F4fhJf/cXy8QpjnLQK4Y9Et4GWzHn9PS8AHUnSkw==
+ dependencies:
+ "@vue/compiler-dom" "3.2.31"
+ "@vue/shared" "3.2.31"
+
+"@vue/devtools-api@^6.0.0", "@vue/devtools-api@^6.0.0-beta.7", "@vue/devtools-api@^6.1.4":
+ version "6.1.4"
+ resolved "https://registry.yarnpkg.com/@vue/devtools-api/-/devtools-api-6.1.4.tgz#b4aec2f4b4599e11ba774a50c67fa378c9824e53"
+ integrity sha512-IiA0SvDrJEgXvVxjNkHPFfDx6SXw0b/TUkqMcDZWNg9fnCAHbTpoo59YfJ9QLFkwa3raau5vSlRVzMSLDnfdtQ==
+
+"@vue/reactivity-transform@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.31.tgz#0f5b25c24e70edab2b613d5305c465b50fc00911"
+ integrity sha512-uS4l4z/W7wXdI+Va5pgVxBJ345wyGFKvpPYtdSgvfJfX/x2Ymm6ophQlXXB6acqGHtXuBqNyyO3zVp9b1r0MOA==
+ dependencies:
+ "@babel/parser" "^7.16.4"
+ "@vue/compiler-core" "3.2.31"
+ "@vue/shared" "3.2.31"
+ estree-walker "^2.0.2"
+ magic-string "^0.25.7"
+
+"@vue/reactivity@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.31.tgz#fc90aa2cdf695418b79e534783aca90d63a46bbd"
+ integrity sha512-HVr0l211gbhpEKYr2hYe7hRsV91uIVGFYNHj73njbARVGHQvIojkImKMaZNDdoDZOIkMsBc9a1sMqR+WZwfSCw==
+ dependencies:
+ "@vue/shared" "3.2.31"
+
+"@vue/runtime-core@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.31.tgz#9d284c382f5f981b7a7b5971052a1dc4ef39ac7a"
+ integrity sha512-Kcog5XmSY7VHFEMuk4+Gap8gUssYMZ2+w+cmGI6OpZWYOEIcbE0TPzzPHi+8XTzAgx1w/ZxDFcXhZeXN5eKWsA==
+ dependencies:
+ "@vue/reactivity" "3.2.31"
+ "@vue/shared" "3.2.31"
+
+"@vue/runtime-dom@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.31.tgz#79ce01817cb3caf2c9d923f669b738d2d7953eff"
+ integrity sha512-N+o0sICVLScUjfLG7u9u5XCjvmsexAiPt17GNnaWHJUfsKed5e85/A3SWgKxzlxx2SW/Hw7RQxzxbXez9PtY3g==
+ dependencies:
+ "@vue/runtime-core" "3.2.31"
+ "@vue/shared" "3.2.31"
+ csstype "^2.6.8"
+
+"@vue/server-renderer@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.31.tgz#201e9d6ce735847d5989403af81ef80960da7141"
+ integrity sha512-8CN3Zj2HyR2LQQBHZ61HexF5NReqngLT3oahyiVRfSSvak+oAvVmu8iNLSu6XR77Ili2AOpnAt1y8ywjjqtmkg==
+ dependencies:
+ "@vue/compiler-ssr" "3.2.31"
+ "@vue/shared" "3.2.31"
+
+"@vue/shared@3.2.31":
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.31.tgz#c90de7126d833dcd3a4c7534d534be2fb41faa4e"
+ integrity sha512-ymN2pj6zEjiKJZbrf98UM2pfDd6F2H7ksKw7NDt/ZZ1fh5Ei39X5tABugtT03ZRlWd9imccoK0hE8hpjpU7irQ==
+
+"@wry/context@^0.6.0":
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/@wry/context/-/context-0.6.1.tgz#c3c29c0ad622adb00f6a53303c4f965ee06ebeb2"
+ integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/equality@^0.5.0":
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/@wry/equality/-/equality-0.5.2.tgz#72c8a7a7d884dff30b612f4f8464eba26c080e73"
+ integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA==
+ dependencies:
+ tslib "^2.3.0"
+
+"@wry/trie@^0.3.0":
+ version "0.3.1"
+ resolved "https://registry.yarnpkg.com/@wry/trie/-/trie-0.3.1.tgz#2279b790f15032f8bcea7fc944d27988e5b3b139"
+ integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw==
+ dependencies:
+ tslib "^2.3.0"
+
+accepts@~1.3.5, accepts@~1.3.8:
+ version "1.3.8"
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
+ integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
+ dependencies:
+ mime-types "~2.1.34"
+ negotiator "0.6.3"
+
+acorn-jsx@^5.2.0, acorn-jsx@^5.3.1:
+ version "5.3.2"
+ resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937"
+ integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
+
+acorn@^7.1.1, acorn@^7.4.1:
+ version "7.4.1"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa"
+ integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==
+
+acorn@^8.7.0:
+ version "8.7.0"
+ resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.7.0.tgz#90951fde0f8f09df93549481e5fc141445b791cf"
+ integrity sha512-V/LGr1APy+PXIwKebEWrkZPwoeoF+w1jiOBUmuxuiUIaOHtob8Qc9BTrYo7VuI5fR8tqsy+buA2WFooR5olqvQ==
+
+ajv@^6.10.0, ajv@^6.12.4:
+ version "6.12.6"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4"
+ integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ fast-json-stable-stringify "^2.0.0"
+ json-schema-traverse "^0.4.1"
+ uri-js "^4.2.2"
+
+ajv@^8.0.1:
+ version "8.11.0"
+ resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.11.0.tgz#977e91dd96ca669f54a11e23e378e33b884a565f"
+ integrity sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==
+ dependencies:
+ fast-deep-equal "^3.1.1"
+ json-schema-traverse "^1.0.0"
+ require-from-string "^2.0.2"
+ uri-js "^4.2.2"
+
+ansi-escapes@^4.2.1:
+ version "4.3.2"
+ resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e"
+ integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==
+ dependencies:
+ type-fest "^0.21.3"
+
+ansi-regex@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
+ integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
+
+ansi-styles@^4.0.0, ansi-styles@^4.1.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
+ integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
+ dependencies:
+ color-convert "^2.0.1"
+
+anymatch@~3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
+ integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
+ dependencies:
+ normalize-path "^3.0.0"
+ picomatch "^2.0.4"
+
+apollo-upload-client@17.0.0:
+ version "17.0.0"
+ resolved "https://registry.yarnpkg.com/apollo-upload-client/-/apollo-upload-client-17.0.0.tgz#d9baaff8d14e54510de9f2855b487e75ca63b392"
+ integrity sha512-pue33bWVbdlXAGFPkgz53TTmxVMrKeQr0mdRcftNY+PoHIdbGZD0hoaXHvO6OePJAkFz7OiCFUf98p1G/9+Ykw==
+ dependencies:
+ extract-files "^11.0.0"
+
+archiver-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/archiver-utils/-/archiver-utils-2.1.0.tgz#e8a460e94b693c3e3da182a098ca6285ba9249e2"
+ integrity sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==
+ dependencies:
+ glob "^7.1.4"
+ graceful-fs "^4.2.0"
+ lazystream "^1.0.0"
+ lodash.defaults "^4.2.0"
+ lodash.difference "^4.5.0"
+ lodash.flatten "^4.4.0"
+ lodash.isplainobject "^4.0.6"
+ lodash.union "^4.6.0"
+ normalize-path "^3.0.0"
+ readable-stream "^2.0.0"
+
+archiver@^5.3.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/archiver/-/archiver-5.3.0.tgz#dd3e097624481741df626267564f7dd8640a45ba"
+ integrity sha512-iUw+oDwK0fgNpvveEsdQ0Ase6IIKztBJU2U0E9MzszMfmVVUyv1QJhS2ITW9ZCqx8dktAxVAjWWkKehuZE8OPg==
+ dependencies:
+ archiver-utils "^2.1.0"
+ async "^3.2.0"
+ buffer-crc32 "^0.2.1"
+ readable-stream "^3.6.0"
+ readdir-glob "^1.0.0"
+ tar-stream "^2.2.0"
+ zip-stream "^4.1.0"
+
+argparse@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
+ integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
+
+array-flatten@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
+ integrity sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=
+
+array-includes@^3.1.4:
+ version "3.1.4"
+ resolved "https://registry.yarnpkg.com/array-includes/-/array-includes-3.1.4.tgz#f5b493162c760f3539631f005ba2bb46acb45ba9"
+ integrity sha512-ZTNSQkmWumEbiHO2GF4GmWxYVTiQyJy2XOTa15sdQSrvKn7l+180egQMqlrMOUMCyLMD7pmyQe4mMDUT6Behrw==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.1"
+ get-intrinsic "^1.1.1"
+ is-string "^1.0.7"
+
+array.prototype.flat@^1.2.5:
+ version "1.2.5"
+ resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.5.tgz#07e0975d84bbc7c48cd1879d609e682598d33e13"
+ integrity sha512-KaYU+S+ndVqyUnignHftkwc58o3uVU1jzczILJ1tN2YaIZpFIKBiP/x/j97E5MVPsaCloPbqWLB/8qCTVvT2qg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.0"
+
+asap@~2.0.3:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46"
+ integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
+
+assert-never@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/assert-never/-/assert-never-1.2.1.tgz#11f0e363bf146205fb08193b5c7b90f4d1cf44fe"
+ integrity sha512-TaTivMB6pYI1kXwrFlEhLeGfOqoDNdTxjCdwRfFFkEA30Eu+k48W34nlok2EYWJfFFzqaEmichdNM7th6M5HNw==
+
+astral-regex@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+ integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
+async@^3.2.0:
+ version "3.2.3"
+ resolved "https://registry.yarnpkg.com/async/-/async-3.2.3.tgz#ac53dafd3f4720ee9e8a160628f18ea91df196c9"
+ integrity sha512-spZRyzKL5l5BZQrr/6m/SqFdBN0q3OCI0f9rjfBzCMBIP4p75P620rR3gTmaksNOhmzgdxcaxdNfMy6anrbM0g==
+
+autoprefixer@10.4.4:
+ version "10.4.4"
+ resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.4.4.tgz#3e85a245b32da876a893d3ac2ea19f01e7ea5a1e"
+ integrity sha512-Tm8JxsB286VweiZ5F0anmbyGiNI3v3wGv3mz9W+cxEDYB/6jbnj6GM9H9mK3wIL8ftgl+C07Lcwb8PG5PCCPzA==
+ dependencies:
+ browserslist "^4.20.2"
+ caniuse-lite "^1.0.30001317"
+ fraction.js "^4.2.0"
+ normalize-range "^0.1.2"
+ picocolors "^1.0.0"
+ postcss-value-parser "^4.2.0"
+
+babel-walk@3.0.0-canary-5:
+ version "3.0.0-canary-5"
+ resolved "https://registry.yarnpkg.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz#f66ecd7298357aee44955f235a6ef54219104b11"
+ integrity sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==
+ dependencies:
+ "@babel/types" "^7.9.6"
+
+balanced-match@^1.0.0:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
+ integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
+
+base64-js@^1.3.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
+ integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+
+binary-extensions@^2.0.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
+ integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
+
+bl@^4.0.3, bl@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+ integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+ dependencies:
+ buffer "^5.5.0"
+ inherits "^2.0.4"
+ readable-stream "^3.4.0"
+
+body-parser@1.19.2:
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.19.2.tgz#4714ccd9c157d44797b8b5607d72c0b89952f26e"
+ integrity sha512-SAAwOxgoCKMGs9uUAUFHygfLAyaniaoun6I8mFY9pRAJL9+Kec34aU+oIjDhTycub1jozEfEwx1W1IuOYxVSFw==
+ dependencies:
+ bytes "3.1.2"
+ content-type "~1.0.4"
+ debug "2.6.9"
+ depd "~1.1.2"
+ http-errors "1.8.1"
+ iconv-lite "0.4.24"
+ on-finished "~2.3.0"
+ qs "6.9.7"
+ raw-body "2.4.3"
+ type-is "~1.6.18"
+
+brace-expansion@^1.1.7:
+ version "1.1.11"
+ resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
+ integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
+ dependencies:
+ balanced-match "^1.0.0"
+ concat-map "0.0.1"
+
+braces@^3.0.2, braces@~3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107"
+ integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==
+ dependencies:
+ fill-range "^7.0.1"
+
+browser-fs-access@0.26.1:
+ version "0.26.1"
+ resolved "https://registry.yarnpkg.com/browser-fs-access/-/browser-fs-access-0.26.1.tgz#76ad32906e2efb67dff2be2ee276479275386bca"
+ integrity sha512-TMloUX9rl/MWe0CniUj5aDKzLuspvOzsxZhRFhG1LThtYqqwk2YaSzzDXhwMbX++fUJqs57+Z7kDocR4zsEGwQ==
+
+browserslist@^4.20.2:
+ version "4.20.2"
+ resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.20.2.tgz#567b41508757ecd904dab4d1c646c612cd3d4f88"
+ integrity sha512-CQOBCqp/9pDvDbx3xfMi+86pr4KXIf2FDkTTdeuYw8OxS9t898LA1Khq57gtufFILXpfgsSx5woNgsBgvGjpsA==
+ dependencies:
+ caniuse-lite "^1.0.30001317"
+ electron-to-chromium "^1.4.84"
+ escalade "^3.1.1"
+ node-releases "^2.0.2"
+ picocolors "^1.0.0"
+
+buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
+ version "0.2.13"
+ resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242"
+ integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=
+
+buffer@^5.5.0:
+ version "5.7.1"
+ resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+ integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+ dependencies:
+ base64-js "^1.3.1"
+ ieee754 "^1.1.13"
+
+builtins@^4.0.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/builtins/-/builtins-4.1.0.tgz#1edd016dd91ce771a1ed6fc3b2b71fb918953250"
+ integrity sha512-1bPRZQtmKaO6h7qV1YHXNtr6nCK28k0Zo95KM4dXfILcZZwoHJBN1m3lfLv9LPkcOZlrSr+J1bzMaZFO98Yq0w==
+ dependencies:
+ semver "^7.0.0"
+
+bytes@3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048"
+ integrity sha1-0ygVQE1olpn4Wk6k+odV3ROpYEg=
+
+bytes@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
+ integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
+
+call-bind@^1.0.0, call-bind@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
+ integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
+ dependencies:
+ function-bind "^1.1.1"
+ get-intrinsic "^1.0.2"
+
+callsites@^3.0.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73"
+ integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
+
+camel-case@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73"
+ integrity sha1-yjw2iKTpzzpM2nd9xNy8cTJJz3M=
+ dependencies:
+ no-case "^2.2.0"
+ upper-case "^1.1.1"
+
+caniuse-lite@^1.0.30001317:
+ version "1.0.30001325"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001325.tgz#2b4ad19b77aa36f61f2eaf72e636d7481d55e606"
+ integrity sha512-sB1bZHjseSjDtijV1Hb7PB2Zd58Kyx+n/9EotvZ4Qcz2K3d0lWB8dB4nb8wN/TsOGFq3UuAm0zQZNQ4SoR7TrQ==
+
+chalk@^4.0.0, chalk@^4.1.0, chalk@^4.1.1:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
+ integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==
+ dependencies:
+ ansi-styles "^4.1.0"
+ supports-color "^7.1.0"
+
+character-parser@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/character-parser/-/character-parser-2.2.0.tgz#c7ce28f36d4bcd9744e5ffc2c5fcde1c73261fc0"
+ integrity sha1-x84o821LzZdE5f/CxfzeHHMmH8A=
+ dependencies:
+ is-regex "^1.0.3"
+
+chardet@^0.7.0:
+ version "0.7.0"
+ resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
+ integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
+
+"chokidar@>=3.0.0 <4.0.0", chokidar@^3.5.3:
+ version "3.5.3"
+ resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd"
+ integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==
+ dependencies:
+ anymatch "~3.1.2"
+ braces "~3.0.2"
+ glob-parent "~5.1.2"
+ is-binary-path "~2.1.0"
+ is-glob "~4.0.1"
+ normalize-path "~3.0.0"
+ readdirp "~3.6.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+ci-info@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/ci-info/-/ci-info-3.3.0.tgz#b4ed1fb6818dea4803a55c623041f9165d2066b2"
+ integrity sha512-riT/3vI5YpVH6/qomlDnJow6TBee2PBKSEpx3O32EGPYbWGIRsIlGRms3Sm74wYE1JMo8RnO04Hb12+v1J5ICw==
+
+clean-css@^4.2.1:
+ version "4.2.4"
+ resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178"
+ integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A==
+ dependencies:
+ source-map "~0.6.0"
+
+cli-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+ integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
+ dependencies:
+ restore-cursor "^3.1.0"
+
+cli-spinners@^2.5.0:
+ version "2.6.1"
+ resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d"
+ integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g==
+
+cli-width@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6"
+ integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw==
+
+clipboard@2.0.10:
+ version "2.0.10"
+ resolved "https://registry.yarnpkg.com/clipboard/-/clipboard-2.0.10.tgz#e61f6f7139ac5044c58c0484dcac9fb2a918bfd6"
+ integrity sha512-cz3m2YVwFz95qSEbCDi2fzLN/epEN9zXBvfgAoGkvGOJZATMl9gtTDVOtBYkx2ODUJl2kvmud7n32sV2BpYR4g==
+ dependencies:
+ good-listener "^1.2.2"
+ select "^1.1.2"
+ tiny-emitter "^2.0.0"
+
+cliui@^7.0.2:
+ version "7.0.4"
+ resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f"
+ integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==
+ dependencies:
+ string-width "^4.2.0"
+ strip-ansi "^6.0.0"
+ wrap-ansi "^7.0.0"
+
+clone-deep@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387"
+ integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==
+ dependencies:
+ is-plain-object "^2.0.4"
+ kind-of "^6.0.2"
+ shallow-clone "^3.0.0"
+
+clone@^1.0.2:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
+ integrity sha1-2jCcwmPfFZlMaIypAheco8fNfH4=
+
+color-convert@^2.0.1:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
+ integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
+ dependencies:
+ color-name "~1.1.4"
+
+color-name@~1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
+ integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+
+commander@^2.19.0:
+ version "2.20.3"
+ resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
+ integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==
+
+compress-commons@^4.1.0:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/compress-commons/-/compress-commons-4.1.1.tgz#df2a09a7ed17447642bad10a85cc9a19e5c42a7d"
+ integrity sha512-QLdDLCKNV2dtoTorqgxngQCMA+gWXkM/Nwu7FpeBhk/RdkzimqC3jueb/FDmaZeXh+uby1jkBqE3xArsLBE5wQ==
+ dependencies:
+ buffer-crc32 "^0.2.13"
+ crc32-stream "^4.0.2"
+ normalize-path "^3.0.0"
+ readable-stream "^3.6.0"
+
+compressible@~2.0.16:
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba"
+ integrity sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==
+ dependencies:
+ mime-db ">= 1.43.0 < 2"
+
+compression@^1.7.4:
+ version "1.7.4"
+ resolved "https://registry.yarnpkg.com/compression/-/compression-1.7.4.tgz#95523eff170ca57c29a0ca41e6fe131f41e5bb8f"
+ integrity sha512-jaSIDzP9pZVS4ZfQ+TzvtiWhdpFhE2RDHz8QJkpX9SIpLq88VueF5jJw6t+6CUQcAoA6t+x89MLrWAqpfDE8iQ==
+ dependencies:
+ accepts "~1.3.5"
+ bytes "3.0.0"
+ compressible "~2.0.16"
+ debug "2.6.9"
+ on-headers "~1.0.2"
+ safe-buffer "5.1.2"
+ vary "~1.1.2"
+
+concat-map@0.0.1:
+ version "0.0.1"
+ resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
+ integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=
+
+constantinople@^4.0.1:
+ version "4.0.1"
+ resolved "https://registry.yarnpkg.com/constantinople/-/constantinople-4.0.1.tgz#0def113fa0e4dc8de83331a5cf79c8b325213151"
+ integrity sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==
+ dependencies:
+ "@babel/parser" "^7.6.0"
+ "@babel/types" "^7.6.1"
+
+content-disposition@0.5.4:
+ version "0.5.4"
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
+ integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
+ dependencies:
+ safe-buffer "5.2.1"
+
+content-type@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
+ integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
+
+cookie-signature@1.0.6:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.6.tgz#e303a882b342cc3ee8ca513a79999734dab3ae2c"
+ integrity sha1-4wOogrNCzD7oylE6eZmXNNqzriw=
+
+cookie@0.4.2:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
+ integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
+
+core-util-is@~1.0.0:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.3.tgz#a6042d3634c2b27e9328f837b965fac83808db85"
+ integrity sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==
+
+crc-32@^1.2.0:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/crc-32/-/crc-32-1.2.2.tgz#3cad35a934b8bf71f25ca524b6da51fb7eace2ff"
+ integrity sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==
+
+crc32-stream@^4.0.2:
+ version "4.0.2"
+ resolved "https://registry.yarnpkg.com/crc32-stream/-/crc32-stream-4.0.2.tgz#c922ad22b38395abe9d3870f02fa8134ed709007"
+ integrity sha512-DxFZ/Hk473b/muq1VJ///PMNLj0ZMnzye9thBpmjpJKCc5eMgB95aK8zCGrGfQ90cWo561Te6HK9D+j4KPdM6w==
+ dependencies:
+ crc-32 "^1.2.0"
+ readable-stream "^3.4.0"
+
+crelt@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/crelt/-/crelt-1.0.5.tgz#57c0d52af8c859e354bace1883eb2e1eb182bb94"
+ integrity sha512-+BO9wPPi+DWTDcNYhr/W90myha8ptzftZT+LwcmUbbok0rcP/fequmFYCw8NMoH7pkAZQzU78b3kYrlua5a9eA==
+
+cross-spawn@^7.0.2, cross-spawn@^7.0.3:
+ version "7.0.3"
+ resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
+ integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
+ dependencies:
+ path-key "^3.1.0"
+ shebang-command "^2.0.0"
+ which "^2.0.1"
+
+csstype@^2.6.8:
+ version "2.6.20"
+ resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.20.tgz#9229c65ea0b260cf4d3d997cb06288e36a8d6dda"
+ integrity sha512-/WwNkdXfckNgw6S5R125rrW8ez139lBHWouiBvX8dfMFtcn6V81REDqnH7+CRpRipfYlyU1CmOnOxrmGcFOjeA==
+
+debug@2.6.9, debug@^2.6.9:
+ version "2.6.9"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
+ integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
+ dependencies:
+ ms "2.0.0"
+
+debug@^3.2.7:
+ version "3.2.7"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
+ integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
+ dependencies:
+ ms "^2.1.1"
+
+debug@^4.1.1, debug@^4.3.1, debug@^4.3.2:
+ version "4.3.4"
+ resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+ integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+ dependencies:
+ ms "2.1.2"
+
+deep-is@^0.1.3:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
+ integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
+
+defaults@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.3.tgz#c656051e9817d9ff08ed881477f3fe4019f3ef7d"
+ integrity sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=
+ dependencies:
+ clone "^1.0.2"
+
+define-lazy-prop@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz#3f7ae421129bcaaac9bc74905c98a0009ec9ee7f"
+ integrity sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==
+
+define-properties@^1.1.3:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
+ integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
+ dependencies:
+ object-keys "^1.0.12"
+
+delegate@^3.1.2:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
+ integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
+
+depd@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/depd/-/depd-1.1.2.tgz#9bcd52e14c097763e749b274c4346ed2e560b5a9"
+ integrity sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=
+
+destroy@~1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80"
+ integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=
+
+doctrine@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d"
+ integrity sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==
+ dependencies:
+ esutils "^2.0.2"
+
+doctrine@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-3.0.0.tgz#addebead72a6574db783639dc87a121773973961"
+ integrity sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==
+ dependencies:
+ esutils "^2.0.2"
+
+doctypes@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/doctypes/-/doctypes-1.1.0.tgz#ea80b106a87538774e8a3a4a5afe293de489e0a9"
+ integrity sha1-6oCxBqh1OHdOijpKWv4pPeSJ4Kk=
+
+dot-prop@6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-6.0.1.tgz#fc26b3cf142b9e59b74dbd39ed66ce620c681083"
+ integrity sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==
+ dependencies:
+ is-obj "^2.0.0"
+
+ee-first@1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
+ integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
+
+ejs@^2.3.1:
+ version "2.7.4"
+ resolved "https://registry.yarnpkg.com/ejs/-/ejs-2.7.4.tgz#48661287573dcc53e366c7a1ae52c3a120eec9ba"
+ integrity sha512-7vmuyh5+kuUyJKePhQfRQBhXV5Ce+RnaeeQArKu1EAMpL3WbgMt5WG6uQZpEVvYSSsxMXRKOewtDk9RaTKXRlA==
+
+electron-to-chromium@^1.4.84:
+ version "1.4.106"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.106.tgz#e7a3bfa9d745dd9b9e597616cb17283cc349781a"
+ integrity sha512-ZYfpVLULm67K7CaaGP7DmjyeMY4naxsbTy+syVVxT6QHI1Ww8XbJjmr9fDckrhq44WzCrcC5kH3zGpdusxwwqg==
+
+elementtree@0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/elementtree/-/elementtree-0.1.7.tgz#9ac91be6e52fb6e6244c4e54a4ac3ed8ae8e29c0"
+ integrity sha1-mskb5uUvtuYkTE5UpKw+2K6OKcA=
+ dependencies:
+ sax "1.1.4"
+
+emoji-regex@^8.0.0:
+ version "8.0.0"
+ resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
+ integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
+
+encodeurl@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59"
+ integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
+
+end-of-stream@^1.4.1:
+ version "1.4.4"
+ resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0"
+ integrity sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==
+ dependencies:
+ once "^1.4.0"
+
+es-abstract@^1.19.0, es-abstract@^1.19.1:
+ version "1.19.2"
+ resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.19.2.tgz#8f7b696d8f15b167ae3640b4060670f3d054143f"
+ integrity sha512-gfSBJoZdlL2xRiOCy0g8gLMryhoe1TlimjzU99L/31Z8QEGIhVQI+EWwt5lT+AuU9SnorVupXFqqOGqGfsyO6w==
+ dependencies:
+ call-bind "^1.0.2"
+ es-to-primitive "^1.2.1"
+ function-bind "^1.1.1"
+ get-intrinsic "^1.1.1"
+ get-symbol-description "^1.0.0"
+ has "^1.0.3"
+ has-symbols "^1.0.3"
+ internal-slot "^1.0.3"
+ is-callable "^1.2.4"
+ is-negative-zero "^2.0.2"
+ is-regex "^1.1.4"
+ is-shared-array-buffer "^1.0.1"
+ is-string "^1.0.7"
+ is-weakref "^1.0.2"
+ object-inspect "^1.12.0"
+ object-keys "^1.1.1"
+ object.assign "^4.1.2"
+ string.prototype.trimend "^1.0.4"
+ string.prototype.trimstart "^1.0.4"
+ unbox-primitive "^1.0.1"
+
+es-to-primitive@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
+ integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
+ dependencies:
+ is-callable "^1.1.4"
+ is-date-object "^1.0.1"
+ is-symbol "^1.0.2"
+
+esbuild-android-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.29.tgz#c0960c84c9b832bade20831515e89d32549d4769"
+ integrity sha512-tJuaN33SVZyiHxRaVTo1pwW+rn3qetJX/SRuc/83rrKYtyZG0XfsQ1ao1nEudIt9w37ZSNXR236xEfm2C43sbw==
+
+esbuild-android-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-android-64/-/esbuild-android-64-0.14.34.tgz#46bc4327dd0809937912346244eaffdb9bfc980d"
+ integrity sha512-XfxcfJqmMYsT/LXqrptzFxmaR3GWzXHDLdFNIhm6S00zPaQF1TBBWm+9t0RZ6LRR7iwH57DPjaOeW20vMqI4Yw==
+
+esbuild-android-arm64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.29.tgz#8eceb3abe5abde5489d6a5cbe6a7c1044f71115f"
+ integrity sha512-D74dCv6yYnMTlofVy1JKiLM5JdVSQd60/rQfJSDP9qvRAI0laPXIG/IXY1RG6jobmFMUfL38PbFnCqyI/6fPXg==
+
+esbuild-android-arm64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-android-arm64/-/esbuild-android-arm64-0.14.34.tgz#a3f7e1ad84b8a7dcb39b5e132768b56ee7133656"
+ integrity sha512-T02+NXTmSRL1Mc6puz+R9CB54rSPICkXKq6+tw8B6vxZFnCPzbJxgwIX4kcluz9p8nYBjF3+lSilTGWb7+Xgew==
+
+esbuild-darwin-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.29.tgz#26f3f14102310ecb8f2d9351c5b7a47a60d2cc8a"
+ integrity sha512-+CJaRvfTkzs9t+CjGa0Oa28WoXa7EeLutQhxus+fFcu0MHhsBhlmeWHac3Cc/Sf/xPi1b2ccDFfzGYJCfV0RrA==
+
+esbuild-darwin-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.34.tgz#a0e4ab7a0cddf76761f1fb5d6bf552a376beb16e"
+ integrity sha512-pLRip2Bh4Ng7Bf6AMgCrSp3pPe/qZyf11h5Qo2mOfJqLWzSVjxrXW+CFRJfrOVP7TCnh/gmZSM2AFdCPB72vtw==
+
+esbuild-darwin-arm64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.29.tgz#6d2d89dfd937992649239711ed5b86e51b13bd89"
+ integrity sha512-5Wgz/+zK+8X2ZW7vIbwoZ613Vfr4A8HmIs1XdzRmdC1kG0n5EG5fvKk/jUxhNlrYPx1gSY7XadQ3l4xAManPSw==
+
+esbuild-darwin-arm64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.14.34.tgz#54c35461f82f83a7f5169d9a6a54201798977b07"
+ integrity sha512-vpidSJEBxx6lf1NWgXC+DCmGqesJuZ5Y8aQVVsaoO4i8tRXbXb0whChRvop/zd3nfNM4dIl5EXAky0knRX5I6w==
+
+esbuild-freebsd-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.29.tgz#2cb41a0765d0040f0838280a213c81bbe62d6394"
+ integrity sha512-VTfS7Bm9QA12JK1YXF8+WyYOfvD7WMpbArtDj6bGJ5Sy5xp01c/q70Arkn596aGcGj0TvQRplaaCIrfBG1Wdtg==
+
+esbuild-freebsd-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-64/-/esbuild-freebsd-64-0.14.34.tgz#aebb50248f5874d04ffeab2db8ee1ed6037e2654"
+ integrity sha512-m0HBjePhe0hAQJgtMRMNV9kMgIyV4/qSnzPx42kRMQBcPhgjAq1JRu4Il26czC+9FgpMbFkUktb07f/Lwnc6CA==
+
+esbuild-freebsd-arm64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.29.tgz#e1b79fbb63eaeff324cf05519efa7ff12ce4586a"
+ integrity sha512-WP5L4ejwLWWvd3Fo2J5mlXvG3zQHaw5N1KxFGnUc4+2ZFZknP0ST63i0IQhpJLgEJwnQpXv2uZlU1iWZjFqEIg==
+
+esbuild-freebsd-arm64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-freebsd-arm64/-/esbuild-freebsd-arm64-0.14.34.tgz#09bef288e29f18b38b0c70a9827b6ee718e36c7f"
+ integrity sha512-cpRc2B94L1KvMPPYB4D6G39jLqpKlD3noAMY4/e86iXXXkhUYJJEtTuyNFTa9JRpWM0xCAp4mxjHjoIiLuoCLA==
+
+esbuild-linux-32@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.29.tgz#a4a5a0b165b15081bc3227986e10dd4943edb7d6"
+ integrity sha512-4myeOvFmQBWdI2U1dEBe2DCSpaZyjdQtmjUY11Zu2eQg4ynqLb8Y5mNjNU9UN063aVsCYYfbs8jbken/PjyidA==
+
+esbuild-linux-32@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-32/-/esbuild-linux-32-0.14.34.tgz#67790061758e008e919e65bbc34549f55dadaca7"
+ integrity sha512-8nQaEaoW7MH/K/RlozJa+lE1ejHIr8fuPIHhc513UebRav7HtXgQvxHQ6VZRUkWtep23M6dd7UqhwO1tMOfzQQ==
+
+esbuild-linux-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.29.tgz#4c450088c84f8bfd22c51d116f59416864b85481"
+ integrity sha512-iaEuLhssReAKE7HMwxwFJFn7D/EXEs43fFy5CJeA4DGmU6JHh0qVJD2p/UP46DvUXLRKXsXw0i+kv5TdJ1w5pg==
+
+esbuild-linux-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-64/-/esbuild-linux-64-0.14.34.tgz#b9b19d4ac07e37495dd2508ec843418aa71c98d6"
+ integrity sha512-Y3of4qQoLLlAgf042MlrY1P+7PnN9zWj8nVtw9XQG5hcLOZLz7IKpU35oeu7n4wvyaZHwvQqDJ93gRLqdJekcQ==
+
+esbuild-linux-arm64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.29.tgz#d1a23993b26cb1f63f740329b2fc09218e498bd1"
+ integrity sha512-KYf7s8wDfUy+kjKymW3twyGT14OABjGHRkm9gPJ0z4BuvqljfOOUbq9qT3JYFnZJHOgkr29atT//hcdD0Pi7Mw==
+
+esbuild-linux-arm64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm64/-/esbuild-linux-arm64-0.14.34.tgz#fd84b11a6ccfe9e83e00d0c45890e9fb3a7248c1"
+ integrity sha512-IlWaGtj9ir7+Nrume1DGcyzBDlK8GcnJq0ANKwcI9pVw8tqr+6GD0eqyF9SF1mR8UmAp+odrx1H5NdR2cHdFHA==
+
+esbuild-linux-arm@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.29.tgz#a7e2fea558525eab812b1fe27d7a2659cd1bb723"
+ integrity sha512-OXa9D9QL1hwrAnYYAHt/cXAuSCmoSqYfTW/0CEY0LgJNyTxJKtqc5mlwjAZAvgyjmha0auS/sQ0bXfGf2wAokQ==
+
+esbuild-linux-arm@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-arm/-/esbuild-linux-arm-0.14.34.tgz#c89d4714b05265a315a97c8933508cc73950e683"
+ integrity sha512-9lpq1NcJqssAF7alCO6zL3gvBVVt/lKw4oetUM7OgNnRX0OWpB+ZIO9FwCrSj/dMdmgDhPLf+119zB8QxSMmAg==
+
+esbuild-linux-mips64le@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.29.tgz#e708c527f0785574e400828cdbed3d9b17b5ddff"
+ integrity sha512-05jPtWQMsZ1aMGfHOvnR5KrTvigPbU35BtuItSSWLI2sJu5VrM8Pr9Owym4wPvA4153DFcOJ1EPN/2ujcDt54g==
+
+esbuild-linux-mips64le@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-mips64le/-/esbuild-linux-mips64le-0.14.34.tgz#d60752c3fb1260dd0737532af2de2a9521656456"
+ integrity sha512-k3or+01Rska1AjUyNjA4buEwB51eyN/xPQAoOx1CjzAQC3l8rpjUDw55kXyL63O/1MUi4ISvtNtl8gLwdyEcxw==
+
+esbuild-linux-ppc64le@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.29.tgz#0137d1b38beae36a57176ef45e90740e734df502"
+ integrity sha512-FYhBqn4Ir9xG+f6B5VIQVbRuM4S6qwy29dDNYFPoxLRnwTEKToIYIUESN1qHyUmIbfO0YB4phG2JDV2JDN9Kgw==
+
+esbuild-linux-ppc64le@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-ppc64le/-/esbuild-linux-ppc64le-0.14.34.tgz#f4c6229269956564f0c6f9825f5e717c2cfc22b3"
+ integrity sha512-+qxb8M9FfM2CJaVU7GgYpJOHM1ngQOx+/VrtBjb4C8oVqaPcESCeg2anjl+HRZy8VpYc71q/iBYausPPbJ+Keg==
+
+esbuild-linux-riscv64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.29.tgz#a2f73235347a58029dcacf0fb91c9eb8bebc8abb"
+ integrity sha512-eqZMqPehkb4nZcffnuOpXJQdGURGd6GXQ4ZsDHSWyIUaA+V4FpMBe+5zMPtXRD2N4BtyzVvnBko6K8IWWr36ew==
+
+esbuild-linux-riscv64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-riscv64/-/esbuild-linux-riscv64-0.14.34.tgz#549bd18a9eba3135b67f7b742730b5343a1be35d"
+ integrity sha512-Y717ltBdQ5j5sZIHdy1DV9kieo0wMip0dCmVSTceowCPYSn1Cg33Kd6981+F/3b9FDMzNWldZFOBRILViENZSA==
+
+esbuild-linux-s390x@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.29.tgz#0f7310ff1daec463ead9b9e26b7aa083a9f9f1ee"
+ integrity sha512-o7EYajF1rC/4ho7kpSG3gENVx0o2SsHm7cJ5fvewWB/TEczWU7teDgusGSujxCYcMottE3zqa423VTglNTYhjg==
+
+esbuild-linux-s390x@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-linux-s390x/-/esbuild-linux-s390x-0.14.34.tgz#2a6b577c437f94c2b37623c755ff5215a05c12bc"
+ integrity sha512-bDDgYO4LhL4+zPs+WcBkXph+AQoPcQRTv18FzZS0WhjfH8TZx2QqlVPGhmhZ6WidrY+jKthUqO6UhGyIb4MpmA==
+
+esbuild-netbsd-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.29.tgz#ba9a0d9cb8aed73b684825126927f75d4fe44ff9"
+ integrity sha512-/esN6tb6OBSot6+JxgeOZeBk6P8V/WdR3GKBFeFpSqhgw4wx7xWUqPrdx4XNpBVO7X4Ipw9SAqgBrWHlXfddww==
+
+esbuild-netbsd-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-netbsd-64/-/esbuild-netbsd-64-0.14.34.tgz#7f0b73229157975eb35597207723df52ba21722a"
+ integrity sha512-cfaFGXdRt0+vHsjNPyF0POM4BVSHPSbhLPe8mppDc7GDDxjIl08mV1Zou14oDWMp/XZMjYN1kWYRSfftiD0vvQ==
+
+esbuild-openbsd-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.29.tgz#36dbe2c32d899106791b5f3af73f359213f71b8a"
+ integrity sha512-jUTdDzhEKrD0pLpjmk0UxwlfNJNg/D50vdwhrVcW/D26Vg0hVbthMfb19PJMatzclbK7cmgk1Nu0eNS+abzoHw==
+
+esbuild-openbsd-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-openbsd-64/-/esbuild-openbsd-64-0.14.34.tgz#b9bc44b4f70031fb01b173b279daeffc4d4f54b7"
+ integrity sha512-vmy9DxXVnRiI14s8GKuYBtess+EVcDALkbpTqd5jw4XITutIzyB7n4x0Tj5utAkKsgZJB22lLWGekr0ABnSLow==
+
+esbuild-sunos-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.29.tgz#e5f857c121441ec63bf9b399a2131409a7d344e5"
+ integrity sha512-EfhQN/XO+TBHTbkxwsxwA7EfiTHFe+MNDfxcf0nj97moCppD9JHPq48MLtOaDcuvrTYOcrMdJVeqmmeQ7doTcg==
+
+esbuild-sunos-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-sunos-64/-/esbuild-sunos-64-0.14.34.tgz#512dd6085ac1a0dccc20c5f932f16a618bea409c"
+ integrity sha512-eNPVatNET1F7tRMhii7goL/eptfxc0ALRjrj9SPFNqp0zmxrehBFD6BaP3R4LjMn6DbMO0jOAnTLFKr8NqcJAA==
+
+esbuild-windows-32@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.29.tgz#9c2f1ab071a828f3901d1d79d205982a74bdda6e"
+ integrity sha512-uoyb0YAJ6uWH4PYuYjfGNjvgLlb5t6b3zIaGmpWPOjgpr1Nb3SJtQiK4YCPGhONgfg2v6DcJgSbOteuKXhwqAw==
+
+esbuild-windows-32@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-32/-/esbuild-windows-32-0.14.34.tgz#3ff1afd5cac08050c7c7140a59e343b06f6b037c"
+ integrity sha512-EFhpXyHEcnqWYe2rAHFd8dRw8wkrd9U+9oqcyoEL84GbanAYjiiIjBZsnR8kl0sCQ5w6bLpk7vCEIA2VS32Vcg==
+
+esbuild-windows-64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.29.tgz#85fbce7c2492521896451b98d649a7db93e52667"
+ integrity sha512-X9cW/Wl95QjsH8WUyr3NqbmfdU72jCp71cH3pwPvI4CgBM2IeOUDdbt6oIGljPu2bf5eGDIo8K3Y3vvXCCTd8A==
+
+esbuild-windows-64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-64/-/esbuild-windows-64-0.14.34.tgz#66f7b43d2a0b132f6748dfa3edac4fc939a99be0"
+ integrity sha512-a8fbl8Ky7PxNEjf1aJmtxdDZj32/hC7S1OcA2ckEpCJRTjiKslI9vAdPpSjrKIWhws4Galpaawy0nB7fjHYf5Q==
+
+esbuild-windows-arm64@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.29.tgz#0aa7a9a1bc43a63350bcf574d94b639176f065b5"
+ integrity sha512-+O/PI+68fbUZPpl3eXhqGHTGK7DjLcexNnyJqtLZXOFwoAjaXlS5UBCvVcR3o2va+AqZTj8o6URaz8D2K+yfQQ==
+
+esbuild-windows-arm64@0.14.34:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild-windows-arm64/-/esbuild-windows-arm64-0.14.34.tgz#b74a6395b7b7e53dba70b71b39542afd83352473"
+ integrity sha512-EYvmKbSa2B3sPnpC28UEu9jBK5atGV4BaVRE7CYGUci2Hlz4AvtV/LML+TcDMT6gBgibnN2gcltWclab3UutMg==
+
+esbuild@0.14.29:
+ version "0.14.29"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.29.tgz#24ad09c0674cbcb4aa2fe761485524eb1f6b1419"
+ integrity sha512-SQS8cO8xFEqevYlrHt6exIhK853Me4nZ4aMW6ieysInLa0FMAL+AKs87HYNRtR2YWRcEIqoXAHh+Ytt5/66qpg==
+ optionalDependencies:
+ esbuild-android-64 "0.14.29"
+ esbuild-android-arm64 "0.14.29"
+ esbuild-darwin-64 "0.14.29"
+ esbuild-darwin-arm64 "0.14.29"
+ esbuild-freebsd-64 "0.14.29"
+ esbuild-freebsd-arm64 "0.14.29"
+ esbuild-linux-32 "0.14.29"
+ esbuild-linux-64 "0.14.29"
+ esbuild-linux-arm "0.14.29"
+ esbuild-linux-arm64 "0.14.29"
+ esbuild-linux-mips64le "0.14.29"
+ esbuild-linux-ppc64le "0.14.29"
+ esbuild-linux-riscv64 "0.14.29"
+ esbuild-linux-s390x "0.14.29"
+ esbuild-netbsd-64 "0.14.29"
+ esbuild-openbsd-64 "0.14.29"
+ esbuild-sunos-64 "0.14.29"
+ esbuild-windows-32 "0.14.29"
+ esbuild-windows-64 "0.14.29"
+ esbuild-windows-arm64 "0.14.29"
+
+esbuild@^0.14.27:
+ version "0.14.34"
+ resolved "https://registry.yarnpkg.com/esbuild/-/esbuild-0.14.34.tgz#3610056f0a57bcfd0b63ddaafdb2e3bef1cf96e4"
+ integrity sha512-QIWdPT/gFF6hCaf4m7kP0cJ+JIuFkdHibI7vVFvu3eJS1HpVmYHWDulyN5WXwbRA0SX/7ZDaJ/1DH8SdY9xOJg==
+ optionalDependencies:
+ esbuild-android-64 "0.14.34"
+ esbuild-android-arm64 "0.14.34"
+ esbuild-darwin-64 "0.14.34"
+ esbuild-darwin-arm64 "0.14.34"
+ esbuild-freebsd-64 "0.14.34"
+ esbuild-freebsd-arm64 "0.14.34"
+ esbuild-linux-32 "0.14.34"
+ esbuild-linux-64 "0.14.34"
+ esbuild-linux-arm "0.14.34"
+ esbuild-linux-arm64 "0.14.34"
+ esbuild-linux-mips64le "0.14.34"
+ esbuild-linux-ppc64le "0.14.34"
+ esbuild-linux-riscv64 "0.14.34"
+ esbuild-linux-s390x "0.14.34"
+ esbuild-netbsd-64 "0.14.34"
+ esbuild-openbsd-64 "0.14.34"
+ esbuild-sunos-64 "0.14.34"
+ esbuild-windows-32 "0.14.34"
+ esbuild-windows-64 "0.14.34"
+ esbuild-windows-arm64 "0.14.34"
+
+escalade@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40"
+ integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==
+
+escape-html@^1.0.1, escape-html@~1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
+ integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
+
+escape-string-regexp@^1.0.5:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
+ integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
+
+escape-string-regexp@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
+ integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
+
+eslint-config-standard@17.0.0-1:
+ version "17.0.0-1"
+ resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.0.0-1.tgz#5496abc783468e3e3a806ae4f2b3273521b1cb9a"
+ integrity sha512-aqRG58dqoBNfOLN+PsitasxmW+W9Os4oQrx081B16T4E4WogsSbpUL6hnKSnyv35sSRYA2XjBtKMOrUboL6jgw==
+
+eslint-import-resolver-node@^0.3.6:
+ version "0.3.6"
+ resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.6.tgz#4048b958395da89668252001dbd9eca6b83bacbd"
+ integrity sha512-0En0w03NRVMn9Uiyn8YRPDKvWjxCWkslUEhGNTdGx15RvPJYQ+lbOlqrlNI2vEAs4pDYK4f/HN2TbDmk5TP0iw==
+ dependencies:
+ debug "^3.2.7"
+ resolve "^1.20.0"
+
+eslint-module-utils@^2.7.3:
+ version "2.7.3"
+ resolved "https://registry.yarnpkg.com/eslint-module-utils/-/eslint-module-utils-2.7.3.tgz#ad7e3a10552fdd0642e1e55292781bd6e34876ee"
+ integrity sha512-088JEC7O3lDZM9xGe0RerkOMd0EjFl+Yvd1jPWIkMT5u3H9+HC34mWWPnqPrN13gieT9pBOO+Qt07Nb/6TresQ==
+ dependencies:
+ debug "^3.2.7"
+ find-up "^2.1.0"
+
+eslint-plugin-es@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-es/-/eslint-plugin-es-4.1.0.tgz#f0822f0c18a535a97c3e714e89f88586a7641ec9"
+ integrity sha512-GILhQTnjYE2WorX5Jyi5i4dz5ALWxBIdQECVQavL6s7cI76IZTDWleTHkxz/QT3kvcs2QlGHvKLYsSlPOlPXnQ==
+ dependencies:
+ eslint-utils "^2.0.0"
+ regexpp "^3.0.0"
+
+eslint-plugin-import@2.26.0:
+ version "2.26.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-import/-/eslint-plugin-import-2.26.0.tgz#f812dc47be4f2b72b478a021605a59fc6fe8b88b"
+ integrity sha512-hYfi3FXaM8WPLf4S1cikh/r4IxnO6zrhZbEGz2b660EJRbuxgpDS5gkCuYgGWg2xxh2rBuIr4Pvhve/7c31koA==
+ dependencies:
+ array-includes "^3.1.4"
+ array.prototype.flat "^1.2.5"
+ debug "^2.6.9"
+ doctrine "^2.1.0"
+ eslint-import-resolver-node "^0.3.6"
+ eslint-module-utils "^2.7.3"
+ has "^1.0.3"
+ is-core-module "^2.8.1"
+ is-glob "^4.0.3"
+ minimatch "^3.1.2"
+ object.values "^1.1.5"
+ resolve "^1.22.0"
+ tsconfig-paths "^3.14.1"
+
+eslint-plugin-n@15.1.0:
+ version "15.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-n/-/eslint-plugin-n-15.1.0.tgz#efb6648dda91bc00e1521f42e5c0208eeae82bb8"
+ integrity sha512-Tgx4Z58QXv2Ha7Qzp0u4wavnZNZ3AOievZMxrAxi7nvDbzD5B/JqOD80LHYcGHFZc2HD9jDmM/+KWMPov46a4A==
+ dependencies:
+ builtins "^4.0.0"
+ eslint-plugin-es "^4.1.0"
+ eslint-utils "^3.0.0"
+ ignore "^5.1.1"
+ is-core-module "^2.3.0"
+ minimatch "^3.0.4"
+ resolve "^1.10.1"
+ semver "^6.3.0"
+
+eslint-plugin-promise@6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-6.0.0.tgz#017652c07c9816413a41e11c30adc42c3d55ff18"
+ integrity sha512-7GPezalm5Bfi/E22PnQxDWH2iW9GTvAlUNTztemeHb6c1BniSyoeTrM87JkC0wYdi6aQrZX9p2qEiAno8aTcbw==
+
+eslint-plugin-vue@8.6.0:
+ version "8.6.0"
+ resolved "https://registry.yarnpkg.com/eslint-plugin-vue/-/eslint-plugin-vue-8.6.0.tgz#fbdf0f13f8d208a4cba752bf54042661a1aec5c3"
+ integrity sha512-abXiF2J18n/7ZPy9foSlJyouKf54IqpKlNvNmzhM93N0zs3QUxZG/oBd3tVPOJTKg7SlhBUtPxugpqzNbgGpQQ==
+ dependencies:
+ eslint-utils "^3.0.0"
+ natural-compare "^1.4.0"
+ semver "^7.3.5"
+ vue-eslint-parser "^8.0.1"
+
+eslint-scope@^7.0.0, eslint-scope@^7.1.1:
+ version "7.1.1"
+ resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-7.1.1.tgz#fff34894c2f65e5226d3041ac480b4513a163642"
+ integrity sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==
+ dependencies:
+ esrecurse "^4.3.0"
+ estraverse "^5.2.0"
+
+eslint-utils@^2.0.0, eslint-utils@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-2.1.0.tgz#d2de5e03424e707dc10c74068ddedae708741b27"
+ integrity sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==
+ dependencies:
+ eslint-visitor-keys "^1.1.0"
+
+eslint-utils@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-3.0.0.tgz#8aebaface7345bb33559db0a1f13a1d2d48c3672"
+ integrity sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==
+ dependencies:
+ eslint-visitor-keys "^2.0.0"
+
+eslint-visitor-keys@^1.1.0, eslint-visitor-keys@^1.3.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz#30ebd1ef7c2fdff01c3a4f151044af25fab0523e"
+ integrity sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==
+
+eslint-visitor-keys@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz#f65328259305927392c938ed44eb0a5c9b2bd303"
+ integrity sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==
+
+eslint-visitor-keys@^3.1.0, eslint-visitor-keys@^3.3.0:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz#f6480fa6b1f30efe2d1968aa8ac745b862469826"
+ integrity sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==
+
+eslint@8.12.0:
+ version "8.12.0"
+ resolved "https://registry.yarnpkg.com/eslint/-/eslint-8.12.0.tgz#c7a5bd1cfa09079aae64c9076c07eada66a46e8e"
+ integrity sha512-it1oBL9alZg1S8UycLm5YDMAkIhtH6FtAzuZs6YvoGVldWjbS08BkAdb/ymP9LlAyq8koANu32U7Ib/w+UNh8Q==
+ dependencies:
+ "@eslint/eslintrc" "^1.2.1"
+ "@humanwhocodes/config-array" "^0.9.2"
+ ajv "^6.10.0"
+ chalk "^4.0.0"
+ cross-spawn "^7.0.2"
+ debug "^4.3.2"
+ doctrine "^3.0.0"
+ escape-string-regexp "^4.0.0"
+ eslint-scope "^7.1.1"
+ eslint-utils "^3.0.0"
+ eslint-visitor-keys "^3.3.0"
+ espree "^9.3.1"
+ esquery "^1.4.0"
+ esutils "^2.0.2"
+ fast-deep-equal "^3.1.3"
+ file-entry-cache "^6.0.1"
+ functional-red-black-tree "^1.0.1"
+ glob-parent "^6.0.1"
+ globals "^13.6.0"
+ ignore "^5.2.0"
+ import-fresh "^3.0.0"
+ imurmurhash "^0.1.4"
+ is-glob "^4.0.0"
+ js-yaml "^4.1.0"
+ json-stable-stringify-without-jsonify "^1.0.1"
+ levn "^0.4.1"
+ lodash.merge "^4.6.2"
+ minimatch "^3.0.4"
+ natural-compare "^1.4.0"
+ optionator "^0.9.1"
+ regexpp "^3.2.0"
+ strip-ansi "^6.0.1"
+ strip-json-comments "^3.1.0"
+ text-table "^0.2.0"
+ v8-compile-cache "^2.0.3"
+
+espree@^6.0.0:
+ version "6.2.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-6.2.1.tgz#77fc72e1fd744a2052c20f38a5b575832e82734a"
+ integrity sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==
+ dependencies:
+ acorn "^7.1.1"
+ acorn-jsx "^5.2.0"
+ eslint-visitor-keys "^1.1.0"
+
+espree@^9.0.0, espree@^9.3.1:
+ version "9.3.1"
+ resolved "https://registry.yarnpkg.com/espree/-/espree-9.3.1.tgz#8793b4bc27ea4c778c19908e0719e7b8f4115bcd"
+ integrity sha512-bvdyLmJMfwkV3NCRl5ZhJf22zBFo1y8bYh3VYb+bfzqNB4Je68P2sSuXyuFquzWLebHpNd2/d5uv7yoP9ISnGQ==
+ dependencies:
+ acorn "^8.7.0"
+ acorn-jsx "^5.3.1"
+ eslint-visitor-keys "^3.3.0"
+
+esquery@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.4.0.tgz#2148ffc38b82e8c7057dfed48425b3e61f0f24a5"
+ integrity sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==
+ dependencies:
+ estraverse "^5.1.0"
+
+esrecurse@^4.3.0:
+ version "4.3.0"
+ resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921"
+ integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==
+ dependencies:
+ estraverse "^5.2.0"
+
+estraverse@^5.1.0, estraverse@^5.2.0:
+ version "5.3.0"
+ resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123"
+ integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==
+
+estree-walker@^2.0.1, estree-walker@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac"
+ integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==
+
+esutils@^2.0.2:
+ version "2.0.3"
+ resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64"
+ integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==
+
+etag@~1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
+ integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=
+
+express@^4.17.3:
+ version "4.17.3"
+ resolved "https://registry.yarnpkg.com/express/-/express-4.17.3.tgz#f6c7302194a4fb54271b73a1fe7a06478c8f85a1"
+ integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==
+ dependencies:
+ accepts "~1.3.8"
+ array-flatten "1.1.1"
+ body-parser "1.19.2"
+ content-disposition "0.5.4"
+ content-type "~1.0.4"
+ cookie "0.4.2"
+ cookie-signature "1.0.6"
+ debug "2.6.9"
+ depd "~1.1.2"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ finalhandler "~1.1.2"
+ fresh "0.5.2"
+ merge-descriptors "1.0.1"
+ methods "~1.1.2"
+ on-finished "~2.3.0"
+ parseurl "~1.3.3"
+ path-to-regexp "0.1.7"
+ proxy-addr "~2.0.7"
+ qs "6.9.7"
+ range-parser "~1.2.1"
+ safe-buffer "5.2.1"
+ send "0.17.2"
+ serve-static "1.14.2"
+ setprototypeof "1.2.0"
+ statuses "~1.5.0"
+ type-is "~1.6.18"
+ utils-merge "1.0.1"
+ vary "~1.1.2"
+
+external-editor@^3.0.3:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495"
+ integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==
+ dependencies:
+ chardet "^0.7.0"
+ iconv-lite "^0.4.24"
+ tmp "^0.0.33"
+
+extract-files@^11.0.0:
+ version "11.0.0"
+ resolved "https://registry.yarnpkg.com/extract-files/-/extract-files-11.0.0.tgz#b72d428712f787eef1f5193aff8ab5351ca8469a"
+ integrity sha512-FuoE1qtbJ4bBVvv94CC7s0oTnKUGvQs+Rjf1L2SJFfS+HTVVjhPFtehPdQ0JiGPqVNfSSZvL5yzHHQq2Z4WNhQ==
+
+fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
+ version "3.1.3"
+ resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
+ integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
+
+fast-glob@3.2.11, fast-glob@^3.2.5:
+ version "3.2.11"
+ resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
+ integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
+ dependencies:
+ "@nodelib/fs.stat" "^2.0.2"
+ "@nodelib/fs.walk" "^1.2.3"
+ glob-parent "^5.1.2"
+ merge2 "^1.3.0"
+ micromatch "^4.0.4"
+
+fast-json-stable-stringify@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
+ integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==
+
+fast-levenshtein@^2.0.6:
+ version "2.0.6"
+ resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917"
+ integrity sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=
+
+fastq@^1.6.0:
+ version "1.13.0"
+ resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
+ integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
+ dependencies:
+ reusify "^1.0.4"
+
+fault@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/fault/-/fault-1.0.4.tgz#eafcfc0a6d214fc94601e170df29954a4f842f13"
+ integrity sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==
+ dependencies:
+ format "^0.2.0"
+
+figures@^3.0.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+ integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
+ dependencies:
+ escape-string-regexp "^1.0.5"
+
+file-entry-cache@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027"
+ integrity sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==
+ dependencies:
+ flat-cache "^3.0.4"
+
+filesize-parser@1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/filesize-parser/-/filesize-parser-1.5.0.tgz#97ad66d5b0d7154b2e8b1b4e83f526aed33c62f3"
+ integrity sha512-UTDpJB22VvozK7t31slU9WCAPSdcUWuwD7P7S6LBXswdgzUz+YhoziLOohknFcx0Kq5LWCAj4MEKY9q3zGq47Q==
+
+filesize@8.0.7:
+ version "8.0.7"
+ resolved "https://registry.yarnpkg.com/filesize/-/filesize-8.0.7.tgz#695e70d80f4e47012c132d57a059e80c6b580bd8"
+ integrity sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==
+
+fill-range@^7.0.1:
+ version "7.0.1"
+ resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
+ integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==
+ dependencies:
+ to-regex-range "^5.0.1"
+
+finalhandler@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
+ integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==
+ dependencies:
+ debug "2.6.9"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ on-finished "~2.3.0"
+ parseurl "~1.3.3"
+ statuses "~1.5.0"
+ unpipe "~1.0.0"
+
+find-up@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/find-up/-/find-up-2.1.0.tgz#45d1b7e506c717ddd482775a2b77920a3c0c57a7"
+ integrity sha1-RdG35QbHF93UgndaK3eSCjwMV6c=
+ dependencies:
+ locate-path "^2.0.0"
+
+flat-cache@^3.0.4:
+ version "3.0.4"
+ resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.0.4.tgz#61b0338302b2fe9f957dcc32fc2a87f1c3048b11"
+ integrity sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==
+ dependencies:
+ flatted "^3.1.0"
+ rimraf "^3.0.2"
+
+flatted@^3.1.0:
+ version "3.2.5"
+ resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.5.tgz#76c8584f4fc843db64702a6bd04ab7a8bd666da3"
+ integrity sha512-WIWGi2L3DyTUvUrwRKgGi9TwxQMUEqPOPQBVi71R96jZXJdFskXEmf54BoZaS1kknGODoIGASGEzBUYdyMCBJg==
+
+format@^0.2.0:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/format/-/format-0.2.2.tgz#d6170107e9efdc4ed30c9dc39016df942b5cb58b"
+ integrity sha1-1hcBB+nv3E7TDJ3DkBbflCtctYs=
+
+forwarded@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
+ integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
+
+fraction.js@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.2.0.tgz#448e5109a313a3527f5a3ab2119ec4cf0e0e2950"
+ integrity sha512-MhLuK+2gUcnZe8ZHlaaINnQLl0xRIGRfcGk2yl8xoQAfHrSsL3rYu6FCmBdkdbhc9EPlwyGHewaRsvwRMJtAlA==
+
+fresh@0.5.2:
+ version "0.5.2"
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
+ integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
+
+fs-constants@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs-constants/-/fs-constants-1.0.0.tgz#6be0de9be998ce16af8afc24497b9ee9b7ccd9ad"
+ integrity sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==
+
+fs-extra@^10.0.1:
+ version "10.0.1"
+ resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.1.tgz#27de43b4320e833f6867cc044bfce29fdf0ef3b8"
+ integrity sha512-NbdoVMZso2Lsrn/QwLXOy6rm0ufY2zEOKCDzJR/0kBsb0E6qed0P3iYK+Ath3BfvXEeu4JhEtXLgILx5psUfag==
+ dependencies:
+ graceful-fs "^4.2.0"
+ jsonfile "^6.0.1"
+ universalify "^2.0.0"
+
+fs.realpath@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
+ integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8=
+
+fsevents@~2.3.2:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
+ integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
+
+function-bind@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
+ integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
+
+functional-red-black-tree@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327"
+ integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=
+
+get-caller-file@^2.0.5:
+ version "2.0.5"
+ resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
+ integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==
+
+get-intrinsic@^1.0.2, get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6"
+ integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==
+ dependencies:
+ function-bind "^1.1.1"
+ has "^1.0.3"
+ has-symbols "^1.0.1"
+
+get-symbol-description@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.0.tgz#7fdb81c900101fbd564dd5f1a30af5aadc1e58d6"
+ integrity sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==
+ dependencies:
+ call-bind "^1.0.2"
+ get-intrinsic "^1.1.1"
+
+glob-parent@^5.1.2, glob-parent@~5.1.2:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
+ integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
+ dependencies:
+ is-glob "^4.0.1"
+
+glob-parent@^6.0.1:
+ version "6.0.2"
+ resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3"
+ integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==
+ dependencies:
+ is-glob "^4.0.3"
+
+glob@^7.1.3, glob@^7.1.4:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
+ integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
+ dependencies:
+ fs.realpath "^1.0.0"
+ inflight "^1.0.4"
+ inherits "2"
+ minimatch "^3.0.4"
+ once "^1.3.0"
+ path-is-absolute "^1.0.0"
+
+globals@^13.6.0, globals@^13.9.0:
+ version "13.13.0"
+ resolved "https://registry.yarnpkg.com/globals/-/globals-13.13.0.tgz#ac32261060d8070e2719dd6998406e27d2b5727b"
+ integrity sha512-EQ7Q18AJlPwp3vUDL4mKA0KXrXyNIQyWon6T6XQiBQF0XHvRsiCSrWmmeATpUzdJN2HhWZU6Pdl0a9zdep5p6A==
+ dependencies:
+ type-fest "^0.20.2"
+
+good-listener@^1.2.2:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
+ integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
+ dependencies:
+ delegate "^3.1.2"
+
+graceful-fs@^4.1.6, graceful-fs@^4.2.0:
+ version "4.2.10"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
+ integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
+
+graphql-tag@2.12.6, graphql-tag@^2.12.3:
+ version "2.12.6"
+ resolved "https://registry.yarnpkg.com/graphql-tag/-/graphql-tag-2.12.6.tgz#d441a569c1d2537ef10ca3d1633b48725329b5f1"
+ integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==
+ dependencies:
+ tslib "^2.1.0"
+
+graphql@16.3.0:
+ version "16.3.0"
+ resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.3.0.tgz#a91e24d10babf9e60c706919bb182b53ccdffc05"
+ integrity sha512-xm+ANmA16BzCT5pLjuXySbQVFwH3oJctUVdy81w1sV0vBU0KgDdBGtxQOUd5zqOBk/JayAFeG8Dlmeq74rjm/A==
+
+has-bigints@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113"
+ integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA==
+
+has-flag@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
+ integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
+
+has-symbols@^1.0.1, has-symbols@^1.0.2, has-symbols@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
+ integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==
+
+has-tostringtag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.0.tgz#7e133818a7d394734f941e73c3d3f9291e658b25"
+ integrity sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==
+ dependencies:
+ has-symbols "^1.0.2"
+
+has@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
+ integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
+ dependencies:
+ function-bind "^1.1.1"
+
+he@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
+ integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==
+
+highlight.js@~10.7.0:
+ version "10.7.3"
+ resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
+ integrity sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==
+
+hoist-non-react-statics@^3.3.2:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
+ integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
+ dependencies:
+ react-is "^16.7.0"
+
+html-minifier@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56"
+ integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig==
+ dependencies:
+ camel-case "^3.0.0"
+ clean-css "^4.2.1"
+ commander "^2.19.0"
+ he "^1.2.0"
+ param-case "^2.1.1"
+ relateurl "^0.2.7"
+ uglify-js "^3.5.1"
+
+http-errors@1.8.1:
+ version "1.8.1"
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c"
+ integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==
+ dependencies:
+ depd "~1.1.2"
+ inherits "2.0.4"
+ setprototypeof "1.2.0"
+ statuses ">= 1.5.0 < 2"
+ toidentifier "1.0.1"
+
+iconv-lite@0.4.24, iconv-lite@^0.4.24:
+ version "0.4.24"
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
+ integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
+ dependencies:
+ safer-buffer ">= 2.1.2 < 3"
+
+ieee754@^1.1.13:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
+ integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
+
+ignore@^5.1.1, ignore@^5.2.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.2.0.tgz#6d3bac8fa7fe0d45d9f9be7bac2fc279577e345a"
+ integrity sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==
+
+import-fresh@^3.0.0, import-fresh@^3.2.1:
+ version "3.3.0"
+ resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b"
+ integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==
+ dependencies:
+ parent-module "^1.0.0"
+ resolve-from "^4.0.0"
+
+imurmurhash@^0.1.4:
+ version "0.1.4"
+ resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
+ integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
+
+inflight@^1.0.4:
+ version "1.0.6"
+ resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
+ integrity sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=
+ dependencies:
+ once "^1.3.0"
+ wrappy "1"
+
+inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
+ integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
+
+inquirer@^8.2.1:
+ version "8.2.2"
+ resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.2.tgz#1310517a87a0814d25336c78a20b44c3d9b7629d"
+ integrity sha512-pG7I/si6K/0X7p1qU+rfWnpTE1UIkTONN1wxtzh0d+dHXtT/JG6qBgLxoyHVsQa8cFABxAPh0pD6uUUHiAoaow==
+ dependencies:
+ ansi-escapes "^4.2.1"
+ chalk "^4.1.1"
+ cli-cursor "^3.1.0"
+ cli-width "^3.0.0"
+ external-editor "^3.0.3"
+ figures "^3.0.0"
+ lodash "^4.17.21"
+ mute-stream "0.0.8"
+ ora "^5.4.1"
+ run-async "^2.4.0"
+ rxjs "^7.5.5"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+ through "^2.3.6"
+
+internal-slot@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
+ integrity sha512-O0DB1JC/sPyZl7cIo78n5dR7eUSwwpYPiXRhTzNxZVAMUuB8vlnRFyLxdrVToks6XPLVnFfbzaVd5WLjhgg+vA==
+ dependencies:
+ get-intrinsic "^1.1.0"
+ has "^1.0.3"
+ side-channel "^1.0.4"
+
+ipaddr.js@1.9.1:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
+ integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
+
+is-bigint@^1.0.1:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-bigint/-/is-bigint-1.0.4.tgz#08147a1875bc2b32005d41ccd8291dffc6691df3"
+ integrity sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==
+ dependencies:
+ has-bigints "^1.0.1"
+
+is-binary-path@~2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09"
+ integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
+ dependencies:
+ binary-extensions "^2.0.0"
+
+is-boolean-object@^1.1.0:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719"
+ integrity sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
+is-callable@^1.1.4, is-callable@^1.2.4:
+ version "1.2.4"
+ resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.4.tgz#47301d58dd0259407865547853df6d61fe471945"
+ integrity sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==
+
+is-core-module@^2.3.0, is-core-module@^2.8.1:
+ version "2.8.1"
+ resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.8.1.tgz#f59fdfca701d5879d0a6b100a40aa1560ce27211"
+ integrity sha512-SdNCUs284hr40hFTFP6l0IfZ/RSrMXF3qgoRHd3/79unUTvrFO/JoXwkGm+5J/Oe3E/b5GsnG330uUNgRpu1PA==
+ dependencies:
+ has "^1.0.3"
+
+is-date-object@^1.0.1:
+ version "1.0.5"
+ resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f"
+ integrity sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-docker@^2.0.0, is-docker@^2.1.1:
+ version "2.2.1"
+ resolved "https://registry.yarnpkg.com/is-docker/-/is-docker-2.2.1.tgz#33eeabe23cfe86f14bde4408a02c0cfb853acdaa"
+ integrity sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==
+
+is-expression@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/is-expression/-/is-expression-4.0.0.tgz#c33155962abf21d0afd2552514d67d2ec16fd2ab"
+ integrity sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==
+ dependencies:
+ acorn "^7.1.1"
+ object-assign "^4.1.1"
+
+is-extglob@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
+ integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
+
+is-fullwidth-code-point@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
+ integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
+
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+ version "4.0.3"
+ resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084"
+ integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
+ dependencies:
+ is-extglob "^2.1.1"
+
+is-interactive@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e"
+ integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==
+
+is-negative-zero@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
+ integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
+
+is-number-object@^1.0.4:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
+ integrity sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-number@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b"
+ integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==
+
+is-obj@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982"
+ integrity sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==
+
+is-plain-object@^2.0.4:
+ version "2.0.4"
+ resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
+ integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==
+ dependencies:
+ isobject "^3.0.1"
+
+is-promise@^2.0.0:
+ version "2.2.2"
+ resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.2.2.tgz#39ab959ccbf9a774cf079f7b40c7a26f763135f1"
+ integrity sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==
+
+is-regex@^1.0.3, is-regex@^1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.4.tgz#eef5663cd59fa4c0ae339505323df6854bb15958"
+ integrity sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==
+ dependencies:
+ call-bind "^1.0.2"
+ has-tostringtag "^1.0.0"
+
+is-shared-array-buffer@^1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz#8f259c573b60b6a32d4058a1a07430c0a7344c79"
+ integrity sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==
+ dependencies:
+ call-bind "^1.0.2"
+
+is-string@^1.0.5, is-string@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.7.tgz#0dd12bf2006f255bb58f695110eff7491eebc0fd"
+ integrity sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==
+ dependencies:
+ has-tostringtag "^1.0.0"
+
+is-symbol@^1.0.2, is-symbol@^1.0.3:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.4.tgz#a6dac93b635b063ca6872236de88910a57af139c"
+ integrity sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==
+ dependencies:
+ has-symbols "^1.0.2"
+
+is-unicode-supported@^0.1.0:
+ version "0.1.0"
+ resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7"
+ integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==
+
+is-weakref@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/is-weakref/-/is-weakref-1.0.2.tgz#9529f383a9338205e89765e0392efc2f100f06f2"
+ integrity sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==
+ dependencies:
+ call-bind "^1.0.2"
+
+is-wsl@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-2.2.0.tgz#74a4c76e77ca9fd3f932f290c17ea326cd157271"
+ integrity sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==
+ dependencies:
+ is-docker "^2.0.0"
+
+isarray@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11"
+ integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=
+
+isbinaryfile@^4.0.8:
+ version "4.0.10"
+ resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3"
+ integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==
+
+isexe@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
+ integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=
+
+isobject@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df"
+ integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8=
+
+js-cookie@3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.1.tgz#9e39b4c6c2f56563708d7d31f6f5f21873a92414"
+ integrity sha512-+0rgsUXZu4ncpPxRL+lNEptWMOWl9etvPHc/koSRp6MPwpRYAhmk0dUG00J4bxVV3r9uUzfo24wW0knS07SKSw==
+
+js-stringify@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/js-stringify/-/js-stringify-1.0.2.tgz#1736fddfd9724f28a3682adc6230ae7e4e9679db"
+ integrity sha1-Fzb939lyTyijaCrcYjCufk6Weds=
+
+"js-tokens@^3.0.0 || ^4.0.0":
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
+ integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
+
+js-yaml@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602"
+ integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ dependencies:
+ argparse "^2.0.1"
+
+json-schema-traverse@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660"
+ integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==
+
+json-schema-traverse@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz#ae7bcb3656ab77a73ba5c49bf654f38e6b6860e2"
+ integrity sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==
+
+json-stable-stringify-without-jsonify@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651"
+ integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=
+
+json5@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.1.tgz#779fb0018604fa854eacbf6252180d83543e3dbe"
+ integrity sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==
+ dependencies:
+ minimist "^1.2.0"
+
+jsonc-eslint-parser@^1.0.1:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/jsonc-eslint-parser/-/jsonc-eslint-parser-1.4.1.tgz#8cbe99f6f5199acbc5a823c4c0b6135411027fa6"
+ integrity sha512-hXBrvsR1rdjmB2kQmUjf1rEIa+TqHBGMge8pwi++C+Si1ad7EjZrJcpgwym+QGK/pqTx+K7keFAtLlVNdLRJOg==
+ dependencies:
+ acorn "^7.4.1"
+ eslint-utils "^2.1.0"
+ eslint-visitor-keys "^1.3.0"
+ espree "^6.0.0"
+ semver "^6.3.0"
+
+jsonfile@^6.0.1:
+ version "6.1.0"
+ resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-6.1.0.tgz#bc55b2634793c679ec6403094eb13698a6ec0aae"
+ integrity sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==
+ dependencies:
+ universalify "^2.0.0"
+ optionalDependencies:
+ graceful-fs "^4.1.6"
+
+jstransformer@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/jstransformer/-/jstransformer-1.0.0.tgz#ed8bf0921e2f3f1ed4d5c1a44f68709ed24722c3"
+ integrity sha1-7Yvwkh4vPx7U1cGkT2hwntJHIsM=
+ dependencies:
+ is-promise "^2.0.0"
+ promise "^7.0.1"
+
+jwt-decode@3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/jwt-decode/-/jwt-decode-3.1.2.tgz#3fb319f3675a2df0c2895c8f5e9fa4b67b04ed59"
+ integrity sha512-UfpWE/VZn0iP50d8cz9NrZLM9lSWhcJ+0Gt/nm4by88UL+J1SiKN8/5dkjMmbEzwL2CAe+67GsegCbIKtbp75A==
+
+kind-of@^6.0.2:
+ version "6.0.3"
+ resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd"
+ integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==
+
+kolorist@^1.5.1:
+ version "1.5.1"
+ resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.5.1.tgz#c3d66dc4fabde4f6b7faa6efda84c00491f9e52b"
+ integrity sha512-lxpCM3HTvquGxKGzHeknB/sUjuVoUElLlfYnXZT73K8geR9jQbroGlSCFBax9/0mpGoD3kzcMLnOlGQPJJNyqQ==
+
+lazystream@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.1.tgz#494c831062f1f9408251ec44db1cba29242a2638"
+ integrity sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==
+ dependencies:
+ readable-stream "^2.0.5"
+
+levn@^0.4.1:
+ version "0.4.1"
+ resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
+ integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==
+ dependencies:
+ prelude-ls "^1.2.1"
+ type-check "~0.4.0"
+
+locate-path@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-2.0.0.tgz#2b568b265eec944c6d9c0de9c3dbbbca0354cd8e"
+ integrity sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=
+ dependencies:
+ p-locate "^2.0.0"
+ path-exists "^3.0.0"
+
+lodash._reinterpolate@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d"
+ integrity sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=
+
+lodash.debounce@^4.0.8:
+ version "4.0.8"
+ resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
+ integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
+
+lodash.defaults@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c"
+ integrity sha1-0JF4cW/+pN3p5ft7N/bwgCJ0WAw=
+
+lodash.difference@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.difference/-/lodash.difference-4.5.0.tgz#9ccb4e505d486b91651345772885a2df27fd017c"
+ integrity sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=
+
+lodash.flatten@^4.4.0:
+ version "4.4.0"
+ resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f"
+ integrity sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=
+
+lodash.isplainobject@^4.0.6:
+ version "4.0.6"
+ resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb"
+ integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=
+
+lodash.merge@^4.6.2:
+ version "4.6.2"
+ resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"
+ integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==
+
+lodash.template@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/lodash.template/-/lodash.template-4.5.0.tgz#f976195cf3f347d0d5f52483569fe8031ccce8ab"
+ integrity sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==
+ dependencies:
+ lodash._reinterpolate "^3.0.0"
+ lodash.templatesettings "^4.0.0"
+
+lodash.templatesettings@^4.0.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz#e481310f049d3cf6d47e912ad09313b154f0fb33"
+ integrity sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==
+ dependencies:
+ lodash._reinterpolate "^3.0.0"
+
+lodash.truncate@^4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
+ integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
+
+lodash.union@^4.6.0:
+ version "4.6.0"
+ resolved "https://registry.yarnpkg.com/lodash.union/-/lodash.union-4.6.0.tgz#48bb5088409f16f1821666641c44dd1aaae3cd88"
+ integrity sha1-SLtQiECfFvGCFmZkHETdGqrjzYg=
+
+lodash@4.17.21, lodash@^4.17.10, lodash@^4.17.20, lodash@^4.17.21:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
+log-symbols@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503"
+ integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==
+ dependencies:
+ chalk "^4.1.0"
+ is-unicode-supported "^0.1.0"
+
+loose-envify@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf"
+ integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==
+ dependencies:
+ js-tokens "^3.0.0 || ^4.0.0"
+
+lower-case@^1.1.1:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
+ integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
+
+lowlight@^1.20.0:
+ version "1.20.0"
+ resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.20.0.tgz#ddb197d33462ad0d93bf19d17b6c301aa3941888"
+ integrity sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==
+ dependencies:
+ fault "^1.0.0"
+ highlight.js "~10.7.0"
+
+lru-cache@^7.4.0:
+ version "7.8.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.0.tgz#649aaeb294a56297b5cbc5d70f198dcc5ebe5747"
+ integrity sha512-AmXqneQZL3KZMIgBpaPTeI6pfwh+xQ2vutMsyqOu1TBdEXFZgpG/80wuJ531w2ZN7TI0/oc8CPxzh/DKQudZqg==
+
+luxon@2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/luxon/-/luxon-2.3.1.tgz#f276b1b53fd9a740a60e666a541a7f6dbed4155a"
+ integrity sha512-I8vnjOmhXsMSlNMZlMkSOvgrxKJl0uOsEzdGgGNZuZPaS9KlefpE9KV95QFftlJSC+1UyCC9/I69R02cz/zcCA==
+
+magic-string@^0.25.7:
+ version "0.25.9"
+ resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c"
+ integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==
+ dependencies:
+ sourcemap-codec "^1.4.8"
+
+media-typer@0.3.0:
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
+ integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=
+
+merge-descriptors@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61"
+ integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
+
+merge2@^1.3.0:
+ version "1.4.1"
+ resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae"
+ integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==
+
+methods@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
+ integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=
+
+micromatch@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6"
+ integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
+ dependencies:
+ braces "^3.0.2"
+ picomatch "^2.3.1"
+
+mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
+ version "1.52.0"
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
+ integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
+
+mime-types@~2.1.24, mime-types@~2.1.34:
+ version "2.1.35"
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
+ integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
+ dependencies:
+ mime-db "1.52.0"
+
+mime@1.6.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
+ integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
+
+mimic-fn@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
+ integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
+
+minimatch@^3.0.4, minimatch@^3.1.2:
+ version "3.1.2"
+ resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+ integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+ dependencies:
+ brace-expansion "^1.1.7"
+
+minimist@^1.2.0, minimist@^1.2.6:
+ version "1.2.6"
+ resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44"
+ integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==
+
+mitt@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/mitt/-/mitt-3.0.0.tgz#69ef9bd5c80ff6f57473e8d89326d01c414be0bd"
+ integrity sha512-7dX2/10ITVyqh4aOSVI9gdape+t9l2/8QxHrFmUXu4EEUpdlxl6RudZUPZoc+zuY2hk1j7XxVroIVIan/pD/SQ==
+
+ms@2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
+ integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=
+
+ms@2.1.2:
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
+ integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
+
+ms@2.1.3, ms@^2.1.1:
+ version "2.1.3"
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
+ integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+
+mute-stream@0.0.8:
+ version "0.0.8"
+ resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d"
+ integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==
+
+nanoid@^3.1.32, nanoid@^3.3.1:
+ version "3.3.2"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.2.tgz#c89622fafb4381cd221421c69ec58547a1eec557"
+ integrity sha512-CuHBogktKwpm5g2sRgv83jEy2ijFzBwMoYA60orPDR7ynsLijJDqgsi4RDGj3OJpy3Ieb+LYwiRmIOGyytgITA==
+
+natural-compare@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
+ integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
+
+negotiator@0.6.3:
+ version "0.6.3"
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
+ integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
+
+no-case@^2.2.0:
+ version "2.3.2"
+ resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac"
+ integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ==
+ dependencies:
+ lower-case "^1.1.1"
+
+node-releases@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.2.tgz#7139fe71e2f4f11b47d4d2986aaf8c48699e0c01"
+ integrity sha512-XxYDdcQ6eKqp/YjI+tb2C5WM2LgjnZrfYg4vgQt49EK268b6gYCHsBLrK2qvJo4FmCtqmKezb0WZFK4fkrZNsg==
+
+normalize-path@^3.0.0, normalize-path@~3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65"
+ integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
+
+normalize-range@^0.1.2:
+ version "0.1.2"
+ resolved "https://registry.yarnpkg.com/normalize-range/-/normalize-range-0.1.2.tgz#2d10c06bdfd312ea9777695a4d28439456b75942"
+ integrity sha1-LRDAa9/TEuqXd2laTShDlFa3WUI=
+
+object-assign@^4.1.1:
+ version "4.1.1"
+ resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
+ integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
+
+object-inspect@^1.12.0, object-inspect@^1.9.0:
+ version "1.12.0"
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.0.tgz#6e2c120e868fd1fd18cb4f18c31741d0d6e776f0"
+ integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==
+
+object-keys@^1.0.12, object-keys@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
+ integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
+
+object.assign@^4.1.2:
+ version "4.1.2"
+ resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.2.tgz#0ed54a342eceb37b38ff76eb831a0e788cb63940"
+ integrity sha512-ixT2L5THXsApyiUPYKmW+2EHpXXe5Ii3M+f4e+aJFAHao5amFRW6J0OO6c/LU8Be47utCx2GL89hxGB6XSmKuQ==
+ dependencies:
+ call-bind "^1.0.0"
+ define-properties "^1.1.3"
+ has-symbols "^1.0.1"
+ object-keys "^1.1.1"
+
+object.values@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.5.tgz#959f63e3ce9ef108720333082131e4a459b716ac"
+ integrity sha512-QUZRW0ilQ3PnPpbNtgdNV1PDbEqLIiSFB3l+EnGtBQ/8SUTLj1PZwtQHABZtLgwpJZTSZhuGLOGk57Drx2IvYg==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+ es-abstract "^1.19.1"
+
+on-finished@~2.3.0:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947"
+ integrity sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=
+ dependencies:
+ ee-first "1.1.1"
+
+on-headers@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f"
+ integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==
+
+once@^1.3.0, once@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
+ integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E=
+ dependencies:
+ wrappy "1"
+
+onetime@^5.1.0:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
+ integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
+ dependencies:
+ mimic-fn "^2.1.0"
+
+open@^8.4.0:
+ version "8.4.0"
+ resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8"
+ integrity sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==
+ dependencies:
+ define-lazy-prop "^2.0.0"
+ is-docker "^2.1.1"
+ is-wsl "^2.2.0"
+
+optimism@^0.16.1:
+ version "0.16.1"
+ resolved "https://registry.yarnpkg.com/optimism/-/optimism-0.16.1.tgz#7c8efc1f3179f18307b887e18c15c5b7133f6e7d"
+ integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg==
+ dependencies:
+ "@wry/context" "^0.6.0"
+ "@wry/trie" "^0.3.0"
+
+optionator@^0.9.1:
+ version "0.9.1"
+ resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.1.tgz#4f236a6373dae0566a6d43e1326674f50c291499"
+ integrity sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==
+ dependencies:
+ deep-is "^0.1.3"
+ fast-levenshtein "^2.0.6"
+ levn "^0.4.1"
+ prelude-ls "^1.2.1"
+ type-check "^0.4.0"
+ word-wrap "^1.2.3"
+
+ora@^5.4.1:
+ version "5.4.1"
+ resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18"
+ integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==
+ dependencies:
+ bl "^4.1.0"
+ chalk "^4.1.0"
+ cli-cursor "^3.1.0"
+ cli-spinners "^2.5.0"
+ is-interactive "^1.0.0"
+ is-unicode-supported "^0.1.0"
+ log-symbols "^4.1.0"
+ strip-ansi "^6.0.0"
+ wcwidth "^1.0.1"
+
+orderedmap@^1.1.0:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/orderedmap/-/orderedmap-1.1.5.tgz#4174c90b61bd7c25294932edf789f3b5677744d0"
+ integrity sha512-/fzlCGKRmfayGoI9UUXvJfc2nMZlJHW30QqEvwPvlg8tsX7jyiUSomYie6mYqx7Z9bOMGoag0H/q1PS/0PjYkg==
+
+os-tmpdir@~1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274"
+ integrity sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=
+
+ouch@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/ouch/-/ouch-2.0.0.tgz#2feab67569fe8037e91db6f6225bea066fc7e716"
+ integrity sha512-kaAZtzpV3iSDdGHQKz7/dRVWd7nXNO1OUNHNtZIW9ryoBvb6y8QtYfpWdcBUFgBzMbMYVA/PGPeoeJU95VHK7Q==
+ dependencies:
+ "@positron/stack-trace" "1.0.0"
+ ejs "^2.3.1"
+ escape-html "^1.0.1"
+ lodash "^4.17.10"
+
+p-limit@^1.1.0:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-1.3.0.tgz#b86bd5f0c25690911c7590fcbfc2010d54b3ccb8"
+ integrity sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==
+ dependencies:
+ p-try "^1.0.0"
+
+p-locate@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-2.0.0.tgz#20a0103b222a70c8fd39cc2e580680f3dde5ec43"
+ integrity sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=
+ dependencies:
+ p-limit "^1.1.0"
+
+p-try@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
+ integrity sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=
+
+param-case@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
+ integrity sha1-35T9jPZTHs915r75oIWPvHK+Ikc=
+ dependencies:
+ no-case "^2.2.0"
+
+parent-module@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2"
+ integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==
+ dependencies:
+ callsites "^3.0.0"
+
+parseurl@~1.3.3:
+ version "1.3.3"
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
+ integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
+
+path-exists@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-3.0.0.tgz#ce0ebeaa5f78cb18925ea7d810d7b59b010fd515"
+ integrity sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=
+
+path-is-absolute@^1.0.0:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
+ integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18=
+
+path-key@^3.1.0:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
+ integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
+
+path-parse@^1.0.7:
+ version "1.0.7"
+ resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
+ integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
+
+path-to-regexp@0.1.7:
+ version "0.1.7"
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
+ integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
+
+picocolors@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
+ integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
+
+picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
+ integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
+
+pinia@2.0.13:
+ version "2.0.13"
+ resolved "https://registry.yarnpkg.com/pinia/-/pinia-2.0.13.tgz#6656fc290dae120a9f0cb2f5c520df400d41b8c5"
+ integrity sha512-B7rSqm1xNpwcPMnqns8/gVBfbbi7lWTByzS6aPZ4JOXSJD4Y531rZHDCoYWBwLyHY/8hWnXljgiXp6rRyrofcw==
+ dependencies:
+ "@vue/devtools-api" "^6.1.4"
+ vue-demi "*"
+
+postcss-value-parser@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
+ integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
+
+postcss@^8.1.10, postcss@^8.4.12, postcss@^8.4.4:
+ version "8.4.12"
+ resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.12.tgz#1e7de78733b28970fa4743f7da6f3763648b1905"
+ integrity sha512-lg6eITwYe9v6Hr5CncVbK70SoioNQIq81nsaG86ev5hAidQvmOeETBqs7jm43K2F5/Ley3ytDtriImV6TpNiSg==
+ dependencies:
+ nanoid "^3.3.1"
+ picocolors "^1.0.0"
+ source-map-js "^1.0.2"
+
+prelude-ls@^1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396"
+ integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+
+process-nextick-args@~2.0.0:
+ version "2.0.1"
+ resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2"
+ integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==
+
+promise@^7.0.1:
+ version "7.3.1"
+ resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf"
+ integrity sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==
+ dependencies:
+ asap "~2.0.3"
+
+prop-types@^15.7.2:
+ version "15.8.1"
+ resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5"
+ integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==
+ dependencies:
+ loose-envify "^1.4.0"
+ object-assign "^4.1.1"
+ react-is "^16.13.1"
+
+prosemirror-commands@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/prosemirror-commands/-/prosemirror-commands-1.2.2.tgz#1bd167372ee20abf488aca9cece63c43fab182c9"
+ integrity sha512-TX+KpWudMon06frryfpO/u7hsQv2hu8L4VSVbCpi3/7wXHBgl+35mV85qfa3RpT8xD2f3MdeoTqH0vy5JdbXPg==
+ dependencies:
+ prosemirror-model "^1.0.0"
+ prosemirror-state "^1.0.0"
+ prosemirror-transform "^1.0.0"
+
+prosemirror-dropcursor@^1.4.0:
+ version "1.4.0"
+ resolved "https://registry.yarnpkg.com/prosemirror-dropcursor/-/prosemirror-dropcursor-1.4.0.tgz#91a859d4ee79c99b1c0ba6ee61c093b195c0d9f0"
+ integrity sha512-6+YwTjmqDwlA/Dm+5wK67ezgqgjA/MhSDgaNxKUzH97SmeuWFXyLeDRxxOPZeSo7yTxcDGUCWTEjmQZsVBuMrQ==
+ dependencies:
+ prosemirror-state "^1.0.0"
+ prosemirror-transform "^1.1.0"
+ prosemirror-view "^1.1.0"
+
+prosemirror-gapcursor@^1.2.1:
+ version "1.2.2"
+ resolved "https://registry.yarnpkg.com/prosemirror-gapcursor/-/prosemirror-gapcursor-1.2.2.tgz#7394613ba4a1601fad1f36f1cff8961968c22ffa"
+ integrity sha512-7YzuRBbu9W7HGQde84kCHfIjaRLNcAdeijbgqrm/R9dsdTWkV+rrdcmic/sCc+bptiNpvjCEE+R6hrbT8zFQeQ==
+ dependencies:
+ prosemirror-keymap "^1.0.0"
+ prosemirror-model "^1.0.0"
+ prosemirror-state "^1.0.0"
+ prosemirror-view "^1.0.0"
+
+prosemirror-history@^1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/prosemirror-history/-/prosemirror-history-1.2.0.tgz#04cc4df8d2f7b2a46651a2780de191ada6d465ea"
+ integrity sha512-B9v9xtf4fYbKxQwIr+3wtTDNLDZcmMMmGiI3TAPShnUzvo+Rmv1GiUrsQChY1meetHl7rhML2cppF3FTs7f7UQ==
+ dependencies:
+ prosemirror-state "^1.2.2"
+ prosemirror-transform "^1.0.0"
+ rope-sequence "^1.3.0"
+
+prosemirror-keymap@^1.0.0, prosemirror-keymap@^1.1.2, prosemirror-keymap@^1.1.5:
+ version "1.1.5"
+ resolved "https://registry.yarnpkg.com/prosemirror-keymap/-/prosemirror-keymap-1.1.5.tgz#b5984c7d30f5c75956c853126c54e9e624c0327b"
+ integrity sha512-8SZgPH3K+GLsHL2wKuwBD9rxhsbnVBTwpHCO4VUO5GmqUQlxd/2GtBVWTsyLq4Dp3N9nGgPd3+lZFKUDuVp+Vw==
+ dependencies:
+ prosemirror-state "^1.0.0"
+ w3c-keyname "^2.2.0"
+
+prosemirror-model@^1.0.0, prosemirror-model@^1.16.0, prosemirror-model@^1.16.1, prosemirror-model@^1.8.1:
+ version "1.16.1"
+ resolved "https://registry.yarnpkg.com/prosemirror-model/-/prosemirror-model-1.16.1.tgz#fb388270bc9609b66298d6a7e15d0cc1d6c61253"
+ integrity sha512-r1/w0HDU40TtkXp0DyKBnFPYwd8FSlUSJmGCGFv4DeynfeSlyQF2FD0RQbVEMOe6P3PpUSXM6LZBV7W/YNZ4mA==
+ dependencies:
+ orderedmap "^1.1.0"
+
+prosemirror-schema-list@^1.1.6:
+ version "1.1.6"
+ resolved "https://registry.yarnpkg.com/prosemirror-schema-list/-/prosemirror-schema-list-1.1.6.tgz#c3e13fe2f74750e4a53ff88d798dc0c4ccca6707"
+ integrity sha512-aFGEdaCWmJzouZ8DwedmvSsL50JpRkqhQ6tcpThwJONVVmCgI36LJHtoQ4VGZbusMavaBhXXr33zyD2IVsTlkw==
+ dependencies:
+ prosemirror-model "^1.0.0"
+ prosemirror-transform "^1.0.0"
+
+prosemirror-state@^1.0.0, prosemirror-state@^1.2.2, prosemirror-state@^1.3.1, prosemirror-state@^1.3.4:
+ version "1.3.4"
+ resolved "https://registry.yarnpkg.com/prosemirror-state/-/prosemirror-state-1.3.4.tgz#4c6b52628216e753fc901c6d2bfd84ce109e8952"
+ integrity sha512-Xkkrpd1y/TQ6HKzN3agsQIGRcLckUMA9u3j207L04mt8ToRgpGeyhbVv0HI7omDORIBHjR29b7AwlATFFf2GLA==
+ dependencies:
+ prosemirror-model "^1.0.0"
+ prosemirror-transform "^1.0.0"
+
+prosemirror-tables@^1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/prosemirror-tables/-/prosemirror-tables-1.1.1.tgz#ad66300cc49500455cf1243bb129c9e7d883321e"
+ integrity sha512-LmCz4jrlqQZRsYRDzCRYf/pQ5CUcSOyqZlAj5kv67ZWBH1SVLP2U9WJEvQfimWgeRlIz0y0PQVqO1arRm1+woA==
+ dependencies:
+ prosemirror-keymap "^1.1.2"
+ prosemirror-model "^1.8.1"
+ prosemirror-state "^1.3.1"
+ prosemirror-transform "^1.2.1"
+ prosemirror-view "^1.13.3"
+
+prosemirror-transform@^1.0.0, prosemirror-transform@^1.1.0, prosemirror-transform@^1.2.1, prosemirror-transform@^1.3.3:
+ version "1.4.2"
+ resolved "https://registry.yarnpkg.com/prosemirror-transform/-/prosemirror-transform-1.4.2.tgz#35f56091bcab3359f1eb90e82ce9f20cc52105c1"
+ integrity sha512-bcIsf3uRZhfab0xRfyyxOEh6eqSszq/hJbDbmUumFnbHBoWhB/uXbpz6vvUxfk0XiEvrZDJ+5pXRrNDc1Hu3vQ==
+ dependencies:
+ prosemirror-model "^1.0.0"
+
+prosemirror-view@^1.0.0, prosemirror-view@^1.1.0, prosemirror-view@^1.13.3, prosemirror-view@^1.23.6:
+ version "1.23.11"
+ resolved "https://registry.yarnpkg.com/prosemirror-view/-/prosemirror-view-1.23.11.tgz#297f6ef8d10e1ff78c505d9c57358e062810a80e"
+ integrity sha512-iBqsyrQZz9NYcJ13JC7sPZ+4PdbBbUXhs1qzbxkDQ2tplcVROwxmAn3bnxpVFst/guv+XFI5KTHHbw5stvKt0g==
+ dependencies:
+ prosemirror-model "^1.16.0"
+ prosemirror-state "^1.0.0"
+ prosemirror-transform "^1.1.0"
+
+proxy-addr@~2.0.7:
+ version "2.0.7"
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
+ integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
+ dependencies:
+ forwarded "0.2.0"
+ ipaddr.js "1.9.1"
+
+pug-attrs@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pug-attrs/-/pug-attrs-3.0.0.tgz#b10451e0348165e31fad1cc23ebddd9dc7347c41"
+ integrity sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==
+ dependencies:
+ constantinople "^4.0.1"
+ js-stringify "^1.0.2"
+ pug-runtime "^3.0.0"
+
+pug-code-gen@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/pug-code-gen/-/pug-code-gen-3.0.2.tgz#ad190f4943133bf186b60b80de483100e132e2ce"
+ integrity sha512-nJMhW16MbiGRiyR4miDTQMRWDgKplnHyeLvioEJYbk1RsPI3FuA3saEP8uwnTb2nTJEKBU90NFVWJBk4OU5qyg==
+ dependencies:
+ constantinople "^4.0.1"
+ doctypes "^1.1.0"
+ js-stringify "^1.0.2"
+ pug-attrs "^3.0.0"
+ pug-error "^2.0.0"
+ pug-runtime "^3.0.0"
+ void-elements "^3.1.0"
+ with "^7.0.0"
+
+pug-error@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pug-error/-/pug-error-2.0.0.tgz#5c62173cb09c34de2a2ce04f17b8adfec74d8ca5"
+ integrity sha512-sjiUsi9M4RAGHktC1drQfCr5C5eriu24Lfbt4s+7SykztEOwVZtbFk1RRq0tzLxcMxMYTBR+zMQaG07J/btayQ==
+
+pug-filters@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/pug-filters/-/pug-filters-4.0.0.tgz#d3e49af5ba8472e9b7a66d980e707ce9d2cc9b5e"
+ integrity sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==
+ dependencies:
+ constantinople "^4.0.1"
+ jstransformer "1.0.0"
+ pug-error "^2.0.0"
+ pug-walk "^2.0.0"
+ resolve "^1.15.1"
+
+pug-lexer@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/pug-lexer/-/pug-lexer-5.0.1.tgz#ae44628c5bef9b190b665683b288ca9024b8b0d5"
+ integrity sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==
+ dependencies:
+ character-parser "^2.2.0"
+ is-expression "^4.0.0"
+ pug-error "^2.0.0"
+
+pug-linker@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/pug-linker/-/pug-linker-4.0.0.tgz#12cbc0594fc5a3e06b9fc59e6f93c146962a7708"
+ integrity sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==
+ dependencies:
+ pug-error "^2.0.0"
+ pug-walk "^2.0.0"
+
+pug-load@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/pug-load/-/pug-load-3.0.0.tgz#9fd9cda52202b08adb11d25681fb9f34bd41b662"
+ integrity sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==
+ dependencies:
+ object-assign "^4.1.1"
+ pug-walk "^2.0.0"
+
+pug-parser@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/pug-parser/-/pug-parser-6.0.0.tgz#a8fdc035863a95b2c1dc5ebf4ecf80b4e76a1260"
+ integrity sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==
+ dependencies:
+ pug-error "^2.0.0"
+ token-stream "1.0.0"
+
+pug-runtime@^3.0.0, pug-runtime@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/pug-runtime/-/pug-runtime-3.0.1.tgz#f636976204723f35a8c5f6fad6acda2a191b83d7"
+ integrity sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==
+
+pug-strip-comments@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz#f94b07fd6b495523330f490a7f554b4ff876303e"
+ integrity sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==
+ dependencies:
+ pug-error "^2.0.0"
+
+pug-walk@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/pug-walk/-/pug-walk-2.0.0.tgz#417aabc29232bb4499b5b5069a2b2d2a24d5f5fe"
+ integrity sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==
+
+pug@3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/pug/-/pug-3.0.2.tgz#f35c7107343454e43bc27ae0ff76c731b78ea535"
+ integrity sha512-bp0I/hiK1D1vChHh6EfDxtndHji55XP/ZJKwsRqrz6lRia6ZC2OZbdAymlxdVFwd1L70ebrVJw4/eZ79skrIaw==
+ dependencies:
+ pug-code-gen "^3.0.2"
+ pug-filters "^4.0.0"
+ pug-lexer "^5.0.1"
+ pug-linker "^4.0.0"
+ pug-load "^3.0.0"
+ pug-parser "^6.0.0"
+ pug-runtime "^3.0.1"
+ pug-strip-comments "^2.0.0"
+
+punycode@^2.1.0:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.1.1.tgz#b58b010ac40c22c5657616c8d2c2c02c7bf479ec"
+ integrity sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==
+
+qs@6.9.7:
+ version "6.9.7"
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.9.7.tgz#4610846871485e1e048f44ae3b94033f0e675afe"
+ integrity sha512-IhMFgUmuNpyRfxA90umL7ByLlgRXu6tIfKPpF5TmcfRLlLCckfP/g3IQmju6jjpu+Hh8rA+2p6A27ZSPOOHdKw==
+
+quasar@2.6.5:
+ version "2.6.5"
+ resolved "https://registry.yarnpkg.com/quasar/-/quasar-2.6.5.tgz#f744e2f12c53430da58da413d31e0720cbd6a6fa"
+ integrity sha512-ABaFt6eXDtXwV6RUt8VW5xxvqb8gpflIdYc8+ucGFnZ5zKH20YzqHOlIg/jJOw3T/C1s8T/c6T4yxvhxxj/uxA==
+
+queue-microtask@^1.2.2:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
+ integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
+
+randombytes@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
+ integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
+ dependencies:
+ safe-buffer "^5.1.0"
+
+range-parser@~1.2.1:
+ version "1.2.1"
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
+ integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
+
+raw-body@2.4.3:
+ version "2.4.3"
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.4.3.tgz#8f80305d11c2a0a545c2d9d89d7a0286fcead43c"
+ integrity sha512-UlTNLIcu0uzb4D2f4WltY6cVjLi+/jEN4lgEUj3E04tpMDpUlkBo/eSn6zou9hum2VMNpCCUone0O0WeJim07g==
+ dependencies:
+ bytes "3.1.2"
+ http-errors "1.8.1"
+ iconv-lite "0.4.24"
+ unpipe "1.0.0"
+
+react-is@^16.13.1, react-is@^16.7.0:
+ version "16.13.1"
+ resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
+ integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
+
+readable-stream@^2.0.0, readable-stream@^2.0.5:
+ version "2.3.7"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57"
+ integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==
+ dependencies:
+ core-util-is "~1.0.0"
+ inherits "~2.0.3"
+ isarray "~1.0.0"
+ process-nextick-args "~2.0.0"
+ safe-buffer "~5.1.1"
+ string_decoder "~1.1.1"
+ util-deprecate "~1.0.1"
+
+readable-stream@^3.1.1, readable-stream@^3.4.0, readable-stream@^3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
+ integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
+ dependencies:
+ inherits "^2.0.3"
+ string_decoder "^1.1.1"
+ util-deprecate "^1.0.1"
+
+readdir-glob@^1.0.0:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/readdir-glob/-/readdir-glob-1.1.1.tgz#f0e10bb7bf7bfa7e0add8baffdc54c3f7dbee6c4"
+ integrity sha512-91/k1EzZwDx6HbERR+zucygRFfiPl2zkIYZtv3Jjr6Mn7SkKcVct8aVO+sSRiGMc6fLf72du3d92/uY63YPdEA==
+ dependencies:
+ minimatch "^3.0.4"
+
+readdirp@~3.6.0:
+ version "3.6.0"
+ resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7"
+ integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
+ dependencies:
+ picomatch "^2.2.1"
+
+regexpp@^3.0.0, regexpp@^3.2.0:
+ version "3.2.0"
+ resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.2.0.tgz#0425a2768d8f23bad70ca4b90461fa2f1213e1b2"
+ integrity sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==
+
+register-service-worker@^1.7.2:
+ version "1.7.2"
+ resolved "https://registry.yarnpkg.com/register-service-worker/-/register-service-worker-1.7.2.tgz#6516983e1ef790a98c4225af1216bc80941a4bd2"
+ integrity sha512-CiD3ZSanZqcMPRhtfct5K9f7i3OLCcBBWsJjLh1gW9RO/nS94sVzY59iS+fgYBOBqaBpf4EzfqUF3j9IG+xo8A==
+
+relateurl@^0.2.7:
+ version "0.2.7"
+ resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9"
+ integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
+
+require-directory@^2.1.1:
+ version "2.1.1"
+ resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42"
+ integrity sha1-jGStX9MNqxyXbiNE/+f3kqam30I=
+
+require-from-string@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
+ integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==
+
+resolve-from@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6"
+ integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
+
+resolve@^1.10.1, resolve@^1.15.1, resolve@^1.20.0, resolve@^1.22.0:
+ version "1.22.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.0.tgz#5e0b8c67c15df57a89bdbabe603a002f21731198"
+ integrity sha512-Hhtrw0nLeSrFQ7phPp4OOcVjLPIeMnRlr5mcnVuMe7M/7eBn98A3hmFRLoFo3DLZkivSYwhRUJTyPyWAk56WLw==
+ dependencies:
+ is-core-module "^2.8.1"
+ path-parse "^1.0.7"
+ supports-preserve-symlinks-flag "^1.0.0"
+
+restore-cursor@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+ integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+ dependencies:
+ onetime "^5.1.0"
+ signal-exit "^3.0.2"
+
+reusify@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
+ integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
+
+rimraf@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
+ integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
+ dependencies:
+ glob "^7.1.3"
+
+rollup-plugin-visualizer@^5.5.4:
+ version "5.6.0"
+ resolved "https://registry.yarnpkg.com/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.6.0.tgz#06aa7cf3fd504a29d404335700f2a3f28ebb33f3"
+ integrity sha512-CKcc8GTUZjC+LsMytU8ocRr/cGZIfMR7+mdy4YnlyetlmIl/dM8BMnOEpD4JPIGt+ZVW7Db9ZtSsbgyeBH3uTA==
+ dependencies:
+ nanoid "^3.1.32"
+ open "^8.4.0"
+ source-map "^0.7.3"
+ yargs "^17.3.1"
+
+rollup@^2.59.0:
+ version "2.70.1"
+ resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.70.1.tgz#824b1f1f879ea396db30b0fc3ae8d2fead93523e"
+ integrity sha512-CRYsI5EuzLbXdxC6RnYhOuRdtz4bhejPMSWjsFLfVM/7w/85n2szZv6yExqUXsBdz5KT8eoubeyDUDjhLHEslA==
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+rope-sequence@^1.3.0:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/rope-sequence/-/rope-sequence-1.3.2.tgz#a19e02d72991ca71feb6b5f8a91154e48e3c098b"
+ integrity sha512-ku6MFrwEVSVmXLvy3dYph3LAMNS0890K7fabn+0YIRQ2T96T9F4gkFf0vf0WW0JUraNWwGRtInEpH7yO4tbQZg==
+
+run-async@^2.4.0:
+ version "2.4.1"
+ resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455"
+ integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==
+
+run-parallel@^1.1.9:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee"
+ integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==
+ dependencies:
+ queue-microtask "^1.2.2"
+
+rxjs@^7.5.5:
+ version "7.5.5"
+ resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f"
+ integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==
+ dependencies:
+ tslib "^2.1.0"
+
+safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
+ version "5.1.2"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
+ integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
+
+safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0:
+ version "5.2.1"
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
+ integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
+
+"safer-buffer@>= 2.1.2 < 3":
+ version "2.1.2"
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
+ integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
+
+sass@1.32.12:
+ version "1.32.12"
+ resolved "https://registry.yarnpkg.com/sass/-/sass-1.32.12.tgz#a2a47ad0f1c168222db5206444a30c12457abb9f"
+ integrity sha512-zmXn03k3hN0KaiVTjohgkg98C3UowhL1/VSGdj4/VAAiMKGQOE80PFPxFP2Kyq0OUskPKcY5lImkhBKEHlypJA==
+ dependencies:
+ chokidar ">=3.0.0 <4.0.0"
+
+sax@1.1.4:
+ version "1.1.4"
+ resolved "https://registry.yarnpkg.com/sax/-/sax-1.1.4.tgz#74b6d33c9ae1e001510f179a91168588f1aedaa9"
+ integrity sha1-dLbTPJrh4AFRDxeakRaFiPGu2qk=
+
+select@^1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
+ integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
+
+semver@^6.3.0:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
+ integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==
+
+semver@^7.0.0, semver@^7.3.5:
+ version "7.3.6"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.6.tgz#5d73886fb9c0c6602e79440b97165c29581cbb2b"
+ integrity sha512-HZWqcgwLsjaX1HBD31msI/rXktuIhS+lWvdE4kN9z+8IVT4Itc7vqU2WvYsyD6/sjYCt4dEKH/m1M3dwI9CC5w==
+ dependencies:
+ lru-cache "^7.4.0"
+
+send@0.17.2:
+ version "0.17.2"
+ resolved "https://registry.yarnpkg.com/send/-/send-0.17.2.tgz#926622f76601c41808012c8bf1688fe3906f7820"
+ integrity sha512-UJYB6wFSJE3G00nEivR5rgWp8c2xXvJ3OPWPhmuteU0IKj8nKbG3DrjiOmLwpnHGYWAVwA69zmTm++YG0Hmwww==
+ dependencies:
+ debug "2.6.9"
+ depd "~1.1.2"
+ destroy "~1.0.4"
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ etag "~1.8.1"
+ fresh "0.5.2"
+ http-errors "1.8.1"
+ mime "1.6.0"
+ ms "2.1.3"
+ on-finished "~2.3.0"
+ range-parser "~1.2.1"
+ statuses "~1.5.0"
+
+serialize-javascript@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8"
+ integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==
+ dependencies:
+ randombytes "^2.1.0"
+
+serve-static@1.14.2:
+ version "1.14.2"
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.14.2.tgz#722d6294b1d62626d41b43a013ece4598d292bfa"
+ integrity sha512-+TMNA9AFxUEGuC0z2mevogSnn9MXKb4fa7ngeRMJaaGv8vTwnIEkKi+QGvPt33HSnf8pRS+WGM0EbMtCJLKMBQ==
+ dependencies:
+ encodeurl "~1.0.2"
+ escape-html "~1.0.3"
+ parseurl "~1.3.3"
+ send "0.17.2"
+
+setprototypeof@1.2.0:
+ version "1.2.0"
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
+ integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+
+shallow-clone@^3.0.0:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3"
+ integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==
+ dependencies:
+ kind-of "^6.0.2"
+
+shebang-command@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
+ integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
+ dependencies:
+ shebang-regex "^3.0.0"
+
+shebang-regex@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
+ integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
+
+side-channel@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
+ integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
+ dependencies:
+ call-bind "^1.0.0"
+ get-intrinsic "^1.0.2"
+ object-inspect "^1.9.0"
+
+signal-exit@^3.0.2:
+ version "3.0.7"
+ resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
+ integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+
+slice-ansi@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+ integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+ dependencies:
+ ansi-styles "^4.0.0"
+ astral-regex "^2.0.0"
+ is-fullwidth-code-point "^3.0.0"
+
+sortablejs@1.14.0:
+ version "1.14.0"
+ resolved "https://registry.yarnpkg.com/sortablejs/-/sortablejs-1.14.0.tgz#6d2e17ccbdb25f464734df621d4f35d4ab35b3d8"
+ integrity sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==
+
+source-map-js@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
+ integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
+
+source-map@0.6.1, source-map@^0.6.1, source-map@~0.6.0:
+ version "0.6.1"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
+ integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+
+source-map@^0.7.3:
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.7.3.tgz#5302f8169031735226544092e64981f751750383"
+ integrity sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==
+
+sourcemap-codec@^1.4.8:
+ version "1.4.8"
+ resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4"
+ integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==
+
+"statuses@>= 1.5.0 < 2", statuses@~1.5.0:
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
+ integrity sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=
+
+string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
+ integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
+ dependencies:
+ emoji-regex "^8.0.0"
+ is-fullwidth-code-point "^3.0.0"
+ strip-ansi "^6.0.1"
+
+string.prototype.trimend@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80"
+ integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+
+string.prototype.trimstart@^1.0.4:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed"
+ integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw==
+ dependencies:
+ call-bind "^1.0.2"
+ define-properties "^1.1.3"
+
+string_decoder@^1.1.1:
+ version "1.3.0"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
+ integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
+ dependencies:
+ safe-buffer "~5.2.0"
+
+string_decoder@~1.1.1:
+ version "1.1.1"
+ resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
+ integrity sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==
+ dependencies:
+ safe-buffer "~5.1.0"
+
+strip-ansi@^6.0.0, strip-ansi@^6.0.1:
+ version "6.0.1"
+ resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
+ integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
+ dependencies:
+ ansi-regex "^5.0.1"
+
+strip-bom@^3.0.0:
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/strip-bom/-/strip-bom-3.0.0.tgz#2334c18e9c759f7bdd56fdef7e9ae3d588e68ed3"
+ integrity sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=
+
+strip-json-comments@^3.1.0, strip-json-comments@^3.1.1:
+ version "3.1.1"
+ resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006"
+ integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==
+
+style-mod@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/style-mod/-/style-mod-4.0.0.tgz#97e7c2d68b592975f2ca7a63d0dd6fcacfe35a01"
+ integrity sha512-OPhtyEjyyN9x3nhPsu76f52yUGXiZcgvsrFVtvTkyGRQJ0XK+GPc6ov1z+lRpbeabka+MYEQxOYRnt5nF30aMw==
+
+supports-color@^7.1.0:
+ version "7.2.0"
+ resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
+ integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
+ dependencies:
+ has-flag "^4.0.0"
+
+supports-preserve-symlinks-flag@^1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09"
+ integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
+
+symbol-observable@^4.0.0:
+ version "4.0.0"
+ resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-4.0.0.tgz#5b425f192279e87f2f9b937ac8540d1984b39205"
+ integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
+
+table@^6.8.0:
+ version "6.8.0"
+ resolved "https://registry.yarnpkg.com/table/-/table-6.8.0.tgz#87e28f14fa4321c3377ba286f07b79b281a3b3ca"
+ integrity sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==
+ dependencies:
+ ajv "^8.0.1"
+ lodash.truncate "^4.4.2"
+ slice-ansi "^4.0.0"
+ string-width "^4.2.3"
+ strip-ansi "^6.0.1"
+
+tar-stream@^2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.2.0.tgz#acad84c284136b060dc3faa64474aa9aebd77287"
+ integrity sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==
+ dependencies:
+ bl "^4.0.3"
+ end-of-stream "^1.4.1"
+ fs-constants "^1.0.0"
+ inherits "^2.0.3"
+ readable-stream "^3.1.1"
+
+text-table@^0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4"
+ integrity sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=
+
+throttle-debounce@^3.0.1:
+ version "3.0.1"
+ resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-3.0.1.tgz#32f94d84dfa894f786c9a1f290e7a645b6a19abb"
+ integrity sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==
+
+through@^2.3.6:
+ version "2.3.8"
+ resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+ integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
+
+tiny-emitter@^2.0.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
+ integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==
+
+tippy.js@6.3.7, tippy.js@^6.3.7:
+ version "6.3.7"
+ resolved "https://registry.yarnpkg.com/tippy.js/-/tippy.js-6.3.7.tgz#8ccfb651d642010ed9a32ff29b0e9e19c5b8c61c"
+ integrity sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==
+ dependencies:
+ "@popperjs/core" "^2.9.0"
+
+tmp@^0.0.33:
+ version "0.0.33"
+ resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9"
+ integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==
+ dependencies:
+ os-tmpdir "~1.0.2"
+
+to-fast-properties@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/to-fast-properties/-/to-fast-properties-2.0.0.tgz#dc5e698cbd079265bc73e0377681a4e4e83f616e"
+ integrity sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=
+
+to-regex-range@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4"
+ integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==
+ dependencies:
+ is-number "^7.0.0"
+
+toidentifier@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
+ integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
+
+token-stream@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/token-stream/-/token-stream-1.0.0.tgz#cc200eab2613f4166d27ff9afc7ca56d49df6eb4"
+ integrity sha1-zCAOqyYT9BZtJ/+a/HylbUnfbrQ=
+
+ts-invariant@^0.9.4:
+ version "0.9.4"
+ resolved "https://registry.yarnpkg.com/ts-invariant/-/ts-invariant-0.9.4.tgz#42ac6c791aade267dd9dc65276549df5c5d71cac"
+ integrity sha512-63jtX/ZSwnUNi/WhXjnK8kz4cHHpYS60AnmA6ixz17l7E12a5puCWFlNpkne5Rl0J8TBPVHpGjsj4fxs8ObVLQ==
+ dependencies:
+ tslib "^2.1.0"
+
+tsconfig-paths@^3.14.1:
+ version "3.14.1"
+ resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz#ba0734599e8ea36c862798e920bcf163277b137a"
+ integrity sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==
+ dependencies:
+ "@types/json5" "^0.0.29"
+ json5 "^1.0.1"
+ minimist "^1.2.6"
+ strip-bom "^3.0.0"
+
+tslib@^2.1.0, tslib@^2.3.0:
+ version "2.3.1"
+ resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
+ integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
+
+type-check@^0.4.0, type-check@~0.4.0:
+ version "0.4.0"
+ resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1"
+ integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==
+ dependencies:
+ prelude-ls "^1.2.1"
+
+type-fest@^0.20.2:
+ version "0.20.2"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4"
+ integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==
+
+type-fest@^0.21.3:
+ version "0.21.3"
+ resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37"
+ integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==
+
+type-is@~1.6.18:
+ version "1.6.18"
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
+ integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
+ dependencies:
+ media-typer "0.3.0"
+ mime-types "~2.1.24"
+
+uglify-js@^3.5.1:
+ version "3.15.3"
+ resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.3.tgz#9aa82ca22419ba4c0137642ba0df800cb06e0471"
+ integrity sha512-6iCVm2omGJbsu3JWac+p6kUiOpg3wFO2f8lIXjfEb8RrmLjzog1wTPMmwKB7swfzzqxj9YM+sGUM++u1qN4qJg==
+
+unbox-primitive@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471"
+ integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw==
+ dependencies:
+ function-bind "^1.1.1"
+ has-bigints "^1.0.1"
+ has-symbols "^1.0.2"
+ which-boxed-primitive "^1.0.2"
+
+universalify@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/universalify/-/universalify-2.0.0.tgz#75a4984efedc4b08975c5aeb73f530d02df25717"
+ integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
+
+unpipe@1.0.0, unpipe@~1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
+ integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=
+
+upper-case@^1.1.1:
+ version "1.1.3"
+ resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598"
+ integrity sha1-9rRQHC7EzdJrp4vnIilh3ndiFZg=
+
+uri-js@^4.2.2:
+ version "4.4.1"
+ resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e"
+ integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==
+ dependencies:
+ punycode "^2.1.0"
+
+util-deprecate@^1.0.1, util-deprecate@~1.0.1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
+ integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
+
+utils-merge@1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
+ integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
+
+uuid@8.3.2:
+ version "8.3.2"
+ resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2"
+ integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==
+
+v-network-graph@0.5.9:
+ version "0.5.9"
+ resolved "https://registry.yarnpkg.com/v-network-graph/-/v-network-graph-0.5.9.tgz#4276a9bc9edf3b7ff8b65eb5e2ce1fe685064336"
+ integrity sha512-gJNRlaEhiR8HuCxDVpylNCO6ZJusTHKHA6vlH6ls5R4lHerHatlTvMAaajqkMVU39Ln8v3dJA/PBVSKZt+RYmg==
+ dependencies:
+ "@dash14/svg-pan-zoom" "^3.6.6"
+ mitt "^3.0.0"
+
+v8-compile-cache@^2.0.3:
+ version "2.3.0"
+ resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz#2de19618c66dc247dcfb6f99338035d8245a2cee"
+ integrity sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==
+
+vary@~1.1.2:
+ version "1.1.2"
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
+ integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=
+
+vite@2.9.1:
+ version "2.9.1"
+ resolved "https://registry.yarnpkg.com/vite/-/vite-2.9.1.tgz#84bce95fae210a7beb566a0af06246748066b48f"
+ integrity sha512-vSlsSdOYGcYEJfkQ/NeLXgnRv5zZfpAsdztkIrs7AZHV8RCMZQkwjo4DS5BnrYTqoWqLoUe1Cah4aVO4oNNqCQ==
+ dependencies:
+ esbuild "^0.14.27"
+ postcss "^8.4.12"
+ resolve "^1.22.0"
+ rollup "^2.59.0"
+ optionalDependencies:
+ fsevents "~2.3.2"
+
+void-elements@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
+ integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk=
+
+vue-demi@*:
+ version "0.12.5"
+ resolved "https://registry.yarnpkg.com/vue-demi/-/vue-demi-0.12.5.tgz#8eeed566a7d86eb090209a11723f887d28aeb2d1"
+ integrity sha512-BREuTgTYlUr0zw0EZn3hnhC3I6gPWv+Kwh4MCih6QcAeaTlaIX0DwOVN0wHej7hSvDPecz4jygy/idsgKfW58Q==
+
+vue-eslint-parser@^8.0.1:
+ version "8.3.0"
+ resolved "https://registry.yarnpkg.com/vue-eslint-parser/-/vue-eslint-parser-8.3.0.tgz#5d31129a1b3dd89c0069ca0a1c88f970c360bd0d"
+ integrity sha512-dzHGG3+sYwSf6zFBa0Gi9ZDshD7+ad14DGOdTLjruRVgZXe2J+DcZ9iUhyR48z5g1PqRa20yt3Njna/veLJL/g==
+ dependencies:
+ debug "^4.3.2"
+ eslint-scope "^7.0.0"
+ eslint-visitor-keys "^3.1.0"
+ espree "^9.0.0"
+ esquery "^1.4.0"
+ lodash "^4.17.21"
+ semver "^7.3.5"
+
+vue-i18n@9.1.9:
+ version "9.1.9"
+ resolved "https://registry.yarnpkg.com/vue-i18n/-/vue-i18n-9.1.9.tgz#cb53e06ab5cc5b7eed59332f151caf48d47be9bb"
+ integrity sha512-JeRdNVxS2OGp1E+pye5XB6+M6BBkHwAv9C80Q7+kzoMdUDGRna06tjC0vCB/jDX9aWrl5swxOMFcyAr7or8XTA==
+ dependencies:
+ "@intlify/core-base" "9.1.9"
+ "@intlify/shared" "9.1.9"
+ "@intlify/vue-devtools" "9.1.9"
+ "@vue/devtools-api" "^6.0.0-beta.7"
+
+vue-router@4.0.14:
+ version "4.0.14"
+ resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-4.0.14.tgz#ce2028c1c5c33e30c7287950c973f397fce1bd65"
+ integrity sha512-wAO6zF9zxA3u+7AkMPqw9LjoUCjSxfFvINQj3E/DceTt6uEz1XZLraDhdg2EYmvVwTBSGlLYsUw8bDmx0754Mw==
+ dependencies:
+ "@vue/devtools-api" "^6.0.0"
+
+vue@3.2.31:
+ version "3.2.31"
+ resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.31.tgz#e0c49924335e9f188352816788a4cca10f817ce6"
+ integrity sha512-odT3W2tcffTiQCy57nOT93INw1auq5lYLLYtWpPYQQYQOOdHiqFct9Xhna6GJ+pJQaF67yZABraH47oywkJgFw==
+ dependencies:
+ "@vue/compiler-dom" "3.2.31"
+ "@vue/compiler-sfc" "3.2.31"
+ "@vue/runtime-dom" "3.2.31"
+ "@vue/server-renderer" "3.2.31"
+ "@vue/shared" "3.2.31"
+
+vuedraggable@4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/vuedraggable/-/vuedraggable-4.1.0.tgz#edece68adb8a4d9e06accff9dfc9040e66852270"
+ integrity sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==
+ dependencies:
+ sortablejs "1.14.0"
+
+w3c-keyname@^2.2.0, w3c-keyname@^2.2.4:
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/w3c-keyname/-/w3c-keyname-2.2.4.tgz#4ade6916f6290224cdbd1db8ac49eab03d0eef6b"
+ integrity sha512-tOhfEwEzFLJzf6d1ZPkYfGj+FWhIpBux9ppoP3rlclw3Z0BZv3N7b7030Z1kYth+6rDuAsXUFr+d0VE6Ed1ikw==
+
+wcwidth@^1.0.1:
+ version "1.0.1"
+ resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"
+ integrity sha1-8LDc+RW8X/FSivrbLA4XtTLaL+g=
+ dependencies:
+ defaults "^1.0.3"
+
+webpack-merge@^5.8.0:
+ version "5.8.0"
+ resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.8.0.tgz#2b39dbf22af87776ad744c390223731d30a68f61"
+ integrity sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==
+ dependencies:
+ clone-deep "^4.0.1"
+ wildcard "^2.0.0"
+
+which-boxed-primitive@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6"
+ integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==
+ dependencies:
+ is-bigint "^1.0.1"
+ is-boolean-object "^1.1.0"
+ is-number-object "^1.0.4"
+ is-string "^1.0.5"
+ is-symbol "^1.0.3"
+
+which@^2.0.1:
+ version "2.0.2"
+ resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
+ integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
+ dependencies:
+ isexe "^2.0.0"
+
+wildcard@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.0.tgz#a77d20e5200c6faaac979e4b3aadc7b3dd7f8fec"
+ integrity sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==
+
+with@^7.0.0:
+ version "7.0.2"
+ resolved "https://registry.yarnpkg.com/with/-/with-7.0.2.tgz#ccee3ad542d25538a7a7a80aad212b9828495bac"
+ integrity sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==
+ dependencies:
+ "@babel/parser" "^7.9.6"
+ "@babel/types" "^7.9.6"
+ assert-never "^1.2.1"
+ babel-walk "3.0.0-canary-5"
+
+word-wrap@^1.2.3:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.3.tgz#610636f6b1f703891bd34771ccb17fb93b47079c"
+ integrity sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==
+
+wrap-ansi@^7.0.0:
+ version "7.0.0"
+ resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
+ integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
+ dependencies:
+ ansi-styles "^4.0.0"
+ string-width "^4.1.0"
+ strip-ansi "^6.0.0"
+
+wrappy@1:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
+ integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=
+
+y18n@^5.0.5:
+ version "5.0.8"
+ resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55"
+ integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==
+
+yaml-eslint-parser@^0.3.2:
+ version "0.3.2"
+ resolved "https://registry.yarnpkg.com/yaml-eslint-parser/-/yaml-eslint-parser-0.3.2.tgz#c7f5f3904f1c06ad55dc7131a731b018426b4898"
+ integrity sha512-32kYO6kJUuZzqte82t4M/gB6/+11WAuHiEnK7FreMo20xsCKPeFH5tDBU7iWxR7zeJpNnMXfJyXwne48D0hGrg==
+ dependencies:
+ eslint-visitor-keys "^1.3.0"
+ lodash "^4.17.20"
+ yaml "^1.10.0"
+
+yaml@^1.10.0:
+ version "1.10.2"
+ resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
+ integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
+
+yargs-parser@^21.0.0:
+ version "21.0.1"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.1.tgz#0267f286c877a4f0f728fceb6f8a3e4cb95c6e35"
+ integrity sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==
+
+yargs@^17.3.1:
+ version "17.4.0"
+ resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.4.0.tgz#9fc9efc96bd3aa2c1240446af28499f0e7593d00"
+ integrity sha512-WJudfrk81yWFSOkZYpAZx4Nt7V4xp7S/uJkX0CnxovMCt1wCE8LNftPpNuF9X/u9gN5nsD7ycYtRcDf2pL3UiA==
+ dependencies:
+ cliui "^7.0.2"
+ escalade "^3.1.1"
+ get-caller-file "^2.0.5"
+ require-directory "^2.1.1"
+ string-width "^4.2.3"
+ y18n "^5.0.5"
+ yargs-parser "^21.0.0"
+
+zen-observable-ts@^1.2.0:
+ version "1.2.3"
+ resolved "https://registry.yarnpkg.com/zen-observable-ts/-/zen-observable-ts-1.2.3.tgz#c2f5ccebe812faf0cfcde547e6004f65b1a6d769"
+ integrity sha512-hc/TGiPkAWpByykMwDcem3SdUgA4We+0Qb36bItSuJC9xD0XVBZoFHYoadAomDSNf64CG8Ydj0Qb8Od8BUWz5g==
+ dependencies:
+ zen-observable "0.8.15"
+
+zen-observable@0.8.15:
+ version "0.8.15"
+ resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
+ integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
+
+zip-stream@^4.1.0:
+ version "4.1.0"
+ resolved "https://registry.yarnpkg.com/zip-stream/-/zip-stream-4.1.0.tgz#51dd326571544e36aa3f756430b313576dc8fc79"
+ integrity sha512-zshzwQW7gG7hjpBlgeQP9RuyPGNxvJdzR8SUM3QhxCnLjWN2E7j3dOvpeDcQoETfHx0urRS7EtmVToql7YpU4A==
+ dependencies:
+ archiver-utils "^2.1.0"
+ compress-commons "^4.1.0"
+ readable-stream "^3.6.0"
+
+zxcvbn@4.4.2:
+ version "4.4.2"
+ resolved "https://registry.yarnpkg.com/zxcvbn/-/zxcvbn-4.4.2.tgz#28ec17cf09743edcab056ddd8b1b06262cc73c30"
+ integrity sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=