diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 1cd1a1c6..cc8218df 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -33,11 +33,15 @@
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or with the host.
- "forwardPorts": [3000, 5432, 8000],
+ "forwardPorts": [3000, 3001, 5432, 8000],
"portsAttributes": {
"3000": {
- "label": "Application",
+ "label": "Application Backend",
+ "onAutoForward": "silent"
+ },
+ "3001": {
+ "label": "Application Frontend",
"onAutoForward": "silent"
},
"5432": {
diff --git a/backend/api/authentication.mjs b/backend/api/authentication.mjs
new file mode 100644
index 00000000..b2b8e68d
--- /dev/null
+++ b/backend/api/authentication.mjs
@@ -0,0 +1,58 @@
+/**
+ * Authentication API Routes
+ */
+async function routes (app, options) {
+ app.get('/sites/:siteId/auth/strategies', {
+ schema: {
+ summary: 'List all site authentication strategies',
+ tags: ['Authentication'],
+ params: {
+ type: 'object',
+ properties: {
+ siteId: {
+ type: 'string',
+ format: 'uuid'
+ }
+ }
+ },
+ querystring: {
+ type: 'object',
+ properties: {
+ visibleOnly: {
+ type: 'boolean',
+ default: false
+ }
+ }
+ }
+ }
+ }, async (req, reply) => {
+ return []
+ })
+
+ app.post('/auth/login', {
+ schema: {
+ summary: 'Login',
+ tags: ['Authentication'],
+ body: {
+ type: 'object',
+ required: ['path'],
+ properties: {
+ path: {
+ type: 'string',
+ minLength: 1,
+ maxLength: 255
+ }
+ },
+ examples: [
+ {
+ path: 'foo/bar'
+ }
+ ]
+ }
+ }
+ }, async (req, reply) => {
+ return []
+ })
+}
+
+export default routes
diff --git a/backend/api/index.mjs b/backend/api/index.mjs
index e6aa0353..9f5f141d 100644
--- a/backend/api/index.mjs
+++ b/backend/api/index.mjs
@@ -2,6 +2,9 @@
* API Routes
*/
async function routes (app, options) {
+ app.register(import('./authentication.mjs'))
+ app.register(import('./locales.mjs'), { prefix: '/locales' })
+ app.register(import('./pages.mjs'))
app.register(import('./sites.mjs'), { prefix: '/sites' })
app.register(import('./system.mjs'), { prefix: '/system' })
app.register(import('./users.mjs'), { prefix: '/users' })
diff --git a/backend/api/locales.mjs b/backend/api/locales.mjs
new file mode 100644
index 00000000..0fc16bee
--- /dev/null
+++ b/backend/api/locales.mjs
@@ -0,0 +1,24 @@
+/**
+ * Locales API Routes
+ */
+async function routes (app, options) {
+ app.get('/', {
+ schema: {
+ summary: 'List all locales',
+ tags: ['Locales']
+ }
+ }, async (req, reply) => {
+ return WIKI.models.locales.getLocales()
+ })
+
+ app.get('/:code/strings', {
+ schema: {
+ summary: 'Get locale strings',
+ tags: ['Locales']
+ }
+ }, async (req, reply) => {
+ return WIKI.models.locales.getStrings(req.params.code)
+ })
+}
+
+export default routes
diff --git a/backend/api/pages.mjs b/backend/api/pages.mjs
new file mode 100644
index 00000000..7c4152b6
--- /dev/null
+++ b/backend/api/pages.mjs
@@ -0,0 +1,92 @@
+/**
+ * Pages API Routes
+ */
+async function routes (app, options) {
+ app.get('/sites/:siteId/pages', {
+ schema: {
+ summary: 'List all pages',
+ tags: ['Pages'],
+ params: {
+ type: 'object',
+ properties: {
+ siteId: {
+ type: 'string',
+ format: 'uuid'
+ }
+ }
+ }
+ }
+ }, async (req, reply) => {
+ return []
+ })
+
+ app.get('/sites/:siteId/pages/:pageIdOrHash', {
+ schema: {
+ summary: 'List all pages',
+ tags: ['Pages'],
+ params: {
+ type: 'object',
+ properties: {
+ siteId: {
+ type: 'string',
+ format: 'uuid'
+ },
+ pageIdOrHash: {
+ type: 'string',
+ oneOf: [
+ { format: 'uuid' },
+ { pattern: '^[a-f0-9]+$' }
+ ]
+ }
+ }
+ },
+ querystring: {
+ type: 'object',
+ properties: {
+ withContent: {
+ type: 'boolean',
+ default: false
+ }
+ }
+ }
+ }
+ }, async (req, reply) => {
+ return []
+ })
+
+ app.post('/sites/:siteId/pages/userPermissions', {
+ schema: {
+ summary: 'Get page user permissions',
+ tags: ['Pages'],
+ params: {
+ type: 'object',
+ properties: {
+ siteId: {
+ type: 'string',
+ format: 'uuid'
+ }
+ }
+ },
+ body: {
+ type: 'object',
+ required: ['path'],
+ properties: {
+ path: {
+ type: 'string',
+ minLength: 1,
+ maxLength: 255
+ }
+ },
+ examples: [
+ {
+ path: 'foo/bar'
+ }
+ ]
+ }
+ }
+ }, async (req, reply) => {
+ return []
+ })
+}
+
+export default routes
diff --git a/backend/api/sites.mjs b/backend/api/sites.mjs
index 6c7fc086..964b900d 100644
--- a/backend/api/sites.mjs
+++ b/backend/api/sites.mjs
@@ -1,3 +1,5 @@
+import { validate as uuidValidate } from 'uuid'
+
/**
* Sites API Routes
*/
@@ -11,13 +13,23 @@ async function routes (app, options) {
return { hello: 'world' }
})
- app.get('/:siteId', {
+ app.get('/:siteIdorHostname', {
schema: {
summary: 'Get site info',
tags: ['Sites']
}
}, async (req, reply) => {
- return { hello: 'world' }
+ const site = (uuidValidate(req.params.siteId))
+ ? await WIKI.models.sites.getSiteById({ id: req.params.siteId })
+ : await WIKI.models.sites.getSiteByHostname({ hostname: req.params.siteId })
+ return site
+ ? {
+ ...site.config,
+ id: site.id,
+ hostname: site.hostname,
+ isEnabled: site.isEnabled
+ }
+ : null
})
/**
diff --git a/backend/api/system.mjs b/backend/api/system.mjs
index de5ca62a..f74cddcc 100644
--- a/backend/api/system.mjs
+++ b/backend/api/system.mjs
@@ -10,6 +10,15 @@ async function routes (app, options) {
}, async (request, reply) => {
return { hello: 'world' }
})
+
+ app.get('/flags', {
+ schema: {
+ summary: 'System Flags',
+ tags: ['System']
+ }
+ }, async (request, reply) => {
+ return { hello: 'world' }
+ })
}
export default routes
diff --git a/backend/core/db.mjs b/backend/core/db.mjs
index 99668c8c..05774627 100644
--- a/backend/core/db.mjs
+++ b/backend/core/db.mjs
@@ -8,6 +8,7 @@ import { Pool } from 'pg'
import PGPubSub from 'pg-pubsub'
import semver from 'semver'
+import { relations } from '../db/relations.mjs'
import { createDeferred } from '../helpers/common.mjs'
// import migrationSource from '../db/migrator-source.mjs'
// const migrateFromLegacy = require('../db/legacy')
@@ -93,7 +94,7 @@ export default {
options: `-c search_path=${WIKI.config.db.schema}`
})
- const db = drizzle({ client: this.pool })
+ const db = drizzle({ client: this.pool, relations })
// Connect
await this.connect(db)
diff --git a/backend/db/relations.mjs b/backend/db/relations.mjs
new file mode 100644
index 00000000..c9efc81e
--- /dev/null
+++ b/backend/db/relations.mjs
@@ -0,0 +1,16 @@
+import { defineRelations } from 'drizzle-orm'
+import * as schema from './schema.mjs'
+
+export const relations = defineRelations(schema,
+ r => ({
+ users: {
+ groups: r.many.groups({
+ from: r.users.id.through(r.userGroups.userId),
+ to: r.groups.id.through(r.userGroups.groupId)
+ })
+ },
+ groups: {
+ members: r.many.users()
+ }
+ })
+)
diff --git a/backend/db/schema.mjs b/backend/db/schema.mjs
index 0820880a..e0338923 100644
--- a/backend/db/schema.mjs
+++ b/backend/db/schema.mjs
@@ -1,4 +1,4 @@
-import { defineRelations, sql } from 'drizzle-orm'
+import { sql } from 'drizzle-orm'
import { bigint, boolean, bytea, customType, index, integer, jsonb, pgEnum, pgTable, primaryKey, text, timestamp, uniqueIndex, uuid, varchar } from 'drizzle-orm/pg-core'
// == CUSTOM TYPES =====================
@@ -325,19 +325,3 @@ export const userGroups = pgTable('userGroups', {
index('userGroups_groupId_idx').on(table.groupId),
index('userGroups_composite_idx').on(table.userId, table.groupId)
])
-
-// == RELATIONS ========================
-
-export const relations = defineRelations({ users, groups, userGroups },
- r => ({
- users: {
- groups: r.many.groups({
- from: r.users.id.through(r.userGroups.userId),
- to: r.groups.id.through(r.userGroups.groupId)
- })
- },
- groups: {
- members: r.many.users()
- }
- })
-)
diff --git a/backend/index.mjs b/backend/index.mjs
index ec2e176a..ac21f72f 100644
--- a/backend/index.mjs
+++ b/backend/index.mjs
@@ -27,6 +27,7 @@ import fastifyView from '@fastify/view'
import gracefulServer from '@gquittet/graceful-server'
import ajvFormats from 'ajv-formats'
import pug from 'pug'
+import NodeCache from 'node-cache'
import configSvc from './core/config.mjs'
import dbManager from './core/db.mjs'
@@ -111,6 +112,8 @@ async function preBoot () {
}
process.exit(1)
}
+
+ WIKI.cache = new NodeCache({ checkperiod: 0 })
}
// ----------------------------------------
@@ -118,6 +121,9 @@ async function preBoot () {
// ----------------------------------------
async function postBoot () {
+ await WIKI.models.locales.refreshFromDisk()
+
+ await WIKI.models.locales.reloadCache()
await WIKI.models.sites.reloadCache()
}
diff --git a/backend/locales/en.json b/backend/locales/en.json
new file mode 100644
index 00000000..09d14615
--- /dev/null
+++ b/backend/locales/en.json
@@ -0,0 +1,1911 @@
+{
+ "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.copyKeyTitle": "Copy API Key",
+ "admin.api.createInvalidData": "Some fields are missing or have invalid data.",
+ "admin.api.createSuccess": "API Key created successfully.",
+ "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.groupSelected": "Use {group} group permissions",
+ "admin.api.groupsMissing": "You must select at least 1 group for this key.",
+ "admin.api.groupsSelected": "Use permissions from {count} groups",
+ "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.key": "API Key",
+ "admin.api.nameInvalidChars": "Key name has invalid characters.",
+ "admin.api.nameMissing": "Key name is missing.",
+ "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.none": "There are no API keys yet.",
+ "admin.api.permissionGroups": "Group Permissions",
+ "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.revoked": "Revoked",
+ "admin.api.revokedHint": "This key has been revoked and can no longer be used.",
+ "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.allowedEmailRegex": "Allowed Email Address Regex",
+ "admin.auth.allowedEmailRegexHint": "(optional) Only allow users to register with an email address that matches the regex expression.",
+ "admin.auth.allowedWebOrigins": "Allowed Web Origins",
+ "admin.auth.autoEnrollGroups": "Assign to group(s)",
+ "admin.auth.autoEnrollGroupsHint": "(optional) Automatically assign new users to these groups. New users are always added to the Users group regardless of this setting.",
+ "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.emailValidation": "Email Validation",
+ "admin.auth.emailValidationHint": "Send a verification email to the user with a validation link when registering.",
+ "admin.auth.enabled": "Enabled",
+ "admin.auth.enabledForced": "This strategy cannot be disabled.",
+ "admin.auth.enabledHint": "Should this strategy be available to sites for login.",
+ "admin.auth.enforceTfa": "Enforce Two-Factor Authentication",
+ "admin.auth.enforceTfaHint": "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.info": "Info",
+ "admin.auth.infoName": "Name",
+ "admin.auth.infoNameHint": "Display name for this strategy.",
+ "admin.auth.loginUrl": "Login URL",
+ "admin.auth.logoutUrl": "Logout URL",
+ "admin.auth.noConfigOption": "This strategy has no configuration options you can modify.",
+ "admin.auth.refreshSuccess": "List of strategies has been refreshed.",
+ "admin.auth.registration": "Registration",
+ "admin.auth.registrationHint": "Allow any user successfully authorized by the strategy to access the wiki.",
+ "admin.auth.registrationLocalHint": "Whether to allow guests to register new accounts.",
+ "admin.auth.saveSuccess": "Authentication configuration saved successfully.",
+ "admin.auth.security": "Security",
+ "admin.auth.siteUrlNotSetup": "You must set a valid {siteUrl} first! Click on {general} in the left sidebar.",
+ "admin.auth.status": "Status",
+ "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.auth.vendor": "Vendor",
+ "admin.auth.vendorWebsite": "Website",
+ "admin.blocks.add": "Add Block",
+ "admin.blocks.builtin": "Built-in",
+ "admin.blocks.custom": "Custom",
+ "admin.blocks.isEnabled": "Enabled",
+ "admin.blocks.saveSuccess": "Blocks state saved successfully.",
+ "admin.blocks.subtitle": "Manage dynamic components available for use inside pages.",
+ "admin.blocks.title": "Content Blocks",
+ "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.asciidocDescription": "Use the AsciiDoc syntax to write content. Includes real-time preview.",
+ "admin.editors.asciidocName": "AsciiDoc 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.markdown.allowHTML": "Allow HTML",
+ "admin.editors.markdown.allowHTMLHint": "Allow HTML tags in content.",
+ "admin.editors.markdown.general": "General",
+ "admin.editors.markdown.kroki": "Kroki",
+ "admin.editors.markdown.krokiHint": "Enable Kroki Diagrams Parser",
+ "admin.editors.markdown.krokiServerUrl": "Kroki Server URL",
+ "admin.editors.markdown.krokiServerUrlHint": "URL to the Kroki server used for image generation.",
+ "admin.editors.markdown.latexEngine": "LaTeX Engine",
+ "admin.editors.markdown.latexEngineHint": "Which engine to use to process TeX/LaTeX expressions.",
+ "admin.editors.markdown.lineBreaks": "Auto Line Breaks",
+ "admin.editors.markdown.lineBreaksHint": "Automatically add linebreaks within paragraphs.",
+ "admin.editors.markdown.linkify": "Auto-linking",
+ "admin.editors.markdown.linkifyHint": "Automatically convert URLs into clickable links.",
+ "admin.editors.markdown.multimdTable": "MultiMarkdown Table",
+ "admin.editors.markdown.multimdTableHint": "Enable support for MultiMarkdown Table features.",
+ "admin.editors.markdown.plantuml": "PlantUML",
+ "admin.editors.markdown.plantumlHint": "Enable PlantUML Parser",
+ "admin.editors.markdown.plantumlServerUrl": "PlantUML Server URL",
+ "admin.editors.markdown.plantumlServerUrlHint": "URL to the PlantUML server used for image generation.",
+ "admin.editors.markdown.quotes": "Quotes Style",
+ "admin.editors.markdown.quotesHint": "When typographer is enabled. Double + single quotes replacement pairs. e.g. «»„“ for Russian, „“‚‘ for German, etc.",
+ "admin.editors.markdown.saveSuccess": "Markdown editor configuration saved successfully.",
+ "admin.editors.markdown.tabWidth": "Code Block Tab Width",
+ "admin.editors.markdown.tabWidthHint": "Amount of spaces for each tab in code blocks.",
+ "admin.editors.markdown.typographer": "Typographer",
+ "admin.editors.markdown.typographerHint": "Enable some language-neutral replacement + quotes beautification.",
+ "admin.editors.markdown.underline": "Underline Emphasis",
+ "admin.editors.markdown.underlineHint": "Enable text underlining by using _underline_ syntax.",
+ "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.saveSuccess": "Editors state saved successfully.",
+ "admin.editors.subtitle": "Manage editors and their configuration",
+ "admin.editors.title": "Editors",
+ "admin.editors.useRenderingPipeline": "Uses the rendering pipeline.",
+ "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.advanced.hint": "Set custom configuration flags. Note that all values are public to all users! Do not insert senstive data.",
+ "admin.flags.advanced.label": "Custom Configuration",
+ "admin.flags.authDebug.hint": "Log detailed debug info of all login / registration attempts.",
+ "admin.flags.authDebug.label": "Auth Debug",
+ "admin.flags.experimental.hint": "Enable unstable / unfinished features. DO NOT enable in a production environment!",
+ "admin.flags.experimental.label": "Experimental Features",
+ "admin.flags.getTokenHint": "Copy your current authentication token for use in GraphQL API testing.",
+ "admin.flags.getTokenLabel": "Get Current Token",
+ "admin.flags.saveSuccess": "Flags have been updated successfully.",
+ "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, performance issues or a broken installation!",
+ "admin.flags.warn.label": "Do NOT enable these flags unless you know what you're doing!",
+ "admin.general.allowBrowse": "Allow Browsing",
+ "admin.general.allowBrowseHint": "Can users browse using the tree structure of the site to pages they have access to?",
+ "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.defaultBaseURLHint": "The default base URL to use when a site URL is not available. (e.g. https://wiki.example.com)",
+ "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.defaultTocDepth": "Default ToC Depth",
+ "admin.general.defaultTocDepthHint": "The default minimum and maximum header levels to show in the table of contents.",
+ "admin.general.defaults": "Site Defaults",
+ "admin.general.discoverable": "Make Discoverable in the Wiki Directory",
+ "admin.general.discoverableHint": "Add your wiki to the Wiki Directory so that it can be discovered by other users and wikis.",
+ "admin.general.discovery": "Discovery",
+ "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, JPG, WEBP 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.footerExtra": "Additional Footer Text",
+ "admin.general.footerExtraHint": "Optionally add more content to the footer, such as additional copyright terms or mandatory regulatory info.",
+ "admin.general.general": "General",
+ "admin.general.logo": "Logo",
+ "admin.general.logoUpl": "Site Logo",
+ "admin.general.logoUplHint": "Logo image file, in SVG, PNG, JPG, WEBP or GIF format.",
+ "admin.general.logoUploadSuccess": "Site logo uploaded successfully.",
+ "admin.general.pageCasing": "Case Sensitive Paths",
+ "admin.general.pageCasingHint": "Treat paths with different casing as distinct pages.",
+ "admin.general.pageExtensions": "Page Extensions",
+ "admin.general.pageExtensionsHint": "A comma-separated list of URL extensions that will be treated as pages. For example, adding md will treat /foobar.md the same as /foobar.",
+ "admin.general.ratingsOff": "Off",
+ "admin.general.ratingsStars": "Stars",
+ "admin.general.ratingsThumbs": "Thumbs",
+ "admin.general.reasonForChange": "Reason for Change",
+ "admin.general.reasonForChangeHint": "Should users be prompted the reason for changes made to a page?",
+ "admin.general.reasonForChangeOff": "Off",
+ "admin.general.reasonForChangeOptional": "Optional",
+ "admin.general.reasonForChangeRequired": "Required",
+ "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.sitemap": "Allow Sitemap",
+ "admin.general.sitemapHint": "Make a sitemap.xml available to search engines with all pages accessible to guests.",
+ "admin.general.subtitle": "Main settings of your wiki",
+ "admin.general.title": "General",
+ "admin.general.uploadClear": "Clear",
+ "admin.general.uploadConflictBehavior": "Upload Conflict Behavior",
+ "admin.general.uploadConflictBehaviorHint": "How should uploads for a file that already exists be handled?",
+ "admin.general.uploadConflictBehaviorNew": "Append Time to Filename",
+ "admin.general.uploadConflictBehaviorOverwrite": "Overwrite",
+ "admin.general.uploadConflictBehaviorReject": "Reject",
+ "admin.general.uploadLogo": "Upload Logo",
+ "admin.general.uploadNormalizeFilename": "Normalize Filenames",
+ "admin.general.uploadNormalizeFilenameHint": "Automatically transform filenames to a standard URL-friendly format.",
+ "admin.general.uploadSizeHint": "An image of {size} pixels is recommended for best results.",
+ "admin.general.uploadTypesHint": "{typeList} or {lastType} files only",
+ "admin.general.uploads": "Uploads",
+ "admin.general.urlHandling": "URL Handling",
+ "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 | {n} 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.groups.usersNone": "This group doesn't have any user yet.",
+ "admin.icons.mandatory": "Used by the system and cannot be disabled.",
+ "admin.icons.reference": "Reference",
+ "admin.icons.subtitle": "Configure the icon packs available for use",
+ "admin.icons.title": "Icons",
+ "admin.icons.warnHint": "Only activate the icon packs you actually use.",
+ "admin.icons.warnLabel": "Enabling additional icon packs can significantly increase page load times!",
+ "admin.instances.activeConnections": "Active Connections",
+ "admin.instances.activeListeners": "Active Listeners",
+ "admin.instances.firstSeen": "First Seen",
+ "admin.instances.lastSeen": "Last Seen",
+ "admin.instances.subtitle": "View a list of active instances",
+ "admin.instances.title": "Instances",
+ "admin.locale.active": "Active Locales",
+ "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.forcePrefix": "Force Locale Prefix",
+ "admin.locale.forcePrefixHint": "Paths without a locale code will always be redirected to the primary 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.primary": "Primary Locale",
+ "admin.locale.primaryHint": "The locale to use as default / fallback for this site.",
+ "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.bgUploadSuccess": "Login background image uploaded successfully.",
+ "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=1 to the url. This is useful to login as local admin while hiding it from normal users.",
+ "admin.login.saveSuccess": "Login configuration saved successfully.",
+ "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.defaultBaseURL": "Default Base URL",
+ "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.smtpName": "Client Identifying Name",
+ "admin.mail.smtpNameHint": "An optional name to send to the SMTP server to identify your mailer. Leave empty to use server hostname. For Google Workspace customers, this should be your main domain name.",
+ "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.templateEditor": "Mail Template Editor",
+ "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.metrics.auth": "You must provide the {headerName} header with a {tokenType} token. Generate an API key with the {permission} permission and use it as the token.",
+ "admin.metrics.disabled": "Endpoint Disabled",
+ "admin.metrics.enabled": "Endpoint Enabled",
+ "admin.metrics.endpoint": "The metrics endpoint can be scraped at {endpoint}",
+ "admin.metrics.endpointWarning": "Note that this override any page at this path.",
+ "admin.metrics.refreshSuccess": "Metrics endpoint state has been refreshed.",
+ "admin.metrics.subtitle": "Manage the Prometheus metrics endpoint",
+ "admin.metrics.title": "Metrics",
+ "admin.metrics.toggleStateDisabledSuccess": "Metrics endpoint disabled successfully.",
+ "admin.metrics.toggleStateEnabledSuccess": "Metrics endpoint enabled successfully.",
+ "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 content rendering pipeline",
+ "admin.rendering.title": "Rendering",
+ "admin.scheduler.active": "Active",
+ "admin.scheduler.activeNone": "There are no active jobs at the moment.",
+ "admin.scheduler.attempt": "Attempt",
+ "admin.scheduler.cancelJob": "Cancel Job",
+ "admin.scheduler.cancelJobSuccess": "Job cancelled successfully.",
+ "admin.scheduler.completed": "Completed",
+ "admin.scheduler.completedIn": "Completed in {duration}",
+ "admin.scheduler.completedNone": "There are no recently completed job to display.",
+ "admin.scheduler.createdAt": "Created",
+ "admin.scheduler.createdBy": "by instance {instance}",
+ "admin.scheduler.cron": "Cron",
+ "admin.scheduler.error": "Error",
+ "admin.scheduler.failed": "Failed",
+ "admin.scheduler.failedNone": "There are no recently failed job to display.",
+ "admin.scheduler.interrupted": "Interrupted",
+ "admin.scheduler.pending": "Pending",
+ "admin.scheduler.result": "Result",
+ "admin.scheduler.retryJob": "Retry Job",
+ "admin.scheduler.retryJobSuccess": "Job has been rescheduled and will execute shortly.",
+ "admin.scheduler.schedule": "Schedule",
+ "admin.scheduler.scheduled": "Scheduled",
+ "admin.scheduler.scheduledNone": "There are no scheduled jobs at the moment.",
+ "admin.scheduler.startedAt": "Started",
+ "admin.scheduler.state": "State",
+ "admin.scheduler.subtitle": "View scheduled and completed jobs",
+ "admin.scheduler.title": "Scheduler",
+ "admin.scheduler.type": "Type",
+ "admin.scheduler.upcoming": "Upcoming",
+ "admin.scheduler.upcomingNone": "There are no upcoming job for the moment.",
+ "admin.scheduler.updatedAt": "Last Updated",
+ "admin.scheduler.useWorker": "Execution Mode",
+ "admin.scheduler.waitUntil": "Start",
+ "admin.search.configSaveSuccess": "Search engine configuration saved successfully.",
+ "admin.search.dictOverrides": "PostgreSQL Dictionary Mapping Overrides",
+ "admin.search.dictOverridesHint": "JSON object of 2 letters locale codes and their PostgreSQL dictionary association. e.g. {0}",
+ "admin.search.engineConfig": "Engine Configuration",
+ "admin.search.engineNoConfig": "This engine has no configuration options you can modify.",
+ "admin.search.highlighting": "Enable Term Highlighting",
+ "admin.search.highlightingHint": "Whether to show the highlighted terms in search results. There is a slight performance impact when enabled.",
+ "admin.search.indexRebuildSuccess": "Index rebuilt successfully.",
+ "admin.search.listRefreshSuccess": "List of search engines has been refreshed.",
+ "admin.search.rebuildIndex": "Rebuild Index",
+ "admin.search.rebuildInitSuccess": "A search index rebuild has been initiated and will start shortly.",
+ "admin.search.saveSuccess": "Search engine configuration saved successfully",
+ "admin.search.searchEngine": "Search Engine",
+ "admin.search.subtitle": "Configure the search capabilities of your wiki",
+ "admin.search.title": "Search Engine",
+ "admin.searchRebuildIndex": "Rebuild Index",
+ "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 some 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.setupRequired": "Setup required",
+ "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.checkForUpdates": "Check",
+ "admin.system.checkUpdate": "Check / Upgrade",
+ "admin.system.checkingForUpdates": "Checking for Updates...",
+ "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.fetchingLatestVersionInfo": "Fetching latest version info...",
+ "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.newVersionAvailable": "A new version is available.",
+ "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.runningLatestVersion": "You're running the latest version.",
+ "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.upgrade": "Upgrade",
+ "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.terminal.clear": "Clear",
+ "admin.terminal.command": "Command",
+ "admin.terminal.connect": "Connect",
+ "admin.terminal.connectError": "Connection Error:",
+ "admin.terminal.connected": "Connected.",
+ "admin.terminal.connecting": "Connecting to server...",
+ "admin.terminal.disconnect": "Disconnect",
+ "admin.terminal.disconnected": "Disconnected.",
+ "admin.terminal.logs": "Logs",
+ "admin.terminal.subtitle": "View process logs in real-time",
+ "admin.terminal.title": "Terminal",
+ "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.appearance": "Appearance",
+ "admin.theme.baseFont": "Base Font",
+ "admin.theme.baseFontHint": "The font used across the site for the interface.",
+ "admin.theme.bodyHtmlInjection": "Body HTML Injection",
+ "admin.theme.bodyHtmlInjectionHint": "HTML code to be injected just before the closing body tag.",
+ "admin.theme.codeBlocks": "Code Blocks",
+ "admin.theme.codeBlocksAppearance": "Code Blocks Appearance",
+ "admin.theme.codeBlocksAppearanceHint": "The color theme used to display code blocks on pages.",
+ "admin.theme.codeInjection": "Code Injection",
+ "admin.theme.contentFont": "Content Font",
+ "admin.theme.contentFontHint": "The font used specifically for page content.",
+ "admin.theme.contentWidth": "Content Width",
+ "admin.theme.contentWidthHint": "Should the content use all available viewport space or stay centered.",
+ "admin.theme.cssOverride": "CSS Override",
+ "admin.theme.cssOverrideHint": "CSS code to inject after system default CSS. Injecting too much CSS code can 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.fonts": "Fonts",
+ "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. Does not apply to the administration area.",
+ "admin.theme.iconset": "Icon Set",
+ "admin.theme.iconsetHint": "Set of icons to use for the sidebar navigation.",
+ "admin.theme.layout": "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. Does not apply to the administration area.",
+ "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.appearance": "Site Appearance",
+ "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.changePasswordSuccess": "User password was updated successfully.",
+ "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.createSendEmailMissingSiteId": "You must specify the wiki site to reference for the welcome email.",
+ "admin.users.createSuccess": "User created successfully!",
+ "admin.users.createdAt": "Created on {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.defaults": "Manage User Defaults",
+ "admin.users.defaultsSaveSuccess": "User defaults saved successfully.",
+ "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 {date}",
+ "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.pronouns": "Pronouns",
+ "admin.users.pronounsHint": "The pronouns used to address this user.",
+ "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.sendWelcomeEmailFromSiteId": "Site to use for the Welcome Email",
+ "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.disconnectWS": "Disconnect WebSocket Sessions",
+ "admin.utilities.disconnectWSHint": "Force all active websocket connections to be closed.",
+ "admin.utilities.disconnectWSSuccess": "All active websocket connections have been terminated.",
+ "admin.utilities.export": "Export",
+ "admin.utilities.exportHint": "Export content to tarball for backup / migration.",
+ "admin.utilities.flushCache": "Flush Cache",
+ "admin.utilities.flushCacheHint": "Pages and Assets are cached to disk for better performance. You can flush the cache to force all content to be fetched from the DB again.",
+ "admin.utilities.graphEndpointSubtitle": "Change the GraphQL endpoint for Wiki.js",
+ "admin.utilities.graphEndpointTitle": "GraphQL Endpoint",
+ "admin.utilities.import": "Import",
+ "admin.utilities.importHint": "Import content from a tarball backup or a 2.X backup.",
+ "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.scanPageProblems": "Scan for Page Problems",
+ "admin.utilities.scanPageProblemsHint": "Scan all pages for invalid, missing or corrupted data.",
+ "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.currentPassword": "Current Password",
+ "auth.changePwd.instructions": "You must choose a new password:",
+ "auth.changePwd.loading": "Changing password...",
+ "auth.changePwd.newPassword": "New Password",
+ "auth.changePwd.newPasswordVerify": "Verify New Password",
+ "auth.changePwd.proceed": "Change Password",
+ "auth.changePwd.subtitle": "Choose a new password",
+ "auth.changePwd.success": "Password updated successfully.",
+ "auth.enterCredentials": "Enter your credentials",
+ "auth.errors.fields": "One or more fields are invalid.",
+ "auth.errors.forgotPassword": "Missing or invalid email address.",
+ "auth.errors.invalidEmail": "Email is invalid.",
+ "auth.errors.invalidLogin": "Invalid Login",
+ "auth.errors.invalidLoginMsg": "The email or password is invalid.",
+ "auth.errors.invalidName": "Name is invalid.",
+ "auth.errors.invalidUserEmail": "Invalid User Email",
+ "auth.errors.login": "Missing or invalid login fields.",
+ "auth.errors.loginError": "Login error",
+ "auth.errors.missingEmail": "Email is missing.",
+ "auth.errors.missingName": "Name is missing.",
+ "auth.errors.missingPassword": "Password is missing.",
+ "auth.errors.missingUsername": "Username is missing.",
+ "auth.errors.missingVerifyPassword": "Password Verification is missing.",
+ "auth.errors.notYetAuthorized": "You have not been authorized to login to this site yet.",
+ "auth.errors.passwordTooShort": "Password is too short.",
+ "auth.errors.passwordsNotMatch": "Passwords do not match.",
+ "auth.errors.register": "One or more fields are invalid.",
+ "auth.errors.tfaMissing": "Missing or incomplete security code.",
+ "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 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.login.title": "Login",
+ "auth.loginRequired": "Login required",
+ "auth.loginSuccess": "Login Successful! Redirecting...",
+ "auth.loginUsingStrategy": "Login using {strategy}",
+ "auth.logoutSuccess": "You've been logged out successfully.",
+ "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.passkeys.signin": "Log In with a Passkey",
+ "auth.passkeys.signinHint": "Enter your email address to login with a passkey:",
+ "auth.passwordNotMatch": "Both passwords do not match.",
+ "auth.passwordTooShort": "Password is too short.",
+ "auth.pleaseWait": "Please wait",
+ "auth.registerCheckEmail": "Check your emails to activate your account.",
+ "auth.registerSubTitle": "Fill-in the form below to create an account.",
+ "auth.registerSuccess": "Account created successfully!",
+ "auth.registerTitle": "Create an account",
+ "auth.registering": "Creating account...",
+ "auth.selectAuthProvider": "Sign in with",
+ "auth.sendResetPassword": "Reset Password",
+ "auth.signingIn": "Signing In...",
+ "auth.switchToLogin.link": "Back to Login",
+ "auth.switchToRegister.link": "Create an Account",
+ "auth.tfa.subtitle": "Security code required:",
+ "auth.tfa.verifyToken": "Verify",
+ "auth.tfaFormTitle": "Enter the security code generated from your trusted device:",
+ "auth.tfaSetupInstrFirst": "Scan the QR code below from your mobile 2FA application:",
+ "auth.tfaSetupInstrSecond": "Enter the security code generated from your trusted device:",
+ "auth.tfaSetupSuccess": "2FA enabled successfully on your account.",
+ "auth.tfaSetupTitle": "Your administrator has required Two-Factor Authentication (2FA) to be enabled on your account.",
+ "auth.tfaSetupVerifying": "Verifying...",
+ "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.copyURL": "Copy URL",
+ "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.duplicate": "Duplicate",
+ "common.actions.edit": "Edit",
+ "common.actions.exit": "Exit",
+ "common.actions.fetch": "Fetch",
+ "common.actions.filter": "Filter",
+ "common.actions.generate": "Generate",
+ "common.actions.goback": "Go Back",
+ "common.actions.howItWorks": "How it works",
+ "common.actions.insert": "Insert",
+ "common.actions.login": "Login",
+ "common.actions.manage": "Manage",
+ "common.actions.move": "Move",
+ "common.actions.moveTo": "Move To",
+ "common.actions.new": "New",
+ "common.actions.newFolder": "New Folder",
+ "common.actions.newPage": "New Page",
+ "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.rerender": "Rerender",
+ "common.actions.returnToTop": "Return to top",
+ "common.actions.save": "Save",
+ "common.actions.saveAndClose": "Save and Close",
+ "common.actions.saveChanges": "Save Changes",
+ "common.actions.select": "Select",
+ "common.actions.update": "Update",
+ "common.actions.upload": "Upload",
+ "common.actions.view": "View",
+ "common.actions.viewDocs": "View Documentation",
+ "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.datetime": "{date} 'at' {time}",
+ "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.generic.hint": "Oops, something went wrong...",
+ "common.error.generic.title": "Unexpected Error",
+ "common.error.notfound.hint": "That page doesn't exist or is not available.",
+ "common.error.notfound.title": "Not Found",
+ "common.error.title": "Error",
+ "common.error.unauthorized.hint": "You don't have the required permissions to access this page.",
+ "common.error.unauthorized.title": "Unauthorized",
+ "common.error.unexpected": "An unexpected error occurred.",
+ "common.error.unknownsite.hint": "There's no wiki site at this host.",
+ "common.error.unknownsite.title": "Unknown Site",
+ "common.field.createdOn": "Created On",
+ "common.field.id": "ID",
+ "common.field.lastUpdated": "Last Updated",
+ "common.field.name": "Name",
+ "common.field.task": "Task",
+ "common.field.title": "Title",
+ "common.footerCopyright": "© {year} {company}. All rights reserved.",
+ "common.footerGeneric": "Powered by {link}, an open source project.",
+ "common.footerLicense": "Content is available under the {license}, by {company}.",
+ "common.footerPoweredBy": "Powered by {link}",
+ "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.good": "Good",
+ "common.password.poor": "Poor",
+ "common.password.strong": "Strong",
+ "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.createPage": "Create Page",
+ "editor.markup.admonitionDanger": "Danger / Important Admonition",
+ "editor.markup.admonitionInfo": "Info / Note Admonition",
+ "editor.markup.admonitionSuccess": "Tip / Success Admonition",
+ "editor.markup.admonitionWarning": "Warning Admonition",
+ "editor.markup.blockquote": "Blockquote",
+ "editor.markup.blockquoteAdmonitions": "Blockquote / Admonition",
+ "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.header": "Header",
+ "editor.markup.headerLevel": "Header {level}",
+ "editor.markup.heading": "Heading {level}",
+ "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.insertEmoji": "Insert Emoji",
+ "editor.markup.insertFootnote": "Insert Footnote",
+ "editor.markup.insertHorizontalBar": "Insert Horizontal Bar",
+ "editor.markup.insertLink": "Insert Link",
+ "editor.markup.insertMathExpression": "Insert Math Expression",
+ "editor.markup.insertTable": "Insert Table",
+ "editor.markup.insertTabset": "Insert Tabset",
+ "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.taskList": "Task List",
+ "editor.markup.taskListChecked": "Checked List Item",
+ "editor.markup.taskListUnchecked": "Unchecked List Item",
+ "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.pendingAssetsUploading": "Uploading assets...",
+ "editor.props.alias": "Alias",
+ "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.icon": "Icon",
+ "editor.props.info": "Info",
+ "editor.props.isSearchable": "Include in Search Results",
+ "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.reasonForChange.field": "Reason",
+ "editor.reasonForChange.optional": "Enter a short description of the reason for this change. This is optional but recommended.",
+ "editor.reasonForChange.reasonMissing": "A reason is missing.",
+ "editor.reasonForChange.required": "You must provide a reason for this change. Enter a small description of what changed.",
+ "editor.reasonForChange.title": "Reason For Change",
+ "editor.renderPreview": "Render Preview",
+ "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.saveAndCloseTip": "Ctrl / Cmd + Click to save and close",
+ "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.settings": "Editor Settings",
+ "editor.settings.markdown": "Markdown Editor Settings",
+ "editor.settings.markdownFontSize": "Editor Font Size",
+ "editor.settings.markdownFontSizeHint": "The font size to use in the editor.",
+ "editor.settings.markdownPreviewShown": "Display Render Preview",
+ "editor.settings.markdownPreviewShownHint": "Whether to display a preview of the rendered content.",
+ "editor.tableEditor.title": "Table Editor",
+ "editor.togglePreviewPane": "Toggle Preview Pane",
+ "editor.toggleScrollSync": "Toggle Scroll Sync",
+ "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?",
+ "error.ERR_PK_ALREADY_REGISTERED": "It looks like this authenticator is already registered.",
+ "error.ERR_PK_HOSTNAME_MISSING": "Your administrator must set a valid site hostname before passkeys can be used.",
+ "error.ERR_PK_USER_CANCELLED": "Passkey registration aborted. Make sure to remove the key from your device.",
+ "fileman.7zFileType": "7zip Archive",
+ "fileman.aacFileType": "AAC Audio File",
+ "fileman.aiFileType": "Adobe Illustrator Document",
+ "fileman.aifFileType": "AIF Audio File",
+ "fileman.apkFileType": "Android Package",
+ "fileman.assetDelete": "Confirm Delete Asset",
+ "fileman.assetDeleteConfirm": "Are you sure you want to delete {name}?",
+ "fileman.assetDeleteId": "Asset ID {id}",
+ "fileman.assetDeleteSuccess": "Asset deleted successfully.",
+ "fileman.assetFileName": "Asset Name",
+ "fileman.assetFileNameHint": "Filename of the asset, including the file extension.",
+ "fileman.assetRename": "Rename Asset",
+ "fileman.aviFileType": "AVI Video File",
+ "fileman.binFileType": "Binary File",
+ "fileman.bz2FileType": "BZIP2 Archive",
+ "fileman.copyURLSuccess": "URL has been copied to the clipboard.",
+ "fileman.createFolderInvalidData": "One or more fields are invalid.",
+ "fileman.createFolderSuccess": "Folder created successfully.",
+ "fileman.cssFileType": "Cascade Style Sheet",
+ "fileman.csvFileType": "Comma Separated Values Document",
+ "fileman.dataFileType": "Data File",
+ "fileman.detailsAssetSize": "File Size",
+ "fileman.detailsAssetType": "Type",
+ "fileman.detailsPageCreated": "Created",
+ "fileman.detailsPageEditor": "Editor",
+ "fileman.detailsPageType": "Type",
+ "fileman.detailsPageUpdated": "Last Updated",
+ "fileman.detailsTitle": "Title",
+ "fileman.dmgFileType": "Apple Disk Image File",
+ "fileman.docxFileType": "Microsoft Word Document",
+ "fileman.epsFileType": "EPS Image",
+ "fileman.exeFileType": "Windows Executable",
+ "fileman.flacFileType": "FLAC Audio File",
+ "fileman.folderChildrenCount": "Empty folder | 1 child | {count} children",
+ "fileman.folderCreate": "New Folder",
+ "fileman.folderFileName": "Path Name",
+ "fileman.folderFileNameHint": "URL friendly version of the folder name. Must consist of lowercase alphanumerical or hypen characters only.",
+ "fileman.folderFileNameInvalid": "Invalid Characters in Folder Path Name. Lowercase alphanumerical and hyphen characters only.",
+ "fileman.folderFileNameMissing": "Missing Folder Path Name",
+ "fileman.folderRename": "Rename Folder",
+ "fileman.folderTitle": "Title",
+ "fileman.folderTitleInvalidChars": "Invalid Characters in Folder Name",
+ "fileman.folderTitleMissing": "Missing Folder Title",
+ "fileman.gifFileType": "Animated GIF Image",
+ "fileman.gzFileType": "GZipped Archive",
+ "fileman.heicFileType": "HEIC Image",
+ "fileman.icoFileType": "Icon File",
+ "fileman.icsFileType": "ICS Calendar Event",
+ "fileman.isoFileType": "Optical Disk Image File",
+ "fileman.jpegFileType": "JPEG Image",
+ "fileman.jpgFileType": "JPEG Image",
+ "fileman.jsonFileType": "JSON Document",
+ "fileman.m4aFileType": "M4A Audio File",
+ "fileman.markdownPageType": "Markdown Page",
+ "fileman.midFileType": "MIDI Audio File",
+ "fileman.movFileType": "QuickTime Video File",
+ "fileman.mp3FileType": "MP3 Audio File",
+ "fileman.mp4FileType": "MP4 Video File",
+ "fileman.mpegFileType": "MPEG Video File",
+ "fileman.mpgFileType": "MPEG Video File",
+ "fileman.oggFileType": "OGG Audio File",
+ "fileman.otfFileType": "OpenType Font File",
+ "fileman.pdfFileType": "PDF Document",
+ "fileman.pngFileType": "PNG Image",
+ "fileman.pptxFileType": "Microsoft Powerpoint Presentation",
+ "fileman.psdFileType": "Adobe Photoshop Document",
+ "fileman.rarFileType": "RAR Archive",
+ "fileman.renameAssetInvalid": "Asset name is invalid.",
+ "fileman.renameAssetSuccess": "Asset renamed successfully",
+ "fileman.renameFolderInvalidData": "One or more fields are invalid.",
+ "fileman.renameFolderSuccess": "Folder renamed successfully.",
+ "fileman.searchFolder": "Search folder...",
+ "fileman.svgFileType": "Scalable Vector Graphic",
+ "fileman.tarFileType": "TAR Archive",
+ "fileman.tgzFileType": "Gzipped TAR Archive",
+ "fileman.tifFileType": "TIFF Image",
+ "fileman.title": "File Manager",
+ "fileman.ttfFileType": "TrueType Font File",
+ "fileman.txtFileType": "Text Document",
+ "fileman.unknownFileType": "{type} file",
+ "fileman.uploadSuccess": "File(s) uploaded successfully.",
+ "fileman.viewOptions": "View Options",
+ "fileman.wavFileType": "WAV Audio File",
+ "fileman.wmaFileType": "WMA Audio File",
+ "fileman.wmvFileType": "WMV Video File",
+ "fileman.woff2FileType": "Web Open Font File",
+ "fileman.woffFileType": "Web Open Font File",
+ "fileman.xlstFileType": "Microsoft Excel Document",
+ "fileman.xmlFileType": "XML Document",
+ "fileman.xzFileType": "XZ Archive",
+ "fileman.zipFileType": "ZIP Archive",
+ "folderDeleteDialog.confirm": "Are you sure you want to delete folder {name} and all its content?",
+ "folderDeleteDialog.deleteSuccess": "Folder has been deleted successfully.",
+ "folderDeleteDialog.folderId": "Folder ID {id}",
+ "folderDeleteDialog.title": "Confirm Delete Folder",
+ "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!",
+ "navEdit.clearItems": "Clear All Items",
+ "navEdit.editMenuItems": "Edit Menu Items",
+ "navEdit.emptyMenuText": "Click the Add button to add your first menu item.",
+ "navEdit.header": "Header",
+ "navEdit.icon": "Icon",
+ "navEdit.iconHint": "Icon to display to the left of the menu item.",
+ "navEdit.label": "Label",
+ "navEdit.labelHint": "Text to display on the menu item.",
+ "navEdit.link": "Link",
+ "navEdit.nestItem": "Nest Item",
+ "navEdit.nestingWarn": "The previous menu item must be a normal link or another nested link. Invalid nested items will be shown in red.",
+ "navEdit.noSelection": "Select a menu item from the left to start editing.",
+ "navEdit.openInNewWindow": "Open in New Window",
+ "navEdit.openInNewWindowHint": "Whether the link should open in a new window / tab.",
+ "navEdit.saveModeSuccess": "Navigation mode set successfully.",
+ "navEdit.saveSuccess": "Menu items saved successfully.",
+ "navEdit.selectGroups": "Group(s):",
+ "navEdit.separator": "Separator",
+ "navEdit.target": "Target",
+ "navEdit.targetHint": "Target path or external link to point to.",
+ "navEdit.title": "Edit Navigation",
+ "navEdit.unnestItem": "Unnest Item",
+ "navEdit.visibility": "Visibility",
+ "navEdit.visibilityAll": "Everyone",
+ "navEdit.visibilityHint": "Whether to show the menu item to everyone or just selected groups.",
+ "navEdit.visibilityLimited": "Selected Groups",
+ "pageDeleteDialog.confirm": "Are you sure you want to delete the page {name}?",
+ "pageDeleteDialog.deleteSuccess": "Page deleted successfully.",
+ "pageDeleteDialog.pageId": "Page ID {id}",
+ "pageDeleteDialog.title": "Confirm Page Deletion",
+ "pageDuplicateDialog.title": "Duplicate and Save As...",
+ "pageRenameDialog.title": "Rename / Move to...",
+ "pageSaveDialog.displayModePath": "Browse Using Paths",
+ "pageSaveDialog.displayModeTitle": "Browse Using Titles",
+ "pageSaveDialog.title": "Save As...",
+ "profile.accessibility": "Accessibility",
+ "profile.activity": "Activity",
+ "profile.appearance": "Site Appearance",
+ "profile.appearanceDark": "Dark",
+ "profile.appearanceDefault": "Site Default",
+ "profile.appearanceHint": "Use the light or dark theme.",
+ "profile.appearanceLight": "Light",
+ "profile.auth": "Authentication",
+ "profile.authChangePassword": "Change Password",
+ "profile.authDisableTfa": "Turn Off 2FA",
+ "profile.authDisableTfaConfirm": "Are you sure you want to disable Two Factor Authentication?",
+ "profile.authDisableTfaFailed": "Failed to turn off 2FA.",
+ "profile.authDisableTfaSuccess": "2FA turned off successfully.",
+ "profile.authInfo": "Your account is associated with the following authentication methods:",
+ "profile.authLoadingFailed": "Failed to load authentication methods.",
+ "profile.authModifyTfa": "Modify 2FA",
+ "profile.authSetTfa": "Set 2FA",
+ "profile.authSetTfaLoading": "Setting up 2FA... Please wait",
+ "profile.avatar": "Avatar",
+ "profile.avatarClearFailed": "Failed to clear profile picture.",
+ "profile.avatarClearSuccess": "Profile picture cleared successfully.",
+ "profile.avatarUploadDisabled": "Your avatar is set by your organization and cannot be changed.",
+ "profile.avatarUploadFailed": "Failed to upload user profile picture.",
+ "profile.avatarUploadHint": "For best results, use a 180x180 image of type JPG or PNG.",
+ "profile.avatarUploadSuccess": "Profile picture uploaded successfully.",
+ "profile.avatarUploadTitle": "Upload your user profile picture.",
+ "profile.cvd": "Color Vision Deficiency",
+ "profile.cvdDeuteranopia": "Deuteranopia",
+ "profile.cvdHint": "Alter the color scheme of certain UI elements to account for certain color vision dificiencies.",
+ "profile.cvdNone": "None",
+ "profile.cvdProtanopia": "Protanopia",
+ "profile.cvdTritanopia": "Tritanopia",
+ "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.editDisabledDescription": "Your wiki administrator has disabled profile editing.",
+ "profile.editDisabledTitle": "Profile info is managed by your organization.",
+ "profile.email": "Email Address",
+ "profile.emailHint": "The email address used for login.",
+ "profile.groups": "Groups",
+ "profile.groupsInfo": "You're currently part of the following groups:",
+ "profile.groupsLoadingFailed": "Failed to load groups.",
+ "profile.groupsNone": "You're not part of any group.",
+ "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.notifications": "Notifications",
+ "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.passkeys": "Passkeys",
+ "profile.passkeysAdd": "Add Passkey",
+ "profile.passkeysDeactivateConfirm": "Are you sure you want to deactivate this passkey?",
+ "profile.passkeysDeactivateFailed": "Failed to deactivate the passkey.",
+ "profile.passkeysDeactivateSuccess": "Passkey deactivated successfully. You may still need to remove the passkey from your device.",
+ "profile.passkeysIntro": "Passkeys are a replacement for passwords for a faster, easier and more secure login. It relies on your device existing biometrics (phone, computer, security key) to validate your identity.",
+ "profile.passkeysInvalidName": "Passkey name is missing or invalid.",
+ "profile.passkeysName": "Passkey Name",
+ "profile.passkeysNameHint": "Enter a name for your passkey:",
+ "profile.passkeysSetupFailed": "Failed to setup new passkey.",
+ "profile.passkeysSetupSuccess": "Passkey registered successfully.",
+ "profile.passkeysUnsupported": "Passkeys are not supported on your device.",
+ "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.saveFailed": "Failed to save profile changes.",
+ "profile.saveSuccess": "Profile saved successfully.",
+ "profile.saving": "Saving profile...",
+ "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.uploadNewAvatar": "Upload New Image",
+ "profile.viewPublicProfile": "View Public Profile",
+ "renderPageDialog.loading": "Rendering page...",
+ "renderPageDialog.success": "Page rerendered successfully.",
+ "search.editorAny": "Any editor",
+ "search.emptyQuery": "Enter a query in the search field above and press Enter.",
+ "search.filterEditor": "Editor",
+ "search.filterLocale": "Locale(s)",
+ "search.filterLocaleDisplay": "Any locale | {n} locale only | {count} locales selected",
+ "search.filterPath": "Path starting with...",
+ "search.filterPublishState": "Publish State",
+ "search.filterTags": "Tags",
+ "search.filters": "Filters",
+ "search.noResults": "No results found for {0} with the current filters.",
+ "search.publishStateAny": "Any publish state",
+ "search.publishStateDraft": "Draft",
+ "search.publishStatePublished": "Published",
+ "search.publishStateScheduled": "Scheduled",
+ "search.results": "Search Results",
+ "search.sortBy": "Sort By",
+ "search.sortByLastUpdated": "Last Updated",
+ "search.sortByRelevance": "Relevance",
+ "search.sortByTitle": "Title",
+ "search.totalResults": "No result | {0} result | {0} results",
+ "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.",
+ "welcome.admin": "Administration Area",
+ "welcome.createHome": "Create the homepage",
+ "welcome.homeDefault.content": "Write some content here...",
+ "welcome.homeDefault.description": "Welcome to my wiki!",
+ "welcome.homeDefault.title": "Home",
+ "welcome.subtitle": "Let's get started...",
+ "welcome.title": "Welcome to Wiki.js!"
+}
diff --git a/backend/locales/metadata.mjs b/backend/locales/metadata.mjs
new file mode 100644
index 00000000..a3c8cc7c
--- /dev/null
+++ b/backend/locales/metadata.mjs
@@ -0,0 +1,81 @@
+const localazyMetadata = {
+ projectUrl: "https://localazy.com/p/wiki",
+ baseLocale: "en",
+ languages: [
+ {
+ language: "de",
+ region: "",
+ script: "",
+ isRtl: false,
+ name: "German",
+ localizedName: "Deutsch",
+ pluralType: (n) => { return (n===1) ? "one" : "other"; }
+ },
+ {
+ language: "en",
+ region: "",
+ script: "",
+ isRtl: false,
+ name: "English",
+ localizedName: "English",
+ pluralType: (n) => { return (n===1) ? "one" : "other"; }
+ },
+ {
+ language: "fr",
+ region: "",
+ script: "",
+ isRtl: false,
+ name: "French",
+ localizedName: "Français",
+ pluralType: (n) => { return (n===0 || n===1) ? "one" : "other"; }
+ },
+ {
+ language: "pt",
+ region: "BR",
+ script: "",
+ isRtl: false,
+ name: "Brazilian Portuguese",
+ localizedName: "Português (Brasil)",
+ pluralType: (n) => { return (n>=0 && n<=1) ? "one" : "other"; }
+ },
+ {
+ language: "ru",
+ region: "",
+ script: "",
+ isRtl: false,
+ name: "Russian",
+ localizedName: "Русский",
+ pluralType: (n) => { return ((n%10===1) && (n%100!==11)) ? "one" : ((n%10>=2 && n%10<=4) && ((n%100<12 || n%100>14))) ? "few" : "many"; }
+ },
+ {
+ language: "zh",
+ region: "",
+ script: "Hans",
+ isRtl: false,
+ name: "Simplified Chinese",
+ localizedName: "简体中文",
+ pluralType: (n) => { return "other"; }
+ }
+ ],
+ files: [
+ {
+ cdnHash: "54b977214afbffe2ffeb07d0ccb03558e75e4408",
+ file: "file.json",
+ path: "",
+ library: "",
+ module: "",
+ buildType: "",
+ productFlavors: [],
+ cdnFiles: {
+ "de#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/de/file.json",
+ "en#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/en/file.json",
+ "fr#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/fr/file.json",
+ "pt_BR#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/pt-BR/file.json",
+ "ru#": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/ru/file.json",
+ "zh#Hans": "https://delivery.localazy.com/_a7797965569058078203416ae5aa/_e0/54b977214afbffe2ffeb07d0ccb03558e75e4408/zh-Hans/file.json"
+ }
+ }
+ ]
+};
+
+export default localazyMetadata;
diff --git a/backend/models/index.mjs b/backend/models/index.mjs
index c6d79beb..05086957 100644
--- a/backend/models/index.mjs
+++ b/backend/models/index.mjs
@@ -1,5 +1,6 @@
import { authentication } from './authentication.mjs'
import { groups } from './groups.mjs'
+import { locales } from './locales.mjs'
import { settings } from './settings.mjs'
import { sites } from './sites.mjs'
import { users } from './users.mjs'
@@ -7,6 +8,7 @@ import { users } from './users.mjs'
export default {
authentication,
groups,
+ locales,
settings,
sites,
users
diff --git a/backend/models/locales.mjs b/backend/models/locales.mjs
new file mode 100644
index 00000000..07dfcee5
--- /dev/null
+++ b/backend/models/locales.mjs
@@ -0,0 +1,108 @@
+import { find } from 'lodash-es'
+import { stat, readFile } from 'node:fs/promises'
+import path from 'node:path'
+import { DateTime } from 'luxon'
+import { locales as localesTable } from '../db/schema.mjs'
+import { eq, sql } from 'drizzle-orm'
+
+/**
+ * Locales model
+ */
+class Locales {
+ async refreshFromDisk ({ force = false } = {}) {
+ try {
+ const localesMeta = (await import('../locales/metadata.mjs')).default
+ WIKI.logger.info(`Found ${localesMeta.languages.length} locales: [ OK ]`)
+
+ const dbLocales = await WIKI.db.select({
+ code: localesTable.code,
+ updatedAt: localesTable.updatedAt
+ }).from(localesTable).orderBy(localesTable.code)
+
+ let localFilesSkipped = 0
+ for (const lang of localesMeta.languages) {
+ // -> Build filename
+ const langFilenameParts = [lang.language]
+ if (lang.region) {
+ langFilenameParts.push(lang.region)
+ }
+ if (lang.script) {
+ langFilenameParts.push(lang.script)
+ }
+ const langFilename = langFilenameParts.join('-')
+
+ // -> Get DB version
+ const dbLang = find(dbLocales, ['code', langFilename])
+
+ // -> Get File version
+ const flPath = path.join(WIKI.SERVERPATH, `locales/${langFilename}.json`)
+ try {
+ const flStat = await stat(flPath)
+ const flUpdatedAt = DateTime.fromJSDate(flStat.mtime)
+
+ // -> Load strings
+ if (!dbLang || DateTime.fromJSDate(dbLang.updatedAt) < flUpdatedAt || force) {
+ WIKI.logger.info(`Loading locale ${langFilename} into DB...`)
+ const flStrings = JSON.parse(await readFile(flPath, 'utf8'))
+ await WIKI.db.insert(localesTable).values({
+ code: langFilename,
+ name: lang.name,
+ nativeName: lang.localizedName,
+ language: lang.language,
+ region: lang.region,
+ script: lang.script,
+ isRTL: lang.isRtl,
+ strings: flStrings
+ }).onConflictDoUpdate({ target: localesTable.code, set: { strings: flStrings, updatedAt: sql`now()` } })
+ WIKI.logger.info(`Locale ${langFilename} loaded successfully. [ OK ]`)
+ } else {
+ WIKI.logger.info(`Locale ${langFilename} is newer in the DB. Skipping disk version. [ OK ]`)
+ }
+ } catch (err) {
+ localFilesSkipped++
+ WIKI.logger.warn(`Locale ${langFilename} not found on disk. Missing strings file. [ SKIPPED ]`)
+ }
+ }
+ if (localFilesSkipped > 0) {
+ WIKI.logger.warn(`${localFilesSkipped} locales were defined in the metadata file but not found on disk. [ SKIPPED ]`)
+ }
+ } catch (err) {
+ WIKI.logger.warn('Failed to load locales from disk: [ FAILED ]')
+ WIKI.logger.warn(err)
+ return false
+ }
+ }
+
+ async getLocales ({ cache = true } = {}) {
+ if (!WIKI.cache.has('locales') || !cache) {
+ const locales = await WIKI.db.select({
+ code: localesTable.code,
+ isRTL: localesTable.isRTL,
+ language: localesTable.language,
+ name: localesTable.name,
+ nativeName: localesTable.nativeName,
+ createdAt: localesTable.createdAt,
+ updatedAt: localesTable.updatedAt,
+ completeness: localesTable.completeness
+ }).from(localesTable).orderBy(localesTable.code)
+ WIKI.cache.set('locales', locales)
+ for (const locale of locales) {
+ WIKI.cache.set(`locale:${locale.code}`, locale)
+ }
+ }
+ return WIKI.cache.get('locales')
+ }
+
+ async getStrings (locale) {
+ const results = await WIKI.db.select({ strings: localesTable.strings }).from(localesTable).where(eq(localesTable.code, locale)).limit(1)
+ return results.length === 1 ? results[0].strings : []
+ }
+
+ async reloadCache () {
+ WIKI.logger.info('Reloading locales cache...')
+ const locales = await WIKI.models.locales.getLocales({ cache: false })
+ WIKI.logger.info(`Loaded ${locales.length} locales into cache [ OK ]`)
+ }
+}
+
+export const locales = new Locales()
diff --git a/backend/models/sites.mjs b/backend/models/sites.mjs
index 616b757f..a25b6625 100644
--- a/backend/models/sites.mjs
+++ b/backend/models/sites.mjs
@@ -6,6 +6,13 @@ import { eq } from 'drizzle-orm'
* Sites model
*/
class Sites {
+ async getSiteById ({ id, forceReload = false }) {
+ if (forceReload) {
+ await WIKI.models.sites.reloadCache()
+ }
+ return WIKI.sites[id]
+ }
+
async getSiteByHostname ({ hostname, forceReload = false }) {
if (forceReload) {
await WIKI.models.sites.reloadCache()
diff --git a/backend/package-lock.json b/backend/package-lock.json
index 32b6ffc0..61fe23ed 100644
--- a/backend/package-lock.json
+++ b/backend/package-lock.json
@@ -30,6 +30,7 @@
"lodash-es": "4.17.23",
"luxon": "3.7.2",
"nanoid": "5.1.6",
+ "node-cache": "5.1.2",
"pem-jwk": "2.0.0",
"pg": "8.17.2",
"pg-pubsub": "0.8.1",
@@ -56,6 +57,7 @@
"resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz",
"integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@azure/core-auth": "^1.10.0",
@@ -73,6 +75,7 @@
"resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz",
"integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"tslib": "^2.6.2"
},
@@ -85,6 +88,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz",
"integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@azure/core-util": "^1.13.0",
@@ -118,6 +122,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.2.tgz",
"integrity": "sha512-Tf6ltdKzOJEgxZeWLCjMxrxbodB/ZeCbzzA1A2qHbhzAjzjHoBVSUeSl/baT/oHAxhc4qdqVaDKnc2+iE932gw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2"
},
@@ -134,6 +139,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.7.2.tgz",
"integrity": "sha512-0YIpccoX8m/k00O7mDDMdJpbr6mf1yWo2dfmxt5A8XVZVVMz2SSKaEbMCeJRvgQ0IaSlqhjT47p4hVIRRy90xw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-util": "^1.2.0",
@@ -149,6 +155,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz",
"integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"tslib": "^2.6.2"
},
@@ -180,6 +187,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz",
"integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"tslib": "^2.6.2"
},
@@ -192,6 +200,7 @@
"resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz",
"integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.1.2",
"@typespec/ts-http-runtime": "^0.3.0",
@@ -206,6 +215,7 @@
"resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz",
"integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.9.0",
@@ -228,6 +238,7 @@
"resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz",
"integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/abort-controller": "^2.0.0",
"@azure/core-auth": "^1.3.0",
@@ -247,6 +258,7 @@
"resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz",
"integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure-rest/core-client": "^2.3.3",
"@azure/abort-controller": "^2.1.2",
@@ -270,6 +282,7 @@
"resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz",
"integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@typespec/ts-http-runtime": "^0.3.0",
"tslib": "^2.6.2"
@@ -283,6 +296,7 @@
"resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.28.2.tgz",
"integrity": "sha512-6vYUMvs6kJxJgxaCmHn/F8VxjLHNh7i9wzfwPGf8kyBJ8Gg2yvBXx175Uev8LdrD1F5C4o7qHa2CC4IrhGE1XQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/msal-common": "15.14.2"
},
@@ -295,6 +309,7 @@
"resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.14.2.tgz",
"integrity": "sha512-n8RBJEUmd5QotoqbZfd+eGBkzuFI1KX6jw2b3WcpSyGjwmzoeI/Jb99opIBPHpb8y312NB+B6+FGi2ZVSR8yfA==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=0.8.0"
}
@@ -304,6 +319,7 @@
"resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.7.tgz",
"integrity": "sha512-a+Xnrae+uwLnlw68bplS1X4kuJ9F/7K6afuMFyRkNIskhjgDezl5Fhrx+1pmAlDmC0VaaAxjRQMp1OmcqVwkIg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/msal-common": "15.14.2",
"jsonwebtoken": "^9.0.0",
@@ -318,6 +334,7 @@
"resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"license": "MIT",
+ "peer": true,
"bin": {
"uuid": "dist/bin/uuid"
}
@@ -1537,7 +1554,8 @@
"version": "5.7.0",
"resolved": "https://registry.npmjs.org/@js-joda/core/-/core-5.7.0.tgz",
"integrity": "sha512-WBu4ULVVxySLLzK1Ppq+OdfP+adRS4ntmDQT915rzDJ++i95gc2jZkM5B6LWEAwN3lGXpfie3yPABozdD3K3Vg==",
- "license": "BSD-3-Clause"
+ "license": "BSD-3-Clause",
+ "peer": true
},
"node_modules/@js-temporal/polyfill": {
"version": "0.5.1",
@@ -1621,7 +1639,8 @@
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/@tediousjs/connection-string/-/connection-string-0.5.0.tgz",
"integrity": "sha512-7qSgZbincDDDFyRweCIEvZULFAw5iz/DeunhvuxpL31nfntX3P4Yd4HkHBRg9H8CdqY1e5WFN1PZIz/REL9MVQ==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@tybys/wasm-util": {
"version": "0.10.1",
@@ -1672,6 +1691,7 @@
"resolved": "https://registry.npmjs.org/@types/node/-/node-25.2.3.tgz",
"integrity": "sha512-m0jEgYlYz+mDJZ2+F4v8D1AyQb+QzsNqRuI7xg1VQX/KlKS0qT9r1Mo16yo5F/MtifXFgaofIFsdFMox2SxIbQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"undici-types": "~7.16.0"
}
@@ -1681,6 +1701,7 @@
"resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-4.0.23.tgz",
"integrity": "sha512-wwXrtQvbMHxCbBgjHaMGEmImFTQxxpfMOR/ZoQnXxB1woqkUbdLGFDgauo00Py9IudiaqSeiBiulSV9i6XIPig==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/node": "*"
}
@@ -1730,7 +1751,6 @@
"integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/scope-manager": "8.52.0",
"@typescript-eslint/types": "8.52.0",
@@ -1947,6 +1967,7 @@
"resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz",
"integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"http-proxy-agent": "^7.0.0",
"https-proxy-agent": "^7.0.0",
@@ -2249,7 +2270,6 @@
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"acorn": "bin/acorn"
},
@@ -2272,6 +2292,7 @@
"resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz",
"integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">= 14"
}
@@ -2677,6 +2698,7 @@
"resolved": "https://registry.npmjs.org/bl/-/bl-6.1.6.tgz",
"integrity": "sha512-jLsPgN/YSvPUg9UX0Kd73CXpm2Psg9FxMeCSXnk3WBO3CMT10JMwijubhGfHCnFu6TPn1ei3b975dxv7K2pWVg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/readable-stream": "^4.0.0",
"buffer": "^6.0.3",
@@ -2742,7 +2764,8 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz",
"integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==",
- "license": "BSD-3-Clause"
+ "license": "BSD-3-Clause",
+ "peer": true
},
"node_modules/buffer-from": {
"version": "1.1.2",
@@ -2755,6 +2778,7 @@
"resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
"integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"run-applescript": "^7.0.0"
},
@@ -2887,6 +2911,15 @@
"node": ">= 6"
}
},
+ "node_modules/clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -2912,6 +2945,7 @@
"resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz",
"integrity": "sha512-yPVavfyCcRhmorC7rWlkHn15b4wDVgVmBA7kV4QVBsF7kv/9TKJAbAXVTxvTnwP8HHKjRCJDClKbciiYS7p0DQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=16"
}
@@ -3082,6 +3116,7 @@
"resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
"integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"bundle-name": "^4.1.0",
"default-browser-id": "^5.0.0"
@@ -3098,6 +3133,7 @@
"resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
"integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=18"
},
@@ -3128,6 +3164,7 @@
"resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
"integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=12"
},
@@ -3436,6 +3473,7 @@
"resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz",
"integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==",
"license": "Apache-2.0",
+ "peer": true,
"dependencies": {
"safe-buffer": "^5.0.1"
}
@@ -3704,7 +3742,6 @@
"integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@eslint-community/eslint-utils": "^4.8.0",
"@eslint-community/regexpp": "^4.12.1",
@@ -3967,7 +4004,6 @@
"integrity": "sha512-vPZZsiOKaBAIATpFE2uMI4w5IRwdv/FpQ+qZZMR4E+PeOcM4OeoEbqxRMnywdxP19TyB/3h6QBB0EWon7letSQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@typescript-eslint/types": "^8.35.0",
"comment-parser": "^1.4.1",
@@ -4970,6 +5006,7 @@
"resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
"integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"agent-base": "^7.1.0",
"debug": "^4.3.4"
@@ -4983,6 +5020,7 @@
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
"integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"agent-base": "^7.1.2",
"debug": "4"
@@ -4996,6 +5034,7 @@
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
"integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@@ -5263,6 +5302,7 @@
"resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
"integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
"license": "MIT",
+ "peer": true,
"bin": {
"is-docker": "cli.js"
},
@@ -5359,6 +5399,7 @@
"resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
"integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"is-docker": "^3.0.0"
},
@@ -5580,6 +5621,7 @@
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
"integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"is-inside-container": "^1.0.0"
},
@@ -5636,7 +5678,8 @@
"version": "0.3.2",
"resolved": "https://registry.npmjs.org/js-md4/-/js-md4-0.3.2.tgz",
"integrity": "sha512-/GDnfQYsltsjRswQhN9fhv3EMw2sCpUdrdxyWDOUK7eyD++r3gRhzgiQgc/x4MAv2i1iuQ4lxO5mvqM3vj4bwA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/js-stringify": {
"version": "1.0.2",
@@ -5746,6 +5789,7 @@
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz",
"integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"jws": "^4.0.1",
"lodash.includes": "^4.3.0",
@@ -5794,6 +5838,7 @@
"resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz",
"integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"buffer-equal-constant-time": "^1.0.1",
"ecdsa-sig-formatter": "1.0.11",
@@ -5805,6 +5850,7 @@
"resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz",
"integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"jwa": "^2.0.1",
"safe-buffer": "^5.0.1"
@@ -5897,37 +5943,43 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz",
"integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.isboolean": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz",
"integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.isinteger": {
"version": "4.0.4",
"resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz",
"integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.isnumber": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz",
"integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.isplainobject": {
"version": "4.0.6",
"resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
"integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.isstring": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz",
"integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/lodash.merge": {
"version": "4.6.2",
@@ -5940,7 +5992,8 @@
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz",
"integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/loose-envify": {
"version": "1.4.0",
@@ -6077,6 +6130,7 @@
"resolved": "https://registry.npmjs.org/mssql/-/mssql-11.0.1.tgz",
"integrity": "sha512-KlGNsugoT90enKlR8/G36H0kTxPthDhmtNUCwEHvgRza5Cjpjoj+P2X6eMpFUDN7pFrJZsKadL4x990G8RBE1w==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@tediousjs/connection-string": "^0.5.0",
"commander": "^11.0.0",
@@ -6097,6 +6151,7 @@
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"safer-buffer": ">= 2.1.2 < 3.0.0"
},
@@ -6109,6 +6164,7 @@
"resolved": "https://registry.npmjs.org/tedious/-/tedious-18.6.2.tgz",
"integrity": "sha512-g7jC56o3MzLkE3lHkaFe2ZdOVFBahq5bsB60/M4NYUbocw/MCrS89IOEQUFr+ba6pb8ZHczZ/VqCyYeYq0xBAg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/core-auth": "^1.7.2",
"@azure/identity": "^4.2.1",
@@ -6163,7 +6219,8 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/native-duplexpair/-/native-duplexpair-1.0.0.tgz",
"integrity": "sha512-E7QQoM+3jvNtlmyfqRZ0/U75VFgCls+fSkbml2MpgWkWyz3ox8Y58gNhfuziuQYGNNQAbFZJQck55LHCnCK6CA==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/natural-compare": {
"version": "1.4.0",
@@ -6214,6 +6271,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/node-cache": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/node-cache/-/node-cache-5.1.2.tgz",
+ "integrity": "sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg==",
+ "license": "MIT",
+ "dependencies": {
+ "clone": "2.x"
+ },
+ "engines": {
+ "node": ">= 8.0.0"
+ }
+ },
"node_modules/nodemon": {
"version": "3.1.11",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.11.tgz",
@@ -6436,6 +6505,7 @@
"resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
"integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"default-browser": "^5.2.1",
"define-lazy-prop": "^3.0.0",
@@ -6619,7 +6689,6 @@
"resolved": "https://registry.npmjs.org/pg/-/pg-8.17.2.tgz",
"integrity": "sha512-vjbKdiBJRqzcYw1fNU5KuHyYvdJ1qpcQg1CeBrHFqV1pWgHeVR6j/+kX0E1AAXfyuLUGY1ICrN2ELKA/z2HWzw==",
"license": "MIT",
- "peer": true,
"dependencies": {
"pg-connection-string": "^2.10.1",
"pg-pool": "^3.11.0",
@@ -7307,6 +7376,7 @@
"resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
"integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=18"
},
@@ -7646,7 +7716,8 @@
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
"integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
- "license": "BSD-3-Clause"
+ "license": "BSD-3-Clause",
+ "peer": true
},
"node_modules/stable-hash": {
"version": "0.0.5",
@@ -7868,6 +7939,7 @@
"resolved": "https://registry.npmjs.org/tarn/-/tarn-3.0.2.tgz",
"integrity": "sha512-51LAVKUSZSVfI05vjPESNc5vwqqZpbXCsU+/+wxlOrUjk2SnFTt97v9ZgQrD4YmxYW1Px6w2KjaDitCfkvgxMQ==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=8.0.0"
}
@@ -7877,6 +7949,7 @@
"resolved": "https://registry.npmjs.org/tedious/-/tedious-19.2.1.tgz",
"integrity": "sha512-pk1Q16Yl62iocuQB+RWbg6rFUFkIyzqOFQ6NfysCltRvQqKwfurgj8v/f2X+CKvDhSL4IJ0cCOfCHDg9PWEEYA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@azure/core-auth": "^1.7.2",
"@azure/identity": "^4.2.1",
@@ -8181,6 +8254,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
+ "peer": true,
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
@@ -8243,7 +8317,8 @@
"version": "7.16.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
"integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/unrs-resolver": {
"version": "1.11.1",
@@ -8468,6 +8543,7 @@
"resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
"integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"is-wsl": "^3.1.0"
},
diff --git a/backend/package.json b/backend/package.json
index ede24554..61442514 100644
--- a/backend/package.json
+++ b/backend/package.json
@@ -59,6 +59,7 @@
"lodash-es": "4.17.23",
"luxon": "3.7.2",
"nanoid": "5.1.6",
+ "node-cache": "5.1.2",
"pem-jwk": "2.0.0",
"pg": "8.17.2",
"pg-pubsub": "0.8.1",
diff --git a/frontend/.editorconfig b/frontend/.editorconfig
new file mode 100644
index 00000000..9d08a1a8
--- /dev/null
+++ b/frontend/.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/frontend/.gitignore b/frontend/.gitignore
new file mode 100644
index 00000000..a37d7e07
--- /dev/null
+++ b/frontend/.gitignore
@@ -0,0 +1,42 @@
+.DS_Store
+.thumbs.db
+node_modules
+
+# Quasar core related directories
+.quasar
+/dist
+/quasar.config.*.temporary.compiled*
+
+# local .env files
+.env.local*
+
+# Cordova related directories and files
+/src-cordova/node_modules
+/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
+
+# Yarn
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/sdks
+!.yarn/versions
diff --git a/frontend/.npmrc b/frontend/.npmrc
new file mode 100644
index 00000000..870e47c7
--- /dev/null
+++ b/frontend/.npmrc
@@ -0,0 +1,4 @@
+audit = false
+fund = false
+save-exact = true
+save-prefix = ""
diff --git a/frontend/.vscode/extensions.json b/frontend/.vscode/extensions.json
new file mode 100644
index 00000000..df0976dc
--- /dev/null
+++ b/frontend/.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/frontend/.vscode/settings.json b/frontend/.vscode/settings.json
new file mode 100644
index 00000000..f39da49b
--- /dev/null
+++ b/frontend/.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/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
new file mode 100644
index 00000000..196e2d69
--- /dev/null
+++ b/frontend/eslint.config.mjs
@@ -0,0 +1,73 @@
+import js from '@eslint/js'
+import neostandard from 'neostandard'
+import pluginVue from 'eslint-plugin-vue'
+import pluginVuePug from 'eslint-plugin-vue-pug'
+import vueParser from 'vue-eslint-parser'
+import { FlatCompat } from '@eslint/eslintrc'
+
+const compat = new FlatCompat()
+
+export default [
+ js.configs.recommended,
+ ...pluginVue.configs['flat/essential'],
+ ...pluginVuePug.configs['flat/recommended'],
+ ...neostandard(),
+ {
+ ignores: [
+ '/dist',
+ '/.quasar',
+ '/node_modules'
+ ],
+ languageOptions: {
+ ecmaVersion: 'latest',
+ sourceType: 'module',
+ parser: vueParser,
+ globals: {
+ __statics: 'readonly',
+ __QUASAR_SSR__: 'readonly',
+ __QUASAR_SSR_SERVER__: 'readonly',
+ __QUASAR_SSR_CLIENT__: 'readonly',
+ __QUASAR_SSR_PWA__: 'readonly',
+ process: 'readonly',
+ Capacitor: 'readonly',
+ chrome: 'readonly',
+ APOLLO_CLIENT: 'readonly',
+ EVENT_BUS: 'readonly'
+ }
+ },
+ 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',
+ 'vue/multi-word-component-names': 'off',
+
+ // allow debugger during development only
+ 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
+
+ // disable bogus rules
+ 'vue/valid-template-root': 'off',
+ 'vue/no-parsing-error': 'off',
+ 'vue-pug/no-parsing-error': 'off',
+ 'vue/valid-v-for': 'off',
+ 'vue/html-quotes': ['warn', 'single'],
+ 'vue/max-attributes-per-line': 'off'
+ }
+ }
+]
diff --git a/frontend/index.html b/frontend/index.html
new file mode 100644
index 00000000..dcba3bf0
--- /dev/null
+++ b/frontend/index.html
@@ -0,0 +1,75 @@
+
+
+
+
+
+
+
+
+ Wiki.js
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/jsconfig.json b/frontend/jsconfig.json
new file mode 100644
index 00000000..940c1973
--- /dev/null
+++ b/frontend/jsconfig.json
@@ -0,0 +1,46 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "jsx": "preserve",
+ "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"
+ ],
+ "vueCompilerOptions": {
+ "target": 3,
+ "plugins": [
+ "@vue/language-plugin-pug"
+ ]
+ }
+}
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
new file mode 100644
index 00000000..63358c4c
--- /dev/null
+++ b/frontend/package-lock.json
@@ -0,0 +1,11014 @@
+{
+ "name": "wiki-ux",
+ "version": "3.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "wiki-ux",
+ "version": "3.0.0",
+ "dependencies": {
+ "@lezer/common": "1.5.1",
+ "@mdi/font": "7.4.47",
+ "@quasar/extras": "1.17.0",
+ "@simplewebauthn/browser": "13.2.2",
+ "@tanstack/vue-query": "5.92.9",
+ "@xterm/xterm": "6.0.0",
+ "browser-fs-access": "0.38.0",
+ "clipboard": "2.0.11",
+ "codemirror": "5.65.11",
+ "codemirror-asciidoc": "1.0.4",
+ "dependency-graph": "1.0.0",
+ "filesize": "11.0.13",
+ "filesize-parser": "1.5.1",
+ "fuse.js": "7.1.0",
+ "highlight.js": "11.11.1",
+ "js-cookie": "3.0.5",
+ "jwt-decode": "4.0.0",
+ "katex": "0.16.33",
+ "ky": "1.14.3",
+ "lodash-es": "4.17.23",
+ "lowlight": "3.3.0",
+ "luxon": "3.7.2",
+ "markdown-it": "14.1.1",
+ "markdown-it-abbr": "2.0.0",
+ "markdown-it-attrs": "4.3.1",
+ "markdown-it-decorate": "1.2.2",
+ "markdown-it-emoji": "3.0.0",
+ "markdown-it-expand-tabs": "1.0.13",
+ "markdown-it-footnote": "4.0.0",
+ "markdown-it-imsize": "2.0.1",
+ "markdown-it-mark": "4.0.0",
+ "markdown-it-mdc": "0.2.12",
+ "markdown-it-multimd-table": "4.2.3",
+ "markdown-it-sub": "2.0.0",
+ "markdown-it-sup": "2.0.0",
+ "markdown-it-task-lists": "2.1.1",
+ "mitt": "3.0.1",
+ "monaco-editor": "0.55.1",
+ "pako": "2.1.0",
+ "pinia": "3.0.4",
+ "prosemirror-commands": "1.7.1",
+ "prosemirror-history": "1.5.0",
+ "prosemirror-keymap": "1.2.3",
+ "prosemirror-model": "1.25.4",
+ "prosemirror-schema-list": "1.5.1",
+ "prosemirror-state": "1.4.4",
+ "prosemirror-transform": "1.11.0",
+ "prosemirror-view": "1.41.6",
+ "pug": "3.0.3",
+ "quasar": "2.18.6",
+ "slugify": "1.6.6",
+ "socket.io-client": "4.8.3",
+ "sortablejs": "1.15.7",
+ "sortablejs-vue3": "1.3.0",
+ "tabulator-tables": "6.3.1",
+ "tippy.js": "6.3.7",
+ "twemoji": "14.0.2",
+ "typescript": "5.9.3",
+ "uuid": "13.0.0",
+ "v-network-graph": "0.9.22",
+ "vue": "3.5.29",
+ "vue-i18n": "11.2.8",
+ "vue-router": "5.0.3",
+ "vue3-otp-input": "0.5.40",
+ "vuedraggable": "4.1.0",
+ "zxcvbn": "4.4.2"
+ },
+ "devDependencies": {
+ "@intlify/unplugin-vue-i18n": "11.0.7",
+ "@quasar/app-vite": "2.4.1",
+ "@quasar/vite-plugin": "1.10.0",
+ "@types/lodash": "4.17.24",
+ "@vue/language-plugin-pug": "3.2.5",
+ "autoprefixer": "10.4.27",
+ "browserlist": "latest",
+ "npm-check-updates": "19.6.3",
+ "sass": "1.97.3",
+ "vite-plugin-vue-devtools": "8.0.6"
+ },
+ "engines": {
+ "node": ">= 18.0",
+ "npm": ">= 6.13.4"
+ }
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
+ "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz",
+ "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz",
+ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-compilation-targets": "^7.28.6",
+ "@babel/helper-module-transforms": "^7.28.6",
+ "@babel/helpers": "^7.28.6",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/traverse": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/remapping": "^2.3.5",
+ "convert-source-map": "^2.0.0",
+ "debug": "^4.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/core/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.1",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz",
+ "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@babel/types": "^7.29.0",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-annotate-as-pure": {
+ "version": "7.27.3",
+ "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.3.tgz",
+ "integrity": "sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.3"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz",
+ "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.28.6",
+ "@babel/helper-validator-option": "^7.27.1",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.28.6.tgz",
+ "integrity": "sha512-dTOdvsjnG3xNT9Y0AUg1wAl38y+4Rl4sf9caSQZOXdNqVn+H+HbbJ4IyyHaIqNR6SW9oJpA/RuRjsjCw2IdIow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/helper-replace-supers": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/traverse": "^7.28.6",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz",
+ "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-member-expression-to-functions": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.28.5.tgz",
+ "integrity": "sha512-cwM7SBRZcPCLgl8a7cY0soT1SptSzAlMH39vwiRpOQkJlh53r5hdHwLSCZpQdVLT39sZt+CRpNwYG4Y2v77atg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.5",
+ "@babel/types": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz",
+ "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz",
+ "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.28.6",
+ "@babel/helper-validator-identifier": "^7.28.5",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-optimise-call-expression": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz",
+ "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz",
+ "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-replace-supers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.28.6.tgz",
+ "integrity": "sha512-mq8e+laIk94/yFec3DxSjCRD2Z0TAjhVbEJY3UQrlwVo15Lmt7C2wAUbK4bjnTs4APkwsYLTahXRraQXhb1WCg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-member-expression-to-functions": "^7.28.5",
+ "@babel/helper-optimise-call-expression": "^7.27.1",
+ "@babel/traverse": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-skip-transparent-expression-wrappers": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz",
+ "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.27.1",
+ "@babel/types": "^7.27.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz",
+ "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.28.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz",
+ "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.27.1",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz",
+ "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz",
+ "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
+ "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.0"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-proposal-decorators": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.29.0.tgz",
+ "integrity": "sha512-CVBVv3VY/XRMxRYq5dwr2DS7/MvqPm23cOCjbwNnVrfOqcWlnefua1uUs0sjdKOGjvPUG633o07uWzJq4oI6dA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/plugin-syntax-decorators": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-decorators": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.28.6.tgz",
+ "integrity": "sha512-71EYI0ONURHJBL4rSFXnITXqXrrY8q4P0q006DPfN+Rk+ASM+++IBXem/ruokgBZR8YNEWZ8R6B+rCb8VcUTqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-attributes": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz",
+ "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-import-meta": {
+ "version": "7.10.4",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+ "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.10.4"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-jsx": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz",
+ "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-syntax-typescript": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz",
+ "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-typescript": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.28.6.tgz",
+ "integrity": "sha512-0YWL2RFxOqEm9Efk5PvreamxPME8OyY0wM5wh5lHjF+VtVhdneCWGzZeSqzOfiobVqQaNCd2z0tQvnI9DaPWPw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-annotate-as-pure": "^7.27.3",
+ "@babel/helper-create-class-features-plugin": "^7.28.6",
+ "@babel/helper-plugin-utils": "^7.28.6",
+ "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1",
+ "@babel/plugin-syntax-typescript": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.28.6",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
+ "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.28.6",
+ "@babel/parser": "^7.28.6",
+ "@babel/types": "^7.28.6"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz",
+ "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.0",
+ "@babel/generator": "^7.29.0",
+ "@babel/helper-globals": "^7.28.0",
+ "@babel/parser": "^7.29.0",
+ "@babel/template": "^7.28.6",
+ "@babel/types": "^7.29.0",
+ "debug": "^4.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.0",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
+ "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.27.1",
+ "@babel/helper-validator-identifier": "^7.28.5"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@bufbuild/protobuf": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.11.0.tgz",
+ "integrity": "sha512-sBXGT13cpmPR5BMgHE6UEEfEaShh5Ror6rfN3yEK5si7QVrtZg8LEPQb0VVhiLRUslD2yLnXtnRzG035J/mZXQ==",
+ "dev": true,
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
+ "node_modules/@dash14/svg-pan-zoom": {
+ "version": "3.6.9",
+ "resolved": "https://registry.npmjs.org/@dash14/svg-pan-zoom/-/svg-pan-zoom-3.6.9.tgz",
+ "integrity": "sha512-6u+KTQec+9+3bRdk2mReix8AGsp2mB40cw0iYfQQzo22QBkeCNpXl2amnfwQzK7xB9oH/62Wvf2z7l6l2w+csA==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ }
+ },
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ }
+ },
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz",
+ "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.2"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.1.tgz",
+ "integrity": "sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/core": {
+ "version": "0.15.2",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz",
+ "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.4.tgz",
+ "integrity": "sha512-4h4MVF8pmBsncB60r0wSJiIeUKTSD4m7FmTFThG8RHlsg9ajqckLm9OraguFGZE4vVdpiI1Q4+hFnisopmG6gQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.1",
+ "minimatch": "^3.1.3",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@eslint/js": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz",
+ "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ }
+ },
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz",
+ "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.15.2",
+ "levn": "^0.4.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ }
+ },
+ "node_modules/@humanfs/core": {
+ "version": "0.19.1",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
+ "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanfs/node": {
+ "version": "0.16.7",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz",
+ "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.1",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
+ "engines": {
+ "node": ">=18.18.0"
+ }
+ },
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=12.22"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.18"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
+ }
+ },
+ "node_modules/@inquirer/external-editor": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@inquirer/external-editor/-/external-editor-1.0.3.tgz",
+ "integrity": "sha512-RWbSrDiYmO4LbejWY7ttpxczuwQyZLBUyygsA9Nsv95hpzUWwnNTVQmAq3xuh7vNwCp07UTmE5i11XAEExx4RA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chardet": "^2.1.1",
+ "iconv-lite": "^0.7.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@inquirer/external-editor/node_modules/iconv-lite": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz",
+ "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/@inquirer/figures": {
+ "version": "1.0.15",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-1.0.15.tgz",
+ "integrity": "sha512-t2IEY+unGHOzAaVM5Xx6DEWKeXlDDcNPeDyUpsRc6CUhBfU3VQOEl+Vssh7VNp1dR8MdUJBWhuObjXCsVpjN5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@intlify/bundle-utils": {
+ "version": "11.0.7",
+ "resolved": "https://registry.npmjs.org/@intlify/bundle-utils/-/bundle-utils-11.0.7.tgz",
+ "integrity": "sha512-fEO3CJGPymxieGh8BHox7d6stgajDQae7wgpH6YYw7WX+cdW6jTTXyljZqz7OV3JcwlS9M9UHSoO+YwiO56IhA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/message-compiler": "^11.1.12",
+ "@intlify/shared": "^11.1.12",
+ "acorn": "^8.8.2",
+ "esbuild": "^0.25.4",
+ "escodegen": "^2.1.0",
+ "estree-walker": "^2.0.2",
+ "jsonc-eslint-parser": "^2.3.0",
+ "source-map-js": "^1.2.1",
+ "yaml-eslint-parser": "^1.2.2"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependenciesMeta": {
+ "petite-vue-i18n": {
+ "optional": true
+ },
+ "vue-i18n": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@intlify/core-base": {
+ "version": "11.2.8",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.2.8.tgz",
+ "integrity": "sha512-nBq6Y1tVkjIUsLsdOjDSJj4AsjvD0UG3zsg9Fyc+OivwlA/oMHSKooUy9tpKj0HqZ+NWFifweHavdljlBLTwdA==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/message-compiler": "11.2.8",
+ "@intlify/shared": "11.2.8"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/message-compiler": {
+ "version": "11.2.8",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.2.8.tgz",
+ "integrity": "sha512-A5n33doOjmHsBtCN421386cG1tWp5rpOjOYPNsnpjIJbQ4POF0QY2ezhZR9kr0boKwaHjbOifvyQvHj2UTrDFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/shared": "11.2.8",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/shared": {
+ "version": "11.2.8",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.2.8.tgz",
+ "integrity": "sha512-l6e4NZyUgv8VyXXH4DbuucFOBmxLF56C/mqh2tvApbzl2Hrhi1aTDcuv5TKdxzfHYmpO3UB0Cz04fgDT9vszfw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/unplugin-vue-i18n": {
+ "version": "11.0.7",
+ "resolved": "https://registry.npmjs.org/@intlify/unplugin-vue-i18n/-/unplugin-vue-i18n-11.0.7.tgz",
+ "integrity": "sha512-wswKprS1D8VfnxxVhKxug5wa3MbDSOcCoXOBjnzhMK+6NfP6h6UI8pFqSBIvcW8nPDuzweTc0Sk3PeBCcubfoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.4.0",
+ "@intlify/bundle-utils": "11.0.7",
+ "@intlify/shared": "^11.1.12",
+ "@intlify/vue-i18n-extensions": "^8.0.0",
+ "@rollup/pluginutils": "^5.1.0",
+ "@typescript-eslint/scope-manager": "^8.13.0",
+ "@typescript-eslint/typescript-estree": "^8.13.0",
+ "debug": "^4.3.3",
+ "fast-glob": "^3.2.12",
+ "pathe": "^2.0.3",
+ "picocolors": "^1.0.0",
+ "unplugin": "^2.3.4",
+ "vue": "^3.5.21"
+ },
+ "engines": {
+ "node": ">= 20"
+ },
+ "peerDependencies": {
+ "petite-vue-i18n": "*",
+ "vue": "^3.2.25",
+ "vue-i18n": "*"
+ },
+ "peerDependenciesMeta": {
+ "petite-vue-i18n": {
+ "optional": true
+ },
+ "vue-i18n": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@intlify/vue-i18n-extensions": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/@intlify/vue-i18n-extensions/-/vue-i18n-extensions-8.0.0.tgz",
+ "integrity": "sha512-w0+70CvTmuqbskWfzeYhn0IXxllr6mU+IeM2MU0M+j9OW64jkrvqY+pYFWrUnIIC9bEdij3NICruicwd5EgUuQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.24.6",
+ "@intlify/shared": "^10.0.0",
+ "@vue/compiler-dom": "^3.2.45",
+ "vue-i18n": "^10.0.0"
+ },
+ "engines": {
+ "node": ">= 18"
+ },
+ "peerDependencies": {
+ "@intlify/shared": "^9.0.0 || ^10.0.0 || ^11.0.0",
+ "@vue/compiler-dom": "^3.0.0",
+ "vue": "^3.0.0",
+ "vue-i18n": "^9.0.0 || ^10.0.0 || ^11.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@intlify/shared": {
+ "optional": true
+ },
+ "@vue/compiler-dom": {
+ "optional": true
+ },
+ "vue": {
+ "optional": true
+ },
+ "vue-i18n": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/core-base": {
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.8.tgz",
+ "integrity": "sha512-FoHslNWSoHjdUBLy35bpm9PV/0LVI/DSv9L6Km6J2ad8r/mm0VaGg06C40FqlE8u2ADcGUM60lyoU7Myo4WNZQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/message-compiler": "10.0.8",
+ "@intlify/shared": "10.0.8"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/message-compiler": {
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.8.tgz",
+ "integrity": "sha512-DV+sYXIkHVd5yVb2mL7br/NEUwzUoLBsMkV3H0InefWgmYa34NLZUvMCGi5oWX+Hqr2Y2qUxnVrnOWF4aBlgWg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/shared": "10.0.8",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/vue-i18n-extensions/node_modules/@intlify/shared": {
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.8.tgz",
+ "integrity": "sha512-BcmHpb5bQyeVNrptC3UhzpBZB/YHHDoEREOUERrmF2BRxsyOEuRrq+Z96C/D4+2KJb8kuHiouzAei7BXlG0YYw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/@intlify/vue-i18n-extensions/node_modules/vue-i18n": {
+ "version": "10.0.8",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.8.tgz",
+ "integrity": "sha512-mIjy4utxMz9lMMo6G9vYePv7gUFt4ztOMhY9/4czDJxZ26xPeJ49MAGa9wBAE3XuXbYCrtVPmPxNjej7JJJkZQ==",
+ "deprecated": "v9 and v10 no longer supported. please migrate to v11. about maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/core-base": "10.0.8",
+ "@intlify/shared": "10.0.8",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/source-map": {
+ "version": "0.3.11",
+ "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz",
+ "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.25"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@lezer/common": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@lezer/common/-/common-1.5.1.tgz",
+ "integrity": "sha512-6YRVG9vBkaY7p1IVxL4s44n5nUnaNnGM2/AckNgYOnxTG2kWh1vR8BMxPseWPjRNpb5VtXnMpeYAEAADoRV1Iw==",
+ "license": "MIT"
+ },
+ "node_modules/@mdi/font": {
+ "version": "7.4.47",
+ "resolved": "https://registry.npmjs.org/@mdi/font/-/font-7.4.47.tgz",
+ "integrity": "sha512-43MtGpd585SNzHZPcYowu/84Vz2a2g31TvPMTm9uTiCSWzaheQySUcSyUH/46fPnuPQWof2yd0pGBtzee/IQWw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@parcel/watcher": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz",
+ "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "detect-libc": "^2.0.3",
+ "is-glob": "^4.0.3",
+ "node-addon-api": "^7.0.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher-android-arm64": "2.5.6",
+ "@parcel/watcher-darwin-arm64": "2.5.6",
+ "@parcel/watcher-darwin-x64": "2.5.6",
+ "@parcel/watcher-freebsd-x64": "2.5.6",
+ "@parcel/watcher-linux-arm-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm-musl": "2.5.6",
+ "@parcel/watcher-linux-arm64-glibc": "2.5.6",
+ "@parcel/watcher-linux-arm64-musl": "2.5.6",
+ "@parcel/watcher-linux-x64-glibc": "2.5.6",
+ "@parcel/watcher-linux-x64-musl": "2.5.6",
+ "@parcel/watcher-win32-arm64": "2.5.6",
+ "@parcel/watcher-win32-ia32": "2.5.6",
+ "@parcel/watcher-win32-x64": "2.5.6"
+ }
+ },
+ "node_modules/@parcel/watcher-android-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz",
+ "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz",
+ "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-darwin-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz",
+ "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-freebsd-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz",
+ "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz",
+ "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz",
+ "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz",
+ "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-arm64-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz",
+ "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-glibc": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz",
+ "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-linux-x64-musl": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz",
+ "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-arm64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz",
+ "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-ia32": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz",
+ "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@parcel/watcher-win32-x64": {
+ "version": "2.5.6",
+ "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz",
+ "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@polka/url": {
+ "version": "1.0.0-next.29",
+ "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz",
+ "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@popperjs/core": {
+ "version": "2.11.8",
+ "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
+ "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/popperjs"
+ }
+ },
+ "node_modules/@quasar/app-vite": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/@quasar/app-vite/-/app-vite-2.4.1.tgz",
+ "integrity": "sha512-JLRXHKjZCZM9qQlxyjtinusVh+UMBQqM5TEc+Zo3i5y+dBffZiyUcdYN2DCmL0U/eoVulc2n5ve7qR0JwaQu6g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@quasar/render-ssr-error": "^1.0.3",
+ "@quasar/ssl-certificate": "^1.0.0",
+ "@quasar/vite-plugin": "^1.10.0",
+ "@types/chrome": "^0.0.262",
+ "@types/compression": "^1.7.5",
+ "@types/cordova": "^11.0.3",
+ "@types/express": "^4.17.21",
+ "@vitejs/plugin-vue": "^6.0.1",
+ "archiver": "^7.0.1",
+ "chokidar": "^3.6.0",
+ "ci-info": "^4.0.0",
+ "compression": "^1.7.5",
+ "confbox": "^0.1.8",
+ "cross-spawn": "^7.0.6",
+ "dot-prop": "9.0.0",
+ "dotenv": "^16.4.5",
+ "dotenv-expand": "^11.0.6",
+ "elementtree": "0.1.7",
+ "esbuild": "^0.25.0",
+ "express": "^4.21.2",
+ "fs-extra": "^11.2.0",
+ "html-minifier-terser": "^7.2.0",
+ "inquirer": "^9.3.7",
+ "isbinaryfile": "^5.0.4",
+ "kolorist": "^1.8.0",
+ "lodash": "^4.17.21",
+ "minimist": "^1.2.8",
+ "mlly": "^1.7.4",
+ "open": "^10.1.0",
+ "rollup-plugin-visualizer": "^5.13.1",
+ "sass-embedded": "^1.93.2",
+ "semver": "^7.6.3",
+ "serialize-javascript": "^6.0.2",
+ "tinyglobby": "^0.2.10",
+ "ts-essentials": "^9.4.2",
+ "vite": "^7.1.6",
+ "webpack-merge": "^6.0.1"
+ },
+ "bin": {
+ "quasar": "bin/quasar.js"
+ },
+ "engines": {
+ "node": "^30 || ^28 || ^26 || ^24 || ^22 || ^20",
+ "npm": ">= 6.14.12",
+ "yarn": ">= 1.17.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ },
+ "peerDependencies": {
+ "@electron/packager": ">= 18",
+ "electron-builder": ">= 22",
+ "pinia": "^2.0.0 || ^3.0.0",
+ "quasar": "^2.16.0",
+ "typescript": ">= 5.4",
+ "vue": "^3.2.29",
+ "vue-router": "^4.0.12 || ^5.0.0",
+ "workbox-build": ">= 6"
+ },
+ "peerDependenciesMeta": {
+ "@electron/packager": {
+ "optional": true
+ },
+ "electron-builder": {
+ "optional": true
+ },
+ "eslint": {
+ "optional": true
+ },
+ "pinia": {
+ "optional": true
+ },
+ "typescript": {
+ "optional": true
+ },
+ "workbox-build": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@quasar/extras": {
+ "version": "1.17.0",
+ "resolved": "https://registry.npmjs.org/@quasar/extras/-/extras-1.17.0.tgz",
+ "integrity": "sha512-KqAHdSJfIDauiR1nJ8rqHWT0diqD0QradZKoVIZJAilHAvgwyPIY7MbyR2z4RIMkUIMUSqBZcbshMpEw+9A30w==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ }
+ },
+ "node_modules/@quasar/render-ssr-error": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@quasar/render-ssr-error/-/render-ssr-error-1.0.3.tgz",
+ "integrity": "sha512-A8RF99q6/sOSe1Ighnh5syEIbliD3qUYEJd2HyfFyBPSMF+WYGXon5dmzg4nUoK662NgOggInevkDyBDJcZugg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "stack-trace": "^1.0.0-pre2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ }
+ },
+ "node_modules/@quasar/ssl-certificate": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@quasar/ssl-certificate/-/ssl-certificate-1.0.0.tgz",
+ "integrity": "sha512-RhZF7rO76T7Ywer1/5lCe7xl3CIiXxSAH1xgwOj0wcHTityDxJqIN/5YIj6BxMvlFw8XkJDoB1udEQafoVFA4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fs-extra": "^11.1.1",
+ "selfsigned": "^2.1.1"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ }
+ },
+ "node_modules/@quasar/vite-plugin": {
+ "version": "1.10.0",
+ "resolved": "https://registry.npmjs.org/@quasar/vite-plugin/-/vite-plugin-1.10.0.tgz",
+ "integrity": "sha512-4PJoTclz4ZjAfyqe0+hlkKcFJt0e2NX3Ac3hy8ILqUPdtZ24nCo5/xEHvTxZGBQMKRPwwePbO8CVs4n9EKJEug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ },
+ "peerDependencies": {
+ "@vitejs/plugin-vue": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
+ "quasar": "^2.16.0",
+ "vite": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-rc.2",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.2.tgz",
+ "integrity": "sha512-izyXV/v+cHiRfozX62W9htOAvwMo4/bXKDrQ+vom1L1qRuexPock/7VZDAhnpHCLNejd3NJ6hiab+tO0D44Rgw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@rollup/pluginutils": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.3.0.tgz",
+ "integrity": "sha512-5EdhGZtnu3V88ces7s53hhfK5KSASnJZv8Lulpc04cWO3REESroJXg73DFsOmgbU2BhwV0E20bu2IDZb3VKW4Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "estree-walker": "^2.0.2",
+ "picomatch": "^4.0.2"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "rollup": "^1.20.0||^2.0.0||^3.0.0||^4.0.0"
+ },
+ "peerDependenciesMeta": {
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.59.0.tgz",
+ "integrity": "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.59.0.tgz",
+ "integrity": "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.59.0.tgz",
+ "integrity": "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.59.0.tgz",
+ "integrity": "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.59.0.tgz",
+ "integrity": "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.59.0.tgz",
+ "integrity": "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.59.0.tgz",
+ "integrity": "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.59.0.tgz",
+ "integrity": "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.59.0.tgz",
+ "integrity": "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.59.0.tgz",
+ "integrity": "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.59.0.tgz",
+ "integrity": "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.59.0.tgz",
+ "integrity": "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.59.0.tgz",
+ "integrity": "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.59.0.tgz",
+ "integrity": "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.59.0.tgz",
+ "integrity": "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.59.0.tgz",
+ "integrity": "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.59.0.tgz",
+ "integrity": "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.59.0.tgz",
+ "integrity": "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.59.0.tgz",
+ "integrity": "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.59.0.tgz",
+ "integrity": "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.59.0.tgz",
+ "integrity": "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.59.0.tgz",
+ "integrity": "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.59.0.tgz",
+ "integrity": "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.59.0.tgz",
+ "integrity": "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@simplewebauthn/browser": {
+ "version": "13.2.2",
+ "resolved": "https://registry.npmjs.org/@simplewebauthn/browser/-/browser-13.2.2.tgz",
+ "integrity": "sha512-FNW1oLQpTJyqG5kkDg5ZsotvWgmBaC6jCHR7Ej0qUNep36Wl9tj2eZu7J5rP+uhXgHaLk+QQ3lqcw2vS5MX1IA==",
+ "license": "MIT"
+ },
+ "node_modules/@socket.io/component-emitter": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz",
+ "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==",
+ "license": "MIT"
+ },
+ "node_modules/@tanstack/match-sorter-utils": {
+ "version": "8.19.4",
+ "resolved": "https://registry.npmjs.org/@tanstack/match-sorter-utils/-/match-sorter-utils-8.19.4.tgz",
+ "integrity": "sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==",
+ "license": "MIT",
+ "dependencies": {
+ "remove-accents": "0.5.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/query-core": {
+ "version": "5.90.20",
+ "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz",
+ "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tanstack/vue-query": {
+ "version": "5.92.9",
+ "resolved": "https://registry.npmjs.org/@tanstack/vue-query/-/vue-query-5.92.9.tgz",
+ "integrity": "sha512-jjAZcqKveyX0C4w/6zUqbnqk/XzuxNWaFsWjGTJWULVFizUNeLGME2gf9vVSDclIyiBhR13oZJPPs6fJgfpIJQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/match-sorter-utils": "^8.19.4",
+ "@tanstack/query-core": "5.90.20",
+ "@vue/devtools-api": "^6.6.3",
+ "vue-demi": "^0.14.10"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.1.2",
+ "vue": "^2.6.0 || ^3.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@tanstack/vue-query/node_modules/vue-demi": {
+ "version": "0.14.10",
+ "resolved": "https://registry.npmjs.org/vue-demi/-/vue-demi-0.14.10.tgz",
+ "integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "vue-demi-fix": "bin/vue-demi-fix.js",
+ "vue-demi-switch": "bin/vue-demi-switch.js"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@vue/composition-api": "^1.0.0-rc.1",
+ "vue": "^3.0.0-0 || ^2.6.0"
+ },
+ "peerDependenciesMeta": {
+ "@vue/composition-api": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@types/body-parser": {
+ "version": "1.19.6",
+ "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
+ "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/connect": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/chrome": {
+ "version": "0.0.262",
+ "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.262.tgz",
+ "integrity": "sha512-TOoj3dqSYE13PD2fRuMQ6X6pggEvL9rRk/yOYOyWE6sfqRWxsJm4VoVm+wr9pkr4Sht/M5t7FFL4vXato8d1gA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/filesystem": "*",
+ "@types/har-format": "*"
+ }
+ },
+ "node_modules/@types/compression": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@types/compression/-/compression-1.8.1.tgz",
+ "integrity": "sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/express": "*",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/connect": {
+ "version": "3.4.38",
+ "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
+ "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/cordova": {
+ "version": "11.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-11.0.3.tgz",
+ "integrity": "sha512-kyuRQ40/NWQVhqGIHq78Ehu2Bf9Mlg0LhmSmis6ZFJK7z933FRfYi8tHe/k/0fB+PGfCf95rJC6TO7dopaFvAg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/express": {
+ "version": "4.17.25",
+ "resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.25.tgz",
+ "integrity": "sha512-dVd04UKsfpINUnK0yBoYHDF3xu7xVH4BuDotC/xGuycx4CgbP48X/KF/586bcObxT0HENHXEU8Nqtu6NR+eKhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/body-parser": "*",
+ "@types/express-serve-static-core": "^4.17.33",
+ "@types/qs": "*",
+ "@types/serve-static": "^1"
+ }
+ },
+ "node_modules/@types/express-serve-static-core": {
+ "version": "4.19.8",
+ "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.19.8.tgz",
+ "integrity": "sha512-02S5fmqeoKzVZCHPZid4b8JH2eM5HzQLZWN2FohQEy/0eXTq8VXZfSN6Pcr3F6N9R/vNrj7cpgbhjie6m/1tCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*",
+ "@types/qs": "*",
+ "@types/range-parser": "*",
+ "@types/send": "*"
+ }
+ },
+ "node_modules/@types/filesystem": {
+ "version": "0.0.36",
+ "resolved": "https://registry.npmjs.org/@types/filesystem/-/filesystem-0.0.36.tgz",
+ "integrity": "sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/filewriter": "*"
+ }
+ },
+ "node_modules/@types/filewriter": {
+ "version": "0.0.33",
+ "resolved": "https://registry.npmjs.org/@types/filewriter/-/filewriter-0.0.33.tgz",
+ "integrity": "sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/har-format": {
+ "version": "1.2.16",
+ "resolved": "https://registry.npmjs.org/@types/har-format/-/har-format-1.2.16.tgz",
+ "integrity": "sha512-fluxdy7ryD3MV6h8pTfTYpy/xQzCFC7m89nOH9y94cNqJ1mDIDPut7MnRHI3F6qRmh/cT2fUjG1MLdCNb4hE9A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/http-errors": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz",
+ "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/lodash": {
+ "version": "4.17.24",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.24.tgz",
+ "integrity": "sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/markdown-it": {
+ "version": "14.1.2",
+ "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
+ "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/linkify-it": "^5",
+ "@types/mdurl": "^2"
+ }
+ },
+ "node_modules/@types/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mime": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
+ "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "25.3.2",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.3.2.tgz",
+ "integrity": "sha512-RpV6r/ij22zRRdyBPcxDeKAzH43phWVKEjL2iksqo1Vz3CuBUrgmPpPhALKiRfU7OMCmeeO9vECBMsV0hMTG8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.18.0"
+ }
+ },
+ "node_modules/@types/node-forge": {
+ "version": "1.3.14",
+ "resolved": "https://registry.npmjs.org/@types/node-forge/-/node-forge-1.3.14.tgz",
+ "integrity": "sha512-mhVF2BnD4BO+jtOp7z1CdzaK4mbuK0LLQYAvdOLqHTavxFNq4zA1EmYkpnFjP8HOUzedfQkRnp0E2ulSAYSzAw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/qs": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz",
+ "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/range-parser": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
+ "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/send": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz",
+ "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/serve-static": {
+ "version": "1.15.10",
+ "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.10.tgz",
+ "integrity": "sha512-tRs1dB+g8Itk72rlSI2ZrW6vZg0YrLI81iQSTkMmOqnqCaNr/8Ek4VwWcN5vZgCYWbg/JJSGBlUaYGAOP73qBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-errors": "*",
+ "@types/node": "*",
+ "@types/send": "<1"
+ }
+ },
+ "node_modules/@types/serve-static/node_modules/@types/send": {
+ "version": "0.17.6",
+ "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.6.tgz",
+ "integrity": "sha512-Uqt8rPBE8SY0RK8JB1EzVOIZ32uqy8HwdxCnoCOsYrvnswqmFZ/k+9Ikidlk/ImhsdvBsloHbAlewb2IEBV/Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/mime": "^1",
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz",
+ "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.56.1",
+ "@typescript-eslint/types": "^8.56.1",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz",
+ "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz",
+ "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz",
+ "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz",
+ "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.56.1",
+ "@typescript-eslint/tsconfig-utils": "8.56.1",
+ "@typescript-eslint/types": "8.56.1",
+ "@typescript-eslint/visitor-keys": "8.56.1",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys": {
+ "version": "8.56.1",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz",
+ "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.56.1",
+ "eslint-visitor-keys": "^5.0.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ }
+ },
+ "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz",
+ "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^20.19.0 || ^22.13.0 || >=24"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "6.0.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.4.tgz",
+ "integrity": "sha512-uM5iXipgYIn13UUQCZNdWkYk+sysBeA97d5mHsAoAt1u/wpN3+zxOmsVJWosuzX+IMGRzeYUNytztrYznboIkQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-rc.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/@volar/source-map": {
+ "version": "2.4.28",
+ "resolved": "https://registry.npmjs.org/@volar/source-map/-/source-map-2.4.28.tgz",
+ "integrity": "sha512-yX2BDBqJkRXfKw8my8VarTyjv48QwxdJtvRgUpNE5erCsgEUdI2DsLbpa+rOQVAJYshY99szEcRDmyHbF10ggQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vue-macros/common": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@vue-macros/common/-/common-3.1.2.tgz",
+ "integrity": "sha512-h9t4ArDdniO9ekYHAD95t9AZcAbb19lEGK+26iAjUODOIJKmObDNBSe4+6ELQAA3vtYiFPPBtHh7+cQCKi3Dng==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-sfc": "^3.5.22",
+ "ast-kit": "^2.1.2",
+ "local-pkg": "^1.1.2",
+ "magic-string-ast": "^1.0.2",
+ "unplugin-utils": "^0.3.0"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/vue-macros"
+ },
+ "peerDependencies": {
+ "vue": "^2.7.0 || ^3.2.25"
+ },
+ "peerDependenciesMeta": {
+ "vue": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/babel-helper-vue-transform-on": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vue/babel-helper-vue-transform-on/-/babel-helper-vue-transform-on-1.5.0.tgz",
+ "integrity": "sha512-0dAYkerNhhHutHZ34JtTl2czVQHUNWv6xEbkdF5W+Yrv5pCWsqjeORdOgbtW2I9gWlt+wBmVn+ttqN9ZxR5tzA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@vue/babel-plugin-jsx": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vue/babel-plugin-jsx/-/babel-plugin-jsx-1.5.0.tgz",
+ "integrity": "sha512-mneBhw1oOqCd2247O0Yw/mRwC9jIGACAJUlawkmMBiNmL4dGA2eMzuNZVNqOUfYTa6vqmND4CtOPzmEEEqLKFw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/plugin-syntax-jsx": "^7.27.1",
+ "@babel/template": "^7.27.2",
+ "@babel/traverse": "^7.28.0",
+ "@babel/types": "^7.28.2",
+ "@vue/babel-helper-vue-transform-on": "1.5.0",
+ "@vue/babel-plugin-resolve-type": "1.5.0",
+ "@vue/shared": "^3.5.18"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@vue/babel-plugin-resolve-type": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@vue/babel-plugin-resolve-type/-/babel-plugin-resolve-type-1.5.0.tgz",
+ "integrity": "sha512-Wm/60o+53JwJODm4Knz47dxJnLDJ9FnKnGZJbUUf8nQRAtt6P+undLUAVU3Ha33LxOJe6IPoifRQ6F/0RrU31w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.27.1",
+ "@babel/helper-module-imports": "^7.27.1",
+ "@babel/helper-plugin-utils": "^7.27.1",
+ "@babel/parser": "^7.28.0",
+ "@vue/compiler-sfc": "^3.5.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@vue/compiler-core": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.29.tgz",
+ "integrity": "sha512-cuzPhD8fwRHk8IGfmYaR4eEe4cAyJEL66Ove/WZL7yWNL134nqLddSLwNRIsFlnnW1kK+p8Ck3viFnC0chXCXw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@vue/shared": "3.5.29",
+ "entities": "^7.0.1",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-dom": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.29.tgz",
+ "integrity": "sha512-n0G5o7R3uBVmVxjTIYcz7ovr8sy7QObFG8OQJ3xGCDNhbG60biP/P5KnyY8NLd81OuT1WJflG7N4KWYHaeeaIg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-core": "3.5.29",
+ "@vue/shared": "3.5.29"
+ }
+ },
+ "node_modules/@vue/compiler-sfc": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.29.tgz",
+ "integrity": "sha512-oJZhN5XJs35Gzr50E82jg2cYdZQ78wEwvRO6Y63TvLVTc+6xICzJHP1UIecdSPPYIbkautNBanDiWYa64QSFIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.0",
+ "@vue/compiler-core": "3.5.29",
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/compiler-ssr": "3.5.29",
+ "@vue/shared": "3.5.29",
+ "estree-walker": "^2.0.2",
+ "magic-string": "^0.30.21",
+ "postcss": "^8.5.6",
+ "source-map-js": "^1.2.1"
+ }
+ },
+ "node_modules/@vue/compiler-ssr": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.29.tgz",
+ "integrity": "sha512-Y/ARJZE6fpjzL5GH/phJmsFwx3g6t2KmHKHx5q+MLl2kencADKIrhH5MLF6HHpRMmlRAYBRSvv347Mepf1zVNw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/shared": "3.5.29"
+ }
+ },
+ "node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
+ "node_modules/@vue/devtools-core": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-core/-/devtools-core-8.0.6.tgz",
+ "integrity": "sha512-fN7iVtpSQQdtMORWwVZ1JiIAKriinhD+lCHqPw9Rr252ae2TczILEmW0zcAZifPW8HfYcbFkn+h7Wv6kQQCayw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^8.0.6",
+ "@vue/devtools-shared": "^8.0.6",
+ "mitt": "^3.0.1",
+ "nanoid": "^5.1.5",
+ "pathe": "^2.0.3",
+ "vite-hot-client": "^2.1.0"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/@vue/devtools-core/node_modules/nanoid": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz",
+ "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ }
+ },
+ "node_modules/@vue/devtools-kit": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-8.0.6.tgz",
+ "integrity": "sha512-9zXZPTJW72OteDXeSa5RVML3zWDCRcO5t77aJqSs228mdopYj5AiTpihozbsfFJ0IodfNs7pSgOGO3qfCuxDtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^8.0.6",
+ "birpc": "^2.6.1",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^2.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/@vue/devtools-shared": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-8.0.6.tgz",
+ "integrity": "sha512-Pp1JylTqlgMJvxW6MGyfTF8vGvlBSCAvMFaDCYa82Mgw7TT5eE5kkHgDvmOGHWeJE4zIDfCpCxHapsK2LtIAJg==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/@vue/language-plugin-pug": {
+ "version": "3.2.5",
+ "resolved": "https://registry.npmjs.org/@vue/language-plugin-pug/-/language-plugin-pug-3.2.5.tgz",
+ "integrity": "sha512-E98NSmXmlYd5HIrv2f3IbPE1dgKjKABlDCdySqMxrlQ/kh4fAf91mUoxQKsEKhikE62QIylFDts8S84/du5dfQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@volar/source-map": "2.4.28",
+ "muggle-string": "^0.4.1",
+ "pug-lexer": "^5.0.1",
+ "pug-parser": "^6.0.0",
+ "vscode-languageserver-textdocument": "^1.0.11"
+ }
+ },
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.29.tgz",
+ "integrity": "sha512-zcrANcrRdcLtmGZETBxWqIkoQei8HaFpZWx/GHKxx79JZsiZ8j1du0VUJtu4eJjgFvU/iKL5lRXFXksVmI+5DA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.29"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.29.tgz",
+ "integrity": "sha512-8DpW2QfdwIWOLqtsNcds4s+QgwSaHSJY/SUe04LptianUQ/0xi6KVsu/pYVh+HO3NTVvVJjIPL2t6GdeKbS4Lg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.29",
+ "@vue/shared": "3.5.29"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.29.tgz",
+ "integrity": "sha512-AHvvJEtcY9tw/uk+s/YRLSlxxQnqnAkjqvK25ZiM4CllCZWzElRAoQnCM42m9AHRLNJ6oe2kC5DCgD4AUdlvXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.29",
+ "@vue/runtime-core": "3.5.29",
+ "@vue/shared": "3.5.29",
+ "csstype": "^3.2.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.29.tgz",
+ "integrity": "sha512-G/1k6WK5MusLlbxSE2YTcqAAezS+VuwHhOvLx2KnQU7G2zCH6KIb+5Wyt6UjMq7a3qPzNEjJXs1hvAxDclQH+g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.29",
+ "@vue/shared": "3.5.29"
+ },
+ "peerDependencies": {
+ "vue": "3.5.29"
+ }
+ },
+ "node_modules/@vue/shared": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.29.tgz",
+ "integrity": "sha512-w7SR0A5zyRByL9XUkCfdLs7t9XOHUyJ67qPGQjOou3p6GvBeBW+AVjUUmlxtZ4PIYaRvE+1LmK44O4uajlZwcg==",
+ "license": "MIT"
+ },
+ "node_modules/@xterm/xterm": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz",
+ "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==",
+ "license": "MIT",
+ "workspaces": [
+ "addons/*"
+ ]
+ },
+ "node_modules/abort-controller": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
+ "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "event-target-shim": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=6.5"
+ }
+ },
+ "node_modules/accepts": {
+ "version": "1.3.8",
+ "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
+ "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-types": "~2.1.34",
+ "negotiator": "0.6.3"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/accepts/node_modules/negotiator": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
+ "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/acorn": {
+ "version": "8.16.0",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz",
+ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==",
+ "license": "MIT",
+ "peer": true,
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/acorn-jsx": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+ "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ansi-escapes": {
+ "version": "4.3.2",
+ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz",
+ "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^0.21.3"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-escapes/node_modules/type-fest": {
+ "version": "0.21.3",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz",
+ "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/ansis": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/ansis/-/ansis-4.2.0.tgz",
+ "integrity": "sha512-HqZ5rWlFjGiV0tDm3UxxgNRqsOTniqoKZu0pIAfh7TZQMGuZK+hH0drySty0si0QXj1ieop4+SkSfPZBPPkHig==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/anymatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz",
+ "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.2",
+ "async": "^3.2.4",
+ "buffer-crc32": "^1.0.0",
+ "readable-stream": "^4.0.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^3.0.0",
+ "zip-stream": "^6.0.1"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz",
+ "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.0.0",
+ "graceful-fs": "^4.2.0",
+ "is-stream": "^2.0.1",
+ "lazystream": "^1.0.0",
+ "lodash": "^4.17.15",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "license": "Python-2.0"
+ },
+ "node_modules/array-flatten": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
+ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/asap": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz",
+ "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==",
+ "license": "MIT"
+ },
+ "node_modules/assert-never": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/assert-never/-/assert-never-1.4.0.tgz",
+ "integrity": "sha512-5oJg84os6NMQNl27T9LnZkvvqzvAnHu03ShCnoj6bsJwS7L8AO4lf+C/XjK/nvzEqQB744moC6V128RucQd1jA==",
+ "license": "MIT"
+ },
+ "node_modules/ast-kit": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/ast-kit/-/ast-kit-2.2.0.tgz",
+ "integrity": "sha512-m1Q/RaVOnTp9JxPX+F+Zn7IcLYMzM8kZofDImfsKZd8MbR+ikdOzTeztStWqfrqIxZnYWryyI9ePm3NGjnZgGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.5",
+ "pathe": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/ast-walker-scope": {
+ "version": "0.8.3",
+ "resolved": "https://registry.npmjs.org/ast-walker-scope/-/ast-walker-scope-0.8.3.tgz",
+ "integrity": "sha512-cbdCP0PGOBq0ASG+sjnKIoYkWMKhhz+F/h9pRexUdX2Hd38+WOlBkRKlqkGOSm0YQpcFMQBJeK4WspUAkwsEdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.4",
+ "ast-kit": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/autoprefixer": {
+ "version": "10.4.27",
+ "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.27.tgz",
+ "integrity": "sha512-NP9APE+tO+LuJGn7/9+cohklunJsXWiaWEfV3si4Gi/XHDwVNgkwr1J3RQYFIvPy76GmJ9/bW8vyoU1LcxwKHA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/autoprefixer"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "browserslist": "^4.28.1",
+ "caniuse-lite": "^1.0.30001774",
+ "fraction.js": "^5.3.4",
+ "picocolors": "^1.1.1",
+ "postcss-value-parser": "^4.2.0"
+ },
+ "bin": {
+ "autoprefixer": "bin/autoprefixer"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ },
+ "peerDependencies": {
+ "postcss": "^8.1.0"
+ }
+ },
+ "node_modules/b4a": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz",
+ "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "react-native-b4a": "*"
+ },
+ "peerDependenciesMeta": {
+ "react-native-b4a": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/babel-walk": {
+ "version": "3.0.0-canary-5",
+ "resolved": "https://registry.npmjs.org/babel-walk/-/babel-walk-3.0.0-canary-5.tgz",
+ "integrity": "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.9.6"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz",
+ "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/bare-events": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz",
+ "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peerDependencies": {
+ "bare-abort-controller": "*"
+ },
+ "peerDependenciesMeta": {
+ "bare-abort-controller": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
+ "integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/birpc": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.9.0.tgz",
+ "integrity": "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bl/node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/body-parser": {
+ "version": "1.20.4",
+ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
+ "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "content-type": "~1.0.5",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "~1.2.0",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "on-finished": "~2.4.1",
+ "qs": "~6.14.0",
+ "raw-body": "~2.5.3",
+ "type-is": "~1.6.18",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/body-parser/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/body-parser/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/brace-expansion": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz",
+ "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^4.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/browser-fs-access": {
+ "version": "0.38.0",
+ "resolved": "https://registry.npmjs.org/browser-fs-access/-/browser-fs-access-0.38.0.tgz",
+ "integrity": "sha512-JveqW2w6pEZqFEEfMgCszXzYpE89dG+nPsmOdcs741mFFAROeL+iqjGEpR07RI+s0YY0EFr+4KnOoACprJTpOw==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/browserlist": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/browserlist/-/browserlist-1.0.2.tgz",
+ "integrity": "sha512-welJSH1jwS/TcyCd/XZ9vj/2ijgDNEbcrS7YCjIB0Tc1sfFnfK9c80xCbTy/kY3oCqWWPHtBNQpVuPu55ErNEw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "browserlist": "cli.js"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.1",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz",
+ "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "baseline-browser-mapping": "^2.9.0",
+ "caniuse-lite": "^1.0.30001759",
+ "electron-to-chromium": "^1.5.263",
+ "node-releases": "^2.0.27",
+ "update-browserslist-db": "^1.2.0"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz",
+ "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bundle-name": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
+ "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "run-applescript": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/bytes": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
+ "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/call-bound": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
+ "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "get-intrinsic": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/callsites": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+ "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/camel-case": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz",
+ "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pascal-case": "^3.1.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001774",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001774.tgz",
+ "integrity": "sha512-DDdwPGz99nmIEv216hKSgLD+D4ikHQHjBC/seF98N9CPqRX4M5mSxT9eTV6oyisnJcuzxtZy4n17yKKQYmYQOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/character-parser": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-2.2.0.tgz",
+ "integrity": "sha512-+UqJQjFEFaTAs3bNsF2j2kEN1baG/zghZbdqoYEDxGZtJo9LBzl1A+m0D4n3qKx8N2FNv8/Xp6yV9mQmBuptaw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-regex": "^1.0.3"
+ }
+ },
+ "node_modules/chardet": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/chardet/-/chardet-2.1.1.tgz",
+ "integrity": "sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/ci-info": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz",
+ "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/clean-css": {
+ "version": "5.3.3",
+ "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz",
+ "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "source-map": "~0.6.0"
+ },
+ "engines": {
+ "node": ">= 10.0"
+ }
+ },
+ "node_modules/cli-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
+ "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "restore-cursor": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-spinners": {
+ "version": "2.9.2",
+ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz",
+ "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cli-width": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-4.1.0.tgz",
+ "integrity": "sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/clipboard": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/clipboard/-/clipboard-2.0.11.tgz",
+ "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+ "license": "MIT",
+ "dependencies": {
+ "good-listener": "^1.2.2",
+ "select": "^1.1.2",
+ "tiny-emitter": "^2.0.0"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/cliui/node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/clone": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz",
+ "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/clone-deep": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz",
+ "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-plain-object": "^2.0.4",
+ "kind-of": "^6.0.2",
+ "shallow-clone": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/codemirror": {
+ "version": "5.65.11",
+ "resolved": "https://registry.npmjs.org/codemirror/-/codemirror-5.65.11.tgz",
+ "integrity": "sha512-Gp62g2eKSCHYt10axmGhKq3WoJSvVpvhXmowNq7pZdRVowwtvBR/hi2LSP5srtctKkRT33T6/n8Kv1UGp7JW4A==",
+ "license": "MIT"
+ },
+ "node_modules/codemirror-asciidoc": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/codemirror-asciidoc/-/codemirror-asciidoc-1.0.4.tgz",
+ "integrity": "sha512-U+G8+ToPONYFGkwTprxpFzV6EV1bCA9zChAA8uT2YAnKFn357JMWL2VkdIPy2yP5N/X13GzslMOGaAk1UNE3rA==",
+ "license": "BSD"
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/colorjs.io": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.5.2.tgz",
+ "integrity": "sha512-twmVoizEW7ylZSN32OgKdXRmo1qg+wT5/6C3xu5b9QsWzSFAhHLn2xd8ro0diCsKfCj1RdaTP/nrcW+vAoQPIw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "10.0.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+ "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz",
+ "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "crc32-stream": "^6.0.0",
+ "is-stream": "^2.0.1",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/compressible": {
+ "version": "2.0.18",
+ "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz",
+ "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": ">= 1.43.0 < 2"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/compression": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
+ "integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "3.1.2",
+ "compressible": "~2.0.18",
+ "debug": "2.6.9",
+ "negotiator": "~0.6.4",
+ "on-headers": "~1.1.0",
+ "safe-buffer": "5.2.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/compression/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/compression/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/confbox": {
+ "version": "0.1.8",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.1.8.tgz",
+ "integrity": "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==",
+ "license": "MIT"
+ },
+ "node_modules/constantinople": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-4.0.1.tgz",
+ "integrity": "sha512-vCrqcSIq4//Gx74TXXCGnHpulY1dskqLTFGDmhrGxzeXL8lF8kvXv6mpNWlJj1uD4DW23D4ljAqbY4RRaaUZIw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.6.0",
+ "@babel/types": "^7.6.1"
+ }
+ },
+ "node_modules/content-disposition": {
+ "version": "0.5.4",
+ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
+ "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "5.2.1"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/content-type": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
+ "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/cookie-signature": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
+ "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/copy-anything": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-4.0.5.tgz",
+ "integrity": "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-what": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+ "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz",
+ "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/deep-is": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+ "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/default-browser": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
+ "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bundle-name": "^4.1.0",
+ "default-browser-id": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/default-browser-id": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
+ "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defaults": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz",
+ "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone": "^1.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/define-lazy-prop": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
+ "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/delegate": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz",
+ "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
+ "license": "MIT"
+ },
+ "node_modules/depd": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
+ "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/dependency-graph": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/dependency-graph/-/dependency-graph-1.0.0.tgz",
+ "integrity": "sha512-cW3gggJ28HZ/LExwxP2B++aiKxhJXMSIt9K48FOXQkm+vuG5gyatXnLsONRJdzO/7VfjDIiaOOa/bs4l464Lwg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/destroy": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
+ "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8",
+ "npm": "1.2.8000 || >= 1.4.16"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/doctypes": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/doctypes/-/doctypes-1.1.0.tgz",
+ "integrity": "sha512-LLBi6pEqS6Do3EKQ3J0NqHWV5hhb78Pi8vvESYwyOy2c31ZEZVdtitdzsQsKb7878PEERhzUk0ftqGhG6Mz+pQ==",
+ "license": "MIT"
+ },
+ "node_modules/dompurify": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.7.tgz",
+ "integrity": "sha512-WhL/YuveyGXJaerVlMYGWhvQswa7myDG17P7Vu65EWC05o8vfeNbvNf4d/BOvH99+ZW+LlQsc1GDKMa1vNK6dw==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
+ "node_modules/dot-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz",
+ "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "9.0.0",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-9.0.0.tgz",
+ "integrity": "sha512-1gxPBJpI/pcjQhKgIU91II6Wkay+dLcN3M6rf2uwP8hRur3HtQXjVrdAK3sjC0piaEuxzMwjXChcETiJl47lAQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "type-fest": "^4.18.2"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "16.6.1",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz",
+ "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dotenv-expand": {
+ "version": "11.0.7",
+ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz",
+ "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "dotenv": "^16.4.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://dotenvx.com"
+ }
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ee-first": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
+ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.302",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.302.tgz",
+ "integrity": "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/elementtree": {
+ "version": "0.1.7",
+ "resolved": "https://registry.npmjs.org/elementtree/-/elementtree-0.1.7.tgz",
+ "integrity": "sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "sax": "1.1.4"
+ },
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/encodeurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
+ "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/engine.io-client": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.6.4.tgz",
+ "integrity": "sha512-+kjUJnZGwzewFDw951CDWcwj35vMNf2fcj7xQWOctq1F2i1jkDdVvdFG9kM/BEChymCH36KgjnW0NsL58JYRxw==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1",
+ "engine.io-parser": "~5.2.1",
+ "ws": "~8.18.3",
+ "xmlhttprequest-ssl": "~2.1.1"
+ }
+ },
+ "node_modules/engine.io-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz",
+ "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz",
+ "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/error-stack-parser-es": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
+ "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-html": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
+ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/escodegen": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz",
+ "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esprima": "^4.0.1",
+ "estraverse": "^5.2.0",
+ "esutils": "^2.0.2"
+ },
+ "bin": {
+ "escodegen": "bin/escodegen.js",
+ "esgenerate": "bin/esgenerate.js"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "optionalDependencies": {
+ "source-map": "~0.6.1"
+ }
+ },
+ "node_modules/eslint": {
+ "version": "9.32.0",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz",
+ "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.12.1",
+ "@eslint/config-array": "^0.21.0",
+ "@eslint/config-helpers": "^0.3.0",
+ "@eslint/core": "^0.15.0",
+ "@eslint/eslintrc": "^3.3.1",
+ "@eslint/js": "9.32.0",
+ "@eslint/plugin-kit": "^0.3.4",
+ "@humanfs/node": "^0.16.6",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@humanwhocodes/retry": "^0.4.2",
+ "@types/estree": "^1.0.6",
+ "@types/json-schema": "^7.0.15",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.6",
+ "debug": "^4.3.2",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^8.4.0",
+ "eslint-visitor-keys": "^4.2.1",
+ "espree": "^10.4.0",
+ "esquery": "^1.5.0",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^8.0.0",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
+ },
+ "peerDependencies": {
+ "jiti": "*"
+ },
+ "peerDependenciesMeta": {
+ "jiti": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/eslint-scope": {
+ "version": "8.4.0",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz",
+ "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eslint/node_modules/brace-expansion": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
+ "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/eslint/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/eslint/node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/eslint/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/espree": {
+ "version": "10.4.0",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz",
+ "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^4.2.1"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/espree/node_modules/eslint-visitor-keys": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/esprima": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "bin": {
+ "esparse": "bin/esparse.js",
+ "esvalidate": "bin/esvalidate.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/esquery": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz",
+ "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "estraverse": "^5.1.0"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/esrecurse": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+ "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estraverse": {
+ "version": "5.3.0",
+ "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+ "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/estree-walker": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
+ "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
+ "license": "MIT"
+ },
+ "node_modules/esutils": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+ "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/etag": {
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
+ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/event-target-shim": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
+ "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/events": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
+ "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.x"
+ }
+ },
+ "node_modules/events-universal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz",
+ "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "bare-events": "^2.7.0"
+ }
+ },
+ "node_modules/express": {
+ "version": "4.22.1",
+ "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
+ "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "accepts": "~1.3.8",
+ "array-flatten": "1.1.1",
+ "body-parser": "~1.20.3",
+ "content-disposition": "~0.5.4",
+ "content-type": "~1.0.4",
+ "cookie": "~0.7.1",
+ "cookie-signature": "~1.0.6",
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "finalhandler": "~1.3.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.0",
+ "merge-descriptors": "1.0.3",
+ "methods": "~1.1.2",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "path-to-regexp": "~0.1.12",
+ "proxy-addr": "~2.0.7",
+ "qs": "~6.14.0",
+ "range-parser": "~1.2.1",
+ "safe-buffer": "5.2.1",
+ "send": "~0.19.0",
+ "serve-static": "~1.16.2",
+ "setprototypeof": "1.2.0",
+ "statuses": "~2.0.1",
+ "type-is": "~1.6.18",
+ "utils-merge": "1.0.1",
+ "vary": "~1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.10.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/express/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/express/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/exsolve": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
+ "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-fifo": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz",
+ "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
+ "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "dev": true,
+ "license": "MIT",
+ "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.8"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-levenshtein": {
+ "version": "2.0.6",
+ "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+ "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fastq": {
+ "version": "1.20.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
+ "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/file-entry-cache": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz",
+ "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flat-cache": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/filesize": {
+ "version": "11.0.13",
+ "resolved": "https://registry.npmjs.org/filesize/-/filesize-11.0.13.tgz",
+ "integrity": "sha512-mYJ/qXKvREuO0uH8LTQJ6v7GsUvVOguqxg2VTwQUkyTPXXRRWPdjuUPVqdBrJQhvci48OHlNGRnux+Slr2Rnvw==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 10.8.0"
+ }
+ },
+ "node_modules/filesize-parser": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/filesize-parser/-/filesize-parser-1.5.1.tgz",
+ "integrity": "sha512-wRjdlQ5JM3WHZp6xpakIHQbkcGig8ANglYQDPcQSgZUN5kcDGOgmAwB0396BxzHxcl+kr+GLuusxBnsjdO6x9A==",
+ "license": "MIT"
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/finalhandler": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
+ "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "on-finished": "~2.4.1",
+ "parseurl": "~1.3.3",
+ "statuses": "~2.0.2",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/finalhandler/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/finalhandler/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/find-up": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+ "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^6.0.0",
+ "path-exists": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/flat": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+ "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "bin": {
+ "flat": "cli.js"
+ }
+ },
+ "node_modules/flat-cache": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz",
+ "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/flatted": {
+ "version": "3.3.3",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz",
+ "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/forwarded": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
+ "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fraction.js": {
+ "version": "5.3.4",
+ "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz",
+ "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/rawify"
+ }
+ },
+ "node_modules/fresh": {
+ "version": "0.5.2",
+ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
+ "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/fs-extra": {
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz",
+ "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/fuse.js": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.1.0.tgz",
+ "integrity": "sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.2",
+ "es-define-property": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "es-object-atoms": "^1.1.1",
+ "function-bind": "^1.1.2",
+ "get-proto": "^1.0.1",
+ "gopd": "^1.2.0",
+ "has-symbols": "^1.1.0",
+ "hasown": "^2.0.2",
+ "math-intrinsics": "^1.1.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/glob/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/good-listener": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz",
+ "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+ "license": "MIT",
+ "dependencies": {
+ "delegate": "^3.1.2"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/hookable": {
+ "version": "5.5.3",
+ "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz",
+ "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==",
+ "license": "MIT"
+ },
+ "node_modules/html-minifier-terser": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz",
+ "integrity": "sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camel-case": "^4.1.2",
+ "clean-css": "~5.3.2",
+ "commander": "^10.0.0",
+ "entities": "^4.4.0",
+ "param-case": "^3.0.4",
+ "relateurl": "^0.2.7",
+ "terser": "^5.15.1"
+ },
+ "bin": {
+ "html-minifier-terser": "cli.js"
+ },
+ "engines": {
+ "node": "^14.13.1 || >=16.0.0"
+ }
+ },
+ "node_modules/html-minifier-terser/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/http-errors": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
+ "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "depd": "~2.0.0",
+ "inherits": "~2.0.4",
+ "setprototypeof": "~1.2.0",
+ "statuses": "~2.0.2",
+ "toidentifier": "~1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.4.24",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
+ "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/ignore": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/immutable": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz",
+ "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/import-fresh": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
+ "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "parent-module": "^1.0.0",
+ "resolve-from": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/imurmurhash": {
+ "version": "0.1.4",
+ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+ "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.8.19"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/inquirer": {
+ "version": "9.3.8",
+ "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-9.3.8.tgz",
+ "integrity": "sha512-pFGGdaHrmRKMh4WoDDSowddgjT1Vkl90atobmTeSmcPGdYiwikch/m/Ef5wRaiamHejtw0cUUMMerzDUXCci2w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@inquirer/external-editor": "^1.0.2",
+ "@inquirer/figures": "^1.0.3",
+ "ansi-escapes": "^4.3.2",
+ "cli-width": "^4.1.0",
+ "mute-stream": "1.0.0",
+ "ora": "^5.4.1",
+ "run-async": "^3.0.0",
+ "rxjs": "^7.8.1",
+ "string-width": "^4.2.3",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^6.2.0",
+ "yoctocolors-cjs": "^2.1.2"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/ipaddr.js": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
+ "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.16.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
+ "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-docker": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
+ "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-expression": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/is-expression/-/is-expression-4.0.0.tgz",
+ "integrity": "sha512-zMIXX63sxzG3XrkHkrAPvm/OVZVSCPNkwMHU8oTX7/U3AL78I0QXCEICXUM13BIa8TYGZ68PiTKfQz3yaTNr4A==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^7.1.1",
+ "object-assign": "^4.1.1"
+ }
+ },
+ "node_modules/is-expression/node_modules/acorn": {
+ "version": "7.4.1",
+ "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz",
+ "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==",
+ "license": "MIT",
+ "bin": {
+ "acorn": "bin/acorn"
+ },
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-inside-container": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
+ "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^3.0.0"
+ },
+ "bin": {
+ "is-inside-container": "cli.js"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-interactive": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz",
+ "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/is-plain-object": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+ "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "isobject": "^3.0.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-promise": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.2.2.tgz",
+ "integrity": "sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==",
+ "license": "MIT"
+ },
+ "node_modules/is-regex": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz",
+ "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==",
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "gopd": "^1.2.0",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-stream": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
+ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-unicode-supported": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+ "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/is-what": {
+ "version": "5.5.0",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-5.5.0.tgz",
+ "integrity": "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
+ }
+ },
+ "node_modules/is-wsl": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
+ "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-inside-container": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/isbinaryfile": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
+ "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/isobject": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+ "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/js-cookie": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
+ "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/js-stringify": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/js-stringify/-/js-stringify-1.0.2.tgz",
+ "integrity": "sha512-rtS5ATOo2Q5k1G+DADISilDA6lv79zIiwFd6CcjuIxGKLFm5C+RLImRscVap9k55i+MOZwgliw+NejvkLuGD5g==",
+ "license": "MIT"
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-stable-stringify-without-jsonify": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonc-eslint-parser": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/jsonc-eslint-parser/-/jsonc-eslint-parser-2.4.2.tgz",
+ "integrity": "sha512-1e4qoRgnn448pRuMvKGsFFymUCquZV0mpGgOyIKNgD3JVDTsVJyRBGH/Fm0tBb8WsWGgmB1mDe6/yJMQM37DUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.5.0",
+ "eslint-visitor-keys": "^3.0.0",
+ "espree": "^9.0.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ }
+ },
+ "node_modules/jsonc-eslint-parser/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/jstransformer": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/jstransformer/-/jstransformer-1.0.0.tgz",
+ "integrity": "sha512-C9YK3Rf8q6VAPDCCU9fnqo3mAfOH6vUGnMcP4AQAYIEpWtfGLpwOTmZ+igtdK5y+VvI2n3CyYSzy4Qh34eq24A==",
+ "license": "MIT",
+ "dependencies": {
+ "is-promise": "^2.0.0",
+ "promise": "^7.0.1"
+ }
+ },
+ "node_modules/jwt-decode": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jwt-decode/-/jwt-decode-4.0.0.tgz",
+ "integrity": "sha512-+KJGIyHgkGuIq3IEBNftfhW/LfWhXUIY6OmyVWjliu5KH1y0fw7VQ8YndE2O4qZdMSd9SqbnC8GOcZEy0Om7sA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/katex": {
+ "version": "0.16.33",
+ "resolved": "https://registry.npmjs.org/katex/-/katex-0.16.33.tgz",
+ "integrity": "sha512-q3N5u+1sY9Bu7T4nlXoiRBXWfwSefNGoKeOwekV+gw0cAXQlz2Ww6BLcmBxVDeXBMUDQv6fK5bcNaJLxob3ZQA==",
+ "funding": [
+ "https://opencollective.com/katex",
+ "https://github.com/sponsors/katex"
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^8.3.0"
+ },
+ "bin": {
+ "katex": "cli.js"
+ }
+ },
+ "node_modules/katex/node_modules/commander": {
+ "version": "8.3.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz",
+ "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/kind-of": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
+ "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/kolorist": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/kolorist/-/kolorist-1.8.0.tgz",
+ "integrity": "sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ky": {
+ "version": "1.14.3",
+ "resolved": "https://registry.npmjs.org/ky/-/ky-1.14.3.tgz",
+ "integrity": "sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/ky?sponsor=1"
+ }
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/linkify-it": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz",
+ "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==",
+ "license": "MIT",
+ "dependencies": {
+ "uc.micro": "^2.0.0"
+ }
+ },
+ "node_modules/local-pkg": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/local-pkg/-/local-pkg-1.1.2.tgz",
+ "integrity": "sha512-arhlxbFRmoQHl33a0Zkle/YWlmNwoyt6QNZEIJcqNbdrsix5Lvc4HyyI3EnwxTYlZYc32EbYrQ8SzEZ7dqgg9A==",
+ "license": "MIT",
+ "dependencies": {
+ "mlly": "^1.7.4",
+ "pkg-types": "^2.3.0",
+ "quansync": "^0.2.11"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
+ "node_modules/local-pkg/node_modules/confbox": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.4.tgz",
+ "integrity": "sha512-ysOGlgTFbN2/Y6Cg3Iye8YKulHw+R2fNXHrgSmXISQdMnomY6eNDprVdW9R5xBguEqI954+S6709UyiO7B+6OQ==",
+ "license": "MIT"
+ },
+ "node_modules/local-pkg/node_modules/pkg-types": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz",
+ "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==",
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.2.2",
+ "exsolve": "^1.0.7",
+ "pathe": "^2.0.3"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+ "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz",
+ "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash-es": {
+ "version": "4.17.23",
+ "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.23.tgz",
+ "integrity": "sha512-kVI48u3PZr38HdYz98UmfPnXl2DXrpdctLrFLCd3kOx1xUkOmpFPx7gCWWM5MPkL/fD8zb+Ph0QzjGFs4+hHWg==",
+ "license": "MIT"
+ },
+ "node_modules/lodash.merge": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+ "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.repeat": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/lodash.repeat/-/lodash.repeat-4.1.0.tgz",
+ "integrity": "sha512-eWsgQW89IewS95ZOcr15HHCX6FVDxq3f2PNUIng3fyzsPev9imFQxIYdFZ6crl8L56UR6ZlGDLcEb3RZsCSSqw==",
+ "license": "MIT"
+ },
+ "node_modules/log-symbols": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+ "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chalk": "^4.1.0",
+ "is-unicode-supported": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/lower-case": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz",
+ "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/lowlight": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
+ "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.0.0",
+ "highlight.js": "~11.11.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/luxon": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
+ "integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
+ "node_modules/magic-string-ast": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/magic-string-ast/-/magic-string-ast-1.0.3.tgz",
+ "integrity": "sha512-CvkkH1i81zl7mmb94DsRiFeG9V2fR2JeuK8yDgS8oiZSFa++wWLEgZ5ufEOyLHbvSbD1gTRKv9NdX69Rnvr9JA==",
+ "license": "MIT",
+ "dependencies": {
+ "magic-string": "^0.30.19"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/markdown-it": {
+ "version": "14.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz",
+ "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "argparse": "^2.0.1",
+ "entities": "^4.4.0",
+ "linkify-it": "^5.0.0",
+ "mdurl": "^2.0.0",
+ "punycode.js": "^2.3.1",
+ "uc.micro": "^2.1.0"
+ },
+ "bin": {
+ "markdown-it": "bin/markdown-it.mjs"
+ }
+ },
+ "node_modules/markdown-it-abbr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-abbr/-/markdown-it-abbr-2.0.0.tgz",
+ "integrity": "sha512-of7C8pXSjXjDojW4neNP+jD7inUYH/DO0Ca+K/4FUEccg0oHAEX/nfscw0jfz66PJbYWOAT9U8mjO21X5p6aAw==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-attrs": {
+ "version": "4.3.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-attrs/-/markdown-it-attrs-4.3.1.tgz",
+ "integrity": "sha512-/ko6cba+H6gdZ0DOw7BbNMZtfuJTRp9g/IrGIuz8lYc/EfnmWRpaR3CFPnNbVz0LDvF8Gf1hFGPqrQqq7De0rg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ },
+ "peerDependencies": {
+ "markdown-it": ">= 9.0.0"
+ }
+ },
+ "node_modules/markdown-it-decorate": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/markdown-it-decorate/-/markdown-it-decorate-1.2.2.tgz",
+ "integrity": "sha512-7BFWJ97KBXgkaPVjKHISQnhSW8RWQ7yRNXpr8pPUV2Rw4GHvGrgb6CelKCM+GSijP0uSLCAVfc/knWIz+2v/Sw==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-emoji": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-emoji/-/markdown-it-emoji-3.0.0.tgz",
+ "integrity": "sha512-+rUD93bXHubA4arpEZO3q80so0qgoFJEKRkRbjKX8RTdca89v2kfyF+xR3i2sQTwql9tpPZPOQN5B+PunspXRg==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-expand-tabs": {
+ "version": "1.0.13",
+ "resolved": "https://registry.npmjs.org/markdown-it-expand-tabs/-/markdown-it-expand-tabs-1.0.13.tgz",
+ "integrity": "sha512-ODpk98FWkGIq2vkwm2NOLt4G6TRgy3M9eTa9SFm06pUyOd0zjjYAwkhsjiCDU42pzKuz0ChiwBO0utuOj3LNOA==",
+ "license": "ISC",
+ "dependencies": {
+ "lodash.repeat": "^4.0.0"
+ }
+ },
+ "node_modules/markdown-it-footnote": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-footnote/-/markdown-it-footnote-4.0.0.tgz",
+ "integrity": "sha512-WYJ7urf+khJYl3DqofQpYfEYkZKbmXmwxQV8c8mO/hGIhgZ1wOe7R4HLFNwqx7TjILbnC98fuyeSsin19JdFcQ==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-imsize": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-imsize/-/markdown-it-imsize-2.0.1.tgz",
+ "integrity": "sha512-5SH90ademqcR8ifQCBXRCfIR4HGfZZOh5pO0j2TglulfSQH+SBXM4Iw/QlTUbSoUwVZArCYgECoMvktDS2kP3w==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-mark": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-mark/-/markdown-it-mark-4.0.0.tgz",
+ "integrity": "sha512-YLhzaOsU9THO/cal0lUjfMjrqSMPjjyjChYM7oyj4DnyaXEzA8gnW6cVJeyCrCVeyesrY2PlEdUYJSPFYL4Nkg==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-mdc": {
+ "version": "0.2.12",
+ "resolved": "https://registry.npmjs.org/markdown-it-mdc/-/markdown-it-mdc-0.2.12.tgz",
+ "integrity": "sha512-kXdgH+wvEFw1KaFDL+IdjJijtjDBj0bhhvVANvl9bhRokkyhcGEd1HCYsj336YqJHihgMEYcbGWLm/qjLMTdzg==",
+ "license": "MIT",
+ "dependencies": {
+ "yaml": "^2.8.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "@types/markdown-it": "*",
+ "markdown-it": "^14.0.0"
+ }
+ },
+ "node_modules/markdown-it-multimd-table": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/markdown-it-multimd-table/-/markdown-it-multimd-table-4.2.3.tgz",
+ "integrity": "sha512-KepCr2OMJqm7IT6sOIbuqHGe+NERhgy66XMrc5lo6dHW7oaPzMDtYwR1EGwK16/blb6mCSg4jqityOe0o/H7HA==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-sub": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sub/-/markdown-it-sub-2.0.0.tgz",
+ "integrity": "sha512-iCBKgwCkfQBRg2vApy9vx1C1Tu6D8XYo8NvevI3OlwzBRmiMtsJ2sXupBgEA7PPxiDwNni3qIUkhZ6j5wofDUA==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-sup": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/markdown-it-sup/-/markdown-it-sup-2.0.0.tgz",
+ "integrity": "sha512-5VgmdKlkBd8sgXuoDoxMpiU+BiEt3I49GItBzzw7Mxq9CxvnhE/k09HFli09zgfFDRixDQDfDxi0mgBCXtaTvA==",
+ "license": "MIT"
+ },
+ "node_modules/markdown-it-task-lists": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/markdown-it-task-lists/-/markdown-it-task-lists-2.1.1.tgz",
+ "integrity": "sha512-TxFAc76Jnhb2OUu+n3yz9RMu4CwGfaT788br6HhEDlvWfdeJcLUsxk1Hgw2yJio0OXsxv7pyIPmvECY7bMbluA==",
+ "license": "ISC"
+ },
+ "node_modules/markdown-it/node_modules/entities": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
+ "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/marked": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/marked/-/marked-14.0.0.tgz",
+ "integrity": "sha512-uIj4+faQ+MgHgwUW1l2PsPglZLOLOT1uErt06dAPtx2kjteLAkbsd/0FiYg/MGS+i7ZKLb7w2WClxHkzOOuryQ==",
+ "license": "MIT",
+ "bin": {
+ "marked": "bin/marked.js"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mdurl": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
+ "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
+ "license": "MIT"
+ },
+ "node_modules/media-typer": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
+ "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/merge-descriptors": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
+ "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/methods": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
+ "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/micromatch/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/mime": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
+ "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.54.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
+ "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types/node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "10.2.4",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz",
+ "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "brace-expansion": "^5.0.2"
+ },
+ "engines": {
+ "node": "18 || 20 || >=22"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
+ "node_modules/mlly": {
+ "version": "1.8.0",
+ "resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
+ "integrity": "sha512-l8D9ODSRWLe2KHJSifWGwBqpTZXIXTeo8mlKjY+E2HAakaTeNpqAyBZ8GSqLzHgw4XmHmC8whvpjJNMbFZN7/g==",
+ "license": "MIT",
+ "dependencies": {
+ "acorn": "^8.15.0",
+ "pathe": "^2.0.3",
+ "pkg-types": "^1.3.1",
+ "ufo": "^1.6.1"
+ }
+ },
+ "node_modules/monaco-editor": {
+ "version": "0.55.1",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
+ "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
+ "license": "MIT",
+ "dependencies": {
+ "dompurify": "3.2.7",
+ "marked": "14.0.0"
+ }
+ },
+ "node_modules/mrmime": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz",
+ "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/muggle-string": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/muggle-string/-/muggle-string-0.4.1.tgz",
+ "integrity": "sha512-VNTrAak/KhO2i8dqqnqnAHOa3cYBwXEZe9h+D5h/1ZqFSTEFHdM65lR7RoIqq3tBBYavsOXV84NoHXZ0AkPyqQ==",
+ "license": "MIT"
+ },
+ "node_modules/mute-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-1.0.0.tgz",
+ "integrity": "sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/natural-compare": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/negotiator": {
+ "version": "0.6.4",
+ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz",
+ "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/no-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz",
+ "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lower-case": "^2.0.2",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/node-addon-api": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz",
+ "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/node-forge": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.3.tgz",
+ "integrity": "sha512-rLvcdSyRCyouf6jcOIPe/BgwG/d7hKjzMKOas33/pHEr6gbq18IK9zV7DiPvzsz0oBJPme6qr6H6kGZuI9/DZg==",
+ "dev": true,
+ "license": "(BSD-3-Clause OR GPL-2.0)",
+ "engines": {
+ "node": ">= 6.13.0"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.27",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
+ "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/npm-check-updates": {
+ "version": "19.6.3",
+ "resolved": "https://registry.npmjs.org/npm-check-updates/-/npm-check-updates-19.6.3.tgz",
+ "integrity": "sha512-VAt9Bp26eLaymZ0nZyh5n/by+YZIuegXlvWR0yv1zBqd984f8VnEnBbn+1lS3nN5LyEjn62BJ+yYgzNSpb6Gzg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "ncu": "build/cli.js",
+ "npm-check-updates": "build/cli.js"
+ },
+ "engines": {
+ "node": ">=20.0.0",
+ "npm": ">=8.12.1"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-inspect": {
+ "version": "1.13.4",
+ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
+ "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/ohash": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
+ "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/on-finished": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
+ "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ee-first": "1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/on-headers": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
+ "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/open": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
+ "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "default-browser": "^5.2.1",
+ "define-lazy-prop": "^3.0.0",
+ "is-inside-container": "^1.0.0",
+ "wsl-utils": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/optionator": {
+ "version": "0.9.4",
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
+ "dev": true,
+ "license": "MIT",
+ "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.5"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/ora": {
+ "version": "5.4.1",
+ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz",
+ "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/orderedmap": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
+ "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
+ "license": "MIT"
+ },
+ "node_modules/p-limit": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "yocto-queue": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/pako": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pako/-/pako-2.1.0.tgz",
+ "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==",
+ "license": "(MIT AND Zlib)"
+ },
+ "node_modules/param-case": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz",
+ "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dot-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/parent-module": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+ "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "callsites": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/parseurl": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
+ "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/pascal-case": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz",
+ "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "no-case": "^3.0.4",
+ "tslib": "^2.0.3"
+ }
+ },
+ "node_modules/path-exists": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+ "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-to-regexp": {
+ "version": "0.1.12",
+ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
+ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/pathe": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz",
+ "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==",
+ "license": "MIT"
+ },
+ "node_modules/perfect-debounce": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz",
+ "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==",
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pinia": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.4.tgz",
+ "integrity": "sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@vue/devtools-api": "^7.7.7"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.5.0",
+ "vue": "^3.5.11"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/pinia/node_modules/@vue/devtools-api": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.9.tgz",
+ "integrity": "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^7.7.9"
+ }
+ },
+ "node_modules/pinia/node_modules/@vue/devtools-kit": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.9.tgz",
+ "integrity": "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-shared": "^7.7.9",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
+ }
+ },
+ "node_modules/pinia/node_modules/@vue/devtools-shared": {
+ "version": "7.7.9",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.9.tgz",
+ "integrity": "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==",
+ "license": "MIT",
+ "dependencies": {
+ "rfdc": "^1.4.1"
+ }
+ },
+ "node_modules/pinia/node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
+ "node_modules/pkg-types": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.3.1.tgz",
+ "integrity": "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==",
+ "license": "MIT",
+ "dependencies": {
+ "confbox": "^0.1.8",
+ "mlly": "^1.7.4",
+ "pathe": "^2.0.1"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.6",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz",
+ "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/prelude-ls": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/process": {
+ "version": "0.11.10",
+ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
+ "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/promise": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
+ "integrity": "sha512-nolQXZ/4L+bP/UGlkfaIujX9BKxGwmQ9OT4mOt5yvy8iK1h3wqTEJCijzGANTCCl9nWjY41juyAn2K3Q1hLLTg==",
+ "license": "MIT",
+ "dependencies": {
+ "asap": "~2.0.3"
+ }
+ },
+ "node_modules/prosemirror-commands": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
+ "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.10.2"
+ }
+ },
+ "node_modules/prosemirror-history": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz",
+ "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.2.2",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.31.0",
+ "rope-sequence": "^1.3.0"
+ }
+ },
+ "node_modules/prosemirror-keymap": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
+ "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-state": "^1.0.0",
+ "w3c-keyname": "^2.2.0"
+ }
+ },
+ "node_modules/prosemirror-model": {
+ "version": "1.25.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz",
+ "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==",
+ "license": "MIT",
+ "dependencies": {
+ "orderedmap": "^2.0.0"
+ }
+ },
+ "node_modules/prosemirror-schema-list": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
+ "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.7.3"
+ }
+ },
+ "node_modules/prosemirror-state": {
+ "version": "1.4.4",
+ "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz",
+ "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.0.0",
+ "prosemirror-transform": "^1.0.0",
+ "prosemirror-view": "^1.27.0"
+ }
+ },
+ "node_modules/prosemirror-transform": {
+ "version": "1.11.0",
+ "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.11.0.tgz",
+ "integrity": "sha512-4I7Ce4KpygXb9bkiPS3hTEk4dSHorfRw8uI0pE8IhxlK2GXsqv5tIA7JUSxtSu7u8APVOTtbUBxTmnHIxVkIJw==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.21.0"
+ }
+ },
+ "node_modules/prosemirror-view": {
+ "version": "1.41.6",
+ "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.6.tgz",
+ "integrity": "sha512-mxpcDG4hNQa/CPtzxjdlir5bJFDlm0/x5nGBbStB2BWX+XOQ9M8ekEG+ojqB5BcVu2Rc80/jssCMZzSstJuSYg==",
+ "license": "MIT",
+ "dependencies": {
+ "prosemirror-model": "^1.20.0",
+ "prosemirror-state": "^1.0.0",
+ "prosemirror-transform": "^1.1.0"
+ }
+ },
+ "node_modules/proxy-addr": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "forwarded": "0.2.0",
+ "ipaddr.js": "1.9.1"
+ },
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/pug": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pug/-/pug-3.0.3.tgz",
+ "integrity": "sha512-uBi6kmc9f3SZ3PXxqcHiUZLmIXgfgWooKWXcwSGwQd2Zi5Rb0bT14+8CJjJgI8AB+nndLaNgHGrcc6bPIB665g==",
+ "license": "MIT",
+ "dependencies": {
+ "pug-code-gen": "^3.0.3",
+ "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"
+ }
+ },
+ "node_modules/pug-attrs": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pug-attrs/-/pug-attrs-3.0.0.tgz",
+ "integrity": "sha512-azINV9dUtzPMFQktvTXciNAfAuVh/L/JCl0vtPCwvOA21uZrC08K/UnmrL+SXGEVc1FwzjW62+xw5S/uaLj6cA==",
+ "license": "MIT",
+ "dependencies": {
+ "constantinople": "^4.0.1",
+ "js-stringify": "^1.0.2",
+ "pug-runtime": "^3.0.0"
+ }
+ },
+ "node_modules/pug-code-gen": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/pug-code-gen/-/pug-code-gen-3.0.3.tgz",
+ "integrity": "sha512-cYQg0JW0w32Ux+XTeZnBEeuWrAY7/HNE6TWnhiHGnnRYlCgyAUPoyh9KzCMa9WhcJlJ1AtQqpEYHc+vbCzA+Aw==",
+ "license": "MIT",
+ "dependencies": {
+ "constantinople": "^4.0.1",
+ "doctypes": "^1.1.0",
+ "js-stringify": "^1.0.2",
+ "pug-attrs": "^3.0.0",
+ "pug-error": "^2.1.0",
+ "pug-runtime": "^3.0.1",
+ "void-elements": "^3.1.0",
+ "with": "^7.0.0"
+ }
+ },
+ "node_modules/pug-error": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/pug-error/-/pug-error-2.1.0.tgz",
+ "integrity": "sha512-lv7sU9e5Jk8IeUheHata6/UThZ7RK2jnaaNztxfPYUY+VxZyk/ePVaNZ/vwmH8WqGvDz3LrNYt/+gA55NDg6Pg==",
+ "license": "MIT"
+ },
+ "node_modules/pug-filters": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pug-filters/-/pug-filters-4.0.0.tgz",
+ "integrity": "sha512-yeNFtq5Yxmfz0f9z2rMXGw/8/4i1cCFecw/Q7+D0V2DdtII5UvqE12VaZ2AY7ri6o5RNXiweGH79OCq+2RQU4A==",
+ "license": "MIT",
+ "dependencies": {
+ "constantinople": "^4.0.1",
+ "jstransformer": "1.0.0",
+ "pug-error": "^2.0.0",
+ "pug-walk": "^2.0.0",
+ "resolve": "^1.15.1"
+ }
+ },
+ "node_modules/pug-lexer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/pug-lexer/-/pug-lexer-5.0.1.tgz",
+ "integrity": "sha512-0I6C62+keXlZPZkOJeVam9aBLVP2EnbeDw3An+k0/QlqdwH6rv8284nko14Na7c0TtqtogfWXcRoFE4O4Ff20w==",
+ "license": "MIT",
+ "dependencies": {
+ "character-parser": "^2.2.0",
+ "is-expression": "^4.0.0",
+ "pug-error": "^2.0.0"
+ }
+ },
+ "node_modules/pug-linker": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/pug-linker/-/pug-linker-4.0.0.tgz",
+ "integrity": "sha512-gjD1yzp0yxbQqnzBAdlhbgoJL5qIFJw78juN1NpTLt/mfPJ5VgC4BvkoD3G23qKzJtIIXBbcCt6FioLSFLOHdw==",
+ "license": "MIT",
+ "dependencies": {
+ "pug-error": "^2.0.0",
+ "pug-walk": "^2.0.0"
+ }
+ },
+ "node_modules/pug-load": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/pug-load/-/pug-load-3.0.0.tgz",
+ "integrity": "sha512-OCjTEnhLWZBvS4zni/WUMjH2YSUosnsmjGBB1An7CsKQarYSWQ0GCVyd4eQPMFJqZ8w9xgs01QdiZXKVjk92EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "object-assign": "^4.1.1",
+ "pug-walk": "^2.0.0"
+ }
+ },
+ "node_modules/pug-parser": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/pug-parser/-/pug-parser-6.0.0.tgz",
+ "integrity": "sha512-ukiYM/9cH6Cml+AOl5kETtM9NR3WulyVP2y4HOU45DyMim1IeP/OOiyEWRr6qk5I5klpsBnbuHpwKmTx6WURnw==",
+ "license": "MIT",
+ "dependencies": {
+ "pug-error": "^2.0.0",
+ "token-stream": "1.0.0"
+ }
+ },
+ "node_modules/pug-runtime": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/pug-runtime/-/pug-runtime-3.0.1.tgz",
+ "integrity": "sha512-L50zbvrQ35TkpHwv0G6aLSuueDRwc/97XdY8kL3tOT0FmhgG7UypU3VztfV/LATAvmUfYi4wNxSajhSAeNN+Kg==",
+ "license": "MIT"
+ },
+ "node_modules/pug-strip-comments": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pug-strip-comments/-/pug-strip-comments-2.0.0.tgz",
+ "integrity": "sha512-zo8DsDpH7eTkPHCXFeAk1xZXJbyoTfdPlNR0bK7rpOMuhBYb0f5qUVCO1xlsitYd3w5FQTK7zpNVKb3rZoUrrQ==",
+ "license": "MIT",
+ "dependencies": {
+ "pug-error": "^2.0.0"
+ }
+ },
+ "node_modules/pug-walk": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/pug-walk/-/pug-walk-2.0.0.tgz",
+ "integrity": "sha512-yYELe9Q5q9IQhuvqsZNwA5hfPkMJ8u92bQLIMcsMxf/VADjNtEYptU+inlufAFYcWdHlwNfZOEnOOQrZrcyJCQ==",
+ "license": "MIT"
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/punycode.js": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
+ "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/qs": {
+ "version": "6.14.2",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz",
+ "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "side-channel": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/quansync": {
+ "version": "0.2.11",
+ "resolved": "https://registry.npmjs.org/quansync/-/quansync-0.2.11.tgz",
+ "integrity": "sha512-AifT7QEbW9Nri4tAwR5M/uzpBuqfZf+zwaEM/QkzEjj7NBuFD2rBuy0K3dE+8wltbezDV7JMA0WfnCPYRSYbXA==",
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/antfu"
+ },
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/quasar": {
+ "version": "2.18.6",
+ "resolved": "https://registry.npmjs.org/quasar/-/quasar-2.18.6.tgz",
+ "integrity": "sha512-ZlK+vJXOBPSFDCNQDBDNwSI+AHoqaFPxK8ve6mhsYLhMKWI5b8zsGY9VU1xYjngO2aBvU4fvGWXy4tTbzrBk8Q==",
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 10.18.1",
+ "npm": ">= 6.13.4",
+ "yarn": ">= 1.21.1"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://donate.quasar.dev"
+ }
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/randombytes": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+ "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "^5.1.0"
+ }
+ },
+ "node_modules/range-parser": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
+ "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/raw-body": {
+ "version": "2.5.3",
+ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
+ "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bytes": "~3.1.2",
+ "http-errors": "~2.0.1",
+ "iconv-lite": "~0.4.24",
+ "unpipe": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "4.7.0",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
+ "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "abort-controller": "^3.0.0",
+ "buffer": "^6.0.3",
+ "events": "^3.3.0",
+ "process": "^0.11.10",
+ "string_decoder": "^1.3.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
+ "node_modules/readable-stream/node_modules/buffer": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
+ "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.2.1"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/readdir-glob/node_modules/brace-expansion": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/readdir-glob/node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/readdirp/node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/relateurl": {
+ "version": "0.2.7",
+ "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz",
+ "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.10"
+ }
+ },
+ "node_modules/remove-accents": {
+ "version": "0.5.0",
+ "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz",
+ "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==",
+ "license": "MIT"
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.11",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
+ "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.16.1",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/resolve-from": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+ "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/restore-cursor": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
+ "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "onetime": "^5.1.0",
+ "signal-exit": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/restore-cursor/node_modules/signal-exit": {
+ "version": "3.0.7",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz",
+ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/reusify": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
+ "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rfdc": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
+ "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
+ "license": "MIT"
+ },
+ "node_modules/rollup": {
+ "version": "4.59.0",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.59.0.tgz",
+ "integrity": "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@types/estree": "1.0.8"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.59.0",
+ "@rollup/rollup-android-arm64": "4.59.0",
+ "@rollup/rollup-darwin-arm64": "4.59.0",
+ "@rollup/rollup-darwin-x64": "4.59.0",
+ "@rollup/rollup-freebsd-arm64": "4.59.0",
+ "@rollup/rollup-freebsd-x64": "4.59.0",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.59.0",
+ "@rollup/rollup-linux-arm-musleabihf": "4.59.0",
+ "@rollup/rollup-linux-arm64-gnu": "4.59.0",
+ "@rollup/rollup-linux-arm64-musl": "4.59.0",
+ "@rollup/rollup-linux-loong64-gnu": "4.59.0",
+ "@rollup/rollup-linux-loong64-musl": "4.59.0",
+ "@rollup/rollup-linux-ppc64-gnu": "4.59.0",
+ "@rollup/rollup-linux-ppc64-musl": "4.59.0",
+ "@rollup/rollup-linux-riscv64-gnu": "4.59.0",
+ "@rollup/rollup-linux-riscv64-musl": "4.59.0",
+ "@rollup/rollup-linux-s390x-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-gnu": "4.59.0",
+ "@rollup/rollup-linux-x64-musl": "4.59.0",
+ "@rollup/rollup-openbsd-x64": "4.59.0",
+ "@rollup/rollup-openharmony-arm64": "4.59.0",
+ "@rollup/rollup-win32-arm64-msvc": "4.59.0",
+ "@rollup/rollup-win32-ia32-msvc": "4.59.0",
+ "@rollup/rollup-win32-x64-gnu": "4.59.0",
+ "@rollup/rollup-win32-x64-msvc": "4.59.0",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/rollup-plugin-visualizer": {
+ "version": "5.14.0",
+ "resolved": "https://registry.npmjs.org/rollup-plugin-visualizer/-/rollup-plugin-visualizer-5.14.0.tgz",
+ "integrity": "sha512-VlDXneTDaKsHIw8yzJAFWtrzguoJ/LnQ+lMpoVfYJ3jJF4Ihe5oYLAqLklIK/35lgUY+1yEzCkHyZ1j4A5w5fA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "open": "^8.4.0",
+ "picomatch": "^4.0.2",
+ "source-map": "^0.7.4",
+ "yargs": "^17.5.1"
+ },
+ "bin": {
+ "rollup-plugin-visualizer": "dist/bin/cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "rolldown": "1.x",
+ "rollup": "2.x || 3.x || 4.x"
+ },
+ "peerDependenciesMeta": {
+ "rolldown": {
+ "optional": true
+ },
+ "rollup": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/define-lazy-prop": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz",
+ "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "is-docker": "cli.js"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-docker": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/open": {
+ "version": "8.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz",
+ "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-lazy-prop": "^2.0.0",
+ "is-docker": "^2.1.1",
+ "is-wsl": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rollup-plugin-visualizer/node_modules/source-map": {
+ "version": "0.7.6",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz",
+ "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 12"
+ }
+ },
+ "node_modules/rope-sequence": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
+ "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
+ "license": "MIT"
+ },
+ "node_modules/run-applescript": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
+ "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/run-async": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/run-async/-/run-async-3.0.0.tgz",
+ "integrity": "sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sass": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.3.tgz",
+ "integrity": "sha512-fDz1zJpd5GycprAbu4Q2PV/RprsRtKC/0z82z0JLgdytmcq0+ujJbJ/09bPGDxCLkKY3Np5cRAOcWiVkLXJURg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chokidar": "^4.0.0",
+ "immutable": "^5.0.2",
+ "source-map-js": ">=0.6.2 <2.0.0"
+ },
+ "bin": {
+ "sass": "sass.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "optionalDependencies": {
+ "@parcel/watcher": "^2.4.1"
+ }
+ },
+ "node_modules/sass-embedded": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.97.3.tgz",
+ "integrity": "sha512-eKzFy13Nk+IRHhlAwP3sfuv+PzOrvzUkwJK2hdoCKYcWGSdmwFpeGpWmyewdw8EgBnsKaSBtgf/0b2K635ecSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@bufbuild/protobuf": "^2.5.0",
+ "colorjs.io": "^0.5.0",
+ "immutable": "^5.0.2",
+ "rxjs": "^7.4.0",
+ "supports-color": "^8.1.1",
+ "sync-child-process": "^1.0.2",
+ "varint": "^6.0.0"
+ },
+ "bin": {
+ "sass": "dist/bin/sass.js"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ },
+ "optionalDependencies": {
+ "sass-embedded-all-unknown": "1.97.3",
+ "sass-embedded-android-arm": "1.97.3",
+ "sass-embedded-android-arm64": "1.97.3",
+ "sass-embedded-android-riscv64": "1.97.3",
+ "sass-embedded-android-x64": "1.97.3",
+ "sass-embedded-darwin-arm64": "1.97.3",
+ "sass-embedded-darwin-x64": "1.97.3",
+ "sass-embedded-linux-arm": "1.97.3",
+ "sass-embedded-linux-arm64": "1.97.3",
+ "sass-embedded-linux-musl-arm": "1.97.3",
+ "sass-embedded-linux-musl-arm64": "1.97.3",
+ "sass-embedded-linux-musl-riscv64": "1.97.3",
+ "sass-embedded-linux-musl-x64": "1.97.3",
+ "sass-embedded-linux-riscv64": "1.97.3",
+ "sass-embedded-linux-x64": "1.97.3",
+ "sass-embedded-unknown-all": "1.97.3",
+ "sass-embedded-win32-arm64": "1.97.3",
+ "sass-embedded-win32-x64": "1.97.3"
+ }
+ },
+ "node_modules/sass-embedded-all-unknown": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-all-unknown/-/sass-embedded-all-unknown-1.97.3.tgz",
+ "integrity": "sha512-t6N46NlPuXiY3rlmG6/+1nwebOBOaLFOOVqNQOC2cJhghOD4hh2kHNQQTorCsbY9S1Kir2la1/XLBwOJfui0xg==",
+ "cpu": [
+ "!arm",
+ "!arm64",
+ "!riscv64",
+ "!x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "sass": "1.97.3"
+ }
+ },
+ "node_modules/sass-embedded-android-arm": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.97.3.tgz",
+ "integrity": "sha512-cRTtf/KV/q0nzGZoUzVkeIVVFv3L/tS1w4WnlHapphsjTXF/duTxI8JOU1c/9GhRPiMdfeXH7vYNcMmtjwX7jg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-arm64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.97.3.tgz",
+ "integrity": "sha512-aiZ6iqiHsUsaDx0EFbbmmA0QgxicSxVVN3lnJJ0f1RStY0DthUkquGT5RJ4TPdaZ6ebeJWkboV4bra+CP766eA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-riscv64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.97.3.tgz",
+ "integrity": "sha512-zVEDgl9JJodofGHobaM/q6pNETG69uuBIGQHRo789jloESxxZe82lI3AWJQuPmYCOG5ElfRthqgv89h3gTeLYA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-android-x64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.97.3.tgz",
+ "integrity": "sha512-3ke0le7ZKepyXn/dKKspYkpBC0zUk/BMciyP5ajQUDy4qJwobd8zXdAq6kOkdiMB+d9UFJOmEkvgFJHl3lqwcw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-darwin-arm64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.97.3.tgz",
+ "integrity": "sha512-fuqMTqO4gbOmA/kC5b9y9xxNYw6zDEyfOtMgabS7Mz93wimSk2M1quQaTJnL98Mkcsl2j+7shNHxIS/qpcIDDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-darwin-x64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.97.3.tgz",
+ "integrity": "sha512-b/2RBs/2bZpP8lMkyZ0Px0vkVkT8uBd0YXpOwK7iOwYkAT8SsO4+WdVwErsqC65vI5e1e5p1bb20tuwsoQBMVA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.97.3.tgz",
+ "integrity": "sha512-2lPQ7HQQg4CKsH18FTsj2hbw5GJa6sBQgDsls+cV7buXlHjqF8iTKhAQViT6nrpLK/e8nFCoaRgSqEC8xMnXuA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.97.3.tgz",
+ "integrity": "sha512-IP1+2otCT3DuV46ooxPaOKV1oL5rLjteRzf8ldZtfIEcwhSgSsHgA71CbjYgLEwMY9h4jeal8Jfv3QnedPvSjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.97.3.tgz",
+ "integrity": "sha512-cBTMU68X2opBpoYsSZnI321gnoaiMBEtc+60CKCclN6PCL3W3uXm8g4TLoil1hDD6mqU9YYNlVG6sJ+ZNef6Lg==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.97.3.tgz",
+ "integrity": "sha512-Lij0SdZCsr+mNRSyDZ7XtJpXEITrYsaGbOTz5e6uFLJ9bmzUbV7M8BXz2/cA7bhfpRPT7/lwRKPdV4+aR9Ozcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-riscv64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.97.3.tgz",
+ "integrity": "sha512-sBeLFIzMGshR4WmHAD4oIM7WJVkSoCIEwutzptFtGlSlwfNiijULp+J5hA2KteGvI6Gji35apR5aWj66wEn/iA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-x64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.97.3.tgz",
+ "integrity": "sha512-/oWJ+OVrDg7ADDQxRLC/4g1+Nsz1g4mkYS2t6XmyMJKFTFK50FVI2t5sOdFH+zmMp+nXHKM036W94y9m4jjEcw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-riscv64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.97.3.tgz",
+ "integrity": "sha512-l3IfySApLVYdNx0Kjm7Zehte1CDPZVcldma3dZt+TfzvlAEerM6YDgsk5XEj3L8eHBCgHgF4A0MJspHEo2WNfA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-x64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.97.3.tgz",
+ "integrity": "sha512-Kwqwc/jSSlcpRjULAOVbndqEy2GBzo6OBmmuBVINWUaJLJ8Kczz3vIsDUWLfWz/kTEw9FHBSiL0WCtYLVAXSLg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-unknown-all": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-unknown-all/-/sass-embedded-unknown-all-1.97.3.tgz",
+ "integrity": "sha512-/GHajyYJmvb0IABUQHbVHf1nuHPtIDo/ClMZ81IDr59wT5CNcMe7/dMNujXwWugtQVGI5UGmqXWZQCeoGnct8Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "!android",
+ "!darwin",
+ "!linux",
+ "!win32"
+ ],
+ "dependencies": {
+ "sass": "1.97.3"
+ }
+ },
+ "node_modules/sass-embedded-win32-arm64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.97.3.tgz",
+ "integrity": "sha512-RDGtRS1GVvQfMGAmVXNxYiUOvPzn9oO1zYB/XUM9fudDRnieYTcUytpNTQZLs6Y1KfJxgt5Y+giRceC92fT8Uw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-x64": {
+ "version": "1.97.3",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.97.3.tgz",
+ "integrity": "sha512-SFRa2lED9UEwV6vIGeBXeBOLKF+rowF3WmNfb/BzhxmdAsKofCXrJ8ePW7OcDVrvNEbTOGwhsReIsF5sH8fVaw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/sass/node_modules/chokidar": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
+ "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 14.16.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/sass/node_modules/readdirp": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz",
+ "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.18.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.1.4.tgz",
+ "integrity": "sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/scule": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/scule/-/scule-1.3.0.tgz",
+ "integrity": "sha512-6FtHJEvt+pVMIB9IBY+IcCJ6Z5f1iQnytgyfKMhDKgmzYG+TeH/wx1y3l27rshSbLiSanrR9ffZDrEsmjlQF2g==",
+ "license": "MIT"
+ },
+ "node_modules/select": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/select/-/select-1.1.2.tgz",
+ "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==",
+ "license": "MIT"
+ },
+ "node_modules/selfsigned": {
+ "version": "2.4.1",
+ "resolved": "https://registry.npmjs.org/selfsigned/-/selfsigned-2.4.1.tgz",
+ "integrity": "sha512-th5B4L2U+eGLq1TVh7zNRGBapioSORUeymIydxgFpwww9d2qyKvtuPU2jJuHvYAwwqi2Y596QBL3eEqcPEYL8Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node-forge": "^1.3.0",
+ "node-forge": "^1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/send": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
+ "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "2.6.9",
+ "depd": "2.0.0",
+ "destroy": "1.2.0",
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "etag": "~1.8.1",
+ "fresh": "~0.5.2",
+ "http-errors": "~2.0.1",
+ "mime": "1.6.0",
+ "ms": "2.1.3",
+ "on-finished": "~2.4.1",
+ "range-parser": "~1.2.1",
+ "statuses": "~2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/send/node_modules/debug": {
+ "version": "2.6.9",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+ "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "2.0.0"
+ }
+ },
+ "node_modules/send/node_modules/debug/node_modules/ms": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/serialize-javascript": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz",
+ "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "randombytes": "^2.1.0"
+ }
+ },
+ "node_modules/serve-static": {
+ "version": "1.16.3",
+ "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
+ "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "encodeurl": "~2.0.0",
+ "escape-html": "~1.0.3",
+ "parseurl": "~1.3.3",
+ "send": "~0.19.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/setprototypeof": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
+ "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/shallow-clone": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz",
+ "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "kind-of": "^6.0.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/side-channel": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
+ "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3",
+ "side-channel-list": "^1.0.0",
+ "side-channel-map": "^1.0.1",
+ "side-channel-weakmap": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-list": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
+ "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-map": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
+ "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/side-channel-weakmap": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
+ "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bound": "^1.0.2",
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.5",
+ "object-inspect": "^1.13.3",
+ "side-channel-map": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/sirv": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz",
+ "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@polka/url": "^1.0.0-next.24",
+ "mrmime": "^2.0.0",
+ "totalist": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/slugify": {
+ "version": "1.6.6",
+ "resolved": "https://registry.npmjs.org/slugify/-/slugify-1.6.6.tgz",
+ "integrity": "sha512-h+z7HKHYXj6wJU+AnS/+IH8Uh9fdcX1Lrhg1/VMdf9PwoBQXFcXiAdsy2tSK0P6gKwJLXp02r90ahUCqHk9rrw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0.0"
+ }
+ },
+ "node_modules/socket.io-client": {
+ "version": "4.8.3",
+ "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.8.3.tgz",
+ "integrity": "sha512-uP0bpjWrjQmUt5DTHq9RuoCBdFJF10cdX9X+a368j/Ft0wmaVgxlrjvK3kjvgCODOMMOz9lcaRzxmso0bTWZ/g==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1",
+ "engine.io-client": "~6.6.1",
+ "socket.io-parser": "~4.2.4"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/socket.io-parser": {
+ "version": "4.2.5",
+ "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.5.tgz",
+ "integrity": "sha512-bPMmpy/5WWKHea5Y/jYAP6k74A+hvmRCQaJuJB6I/ML5JZq/KfNieUVo/3Mh7SAqn7TyFdIo6wqYHInG1MU1bQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@socket.io/component-emitter": "~3.1.0",
+ "debug": "~4.4.1"
+ },
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/sortablejs": {
+ "version": "1.15.7",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.15.7.tgz",
+ "integrity": "sha512-Kk8wLQPlS+yi1ZEf48a4+fzHa4yxjC30M/Sr2AnQu+f/MPwvvX9XjZ6OWejiz8crBsLwSq8GHqaxaET7u6ux0A==",
+ "license": "MIT"
+ },
+ "node_modules/sortablejs-vue3": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/sortablejs-vue3/-/sortablejs-vue3-1.3.0.tgz",
+ "integrity": "sha512-71vrhfinoYEOWfpbFJUgYJNXJwMhqGgk4BLustx3WNWR1SoPgd+ZD8fZABacvDJV2e2CZo5RJtfGdnsiZL1blQ==",
+ "license": "MIT",
+ "dependencies": {
+ "sortablejs": "^1.15.0",
+ "vue": "^3.3.7"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://github.com/sponsors/MaxLeiter/"
+ },
+ "peerDependencies": {
+ "sortablejs": "^1.15.0",
+ "vue": "^3.2.25"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/stack-trace": {
+ "version": "1.0.0-pre2",
+ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-1.0.0-pre2.tgz",
+ "integrity": "sha512-2ztBJRek8IVofG9DBJqdy2N5kulaacX30Nz7xmkYF6ale9WBVmIy6mFBchvGX7Vx/MyjBhx+Rcxqrj+dbOnQ6A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/statuses": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
+ "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/streamx": {
+ "version": "2.23.0",
+ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz",
+ "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "events-universal": "^1.0.0",
+ "fast-fifo": "^1.3.2",
+ "text-decoder": "^1.1.0"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-json-comments": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+ "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/superjson": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.6.tgz",
+ "integrity": "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^4"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/sync-child-process": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
+ "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sync-message-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/sync-message-port": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.2.0.tgz",
+ "integrity": "sha512-gAQ9qrUN/UCypHtGFbbe7Rc/f9bzO88IwrG8TDo/aMKAApKyD6E3W4Cm0EfhfBb6Z6SKt59tTCTfD+n1xmAvMg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/tabulator-tables": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/tabulator-tables/-/tabulator-tables-6.3.1.tgz",
+ "integrity": "sha512-qFW7kfadtcaISQIibKAIy0f3eeIXUVi8242Vly1iJfMD79kfEGzfczNuPBN/80hDxHzQJXYbmJ8VipI40hQtfA==",
+ "license": "MIT"
+ },
+ "node_modules/tar-stream": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz",
+ "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "b4a": "^1.6.4",
+ "fast-fifo": "^1.2.0",
+ "streamx": "^2.15.0"
+ }
+ },
+ "node_modules/terser": {
+ "version": "5.46.0",
+ "resolved": "https://registry.npmjs.org/terser/-/terser-5.46.0.tgz",
+ "integrity": "sha512-jTwoImyr/QbOWFFso3YoU3ik0jBBDJ6JTOQiy/J2YxVJdZCc+5u7skhNwiOR3FQIygFqVUPHl7qbbxtjW2K3Qg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "@jridgewell/source-map": "^0.3.3",
+ "acorn": "^8.15.0",
+ "commander": "^2.20.0",
+ "source-map-support": "~0.5.20"
+ },
+ "bin": {
+ "terser": "bin/terser"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/terser/node_modules/commander": {
+ "version": "2.20.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
+ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/text-decoder": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz",
+ "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "b4a": "^1.6.4"
+ }
+ },
+ "node_modules/tiny-emitter": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+ "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+ "license": "MIT"
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/tippy.js": {
+ "version": "6.3.7",
+ "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
+ "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@popperjs/core": "^2.9.0"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/toidentifier": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
+ "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.6"
+ }
+ },
+ "node_modules/token-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/token-stream/-/token-stream-1.0.0.tgz",
+ "integrity": "sha512-VSsyNPPW74RpHwR8Fc21uubwHY7wMDeJLys2IX5zJNih+OnAnaifKHo+1LHT7DAdloQ7apeaaWg8l7qnf/TnEg==",
+ "license": "MIT"
+ },
+ "node_modules/totalist": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz",
+ "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/ts-api-utils": {
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18.12"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4"
+ }
+ },
+ "node_modules/ts-essentials": {
+ "version": "9.4.2",
+ "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-9.4.2.tgz",
+ "integrity": "sha512-mB/cDhOvD7pg3YCLk2rOtejHjjdSi9in/IBYE13S+8WA5FBSraYf4V/ws55uvs0IvQ/l0wBOlXy5yBNZ9Bl8ZQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "typescript": ">=4.1.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "dev": true,
+ "license": "0BSD"
+ },
+ "node_modules/twemoji": {
+ "version": "14.0.2",
+ "resolved": "https://registry.npmjs.org/twemoji/-/twemoji-14.0.2.tgz",
+ "integrity": "sha512-BzOoXIe1QVdmsUmZ54xbEH+8AgtOKUiG53zO5vVP2iUu6h5u9lN15NcuS6te4OY96qx0H7JK9vjjl9WQbkTRuA==",
+ "license": "MIT",
+ "dependencies": {
+ "fs-extra": "^8.0.1",
+ "jsonfile": "^5.0.0",
+ "twemoji-parser": "14.0.0",
+ "universalify": "^0.1.2"
+ }
+ },
+ "node_modules/twemoji-parser": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/twemoji-parser/-/twemoji-parser-14.0.0.tgz",
+ "integrity": "sha512-9DUOTGLOWs0pFWnh1p6NF+C3CkQ96PWmEFwhOVmT3WbecRC+68AIqpsnJXygfkFcp4aXbOp8Dwbhh/HQgvoRxA==",
+ "license": "MIT"
+ },
+ "node_modules/twemoji/node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/twemoji/node_modules/fs-extra/node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/twemoji/node_modules/jsonfile": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-5.0.0.tgz",
+ "integrity": "sha512-NQRZ5CRo74MhMMC3/3r5g2k4fjodJ/wh8MxjFbCViWKFjxrnudWSY5vomh+23ZaXzAS7J3fBZIR2dV6WbmfM0w==",
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^0.1.2"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/twemoji/node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/type-check": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+ "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/type-is": {
+ "version": "1.6.18",
+ "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
+ "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "media-typer": "0.3.0",
+ "mime-types": "~2.1.24"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/uc.micro": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz",
+ "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==",
+ "license": "MIT"
+ },
+ "node_modules/ufo": {
+ "version": "1.6.3",
+ "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
+ "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
+ "license": "MIT"
+ },
+ "node_modules/undici-types": {
+ "version": "7.18.2",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz",
+ "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/unpipe": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
+ "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/unplugin": {
+ "version": "2.3.11",
+ "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.11.tgz",
+ "integrity": "sha512-5uKD0nqiYVzlmCRs01Fhs2BdkEgBS3SAVP6ndrBsuK42iC2+JHyxM05Rm9G8+5mkmRtzMZGY8Ct5+mliZxU/Ww==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "acorn": "^8.15.0",
+ "picomatch": "^4.0.3",
+ "webpack-virtual-modules": "^0.6.2"
+ },
+ "engines": {
+ "node": ">=18.12.0"
+ }
+ },
+ "node_modules/unplugin-utils": {
+ "version": "0.3.1",
+ "resolved": "https://registry.npmjs.org/unplugin-utils/-/unplugin-utils-0.3.1.tgz",
+ "integrity": "sha512-5lWVjgi6vuHhJ526bI4nlCOmkCIF3nnfXkCMDeMJrtdvxTs6ZFCM8oNufGTsDbKv/tJ/xj8RpvXjRuPBZJuJog==",
+ "license": "MIT",
+ "dependencies": {
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=20.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sxzz"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/utils-merge": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
+ "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4.0"
+ }
+ },
+ "node_modules/uuid": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz",
+ "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist-node/bin/uuid"
+ }
+ },
+ "node_modules/v-network-graph": {
+ "version": "0.9.22",
+ "resolved": "https://registry.npmjs.org/v-network-graph/-/v-network-graph-0.9.22.tgz",
+ "integrity": "sha512-kgG3uiGF6DSA9DrjNEWmD6yy4/AP0jf/uhkq2yVkfub3mJavxcXU0ow2y5z5jFyFlA9L+uzzIZVTSY2iJLMv1w==",
+ "license": "MIT",
+ "dependencies": {
+ "@dash14/svg-pan-zoom": "^3.6.9",
+ "lodash-es": "^4.17.22",
+ "mitt": "^3.0.1"
+ },
+ "peerDependencies": {
+ "d3-force": "^3.0.0",
+ "vue": "^3.5.13"
+ },
+ "peerDependenciesMeta": {
+ "d3-force": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/varint": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+ "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vary": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
+ "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/vite": {
+ "version": "7.3.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz",
+ "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "esbuild": "^0.27.0",
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3",
+ "postcss": "^8.5.6",
+ "rollup": "^4.43.0",
+ "tinyglobby": "^0.2.15"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^20.19.0 || >=22.12.0",
+ "jiti": ">=1.21.0",
+ "less": "^4.0.0",
+ "lightningcss": "^1.21.0",
+ "sass": "^1.70.0",
+ "sass-embedded": "^1.70.0",
+ "stylus": ">=0.54.8",
+ "sugarss": "^5.0.0",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-dev-rpc": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/vite-dev-rpc/-/vite-dev-rpc-1.1.0.tgz",
+ "integrity": "sha512-pKXZlgoXGoE8sEKiKJSng4hI1sQ4wi5YT24FCrwrLt6opmkjlqPPVmiPWWJn8M8byMxRGzp1CrFuqQs4M/Z39A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "birpc": "^2.4.0",
+ "vite-hot-client": "^2.1.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vite": "^2.9.0 || ^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.1 || ^7.0.0-0"
+ }
+ },
+ "node_modules/vite-hot-client": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/vite-hot-client/-/vite-hot-client-2.1.0.tgz",
+ "integrity": "sha512-7SpgZmU7R+dDnSmvXE1mfDtnHLHQSisdySVR7lO8ceAXvM0otZeuQQ6C8LrS5d/aYyP/QZ0hI0L+dIPrm4YlFQ==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vite": "^2.6.0 || ^3.0.0 || ^4.0.0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-inspect": {
+ "version": "11.3.3",
+ "resolved": "https://registry.npmjs.org/vite-plugin-inspect/-/vite-plugin-inspect-11.3.3.tgz",
+ "integrity": "sha512-u2eV5La99oHoYPHE6UvbwgEqKKOQGz86wMg40CCosP6q8BkB6e5xPneZfYagK4ojPJSj5anHCrnvC20DpwVdRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansis": "^4.1.0",
+ "debug": "^4.4.1",
+ "error-stack-parser-es": "^1.0.5",
+ "ohash": "^2.0.11",
+ "open": "^10.2.0",
+ "perfect-debounce": "^2.0.0",
+ "sirv": "^3.0.1",
+ "unplugin-utils": "^0.3.0",
+ "vite-dev-rpc": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ },
+ "peerDependencies": {
+ "vite": "^6.0.0 || ^7.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@nuxt/kit": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vite-plugin-vue-devtools": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/vite-plugin-vue-devtools/-/vite-plugin-vue-devtools-8.0.6.tgz",
+ "integrity": "sha512-IiTCIJDb1ZliOT8fPbYXllyfgARzz1+R1r8RN9ScGIDzAB6o8bDME1a9JjrfdSJibL7i8DIPQH+pGv0U7haBeA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-core": "^8.0.6",
+ "@vue/devtools-kit": "^8.0.6",
+ "@vue/devtools-shared": "^8.0.6",
+ "sirv": "^3.0.2",
+ "vite-plugin-inspect": "^11.3.3",
+ "vite-plugin-vue-inspector": "^5.3.2"
+ },
+ "engines": {
+ "node": ">=v14.21.3"
+ },
+ "peerDependencies": {
+ "vite": "^6.0.0 || ^7.0.0-0"
+ }
+ },
+ "node_modules/vite-plugin-vue-inspector": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/vite-plugin-vue-inspector/-/vite-plugin-vue-inspector-5.3.2.tgz",
+ "integrity": "sha512-YvEKooQcSiBTAs0DoYLfefNja9bLgkFM7NI2b07bE2SruuvX0MEa9cMaxjKVMkeCp5Nz9FRIdcN1rOdFVBeL6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.23.0",
+ "@babel/plugin-proposal-decorators": "^7.23.0",
+ "@babel/plugin-syntax-import-attributes": "^7.22.5",
+ "@babel/plugin-syntax-import-meta": "^7.10.4",
+ "@babel/plugin-transform-typescript": "^7.22.15",
+ "@vue/babel-plugin-jsx": "^1.1.5",
+ "@vue/compiler-dom": "^3.3.4",
+ "kolorist": "^1.8.0",
+ "magic-string": "^0.30.4"
+ },
+ "peerDependencies": {
+ "vite": "^3.0.0-0 || ^4.0.0-0 || ^5.0.0-0 || ^6.0.0-0 || ^7.0.0-0"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/android-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/darwin-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/freebsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-loong64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-mips64el": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-riscv64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-s390x": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/linux-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/netbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openbsd-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/sunos-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-ia32": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/@esbuild/win32-x64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/vite/node_modules/esbuild": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
+ }
+ },
+ "node_modules/void-elements": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz",
+ "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/vscode-languageserver-textdocument": {
+ "version": "1.0.12",
+ "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz",
+ "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/vue": {
+ "version": "3.5.29",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.29.tgz",
+ "integrity": "sha512-BZqN4Ze6mDQVNAni0IHeMJ5mwr8VAJ3MQC9FmprRhcBYENw+wOAAjRj8jfmN6FLl0j96OXbR+CjWhmAmM+QGnA==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@vue/compiler-dom": "3.5.29",
+ "@vue/compiler-sfc": "3.5.29",
+ "@vue/runtime-dom": "3.5.29",
+ "@vue/server-renderer": "3.5.29",
+ "@vue/shared": "3.5.29"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-i18n": {
+ "version": "11.2.8",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.2.8.tgz",
+ "integrity": "sha512-vJ123v/PXCZntd6Qj5Jumy7UBmIuE92VrtdX+AXr+1WzdBHojiBxnAxdfctUFL+/JIN+VQH4BhsfTtiGsvVObg==",
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "@intlify/core-base": "11.2.8",
+ "@intlify/shared": "11.2.8",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/vue-router": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-5.0.3.tgz",
+ "integrity": "sha512-nG1c7aAFac7NYj8Hluo68WyWfc41xkEjaR0ViLHCa3oDvTQ/nIuLJlXJX1NUPw/DXzx/8+OKMng045HHQKQKWw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/generator": "^7.28.6",
+ "@vue-macros/common": "^3.1.1",
+ "@vue/devtools-api": "^8.0.6",
+ "ast-walker-scope": "^0.8.3",
+ "chokidar": "^5.0.0",
+ "json5": "^2.2.3",
+ "local-pkg": "^1.1.2",
+ "magic-string": "^0.30.21",
+ "mlly": "^1.8.0",
+ "muggle-string": "^0.4.1",
+ "pathe": "^2.0.3",
+ "picomatch": "^4.0.3",
+ "scule": "^1.3.0",
+ "tinyglobby": "^0.2.15",
+ "unplugin": "^3.0.0",
+ "unplugin-utils": "^0.3.1",
+ "yaml": "^2.8.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "@pinia/colada": ">=0.21.2",
+ "@vue/compiler-sfc": "^3.5.17",
+ "pinia": "^3.0.4",
+ "vue": "^3.5.0"
+ },
+ "peerDependenciesMeta": {
+ "@pinia/colada": {
+ "optional": true
+ },
+ "@vue/compiler-sfc": {
+ "optional": true
+ },
+ "pinia": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/vue-router/node_modules/@vue/devtools-api": {
+ "version": "8.0.6",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-8.0.6.tgz",
+ "integrity": "sha512-+lGBI+WTvJmnU2FZqHhEB8J1DXcvNlDeEalz77iYgOdY1jTj1ipSBaKj3sRhYcy+kqA8v/BSuvOz1XJucfQmUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-kit": "^8.0.6"
+ }
+ },
+ "node_modules/vue-router/node_modules/chokidar": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz",
+ "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==",
+ "license": "MIT",
+ "dependencies": {
+ "readdirp": "^5.0.0"
+ },
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/vue-router/node_modules/readdirp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz",
+ "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "type": "individual",
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/vue-router/node_modules/unplugin": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-3.0.0.tgz",
+ "integrity": "sha512-0Mqk3AT2TZCXWKdcoaufeXNukv2mTrEZExeXlHIOZXdqYoHHr4n51pymnwV8x2BOVxwXbK2HLlI7usrqMpycdg==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/remapping": "^2.3.5",
+ "picomatch": "^4.0.3",
+ "webpack-virtual-modules": "^0.6.2"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ }
+ },
+ "node_modules/vue3-otp-input": {
+ "version": "0.5.40",
+ "resolved": "https://registry.npmjs.org/vue3-otp-input/-/vue3-otp-input-0.5.40.tgz",
+ "integrity": "sha512-3AMYHqNz9ZDa9y7ICwcEcsJG7XdZGaLAr6IRLIl3whvseFE95F5Duc9q963HcqEbu8CeMWilkmbAt/0eZOZxow==",
+ "license": "MIT",
+ "dependencies": {
+ "vue": "^3.4.27"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.*"
+ }
+ },
+ "node_modules/vuedraggable": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
+ "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
+ "license": "MIT",
+ "dependencies": {
+ "sortablejs": "1.14.0"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.1"
+ }
+ },
+ "node_modules/vuedraggable/node_modules/sortablejs": {
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
+ "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
+ "license": "MIT"
+ },
+ "node_modules/w3c-keyname": {
+ "version": "2.2.8",
+ "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
+ "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
+ "license": "MIT"
+ },
+ "node_modules/wcwidth": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
+ "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defaults": "^1.0.3"
+ }
+ },
+ "node_modules/webpack-merge": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz",
+ "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "clone-deep": "^4.0.1",
+ "flat": "^5.0.2",
+ "wildcard": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/webpack-virtual-modules": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
+ "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
+ "license": "MIT"
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wildcard": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz",
+ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/with": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/with/-/with-7.0.2.tgz",
+ "integrity": "sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.9.6",
+ "@babel/types": "^7.9.6",
+ "assert-never": "^1.2.1",
+ "babel-walk": "3.0.0-canary-5"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/word-wrap": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz",
+ "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/wsl-utils": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
+ "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-wsl": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/xmlhttprequest-ssl": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.1.2.tgz",
+ "integrity": "sha512-TEU+nJVUUnA4CYJFLvK5X9AOeH4KvDvhIfm0vV1GaQRtchnG0hgK5p8hw/xjv8cunWYCsiPCSDzObPyhEwq3KQ==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yaml": {
+ "version": "2.8.2",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
+ "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/eemeli"
+ }
+ },
+ "node_modules/yaml-eslint-parser": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/yaml-eslint-parser/-/yaml-eslint-parser-1.3.2.tgz",
+ "integrity": "sha512-odxVsHAkZYYglR30aPYRY4nUGJnoJ2y1ww2HDvZALo0BDETv9kWbi16J52eHs+PWRNmF4ub6nZqfVOeesOvntg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eslint-visitor-keys": "^3.0.0",
+ "yaml": "^2.0.0"
+ },
+ "engines": {
+ "node": "^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ota-meshi"
+ }
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "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.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yocto-queue": {
+ "version": "0.1.0",
+ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+ "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/yoctocolors-cjs": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/yoctocolors-cjs/-/yoctocolors-cjs-2.1.3.tgz",
+ "integrity": "sha512-U/PBtDf35ff0D8X8D0jfdzHYEPFxAI7jJlxZXwCSez5M3190m+QobIfh+sWDWSHMCWWJN2AWamkegn6vr6YBTw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz",
+ "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "archiver-utils": "^5.0.0",
+ "compress-commons": "^6.0.2",
+ "readable-stream": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/zxcvbn": {
+ "version": "4.4.2",
+ "resolved": "https://registry.npmjs.org/zxcvbn/-/zxcvbn-4.4.2.tgz",
+ "integrity": "sha512-Bq0B+ixT/DMyG8kgX2xWcI5jUvCwqrMxSFam7m0lAf78nf04hv6lNCsyLYdyYTrCVMqNDY/206K7eExYCeSyUQ==",
+ "license": "MIT"
+ }
+ }
+}
diff --git a/frontend/package.json b/frontend/package.json
new file mode 100644
index 00000000..5cf77e2c
--- /dev/null
+++ b/frontend/package.json
@@ -0,0 +1,99 @@
+{
+ "name": "wiki-ux",
+ "version": "3.0.0",
+ "description": "The most powerful and extensible open source Wiki software",
+ "productName": "Wiki.js",
+ "author": "Nicolas Giard ",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite --force",
+ "build": "NODE_OPTIONS=--max-old-space-size=8192 vite build --emptyOutDir",
+ "ncu": "ncu -i",
+ "ncu-u": "ncu -u"
+ },
+ "dependencies": {
+ "@lezer/common": "1.5.1",
+ "@mdi/font": "7.4.47",
+ "@quasar/extras": "1.17.0",
+ "@simplewebauthn/browser": "13.2.2",
+ "@tanstack/vue-query": "5.92.9",
+ "@xterm/xterm": "6.0.0",
+ "browser-fs-access": "0.38.0",
+ "clipboard": "2.0.11",
+ "codemirror": "5.65.11",
+ "codemirror-asciidoc": "1.0.4",
+ "dependency-graph": "1.0.0",
+ "filesize": "11.0.13",
+ "filesize-parser": "1.5.1",
+ "fuse.js": "7.1.0",
+ "highlight.js": "11.11.1",
+ "js-cookie": "3.0.5",
+ "jwt-decode": "4.0.0",
+ "katex": "0.16.33",
+ "ky": "1.14.3",
+ "lodash-es": "4.17.23",
+ "lowlight": "3.3.0",
+ "luxon": "3.7.2",
+ "markdown-it": "14.1.1",
+ "markdown-it-abbr": "2.0.0",
+ "markdown-it-attrs": "4.3.1",
+ "markdown-it-decorate": "1.2.2",
+ "markdown-it-emoji": "3.0.0",
+ "markdown-it-expand-tabs": "1.0.13",
+ "markdown-it-footnote": "4.0.0",
+ "markdown-it-imsize": "2.0.1",
+ "markdown-it-mark": "4.0.0",
+ "markdown-it-mdc": "0.2.12",
+ "markdown-it-multimd-table": "4.2.3",
+ "markdown-it-sub": "2.0.0",
+ "markdown-it-sup": "2.0.0",
+ "markdown-it-task-lists": "2.1.1",
+ "mitt": "3.0.1",
+ "monaco-editor": "0.55.1",
+ "pako": "2.1.0",
+ "pinia": "3.0.4",
+ "prosemirror-commands": "1.7.1",
+ "prosemirror-history": "1.5.0",
+ "prosemirror-keymap": "1.2.3",
+ "prosemirror-model": "1.25.4",
+ "prosemirror-schema-list": "1.5.1",
+ "prosemirror-state": "1.4.4",
+ "prosemirror-transform": "1.11.0",
+ "prosemirror-view": "1.41.6",
+ "pug": "3.0.3",
+ "quasar": "2.18.6",
+ "slugify": "1.6.6",
+ "socket.io-client": "4.8.3",
+ "sortablejs": "1.15.7",
+ "sortablejs-vue3": "1.3.0",
+ "tabulator-tables": "6.3.1",
+ "tippy.js": "6.3.7",
+ "twemoji": "14.0.2",
+ "typescript": "5.9.3",
+ "uuid": "13.0.0",
+ "v-network-graph": "0.9.22",
+ "vue": "3.5.29",
+ "vue-i18n": "11.2.8",
+ "vue-router": "5.0.3",
+ "vue3-otp-input": "0.5.40",
+ "vuedraggable": "4.1.0",
+ "zxcvbn": "4.4.2"
+ },
+ "devDependencies": {
+ "@intlify/unplugin-vue-i18n": "11.0.7",
+ "@quasar/app-vite": "2.4.1",
+ "@quasar/vite-plugin": "1.10.0",
+ "@types/lodash": "4.17.24",
+ "@vue/language-plugin-pug": "3.2.5",
+ "autoprefixer": "10.4.27",
+ "browserlist": "latest",
+ "npm-check-updates": "19.6.3",
+ "sass": "1.97.3",
+ "vite-plugin-vue-devtools": "8.0.6"
+ },
+ "engines": {
+ "node": ">= 18.0",
+ "npm": ">= 6.13.4"
+ }
+}
diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js
new file mode 100644
index 00000000..aa61ec3d
--- /dev/null
+++ b/frontend/postcss.config.js
@@ -0,0 +1,29 @@
+import autoprefixer from 'autoprefixer'
+
+/* eslint-disable */
+// https://github.com/michael-ciniawsky/postcss-load-config
+
+export default {
+ plugins: [
+ // https://github.com/postcss/autoprefixer
+ 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/frontend/public/_assets/bg/login.jpg b/frontend/public/_assets/bg/login.jpg
new file mode 100644
index 00000000..58367b26
Binary files /dev/null and b/frontend/public/_assets/bg/login.jpg differ
diff --git a/frontend/public/_assets/fonts/roboto-mono/roboto-mono.woff2 b/frontend/public/_assets/fonts/roboto-mono/roboto-mono.woff2
new file mode 100644
index 00000000..213e0cc2
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto-mono/roboto-mono.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto-all-300.woff2 b/frontend/public/_assets/fonts/roboto/roboto-all-300.woff2
new file mode 100644
index 00000000..dd832081
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto/roboto-all-300.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto-all-500.woff2 b/frontend/public/_assets/fonts/roboto/roboto-all-500.woff2
new file mode 100644
index 00000000..0c5ede00
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto/roboto-all-500.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto-all-700.woff2 b/frontend/public/_assets/fonts/roboto/roboto-all-700.woff2
new file mode 100644
index 00000000..90e504f0
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto/roboto-all-700.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto-all-900.woff2 b/frontend/public/_assets/fonts/roboto/roboto-all-900.woff2
new file mode 100644
index 00000000..c3938e1c
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto/roboto-all-900.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto-all-regular.woff2 b/frontend/public/_assets/fonts/roboto/roboto-all-regular.woff2
new file mode 100644
index 00000000..f7a80a01
Binary files /dev/null and b/frontend/public/_assets/fonts/roboto/roboto-all-regular.woff2 differ
diff --git a/frontend/public/_assets/fonts/roboto/roboto.css b/frontend/public/_assets/fonts/roboto/roboto.css
new file mode 100644
index 00000000..9354507c
--- /dev/null
+++ b/frontend/public/_assets/fonts/roboto/roboto.css
@@ -0,0 +1,40 @@
+/* roboto-300 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 300;
+ src: local(''),
+ url('/_assets/fonts/roboto/roboto-all-300.woff2') format('woff2')
+}
+/* roboto-regular - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 400;
+ src: local(''),
+ url('/_assets/fonts/roboto/roboto-all-regular.woff2') format('woff2')
+}
+/* roboto-500 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 500;
+ src: local(''),
+ url('/_assets/fonts/roboto/roboto-all-500.woff2') format('woff2')
+}
+/* roboto-700 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 700;
+ src: local(''),
+ url('/_assets/fonts/roboto/roboto-all-700.woff2') format('woff2')
+}
+/* roboto-900 - vietnamese_latin-ext_latin_greek-ext_greek_cyrillic-ext_cyrillic */
+@font-face {
+ font-family: 'Roboto';
+ font-style: normal;
+ font-weight: 900;
+ src: local(''),
+ url('/_assets/fonts/roboto/roboto-all-900.woff2') format('woff2')
+}
diff --git a/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic-ext.woff2 b/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic-ext.woff2
new file mode 100644
index 00000000..a5a8edc9
Binary files /dev/null and b/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic-ext.woff2 differ
diff --git a/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic.woff2 b/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic.woff2
new file mode 100644
index 00000000..fa2eee0c
Binary files /dev/null and b/frontend/public/_assets/fonts/rubik/rubik-variable-cyrillic.woff2 differ
diff --git a/frontend/public/_assets/fonts/rubik/rubik-variable-hebrew.woff2 b/frontend/public/_assets/fonts/rubik/rubik-variable-hebrew.woff2
new file mode 100644
index 00000000..d51ce738
Binary files /dev/null and b/frontend/public/_assets/fonts/rubik/rubik-variable-hebrew.woff2 differ
diff --git a/frontend/public/_assets/fonts/rubik/rubik-variable-latin-ext.woff2 b/frontend/public/_assets/fonts/rubik/rubik-variable-latin-ext.woff2
new file mode 100644
index 00000000..9961a9b1
Binary files /dev/null and b/frontend/public/_assets/fonts/rubik/rubik-variable-latin-ext.woff2 differ
diff --git a/frontend/public/_assets/fonts/rubik/rubik-variable-latin.woff2 b/frontend/public/_assets/fonts/rubik/rubik-variable-latin.woff2
new file mode 100644
index 00000000..f9ec108b
Binary files /dev/null and b/frontend/public/_assets/fonts/rubik/rubik-variable-latin.woff2 differ
diff --git a/frontend/public/_assets/fonts/rubik/rubik.css b/frontend/public/_assets/fonts/rubik/rubik.css
new file mode 100644
index 00000000..24356550
--- /dev/null
+++ b/frontend/public/_assets/fonts/rubik/rubik.css
@@ -0,0 +1,39 @@
+/* cyrillic-ext */
+@font-face {
+ font-family: 'Rubik';
+ font-display: swap;
+ src: url(/_assets/fonts/rubik/rubik-variable-cyrillic-ext.woff2) format('woff2');
+ unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
+}
+/* cyrillic */
+@font-face {
+ font-family: 'Rubik';
+ font-display: swap;
+ src: url(/_assets/fonts/rubik/rubik-variable-cyrillic.woff2) format('woff2');
+ unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
+}
+/* hebrew */
+@font-face {
+ font-family: 'Rubik';
+ font-display: swap;
+ src: url(/_assets/fonts/rubik/rubik-variable-hebrew.woff2) format('woff2');
+ unicode-range: U+0590-05FF, U+200C-2010, U+20AA, U+25CC, U+FB1D-FB4F;
+}
+/* latin-ext */
+@font-face {
+ font-family: 'Rubik';
+ font-display: swap;
+ src: url(/_assets/fonts/rubik/rubik-variable-latin-ext.woff2) format('woff2');
+ unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
+}
+/* latin */
+@font-face {
+ font-family: 'Rubik';
+ font-display: swap;
+ src: url(/_assets/fonts/rubik/rubik-variable-latin.woff2) format('woff2');
+ unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
+}
+
+body.wiki-root {
+ font-family: 'Rubik', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
+}
diff --git a/frontend/public/_assets/icons/anim-book.png b/frontend/public/_assets/icons/anim-book.png
new file mode 100644
index 00000000..1da2dad4
Binary files /dev/null and b/frontend/public/_assets/icons/anim-book.png differ
diff --git a/frontend/public/_assets/icons/carbon-copy-empty-box.svg b/frontend/public/_assets/icons/carbon-copy-empty-box.svg
new file mode 100644
index 00000000..2d018143
--- /dev/null
+++ b/frontend/public/_assets/icons/carbon-copy-empty-box.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-7zip.svg b/frontend/public/_assets/icons/color-7zip.svg
new file mode 100644
index 00000000..8f2d93b6
--- /dev/null
+++ b/frontend/public/_assets/icons/color-7zip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-aac.svg b/frontend/public/_assets/icons/color-aac.svg
new file mode 100644
index 00000000..311754cc
--- /dev/null
+++ b/frontend/public/_assets/icons/color-aac.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-ai.svg b/frontend/public/_assets/icons/color-ai.svg
new file mode 100644
index 00000000..8ed984d2
--- /dev/null
+++ b/frontend/public/_assets/icons/color-ai.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-apk.svg b/frontend/public/_assets/icons/color-apk.svg
new file mode 100644
index 00000000..eff1bb8f
--- /dev/null
+++ b/frontend/public/_assets/icons/color-apk.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-archive.svg b/frontend/public/_assets/icons/color-archive.svg
new file mode 100644
index 00000000..14b1316f
--- /dev/null
+++ b/frontend/public/_assets/icons/color-archive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-audio-file.svg b/frontend/public/_assets/icons/color-audio-file.svg
new file mode 100644
index 00000000..a6850cc4
--- /dev/null
+++ b/frontend/public/_assets/icons/color-audio-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-avi.svg b/frontend/public/_assets/icons/color-avi.svg
new file mode 100644
index 00000000..b071d54f
--- /dev/null
+++ b/frontend/public/_assets/icons/color-avi.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-binary-file.svg b/frontend/public/_assets/icons/color-binary-file.svg
new file mode 100644
index 00000000..41763c9e
--- /dev/null
+++ b/frontend/public/_assets/icons/color-binary-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-blog.svg b/frontend/public/_assets/icons/color-blog.svg
new file mode 100644
index 00000000..49f8cd0a
--- /dev/null
+++ b/frontend/public/_assets/icons/color-blog.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-cd.svg b/frontend/public/_assets/icons/color-cd.svg
new file mode 100644
index 00000000..59fb4452
--- /dev/null
+++ b/frontend/public/_assets/icons/color-cd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-code-file.svg b/frontend/public/_assets/icons/color-code-file.svg
new file mode 100644
index 00000000..feaf0067
--- /dev/null
+++ b/frontend/public/_assets/icons/color-code-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-css-filetype.svg b/frontend/public/_assets/icons/color-css-filetype.svg
new file mode 100644
index 00000000..bf077d12
--- /dev/null
+++ b/frontend/public/_assets/icons/color-css-filetype.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-csv.svg b/frontend/public/_assets/icons/color-csv.svg
new file mode 100644
index 00000000..a0746e4e
--- /dev/null
+++ b/frontend/public/_assets/icons/color-csv.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-data-grid.svg b/frontend/public/_assets/icons/color-data-grid.svg
new file mode 100644
index 00000000..a2fbd8ab
--- /dev/null
+++ b/frontend/public/_assets/icons/color-data-grid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-data-pending.svg b/frontend/public/_assets/icons/color-data-pending.svg
new file mode 100644
index 00000000..b6c20524
--- /dev/null
+++ b/frontend/public/_assets/icons/color-data-pending.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-dmg.svg b/frontend/public/_assets/icons/color-dmg.svg
new file mode 100644
index 00000000..fbe3e0c2
--- /dev/null
+++ b/frontend/public/_assets/icons/color-dmg.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-doc.svg b/frontend/public/_assets/icons/color-doc.svg
new file mode 100644
index 00000000..1d31eeef
--- /dev/null
+++ b/frontend/public/_assets/icons/color-doc.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-document.svg b/frontend/public/_assets/icons/color-document.svg
new file mode 100644
index 00000000..069015ba
--- /dev/null
+++ b/frontend/public/_assets/icons/color-document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-documents.svg b/frontend/public/_assets/icons/color-documents.svg
new file mode 100644
index 00000000..3eda1e7c
--- /dev/null
+++ b/frontend/public/_assets/icons/color-documents.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-exe.svg b/frontend/public/_assets/icons/color-exe.svg
new file mode 100644
index 00000000..d3b0533c
--- /dev/null
+++ b/frontend/public/_assets/icons/color-exe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-gif.svg b/frontend/public/_assets/icons/color-gif.svg
new file mode 100644
index 00000000..7ccf47d9
--- /dev/null
+++ b/frontend/public/_assets/icons/color-gif.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-image-file.svg b/frontend/public/_assets/icons/color-image-file.svg
new file mode 100644
index 00000000..72bd0f22
--- /dev/null
+++ b/frontend/public/_assets/icons/color-image-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-jpg.svg b/frontend/public/_assets/icons/color-jpg.svg
new file mode 100644
index 00000000..244706ee
--- /dev/null
+++ b/frontend/public/_assets/icons/color-jpg.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-json.svg b/frontend/public/_assets/icons/color-json.svg
new file mode 100644
index 00000000..7464b442
--- /dev/null
+++ b/frontend/public/_assets/icons/color-json.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-mov.svg b/frontend/public/_assets/icons/color-mov.svg
new file mode 100644
index 00000000..fec349f1
--- /dev/null
+++ b/frontend/public/_assets/icons/color-mov.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-mp3.svg b/frontend/public/_assets/icons/color-mp3.svg
new file mode 100644
index 00000000..a6ae4485
--- /dev/null
+++ b/frontend/public/_assets/icons/color-mp3.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-mpg.svg b/frontend/public/_assets/icons/color-mpg.svg
new file mode 100644
index 00000000..2f96faca
--- /dev/null
+++ b/frontend/public/_assets/icons/color-mpg.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-nodejs.svg b/frontend/public/_assets/icons/color-nodejs.svg
new file mode 100644
index 00000000..d7814ddb
--- /dev/null
+++ b/frontend/public/_assets/icons/color-nodejs.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-ogg.svg b/frontend/public/_assets/icons/color-ogg.svg
new file mode 100644
index 00000000..10f084c1
--- /dev/null
+++ b/frontend/public/_assets/icons/color-ogg.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-otf.svg b/frontend/public/_assets/icons/color-otf.svg
new file mode 100644
index 00000000..8fd59e72
--- /dev/null
+++ b/frontend/public/_assets/icons/color-otf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-pdf.svg b/frontend/public/_assets/icons/color-pdf.svg
new file mode 100644
index 00000000..3c22eb0c
--- /dev/null
+++ b/frontend/public/_assets/icons/color-pdf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-png.svg b/frontend/public/_assets/icons/color-png.svg
new file mode 100644
index 00000000..d8493691
--- /dev/null
+++ b/frontend/public/_assets/icons/color-png.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-postgresql.svg b/frontend/public/_assets/icons/color-postgresql.svg
new file mode 100644
index 00000000..1b811574
--- /dev/null
+++ b/frontend/public/_assets/icons/color-postgresql.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-ppt.svg b/frontend/public/_assets/icons/color-ppt.svg
new file mode 100644
index 00000000..9d6234ab
--- /dev/null
+++ b/frontend/public/_assets/icons/color-ppt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-psd.svg b/frontend/public/_assets/icons/color-psd.svg
new file mode 100644
index 00000000..0ac65711
--- /dev/null
+++ b/frontend/public/_assets/icons/color-psd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-rar.svg b/frontend/public/_assets/icons/color-rar.svg
new file mode 100644
index 00000000..091683f6
--- /dev/null
+++ b/frontend/public/_assets/icons/color-rar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-schedule.svg b/frontend/public/_assets/icons/color-schedule.svg
new file mode 100644
index 00000000..68bbf510
--- /dev/null
+++ b/frontend/public/_assets/icons/color-schedule.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-tar.svg b/frontend/public/_assets/icons/color-tar.svg
new file mode 100644
index 00000000..a056b143
--- /dev/null
+++ b/frontend/public/_assets/icons/color-tar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-tif.svg b/frontend/public/_assets/icons/color-tif.svg
new file mode 100644
index 00000000..11bba856
--- /dev/null
+++ b/frontend/public/_assets/icons/color-tif.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-ttf.svg b/frontend/public/_assets/icons/color-ttf.svg
new file mode 100644
index 00000000..2c3fd1c9
--- /dev/null
+++ b/frontend/public/_assets/icons/color-ttf.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-txt.svg b/frontend/public/_assets/icons/color-txt.svg
new file mode 100644
index 00000000..49b0e5f2
--- /dev/null
+++ b/frontend/public/_assets/icons/color-txt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-video-file.svg b/frontend/public/_assets/icons/color-video-file.svg
new file mode 100644
index 00000000..963d94e7
--- /dev/null
+++ b/frontend/public/_assets/icons/color-video-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-wav.svg b/frontend/public/_assets/icons/color-wav.svg
new file mode 100644
index 00000000..df2aa230
--- /dev/null
+++ b/frontend/public/_assets/icons/color-wav.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-woff.svg b/frontend/public/_assets/icons/color-woff.svg
new file mode 100644
index 00000000..f2653cc2
--- /dev/null
+++ b/frontend/public/_assets/icons/color-woff.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-word.svg b/frontend/public/_assets/icons/color-word.svg
new file mode 100644
index 00000000..a25df339
--- /dev/null
+++ b/frontend/public/_assets/icons/color-word.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-xls.svg b/frontend/public/_assets/icons/color-xls.svg
new file mode 100644
index 00000000..c2637d19
--- /dev/null
+++ b/frontend/public/_assets/icons/color-xls.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-xml-file.svg b/frontend/public/_assets/icons/color-xml-file.svg
new file mode 100644
index 00000000..7ba25788
--- /dev/null
+++ b/frontend/public/_assets/icons/color-xml-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/color-zip.svg b/frontend/public/_assets/icons/color-zip.svg
new file mode 100644
index 00000000..194d12b9
--- /dev/null
+++ b/frontend/public/_assets/icons/color-zip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-account.svg b/frontend/public/_assets/icons/fluent-account.svg
new file mode 100644
index 00000000..eb2a574b
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-account.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-add-key.svg b/frontend/public/_assets/icons/fluent-add-key.svg
new file mode 100644
index 00000000..9920976a
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-add-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-advance.svg b/frontend/public/_assets/icons/fluent-advance.svg
new file mode 100644
index 00000000..14259474
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-advance.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-api.svg b/frontend/public/_assets/icons/fluent-api.svg
new file mode 100644
index 00000000..247028a4
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-apps-tab.svg b/frontend/public/_assets/icons/fluent-apps-tab.svg
new file mode 100644
index 00000000..46809947
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-apps-tab.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-bar-chart.svg b/frontend/public/_assets/icons/fluent-bar-chart.svg
new file mode 100644
index 00000000..361def4d
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-bar-chart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-bot-animated.svg b/frontend/public/_assets/icons/fluent-bot-animated.svg
new file mode 100644
index 00000000..5dca9ef4
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-bot-animated.svg
@@ -0,0 +1,31 @@
+
diff --git a/frontend/public/_assets/icons/fluent-bot.svg b/frontend/public/_assets/icons/fluent-bot.svg
new file mode 100644
index 00000000..a623e223
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-bot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-bunch-of-keys.svg b/frontend/public/_assets/icons/fluent-bunch-of-keys.svg
new file mode 100644
index 00000000..73356398
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-bunch-of-keys.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-bursts.svg b/frontend/public/_assets/icons/fluent-bursts.svg
new file mode 100644
index 00000000..76c96fc0
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-bursts.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-cashbook.svg b/frontend/public/_assets/icons/fluent-cashbook.svg
new file mode 100644
index 00000000..e2928a1b
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-cashbook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-change-theme.svg b/frontend/public/_assets/icons/fluent-change-theme.svg
new file mode 100644
index 00000000..ae36332b
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-change-theme.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-choose.svg b/frontend/public/_assets/icons/fluent-choose.svg
new file mode 100644
index 00000000..c446c752
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-choose.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-code.svg b/frontend/public/_assets/icons/fluent-code.svg
new file mode 100644
index 00000000..85a9ccc2
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-code.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-color-wheel.svg b/frontend/public/_assets/icons/fluent-color-wheel.svg
new file mode 100644
index 00000000..87410e46
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-color-wheel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-comments.svg b/frontend/public/_assets/icons/fluent-comments.svg
new file mode 100644
index 00000000..77be6513
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-comments.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-database.svg b/frontend/public/_assets/icons/fluent-database.svg
new file mode 100644
index 00000000..78bf7453
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-database.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-delete-bin.svg b/frontend/public/_assets/icons/fluent-delete-bin.svg
new file mode 100644
index 00000000..252ee303
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-delete-bin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-down.svg b/frontend/public/_assets/icons/fluent-down.svg
new file mode 100644
index 00000000..7d4cb5c5
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-down.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-downloading-updates.svg b/frontend/public/_assets/icons/fluent-downloading-updates.svg
new file mode 100644
index 00000000..346e9680
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-downloading-updates.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-event-log.svg b/frontend/public/_assets/icons/fluent-event-log.svg
new file mode 100644
index 00000000..bfad419b
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-event-log.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-fantasy.svg b/frontend/public/_assets/icons/fluent-fantasy.svg
new file mode 100644
index 00000000..d5e24ebe
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-fantasy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-female-working-with-a-laptop.svg b/frontend/public/_assets/icons/fluent-female-working-with-a-laptop.svg
new file mode 100644
index 00000000..35a1ea26
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-female-working-with-a-laptop.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-find-and-replace.svg b/frontend/public/_assets/icons/fluent-find-and-replace.svg
new file mode 100644
index 00000000..f74b10ac
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-find-and-replace.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-fingerprint.svg b/frontend/public/_assets/icons/fluent-fingerprint.svg
new file mode 100644
index 00000000..b335cd74
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-fingerprint.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-firewall.svg b/frontend/public/_assets/icons/fluent-firewall.svg
new file mode 100644
index 00000000..61a7502d
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-firewall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-folder-tree.svg b/frontend/public/_assets/icons/fluent-folder-tree.svg
new file mode 100644
index 00000000..4248e308
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-folder-tree.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-folder.svg b/frontend/public/_assets/icons/fluent-folder.svg
new file mode 100644
index 00000000..e899ab93
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-ftp.svg b/frontend/public/_assets/icons/fluent-ftp.svg
new file mode 100644
index 00000000..2ecc2854
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-ftp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-graph.svg b/frontend/public/_assets/icons/fluent-graph.svg
new file mode 100644
index 00000000..ce8f29cf
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-graph.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-inspection.svg b/frontend/public/_assets/icons/fluent-inspection.svg
new file mode 100644
index 00000000..214eb812
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-inspection.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-key-2.svg b/frontend/public/_assets/icons/fluent-key-2.svg
new file mode 100644
index 00000000..44d16ad2
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-key-2.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-language.svg b/frontend/public/_assets/icons/fluent-language.svg
new file mode 100644
index 00000000..05b1f8bf
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-language.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-lightning-bolt.svg b/frontend/public/_assets/icons/fluent-lightning-bolt.svg
new file mode 100644
index 00000000..3ee3fcc3
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-lightning-bolt.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/frontend/public/_assets/icons/fluent-linux-terminal-animated.svg b/frontend/public/_assets/icons/fluent-linux-terminal-animated.svg
new file mode 100644
index 00000000..770515ee
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-linux-terminal-animated.svg
@@ -0,0 +1,26 @@
+
+
diff --git a/frontend/public/_assets/icons/fluent-linux-terminal.svg b/frontend/public/_assets/icons/fluent-linux-terminal.svg
new file mode 100644
index 00000000..fc1a6a65
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-linux-terminal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-markdown.svg b/frontend/public/_assets/icons/fluent-markdown.svg
new file mode 100644
index 00000000..be0a381a
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-markdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-message-settings-animated.svg b/frontend/public/_assets/icons/fluent-message-settings-animated.svg
new file mode 100644
index 00000000..2d8d2562
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-message-settings-animated.svg
@@ -0,0 +1,50 @@
+
diff --git a/frontend/public/_assets/icons/fluent-message-settings.svg b/frontend/public/_assets/icons/fluent-message-settings.svg
new file mode 100644
index 00000000..ff180bfb
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-message-settings.svg
@@ -0,0 +1,48 @@
+
diff --git a/frontend/public/_assets/icons/fluent-module.svg b/frontend/public/_assets/icons/fluent-module.svg
new file mode 100644
index 00000000..5d78233e
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-module.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-network.svg b/frontend/public/_assets/icons/fluent-network.svg
new file mode 100644
index 00000000..e6c3227c
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-network.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-open-file-under-cursor.svg b/frontend/public/_assets/icons/fluent-open-file-under-cursor.svg
new file mode 100644
index 00000000..f9837371
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-open-file-under-cursor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-opened-folder.svg b/frontend/public/_assets/icons/fluent-opened-folder.svg
new file mode 100644
index 00000000..0ed6914c
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-opened-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-paint-roller-animated.svg b/frontend/public/_assets/icons/fluent-paint-roller-animated.svg
new file mode 100644
index 00000000..5a937043
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-paint-roller-animated.svg
@@ -0,0 +1,55 @@
+
diff --git a/frontend/public/_assets/icons/fluent-paint-roller.svg b/frontend/public/_assets/icons/fluent-paint-roller.svg
new file mode 100644
index 00000000..81b810ac
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-paint-roller.svg
@@ -0,0 +1,24 @@
+
diff --git a/frontend/public/_assets/icons/fluent-password-reset.svg b/frontend/public/_assets/icons/fluent-password-reset.svg
new file mode 100644
index 00000000..865f0245
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-password-reset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-pencil-drawing.svg b/frontend/public/_assets/icons/fluent-pencil-drawing.svg
new file mode 100644
index 00000000..fdb425c7
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-pencil-drawing.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-people.svg b/frontend/public/_assets/icons/fluent-people.svg
new file mode 100644
index 00000000..84563530
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-people.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-plus-plus.svg b/frontend/public/_assets/icons/fluent-plus-plus.svg
new file mode 100644
index 00000000..03d530ac
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-plus-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-processor.svg b/frontend/public/_assets/icons/fluent-processor.svg
new file mode 100644
index 00000000..64b4becd
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-processor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-protect.svg b/frontend/public/_assets/icons/fluent-protect.svg
new file mode 100644
index 00000000..84c0bf67
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-protect.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-query.svg b/frontend/public/_assets/icons/fluent-query.svg
new file mode 100644
index 00000000..a893f9b6
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-query.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-rename.svg b/frontend/public/_assets/icons/fluent-rename.svg
new file mode 100644
index 00000000..e8edc77d
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-rename.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-rest-api-animated.svg b/frontend/public/_assets/icons/fluent-rest-api-animated.svg
new file mode 100644
index 00000000..e684aa6a
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-rest-api-animated.svg
@@ -0,0 +1,21 @@
+
diff --git a/frontend/public/_assets/icons/fluent-rest-api.svg b/frontend/public/_assets/icons/fluent-rest-api.svg
new file mode 100644
index 00000000..5afb29d5
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-rest-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-rfid-tag.svg b/frontend/public/_assets/icons/fluent-rfid-tag.svg
new file mode 100644
index 00000000..2ec98bf7
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-rfid-tag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-rich-text-converter.svg b/frontend/public/_assets/icons/fluent-rich-text-converter.svg
new file mode 100644
index 00000000..73383655
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-rich-text-converter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-save-as.svg b/frontend/public/_assets/icons/fluent-save-as.svg
new file mode 100644
index 00000000..496f2ba0
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-save-as.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-security-lock.svg b/frontend/public/_assets/icons/fluent-security-lock.svg
new file mode 100644
index 00000000..b45228e4
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-security-lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-security-ssl.svg b/frontend/public/_assets/icons/fluent-security-ssl.svg
new file mode 100644
index 00000000..e0c7dcbc
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-security-ssl.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-shutdown.svg b/frontend/public/_assets/icons/fluent-shutdown.svg
new file mode 100644
index 00000000..5c384dff
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-shutdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-sidebar-menu.svg b/frontend/public/_assets/icons/fluent-sidebar-menu.svg
new file mode 100644
index 00000000..2b7d00c7
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-sidebar-menu.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-spring.svg b/frontend/public/_assets/icons/fluent-spring.svg
new file mode 100644
index 00000000..52e0f030
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-spring.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-ssd-animated.svg b/frontend/public/_assets/icons/fluent-ssd-animated.svg
new file mode 100644
index 00000000..43e1cb9d
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-ssd-animated.svg
@@ -0,0 +1,76 @@
+
diff --git a/frontend/public/_assets/icons/fluent-ssd.svg b/frontend/public/_assets/icons/fluent-ssd.svg
new file mode 100644
index 00000000..79baeaf3
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-ssd.svg
@@ -0,0 +1,74 @@
+
diff --git a/frontend/public/_assets/icons/fluent-swiss-army-knife.svg b/frontend/public/_assets/icons/fluent-swiss-army-knife.svg
new file mode 100644
index 00000000..1087649b
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-swiss-army-knife.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-tag.svg b/frontend/public/_assets/icons/fluent-tag.svg
new file mode 100644
index 00000000..0d873492
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-tag.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-template.svg b/frontend/public/_assets/icons/fluent-template.svg
new file mode 100644
index 00000000..f7323f2f
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-template.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-tree-structure.svg b/frontend/public/_assets/icons/fluent-tree-structure.svg
new file mode 100644
index 00000000..835a1f47
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-tree-structure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-unavailable.svg b/frontend/public/_assets/icons/fluent-unavailable.svg
new file mode 100644
index 00000000..12e86013
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-unavailable.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-upload.svg b/frontend/public/_assets/icons/fluent-upload.svg
new file mode 100644
index 00000000..0c4d998d
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-upload.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-web.svg b/frontend/public/_assets/icons/fluent-web.svg
new file mode 100644
index 00000000..5f8ae0f5
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-web.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/fluent-windsock.svg b/frontend/public/_assets/icons/fluent-windsock.svg
new file mode 100644
index 00000000..eef7c8db
--- /dev/null
+++ b/frontend/public/_assets/icons/fluent-windsock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-3d-touch.svg b/frontend/public/_assets/icons/ultraviolet-3d-touch.svg
new file mode 100644
index 00000000..1e530730
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-3d-touch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-access.svg b/frontend/public/_assets/icons/ultraviolet-access.svg
new file mode 100644
index 00000000..8dd18aa1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-access.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-add-folder.svg b/frontend/public/_assets/icons/ultraviolet-add-folder.svg
new file mode 100644
index 00000000..5c6cbbe2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-add-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-add-image.svg b/frontend/public/_assets/icons/ultraviolet-add-image.svg
new file mode 100644
index 00000000..47caa6d0
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-add-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-address.svg b/frontend/public/_assets/icons/ultraviolet-address.svg
new file mode 100644
index 00000000..1d61c96a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-address.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-administrative-tools.svg b/frontend/public/_assets/icons/ultraviolet-administrative-tools.svg
new file mode 100644
index 00000000..9c924a18
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-administrative-tools.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-administrator-male.svg b/frontend/public/_assets/icons/ultraviolet-administrator-male.svg
new file mode 100644
index 00000000..42bd1870
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-administrator-male.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-advance.svg b/frontend/public/_assets/icons/ultraviolet-advance.svg
new file mode 100644
index 00000000..8c48405a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-advance.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-amazon-web-services.svg b/frontend/public/_assets/icons/ultraviolet-amazon-web-services.svg
new file mode 100644
index 00000000..5ebd14e9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-amazon-web-services.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-amazon.svg b/frontend/public/_assets/icons/ultraviolet-amazon.svg
new file mode 100644
index 00000000..798a682c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-amazon.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-api.svg b/frontend/public/_assets/icons/ultraviolet-api.svg
new file mode 100644
index 00000000..03ec242e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-api.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-apple-logo.svg b/frontend/public/_assets/icons/ultraviolet-apple-logo.svg
new file mode 100644
index 00000000..b243ef49
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-apple-logo.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-apply.svg b/frontend/public/_assets/icons/ultraviolet-apply.svg
new file mode 100644
index 00000000..75c8c543
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-apply.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-archive-folder.svg b/frontend/public/_assets/icons/ultraviolet-archive-folder.svg
new file mode 100644
index 00000000..4a25278c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-archive-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-asciidoc.svg b/frontend/public/_assets/icons/ultraviolet-asciidoc.svg
new file mode 100644
index 00000000..b423da0b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-asciidoc.svg
@@ -0,0 +1,8 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-asterisk.svg b/frontend/public/_assets/icons/ultraviolet-asterisk.svg
new file mode 100644
index 00000000..8d8ce9b6
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-asterisk.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-auth0.svg b/frontend/public/_assets/icons/ultraviolet-auth0.svg
new file mode 100644
index 00000000..801cbd26
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-auth0.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-automation.svg b/frontend/public/_assets/icons/ultraviolet-automation.svg
new file mode 100644
index 00000000..d75f0119
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-automation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-azure.svg b/frontend/public/_assets/icons/ultraviolet-azure.svg
new file mode 100644
index 00000000..250e2d79
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-azure.svg
@@ -0,0 +1,7 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-back.svg b/frontend/public/_assets/icons/ultraviolet-back.svg
new file mode 100644
index 00000000..1a6c816b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-back.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-binary-file.svg b/frontend/public/_assets/icons/ultraviolet-binary-file.svg
new file mode 100644
index 00000000..bd613c8c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-binary-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-bot.svg b/frontend/public/_assets/icons/ultraviolet-bot.svg
new file mode 100644
index 00000000..060f66de
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-bot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-breakable.svg b/frontend/public/_assets/icons/ultraviolet-breakable.svg
new file mode 100644
index 00000000..4fc06553
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-breakable.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-brick.svg b/frontend/public/_assets/icons/ultraviolet-brick.svg
new file mode 100644
index 00000000..521e18c3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-brick.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-building.svg b/frontend/public/_assets/icons/ultraviolet-building.svg
new file mode 100644
index 00000000..a8fddbc0
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-building.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-butterfly.svg b/frontend/public/_assets/icons/ultraviolet-butterfly.svg
new file mode 100644
index 00000000..192912ec
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-butterfly.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-calendar-plus.svg b/frontend/public/_assets/icons/ultraviolet-calendar-plus.svg
new file mode 100644
index 00000000..de817219
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-calendar-plus.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-calendar.svg b/frontend/public/_assets/icons/ultraviolet-calendar.svg
new file mode 100644
index 00000000..ba53ca89
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-calendar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-cellular-network.svg b/frontend/public/_assets/icons/ultraviolet-cellular-network.svg
new file mode 100644
index 00000000..03c3d5db
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-cellular-network.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-chat.svg b/frontend/public/_assets/icons/ultraviolet-chat.svg
new file mode 100644
index 00000000..3a09f7a7
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-chat.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-chevron-right.svg b/frontend/public/_assets/icons/ultraviolet-chevron-right.svg
new file mode 100644
index 00000000..c06de194
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-chevron-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-clock.svg b/frontend/public/_assets/icons/ultraviolet-clock.svg
new file mode 100644
index 00000000..bb75f4c8
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-clock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-close-pane.svg b/frontend/public/_assets/icons/ultraviolet-close-pane.svg
new file mode 100644
index 00000000..72c2f0e0
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-close-pane.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-cloud-checked.svg b/frontend/public/_assets/icons/ultraviolet-cloud-checked.svg
new file mode 100644
index 00000000..c8714a69
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-cloud-checked.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-code-file.svg b/frontend/public/_assets/icons/ultraviolet-code-file.svg
new file mode 100644
index 00000000..022237dc
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-code-file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-code-fork.svg b/frontend/public/_assets/icons/ultraviolet-code-fork.svg
new file mode 100644
index 00000000..7b029a67
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-code-fork.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-code.svg b/frontend/public/_assets/icons/ultraviolet-code.svg
new file mode 100644
index 00000000..1db66db6
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-code.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-comments.svg b/frontend/public/_assets/icons/ultraviolet-comments.svg
new file mode 100644
index 00000000..af1119f1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-comments.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-computer.svg b/frontend/public/_assets/icons/ultraviolet-computer.svg
new file mode 100644
index 00000000..9061692d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-computer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-confusion.svg b/frontend/public/_assets/icons/ultraviolet-confusion.svg
new file mode 100644
index 00000000..458bf3be
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-confusion.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-contact.svg b/frontend/public/_assets/icons/ultraviolet-contact.svg
new file mode 100644
index 00000000..209bec4b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-contact.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-cookies.svg b/frontend/public/_assets/icons/ultraviolet-cookies.svg
new file mode 100644
index 00000000..c4a81e82
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-cookies.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-copyright.svg b/frontend/public/_assets/icons/ultraviolet-copyright.svg
new file mode 100644
index 00000000..06796307
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-copyright.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-css.svg b/frontend/public/_assets/icons/ultraviolet-css.svg
new file mode 100644
index 00000000..7f5213a3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-css.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-curly-arrow.svg b/frontend/public/_assets/icons/ultraviolet-curly-arrow.svg
new file mode 100644
index 00000000..157530fc
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-curly-arrow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-customer.svg b/frontend/public/_assets/icons/ultraviolet-customer.svg
new file mode 100644
index 00000000..11bada10
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-customer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-data-protection.svg b/frontend/public/_assets/icons/ultraviolet-data-protection.svg
new file mode 100644
index 00000000..525cbe37
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-data-protection.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-data-sheet.svg b/frontend/public/_assets/icons/ultraviolet-data-sheet.svg
new file mode 100644
index 00000000..6bc41533
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-data-sheet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-database-daily-export.svg b/frontend/public/_assets/icons/ultraviolet-database-daily-export.svg
new file mode 100644
index 00000000..5e3ed815
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-database-daily-export.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-database-daily-import.svg b/frontend/public/_assets/icons/ultraviolet-database-daily-import.svg
new file mode 100644
index 00000000..82225d63
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-database-daily-import.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-database-export.svg b/frontend/public/_assets/icons/ultraviolet-database-export.svg
new file mode 100644
index 00000000..a372e506
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-database-export.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-database-restore.svg b/frontend/public/_assets/icons/ultraviolet-database-restore.svg
new file mode 100644
index 00000000..cd225db2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-database-restore.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-database.svg b/frontend/public/_assets/icons/ultraviolet-database.svg
new file mode 100644
index 00000000..e6183dab
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-database.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-datalake.svg b/frontend/public/_assets/icons/ultraviolet-datalake.svg
new file mode 100644
index 00000000..58398d9b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-datalake.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-denied.svg b/frontend/public/_assets/icons/ultraviolet-denied.svg
new file mode 100644
index 00000000..83e809f4
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-denied.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-depth.svg b/frontend/public/_assets/icons/ultraviolet-depth.svg
new file mode 100644
index 00000000..92a82d17
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-depth.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-disconnected.svg b/frontend/public/_assets/icons/ultraviolet-disconnected.svg
new file mode 100644
index 00000000..47ed81fc
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-disconnected.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-discord.svg b/frontend/public/_assets/icons/ultraviolet-discord.svg
new file mode 100644
index 00000000..5f123ce8
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-discord.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-discussion-forum.svg b/frontend/public/_assets/icons/ultraviolet-discussion-forum.svg
new file mode 100644
index 00000000..59990334
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-discussion-forum.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-dns.svg b/frontend/public/_assets/icons/ultraviolet-dns.svg
new file mode 100644
index 00000000..bc20cb42
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-dns.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-do-not-touch.svg b/frontend/public/_assets/icons/ultraviolet-do-not-touch.svg
new file mode 100644
index 00000000..aae9b6e3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-do-not-touch.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-docker-container.svg b/frontend/public/_assets/icons/ultraviolet-docker-container.svg
new file mode 100644
index 00000000..3820de09
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-docker-container.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg b/frontend/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg
new file mode 100644
index 00000000..1b523456
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-door-sensor-alarmed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-double-right.svg b/frontend/public/_assets/icons/ultraviolet-double-right.svg
new file mode 100644
index 00000000..57f637d4
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-double-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-download-from-cloud.svg b/frontend/public/_assets/icons/ultraviolet-download-from-cloud.svg
new file mode 100644
index 00000000..9d506238
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-download-from-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-downloads.svg b/frontend/public/_assets/icons/ultraviolet-downloads.svg
new file mode 100644
index 00000000..e78ac223
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-downloads.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-dropbox.svg b/frontend/public/_assets/icons/ultraviolet-dropbox.svg
new file mode 100644
index 00000000..fd9d5205
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-dropbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-duplicate.svg b/frontend/public/_assets/icons/ultraviolet-duplicate.svg
new file mode 100644
index 00000000..d2697f4d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-duplicate.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-email-open.svg b/frontend/public/_assets/icons/ultraviolet-email-open.svg
new file mode 100644
index 00000000..6cc6b6db
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-email-open.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-email.svg b/frontend/public/_assets/icons/ultraviolet-email.svg
new file mode 100644
index 00000000..ccb35a50
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-email.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-enter-key.svg b/frontend/public/_assets/icons/ultraviolet-enter-key.svg
new file mode 100644
index 00000000..94666e42
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-enter-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-enter.svg b/frontend/public/_assets/icons/ultraviolet-enter.svg
new file mode 100644
index 00000000..d46d59c5
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-enter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-envelope.svg b/frontend/public/_assets/icons/ultraviolet-envelope.svg
new file mode 100644
index 00000000..93a65aaa
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-envelope.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-ethernet-off.svg b/frontend/public/_assets/icons/ultraviolet-ethernet-off.svg
new file mode 100644
index 00000000..c4dc8023
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-ethernet-off.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-exit.svg b/frontend/public/_assets/icons/ultraviolet-exit.svg
new file mode 100644
index 00000000..e2d259d7
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-exit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-expired.svg b/frontend/public/_assets/icons/ultraviolet-expired.svg
new file mode 100644
index 00000000..7088ca8a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-expired.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-explosion.svg b/frontend/public/_assets/icons/ultraviolet-explosion.svg
new file mode 100644
index 00000000..db8b58d2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-explosion.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-external-link.svg b/frontend/public/_assets/icons/ultraviolet-external-link.svg
new file mode 100644
index 00000000..d763104a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-external-link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-facebook.svg b/frontend/public/_assets/icons/ultraviolet-facebook.svg
new file mode 100644
index 00000000..d9aad53c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-facebook.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-file-submodule.svg b/frontend/public/_assets/icons/ultraviolet-file-submodule.svg
new file mode 100644
index 00000000..f2b4d87f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-file-submodule.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-fill-color.svg b/frontend/public/_assets/icons/ultraviolet-fill-color.svg
new file mode 100644
index 00000000..1a84ac22
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-fill-color.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-filtration.svg b/frontend/public/_assets/icons/ultraviolet-filtration.svg
new file mode 100644
index 00000000..2f33d9ca
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-filtration.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-fingerprint-scan.svg b/frontend/public/_assets/icons/ultraviolet-fingerprint-scan.svg
new file mode 100644
index 00000000..16d33d1b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-fingerprint-scan.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-firewall.svg b/frontend/public/_assets/icons/ultraviolet-firewall.svg
new file mode 100644
index 00000000..fb665978
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-firewall.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-flag-filled.svg b/frontend/public/_assets/icons/ultraviolet-flag-filled.svg
new file mode 100644
index 00000000..ce3adc90
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-flag-filled.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-folder.svg b/frontend/public/_assets/icons/ultraviolet-folder.svg
new file mode 100644
index 00000000..5326534c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-fonts-app.svg b/frontend/public/_assets/icons/ultraviolet-fonts-app.svg
new file mode 100644
index 00000000..fa45e6bf
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-fonts-app.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-full-image.svg b/frontend/public/_assets/icons/ultraviolet-full-image.svg
new file mode 100644
index 00000000..c573a148
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-full-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-future.svg b/frontend/public/_assets/icons/ultraviolet-future.svg
new file mode 100644
index 00000000..f0b59b7e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-future.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-gender.svg b/frontend/public/_assets/icons/ultraviolet-gender.svg
new file mode 100644
index 00000000..b5b04f0a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-gender.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-genealogy.svg b/frontend/public/_assets/icons/ultraviolet-genealogy.svg
new file mode 100644
index 00000000..ca54032e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-genealogy.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-geography.svg b/frontend/public/_assets/icons/ultraviolet-geography.svg
new file mode 100644
index 00000000..3cee54e1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-geography.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-git.svg b/frontend/public/_assets/icons/ultraviolet-git.svg
new file mode 100644
index 00000000..2310ddb1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-git.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-github.svg b/frontend/public/_assets/icons/ultraviolet-github.svg
new file mode 100644
index 00000000..25ae025f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-github.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-gitlab.svg b/frontend/public/_assets/icons/ultraviolet-gitlab.svg
new file mode 100644
index 00000000..c0fdee5c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-gitlab.svg
@@ -0,0 +1,14 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-good-pincode.svg b/frontend/public/_assets/icons/ultraviolet-good-pincode.svg
new file mode 100644
index 00000000..04a4b2be
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-good-pincode.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-google-drive.svg b/frontend/public/_assets/icons/ultraviolet-google-drive.svg
new file mode 100644
index 00000000..63dd00a5
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-google-drive.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-google-maps.svg b/frontend/public/_assets/icons/ultraviolet-google-maps.svg
new file mode 100644
index 00000000..4ed2e247
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-google-maps.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-google-presentation.svg b/frontend/public/_assets/icons/ultraviolet-google-presentation.svg
new file mode 100644
index 00000000..5fb85f70
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-google-presentation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-google.svg b/frontend/public/_assets/icons/ultraviolet-google.svg
new file mode 100644
index 00000000..bac7ed4b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-google.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-grand-master-key.svg b/frontend/public/_assets/icons/ultraviolet-grand-master-key.svg
new file mode 100644
index 00000000..31bcefdc
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-grand-master-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-hdd.svg b/frontend/public/_assets/icons/ultraviolet-hdd.svg
new file mode 100644
index 00000000..9df08564
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-hdd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-heart-outline.svg b/frontend/public/_assets/icons/ultraviolet-heart-outline.svg
new file mode 100644
index 00000000..1fddb085
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-heart-outline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-hips.svg b/frontend/public/_assets/icons/ultraviolet-hips.svg
new file mode 100644
index 00000000..b541fe5f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-hips.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-historical.svg b/frontend/public/_assets/icons/ultraviolet-historical.svg
new file mode 100644
index 00000000..48b04f0c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-historical.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-home.svg b/frontend/public/_assets/icons/ultraviolet-home.svg
new file mode 100644
index 00000000..71f5b710
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-home.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-html.svg b/frontend/public/_assets/icons/ultraviolet-html.svg
new file mode 100644
index 00000000..5d080f4a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-html.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-image.svg b/frontend/public/_assets/icons/ultraviolet-image.svg
new file mode 100644
index 00000000..c573a148
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-image.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-inbox.svg b/frontend/public/_assets/icons/ultraviolet-inbox.svg
new file mode 100644
index 00000000..de260513
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-inbox.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-index.svg b/frontend/public/_assets/icons/ultraviolet-index.svg
new file mode 100644
index 00000000..3fb06c2f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-index.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-info-popup.svg b/frontend/public/_assets/icons/ultraviolet-info-popup.svg
new file mode 100644
index 00000000..959d4c39
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-info-popup.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-information.svg b/frontend/public/_assets/icons/ultraviolet-information.svg
new file mode 100644
index 00000000..9e6920ff
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-information.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-internet.svg b/frontend/public/_assets/icons/ultraviolet-internet.svg
new file mode 100644
index 00000000..c31e8b61
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-internet.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-join.svg b/frontend/public/_assets/icons/ultraviolet-join.svg
new file mode 100644
index 00000000..8f49222b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-join.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-key.svg b/frontend/public/_assets/icons/ultraviolet-key.svg
new file mode 100644
index 00000000..3fa8b038
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-key.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-keycloak.svg b/frontend/public/_assets/icons/ultraviolet-keycloak.svg
new file mode 100644
index 00000000..eaa496c8
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-keycloak.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-light-on.svg b/frontend/public/_assets/icons/ultraviolet-light-on.svg
new file mode 100644
index 00000000..6a9270be
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-light-on.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-lightning-bolt.svg b/frontend/public/_assets/icons/ultraviolet-lightning-bolt.svg
new file mode 100644
index 00000000..48591199
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-lightning-bolt.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-link.svg b/frontend/public/_assets/icons/ultraviolet-link.svg
new file mode 100644
index 00000000..eea34219
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-link.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-linux.svg b/frontend/public/_assets/icons/ultraviolet-linux.svg
new file mode 100644
index 00000000..b2f09092
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-linux.svg
@@ -0,0 +1,17 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-lock.svg b/frontend/public/_assets/icons/ultraviolet-lock.svg
new file mode 100644
index 00000000..363d1f2d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-lock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-lowercase.svg b/frontend/public/_assets/icons/ultraviolet-lowercase.svg
new file mode 100644
index 00000000..a0e4ef75
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-lowercase.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-markdown.svg b/frontend/public/_assets/icons/ultraviolet-markdown.svg
new file mode 100644
index 00000000..17b1c789
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-markdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-matches.svg b/frontend/public/_assets/icons/ultraviolet-matches.svg
new file mode 100644
index 00000000..25a23f83
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-matches.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-maximize-window.svg b/frontend/public/_assets/icons/ultraviolet-maximize-window.svg
new file mode 100644
index 00000000..a11efd32
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-maximize-window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-memory-slot.svg b/frontend/public/_assets/icons/ultraviolet-memory-slot.svg
new file mode 100644
index 00000000..3f86b754
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-memory-slot.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-merge-files.svg b/frontend/public/_assets/icons/ultraviolet-merge-files.svg
new file mode 100644
index 00000000..3a86ba9a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-merge-files.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-microchip.svg b/frontend/public/_assets/icons/ultraviolet-microchip.svg
new file mode 100644
index 00000000..e687adba
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-microchip.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-microsoft.svg b/frontend/public/_assets/icons/ultraviolet-microsoft.svg
new file mode 100644
index 00000000..4cd92808
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-microsoft.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-module.svg b/frontend/public/_assets/icons/ultraviolet-module.svg
new file mode 100644
index 00000000..1c7f58b3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-module.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-nas.svg b/frontend/public/_assets/icons/ultraviolet-nas.svg
new file mode 100644
index 00000000..54ff72bf
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-nas.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg b/frontend/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg
new file mode 100644
index 00000000..3361cf63
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-navigation-toolbar-top.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-new-document.svg b/frontend/public/_assets/icons/ultraviolet-new-document.svg
new file mode 100644
index 00000000..cda328d3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-new-document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-new-job.svg b/frontend/public/_assets/icons/ultraviolet-new-job.svg
new file mode 100644
index 00000000..c8466eeb
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-new-job.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-no-access.svg b/frontend/public/_assets/icons/ultraviolet-no-access.svg
new file mode 100644
index 00000000..3d6943dc
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-no-access.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-nodejs.svg b/frontend/public/_assets/icons/ultraviolet-nodejs.svg
new file mode 100644
index 00000000..537674c2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-nodejs.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-oauth2.svg b/frontend/public/_assets/icons/ultraviolet-oauth2.svg
new file mode 100644
index 00000000..a85fa2f3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-oauth2.svg
@@ -0,0 +1,12 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-okta.svg b/frontend/public/_assets/icons/ultraviolet-okta.svg
new file mode 100644
index 00000000..868851d7
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-okta.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-open-box.svg b/frontend/public/_assets/icons/ultraviolet-open-box.svg
new file mode 100644
index 00000000..d1ea0696
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-open-box.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-opened-folder.svg b/frontend/public/_assets/icons/ultraviolet-opened-folder.svg
new file mode 100644
index 00000000..87558cb3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-opened-folder.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-openid.svg b/frontend/public/_assets/icons/ultraviolet-openid.svg
new file mode 100644
index 00000000..82bf0459
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-openid.svg
@@ -0,0 +1,11 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-password-reset.svg b/frontend/public/_assets/icons/ultraviolet-password-reset.svg
new file mode 100644
index 00000000..b8c862a2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-password-reset.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-password.svg b/frontend/public/_assets/icons/ultraviolet-password.svg
new file mode 100644
index 00000000..2e6ba2e9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-password.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-pen.svg b/frontend/public/_assets/icons/ultraviolet-pen.svg
new file mode 100644
index 00000000..d5fca6a2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-pen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-person.svg b/frontend/public/_assets/icons/ultraviolet-person.svg
new file mode 100644
index 00000000..6de77337
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-person.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-pin-pad.svg b/frontend/public/_assets/icons/ultraviolet-pin-pad.svg
new file mode 100644
index 00000000..85a41089
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-pin-pad.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-plugin.svg b/frontend/public/_assets/icons/ultraviolet-plugin.svg
new file mode 100644
index 00000000..a41d6b0e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-plugin.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-polyline.svg b/frontend/public/_assets/icons/ultraviolet-polyline.svg
new file mode 100644
index 00000000..aa81c445
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-polyline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-postgresql.svg b/frontend/public/_assets/icons/ultraviolet-postgresql.svg
new file mode 100644
index 00000000..f55ba9cd
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-postgresql.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-print.svg b/frontend/public/_assets/icons/ultraviolet-print.svg
new file mode 100644
index 00000000..bb9ff078
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-print.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-private.svg b/frontend/public/_assets/icons/ultraviolet-private.svg
new file mode 100644
index 00000000..87849d72
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-private.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-processor.svg b/frontend/public/_assets/icons/ultraviolet-processor.svg
new file mode 100644
index 00000000..3849028c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-processor.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-program.svg b/frontend/public/_assets/icons/ultraviolet-program.svg
new file mode 100644
index 00000000..a8862c97
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-program.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-quote-left.svg b/frontend/public/_assets/icons/ultraviolet-quote-left.svg
new file mode 100644
index 00000000..12d69226
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-quote-left.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-received.svg b/frontend/public/_assets/icons/ultraviolet-received.svg
new file mode 100644
index 00000000..466ca480
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-received.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-register.svg b/frontend/public/_assets/icons/ultraviolet-register.svg
new file mode 100644
index 00000000..c8cdf3f9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-register.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-rename.svg b/frontend/public/_assets/icons/ultraviolet-rename.svg
new file mode 100644
index 00000000..d9e4bcb7
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-rename.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-renew.svg b/frontend/public/_assets/icons/ultraviolet-renew.svg
new file mode 100644
index 00000000..6478f461
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-renew.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-rescan-document.svg b/frontend/public/_assets/icons/ultraviolet-rescan-document.svg
new file mode 100644
index 00000000..050153b5
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-rescan-document.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-resume-template.svg b/frontend/public/_assets/icons/ultraviolet-resume-template.svg
new file mode 100644
index 00000000..02ca1a48
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-resume-template.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-rfid-signal.svg b/frontend/public/_assets/icons/ultraviolet-rfid-signal.svg
new file mode 100644
index 00000000..853576cd
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-rfid-signal.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg b/frontend/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg
new file mode 100644
index 00000000..7371be28
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-right-navigation-toolbar.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-rocketchat.svg b/frontend/public/_assets/icons/ultraviolet-rocketchat.svg
new file mode 100644
index 00000000..2381cf64
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-rocketchat.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-rules.svg b/frontend/public/_assets/icons/ultraviolet-rules.svg
new file mode 100644
index 00000000..7b92730b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-rules.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-run-command.svg b/frontend/public/_assets/icons/ultraviolet-run-command.svg
new file mode 100644
index 00000000..78d1fcb9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-run-command.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-saml.svg b/frontend/public/_assets/icons/ultraviolet-saml.svg
new file mode 100644
index 00000000..69c8c806
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-saml.svg
@@ -0,0 +1,7 @@
+
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-scan-stock.svg b/frontend/public/_assets/icons/ultraviolet-scan-stock.svg
new file mode 100644
index 00000000..9de31b21
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-scan-stock.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-schedule.svg b/frontend/public/_assets/icons/ultraviolet-schedule.svg
new file mode 100644
index 00000000..0540c91a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-schedule.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-search.svg b/frontend/public/_assets/icons/ultraviolet-search.svg
new file mode 100644
index 00000000..40e6b346
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-search.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-secure.svg b/frontend/public/_assets/icons/ultraviolet-secure.svg
new file mode 100644
index 00000000..ebc533df
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-secure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-security-configuration.svg b/frontend/public/_assets/icons/ultraviolet-security-configuration.svg
new file mode 100644
index 00000000..df121138
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-security-configuration.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-security-ssl.svg b/frontend/public/_assets/icons/ultraviolet-security-ssl.svg
new file mode 100644
index 00000000..fcb674e1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-security-ssl.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-select-all.svg b/frontend/public/_assets/icons/ultraviolet-select-all.svg
new file mode 100644
index 00000000..975c028d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-select-all.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-server.svg b/frontend/public/_assets/icons/ultraviolet-server.svg
new file mode 100644
index 00000000..3b152857
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-server.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-share.svg b/frontend/public/_assets/icons/ultraviolet-share.svg
new file mode 100644
index 00000000..e6b99c91
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-share.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-shipping-container.svg b/frontend/public/_assets/icons/ultraviolet-shipping-container.svg
new file mode 100644
index 00000000..df36ecdd
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-shipping-container.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-shutdown.svg b/frontend/public/_assets/icons/ultraviolet-shutdown.svg
new file mode 100644
index 00000000..2c6cbdaa
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-shutdown.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-sigma.svg b/frontend/public/_assets/icons/ultraviolet-sigma.svg
new file mode 100644
index 00000000..df85a30b
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-sigma.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-skype.svg b/frontend/public/_assets/icons/ultraviolet-skype.svg
new file mode 100644
index 00000000..f0574f15
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-skype.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-slack.svg b/frontend/public/_assets/icons/ultraviolet-slack.svg
new file mode 100644
index 00000000..a441c3ab
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-slack.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-small-icons.svg b/frontend/public/_assets/icons/ultraviolet-small-icons.svg
new file mode 100644
index 00000000..10ff0d4e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-small-icons.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-sort-by-follow-up-date.svg b/frontend/public/_assets/icons/ultraviolet-sort-by-follow-up-date.svg
new file mode 100644
index 00000000..2211d081
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-sort-by-follow-up-date.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-spring.svg b/frontend/public/_assets/icons/ultraviolet-spring.svg
new file mode 100644
index 00000000..e8910175
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-spring.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-ssd.svg b/frontend/public/_assets/icons/ultraviolet-ssd.svg
new file mode 100644
index 00000000..62f41e23
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-ssd.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-star-half-empty.svg b/frontend/public/_assets/icons/ultraviolet-star-half-empty.svg
new file mode 100644
index 00000000..adb87e08
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-star-half-empty.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-starfish.svg b/frontend/public/_assets/icons/ultraviolet-starfish.svg
new file mode 100644
index 00000000..356306a6
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-starfish.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-subtitles.svg b/frontend/public/_assets/icons/ultraviolet-subtitles.svg
new file mode 100644
index 00000000..f2b6543f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-subtitles.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-summertime.svg b/frontend/public/_assets/icons/ultraviolet-summertime.svg
new file mode 100644
index 00000000..1a44be56
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-summertime.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-swipe-right.svg b/frontend/public/_assets/icons/ultraviolet-swipe-right.svg
new file mode 100644
index 00000000..85cbab6f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-swipe-right.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-symlink-directory.svg b/frontend/public/_assets/icons/ultraviolet-symlink-directory.svg
new file mode 100644
index 00000000..a0ca1cec
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-symlink-directory.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-synchronize.svg b/frontend/public/_assets/icons/ultraviolet-synchronize.svg
new file mode 100644
index 00000000..8d3dd2db
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-synchronize.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-team.svg b/frontend/public/_assets/icons/ultraviolet-team.svg
new file mode 100644
index 00000000..9c4a0b26
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-team.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-test-account.svg b/frontend/public/_assets/icons/ultraviolet-test-account.svg
new file mode 100644
index 00000000..a5a30015
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-test-account.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-test-passed.svg b/frontend/public/_assets/icons/ultraviolet-test-passed.svg
new file mode 100644
index 00000000..08ec3a5d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-test-passed.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-this-way-up.svg b/frontend/public/_assets/icons/ultraviolet-this-way-up.svg
new file mode 100644
index 00000000..c2848e21
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-this-way-up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-ticket.svg b/frontend/public/_assets/icons/ultraviolet-ticket.svg
new file mode 100644
index 00000000..53c95eb0
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-ticket.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-timer.svg b/frontend/public/_assets/icons/ultraviolet-timer.svg
new file mode 100644
index 00000000..6f7d514a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-timer.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-timezone.svg b/frontend/public/_assets/icons/ultraviolet-timezone.svg
new file mode 100644
index 00000000..daece1e1
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-timezone.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-todo-list.svg b/frontend/public/_assets/icons/ultraviolet-todo-list.svg
new file mode 100644
index 00000000..a6ed7200
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-todo-list.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-translation.svg b/frontend/public/_assets/icons/ultraviolet-translation.svg
new file mode 100644
index 00000000..80051f10
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-translation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-trash.svg b/frontend/public/_assets/icons/ultraviolet-trash.svg
new file mode 100644
index 00000000..9b26a12d
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-trash.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-tree-structure.svg b/frontend/public/_assets/icons/ultraviolet-tree-structure.svg
new file mode 100644
index 00000000..4548fd4e
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-tree-structure.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-tune.svg b/frontend/public/_assets/icons/ultraviolet-tune.svg
new file mode 100644
index 00000000..8fd6f1e2
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-tune.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-twitch.svg b/frontend/public/_assets/icons/ultraviolet-twitch.svg
new file mode 100644
index 00000000..689aa92a
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-twitch.svg
@@ -0,0 +1,9 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-typewriter-with-paper.svg b/frontend/public/_assets/icons/ultraviolet-typewriter-with-paper.svg
new file mode 100644
index 00000000..6c21db45
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-typewriter-with-paper.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-typography.svg b/frontend/public/_assets/icons/ultraviolet-typography.svg
new file mode 100644
index 00000000..8c7d423f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-typography.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-ubuntu.svg b/frontend/public/_assets/icons/ultraviolet-ubuntu.svg
new file mode 100644
index 00000000..5bc519a4
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-ubuntu.svg
@@ -0,0 +1,21 @@
+
+
diff --git a/frontend/public/_assets/icons/ultraviolet-underline.svg b/frontend/public/_assets/icons/ultraviolet-underline.svg
new file mode 100644
index 00000000..1f3fe0c0
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-underline.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-unfriend.svg b/frontend/public/_assets/icons/ultraviolet-unfriend.svg
new file mode 100644
index 00000000..13c1f223
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-unfriend.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-unit.svg b/frontend/public/_assets/icons/ultraviolet-unit.svg
new file mode 100644
index 00000000..20e3c8cb
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-unit.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-unknown-status.svg b/frontend/public/_assets/icons/ultraviolet-unknown-status.svg
new file mode 100644
index 00000000..56afba76
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-unknown-status.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-upload-to-ftp.svg b/frontend/public/_assets/icons/ultraviolet-upload-to-ftp.svg
new file mode 100644
index 00000000..bf5a810f
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-upload-to-ftp.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg b/frontend/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg
new file mode 100644
index 00000000..3cdfa9c9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-upload-to-the-cloud.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-user-groups.svg b/frontend/public/_assets/icons/ultraviolet-user-groups.svg
new file mode 100644
index 00000000..0a87dbcb
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-user-groups.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg b/frontend/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg
new file mode 100644
index 00000000..289c3a9c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-user-typing-using-typewriter.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-validation.svg b/frontend/public/_assets/icons/ultraviolet-validation.svg
new file mode 100644
index 00000000..bc9f1a2c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-validation.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-visualy-impaired.svg b/frontend/public/_assets/icons/ultraviolet-visualy-impaired.svg
new file mode 100644
index 00000000..28778a94
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-visualy-impaired.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-washing-machine.svg b/frontend/public/_assets/icons/ultraviolet-washing-machine.svg
new file mode 100644
index 00000000..c8cfdaa3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-washing-machine.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-web-design.svg b/frontend/public/_assets/icons/ultraviolet-web-design.svg
new file mode 100644
index 00000000..6e598c60
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-web-design.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-website.svg b/frontend/public/_assets/icons/ultraviolet-website.svg
new file mode 100644
index 00000000..4c4fbac3
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-website.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-widescreen.svg b/frontend/public/_assets/icons/ultraviolet-widescreen.svg
new file mode 100644
index 00000000..c5650fb9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-widescreen.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-width.svg b/frontend/public/_assets/icons/ultraviolet-width.svg
new file mode 100644
index 00000000..a92fbec9
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-width.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-windows8.svg b/frontend/public/_assets/icons/ultraviolet-windows8.svg
new file mode 100644
index 00000000..aea92c65
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-windows8.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/icons/ultraviolet-workflow.svg b/frontend/public/_assets/icons/ultraviolet-workflow.svg
new file mode 100644
index 00000000..deae597c
--- /dev/null
+++ b/frontend/public/_assets/icons/ultraviolet-workflow.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/fileman-failed.svg b/frontend/public/_assets/illustrations/fileman-failed.svg
new file mode 100644
index 00000000..0dc66200
--- /dev/null
+++ b/frontend/public/_assets/illustrations/fileman-failed.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/frontend/public/_assets/illustrations/fileman-page.svg b/frontend/public/_assets/illustrations/fileman-page.svg
new file mode 100644
index 00000000..9c83d4f5
--- /dev/null
+++ b/frontend/public/_assets/illustrations/fileman-page.svg
@@ -0,0 +1,13 @@
+
+
+
diff --git a/frontend/public/_assets/illustrations/fileman-pending.svg b/frontend/public/_assets/illustrations/fileman-pending.svg
new file mode 100644
index 00000000..69df0e58
--- /dev/null
+++ b/frontend/public/_assets/illustrations/fileman-pending.svg
@@ -0,0 +1,20 @@
+
+
+
diff --git a/frontend/public/_assets/illustrations/undraw_file_searching.svg b/frontend/public/_assets/illustrations/undraw_file_searching.svg
new file mode 100644
index 00000000..0cd2a0a9
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_file_searching.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_floating.svg b/frontend/public/_assets/illustrations/undraw_floating.svg
new file mode 100644
index 00000000..d5aabe76
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_floating.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_going_up.svg b/frontend/public/_assets/illustrations/undraw_going_up.svg
new file mode 100644
index 00000000..b9dce042
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_going_up.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_icon_design.svg b/frontend/public/_assets/illustrations/undraw_icon_design.svg
new file mode 100644
index 00000000..2d5fdbbd
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_icon_design.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_settings.svg b/frontend/public/_assets/illustrations/undraw_settings.svg
new file mode 100644
index 00000000..2280f9af
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_upload.svg b/frontend/public/_assets/illustrations/undraw_upload.svg
new file mode 100644
index 00000000..b54240d4
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_upload.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_walking_outside.svg b/frontend/public/_assets/illustrations/undraw_walking_outside.svg
new file mode 100644
index 00000000..72e42f2e
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_walking_outside.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/illustrations/undraw_world.svg b/frontend/public/_assets/illustrations/undraw_world.svg
new file mode 100644
index 00000000..d7b232a1
--- /dev/null
+++ b/frontend/public/_assets/illustrations/undraw_world.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/frontend/public/_assets/logo-wikijs-full.svg b/frontend/public/_assets/logo-wikijs-full.svg
new file mode 100644
index 00000000..1e8b120f
--- /dev/null
+++ b/frontend/public/_assets/logo-wikijs-full.svg
@@ -0,0 +1,159 @@
+
+
+
diff --git a/frontend/public/_assets/logo-wikijs.svg b/frontend/public/_assets/logo-wikijs.svg
new file mode 100644
index 00000000..52c4a790
--- /dev/null
+++ b/frontend/public/_assets/logo-wikijs.svg
@@ -0,0 +1,119 @@
+
+
+
diff --git a/frontend/public/_assets/storage/azure.jpg b/frontend/public/_assets/storage/azure.jpg
new file mode 100644
index 00000000..84c73ea0
Binary files /dev/null and b/frontend/public/_assets/storage/azure.jpg differ
diff --git a/frontend/public/_assets/storage/database.jpg b/frontend/public/_assets/storage/database.jpg
new file mode 100644
index 00000000..e695a144
Binary files /dev/null and b/frontend/public/_assets/storage/database.jpg differ
diff --git a/frontend/public/_assets/storage/disk.jpg b/frontend/public/_assets/storage/disk.jpg
new file mode 100644
index 00000000..09c9b4a0
Binary files /dev/null and b/frontend/public/_assets/storage/disk.jpg differ
diff --git a/frontend/public/_assets/storage/gcs.jpg b/frontend/public/_assets/storage/gcs.jpg
new file mode 100644
index 00000000..7871d509
Binary files /dev/null and b/frontend/public/_assets/storage/gcs.jpg differ
diff --git a/frontend/public/_assets/storage/git.jpg b/frontend/public/_assets/storage/git.jpg
new file mode 100644
index 00000000..214b49c8
Binary files /dev/null and b/frontend/public/_assets/storage/git.jpg differ
diff --git a/frontend/public/_assets/storage/github.jpg b/frontend/public/_assets/storage/github.jpg
new file mode 100644
index 00000000..69754b3f
Binary files /dev/null and b/frontend/public/_assets/storage/github.jpg differ
diff --git a/frontend/public/_assets/storage/s3.jpg b/frontend/public/_assets/storage/s3.jpg
new file mode 100644
index 00000000..3d599e59
Binary files /dev/null and b/frontend/public/_assets/storage/s3.jpg differ
diff --git a/frontend/public/_assets/storage/ssh.jpg b/frontend/public/_assets/storage/ssh.jpg
new file mode 100644
index 00000000..6269ce88
Binary files /dev/null and b/frontend/public/_assets/storage/ssh.jpg differ
diff --git a/frontend/public/favicon.ico b/frontend/public/favicon.ico
new file mode 100644
index 00000000..b69c4b6f
Binary files /dev/null and b/frontend/public/favicon.ico differ
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
new file mode 100644
index 00000000..925bcdfd
--- /dev/null
+++ b/frontend/src/App.vue
@@ -0,0 +1,185 @@
+
+router-view
+
+
+
diff --git a/frontend/src/assets/quasar-logo-vertical.svg b/frontend/src/assets/quasar-logo-vertical.svg
new file mode 100644
index 00000000..82108310
--- /dev/null
+++ b/frontend/src/assets/quasar-logo-vertical.svg
@@ -0,0 +1,15 @@
+
\ No newline at end of file
diff --git a/frontend/src/boot/.gitkeep b/frontend/src/boot/.gitkeep
new file mode 100644
index 00000000..e69de29b
diff --git a/frontend/src/boot/api.js b/frontend/src/boot/api.js
new file mode 100644
index 00000000..57d7fd0b
--- /dev/null
+++ b/frontend/src/boot/api.js
@@ -0,0 +1,55 @@
+import ky from 'ky'
+
+import { useUserStore } from '@/stores/user'
+
+export function initializeApi (store) {
+ const userStore = useUserStore(store)
+
+ let refreshPromise = null
+ let fetching = false
+
+ const client = ky.create({
+ prefixUrl: '/_api',
+ credentials: 'omit',
+ hooks: {
+ beforeRequest: [
+ async (request) => {
+ // -> Guest
+ if (!userStore.token) {
+ request.headers.set('Authorization', '')
+ return
+ }
+
+ // -> Refresh Token
+ if (!userStore.isTokenValid({ minutes: 1 })) {
+ if (!fetching) {
+ refreshPromise = new Promise((resolve, reject) => {
+ (async () => {
+ fetching = true
+ try {
+ await userStore.refreshToken()
+ resolve()
+ } catch (err) {
+ reject(err)
+ }
+ fetching = false
+ })()
+ })
+ } else {
+ // -> Another request is already executing, wait for it to complete
+ await refreshPromise
+ }
+ }
+
+ request.headers.set('Authorization', userStore.token ? `Bearer ${userStore.token}` : '')
+ }
+ ]
+ }
+ })
+
+ if (import.meta.env.SSR) {
+ global.API_CLIENT = client
+ } else {
+ window.API_CLIENT = client
+ }
+}
diff --git a/frontend/src/boot/components.js b/frontend/src/boot/components.js
new file mode 100644
index 00000000..6204e731
--- /dev/null
+++ b/frontend/src/boot/components.js
@@ -0,0 +1,11 @@
+import BlueprintIcon from '@/components/BlueprintIcon.vue'
+import StatusLight from '@/components/StatusLight.vue'
+import LoadingGeneric from '@/components/LoadingGeneric.vue'
+import VNetworkGraph from 'v-network-graph'
+
+export function initializeComponents (app) {
+ app.component('BlueprintIcon', BlueprintIcon)
+ app.component('LoadingGeneric', LoadingGeneric)
+ app.component('StatusLight', StatusLight)
+ app.use(VNetworkGraph)
+}
diff --git a/frontend/src/boot/eventbus.js b/frontend/src/boot/eventbus.js
new file mode 100644
index 00000000..db06892b
--- /dev/null
+++ b/frontend/src/boot/eventbus.js
@@ -0,0 +1,11 @@
+import mitt from 'mitt'
+
+export function initializeEventBus () {
+ const emitter = mitt()
+
+ if (import.meta.env.SSR) {
+ global.EVENT_BUS = emitter
+ } else {
+ window.EVENT_BUS = emitter
+ }
+}
diff --git a/frontend/src/boot/externals.js b/frontend/src/boot/externals.js
new file mode 100644
index 00000000..09d8f8a1
--- /dev/null
+++ b/frontend/src/boot/externals.js
@@ -0,0 +1,21 @@
+import { usePageStore } from '@/stores/page'
+import { useSiteStore } from '@/stores/site'
+import { useUserStore } from '@/stores/user'
+
+export function initializeExternals (router, store) {
+ if (import.meta.env.SSR) {
+ global.WIKI_STATE = {
+ page: usePageStore(store),
+ site: useSiteStore(store),
+ user: useUserStore(store)
+ }
+ global.WIKI_ROUTER = router
+ } else {
+ window.WIKI_STATE = {
+ page: usePageStore(store),
+ site: useSiteStore(store),
+ user: useUserStore(store)
+ }
+ window.WIKI_ROUTER = router
+ }
+}
diff --git a/frontend/src/boot/i18n.js b/frontend/src/boot/i18n.js
new file mode 100644
index 00000000..d975d03c
--- /dev/null
+++ b/frontend/src/boot/i18n.js
@@ -0,0 +1,17 @@
+import { createI18n } from 'vue-i18n'
+import { useCommonStore } from '@/stores/common'
+
+export function initializeI18n (app, store) {
+ const commonStore = useCommonStore(store)
+
+ const i18n = createI18n({
+ legacy: false,
+ locale: commonStore.locale || 'en',
+ fallbackLocale: 'en',
+ fallbackWarn: false,
+ messages: {}
+ })
+
+ // Set i18n instance on app
+ app.use(i18n)
+}
diff --git a/frontend/src/boot/monaco.js b/frontend/src/boot/monaco.js
new file mode 100644
index 00000000..14093630
--- /dev/null
+++ b/frontend/src/boot/monaco.js
@@ -0,0 +1,23 @@
+import EditorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker'
+import JsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker'
+import CssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker'
+import HtmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker'
+import TsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker'
+
+self.MonacoEnvironment = {
+ getWorker (_, label) {
+ if (label === 'json') {
+ return new JsonWorker()
+ }
+ if (label === 'css' || label === 'scss' || label === 'less') {
+ return new CssWorker()
+ }
+ if (label === 'html' || label === 'handlebars' || label === 'razor') {
+ return new HtmlWorker()
+ }
+ if (label === 'typescript' || label === 'javascript') {
+ return new TsWorker()
+ }
+ return new EditorWorker()
+ }
+}
diff --git a/frontend/src/components/AccountMenu.vue b/frontend/src/components/AccountMenu.vue
new file mode 100644
index 00000000..32b572f8
--- /dev/null
+++ b/frontend/src/components/AccountMenu.vue
@@ -0,0 +1,55 @@
+
+q-btn.account-avbtn.q-ml-md(flat, round, dense, color='custom-color')
+ q-icon(
+ v-if='!userStore.authenticated || !userStore.hasAvatar'
+ name='las la-user-circle'
+ )
+ q-avatar(
+ v-else
+ size='32px'
+ )
+ img(:src='`/_user/` + userStore.id + `/avatar`')
+ q-menu.translucent-menu(auto-close)
+ q-card(flat, style='width: 300px;', :dark='false')
+ q-card-section(align='center')
+ .text-subtitle1.text-grey-7 {{userStore.name}}
+ .text-caption.text-grey-8 {{userStore.email}}
+ q-separator(:dark='false')
+ q-card-actions(align='center')
+ q-btn(
+ flat
+ :label='t(`common.header.profile`)'
+ icon='las la-user-alt'
+ color='primary'
+ to='/_profile'
+ no-caps
+ )
+ q-btn(flat
+ :label='t(`common.header.logout`)'
+ icon='las la-sign-out-alt'
+ color='red'
+ @click='userStore.logout()'
+ no-caps
+ )
+ q-tooltip {{ t('common.header.account') }}
+
+
+
+
+
diff --git a/frontend/src/components/ApiKeyCopyDialog.vue b/frontend/src/components/ApiKeyCopyDialog.vue
new file mode 100644
index 00000000..3cbb5cbe
--- /dev/null
+++ b/frontend/src/components/ApiKeyCopyDialog.vue
@@ -0,0 +1,63 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card(style='min-width: 600px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-key-2.svg', left, size='sm')
+ span {{t(`admin.api.copyKeyTitle`)}}
+ q-card-section.card-negative
+ i18n-t(tag='span', keypath='admin.api.newKeyCopyWarn', scope='global')
+ template(#bold)
+ strong {{t('admin.api.newKeyCopyWarnBold')}}
+ q-form.q-py-sm
+ q-item
+ blueprint-icon.self-start(icon='binary-file')
+ q-item-section
+ q-input(
+ type='textarea'
+ outlined
+ :model-value='props.keyValue'
+ dense
+ hide-bottom-space
+ :label='t(`admin.api.key`)'
+ :aria-label='t(`admin.api.key`)'
+ autofocus
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn(
+ unelevated
+ :label='t(`common.actions.close`)'
+ color='primary'
+ padding='xs md'
+ @click='onDialogOK'
+ )
+
+
+
diff --git a/frontend/src/components/ApiKeyCreateDialog.vue b/frontend/src/components/ApiKeyCreateDialog.vue
new file mode 100644
index 00000000..ccc331bc
--- /dev/null
+++ b/frontend/src/components/ApiKeyCreateDialog.vue
@@ -0,0 +1,253 @@
+
+q-dialog(ref='dialogRef', @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.api.newKeyTitle`)}}
+ q-form.q-py-sm(ref='createKeyForm', @submit='create')
+ q-item
+ blueprint-icon.self-start(icon='grand-master-key')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.keyName'
+ dense
+ :rules='keyNameValidation'
+ hide-bottom-space
+ :label='t(`admin.api.newKeyName`)'
+ :aria-label='t(`admin.api.newKeyName`)'
+ :hint='t(`admin.api.newKeyNameHint`)'
+ lazy-rules='ondemand'
+ autofocus
+ ref='iptName'
+ )
+ q-item
+ blueprint-icon.self-start(icon='schedule')
+ q-item-section
+ q-select(
+ outlined
+ :options='expirations'
+ v-model='state.keyExpiration'
+ multiple
+ map-options
+ option-value='value'
+ option-label='text'
+ emit-value
+ options-dense
+ dense
+ hide-bottom-space
+ :label='t(`admin.api.newKeyExpiration`)'
+ :aria-label='t(`admin.api.newKeyExpiration`)'
+ :hint='t(`admin.api.newKeyExpirationHint`)'
+ )
+ q-item
+ blueprint-icon.self-start(icon='access')
+ q-item-section
+ q-select(
+ outlined
+ :options='state.groups'
+ v-model='state.keyGroups'
+ multiple
+ map-options
+ emit-value
+ option-value='id'
+ option-label='name'
+ options-dense
+ dense
+ :rules='keyGroupsValidation'
+ hide-bottom-space
+ :label='t(`admin.api.permissionGroups`)'
+ :aria-label='t(`admin.api.permissionGroups`)'
+ :hint='t(`admin.api.newKeyGroupHint`)'
+ lazy-rules='ondemand'
+ :loading='state.loadingGroups'
+ )
+ template(v-slot:selected)
+ .text-caption(v-if='state.keyGroups.length > 1')
+ i18n-t(keypath='admin.api.groupsSelected', scope='global')
+ template(#count)
+ strong {{ state.keyGroups.length }}
+ .text-caption(v-else-if='state.keyGroups.length === 1')
+ i18n-t(keypath='admin.api.groupSelected', scope='global')
+ template(#group)
+ strong {{ selectedGroupName }}
+ span(v-else)
+ template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ )
+ 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-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.loading > 0'
+ )
+
+
+
diff --git a/frontend/src/components/ApiKeyRevokeDialog.vue b/frontend/src/components/ApiKeyRevokeDialog.vue
new file mode 100644
index 00000000..77e6144d
--- /dev/null
+++ b/frontend/src/components/ApiKeyRevokeDialog.vue
@@ -0,0 +1,104 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-unavailable.svg', left, size='sm')
+ span {{t(`admin.api.revokeConfirm`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='admin.api.revokeConfirmText')
+ template(#name)
+ strong {{apiKey.name}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`admin.api.revoke`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/AssetDeleteDialog.vue b/frontend/src/components/AssetDeleteDialog.vue
new file mode 100644
index 00000000..974cddb5
--- /dev/null
+++ b/frontend/src/components/AssetDeleteDialog.vue
@@ -0,0 +1,109 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 550px; max-width: 850px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{ t(`fileman.assetDelete`) }}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='fileman.assetDeleteConfirm')
+ template(#name)
+ strong {{assetName}}
+ .text-caption.text-grey.q-mt-sm {{ t('fileman.assetDeleteId', { id: assetId }) }}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/AssetRenameDialog.vue b/frontend/src/components/AssetRenameDialog.vue
new file mode 100644
index 00000000..95b71603
--- /dev/null
+++ b/frontend/src/components/AssetRenameDialog.vue
@@ -0,0 +1,165 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
+ span {{ t(`fileman.assetRename`) }}
+ q-form.q-py-sm(@submit='rename')
+ q-item
+ blueprint-icon.self-start(icon='image')
+ q-item-section
+ q-input(
+ autofocus
+ outlined
+ v-model='state.path'
+ dense
+ hide-bottom-space
+ :label='t(`fileman.assetFileName`)'
+ :aria-label='t(`fileman.assetFileName`)'
+ :hint='t(`fileman.assetFileNameHint`)'
+ lazy-rules='ondemand'
+ @keyup.enter='rename'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.rename`)'
+ color='primary'
+ padding='xs md'
+ @click='rename'
+ :loading='state.loading > 0'
+ )
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/AuthLoginPanel.vue b/frontend/src/components/AuthLoginPanel.vue
new file mode 100644
index 00000000..9c2e750a
--- /dev/null
+++ b/frontend/src/components/AuthLoginPanel.vue
@@ -0,0 +1,1027 @@
+
+.auth-login
+ //- -----------------------------------------------------
+ //- LOGIN SCREEN
+ //- -----------------------------------------------------
+ template(v-if='state.screen === `login`')
+ template(v-if='state.strategies?.length > 1')
+ p {{t('auth.selectAuthProvider')}}
+ .auth-strategies.q-mb-md
+ q-btn(
+ v-for='str of state.strategies'
+ :label='str.activeStrategy.displayName'
+ :icon='`img:` + str.activeStrategy.strategy.icon'
+ push
+ no-caps
+ :color='str.id === state.selectedStrategyId ? `primary` : ($q.dark.isActive ? `blue-grey-9` : `grey-1`)'
+ :text-color='str.id === state.selectedStrategyId || $q.dark.isActive ? `white` : `blue-grey-9`'
+ @click='state.selectedStrategyId = str.id'
+ )
+ q-form(ref='loginForm', @submit='login')
+ q-input(
+ ref='loginEmailIpt'
+ v-model='state.username'
+ autofocus
+ outlined
+ :label='t(`auth.fields.` + (selectedStrategy.activeStrategy?.strategy?.usernameType ?? `email`))'
+ :rules='selectedStrategy.activeStrategy?.strategy?.usernameType === `username` ? loginUsernameValidation : userEmailValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ :autocomplete='selectedStrategy.activeStrategy?.strategy?.usernameType ?? `email`'
+ )
+ template(#prepend)
+ i.las.la-user
+ q-input.q-mt-sm(
+ v-model='state.password'
+ outlined
+ :label='t(`auth.fields.password`)'
+ :rules='loginPasswordValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ type='password'
+ autocomplete='current-password'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-btn.full-width.q-mt-sm(
+ type='submit'
+ push
+ color='primary'
+ :label='t(`auth.actions.login`)'
+ no-caps
+ icon='las la-sign-in-alt'
+ )
+ template(v-if='canUsePasskeys')
+ q-separator.q-my-md
+ q-btn.acrylic-btn.full-width(
+ flat
+ color='primary'
+ :label='t(`auth.passkeys.signin`)'
+ no-caps
+ icon='las la-key'
+ @click='switchTo(`passkey`)'
+ )
+ template(v-if='selectedStrategy.activeStrategy?.strategy?.key === `local`')
+ q-separator.q-my-md
+ q-btn.acrylic-btn.full-width.q-mb-sm(
+ v-if='selectedStrategy.activeStrategy.registration'
+ flat
+ color='primary'
+ :label='t(`auth.switchToRegister.link`)'
+ no-caps
+ icon='las la-user-plus'
+ @click='switchTo(`register`)'
+ )
+ q-btn.acrylic-btn.full-width(
+ flat
+ color='primary'
+ :label='t(`auth.forgotPasswordLink`)'
+ no-caps
+ icon='las la-life-ring'
+ @click='switchTo(`forgot`)'
+ )
+
+ //- -----------------------------------------------------
+ //- PASSKEY LOGIN SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `passkey`')
+ p {{t('auth.passkeys.signinHint')}}
+ q-form(ref='passkeyForm', @submit='loginWithPasskey')
+ q-input(
+ ref='passkeyEmailIpt'
+ v-model='state.username'
+ outlined
+ hide-bottom-space
+ :label='t(`auth.fields.email`)'
+ autocomplete='webauthn'
+ )
+ template(#prepend)
+ i.las.la-envelope
+ q-btn.full-width.q-mt-sm(
+ type='submit'
+ push
+ color='primary'
+ :label='t(`auth.actions.login`)'
+ no-caps
+ icon='las la-key'
+ )
+ q-separator.q-my-md
+ q-btn.acrylic-btn.full-width(
+ flat
+ color='primary'
+ :label='t(`auth.forgotPasswordCancel`)'
+ no-caps
+ icon='las la-arrow-circle-left'
+ @click='switchTo(`login`)'
+ )
+
+ //- -----------------------------------------------------
+ //- FORGOT PASSWORD SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `forgot`')
+ p {{t('auth.forgotPasswordSubtitle')}}
+ q-form(ref='forgotForm', @submit='forgotPassword')
+ q-input(
+ ref='forgotEmailIpt'
+ v-model='state.username'
+ outlined
+ :rules='userEmailValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ :label='t(`auth.fields.email`)'
+ autocomplete='email'
+ )
+ template(#prepend)
+ i.las.la-envelope
+ q-btn.full-width.q-mt-sm(
+ type='submit'
+ push
+ color='primary'
+ :label='t(`auth.sendResetPassword`)'
+ no-caps
+ icon='las la-life-ring'
+ )
+ q-separator.q-my-md
+ q-btn.acrylic-btn.full-width(
+ flat
+ color='primary'
+ :label='t(`auth.forgotPasswordCancel`)'
+ no-caps
+ icon='las la-arrow-circle-left'
+ @click='switchTo(`login`)'
+ )
+
+ //- -----------------------------------------------------
+ //- REGISTER SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `register`')
+ p {{t('auth.registerSubTitle')}}
+ q-form(ref='registerForm', @submit='register')
+ q-input(
+ ref='registerNameIpt'
+ v-model='state.newName'
+ outlined
+ :rules='userNameValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ :label='t(`auth.fields.name`)'
+ autocomplete='name'
+ )
+ template(#prepend)
+ i.las.la-user-circle
+ q-input.q-mt-sm(
+ type='email'
+ v-model='state.newEmail'
+ outlined
+ :rules='userEmailValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ :label='t(`auth.fields.email`)'
+ autocomplete='email'
+ )
+ template(#prepend)
+ i.las.la-envelope
+ q-input.q-mt-sm(
+ v-model='state.newPassword'
+ outlined
+ :label='t(`auth.fields.password`)'
+ type='password'
+ autocomplete='new-password'
+ :rules='userPasswordValidation'
+ hide-bottom-space
+ lazy-rules='ondemand'
+ )
+ template(#append)
+ q-badge(
+ v-show='state.newPassword'
+ :color='passwordStrength.color'
+ :label='passwordStrength.label'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-input.q-mt-sm(
+ v-model='state.newPasswordVerify'
+ outlined
+ :label='t(`auth.fields.verifyPassword`)'
+ type='password'
+ autocomplete='new-password'
+ :rules='userPasswordVerifyValidation'
+ hide-bottom-space
+ lazy-rules='ondemand'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-btn.full-width.q-mt-sm(
+ type='submit'
+ push
+ color='primary'
+ :label='t(`auth.actions.register`)'
+ no-caps
+ icon='las la-user-plus'
+ )
+ q-separator.q-my-md
+ q-btn.acrylic-btn.full-width(
+ flat
+ color='primary'
+ :label='t(`auth.switchToLogin.link`)'
+ no-caps
+ icon='las la-arrow-circle-left'
+ @click='switchTo(`login`)'
+ )
+
+ //- -----------------------------------------------------
+ //- CHANGE PASSWORD SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `changePwd`')
+ p(v-if='state.continuationToken') {{t('auth.changePwd.instructions')}}
+ q-form(ref='changePwdForm', @submit='changePwd')
+ q-input(
+ v-if='!state.continuationToken'
+ ref='changePwdCurrentIpt'
+ v-model='state.password'
+ outlined
+ type='password'
+ :rules='loginPasswordValidation'
+ lazy-rules='ondemand'
+ hide-bottom-space
+ :label='t(`auth.changePwd.currentPassword`)'
+ autocomplete='password'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-input.q-mt-sm(
+ ref='changePwdNewPwdIpt'
+ v-model='state.newPassword'
+ outlined
+ :label='t(`auth.changePwd.newPassword`)'
+ type='password'
+ autocomplete='new-password'
+ :rules='userPasswordValidation'
+ hide-bottom-space
+ lazy-rules='ondemand'
+ )
+ template(#append)
+ q-badge(
+ v-show='state.newPassword'
+ :color='passwordStrength.color'
+ :label='passwordStrength.label'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-input.q-mt-sm(
+ v-model='state.newPasswordVerify'
+ outlined
+ :label='t(`auth.changePwd.newPasswordVerify`)'
+ type='password'
+ autocomplete='new-password'
+ :rules='userPasswordVerifyValidation'
+ hide-bottom-space
+ lazy-rules='ondemand'
+ )
+ template(#prepend)
+ i.las.la-key
+ q-btn.full-width.q-mt-sm(
+ type='submit'
+ push
+ color='primary'
+ :label='t(`auth.changePwd.proceed`)'
+ no-caps
+ icon='las la-sync-alt'
+ )
+ //- -----------------------------------------------------
+ //- TFA SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `tfa`')
+ p {{t('auth.tfa.subtitle')}}
+ v-otp-input(
+ v-model:value='state.securityCode'
+ :num-inputs='6'
+ :should-auto-focus='true'
+ input-classes='otp-input'
+ input-type='number'
+ separator=''
+ @on-complete='verifyTFA'
+ )
+ q-btn.full-width.q-mt-md(
+ push
+ color='primary'
+ :label='t(`auth.tfa.verifyToken`)'
+ no-caps
+ icon='las la-sign-in-alt'
+ @click='verifyTFA'
+ )
+ //- -----------------------------------------------------
+ //- TFA SETUP SCREEN
+ //- -----------------------------------------------------
+ template(v-else-if='state.screen === `tfasetup`')
+ p {{t('auth.tfaSetupTitle')}}
+ p {{t('auth.tfaSetupInstrFirst')}}
+ div(style='justify-content: center; display: flex;')
+ div(v-html='state.tfaQRImage', style='width: 200px;')
+ p.q-mt-sm {{t('auth.tfaSetupInstrSecond')}}
+ v-otp-input(
+ v-model:value='state.securityCode'
+ :num-inputs='6'
+ :should-auto-focus='true'
+ input-classes='otp-input'
+ input-type='number'
+ separator=''
+ )
+ q-btn.full-width.q-mt-md(
+ push
+ color='primary'
+ :label='t(`auth.tfa.verifyToken`)'
+ no-caps
+ icon='las la-sign-in-alt'
+ @click='finishSetupTFA'
+ )
+
+
+
+
+
diff --git a/frontend/src/components/BlockVideoPlayer.vue b/frontend/src/components/BlockVideoPlayer.vue
new file mode 100644
index 00000000..e3a58c97
--- /dev/null
+++ b/frontend/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/frontend/src/components/BlueprintIcon.vue b/frontend/src/components/BlueprintIcon.vue
new file mode 100644
index 00000000..1653d981
--- /dev/null
+++ b/frontend/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='props.hueRotate !== 0 ? `filter: hue-rotate(` + props.hueRotate + `deg)` : ``'
+ )
+ q-badge(
+ v-if='indicatorDot'
+ rounded
+ :color='indicatorDot'
+ floating
+ )
+ q-tooltip(v-if='props.indicatorText') {{props.indicatorText}}
+ q-icon(
+ v-if='!textMode'
+ :name='`img:/_assets/icons/ultraviolet-` + icon + `.svg`'
+ size='sm'
+ )
+ span.text-uppercase(v-else) {{props.text}}
+
+
+
diff --git a/frontend/src/components/ChangePwdDialog.vue b/frontend/src/components/ChangePwdDialog.vue
new file mode 100644
index 00000000..784f8b0a
--- /dev/null
+++ b/frontend/src/components/ChangePwdDialog.vue
@@ -0,0 +1,248 @@
+
+q-dialog(ref='dialogRef', @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='lock')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.currentPassword'
+ dense
+ :rules='currentPasswordValidation'
+ hide-bottom-space
+ :label='t(`auth.changePwd.currentPassword`)'
+ :aria-label='t(`auth.changePwd.currentPassword`)'
+ lazy-rules='ondemand'
+ autofocus
+ )
+ q-item
+ blueprint-icon(icon='password')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.newPassword'
+ dense
+ :rules='newPasswordValidation'
+ hide-bottom-space
+ :label='t(`auth.changePwd.newPassword`)'
+ :aria-label='t(`auth.changePwd.newPassword`)'
+ 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='good-pincode')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.verifyPassword'
+ dense
+ :rules='verifyPasswordValidation'
+ hide-bottom-space
+ :label='t(`auth.changePwd.newPasswordVerify`)'
+ :aria-label='t(`auth.changePwd.newPasswordVerify`)'
+ 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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.update`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/CheckUpdateDialog.vue b/frontend/src/components/CheckUpdateDialog.vue
new file mode 100644
index 00000000..07f5f165
--- /dev/null
+++ b/frontend/src/components/CheckUpdateDialog.vue
@@ -0,0 +1,128 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-downloading-updates.svg', left, size='sm')
+ span {{t(`admin.system.checkingForUpdates`)}}
+ q-card-section
+ .q-pa-md.text-center
+ img(src='/_assets/illustrations/undraw_going_up.svg', style='width: 150px;')
+ template(v-if='state.isLoading')
+ q-linear-progress(
+ indeterminate
+ size='lg'
+ rounded
+ )
+ .q-mt-sm.text-center.text-caption {{ $t('admin.system.fetchingLatestVersionInfo') }}
+ template(v-else)
+ .text-center
+ strong.text-positive(v-if='isLatest') {{ $t('admin.system.runningLatestVersion') }}
+ strong.text-pink(v-else) {{ $t('admin.system.newVersionAvailable') }}
+ .text-body2.q-mt-md Current: #[strong {{ state.current }}]
+ .text-body2 Latest: #[strong {{ state.latest }}]
+ .text-body2 Release Date: #[strong {{ state.latestDate }}]
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='state.isLoading ? t(`common.actions.cancel`) : t(`common.actions.close`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ v-if='state.canUpgrade'
+ unelevated
+ :label='t(`admin.system.upgrade`)'
+ color='primary'
+ padding='xs md'
+ @click='upgrade'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/EditorChannel.vue b/frontend/src/components/EditorChannel.vue
new file mode 100644
index 00000000..67769353
--- /dev/null
+++ b/frontend/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/frontend/src/components/EditorMarkdown.vue b/frontend/src/components/EditorMarkdown.vue
new file mode 100644
index 00000000..cbb7e6b8
--- /dev/null
+++ b/frontend/src/components/EditorMarkdown.vue
@@ -0,0 +1,913 @@
+
+.editor-markdown
+ .editor-markdown-main
+ .editor-markdown-sidebar
+ //--------------------------------------------------------
+ //- SIDE TOOLBAR
+ //--------------------------------------------------------
+ q-btn(
+ icon='mdi-link-variant-plus'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertLink') }}
+ q-btn(
+ icon='mdi-image-plus-outline'
+ padding='sm sm'
+ flat
+ )
+ q-menu(anchor='top right' self='top left')
+ q-list(separator, auto-close)
+ q-item(
+ clickable
+ @click='insertAssets'
+ )
+ q-item-section(side)
+ q-icon(name='las la-folder-open', color='positive')
+ q-item-section
+ q-item-label From File Manager...
+ q-item(
+ clickable
+ @click='getAssetFromClipboard'
+ v-close-popup
+ )
+ q-item-section(side)
+ q-icon(name='las la-clipboard', color='brown')
+ q-item-section
+ q-item-label From Clipboard...
+ q-item(
+ clickable
+ @click='notImplemented'
+ v-close-popup
+ )
+ q-item-section(side)
+ q-icon(name='las la-cloud-download-alt', color='blue')
+ q-item-section
+ q-item-label From Remote URL...
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertAssets') }}
+ q-btn(
+ icon='mdi-code-json'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertCodeBlock') }}
+ q-btn(
+ icon='mdi-table-large-plus'
+ padding='sm sm'
+ flat
+ @click='insertTable'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertTable') }}
+ q-btn(
+ icon='mdi-tab-plus'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertTabset') }}
+ q-btn(
+ icon='mdi-toy-brick-plus'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertBlock') }}
+ q-btn(
+ icon='mdi-chart-multiline'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertDiagram') }}
+ q-btn(
+ icon='mdi-book-plus'
+ padding='sm sm'
+ flat
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertFootnote') }}
+ q-btn(
+ icon='mdi-cookie-plus'
+ padding='sm sm'
+ @click='notImplemented'
+ flat
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertEmoji') }}
+ q-btn(
+ icon='mdi-line-scan'
+ padding='sm sm'
+ flat
+ @click='insertHorizontalBar'
+ )
+ q-tooltip(anchor='center right' self='center left') {{ t('editor.markup.insertHorizontalBar') }}
+ q-space
+ span.editor-markdown-type Markdown
+ .editor-markdown-mid
+ //--------------------------------------------------------
+ //- TOP TOOLBAR
+ //--------------------------------------------------------
+ .editor-markdown-toolbar
+ q-btn(
+ icon='mdi-format-bold'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: `**` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.bold') }}
+ q-btn(
+ icon='mdi-format-italic'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: `*` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.italic') }}
+ q-btn(
+ icon='mdi-format-strikethrough'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: `~~` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.strikethrough') }}
+ q-btn(
+ icon='mdi-format-header-pound'
+ padding='xs sm'
+ flat
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.header') }}
+ q-menu(auto-close)
+ q-list(separator)
+ q-item(
+ v-for='lvl in 6'
+ clickable
+ @click='setHeaderLine(lvl)'
+ )
+ q-item-section(side)
+ q-icon(:name='`mdi-format-header-` + lvl')
+ q-item-section {{ t('editor.markup.headerLevel', { level: lvl }) }}
+ q-btn(
+ icon='mdi-format-subscript'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: `~` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.subscript') }}
+ q-btn(
+ icon='mdi-format-superscript'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: `^` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.superscript') }}
+ q-btn(
+ icon='mdi-alpha-t-box-outline'
+ padding='xs sm'
+ flat
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.blockquoteAdmonitions') }}
+ q-menu(auto-close)
+ q-list(separator)
+ q-item(clickable, @click='insertBeforeEachLine({ content: `> `})')
+ q-item-section(side)
+ q-icon(name='mdi-format-quote-close')
+ q-item-section {{ t('editor.markup.blockquote') }}
+ q-item(clickable, @click='insertBeforeEachLine({ content: `> `, after: `{.is-info}`})')
+ q-item-section(side)
+ q-icon(name='mdi-information-box', color='blue-7')
+ q-item-section {{ t('editor.markup.admonitionInfo') }}
+ q-item(clickable, @click='insertBeforeEachLine({ content: `> `, after: `{.is-success}`})')
+ q-item-section(side)
+ q-icon(name='mdi-check-circle', color='positive')
+ q-item-section {{ t('editor.markup.admonitionSuccess') }}
+ q-item(clickable, @click='insertBeforeEachLine({ content: `> `, after: `{.is-warning}`})')
+ q-item-section(side)
+ q-icon(name='mdi-alert-box', color='orange')
+ q-item-section {{ t('editor.markup.admonitionWarning') }}
+ q-item(clickable, @click='insertBeforeEachLine({ content: `> `, after: `{.is-danger}`})')
+ q-item-section(side)
+ q-icon(name='mdi-close-box', color='negative')
+ q-item-section {{ t('editor.markup.admonitionDanger') }}
+ q-btn(
+ icon='mdi-format-list-bulleted'
+ padding='xs sm'
+ flat
+ @click='insertBeforeEachLine({ content: `- `})'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.unorderedList') }}
+ q-btn(
+ icon='mdi-format-list-numbered'
+ padding='xs sm'
+ flat
+ @click='insertBeforeEachLine({ content: `1. `})'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.orderedList') }}
+ q-btn(
+ icon='mdi-format-list-checks'
+ padding='xs sm'
+ flat
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.taskList') }}
+ q-menu(auto-close)
+ q-list(separator)
+ q-item(clickable, @click='insertBeforeEachLine({ content: `- [ ] `})')
+ q-item-section(side)
+ q-icon(name='mdi-checkbox-blank-outline')
+ q-item-section {{ t('editor.markup.taskListUnchecked') }}
+ q-item(clickable, @click='insertBeforeEachLine({ content: `- [x] `})')
+ q-item-section(side)
+ q-icon(name='mdi-checkbox-outline')
+ q-item-section {{ t('editor.markup.taskListChecked') }}
+ q-btn(
+ icon='mdi-code-tags'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: "`" })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.inlineCode') }}
+ q-btn(
+ icon='mdi-keyboard-variant'
+ padding='xs sm'
+ flat
+ @click='toggleMarkup({ start: ``, end: `` })'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.markup.keyboardKey') }}
+ //--------------------------------------------------------
+ //- MONACO EDITOR
+ //--------------------------------------------------------
+ .editor-markdown-editor
+ div(ref='monacoRef')
+ transition(name='editor-markdown-preview')
+ .editor-markdown-preview(v-if='state.previewShown')
+ .editor-markdown-preview-toolbar
+ strong: em {{ t('editor.renderPreview') }}
+ q-separator.q-ml-md.q-mr-sm(vertical, inset)
+ q-btn(
+ icon='mdi-arrow-vertical-lock'
+ padding='xs sm'
+ flat
+ @click='state.previewScrollSync = !state.previewScrollSync'
+ :color='state.previewScrollSync ? `primary` : null'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.toggleScrollSync') }}
+ q-btn(
+ icon='mdi-eye-off-outline'
+ padding='xs sm'
+ flat
+ @click='state.previewShown = false'
+ )
+ q-tooltip(anchor='top middle' self='bottom middle') {{ t('editor.togglePreviewPane') }}
+ .editor-markdown-preview-content.page-contents(ref='editorPreviewContainerRef')
+ div(
+ ref='editorPreview'
+ v-html='pageStore.render'
+ )
+
+
+
+
+
diff --git a/frontend/src/components/EditorMarkdownConfigOverlay.vue b/frontend/src/components/EditorMarkdownConfigOverlay.vue
new file mode 100644
index 00000000..320d6ede
--- /dev/null
+++ b/frontend/src/components/EditorMarkdownConfigOverlay.vue
@@ -0,0 +1,416 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/ultraviolet-markdown.svg', left, size='md')
+ span {{t(`admin.editors.markdownName`)}}
+ q-space
+ q-btn.q-mr-sm(
+ flat
+ rounded
+ color='white'
+ :aria-label='t(`common.actions.refresh`)'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/admin/editors/markdown`'
+ target='_blank'
+ type='a'
+ )
+ 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='state.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.cancel`)'
+ :aria-label='t(`common.actions.cancel`)'
+ 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='state.loading > 0'
+ )
+ q-page-container
+ q-page.q-pa-md(style='max-width: 1200px; margin: 0 auto;')
+ q-card.shadow-1.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('admin.editors.markdown.general')}}
+ q-item(tag='label')
+ blueprint-icon(icon='html')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.allowHTML`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.allowHTMLHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.allowHTML'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.allowHTML`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='link')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.linkify`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.linkifyHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.linkify'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.linkify`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='enter-key')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.lineBreaks`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.lineBreaksHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.lineBreaks'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.lineBreaks`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='width')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.tabWidth`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.tabWidthHint`)}}
+ q-item-section(side)
+ q-input(
+ type='number'
+ min='1'
+ max='8'
+ style='width: 100px;'
+ outlined
+ v-model='state.config.tabWidth'
+ dense
+ :aria-label='t(`admin.editors.markdown.tabWidth`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='sigma')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.latexEngine`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.latexEngineHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.config.latexEngine'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='latexEngines'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='data-sheet')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.multimdTable`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.multimdTableHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.multimdTable'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.multimdTable`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='asterisk')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.typographer`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.typographerHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.typographer'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.typographer`)'
+ )
+ template(v-if='state.config.typographer')
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='quote-left')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.quotes`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.quotesHint`)}}
+ q-item-section(avatar)
+ q-select(
+ style='width: 200px;'
+ outlined
+ v-model='state.config.quotes'
+ :options='quoteStyles'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :aria-label='t(`admin.editors.markdown.quotes`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='underline')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.underline`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.underlineHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.underline'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.underline`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.editors.markdown.plantuml')}}
+ q-item(tag='label')
+ blueprint-icon(icon='workflow')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.plantuml`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.plantumlHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.plantuml'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.plantuml`)'
+ )
+ template(v-if='state.config.plantuml')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='website')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.plantumlServerUrl`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.plantumlServerUrlHint`)}}
+ q-item-section(side)
+ q-input(
+ style='width: 450px;'
+ outlined
+ v-model='state.config.plantumlServerUrl'
+ dense
+ :aria-label='t(`admin.editors.markdown.plantumlServerUrl`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.editors.markdown.kroki')}}
+ q-item(tag='label')
+ blueprint-icon(icon='workflow')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.kroki`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.krokiHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.kroki'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.editors.markdown.kroki`)'
+ )
+ template(v-if='state.config.kroki')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='website')
+ q-item-section
+ q-item-label {{t(`admin.editors.markdown.krokiServerUrl`)}}
+ q-item-label(caption) {{t(`admin.editors.markdown.krokiServerUrlHint`)}}
+ q-item-section(side)
+ q-input(
+ style='width: 450px;'
+ outlined
+ v-model='state.config.krokiServerUrl'
+ dense
+ :aria-label='t(`admin.editors.markdown.krokiServerUrl`)'
+ )
+
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/EditorMarkdownUserSettingsOverlay.vue b/frontend/src/components/EditorMarkdownUserSettingsOverlay.vue
new file mode 100644
index 00000000..67215948
--- /dev/null
+++ b/frontend/src/components/EditorMarkdownUserSettingsOverlay.vue
@@ -0,0 +1,197 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/ultraviolet-markdown.svg', left, size='md')
+ span {{t('editor.settings.markdown')}}
+ q-space
+ q-btn.q-mr-sm(
+ flat
+ rounded
+ color='white'
+ :aria-label='t(`common.actions.refresh`)'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/editor/markdown`'
+ target='_blank'
+ type='a'
+ )
+ 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='state.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.cancel`)'
+ :aria-label='t(`common.actions.cancel`)'
+ icon='las la-times'
+ @click='close'
+ )
+ q-btn(
+ push
+ color='positive'
+ text-color='white'
+ :label='t(`common.actions.apply`)'
+ :aria-label='t(`common.actions.apply`)'
+ icon='las la-check'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-page-container
+ q-page.q-pa-md(style='max-width: 1200px; margin: 0 auto;')
+ q-card.shadow-1.q-py-sm
+ q-item(tag='label')
+ blueprint-icon(icon='enter-key')
+ q-item-section
+ q-item-label {{t(`editor.settings.markdownPreviewShown`)}}
+ q-item-label(caption) {{t(`editor.settings.markdownPreviewShownHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.previewShown'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`editor.settings.markdownPreviewShown`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='width')
+ q-item-section
+ q-item-label {{t(`editor.settings.markdownFontSize`)}}
+ q-item-label(caption) {{t(`editor.settings.markdownFontSizeHint`)}}
+ q-item-section(side)
+ q-input(
+ type='number'
+ min='10'
+ max='32'
+ style='width: 100px;'
+ outlined
+ v-model='state.config.fontSize'
+ dense
+ :aria-label='t(`editor.settings.markdownFontSize`)'
+ )
+
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/EditorWysiwyg.vue b/frontend/src/components/EditorWysiwyg.vue
new file mode 100644
index 00000000..b7c62a49
--- /dev/null
+++ b/frontend/src/components/EditorWysiwyg.vue
@@ -0,0 +1,910 @@
+
+.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='`ddn-` + 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='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='`btngrp-` + menuItem.key'
+ flat
+ )
+ q-btn(
+ v-for='child of menuItem.children'
+ :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
+ :key='`btn-` + menuItem.key'
+ 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/frontend/src/components/FileManager.vue b/frontend/src/components/FileManager.vue
new file mode 100644
index 00000000..eaa2c11a
--- /dev/null
+++ b/frontend/src/components/FileManager.vue
@@ -0,0 +1,1287 @@
+
+q-layout.fileman(view='hHh lpR lFr', container)
+ q-header.card-header
+ q-toolbar(dark)
+ q-icon(name='img:/_assets/icons/fluent-folder.svg', left, size='md')
+ span {{ t(`fileman.title`) }}
+ q-toolbar(dark)
+ q-btn.q-mr-sm.acrylic-btn(
+ flat
+ color='white'
+ :label='commonStore.locale'
+ :aria-label='commonStore.locale'
+ style='height: 40px;'
+ )
+ locale-selector-menu
+ q-input(
+ dark
+ v-model='state.search'
+ standout='bg-white text-dark'
+ dense
+ ref='searchField'
+ style='width: 100%;'
+ :label='t(`fileman.searchFolder`)'
+ :debounce='500'
+ )
+ template(#prepend)
+ q-icon(name='las la-search')
+ template(#append)
+ q-icon.cursor-pointer(
+ name='las la-times'
+ @click='state.search=``'
+ v-if='state.search.length > 0'
+ :color='$q.dark.isActive ? `blue` : `grey-4`'
+ )
+ q-toolbar(dark)
+ q-space
+ q-btn(
+ flat
+ dense
+ no-caps
+ color='red-3'
+ :aria-label='t(`common.actions.close`)'
+ icon='las la-times'
+ @click='close'
+ )
+ q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.close`)}}
+ q-drawer.fileman-left(:model-value='true', :width='350')
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ .q-px-md.q-pb-sm
+ tree(
+ ref='treeComp'
+ :nodes='state.treeNodes'
+ :roots='state.treeRoots'
+ v-model:selected='state.currentFolderId'
+ @lazy-load='treeLazyLoad'
+ :use-lazy-load='true'
+ @context-action='treeContextAction'
+ :display-mode='state.displayMode'
+ )
+ q-drawer.fileman-right(:model-value='$q.screen.gt.md', :width='350', side='right')
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ .q-pa-md
+ template(v-if='currentFileDetails')
+ q-img.rounded-borders.q-mb-md(
+ :src='currentFileDetails.thumbnail'
+ width='100%'
+ fit='cover'
+ :ratio='16/10'
+ no-spinner
+ )
+ .fileman-details-row(
+ v-for='item of currentFileDetails.items'
+ :key='item.id'
+ )
+ label {{ item.label }}
+ span {{ item.value }}
+ template(v-if='insertMode')
+ q-separator.q-my-md
+ q-btn.full-width(
+ @click='insertItem()'
+ :label='t(`common.actions.insert`)'
+ color='primary'
+ icon='las la-plus-circle'
+ push
+ padding='sm'
+ )
+ q-page-container
+ q-page.fileman-center.column
+ //- TOOLBAR -----------------------------------------------------
+ q-toolbar.fileman-toolbar
+ template(v-if='state.isUploading')
+ .fileman-progressbar
+ div(:style='`width: ` + state.uploadPercentage + `%`') {{ state.uploadPercentage }}%
+ q-btn.acrylic-btn.q-ml-sm(
+ flat
+ dense
+ no-caps
+ color='negative'
+ :aria-label='t(`common.actions.cancel`)'
+ icon='las la-square'
+ @click='uploadCancel'
+ v-if='state.uploadPercentage < 100'
+ )
+ template(v-else)
+ q-space
+ q-btn.q-mr-sm(
+ flat
+ dense
+ no-caps
+ color='grey'
+ :aria-label='t(`fileman.viewOptions`)'
+ icon='las la-th-list'
+ )
+ q-tooltip(anchor='bottom middle', self='top middle') {{ t(`fileman.viewOptions`) }}
+ q-menu(
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ anchor='bottom right'
+ self='top right'
+ )
+ q-card.q-pa-sm
+ .text-center
+ small.text-grey {{ t(`fileman.viewOptions`) }}
+ q-list(dense)
+ q-separator.q-my-sm
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(name='las la-list', color='grey', size='xs')
+ q-item-section.q-pr-sm Browse using...
+ q-item-section(side)
+ q-icon(name='las la-angle-right', color='grey', size='xs')
+ q-menu(
+ anchor='top end'
+ self='top start'
+ )
+ q-list.q-pa-sm(dense)
+ q-item(clickable, @click='state.displayMode = `path`')
+ q-item-section(side)
+ q-icon(
+ :name='state.displayMode === `path` ? `las la-check-circle` : `las la-circle`'
+ :color='state.displayMode === `path` ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm Browse Using Paths
+ q-item(clickable, @click='state.displayMode = `title`')
+ q-item-section(side)
+ q-icon(
+ :name='state.displayMode === `title` ? `las la-check-circle` : `las la-circle`'
+ :color='state.displayMode === `title` ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm Browse Using Titles
+ q-item(clickable, @click='state.isCompact = !state.isCompact')
+ q-item-section(side)
+ q-icon(
+ :name='state.isCompact ? `las la-check-square` : `las la-stop`'
+ :color='state.isCompact ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm Compact List
+ q-item(clickable, @click='state.shouldShowFolders = !state.shouldShowFolders')
+ q-item-section(side)
+ q-icon(
+ :name='state.shouldShowFolders ? `las la-check-square` : `las la-stop`'
+ :color='state.shouldShowFolders ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm Show Folders
+ q-btn.q-mr-sm(
+ flat
+ dense
+ no-caps
+ color='grey'
+ :aria-label='t(`common.actions.refresh`)'
+ icon='las la-redo-alt'
+ @click='reloadFolder(state.currentFolderId)'
+ )
+ q-tooltip(anchor='bottom middle', self='top middle') {{ t(`common.actions.refresh`) }}
+ q-separator.q-mr-sm(inset, vertical)
+ q-btn.q-mr-sm(
+ flat
+ dense
+ no-caps
+ color='blue'
+ :label='t(`common.actions.new`)'
+ :aria-label='t(`common.actions.new`)'
+ icon='las la-plus-circle'
+ )
+ new-menu(
+ :hide-asset-btn='true'
+ :show-new-folder='true'
+ @new-folder='() => newFolder(state.currentFolderId)'
+ @new-page='() => close()'
+ :base-path='folderPath'
+ )
+ q-btn(
+ flat
+ dense
+ no-caps
+ color='positive'
+ :label='t(`common.actions.upload`)'
+ :aria-label='t(`common.actions.upload`)'
+ icon='las la-cloud-upload-alt'
+ @click='uploadFile'
+ )
+
+ .row(style='flex: 1 1 100%;')
+ .col
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ .fileman-loadinglist(v-if='state.fileListLoading')
+ q-spinner.q-mr-sm(color='primary', size='64px', :thickness='1')
+ span.text-primary Fetching folder contents...
+ .fileman-emptylist(v-else-if='files.length < 1')
+ img(src='/_assets/icons/carbon-copy-empty-box.svg')
+ span This folder is empty.
+ q-list.fileman-filelist(
+ v-else
+ :class='state.isCompact && `is-compact`'
+ )
+ q-item(
+ v-for='item of files'
+ :key='item.id'
+ clickable
+ active-class='active'
+ :active='item.id === state.currentFileId'
+ @click='selectItem(item)'
+ @dblclick='doubleClickItem(item)'
+ )
+ q-item-section.fileman-filelist-icon(avatar)
+ q-icon(:name='item.icon', :size='state.isCompact ? `md` : `xl`')
+ q-item-section.fileman-filelist-label
+ q-item-label {{ usePathTitle ? item.fileName : item.title }}
+ q-item-label(caption, v-if='!state.isCompact') {{ item.caption }}
+ q-item-section.fileman-filelist-side(side, v-if='item.side')
+ .text-caption {{ item.side }}
+ //- RIGHT-CLICK MENU
+ q-menu.translucent-menu(
+ touch-position
+ context-menu
+ auto-close
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ )
+ q-card.q-pa-sm
+ q-list(dense, style='min-width: 150px;')
+ q-item(clickable, v-if='insertMode && item.type !== `folder`', @click='insertItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-plus-circle', color='primary')
+ q-item-section {{ t(`common.actions.insert`) }}
+ q-item(clickable, v-if='item.type === `page`', @click='editItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-edit', color='orange')
+ q-item-section {{ t(`common.actions.edit`) }}
+ q-item(clickable, v-if='item.type === `page`', @click='rerenderPage(item)')
+ q-item-section(side)
+ q-icon(name='las la-magic', color='orange')
+ q-item-section {{ t(`common.actions.rerender`) }}
+ q-item(clickable, v-if='item.type !== `folder`', @click='openItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-eye', color='primary')
+ q-item-section {{ t(`common.actions.view`) }}
+ template(v-if='item.type === `asset` && item.imageEdit')
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(name='las la-edit', color='orange')
+ q-item-section Edit Image...
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(name='las la-crop', color='orange')
+ q-item-section Resize Image...
+ q-item(clickable, v-if='item.type !== `folder`', @click='copyItemURL(item)')
+ q-item-section(side)
+ q-icon(name='las la-clipboard', color='primary')
+ q-item-section {{ t(`common.actions.copyURL`) }}
+ q-item(clickable, v-if='item.type !== `folder`', @click='downloadItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-download', color='primary')
+ q-item-section {{ t(`common.actions.download`) }}
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(name='las la-copy', color='teal')
+ q-item-section Duplicate...
+ q-item(clickable, @click='renameItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-redo', color='teal')
+ q-item-section Rename...
+ q-item(clickable)
+ q-item-section(side)
+ q-icon(name='las la-arrow-right', color='teal')
+ q-item-section Move to...
+ q-item(clickable, @click='delItem(item)')
+ q-item-section(side)
+ q-icon(name='las la-trash-alt', color='negative')
+ q-item-section.text-negative {{ t(`common.actions.delete`) }}
+ q-footer
+ q-bar.fileman-path
+ small.text-caption.text-grey-7 {{folderPath}}
+
+ input(
+ type='file'
+ ref='fileIpt'
+ multiple
+ @change='uploadNewFiles'
+ style='display: none'
+ )
+
+
+
+
+
diff --git a/frontend/src/components/FolderCreateDialog.vue b/frontend/src/components/FolderCreateDialog.vue
new file mode 100644
index 00000000..332af161
--- /dev/null
+++ b/frontend/src/components/FolderCreateDialog.vue
@@ -0,0 +1,190 @@
+
+q-dialog(ref='dialogRef', @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(`fileman.folderCreate`)}}
+ q-form.q-py-sm(ref='newFolderForm', @submit='create')
+ q-item
+ blueprint-icon(icon='folder')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.title'
+ dense
+ :rules='titleValidation'
+ hide-bottom-space
+ :label='t(`fileman.folderTitle`)'
+ :aria-label='t(`fileman.folderTitle`)'
+ lazy-rules='ondemand'
+ autofocus
+ ref='iptTitle'
+ @keyup.enter='create'
+ )
+ q-item
+ blueprint-icon.self-start(icon='file-submodule')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.path'
+ dense
+ :rules='pathValidation'
+ hide-bottom-space
+ :label='t(`fileman.folderFileName`)'
+ :aria-label='t(`fileman.folderFileName`)'
+ :hint='t(`fileman.folderFileNameHint`)'
+ lazy-rules='ondemand'
+ @focus='state.pathDirty = true'
+ @keyup.enter='create'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.loading > 0'
+ )
+
+
+
diff --git a/frontend/src/components/FolderDeleteDialog.vue b/frontend/src/components/FolderDeleteDialog.vue
new file mode 100644
index 00000000..94282892
--- /dev/null
+++ b/frontend/src/components/FolderDeleteDialog.vue
@@ -0,0 +1,109 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 550px; max-width: 850px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{t(`folderDeleteDialog.title`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='folderDeleteDialog.confirm')
+ template(v-slot:name)
+ strong {{folderName}}
+ .text-caption.text-grey.q-mt-sm {{t('folderDeleteDialog.folderId', { id: folderId })}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/FolderRenameDialog.vue b/frontend/src/components/FolderRenameDialog.vue
new file mode 100644
index 00000000..e07d9c11
--- /dev/null
+++ b/frontend/src/components/FolderRenameDialog.vue
@@ -0,0 +1,227 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
+ span {{t(`fileman.folderRename`)}}
+ q-form.q-py-sm(ref='renameFolderForm', @submit='rename')
+ q-item
+ blueprint-icon(icon='folder')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.title'
+ dense
+ :rules='titleValidation'
+ hide-bottom-space
+ :label='t(`fileman.folderTitle`)'
+ :aria-label='t(`fileman.folderTitle`)'
+ lazy-rules='ondemand'
+ autofocus
+ ref='iptTitle'
+ @keyup.enter='rename'
+ )
+ q-item
+ blueprint-icon.self-start(icon='file-submodule')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.path'
+ dense
+ :rules='pathValidation'
+ hide-bottom-space
+ :label='t(`fileman.folderFileName`)'
+ :aria-label='t(`fileman.folderFileName`)'
+ :hint='t(`fileman.folderFileNameHint`)'
+ lazy-rules='ondemand'
+ @focus='state.pathDirty = true'
+ @keyup.enter='rename'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.rename`)'
+ color='primary'
+ padding='xs md'
+ @click='rename'
+ :loading='state.loading > 0'
+ )
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/FooterNav.vue b/frontend/src/components/FooterNav.vue
new file mode 100644
index 00000000..ec341d62
--- /dev/null
+++ b/frontend/src/components/FooterNav.vue
@@ -0,0 +1,93 @@
+
+q-footer.site-footer
+ .site-footer-line
+ i18n-t.q-mr-xs(
+ v-if='hasSiteFooter'
+ :keypath='isCopyright ? `common.footerCopyright` : `common.footerLicense`'
+ tag='span'
+ scope='global'
+ )
+ template(#company)
+ strong {{siteStore.company}}
+ template(#year)
+ span {{currentYear}}
+ template(#license)
+ span {{t(`common.license.` + siteStore.contentLicense)}}
+ i18n-t(
+ :keypath='props.generic ? `common.footerGeneric` : `common.footerPoweredBy`'
+ tag='span'
+ scope='global'
+ )
+ template(#link)
+ a(href='https://js.wiki', target='_blank', ref='noopener noreferrer'): strong Wiki.js
+ .site-footer-line(v-if='!props.generic && siteStore.footerExtra')
+ span {{ siteStore.footerExtra }}
+
+
+
+
+
diff --git a/frontend/src/components/GithubSetupInstallDialog.vue b/frontend/src/components/GithubSetupInstallDialog.vue
new file mode 100644
index 00000000..6d6b775a
--- /dev/null
+++ b/frontend/src/components/GithubSetupInstallDialog.vue
@@ -0,0 +1,41 @@
+
+q-dialog(ref='dialogRef', @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='onDialogOK'
+ )
+
+
+
diff --git a/frontend/src/components/GroupCreateDialog.vue b/frontend/src/components/GroupCreateDialog.vue
new file mode 100644
index 00000000..6d9f5242
--- /dev/null
+++ b/frontend/src/components/GroupCreateDialog.vue
@@ -0,0 +1,125 @@
+
+q-dialog(ref='dialogRef', @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='state.groupName'
+ dense
+ :rules='groupNameValidation'
+ 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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/GroupDeleteDialog.vue b/frontend/src/components/GroupDeleteDialog.vue
new file mode 100644
index 00000000..c13e73e0
--- /dev/null
+++ b/frontend/src/components/GroupDeleteDialog.vue
@@ -0,0 +1,96 @@
+
+q-dialog(ref='dialogRef', @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 {{props.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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ )
+
+
+
diff --git a/frontend/src/components/GroupEditOverlay.vue b/frontend/src/components/GroupEditOverlay.vue
new file mode 100644
index 00000000..9b004d0c
--- /dev/null
+++ b/frontend/src/components/GroupEditOverlay.vue
@@ -0,0 +1,1205 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/fluent-people.svg', left, size='md')
+ div
+ span {{ t(`admin.groups.edit`) }}
+ .text-caption {{ state.group.name }}
+ 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='!state.isLoading')
+ template(v-for='sc of sections', :key='`section-` + sc.key')
+ q-item(
+ v-if='!(isGuestGroup && sc.excludeGuests)'
+ 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='state.usersTotal')
+ q-item-section(side, v-if='sc.rulesTotal && state.group.rules')
+ q-badge(color='dark-3', :label='state.group.rules.length')
+ q-page-container
+ q-page(v-if='state.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='state.group.name'
+ dense
+ :rules='groupNameValidation'
+ hide-bottom-space
+ :aria-label='t(`admin.groups.name`)'
+ :disable='isGuestGroup'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='!isGuestGroup')
+ 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='state.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='state.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='state.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 {{state.group.id}}
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='calendar-plus', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ t(`common.field.createdOn`) }}
+ q-item-label: strong {{humanizeDate(state.group.createdAt)}}
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='summertime', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ t(`common.field.lastUpdated`) }}
+ q-item-label: strong {{humanizeDate(state.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='siteStore.docsBase + `/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='!state.group.rules || state.group.rules.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
+ ) {{ t('admin.groups.rulesNone') }}
+ q-card.shadow-1.q-pb-sm(v-else)
+ q-card-section
+ .admin-groups-rule(
+ v-for='rule of state.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(#selected-item='scope')
+ q-chip(
+ square
+ dense
+ :tabindex='scope.tabindex'
+ :color='getRuleModeBgColor(rule.mode)'
+ text-color='white'
+ )
+ span.text-caption {{ scope.opt.title }}
+ template(#option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(v-bind='itemProps', v-on='itemEvents')
+ 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.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='adminStore.sites'
+ option-value='id'
+ option-label='title'
+ multiple
+ behavior='dialog'
+ :display-value='t(`admin.groups.selectedSites`, rule.sites.length, { count: rule.sites.length })'
+ )
+ template(#option='{ itemProps, itemEvents, opt, selected, toggleOption }')
+ q-item(v-bind='itemProps', v-on='itemEvents')
+ q-item-section
+ q-item-label {{ opt.title }}
+ q-item-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.label'
+ )
+ q-select.q-mt-sm(
+ standout
+ v-model='rule.locales'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.groups.ruleLocales`)'
+ :options='adminStore.locales'
+ option-value='code'
+ option-label='name'
+ multiple
+ behavior='dialog'
+ :display-value='t(`admin.groups.selectedLocales`, { n: rule.locales.length > 0 ? rule.locales[0].toUpperCase() : rule.locales.length }, rule.locales.length)'
+ )
+ template(#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='siteStore.docsBase + `/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='state.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='siteStore.docsBase + `/admin/groups#users`'
+ target='_blank'
+ )
+ q-input.denser.fill-outline.q-mr-sm(
+ outlined
+ v-model='state.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-banner(
+ v-if='!state.users || state.users.length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-4 text-grey-9`'
+ ) {{ t('admin.groups.usersNone') }}
+ q-card.shadow-1
+ q-table(
+ :rows='state.users'
+ :columns='usersHeaders'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.isLoadingUsers'
+ )
+ template(#body-cell-id='props')
+ q-td(:props='props')
+ q-icon(name='las la-user', color='primary', size='sm')
+ template(#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(#body-cell-email='props')
+ q-td(:props='props')
+ em {{ props.value }}
+ template(#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(#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='state.usersPage'
+ :max='usersTotalPages'
+ :max-pages='9'
+ boundary-numbers
+ direction-links
+ )
+
+
+
+
+
diff --git a/frontend/src/components/HeaderNav.vue b/frontend/src/components/HeaderNav.vue
new file mode 100644
index 00000000..13d585f5
--- /dev/null
+++ b/frontend/src/components/HeaderNav.vue
@@ -0,0 +1,127 @@
+
+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(
+ v-if='siteStore.logoText'
+ size='34px'
+ square
+ )
+ img(:src='`/_site/logo`')
+ img(
+ v-else
+ :src='`/_site/logo`'
+ style='height: 34px'
+ )
+ q-toolbar-title.text-h6(v-if='siteStore.logoText') {{siteStore.title}}
+ header-search
+ q-toolbar(
+ style='height: 64px;'
+ dark
+ )
+ q-space
+ transition(name='syncing')
+ q-spinner-tail(
+ v-show='commonStore.routerLoading'
+ color='accent'
+ size='24px'
+ )
+ q-btn.q-ml-md(
+ v-if='userStore.can(`write:pages`)'
+ 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(
+ v-if='userStore.can(`browse:fileman`)'
+ flat
+ round
+ dense
+ icon='las la-folder-open'
+ color='positive'
+ aria-label='File Manager'
+ @click='openFileManager'
+ )
+ q-tooltip File Manager
+ q-btn.q-ml-md(
+ v-if='userStore.can(`access:admin`)'
+ flat
+ round
+ dense
+ icon='las la-tools'
+ color='pink'
+ to='/_admin'
+ :aria-label='t(`common.header.admin`)'
+ )
+ q-tooltip {{ t('common.header.admin') }}
+
+ //- USER BUTTON / DROPDOWN
+ account-menu(v-if='userStore.authenticated')
+ q-btn.q-ml-md(
+ v-else
+ flat
+ rounded
+ icon='las la-sign-in-alt'
+ color='white'
+ :label='$t(`common.actions.login`)'
+ :aria-label='$t(`common.actions.login`)'
+ to='/login'
+ padding='sm'
+ no-caps
+ )
+
+
+
diff --git a/frontend/src/components/HeaderSearch.vue b/frontend/src/components/HeaderSearch.vue
new file mode 100644
index 00000000..f45073fb
--- /dev/null
+++ b/frontend/src/components/HeaderSearch.vue
@@ -0,0 +1,229 @@
+
+q-toolbar(
+ style='height: 64px;'
+ dark
+ v-if='siteStore.features.search'
+ )
+ q-input(
+ dark
+ v-model='siteStore.search'
+ standout='bg-white text-dark'
+ dense
+ rounded
+ ref='searchField'
+ style='width: 100%;'
+ label='Search...'
+ @keyup.enter='onSearchEnter'
+ @focus='state.searchIsFocused = true'
+ @blur='checkSearchFocus'
+ )
+ template(v-slot:prepend)
+ q-circular-progress.q-mr-xs(
+ v-if='siteStore.searchIsLoading && route.path !== `/_search`'
+ instant-feedback
+ indeterminate
+ rounded
+ color='primary'
+ size='20px'
+ )
+ q-icon(v-else, name='las la-search')
+ template(v-slot:append)
+ q-badge.search-kbdbadge.q-mr-sm(
+ v-if='!state.searchIsFocused'
+ label='Ctrl+K'
+ color='custom-color'
+ outline
+ @click='searchField.focus()'
+ )
+ q-badge.q-mr-sm(
+ v-else-if='siteStore.search && siteStore.search !== siteStore.searchLastQuery'
+ label='Press Enter'
+ color='grey-7'
+ outline
+ @click='searchField.focus()'
+ )
+ q-icon.cursor-pointer(
+ name='las la-times'
+ size='20px'
+ @click='siteStore.search=``'
+ v-if='siteStore.search.length > 0'
+ color='grey-6'
+ )
+ .searchpanel(
+ ref='searchPanel'
+ v-if='searchPanelIsShown'
+ )
+ template(v-if='siteStore.tagsLoaded && siteStore.tags.length > 0')
+ .searchpanel-header
+ span Popular Tags
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ label='View All'
+ rounded
+ size='xs'
+ )
+ .flex.q-mb-md
+ q-chip(
+ v-for='tag of popularTags'
+ :key='tag'
+ square
+ color='grey-8'
+ text-color='white'
+ icon='las la-hashtag'
+ size='sm'
+ clickable
+ @click='addTag(tag)'
+ ) {{ tag }}
+ .searchpanel-header Search Operators
+ .searchpanel-tip #[code !foo] or #[code -bar] to exclude "foo" and "bar".
+ .searchpanel-tip #[code bana*] for to match any term starting with "bana" (e.g. banana).
+ .searchpanel-tip #[code foo,bar] or #[code foo|bar] to search for "foo" OR "bar".
+ .searchpanel-tip #[code "foo bar"] to match exactly the phrase "foo bar".
+
+
+
+
+
diff --git a/frontend/src/components/IconPickerDialog.vue b/frontend/src/components/IconPickerDialog.vue
new file mode 100644
index 00000000..f7aa2130
--- /dev/null
+++ b/frontend/src/components/IconPickerDialog.vue
@@ -0,0 +1,228 @@
+
+
+q-card.icon-picker(flat, style='width: 400px')
+ q-tabs.text-primary(
+ v-model='state.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='state.currentTab'
+ )
+ q-tab-panel(name='icon')
+ q-select(
+ :options='iconPacks'
+ v-model='state.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='state.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='state.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/frontend/src/components/LoadingGeneric.vue b/frontend/src/components/LoadingGeneric.vue
new file mode 100644
index 00000000..1d96376a
--- /dev/null
+++ b/frontend/src/components/LoadingGeneric.vue
@@ -0,0 +1,39 @@
+
+.loader-generic
+ div
+
+
+
diff --git a/frontend/src/components/LocaleSelectorMenu.vue b/frontend/src/components/LocaleSelectorMenu.vue
new file mode 100644
index 00000000..d9aa6126
--- /dev/null
+++ b/frontend/src/components/LocaleSelectorMenu.vue
@@ -0,0 +1,62 @@
+
+q-menu.translucent-menu(
+ auto-close
+ :anchor='props.anchor'
+ :self='props.self'
+ :offset='props.offset'
+ )
+ q-list(padding, style='min-width: 200px;')
+ q-item(
+ v-for='lang of siteStore.locales.active'
+ :key='lang.code'
+ clickable
+ @click='commonStore.setLocale(lang.code)'
+ )
+ q-item-section(side)
+ q-avatar(rounded, :color='lang.code === commonStore.locale ? `secondary` : `primary`', text-color='white', size='sm')
+ .text-caption.text-uppercase: strong {{ lang.language }}
+ q-item-section
+ q-item-label {{ lang.nativeName }}
+ q-item-label(caption) {{ lang.name }}
+
+
+
diff --git a/frontend/src/components/MailTemplateEditorOverlay.vue b/frontend/src/components/MailTemplateEditorOverlay.vue
new file mode 100644
index 00000000..39334088
--- /dev/null
+++ b/frontend/src/components/MailTemplateEditorOverlay.vue
@@ -0,0 +1,163 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/fluent-template.svg', left, size='md')
+ span {{ t(`admin.mail.templateEditor`) }}
+ q-space
+ q-btn.q-mr-sm(
+ flat
+ rounded
+ color='white'
+ :aria-label='t(`common.actions.viewDocs`)'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/system/mail`'
+ target='_blank'
+ type='a'
+ )
+ q-btn-group(push)
+ q-btn(
+ push
+ color='white'
+ text-color='grey-7'
+ :label='t(`common.actions.cancel`)'
+ :aria-label='t(`common.actions.cancel`)'
+ 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'
+ :disabled='state.loading > 0'
+ )
+ q-page-container
+ q-page
+ //--------------------------------------------------------
+ //- MONACO EDITOR
+ //--------------------------------------------------------
+ .mail-template-editor
+ repl(:editor='Monaco' :store='store' :show-ts-config='false' theme='dark' :auto-resize='true' :ssr='false' :show-compile-output='false')
+
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
+
+
diff --git a/frontend/src/components/MainOverlayDialog.vue b/frontend/src/components/MainOverlayDialog.vue
new file mode 100644
index 00000000..09dcc4cb
--- /dev/null
+++ b/frontend/src/components/MainOverlayDialog.vue
@@ -0,0 +1,51 @@
+
+q-dialog.main-overlay(
+ v-model='siteStore.overlayIsShown'
+ persistent
+ full-width
+ full-height
+ no-shake
+ transition-show='jump-up'
+ transition-hide='jump-down'
+ )
+ component(:is='overlays[siteStore.overlay]')
+
+
+
diff --git a/frontend/src/components/NavEditMenu.vue b/frontend/src/components/NavEditMenu.vue
new file mode 100644
index 00000000..7e59c152
--- /dev/null
+++ b/frontend/src/components/NavEditMenu.vue
@@ -0,0 +1,210 @@
+
+q-card(style='min-width: 350px')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-sidebar-menu.svg', left, size='sm')
+ span {{t(`navEdit.title`)}}
+ q-list(padding)
+ template(v-if='isRoot')
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='inherit')
+ q-item-section
+ q-item-label Show
+ q-item-label(caption) Show the left sidebar navigaiton menu items.
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='hide')
+ q-item-section
+ q-item-label Hide
+ q-item-label(caption) Completely hide the left sidebar navigation.
+ template(v-else)
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='inherit')
+ q-item-section
+ q-item-label Inherit
+ q-item-label(caption) Use the menu items and settings from the parent path.
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='override')
+ q-item-section
+ q-item-label Override Current + Descendants
+ q-item-label(caption) Set menu items and settings for this path and all descendants.
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='overrideExact')
+ q-item-section
+ q-item-label Override Current Only
+ q-item-label(caption) Set menu items and settings only for this path.
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='hide')
+ q-item-section
+ q-item-label Hide Current + Descendants
+ q-item-label(caption) Completely hide the left sidebar navigation for this path and all descendants.
+ q-item(tag='label')
+ q-item-section(side)
+ q-radio(v-model='state.mode', val='hideExact')
+ q-item-section
+ q-item-label Hide Current Only
+ q-item-label(caption) Completely hide the left sidebar navigation only for this path.
+
+ template(v-if='canEditMenuItems')
+ q-separator(inset)
+ q-card-section
+ q-btn.full-width(
+ unelevated
+ icon='mdi-playlist-edit'
+ color='deep-orange-9'
+ :label='t(`navEdit.editMenuItems`)'
+ @click='startEditing'
+ )
+
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='props.menuHideHandler'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='positive'
+ padding='xs md'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+
+
+
diff --git a/frontend/src/components/NavEditOverlay.vue b/frontend/src/components/NavEditOverlay.vue
new file mode 100644
index 00000000..3b16d8ef
--- /dev/null
+++ b/frontend/src/components/NavEditOverlay.vue
@@ -0,0 +1,783 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/fluent-sidebar-menu.svg', left, size='md')
+ span {{t(`navEdit.editMenuItems`)}}
+ q-space
+ transition(name='syncing')
+ q-spinner-tail.q-mr-sm(
+ v-show='state.loading > 0'
+ color='accent'
+ size='24px'
+ )
+ q-btn.q-mr-sm(
+ flat
+ rounded
+ color='white'
+ :aria-label='t(`common.actions.viewDocs`)'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/admin/editors/markdown`'
+ target='_blank'
+ type='a'
+ )
+ q-btn-group(push)
+ q-btn(
+ push
+ color='white'
+ text-color='grey-7'
+ :label='t(`common.actions.cancel`)'
+ :aria-label='t(`common.actions.cancel`)'
+ 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'
+ :disabled='state.loading > 0'
+ @click='save'
+ )
+
+ q-drawer.bg-dark-6(:model-value='true', :width='295', dark)
+ q-scroll-area.nav-edit(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ )
+ sortable(
+ class='q-list q-list--dense q-list--dark nav-edit-list'
+ :list='state.items'
+ item-key='id'
+ :options='sortableOptions'
+ @end='updateItemPosition'
+ )
+ template(#item='{element}')
+ .nav-edit-item.nav-edit-item-header(
+ v-if='element.type === `header`'
+ :class='state.selected === element.id ? `is-active` : ``'
+ @click='setItem(element)'
+ )
+ q-item-label.text-caption(
+ header
+ ) {{ element.label }}
+ q-space
+ q-item-section(side)
+ q-icon.handle(name='mdi-drag-horizontal', size='sm')
+ q-item.nav-edit-item.nav-edit-item-link(
+ v-else-if='element.type === `link`'
+ :class='{ "is-active": state.selected === element.id, "is-nested": element.isNested }'
+ @click='setItem(element)'
+ clickable
+ )
+ q-item-section(side)
+ q-icon(:name='element.icon', color='white')
+ q-item-section.text-wordbreak-all {{ element.label }}
+ q-item-section(side)
+ q-icon.handle(name='mdi-drag-horizontal', size='sm')
+ .nav-edit-item.nav-edit-item-separator(
+ v-else
+ :class='state.selected === element.id ? `is-active` : ``'
+ @click='setItem(element)'
+ )
+ q-separator(
+ dark
+ inset
+ style='flex: 1; margin-top: 11px;'
+ )
+ q-item-section(side)
+ q-icon.handle(name='mdi-drag-horizontal', size='sm')
+
+ .q-pa-md.flex
+ q-btn.acrylic-btn(
+ style='flex: 1;'
+ flat
+ color='positive'
+ :label='t(`common.actions.add`)'
+ :aria-label='t(`common.actions.add`)'
+ icon='las la-plus-circle'
+ )
+ q-menu(fit, :offset='[0, 10]', auto-close)
+ q-list(separator)
+ q-item(clickable, @click='addItem(`header`)')
+ q-item-section(side)
+ q-icon(name='las la-heading')
+ q-item-section
+ q-item-label {{t('navEdit.header')}}
+ q-item(clickable, @click='addItem(`link`)')
+ q-item-section(side)
+ q-icon(name='las la-link')
+ q-item-section
+ q-item-label {{t('navEdit.link')}}
+ q-item(clickable, @click='addItem(`separator`)')
+ q-item-section(side)
+ q-icon(name='las la-minus')
+ q-item-section
+ q-item-label {{t('navEdit.separator')}}
+ q-btn.q-ml-sm.acrylic-btn(
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.add`)'
+ icon='las la-ellipsis-v'
+ padding='xs sm'
+ )
+ q-menu(:offset='[0, 10]' anchor='bottom right' self='top right' auto-close)
+ q-list(separator)
+ q-item(clickable, @click='clearItems', :disable='state.items.length < 1')
+ q-item-section(side)
+ q-icon(name='las la-trash-alt', color='negative')
+ q-item-section
+ q-item-label {{t('navEdit.clearItems')}}
+ //- q-item(clickable)
+ //- q-item-section(side)
+ //- q-icon(name='mdi-import')
+ //- q-item-section
+ //- q-item-label Copy from...
+
+ q-page-container
+ q-page.q-pa-md
+ template(v-if='state.items.length < 1')
+ q-card
+ q-card-section
+ q-icon.q-mr-sm(name='las la-arrow-left', size='xs')
+ span {{ t('navEdit.emptyMenuText') }}
+ template(v-else-if='!state.selected')
+ q-card
+ q-card-section
+ q-icon.q-mr-sm(name='las la-arrow-left', size='xs')
+ span {{ t('navEdit.noSelection') }}
+
+ template(v-if='state.current.type === `header`')
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('navEdit.header')}}
+ q-item
+ blueprint-icon(icon='typography')
+ q-item-section
+ q-item-label {{t(`navEdit.label`)}}
+ q-item-label(caption) {{t(`navEdit.labelHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.current.label'
+ dense
+ hide-bottom-space
+ :aria-label='t(`navEdit.label`)'
+ )
+ q-item
+ blueprint-icon(icon='user-groups')
+ q-item-section
+ q-item-label {{t(`navEdit.visibility`)}}
+ q-item-label(caption) {{t(`navEdit.visibilityHint`)}}
+ q-item-section(avatar)
+ q-btn-toggle(
+ v-model='state.current.visibilityLimited'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='visibilityOptions'
+ )
+ q-item.items-center(v-if='state.current.visibilityLimited')
+ q-space
+ .text-caption.q-mr-md {{ t('navEdit.selectGroups') }}
+ q-select(
+ style='width: 100%; max-width: calc(50% - 34px);'
+ outlined
+ v-model='state.current.visibilityGroups'
+ :options='state.groups'
+ option-value='id'
+ option-label='name'
+ emit-value
+ map-options
+ dense
+ multiple
+ :aria-label='t(`navEdit.selectGroups`)'
+ )
+ q-card.q-pa-md.q-mt-md.flex
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash-alt'
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='removeItem(state.current.id)'
+ )
+
+ template(v-if='state.current.type === `link`')
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('navEdit.link')}}
+ q-item
+ blueprint-icon(icon='typography')
+ q-item-section
+ q-item-label {{t(`navEdit.label`)}}
+ q-item-label(caption) {{t(`navEdit.labelHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.current.label'
+ dense
+ hide-bottom-space
+ :aria-label='t(`navEdit.label`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='spring')
+ q-item-section
+ q-item-label {{t(`navEdit.icon`)}}
+ q-item-label(caption) {{t(`navEdit.iconHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.current.icon'
+ dense
+ :aria-label='t(`navEdit.icon`)'
+ )
+ template(#append)
+ q-icon.cursor-pointer(
+ name='las la-icons'
+ color='primary'
+ )
+ q-menu(content-class='shadow-7')
+ .q-pa-lg: em [ TODO: Icon Picker Dialog ]
+ // icon-picker-dialog(v-model='pageStore.icon')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='link')
+ q-item-section
+ q-item-label {{t(`navEdit.target`)}}
+ q-item-label(caption) {{t(`navEdit.targetHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.current.target'
+ dense
+ hide-bottom-space
+ :aria-label='t(`navEdit.target`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='external-link')
+ q-item-section
+ q-item-label {{t(`navEdit.openInNewWindow`)}}
+ q-item-label(caption) {{t(`navEdit.openInNewWindowHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.current.openInNewWindow'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`navEdit.openInNewWindow`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='user-groups')
+ q-item-section
+ q-item-label {{t(`navEdit.visibility`)}}
+ q-item-label(caption) {{t(`navEdit.visibilityHint`)}}
+ q-item-section(avatar)
+ q-btn-toggle(
+ v-model='state.current.visibilityLimited'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='visibilityOptions'
+ )
+ q-item.items-center(v-if='state.current.visibilityLimited')
+ q-space
+ .text-caption.q-mr-md {{ t('navEdit.selectGroups') }}
+ q-select(
+ style='width: 100%; max-width: calc(50% - 34px);'
+ outlined
+ v-model='state.current.visibilityGroups'
+ :options='state.groups'
+ option-value='id'
+ option-label='name'
+ emit-value
+ map-options
+ dense
+ multiple
+ :aria-label='t(`navEdit.selectGroups`)'
+ )
+
+ q-card.q-pa-md.q-mt-md.flex.items-start
+ div
+ q-btn.acrylic-btn(
+ v-if='state.current.isNested'
+ flat
+ :label='t(`navEdit.unnestItem`)'
+ icon='mdi-format-indent-decrease'
+ color='teal'
+ padding='xs md'
+ @click='state.current.isNested = false'
+ )
+ q-btn.acrylic-btn(
+ v-else
+ flat
+ :label='t(`navEdit.nestItem`)'
+ icon='mdi-format-indent-increase'
+ color='teal'
+ padding='xs md'
+ @click='state.current.isNested = true'
+ )
+ .text-caption.q-mt-md.text-grey-7 {{ t('navEdit.nestingWarn') }}
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash-alt'
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='removeItem(state.current.id)'
+ )
+
+ template(v-if='state.current.type === `separator`')
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('navEdit.separator')}}
+ q-item
+ blueprint-icon(icon='user-groups')
+ q-item-section
+ q-item-label {{t(`navEdit.visibility`)}}
+ q-item-label(caption) {{t(`navEdit.visibilityHint`)}}
+ q-item-section(avatar)
+ q-btn-toggle(
+ v-model='state.current.visibilityLimited'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='visibilityOptions'
+ )
+ q-item.items-center(v-if='state.current.visibilityLimited')
+ q-space
+ .text-caption.q-mr-md {{ t('navEdit.selectGroups') }}
+ q-select(
+ style='width: 100%; max-width: calc(50% - 34px);'
+ outlined
+ v-model='state.current.visibilityGroups'
+ :options='state.groups'
+ option-value='id'
+ option-label='name'
+ emit-value
+ map-options
+ dense
+ multiple
+ :aria-label='t(`navEdit.selectGroups`)'
+ )
+ q-card.q-pa-md.q-mt-md.flex
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash-alt'
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='removeItem(state.current.id)'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/components/NavSidebar.vue b/frontend/src/components/NavSidebar.vue
new file mode 100644
index 00000000..72f4e3ae
--- /dev/null
+++ b/frontend/src/components/NavSidebar.vue
@@ -0,0 +1,184 @@
+
+q-scroll-area.sidebar-nav(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ )
+ q-list.sidebar-nav-list(
+ clickable
+ dense
+ dark
+ )
+ template(v-for='item of siteStore.nav.items', :key='item.id')
+ q-item-label.sidebar-nav-header.text-caption.text-wordbreak-all(
+ v-if='item.type === `header`'
+ header
+ ) {{ item.label }}
+ q-expansion-item(
+ v-else-if='item.type === `link` && item.children?.length > 0'
+ :icon='item.icon'
+ :label='item.label'
+ dense
+ )
+ q-list(
+ clickable
+ dense
+ dark
+ )
+ q-item(
+ v-for='itemChild of item.children'
+ :to='itemChild.target'
+ :key='itemChild.id'
+ )
+ q-item-section(side)
+ q-icon(:name='itemChild.icon', color='white')
+ q-item-section.text-wordbreak-all.text-white {{ itemChild.label }}
+ q-item(
+ v-else-if='item.type === `link`'
+ :to='item.target'
+ )
+ q-item-section(side)
+ q-icon(:name='item.icon', color='white')
+ q-item-section.text-wordbreak-all.text-white {{ item.label }}
+ q-separator(
+ v-else-if='item.type === `separator`'
+ dark
+ )
+
+
+
+
+
diff --git a/frontend/src/components/PageActionsCol.vue b/frontend/src/components/PageActionsCol.vue
new file mode 100644
index 00000000..bb7ea2fa
--- /dev/null
+++ b/frontend/src/components/PageActionsCol.vue
@@ -0,0 +1,343 @@
+
+.page-actions.column.items-stretch.order-last(:class='editorStore.isActive ? `is-editor` : ``')
+ template(v-if='userStore.can(`edit:pages`)')
+ q-btn.q-py-md(
+ flat
+ icon='las la-pen-nib'
+ :color='editorStore.isActive ? `white` : `deep-orange-9`'
+ aria-label='Page Properties'
+ @click='togglePageProperties'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Properties
+ q-btn.q-py-md(
+ v-if='flagsStore.experimental'
+ flat
+ icon='las la-project-diagram'
+ :color='editorStore.isActive ? `white` : `deep-orange-9`'
+ aria-label='Page Data'
+ @click='togglePageData'
+ disable
+ )
+ q-tooltip(anchor='center left' self='center right') Page Data
+ q-btn.q-py-md(
+ v-if='editorStore.isActive'
+ flat
+ color='white'
+ :text-color='hasPendingAssets ? `white` : `deep-orange-3`'
+ aria-label='Pending Asset Uploads'
+ )
+ q-icon(name='mdi-image-sync-outline')
+ q-badge.page-actions-pending-badge(
+ v-if='hasPendingAssets'
+ color='white'
+ text-color='orange-9'
+ rounded
+ floating
+ )
+ strong {{ editorStore.pendingAssets.length * 1 }}
+ q-tooltip(anchor='center left' self='center right') Pending Asset Uploads
+ q-menu(
+ ref='menuPendingAssets'
+ anchor='top left'
+ self='top right'
+ :offset='[10, 0]'
+ )
+ q-card(style='width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/color-data-pending.svg', left, size='sm')
+ span Pending Asset Uploads
+ q-card-section(v-if='!hasPendingAssets') There are no assets pending uploads.
+ q-list(v-else, separator)
+ q-item(
+ v-for='item of editorStore.pendingAssets'
+ :key='item.id'
+ )
+ q-item-section(side)
+ q-icon(name='las la-file-image')
+ q-item-section {{ item.fileName }}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ color='negative'
+ round
+ icon='las la-times'
+ size='xs'
+ flat
+ @click='removePendingAsset(item)'
+ )
+ q-card-section.card-actions
+ em.text-caption Assets that are pasted or dropped onto this page will be held here until the page is saved.
+ q-separator.q-my-sm(inset)
+ q-btn.q-py-md(
+ flat
+ icon='las la-history'
+ :color='editorStore.isActive ? `white` : `grey`'
+ aria-label='Page History'
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center left' self='center right') Page History
+ q-btn.q-py-md(
+ flat
+ icon='las la-code'
+ :color='editorStore.isActive ? `white` : `grey`'
+ aria-label='Page Source'
+ @click='viewPageSource'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Source
+ template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
+ q-separator.q-my-sm(inset)
+ q-btn.q-py-sm(
+ flat
+ icon='las la-ellipsis-h'
+ :color='editorStore.isActive ? `deep-orange-2` : `grey`'
+ aria-label='Page Actions'
+ )
+ q-tooltip(anchor='center left' self='center right') Page Actions
+ q-menu.translucent-menu(
+ anchor='top left'
+ self='top right'
+ auto-close
+ transition-show='jump-left'
+ )
+ q-list(padding, style='min-width: 225px;')
+ q-item(clickable, disabled, v-if='userStore.can(`manage:pages`)')
+ 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, v-if='userStore.can(`edit:pages`)', @click='rerenderPage')
+ q-item-section.items-center(avatar)
+ q-icon(color='deep-orange-9', name='las la-magic', size='sm')
+ q-item-section
+ q-item-label Rerender Page
+ q-item(clickable, disabled)
+ 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
+ template(v-if='!(editorStore.isActive && editorStore.mode === `create`)')
+ q-btn.q-py-sm(
+ v-if='userStore.can(`create:pages`)'
+ flat
+ icon='las la-copy'
+ :color='editorStore.isActive ? `deep-orange-2` : `grey`'
+ aria-label='Duplicate Page'
+ @click='duplicatePage'
+ )
+ q-tooltip(anchor='center left' self='center right') Duplicate Page
+ q-btn.q-py-sm(
+ v-if='userStore.can(`manage:pages`)'
+ flat
+ icon='las la-share'
+ :color='editorStore.isActive ? `deep-orange-2` : `grey`'
+ aria-label='Rename / Move Page'
+ @click='renamePage'
+ )
+ q-tooltip(anchor='center left' self='center right') Rename / Move Page
+ q-btn.q-py-sm(
+ v-if='userStore.can(`delete:pages`)'
+ flat
+ icon='las la-trash'
+ :color='editorStore.isActive ? `deep-orange-2` : `grey`'
+ aria-label='Delete Page'
+ @click='deletePage'
+ :class='editorStore.isActive ? `q-pb-md` : ``'
+ )
+ q-tooltip(anchor='center left' self='center right') Delete Page
+ span.page-actions-mode(v-else) {{ t('common.actions.newPage') }}
+
+
+
+
+
diff --git a/frontend/src/components/PageDataDialog.vue b/frontend/src/components/PageDataDialog.vue
new file mode 100644
index 00000000..e644cb70
--- /dev/null
+++ b/frontend/src/components/PageDataDialog.vue
@@ -0,0 +1,162 @@
+
+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='state.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='state.mode'
+ inline-label
+ no-caps
+ )
+ q-tab(
+ name='visual'
+ label='Visual'
+ )
+ q-tab(
+ name='code'
+ label='YAML'
+ )
+ q-scroll-area(
+ :thumb-style='siteStore.thumbStyle'
+ :bar-style='siteStore.barStyle'
+ style='height: calc(100% - 50px - 75px - 48px);'
+ )
+ q-card-section(v-if='state.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='state.content'
+ :options='{ mode: `text/yaml` }'
+ )
+
+ q-dialog(
+ v-model='state.showDataTemplateDialog'
+ )
+ page-data-template-dialog
+
+
+
+
+
diff --git a/frontend/src/components/PageDataTemplateDialog.vue b/frontend/src/components/PageDataTemplateDialog.vue
new file mode 100644
index 00000000..40365786
--- /dev/null
+++ b/frontend/src/components/PageDataTemplateDialog.vue
@@ -0,0 +1,446 @@
+
+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='state.selectedTemplateId'
+ :options='siteStore.pageDataTemplates'
+ 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='state.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='state.dragStarted = true'
+ @end='state.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='state.dragStarted = true'
+ @end='state.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='siteStore.thumbStyle'
+ :bar-style='siteStore.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='state.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='(state.dragStarted || state.tmpl.data.length < 1 ? `page-datatmpl-box` : ``)')
+ .text-caption.text-primary.q-pa-md(v-if='state.tmpl.data.length < 1 && !state.dragStarted'): em {{t('editor.pageData.dragDropHint')}}
+ draggable(
+ class='q-list rounded-borders'
+ :list='state.tmpl.data'
+ group='shared'
+ :animation='150'
+ handle='.handle'
+ @end='state.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='siteStore.pageDataTemplates.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/frontend/src/components/PageDeleteDialog.vue b/frontend/src/components/PageDeleteDialog.vue
new file mode 100644
index 00000000..5dbeb3f2
--- /dev/null
+++ b/frontend/src/components/PageDeleteDialog.vue
@@ -0,0 +1,109 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 550px; max-width: 850px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-delete-bin.svg', left, size='sm')
+ span {{t(`pageDeleteDialog.title`)}}
+ q-card-section
+ .text-body2
+ i18n-t(keypath='pageDeleteDialog.confirm')
+ template(v-slot:name)
+ strong {{pageName}}
+ .text-caption.text-grey.q-mt-sm {{t('pageDeleteDialog.pageId', { id: pageId })}}
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/PageHeader.vue b/frontend/src/components/PageHeader.vue
new file mode 100644
index 00000000..4bb37333
--- /dev/null
+++ b/frontend/src/components/PageHeader.vue
@@ -0,0 +1,409 @@
+
+.page-header.row
+ //- PAGE ICON
+ .col-auto.q-pl-md.flex.items-center
+ q-btn.rounded-borders(
+ v-if='editorStore.isActive'
+ padding='none'
+ size='37px'
+ :icon='pageStore.icon'
+ color='primary'
+ flat
+ )
+ q-badge(color='grey' floating rounded)
+ q-icon(name='las la-pen', size='xs', padding='xs xs')
+ q-menu(content-class='shadow-7')
+ .q-pa-lg: em [ TODO: Icon Picker Dialog ]
+ // icon-picker-dialog(v-model='pageStore.icon')
+ q-icon.rounded-borders(
+ v-else
+ :name='pageStore.icon'
+ size='64px'
+ color='primary'
+ )
+ //- PAGE HEADER
+ .col.q-pa-md
+ .text-h4.page-header-title
+ span {{pageStore.title}}
+ template(v-if='editorStore.isActive')
+ span.text-grey(v-if='!pageStore.title') {{ t(`editor.props.title`)}}
+ q-btn.acrylic-btn.q-ml-md(
+ icon='las la-pen'
+ flat
+ padding='xs'
+ size='sm'
+ )
+ q-popup-edit(
+ v-model='pageStore.title'
+ auto-save
+ v-slot='scope'
+ )
+ q-input(
+ outlined
+ style='width: 450px;'
+ v-model='scope.value'
+ dense
+ autofocus
+ @keyup.enter='scope.set'
+ :label='t(`editor.props.title`)'
+ )
+ .text-subtitle2.page-header-subtitle
+ span {{ pageStore.description }}
+ template(v-if='editorStore.isActive')
+ span.text-grey(v-if='!pageStore.description') {{ t(`editor.props.shortDescription`)}}
+ q-btn.acrylic-btn.q-ml-md(
+ icon='las la-pen'
+ flat
+ padding='none xs'
+ size='xs'
+ )
+ q-popup-edit(
+ v-model='pageStore.description'
+ auto-save
+ v-slot='scope'
+ )
+ q-input(
+ outlined
+ style='width: 450px;'
+ v-model='scope.value'
+ dense
+ autofocus
+ @keyup.enter='scope.set'
+ :label='t(`editor.props.shortDescription`)'
+ )
+ //- PAGE ACTIONS
+ .col-auto.q-pa-md.flex.items-center.justify-end
+ template(v-if='!editorStore.isActive')
+ q-btn.q-ml-md(
+ v-if='userStore.authenticated'
+ flat
+ dense
+ icon='las la-bell'
+ color='grey'
+ aria-label='Watch Page'
+ @click='notImplemented'
+ )
+ q-tooltip Watch Page
+ q-btn.q-ml-md(
+ v-if='userStore.authenticated'
+ flat
+ dense
+ icon='las la-bookmark'
+ color='grey'
+ aria-label='Bookmark Page'
+ @click='notImplemented'
+ )
+ q-tooltip Bookmark Page
+ q-btn.q-ml-md(
+ v-if='siteStore.theme.showSharingMenu'
+ flat
+ dense
+ icon='las la-share-alt'
+ color='grey'
+ aria-label='Share'
+ )
+ q-tooltip Share
+ social-sharing-menu
+ q-btn.q-ml-md(
+ v-if='siteStore.theme.showPrintBtn'
+ flat
+ dense
+ icon='las la-print'
+ color='grey'
+ aria-label='Print'
+ @click='printPage'
+ )
+ q-tooltip Print
+ template(v-if='editorStore.isActive')
+ q-btn.q-ml-md.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :href='siteStore.docsBase + `/editor/${editorStore.editor}`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-ml-sm.acrylic-btn(
+ icon='las la-cog'
+ flat
+ color='grey'
+ :aria-label='t(`editor.settings`)'
+ @click='openEditorSettings'
+ )
+ q-tooltip {{ t(`editor.settings`) }}
+ template(v-if='editorStore.isActive || editorStore.hasPendingChanges')
+ q-btn.acrylic-btn.q-ml-sm(
+ flat
+ icon='las la-times'
+ color='negative'
+ :label='editorStore.hasPendingChanges ? t(`common.actions.discard`) : t(`common.actions.close`)'
+ :aria-label='editorStore.hasPendingChanges ? t(`common.actions.discard`) : t(`common.actions.close`)'
+ no-caps
+ @click='discardChanges'
+ )
+ q-btn.acrylic-btn.q-ml-sm(
+ v-if='editorStore.mode === `create`'
+ flat
+ icon='las la-check'
+ color='positive'
+ :label='t(`editor.createPage`)'
+ :aria-label='t(`editor.createPage`)'
+ no-caps
+ @click='createPage'
+ )
+ q-btn-group.q-ml-sm(
+ v-else
+ flat
+ )
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-check'
+ color='positive'
+ :label='t(`common.actions.saveChanges`)'
+ :aria-label='t(`common.actions.saveChanges`)'
+ :disabled='!editorStore.hasPendingChanges'
+ no-caps
+ @click.exact='saveChanges(false)'
+ @click.ctrl.exact='saveChanges(true)'
+ )
+ template(v-if='editorStore.isActive')
+ q-separator(vertical, dark)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-check-double'
+ color='positive'
+ :aria-label='t(`common.actions.saveAndClose`)'
+ :disabled='!editorStore.hasPendingChanges'
+ @click='saveChanges(true)'
+ )
+ q-tooltip {{ t(`common.actions.saveAndClose`) }}
+ template(v-else-if='userStore.can(`edit:pages`)')
+ q-btn.acrylic-btn.q-ml-md(
+ flat
+ icon='las la-edit'
+ color='deep-orange-9'
+ :label='t(`common.actions.edit`)'
+ :aria-label='t(`common.actions.edit`)'
+ no-caps
+ @click='editPage'
+ )
+
+
+
diff --git a/frontend/src/components/PageNewMenu.vue b/frontend/src/components/PageNewMenu.vue
new file mode 100644
index 00000000..969d80c4
--- /dev/null
+++ b/frontend/src/components/PageNewMenu.vue
@@ -0,0 +1,133 @@
+
+q-menu.translucent-menu(
+ auto-close
+ anchor='bottom right'
+ self='top right'
+ )
+ q-list(padding)
+ q-item(
+ clickable
+ @click='create(`wysiwyg`)'
+ v-if='siteStore.editors.wysiwyg && flagsStore.experimental'
+ )
+ blueprint-icon(icon='google-presentation')
+ q-item-section.q-pr-sm New Page
+ q-item(
+ clickable
+ @click='create(`markdown`)'
+ v-if='siteStore.editors.markdown'
+ )
+ blueprint-icon(icon='markdown')
+ q-item-section.q-pr-sm New Markdown Page
+ template(v-if='flagsStore.experimental')
+ q-item(
+ clickable
+ @click='create(`asciidoc`)'
+ v-if='siteStore.editors.asciidoc'
+ )
+ blueprint-icon(icon='asciidoc')
+ q-item-section.q-pr-sm New AsciiDoc 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
+ template(v-if='props.hideAssetBtn === false')
+ q-separator.q-my-sm(inset)
+ q-item(
+ clickable
+ @click='openFileManager'
+ )
+ blueprint-icon(icon='add-image')
+ q-item-section.q-pr-sm Upload Media Asset
+ template(v-if='props.showNewFolder')
+ q-separator.q-my-sm(inset)
+ q-item(
+ clickable
+ @click='newFolder'
+ )
+ blueprint-icon(icon='add-folder')
+ q-item-section.q-pr-sm New Folder
+
+
+
diff --git a/frontend/src/components/PagePropertiesDialog.vue b/frontend/src/components/PagePropertiesDialog.vue
new file mode 100644
index 00000000..9b8ab1be
--- /dev/null
+++ b/frontend/src/components/PagePropertiesDialog.vue
@@ -0,0 +1,436 @@
+
+q-card.page-properties-dialog
+ .floating-sidepanel-quickaccess.animated.fadeIn(v-if='state.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.q-mr-sm(
+ dense
+ flat
+ rounded
+ color='white'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/editor/properties`'
+ target='_blank'
+ type='a'
+ )
+ q-btn(
+ icon='las la-times'
+ dense
+ flat
+ v-close-popup
+ )
+ q-scroll-area(
+ ref='scrollArea'
+ :thumb-style='siteStore.scrollStyle.thumb'
+ :bar-style='siteStore.scrollStyle.bar'
+ style='height: calc(100% - 50px);'
+ )
+ q-card-section(id='refCardInfo')
+ .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='pageStore.title'
+ :label='t(`editor.props.title`)'
+ outlined
+ dense
+ )
+ q-input(
+ v-model='pageStore.description'
+ :label='t(`editor.props.shortDescription`)'
+ outlined
+ dense
+ )
+ q-input(
+ v-model='pageStore.icon'
+ :label='t(`editor.props.icon`)'
+ outlined
+ dense
+ )
+ template(#append)
+ q-icon.cursor-pointer(
+ name='las la-icons'
+ color='primary'
+ )
+ q-menu(content-class='shadow-7')
+ .q-pa-lg: em [ TODO: Icon Picker Dialog ]
+ // icon-picker-dialog(v-model='pageStore.icon')
+ q-input(
+ v-if='pageStore.path !== `home`'
+ v-model='pageStore.alias'
+ :label='t(`editor.props.alias`)'
+ outlined
+ dense
+ prefix='/a/'
+ )
+ q-card-section.alt-card(id='refCardPublishState')
+ .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='pageStore.publishState'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: t('editor.props.draft'), value: 'draft' },
+ { label: t('editor.props.published'), value: 'published' },
+ { label: t('editor.props.dateRange'), value: 'scheduled' }
+ ]`
+ )
+ .text-caption(v-if='pageStore.publishState === `published`'): em {{t('editor.props.publishedHint')}}
+ .text-caption(v-else-if='pageStore.publishState === `draft`'): em {{t('editor.props.draftHint')}}
+ template(v-else-if='pageStore.publishState === `scheduled`')
+ .text-caption: em {{t('editor.props.dateRangeHint')}}
+ q-date(
+ v-model='publishingRange'
+ range
+ flat
+ bordered
+ landscape
+ minimal
+ )
+ q-card-section(id='refCardRelations')
+ .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='pageStore.relations.length > 0'
+ separator
+ bordered
+ )
+ q-item(v-for='rel of pageStore.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(id='refCardScripts')
+ .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(id='refCardSidebar')
+ .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='pageStore.showSidebar'
+ dense
+ :label='t(`editor.props.showSidebar`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-if='pageStore.showSidebar'
+ v-model='pageStore.showToc'
+ dense
+ :label='t(`editor.props.showToc`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div(
+ v-if='pageStore.showSidebar && pageStore.showToc'
+ style='padding-left: 40px;'
+ )
+ .text-caption {{t('editor.props.tocMinMaxDepth')}} #[strong (H{{pageStore.tocDepth.min}} → H{{pageStore.tocDepth.max}})]
+ q-range(
+ v-model='pageStore.tocDepth'
+ :min='1'
+ :max='6'
+ color='primary'
+ :left-label-value='`H` + pageStore.tocDepth.min'
+ :right-label-value='`H` + pageStore.tocDepth.max'
+ snap
+ label
+ markers
+ )
+ div
+ q-toggle(
+ v-if='pageStore.showSidebar'
+ v-model='pageStore.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(id='refCardSocial')
+ .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='pageStore.allowComments'
+ dense
+ :label='t(`editor.props.allowComments`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='pageStore.allowContributions'
+ dense
+ :label='t(`editor.props.allowContributions`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='pageStore.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(id='refCardTags')
+ .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(id='refCardVisibility')
+ .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='pageStore.isBrowsable'
+ dense
+ :label='$t(`editor.props.showInTree`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='pageStore.isSearchable'
+ dense
+ :label='$t(`editor.props.isSearchable`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div
+ q-toggle(
+ v-model='state.requirePassword'
+ @update:model-value='toggleRequirePassword'
+ dense
+ :label='$t(`editor.props.requirePassword`)'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ )
+ div(
+ v-if='state.requirePassword'
+ style='padding-left: 40px;'
+ )
+ q-input(
+ ref='iptPagePassword'
+ v-model='pageStore.password'
+ :label='t(`editor.props.password`)'
+ :hint='t(`editor.props.passwordHint`)'
+ outlined
+ dense
+ )
+ q-dialog(
+ v-model='state.showRelationDialog'
+ )
+ page-relation-dialog(:edit-id='state.editRelationId')
+
+ q-dialog(
+ v-model='state.showScriptsDialog'
+ )
+ page-scripts-dialog(:mode='state.pageScriptsMode')
+
+
+
diff --git a/frontend/src/components/PageReasonForChangeDialog.vue b/frontend/src/components/PageReasonForChangeDialog.vue
new file mode 100644
index 00000000..407ad39e
--- /dev/null
+++ b/frontend/src/components/PageReasonForChangeDialog.vue
@@ -0,0 +1,105 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-query.svg', left, size='sm')
+ span {{t(`editor.reasonForChange.title`)}}
+ q-card-section
+ .text-body2(v-if='props.required') {{t(`editor.reasonForChange.required`)}}
+ .text-body2(v-else) {{t(`editor.reasonForChange.optional`)}}
+ q-form.q-pb-sm(ref='reasonForm', @submit.prevent='commit')
+ q-item
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.reason'
+ dense
+ :rules='reasonValidation'
+ hide-bottom-space
+ :label='t(`editor.reasonForChange.field`)'
+ :aria-label='t(`editor.reasonForChange.field`)'
+ 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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='commit'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/PageRelationDialog.vue b/frontend/src/components/PageRelationDialog.vue
new file mode 100644
index 00000000..a82a573f
--- /dev/null
+++ b/frontend/src/components/PageRelationDialog.vue
@@ -0,0 +1,238 @@
+
+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='state.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='state.label'
+ )
+ template(v-if='state.pos !== `center`')
+ q-input(
+ outlined
+ dense
+ :label='t(`editor.pageRel.caption`)'
+ v-model='state.caption'
+ )
+ q-btn.rounded-borders(
+ :label='t(`editor.pageRel.selectIcon`)'
+ color='primary'
+ outline
+ )
+ q-menu(content-class='shadow-7')
+ icon-picker-dialog(v-model='state.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='state.pos === `left`'
+ padding='sm md'
+ outline
+ :icon='state.icon'
+ no-caps
+ color='primary'
+ )
+ .column.text-left.q-pl-md
+ .text-body2: strong {{state.label}}
+ .text-caption {{state.caption}}
+ q-btn.full-width(
+ v-else-if='state.pos === `center`'
+ :label='state.label'
+ color='primary'
+ flat
+ no-caps
+ :icon='state.icon'
+ )
+ q-btn(
+ v-else-if='state.pos === `right`'
+ padding='sm md'
+ outline
+ :icon-right='state.icon'
+ no-caps
+ color='primary'
+ )
+ .column.text-left.q-pr-md
+ .text-body2: strong {{state.label}}
+ .text-caption {{state.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/frontend/src/components/PageScriptsDialog.vue b/frontend/src/components/PageScriptsDialog.vue
new file mode 100644
index 00000000..3a284190
--- /dev/null
+++ b/frontend/src/components/PageScriptsDialog.vue
@@ -0,0 +1,132 @@
+
+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.' + props.mode)}}
+ q-space
+ q-chip(
+ square
+ style='background-color: rgba(0,0,0,.1)'
+ text-color='white'
+ )
+ .text-caption {{languageLabel}}
+ div(style='min-height: 450px;')
+ q-no-ssr(:placeholder='t(`common.loading`)')
+ util-code-editor(
+ v-if='state.showEditor'
+ ref='editor'
+ v-model='state.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/frontend/src/components/PageSourceOverlay.vue b/frontend/src/components/PageSourceOverlay.vue
new file mode 100644
index 00000000..046e99e3
--- /dev/null
+++ b/frontend/src/components/PageSourceOverlay.vue
@@ -0,0 +1,161 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/fluent-code.svg', left, size='md')
+ span Page Source
+ q-space
+ transition(name='syncing')
+ q-spinner-tail.q-mr-sm(
+ v-show='state.loading > 0'
+ color='accent'
+ size='24px'
+ )
+ q-btn.q-mr-md(
+ icon='las la-download'
+ color='teal-3'
+ dense
+ flat
+ @click='download'
+ )
+ q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.download`)}}
+ q-btn(
+ icon='las la-times'
+ color='pink-2'
+ dense
+ flat
+ @click='close'
+ )
+ q-tooltip(anchor='bottom middle', self='top middle') {{t(`common.actions.close`)}}
+
+ q-page-container
+ q-page.bg-dark-6.text-white.font-robotomono.pagesource
+ q-scroll-area(
+ :thumb-style='thumb'
+ :bar-style='bar'
+ :horizontal-thumb-style='{ height: `5px` }'
+ style="width: 100%; height: calc(100vh - 100px);"
+ )
+ pre.q-px-md(v-text='state.content')
+
+
+
+
+
diff --git a/frontend/src/components/PageTags.vue b/frontend/src/components/PageTags.vue
new file mode 100644
index 00000000..42ce6b69
--- /dev/null
+++ b/frontend/src/components/PageTags.vue
@@ -0,0 +1,139 @@
+
+.q-gutter-xs
+ template(v-if='pageStore.tags && pageStore.tags.length > 0')
+ q-chip(
+ square
+ color='secondary'
+ text-color='white'
+ dense
+ :clickable='!props.edit'
+ :removable='props.edit'
+ @remove='removeTag(tag)'
+ v-for='tag of pageStore.tags'
+ :key='`tag-` + tag'
+ )
+ q-icon.q-mr-xs(name='las la-hashtag', size='14px')
+ span.text-caption {{tag}}
+ q-select.q-mt-md(
+ v-if='props.edit'
+ outlined
+ v-model='pageStore.tags'
+ :options='state.filteredTags'
+ dense
+ options-dense
+ use-input
+ use-chips
+ multiple
+ hide-selected
+ hide-dropdown-icon
+ :input-debounce='0'
+ new-value-mode='add-unique'
+ @new-value='createTag'
+ @filter='filterTags'
+ placeholder='Select or create tags...'
+ :loading='state.loading'
+ )
+ template(v-slot:option='scope')
+ q-item(v-bind='scope.itemProps')
+ q-item-section(side)
+ q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
+ q-item-section
+ q-item-label
+ span(v-html='scope.opt')
+
+
+
diff --git a/frontend/src/components/PasskeyCreateDialog.vue b/frontend/src/components/PasskeyCreateDialog.vue
new file mode 100644
index 00000000..4bed6012
--- /dev/null
+++ b/frontend/src/components/PasskeyCreateDialog.vue
@@ -0,0 +1,83 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card(style='min-width: 650px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-add-key.svg', left, size='sm')
+ span {{t(`profile.passkeysAdd`)}}
+ .q-py-sm
+ .text-body2.q-px-md.q-py-sm {{t(`profile.passkeysNameHint`)}}
+ q-item
+ blueprint-icon(icon='key')
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.name'
+ dense
+ hide-bottom-space
+ :label='t(`profile.passkeysName`)'
+ :aria-label='t(`profile.passkeysName`)'
+ autofocus
+ @keyup.enter='save'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ )
+
+
+
diff --git a/frontend/src/components/RerenderPageDialog.vue b/frontend/src/components/RerenderPageDialog.vue
new file mode 100644
index 00000000..1478d7d0
--- /dev/null
+++ b/frontend/src/components/RerenderPageDialog.vue
@@ -0,0 +1,90 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide' position='bottom', persistent)
+ q-card(style='width: 350px;')
+ q-linear-progress(query, color='page')
+ q-card-section.text-center {{ t('renderPageDialog.loading') }}
+
+
+
diff --git a/frontend/src/components/SetupTfaDialog.vue b/frontend/src/components/SetupTfaDialog.vue
new file mode 100644
index 00000000..50783f26
--- /dev/null
+++ b/frontend/src/components/SetupTfaDialog.vue
@@ -0,0 +1,235 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card.setup2fadialog(style='min-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-fingerprint.svg', left, size='sm')
+ span {{t(`profile.authSetTfa`)}}
+ template(v-if='!state.isInit')
+ q-linear-progress(query, color='positive')
+ q-card-section.text-center.text-grey {{t(`profile.authSetTfaLoading`)}}
+ template(v-else)
+ q-card-section.text-center
+ p {{t('auth.tfaSetupInstrFirst')}}
+ div(style='justify-content: center; display: flex;')
+ div(v-html='state.tfaQRImage', style='width: 200px;')
+ p.q-mt-sm {{t('auth.tfaSetupInstrSecond')}}
+ .flex.justify-center
+ v-otp-input(
+ v-model:value='state.securityCode'
+ :num-inputs='6'
+ :should-auto-focus='true'
+ input-classes='otp-input'
+ input-type='number'
+ separator=''
+ )
+ q-inner-loading(:showing='state.isLoading')
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ @click='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`auth.tfa.verifyToken`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='state.isLoading'
+ )
+
+
+
+
+
diff --git a/frontend/src/components/SideDialog.vue b/frontend/src/components/SideDialog.vue
new file mode 100644
index 00000000..4dd0e47c
--- /dev/null
+++ b/frontend/src/components/SideDialog.vue
@@ -0,0 +1,130 @@
+
+q-dialog(
+ v-model='siteStore.sideDialogShown'
+ position='right'
+ full-height
+ transition-show='jump-left'
+ transition-hide='jump-right'
+ class='floating-sidepanel'
+ no-shake
+ )
+ component(:is='sideDialogs[siteStore.sideDialogComponent]')
+
+
+
+
+
diff --git a/frontend/src/components/SiteActivateDialog.vue b/frontend/src/components/SiteActivateDialog.vue
new file mode 100644
index 00000000..4d51c3dc
--- /dev/null
+++ b/frontend/src/components/SiteActivateDialog.vue
@@ -0,0 +1,135 @@
+
+q-dialog(ref='dialogRef', @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 {{props.targetState ? t(`admin.sites.activate`) : t(`admin.sites.deactivate`)}}
+ q-card-section
+ .text-body2
+ i18n-t(:keypath='props.targetState ? `admin.sites.activateConfirm` : `admin.sites.deactivateConfirm`')
+ template(v-slot:siteTitle)
+ strong {{props.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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='props.targetState ? t(`common.actions.activate`) : t(`common.actions.deactivate`)'
+ :color='props.targetState ? `positive` : `negative`'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/SiteCreateDialog.vue b/frontend/src/components/SiteCreateDialog.vue
new file mode 100644
index 00000000..48c7a46d
--- /dev/null
+++ b/frontend/src/components/SiteCreateDialog.vue
@@ -0,0 +1,154 @@
+
+q-dialog(ref='dialogRef', @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='state.siteName'
+ dense
+ :rules='siteNameValidation'
+ 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='state.siteHostname'
+ dense
+ :rules='siteHostnameValidation'
+ :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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/SiteDeleteDialog.vue b/frontend/src/components/SiteDeleteDialog.vue
new file mode 100644
index 00000000..0bfc52df
--- /dev/null
+++ b/frontend/src/components/SiteDeleteDialog.vue
@@ -0,0 +1,115 @@
+
+q-dialog(ref='dialogRef', @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 {{props.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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/SocialSharingMenu.vue b/frontend/src/components/SocialSharingMenu.vue
new file mode 100644
index 00000000..7f77d4d2
--- /dev/null
+++ b/frontend/src/components/SocialSharingMenu.vue
@@ -0,0 +1,124 @@
+
+q-menu(
+ auto-close
+ anchor='bottom middle'
+ self='top middle'
+ @show='menuShown'
+ @before-hide='menuHidden'
+ )
+ q-list(dense, padding)
+ q-item(clickable, 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(props.title) + `&body=` + encodeURIComponent(urlFormatted) + `%0D%0A%0D%0A` + encodeURIComponent(props.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
+
+
+
diff --git a/frontend/src/components/StatusLight.vue b/frontend/src/components/StatusLight.vue
new file mode 100644
index 00000000..530ff06d
--- /dev/null
+++ b/frontend/src/components/StatusLight.vue
@@ -0,0 +1,65 @@
+
+.status-light(:class='cssClasses')
+
+
+
+
+
diff --git a/frontend/src/components/TableEditorOverlay.vue b/frontend/src/components/TableEditorOverlay.vue
new file mode 100644
index 00000000..534285e8
--- /dev/null
+++ b/frontend/src/components/TableEditorOverlay.vue
@@ -0,0 +1,87 @@
+
+q-layout(view='hHh lpR fFf', container)
+ q-header.card-header.q-px-md.q-py-sm
+ q-icon(name='img:/_assets/icons/color-data-grid.svg', left, size='md')
+ span {{t(`editor.tableEditor.title`)}}
+ q-space
+ q-btn.q-mr-sm(
+ flat
+ rounded
+ color='white'
+ :aria-label='t(`common.actions.refresh`)'
+ icon='las la-question-circle'
+ :href='siteStore.docsBase + `/admin/editors/markdown`'
+ target='_blank'
+ type='a'
+ )
+ q-btn-group(push)
+ q-btn(
+ push
+ color='white'
+ text-color='grey-7'
+ :label='t(`common.actions.cancel`)'
+ :aria-label='t(`common.actions.cancel`)'
+ 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'
+ :disabled='state.loading > 0'
+ )
+ q-page-container
+ q-page.q-pa-md
+ div(ref='tblRef')
+
+ q-inner-loading(:showing='state.loading > 0')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/TreeBrowserDialog.vue b/frontend/src/components/TreeBrowserDialog.vue
new file mode 100644
index 00000000..0a36c170
--- /dev/null
+++ b/frontend/src/components/TreeBrowserDialog.vue
@@ -0,0 +1,506 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card.page-save-dialog(style='width: 860px; max-width: 90vw;')
+ q-card-section.card-header(v-if='props.mode === `savePage`')
+ q-icon(name='img:/_assets/icons/fluent-save-as.svg', left, size='sm')
+ span {{ t('pageSaveDialog.title') }}
+ q-card-section.card-header(v-else-if='props.mode === `duplicatePage`')
+ q-icon(name='img:/_assets/icons/color-documents.svg', left, size='sm')
+ span {{ t('pageDuplicateDialog.title') }}
+ q-card-section.card-header(v-else-if='props.mode === `renamePage`')
+ q-icon(name='img:/_assets/icons/fluent-rename.svg', left, size='sm')
+ span {{ t('pageRenameDialog.title') }}
+ .row.page-save-dialog-browser
+ .col-4
+ q-scroll-area(
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 300px'
+ )
+ .q-px-sm
+ tree(
+ :nodes='state.treeNodes'
+ :roots='state.treeRoots'
+ v-model:selected='state.currentFolderId'
+ @lazy-load='treeLazyLoad'
+ :use-lazy-load='true'
+ @context-action='treeContextAction'
+ :context-action-list='[`newFolder`]'
+ :display-mode='state.displayMode'
+ )
+ .col-8
+ q-list.page-save-dialog-filelist(dense)
+ q-item(
+ v-for='item of files'
+ :key='item.id'
+ clickable
+ active-class='active'
+ :active='item.id === state.currentFileId'
+ @click='selectItem(item)'
+ )
+ q-item-section(side)
+ q-icon(:name='item.icon', size='sm')
+ q-item-section
+ q-item-label {{item.title}}
+ .page-save-dialog-path.font-robotomono {{ currentFolderPath }}
+ q-list.q-py-sm
+ q-item
+ blueprint-icon(icon='new-document')
+ q-item-section
+ q-input(
+ v-model='state.title'
+ label='Page Title'
+ dense
+ outlined
+ autofocus
+ @focus='state.currentFileId = null'
+ )
+ q-item
+ blueprint-icon(icon='file-submodule')
+ q-item-section
+ q-input(
+ v-model='state.path'
+ label='Path Name'
+ dense
+ outlined
+ @focus='state.pathDirty = true; state.currentFileId = null'
+ )
+ //- template(#append)
+ //- q-badge(outline, color='grey', label='valid')
+ q-card-actions.card-actions.q-px-md
+ q-btn.acrylic-btn(
+ icon='las la-ellipsis-h'
+ color='blue-grey'
+ padding='xs sm'
+ flat
+ )
+ q-tooltip(anchor='center right' self='center left') Display Options
+ q-menu(
+ auto-close
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ anchor='top left'
+ self='bottom left'
+ )
+ q-card.q-pa-sm
+ q-list(dense)
+ q-item(clickable, @click='state.displayMode = `path`')
+ q-item-section(side)
+ q-icon(
+ :name='state.displayMode === `path` ? `las la-check-circle` : `las la-circle`'
+ :color='state.displayMode === `path` ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm {{ t('pageSaveDialog.displayModePath') }}
+ q-item(clickable, @click='state.displayMode = `title`')
+ q-item-section(side)
+ q-icon(
+ :name='state.displayMode === `title` ? `las la-check-circle` : `las la-circle`'
+ :color='state.displayMode === `title` ? `positive` : `grey`'
+ size='xs'
+ )
+ q-item-section.q-pr-sm {{ t('pageSaveDialog.displayModeTitle') }}
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-times'
+ :label='t(`common.actions.cancel`)'
+ color='grey-7'
+ padding='xs md'
+ @click='onDialogCancel'
+ flat
+ )
+ q-btn(
+ icon='las la-check'
+ :label='t(`common.actions.save`)'
+ unelevated
+ color='primary'
+ padding='xs md'
+ @click='save'
+ v-close-popup
+ )
+
+
+
+
+
diff --git a/frontend/src/components/TreeLevel.vue b/frontend/src/components/TreeLevel.vue
new file mode 100644
index 00000000..08f1452c
--- /dev/null
+++ b/frontend/src/components/TreeLevel.vue
@@ -0,0 +1,105 @@
+
+ul.treeview-level
+ //- ROOT NODE
+ li.treeview-node(v-if='!props.parentId')
+ .treeview-label(@click='setRoot', :class='{ "active": !selection }')
+ q-icon(name='img:/_assets/icons/fluent-ftp.svg', size='sm')
+ .treeview-label-text(:class='$q.dark.isActive ? `text-purple-4` : `text-purple`') root
+ q-menu(
+ v-if='rootContextActionList.length > 0'
+ touch-position
+ context-menu
+ auto-close
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ )
+ q-card.q-pa-sm
+ q-list(dense, style='min-width: 150px;')
+ q-item(
+ v-for='action of rootContextActionList'
+ :key='action.key'
+ clickable
+ @click='action.handler(null)'
+ )
+ q-item-section(side)
+ q-icon(:name='action.icon', :color='action.iconColor')
+ q-item-section(:class='action.labelColor && (`text-` + action.labelColor)') {{action.label}}
+ q-icon(
+ v-if='!selection'
+ name='las la-angle-right'
+ :color='$q.dark.isActive ? `purple-4` : `purple`'
+ )
+ //- NORMAL NODES
+ tree-node(
+ v-for='node of level'
+ :key='node.id'
+ :node='node'
+ :depth='props.depth'
+ :parent-id='props.parentId'
+ )
+
+
+
diff --git a/frontend/src/components/TreeNav.vue b/frontend/src/components/TreeNav.vue
new file mode 100644
index 00000000..656feb5c
--- /dev/null
+++ b/frontend/src/components/TreeNav.vue
@@ -0,0 +1,240 @@
+
+.treeview
+ tree-level(
+ :depth='0'
+ :parent-id='null'
+ )
+
+
+
+
+
diff --git a/frontend/src/components/TreeNode.vue b/frontend/src/components/TreeNode.vue
new file mode 100644
index 00000000..36872af4
--- /dev/null
+++ b/frontend/src/components/TreeNode.vue
@@ -0,0 +1,140 @@
+
+li.treeview-node
+ //- NODE
+ .treeview-label(@click='openNode', :class='{ "active": isActive }')
+ q-icon(
+ :name='icon'
+ size='sm'
+ @click.stop='toggleNode()'
+ )
+ .treeview-label-text {{displayMode === 'path' ? node.fileName : node.title}}
+ q-spinner.q-mr-xs(
+ color='primary'
+ v-if='state.isLoading'
+ )
+ q-icon(
+ v-if='isActive'
+ name='las la-angle-right'
+ :color='$q.dark.isActive ? `yellow-9` : `brown-4`'
+ )
+ //- RIGHT-CLICK MENU
+ q-menu(
+ v-if='contextActionList.length > 0'
+ touch-position
+ context-menu
+ auto-close
+ transition-show='jump-down'
+ transition-hide='jump-up'
+ @before-show='state.isContextMenuShown = true'
+ @before-hide='state.isContextMenuShown = false'
+ )
+ q-card.q-pa-sm
+ q-list(dense, style='min-width: 150px;')
+ q-item(
+ v-for='action of contextActionList'
+ :key='action.key'
+ clickable
+ @click='action.handler(node.id)'
+ )
+ q-item-section(side)
+ q-icon(:name='action.icon', :color='action.iconColor')
+ q-item-section(:class='action.labelColor && (`text-` + action.labelColor)') {{action.label}}
+ //- SUB-LEVEL
+ transition(name='treeview')
+ tree-level(
+ v-if='hasChildren && isOpened'
+ :parent-id='props.node.id'
+ :depth='props.depth + 1'
+ )
+
+
+
diff --git a/frontend/src/components/UploadPendingAssetsDialog.vue b/frontend/src/components/UploadPendingAssetsDialog.vue
new file mode 100644
index 00000000..226cee62
--- /dev/null
+++ b/frontend/src/components/UploadPendingAssetsDialog.vue
@@ -0,0 +1,115 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide', persistent)
+ q-card(style='min-width: 350px; max-width: 450px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-upload.svg', left, size='sm')
+ span {{t(`editor.pendingAssetsUploading`)}}
+ q-card-section
+ .q-pa-md.text-center
+ img(src='/_assets/illustrations/undraw_upload.svg', style='width: 150px;')
+ q-linear-progress(
+ indeterminate
+ size='lg'
+ rounded
+ )
+ .q-mt-sm.text-center.text-caption {{ state.current }} / {{ state.total }}
+
+
+
diff --git a/frontend/src/components/UserChangePwdDialog.vue b/frontend/src/components/UserChangePwdDialog.vue
new file mode 100644
index 00000000..82eca05a
--- /dev/null
+++ b/frontend/src/components/UserChangePwdDialog.vue
@@ -0,0 +1,219 @@
+
+q-dialog(ref='dialogRef', @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='state.userPassword'
+ dense
+ :rules='userPasswordValidation'
+ 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='state.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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.update`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/UserCreateDialog.vue b/frontend/src/components/UserCreateDialog.vue
new file mode 100644
index 00000000..aec3040b
--- /dev/null
+++ b/frontend/src/components/UserCreateDialog.vue
@@ -0,0 +1,398 @@
+
+q-dialog(ref='dialogRef', @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='state.userName'
+ dense
+ :rules='userNameValidation'
+ 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='state.userEmail'
+ dense
+ type='email'
+ :rules='userEmailValidation'
+ 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='state.userPassword'
+ dense
+ :rules='userPasswordValidation'
+ 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='state.groups'
+ v-model='state.userGroups'
+ multiple
+ map-options
+ emit-value
+ option-value='id'
+ option-label='name'
+ options-dense
+ dense
+ :rules='userGroupsValidation'
+ hide-bottom-space
+ :label='t(`admin.users.groups`)'
+ :aria-label='t(`admin.users.groups`)'
+ lazy-rules='ondemand'
+ :loading='state.loadingGroups'
+ )
+ template(v-slot:selected)
+ .text-caption(v-if='state.userGroups.length > 1')
+ i18n-t(keypath='admin.users.groupsSelected')
+ template(#count)
+ strong {{ state.userGroups.length }}
+ .text-caption(v-else-if='state.userGroups.length === 1')
+ i18n-t(keypath='admin.users.groupSelected')
+ template(#group)
+ strong {{ selectedGroupName }}
+ span(v-else)
+ template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ )
+ 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='state.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='state.userSendWelcomeEmail'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.users.sendWelcomeEmail`)'
+ )
+ q-item(v-if='state.userSendWelcomeEmail')
+ blueprint-icon(icon='web-design')
+ q-item-section
+ q-select(
+ outlined
+ :options='adminStore.sites'
+ v-model='state.userSendWelcomeEmailFromSiteId'
+ multiple
+ map-options
+ emit-value
+ option-value='id'
+ option-label='title'
+ options-dense
+ dense
+ hide-bottom-space
+ :label='t(`admin.users.sendWelcomeEmailFromSiteId`)'
+ :aria-label='t(`admin.users.sendWelcomeEmailFromSiteId`)'
+ )
+ q-card-actions.card-actions
+ q-checkbox(
+ v-model='state.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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.loading > 0'
+ )
+
+
+
diff --git a/frontend/src/components/UserDefaultsMenu.vue b/frontend/src/components/UserDefaultsMenu.vue
new file mode 100644
index 00000000..9a80aa8e
--- /dev/null
+++ b/frontend/src/components/UserDefaultsMenu.vue
@@ -0,0 +1,211 @@
+
+q-menu.translucent-menu(
+ anchor='bottom right'
+ self='top right'
+ :offset='[0, 10]'
+ ref='menuRef'
+ )
+ q-card(style='width: 850px;')
+ q-card-section.card-header
+ q-icon(name='img:/_assets/icons/fluent-choose.svg', left, size='sm')
+ span {{t(`admin.users.defaults`)}}
+ q-list(padding)
+ 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='state.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='state.dateFormat'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.general.defaultDateFormat`)'
+ :options='dateFormats'
+ )
+ 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='state.timeFormat'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='timeFormats'
+ )
+ q-card-actions.card-actions
+ q-space
+ q-btn.acrylic-btn(
+ flat
+ :label='t(`common.actions.cancel`)'
+ color='grey'
+ padding='xs md'
+ v-close-popup
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ )
+ q-inner-loading(:showing='state.loading > 0')
+
+
+
diff --git a/frontend/src/components/UserEditOverlay.vue b/frontend/src/components/UserEditOverlay.vue
new file mode 100644
index 00000000..530acde8
--- /dev/null
+++ b/frontend/src/components/UserEditOverlay.vue
@@ -0,0 +1,871 @@
+
+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 {{state.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='fetchUser'
+ :loading='state.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='state.loading > 0'
+ )
+ q-drawer.bg-dark-6(:model-value='true', :width='250', dark)
+ q-list(padding, v-if='state.loading < 1')
+ template(
+ v-for='sc of sections'
+ :key='`section-` + sc.key'
+ )
+ q-item(
+ v-if='!sc.disabled || flagsStore.experimental'
+ 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='state.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='state.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='state.user.email'
+ dense
+ :aria-label='t(`admin.users.email`)'
+ )
+ template(v-if='state.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='state.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='state.user.meta.jobTitle'
+ dense
+ :aria-label='t(`admin.users.jobTitle`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='gender')
+ q-item-section
+ q-item-label {{t(`admin.users.pronouns`)}}
+ q-item-label(caption) {{t(`admin.users.pronounsHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.user.meta.pronouns'
+ dense
+ :aria-label='t(`admin.users.pronouns`)'
+ )
+
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.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='state.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='state.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='state.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
+ blueprint-icon(icon='light-on')
+ q-item-section
+ q-item-label {{t(`admin.users.appearance`)}}
+ q-item-label(caption) {{t(`admin.users.darkModeHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.user.prefs.appearance'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { label: t('profile.appearanceDefault'), value: 'site' },
+ { label: t('profile.appearanceLight'), value: 'light' },
+ { label: t('profile.appearanceDark'), value: 'dark' }
+ ]`
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='visualy-impaired')
+ q-item-section
+ q-item-label {{t(`profile.cvd`)}}
+ q-item-label(caption) {{t(`profile.cvdHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.user.prefs.cvd'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options=`[
+ { value: 'none', label: t('profile.cvdNone') },
+ { value: 'protanopia', label: t('profile.cvdProtanopia') },
+ { value: 'deuteranopia', label: t('profile.cvdDeuteranopia') },
+ { value: 'tritanopia', label: t('profile.cvdTritanopia') }
+ ]`
+ )
+
+ .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 {{state.user.id}}
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='calendar-plus', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{t(`common.field.createdOn`)}}
+ q-item-label: strong {{formattedDate(state.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 {{formattedDate(state.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 {{formattedDate(state.user.lastLoginAt)}}
+
+ q-card.shadow-1.q-pb-sm.q-mt-md(v-if='state.user.meta')
+ q-card-section
+ .text-subtitle1 {{t('admin.users.notes')}}
+ q-input.q-mt-sm(
+ outlined
+ v-model='state.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.isPasswordSet ? `text-positive` : `text-negative`') {{localAuth.isPasswordSet ? 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.isTfaRequired'
+ 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.isTfaSetup ? `text-positive` : `text-negative`') {{localAuth.isTfaSetup ? 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.authId'
+ )
+ q-separator.q-my-sm(inset, v-if='idx > 0')
+ q-item
+ blueprint-icon(:icon='prv.strategyIcon', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{prv.authName}}
+ q-item-label(caption) {{prv.config.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 state.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='state.groups'
+ v-model='state.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='state.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='state.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 {{state.user.isVerified ? t(`admin.users.unverify`) : t(`admin.users.verify`)}}
+ q-item-label(caption) {{state.user.isVerified ? t(`admin.users.unverifyHint`) : t(`admin.users.verifyHint`)}}
+ q-item-label(caption): strong(:class='state.user.isVerified ? `text-positive` : `text-negative`') {{state.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 {{state.user.isActive ? t(`admin.users.ban`) : t(`admin.users.unban`)}}
+ q-item-label(caption) {{state.user.isActive ? t(`admin.users.banHint`) : t(`admin.users.unbanHint`)}}
+ q-item-label(caption): strong(:class='state.user.isActive ? `text-positive` : `text-negative`') {{state.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/frontend/src/components/UtilCodeEditor.vue b/frontend/src/components/UtilCodeEditor.vue
new file mode 100644
index 00000000..845be83f
--- /dev/null
+++ b/frontend/src/components/UtilCodeEditor.vue
@@ -0,0 +1,133 @@
+
+.util-code-editor
+ textarea(ref='cmRef')
+
+
+
+
+
diff --git a/frontend/src/components/WebhookDeleteDialog.vue b/frontend/src/components/WebhookDeleteDialog.vue
new file mode 100644
index 00000000..45ca7ca9
--- /dev/null
+++ b/frontend/src/components/WebhookDeleteDialog.vue
@@ -0,0 +1,106 @@
+
+q-dialog(ref='dialogRef', @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='onDialogCancel'
+ )
+ q-btn(
+ unelevated
+ :label='t(`common.actions.delete`)'
+ color='negative'
+ padding='xs md'
+ @click='confirm'
+ :loading='state.isLoading'
+ )
+
+
+
diff --git a/frontend/src/components/WebhookEditDialog.vue b/frontend/src/components/WebhookEditDialog.vue
new file mode 100644
index 00000000..a8c220a0
--- /dev/null
+++ b/frontend/src/components/WebhookEditDialog.vue
@@ -0,0 +1,440 @@
+
+q-dialog(ref='dialogRef', @hide='onDialogHide')
+ q-card(style='min-width: 850px;')
+ q-card-section.card-header
+ template(v-if='props.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='props.hookId && state.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='props.hookId && state.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='props.hookId && state.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 {{state.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='state.hook.name'
+ dense
+ :rules='hookNameValidation'
+ 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='state.hook.events'
+ multiple
+ map-options
+ emit-value
+ option-value='key'
+ option-label='name'
+ options-dense
+ dense
+ :rules='hookEventsValidation'
+ 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='state.hook.events.length > 0') {{t(`admin.webhooks.eventsSelected`, state.hook.events.length, { count: state.hook.events.length })}}
+ span(v-else)
+ template(v-slot:option='{ itemProps, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ )
+ 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='state.hook.url'
+ dense
+ :rules='hookUrlValidation'
+ 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='state.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='state.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='state.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='state.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='onDialogCancel'
+ )
+ q-btn(
+ v-if='props.hookId'
+ unelevated
+ :label='t(`common.actions.save`)'
+ color='primary'
+ padding='xs md'
+ @click='save'
+ :loading='state.isLoading'
+ )
+ q-btn(
+ v-else
+ unelevated
+ :label='t(`common.actions.create`)'
+ color='primary'
+ padding='xs md'
+ @click='create'
+ :loading='state.isLoading'
+ )
+
+ q-inner-loading(:showing='state.isLoading')
+ q-spinner(color='accent', size='lg')
+
+
+
diff --git a/frontend/src/components/WelcomeOverlay.vue b/frontend/src/components/WelcomeOverlay.vue
new file mode 100644
index 00000000..bb7dcac9
--- /dev/null
+++ b/frontend/src/components/WelcomeOverlay.vue
@@ -0,0 +1,184 @@
+
+.welcome
+ .welcome-bg
+ .welcome-content
+ .welcome-logo
+ img(src='/_assets/logo-wikijs.svg')
+ .welcome-title {{ t('welcome.title') }}
+ .welcome-subtitle {{ t('welcome.subtitle') }}
+ .welcome-actions
+ q-btn(
+ push
+ color='primary'
+ :label='t(`welcome.createHome`)'
+ icon='las la-plus'
+ no-caps
+ )
+ q-menu.translucent-menu(
+ auto-close
+ anchor='top left'
+ self='bottom left'
+ )
+ q-list(padding)
+ q-item(
+ clickable
+ @click='createHomePage(`wysiwyg`)'
+ v-if='flagsStore.experimental && siteStore.editors.wysiwyg'
+ )
+ blueprint-icon(icon='google-presentation')
+ q-item-section.q-pr-sm Using the Visual Editor
+ q-item-section(side): q-icon(name='mdi-chevron-right')
+ q-item(
+ clickable
+ @click='createHomePage(`markdown`)'
+ v-if='siteStore.editors.markdown'
+ )
+ blueprint-icon(icon='markdown')
+ q-item-section.q-pr-sm Using the Markdown Editor
+ q-item-section(side): q-icon(name='mdi-chevron-right')
+ q-item(
+ clickable
+ @click='createHomePage(`asciidoc`)'
+ v-if='flagsStore.experimental && siteStore.editors.asciidoc'
+ )
+ blueprint-icon(icon='asciidoc')
+ q-item-section.q-pr-sm Using the AsciiDoc Editor
+ q-item-section(side): q-icon(name='mdi-chevron-right')
+ q-btn(
+ push
+ color='primary'
+ :label='t(`welcome.admin`)'
+ icon='las la-cog'
+ no-caps
+ @click='loadAdmin'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/css/_animation.scss b/frontend/src/css/_animation.scss
new file mode 100644
index 00000000..654fd0de
--- /dev/null
+++ b/frontend/src/css/_animation.scss
@@ -0,0 +1,157 @@
+// ------------------------------------------------------------------
+// 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
+}
+
+// -> Slide Up Transition
+
+.slide-up-enter-active,
+.slide-up-leave-active {
+ transition: all 0.25s ease-out;
+}
+
+.slide-up-enter-from {
+ opacity: 0;
+ transform: translateY(15px);
+}
+
+.slide-up-leave-to {
+ opacity: 0;
+ transform: translateY(-15px);
+}
+
+// -> 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/frontend/src/css/_base.scss b/frontend/src/css/_base.scss
new file mode 100644
index 00000000..d1d7df6e
--- /dev/null
+++ b/frontend/src/css/_base.scss
@@ -0,0 +1,269 @@
+@use 'sass:color';
+@use '../../node_modules/quasar/src/css/variables.sass' as quasar;
+@use 'theme';
+
+// 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: 'Roboto Mono';
+ src: url(/_assets/fonts/roboto-mono/roboto-mono.woff2);
+}
+
+.font-robotomono {
+ font-family: 'Roboto Mono', Consolas, "Liberation Mono", Courier, monospace;
+}
+
+.text-wordbreak-all {
+ word-break: break-all;
+}
+
+// ------------------------------------------------------------------
+// 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; }
+.bg-dark-2 { background-color: #292f39; }
+.bg-dark-1 { background-color: #343b48; }
+
+// ------------------------------------------------------------------
+// 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: theme.$dark;
+ }
+}
+
+.q-field--dark .q-field__control:before {
+ background-color: theme.$dark-5;
+ border-color: rgba(255,255,255,.25);
+}
+
+// ------------------------------------------------------------------
+// ICONS SIZE FIX
+// ------------------------------------------------------------------
+
+.q-btn .q-icon {
+ &.fa-solid,
+ &.fa-regular {
+ font-size: 1.3em;
+ }
+}
+
+.q-select__dropdown-icon {
+ font-size: 16px;
+}
+
+// ------------------------------------------------------------------
+// BUTTONS
+// ------------------------------------------------------------------
+
+.q-btn.acrylic-btn {
+ .q-focus-helper {
+ background-color: currentColor;
+ opacity: .1;
+ }
+
+ &:hover .q-focus-helper {
+ opacity: .3 !important;
+ }
+}
+
+// ------------------------------------------------------------------
+// ICONS
+// ------------------------------------------------------------------
+
+.blueprint-icon {
+ &-alt {
+ filter: hue-rotate(100);
+ }
+}
+
+// ------------------------------------------------------------------
+// DIALOGS
+// ------------------------------------------------------------------
+
+.card-header {
+ display: flex;
+ align-items: center;
+ font-weight: 500;
+ font-size: .9rem;
+ background-color: theme.$dark-3;
+ background-image: radial-gradient(at bottom right, theme.$dark-3, theme.$dark-5);
+ color: #FFF;
+
+ @at-root .body--light & {
+ border-bottom: 1px solid theme.$dark-3;
+ box-shadow: 0 1px 0 0 theme.$dark-6;
+ }
+ @at-root .body--dark & {
+ border-bottom: 1px solid #000;
+ box-shadow: 0 1px 0 0 color.adjust(theme.$dark-3, $lightness: 2%);
+ }
+}
+
+.card-negative {
+ display: flex;
+ align-items: center;
+ font-size: .9rem;
+
+ @at-root .body--light & {
+ background-color: quasar.$red-1;
+ background-image: radial-gradient(at bottom center, color.adjust(quasar.$red-1, $lightness: 2%), color.adjust(quasar.$red-2, $lightness: 2%));
+ border-bottom: 1px solid quasar.$red-3;
+ text-shadow: 0 0 4px #FFF;
+ color: quasar.$red-9;
+ }
+ @at-root .body--dark & {
+ background-color: quasar.$red-9;
+ background-image: radial-gradient(at bottom center, quasar.$red-7, quasar.$red-9);
+ border-bottom: 1px solid quasar.$red-7;
+ text-shadow: 0 0 4px color.adjust(quasar.$red-9, $lightness: -10%);
+ color: #FFF;
+ }
+}
+
+.card-actions {
+ @at-root .body--light & {
+ background-color: #FAFAFA;
+ background-image: linear-gradient(to bottom, #FCFCFC, #F0F0F0);
+ color: theme.$dark-3;
+ border-top: 1px solid #EEE;
+ box-shadow: inset 0 1px 0 0 #FFF;
+ }
+ @at-root .body--dark & {
+ background-color: theme.$dark-3;
+ background-image: radial-gradient(at top left, theme.$dark-3, theme.$dark-5);
+ border-top: 1px solid #000;
+ box-shadow: 0 -1px 0 0 color.adjust(theme.$dark-3, $lightness: 2%);
+ }
+}
+
+// ------------------------------------------------------------------
+// CARDS
+// ------------------------------------------------------------------
+
+.q-card {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
+
+ &.q-card--dark {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
+ }
+
+ .q-separator--dark {
+ background-color: rgba(0,0,0,.7);
+ }
+}
+
+// ------------------------------------------------------------------
+// DROPDOWN MENUS
+// ------------------------------------------------------------------
+
+.translucent-menu {
+ backdrop-filter: blur(10px) saturate(180%);
+
+ @at-root .body--light & {
+ background-color: rgba(255,255,255,.8);
+ }
+ @at-root .body--dark & {
+ background-color: rgba(theme.$dark,.7);
+ }
+
+ > .q-card {
+ background-color: transparent !important;
+ }
+}
+
+.q-menu--dark {
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.2), 0 1px 1px rgba(0, 0, 0, 0.14), 0 2px 1px -1px rgba(0, 0, 0, 0.12);
+}
+
+// ------------------------------------------------------------------
+// LOADING ANIMATIONS
+// ------------------------------------------------------------------
+
+.syncing-enter-active {
+ animation: syncing-anim .1s;
+}
+.syncing-leave-active {
+ animation: syncing-anim 1s reverse;
+}
+@keyframes syncing-anim {
+ 0% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+}
+
+.loading-darker {
+ .q-loading__backdrop {
+ opacity: .75;
+ }
+}
diff --git a/frontend/src/css/_page-contents.scss b/frontend/src/css/_page-contents.scss
new file mode 100644
index 00000000..44befe17
--- /dev/null
+++ b/frontend/src/css/_page-contents.scss
@@ -0,0 +1,597 @@
+@use 'sass:color';
+@use '../../node_modules/quasar/src/css/variables.sass' as quasar;
+@use 'theme';
+
+.page-contents {
+ color: #424242;
+ font-size: 16px;
+
+ > *:first-child {
+ margin-top: 0;
+ }
+
+ @at-root .body--dark & {
+ color: #FFF;
+ }
+
+ // ---------------------------------
+ // LINKS
+ // ---------------------------------
+
+ a {
+ color: var(--q-primary);
+
+ &.is-internal-link.is-invalid-page {
+ color: quasar.$red-8;
+
+ @at-root .body--dark & {
+ color: quasar.$red-2;
+ }
+ }
+
+ &.is-external-link {
+ padding-right: 3px;
+
+ &::after {
+ font-family: 'Line Awesome Free';
+ font-size: 24px/1;
+ padding-left: 3px;
+ display: inline-block;
+ content: "\f35d";
+ color: quasar.$grey-5;
+ text-decoration: none;
+ }
+ }
+
+ @at-root .body--dark & {
+ color: quasar.$blue-4;
+ }
+ }
+
+ // ---------------------------------
+ // HEADERS
+ // ---------------------------------
+
+ h1, h2, h3, h4, h5, h6 {
+ padding: 0;
+ margin: 0;
+ font-weight: 400;
+ position: relative;
+ line-height: normal;
+
+ &:first-child {
+ padding-top: 0;
+ }
+
+ &:hover {
+ .toc-anchor {
+ display: block;
+ }
+ }
+ }
+
+ h1 {
+ font-size: 3em;
+ font-weight: 500;
+ padding: 12px 0;
+ // color: var(--q-primary);
+ }
+ h2 {
+ font-size: 2.4em;
+ padding: 12px 0;
+ }
+ h3 {
+ font-size: 2em;
+ padding: 12px 0;
+ }
+ h4 {
+ font-size: 1.75em;
+ }
+ h5 {
+ font-size: 1.5em;
+ }
+ h6 {
+ font-size: 1.25em;
+ }
+
+ * + h1 {
+ margin-top: .5em;
+ padding-top: .5em;
+ // border-top: 2px solid var(--q-primary);
+ position: relative;
+
+ &::before {
+ position: absolute;
+ width: 100%;
+ height: 1px;
+ content: ' ';
+ background: linear-gradient(to right, var(--q-primary), transparent);
+ top: 0;
+ left: -16px;
+ }
+ }
+
+ *:not(h1) + h2 {
+ margin-top: .5em;
+ padding-top: .5em;
+ border-top: 1px dotted #CCC;
+ }
+
+ .toc-anchor {
+ display: none;
+ position: absolute;
+ right: 1rem;
+ bottom: .5rem;
+ font-size: 1.25rem;
+ text-decoration: none;
+ color: #666;
+ }
+
+ // ---------------------------------
+ // PARAGRAPHS
+ // ---------------------------------
+
+ p {
+ padding: 0;
+ margin: .3em 0 1em 0;
+ }
+
+ // ---------------------------------
+ // BLOCKQUOTES
+ // ---------------------------------
+
+ blockquote {
+ padding: 1em 1em .3em 1em;
+ background-color: quasar.$blue-grey-1;
+ border-left: 55px solid quasar.$blue-grey-5;
+ border-radius: .5rem;
+ margin: 1rem 0;
+ position: relative;
+
+ @at-root .body--dark & {
+ background-color: quasar.$blue-grey-9;
+ }
+
+ &::before {
+ display: inline-block;
+ font: normal normal normal 24px/1 "Material Design Icons", sans-serif;
+ position: absolute;
+ margin-top: -12px;
+ top: 50%;
+ left: -38px;
+ color: rgba(255, 255, 255, .7);
+ content: "\F0757";
+ }
+
+ > p:first-child .emoji {
+ margin-right: .5rem;
+ }
+
+ &.valign-center > p {
+ display: flex;
+ align-items: center;
+ }
+
+ &.is-info {
+ background-color: quasar.$blue-1;
+ border-color: quasar.$blue-3;
+ color: quasar.$blue-9;
+
+ &::before {
+ content: "\F02FC";
+ }
+
+ code {
+ background-color: quasar.$blue-1;
+ color: quasar.$blue-8;
+ }
+
+ @at-root .body--dark & {
+ background-color: quasar.$blue-9;
+ color: quasar.$blue-5;
+ border-color: quasar.$blue-5;
+ }
+ }
+ &.is-warning {
+ background-color: quasar.$orange-1;
+ border-color: quasar.$orange-3;
+ color: color.adjust(quasar.$orange-9, $lightness: -10%);
+
+ &::before {
+ content: "\F0026";
+ }
+
+ code {
+ background-color: quasar.$orange-1;
+ color: quasar.$orange-8;
+ }
+
+ @at-root .body--dark & {
+ background-color: color.adjust(quasar.$orange-9, $lightness: -5%);
+ color: quasar.$orange-1;
+ border-color: quasar.$orange-5;
+ box-shadow: 0 0 2px 0 quasar.$grey-9;
+ }
+ }
+ &.is-danger {
+ background-color: quasar.$red-1;
+ border-color: quasar.$red-3;
+ color: quasar.$red-9;
+
+ &::before {
+ content: "\F0159";
+ }
+
+ code {
+ background-color: quasar.$red-1;
+ color: quasar.$red-8;
+ }
+
+ @at-root .body--dark & {
+ background-color: quasar.$red-9;
+ color: quasar.$red-1;
+ border-color: quasar.$red-5;
+ }
+ }
+ &.is-success {
+ background-color: quasar.$green-1;
+ border-color: quasar.$green-3;
+ color: quasar.$green-9;
+
+ &::before {
+ content: "\F0E1E";
+ }
+
+ code {
+ background-color: quasar.$green-1;
+ color: quasar.$green-8;
+ }
+
+ @at-root .body--dark & {
+ background-color: quasar.$green-9;
+ color: quasar.$green-5;
+ border-color: quasar.$green-5;
+ }
+ }
+
+ .codeblock > code {
+ background-color: inherit;
+ color: inherit;
+ }
+ }
+
+ // ---------------------------------
+ // LISTS
+ // ---------------------------------
+
+ ol, ul:not(.tabset-tabs) {
+ width: 100%;
+
+ li > p {
+ &:first-child {
+ margin-top: 0;
+ }
+ &:last-child {
+ margin-bottom: 0;
+ }
+ }
+
+ @at-root .is-rtl & {
+ padding-left: 0;
+ padding-right: 1em;
+ }
+
+ li > ul, li > ol {
+ padding-top: .5rem;
+ padding-left: 1em;
+
+ @at-root .is-rtl & {
+ padding-left: 0;
+ padding-right: 1em;
+ }
+ }
+
+ li + li {
+ margin-top: .5rem;
+ }
+
+ &.links-list {
+ padding-left: 0;
+ list-style-type: none;
+
+ @at-root .is-rtl & {
+ padding-right: 0;
+ }
+
+ li {
+ background-color: quasar.$grey-1;
+ background-image: linear-gradient(to bottom, #FFF, quasar.$grey-1);
+ border-right: 1px solid quasar.$grey-3;
+ border-bottom: 1px solid quasar.$grey-3;
+ border-left: 5px solid quasar.$grey-4;
+ box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1);
+ padding: 1rem;
+ border-radius: 5px;
+ font-weight: 500;
+
+ @at-root .is-rtl & {
+ border-left-width: 1px;
+ border-right-width: 5px;
+ }
+
+ &:hover {
+ background-image: linear-gradient(to bottom, #FFF, color.adjust(quasar.$blue-1, $lightness: 4%));
+ border-left-color: quasar.$blue-5;
+ cursor: pointer;
+
+ @at-root .is-rtl & {
+ border-left-color: quasar.$grey-3;
+ border-right-width: quasar.$blue-5;
+ }
+ }
+
+ &::before {
+ content: '';
+ display: none;
+ }
+
+ > a {
+ display: block;
+ text-decoration: none;
+ margin: -1rem;
+ padding: 1rem;
+
+ > em {
+ font-weight: 400;
+ font-style: normal;
+ color: quasar.$grey-7;
+ display: inline-block;
+ padding-left: .5rem;
+ border-left: 1px solid quasar.$grey-4;
+ margin-left: .5rem;
+
+ &.is-block {
+ display: block;
+ padding-left: 0;
+ margin-left: 0;
+ border-left: none;
+ }
+ }
+ }
+
+ > em {
+ font-weight: 400;
+ font-style: normal;
+ }
+
+ @at-root .body--dark & {
+ background-color: quasar.$grey-1;
+ background-image: linear-gradient(to bottom, color.adjust(quasar.$grey-9, $lightness: 5%), quasar.$grey-9);
+ border-right: 1px solid quasar.$grey-9;
+ border-bottom: 1px solid quasar.$grey-9;
+ border-left: 5px solid quasar.$grey-7;
+ box-shadow: 0 3px 8px 0 rgba(0, 0, 0, 0.1);
+
+ @at-root .body--dark.is-rtl & {
+ border-left-width: 1px;
+ border-right-width: 5px;
+ }
+
+ &:hover {
+ background-image: linear-gradient(to bottom, color.adjust(quasar.$grey-9, $lightness: 2%), color.adjust(quasar.$grey-9, $lightness: -3%));
+ border-left-color: mc('indigo', '300');
+ cursor: pointer;
+
+ @at-root .body--dark.is-rtl & {
+ border-left-color: quasar.$grey-9;
+ border-right-width: mc('indigo', '300');
+ }
+ }
+ }
+ }
+ }
+
+ &.grid-list {
+ margin: 1rem 0 0 0;
+ background-color: #FFF;
+ border: 1px solid quasar.$grey-3;
+ padding: 1px;
+ display: inline-block;
+ list-style-type: none;
+
+ @at-root .body--dark & {
+ background-color: #000;
+ border: 1px solid mc('grey', '800');
+ }
+
+ li {
+ background-color: quasar.$grey-1;
+ padding: .6rem 1rem;
+ display: block;
+
+ &:nth-child(odd) {
+ background-color: mc('grey', '100');
+ }
+
+ & + li {
+ margin-top: 0;
+ }
+
+ &::before {
+ content: '';
+ display: none;
+ }
+
+ @at-root .body--dark & {
+ background-color: quasar.$grey-9;
+
+ &:nth-child(odd) {
+ background-color: color.adjust(quasar.$grey-9, $lightness: -5%);
+ }
+ }
+ }
+ }
+ }
+
+ ul:not(.tabset-tabs):not(.contains-task-list) {
+ list-style: square;
+ }
+ ol, ul:not(.tabset-tabs) {
+ > li {
+ position: relative;
+ > p {
+ display:inline-block;
+ vertical-align:top;
+ padding-top:0;
+ }
+ }
+ }
+
+ // ---------------------------------
+ // TASK LISTS
+ // ---------------------------------
+
+ .contains-task-list {
+ padding-left: 1em;
+ }
+
+ .task-list-item {
+ position: relative;
+ list-style-type: none;
+
+ &-checkbox[disabled] {
+ width: 1.1rem;
+ height: 1.1rem;
+ top: 2px;
+ position: relative;
+ margin-right: .4em;
+ background-color: theme.$dark-5;
+ border-width: 0;
+
+ &::after {
+ position: absolute;
+ left: 0;
+ top: 0;
+ font-family: "Material Design Icons";
+ font-size: 1.25em;
+ font-weight: normal;
+ content: '\F0131';
+ color: quasar.$grey-10;
+ display: block;
+ border: none;
+ background-color: #FFF;
+ line-height: 1em;
+ cursor: default;
+
+ @at-root .body--dark & {
+ color: #FFF;
+ background-color: theme.$dark-6;
+ }
+ }
+
+ &[checked]::after {
+ content: '\F0C52';
+ }
+ }
+
+ .contains-task-list {
+ padding: .5rem 0 0 1.5rem;
+ }
+ }
+
+ // ---------------------------------
+ // CODE
+ // ---------------------------------
+
+ // code {
+ // background-color: mc('indigo', '50');
+ // padding: 0 5px;
+ // color: mc('indigo', '800');
+ // font-family: 'Roboto Mono', monospace;
+ // font-weight: normal;
+ // font-size: 1rem;
+ // box-shadow: none;
+
+ // &::before, &::after {
+ // display: none;
+ // }
+
+ // @at-root .theme--dark & {
+ // background-color: color.adjust(mc('grey', '900'), $lightness: -5%);
+ // color: mc('indigo', '100');
+ // }
+ // }
+
+ pre.codeblock {
+ border: 1px solid rgba(0,0,0,.2);
+ border-radius: 5px;
+ box-shadow: initial;
+ padding: 1rem;
+ margin: 1rem 0;
+ overflow: auto;
+
+ > code {
+ // background-color: transparent;
+ padding: 0;
+ // color: #FFF;
+ box-shadow: initial;
+ display: block;
+ font-size: .85rem;
+ font-family: 'Roboto Mono', monospace;
+
+ &:after, &:before {
+ content: initial;
+ letter-spacing: initial;
+ }
+ }
+
+ &.line-numbers {
+ counter-reset: linenumber;
+ padding-left: 3rem;
+
+ > code {
+ position: relative;
+ white-space: inherit;
+ }
+
+ .line-numbers-rows {
+ position: absolute;
+ pointer-events: none;
+ top: 0;
+ font-size: 100%;
+ left: -3.8em;
+ width: 3em;
+ letter-spacing: -1px;
+ border-right: 1px solid #999;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ user-select: none;
+
+ & > span {
+ display: block;
+ counter-increment: linenumber;
+
+ &:before {
+ content: counter(linenumber);
+ color: #999;
+ display: block;
+ padding-right: .8em;
+ text-align: right;
+ }
+ }
+ }
+ }
+ }
+
+ // ---------------------------------
+ // LEGACY
+ // ---------------------------------
+
+ .align-abstopright {
+ width: 100px;
+ max-height: 100px;
+ border: 2px dashed quasar.$red;
+ border-radius: 5px;
+ padding: 5px;
+ }
+}
diff --git a/frontend/src/css/_theme.scss b/frontend/src/css/_theme.scss
new file mode 100644
index 00000000..5de0086d
--- /dev/null
+++ b/frontend/src/css/_theme.scss
@@ -0,0 +1,34 @@
+// 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;
+$dark-2: #292f39;
+$dark-1: #343b48;
diff --git a/frontend/src/css/app.scss b/frontend/src/css/app.scss
new file mode 100644
index 00000000..36a8a4f1
--- /dev/null
+++ b/frontend/src/css/app.scss
@@ -0,0 +1,5 @@
+@use 'base';
+@use 'animation';
+@use 'page-contents';
+
+@import 'v-network-graph/lib/style.css';
diff --git a/frontend/src/helpers/accessibility.js b/frontend/src/helpers/accessibility.js
new file mode 100644
index 00000000..3c4338ce
--- /dev/null
+++ b/frontend/src/helpers/accessibility.js
@@ -0,0 +1,33 @@
+const protanopia = {
+ negative: '#fb8c00',
+ positive: '#2196f3',
+ primary: '#1976D2',
+ secondary: '#2196f3'
+}
+
+const deuteranopia = {
+ negative: '#ef6c00',
+ positive: '#2196f3',
+ primary: '#1976D2',
+ secondary: '#2196f3'
+}
+
+const tritanopia = {
+ primary: '#e91e63',
+ secondary: '#02C39A'
+}
+
+export function getAccessibleColor (name, base, cvd) {
+ switch (cvd) {
+ case 'protanopia': {
+ return protanopia[name] ?? base
+ }
+ case 'deuteranopia': {
+ return deuteranopia[name] ?? base
+ }
+ case 'tritanopia': {
+ return tritanopia[name] ?? base
+ }
+ }
+ return base
+}
diff --git a/frontend/src/helpers/fileTypes.js b/frontend/src/helpers/fileTypes.js
new file mode 100644
index 00000000..75712df4
--- /dev/null
+++ b/frontend/src/helpers/fileTypes.js
@@ -0,0 +1,173 @@
+export default {
+ '7z': {
+ icon: 'img:/_assets/icons/color-7zip.svg'
+ },
+ aac: {
+ icon: 'img:/_assets/icons/color-aac.svg'
+ },
+ ai: {
+ icon: 'img:/_assets/icons/color-ai.svg'
+ },
+ aif: {
+ icon: 'img:/_assets/icons/color-audio-file.svg'
+ },
+ apk: {
+ icon: 'img:/_assets/icons/color-apk.svg'
+ },
+ avi: {
+ icon: 'img:/_assets/icons/color-avi.svg'
+ },
+ bin: {
+ icon: 'img:/_assets/icons/color-binary-file.svg'
+ },
+ bz2: {
+ icon: 'img:/_assets/icons/color-archive.svg'
+ },
+ css: {
+ icon: 'img:/_assets/icons/color-css-filetype.svg'
+ },
+ csv: {
+ icon: 'img:/_assets/icons/color-csv.svg'
+ },
+ dat: {
+ icon: 'img:/_assets/icons/color-binary-file.svg'
+ },
+ dmg: {
+ icon: 'img:/_assets/icons/color-dmg.svg'
+ },
+ docx: {
+ icon: 'img:/_assets/icons/color-word.svg'
+ },
+ eps: {
+ icon: 'img:/_assets/icons/color-image-file.svg'
+ },
+ exe: {
+ icon: 'img:/_assets/icons/color-exe.svg'
+ },
+ flac: {
+ icon: 'img:/_assets/icons/color-audio-file.svg'
+ },
+ folder: {
+ icon: 'img:/_assets/icons/fluent-folder.svg'
+ },
+ gif: {
+ icon: 'img:/_assets/icons/color-gif.svg'
+ },
+ gz: {
+ icon: 'img:/_assets/icons/color-archive.svg'
+ },
+ heic: {
+ icon: 'img:/_assets/icons/color-image-file.svg'
+ },
+ ico: {
+ icon: 'img:/_assets/icons/color-image-file.svg'
+ },
+ ics: {
+ icon: 'img:/_assets/icons/color-schedule.svg'
+ },
+ iso: {
+ icon: 'img:/_assets/icons/color-cd.svg'
+ },
+ jpg: {
+ icon: 'img:/_assets/icons/color-jpg.svg',
+ imageEdit: true
+ },
+ jpeg: {
+ icon: 'img:/_assets/icons/color-jpg.svg',
+ imageEdit: true
+ },
+ json: {
+ icon: 'img:/_assets/icons/color-json.svg'
+ },
+ m4a: {
+ icon: 'img:/_assets/icons/color-audio-file.svg'
+ },
+ mid: {
+ icon: 'img:/_assets/icons/color-audio-file.svg'
+ },
+ mov: {
+ icon: 'img:/_assets/icons/color-mov.svg'
+ },
+ mp3: {
+ icon: 'img:/_assets/icons/color-mp3.svg'
+ },
+ mp4: {
+ icon: 'img:/_assets/icons/color-mpg.svg'
+ },
+ mpg: {
+ icon: 'img:/_assets/icons/color-mpg.svg'
+ },
+ mpeg: {
+ icon: 'img:/_assets/icons/color-mpg.svg'
+ },
+ ogg: {
+ icon: 'img:/_assets/icons/color-ogg.svg'
+ },
+ otf: {
+ icon: 'img:/_assets/icons/color-otf.svg'
+ },
+ page: {
+ icon: 'img:/_assets/icons/color-document.svg'
+ },
+ pdf: {
+ icon: 'img:/_assets/icons/color-pdf.svg'
+ },
+ png: {
+ icon: 'img:/_assets/icons/color-png.svg',
+ imageEdit: true
+ },
+ pptx: {
+ icon: 'img:/_assets/icons/color-ppt.svg'
+ },
+ psd: {
+ icon: 'img:/_assets/icons/color-psd.svg'
+ },
+ rar: {
+ icon: 'img:/_assets/icons/color-rar.svg'
+ },
+ svg: {
+ icon: 'img:/_assets/icons/color-image-file.svg'
+ },
+ tar: {
+ icon: 'img:/_assets/icons/color-tar.svg'
+ },
+ tgz: {
+ icon: 'img:/_assets/icons/color-archive.svg'
+ },
+ tif: {
+ icon: 'img:/_assets/icons/color-tif.svg'
+ },
+ ttf: {
+ icon: 'img:/_assets/icons/color-ttf.svg'
+ },
+ txt: {
+ icon: 'img:/_assets/icons/color-txt.svg'
+ },
+ wav: {
+ icon: 'img:/_assets/icons/color-wav.svg'
+ },
+ wma: {
+ icon: 'img:/_assets/icons/color-audio-file.svg'
+ },
+ wmv: {
+ icon: 'img:/_assets/icons/color-video-file.svg'
+ },
+ woff: {
+ icon: 'img:/_assets/icons/color-woff.svg'
+ },
+ woff2: {
+ icon: 'img:/_assets/icons/color-woff.svg'
+ },
+ xlst: {
+ icon: 'img:/_assets/icons/color-xls.svg'
+ },
+ xml: {
+ icon: 'img:/_assets/icons/color-xml-file.svg'
+ },
+ xz: {
+ icon: 'img:/_assets/icons/color-archive.svg'
+ },
+ zip: {
+ icon: 'img:/_assets/icons/color-zip.svg'
+ }
+}
diff --git a/frontend/src/helpers/localization.js b/frontend/src/helpers/localization.js
new file mode 100644
index 00000000..8f127d51
--- /dev/null
+++ b/frontend/src/helpers/localization.js
@@ -0,0 +1,13 @@
+/**
+ * Parse an error message for an error code and translate
+ *
+ * @param {String} val Value to parse
+ * @param {Function} t vue-i18n translation method
+ */
+export function localizeError (val, t) {
+ if (val?.startsWith('ERR_')) {
+ return t(`error.${val}`)
+ } else {
+ return val
+ }
+}
diff --git a/frontend/src/helpers/monacoTypes.js b/frontend/src/helpers/monacoTypes.js
new file mode 100644
index 00000000..b10f4065
--- /dev/null
+++ b/frontend/src/helpers/monacoTypes.js
@@ -0,0 +1,621 @@
+// Adapted from https://github.com/trofimander/monaco-markdown/blob/master/src/ts/extHostTypes.ts
+// by https://github.com/trofimander
+// MIT Licensed
+
+// export function values(set: Set): V[];
+// export function values(map: Map): V[];
+export function values (forEachable) {
+ const result = []
+ forEachable.forEach(value => result.push(value))
+ return result
+}
+
+export class Position {
+ static Min (...positions) {
+ if (positions.length === 0) {
+ throw new TypeError()
+ }
+ let result = positions[0]
+ for (let i = 1; i < positions.length; i++) {
+ const p = positions[i]
+ if (p.isBefore(result)) {
+ result = p
+ }
+ }
+ return result
+ }
+
+ static Max (...positions) {
+ if (positions.length === 0) {
+ throw new TypeError()
+ }
+ let result = positions[0]
+ for (let i = 1; i < positions.length; i++) {
+ const p = positions[i]
+ if (p.isAfter(result)) {
+ result = p
+ }
+ }
+ return result
+ }
+
+ static isPosition (other) {
+ if (!other) {
+ return false
+ }
+ if (other instanceof Position) {
+ return true
+ }
+ const { line, character } = other
+ if (typeof line === 'number' && typeof character === 'number') {
+ return true
+ }
+ return false
+ }
+
+ get line () {
+ return this._line
+ }
+
+ get character () {
+ return this._character
+ }
+
+ constructor (line, character) {
+ if (line < 0) {
+ throw new Error('line must be non-negative')
+ }
+ if (character < 0) {
+ throw new Error('character must be non-negative')
+ }
+ this._line = line
+ this._character = character
+ }
+
+ isBefore (other) {
+ if (this._line < other._line) {
+ return true
+ }
+ if (other._line < this._line) {
+ return false
+ }
+ return this._character < other._character
+ }
+
+ isBeforeOrEqual (other) {
+ if (this._line < other._line) {
+ return true
+ }
+ if (other._line < this._line) {
+ return false
+ }
+ return this._character <= other._character
+ }
+
+ isAfter (other) {
+ return !this.isBeforeOrEqual(other)
+ }
+
+ isAfterOrEqual (other) {
+ return !this.isBefore(other)
+ }
+
+ isEqual (other) {
+ return this._line === other._line && this._character === other._character
+ }
+
+ compareTo (other) {
+ if (this._line < other._line) {
+ return -1
+ } else if (this._line > other.line) {
+ return 1
+ } else {
+ // equal line
+ if (this._character < other._character) {
+ return -1
+ } else if (this._character > other._character) {
+ return 1
+ } else {
+ // equal line and character
+ return 0
+ }
+ }
+ }
+
+ translate (lineDeltaOrChange, characterDelta = 0) {
+ if (lineDeltaOrChange === null || characterDelta === null) {
+ throw new Error()
+ }
+
+ let lineDelta
+ if (typeof lineDeltaOrChange === 'undefined') {
+ lineDelta = 0
+ } else if (typeof lineDeltaOrChange === 'number') {
+ lineDelta = lineDeltaOrChange
+ } else {
+ lineDelta = typeof lineDeltaOrChange.lineDelta === 'number' ? lineDeltaOrChange.lineDelta : 0
+ characterDelta = typeof lineDeltaOrChange.characterDelta === 'number' ? lineDeltaOrChange.characterDelta : 0
+ }
+
+ if (lineDelta === 0 && characterDelta === 0) {
+ return this
+ }
+ return new Position(this.line + lineDelta, this.character + characterDelta)
+ }
+
+ with (lineOrChange, character = this.character) {
+ if (lineOrChange === null || character === null) {
+ throw new Error()
+ }
+
+ let line
+ if (typeof lineOrChange === 'undefined') {
+ line = this.line
+ } else if (typeof lineOrChange === 'number') {
+ line = lineOrChange
+ } else {
+ line = typeof lineOrChange.line === 'number' ? lineOrChange.line : this.line
+ character = typeof lineOrChange.character === 'number' ? lineOrChange.character : this.character
+ }
+
+ if (line === this.line && character === this.character) {
+ return this
+ }
+ return new Position(line, character)
+ }
+
+ toJSON () {
+ return { line: this.line, character: this.character }
+ }
+}
+
+export class Range {
+ static isRange (thing) {
+ if (thing instanceof Range) {
+ return true
+ }
+ if (!thing) {
+ return false
+ }
+ return Position.isPosition(thing.start) && Position.isPosition(thing.end)
+ }
+
+ get start () {
+ return this._start
+ }
+
+ get end () {
+ return this._end
+ }
+
+ constructor (startLineOrStart, startColumnOrEnd, endLine, endColumn) {
+ let start
+ let end
+
+ if (typeof startLineOrStart === 'number' && typeof startColumnOrEnd === 'number' && typeof endLine === 'number' && typeof endColumn === 'number') {
+ start = new Position(startLineOrStart, startColumnOrEnd)
+ end = new Position(endLine, endColumn)
+ } else if (startLineOrStart instanceof Position && startColumnOrEnd instanceof Position) {
+ start = startLineOrStart
+ end = startColumnOrEnd
+ }
+
+ if (!start || !end) {
+ throw new Error('Invalid arguments')
+ }
+
+ if (start.isBefore(end)) {
+ this._start = start
+ this._end = end
+ } else {
+ this._start = end
+ this._end = start
+ }
+ }
+
+ contains (positionOrRange) {
+ if (positionOrRange instanceof Range) {
+ return this.contains(positionOrRange._start) &&
+ this.contains(positionOrRange._end)
+ } else if (positionOrRange instanceof Position) {
+ if (positionOrRange.isBefore(this._start)) {
+ return false
+ }
+ if (this._end.isBefore(positionOrRange)) {
+ return false
+ }
+ return true
+ }
+ return false
+ }
+
+ isEqual (other) {
+ return this._start.isEqual(other._start) && this._end.isEqual(other._end)
+ }
+
+ intersection (other) {
+ const start = Position.Max(other.start, this._start)
+ const end = Position.Min(other.end, this._end)
+ if (start.isAfter(end)) {
+ // this happens when there is no overlap:
+ // |-----|
+ // |----|
+ return undefined
+ }
+ return new Range(start, end)
+ }
+
+ union (other) {
+ if (this.contains(other)) {
+ return this
+ } else if (other.contains(this)) {
+ return other
+ }
+ const start = Position.Min(other.start, this._start)
+ const end = Position.Max(other.end, this.end)
+ return new Range(start, end)
+ }
+
+ get isEmpty () {
+ return this._start.isEqual(this._end)
+ }
+
+ get isSingleLine () {
+ return this._start.line === this._end.line
+ }
+
+ with (startOrChange, end = this.end) {
+ if (startOrChange === null || end === null) {
+ throw new Error()
+ }
+
+ let start
+ if (!startOrChange) {
+ start = this.start
+ } else if (Position.isPosition(startOrChange)) {
+ start = startOrChange
+ } else {
+ start = startOrChange.start || this.start
+ end = startOrChange.end || this.end
+ }
+
+ if (start.isEqual(this._start) && end.isEqual(this.end)) {
+ return this
+ }
+ return new Range(start, end)
+ }
+
+ toJSON () {
+ return [this.start, this.end]
+ }
+}
+
+export class Selection extends Range {
+ static isSelection (thing) {
+ if (thing instanceof Selection) {
+ return true
+ }
+ if (!thing) {
+ return false
+ }
+ return Range.isRange(thing) &&
+ Position.isPosition(thing.anchor) &&
+ Position.isPosition(thing.active) &&
+ typeof thing.isReversed === 'boolean'
+ }
+
+ get anchor () {
+ return this._anchor
+ }
+
+ get active () {
+ return this._active
+ }
+
+ constructor (anchorLineOrAnchor, anchorColumnOrActive, activeLine, activeColumn) {
+ let anchor
+ let active
+
+ if (typeof anchorLineOrAnchor === 'number' && typeof anchorColumnOrActive === 'number' && typeof activeLine === 'number' && typeof activeColumn === 'number') {
+ anchor = new Position(anchorLineOrAnchor, anchorColumnOrActive)
+ active = new Position(activeLine, activeColumn)
+ } else if (anchorLineOrAnchor instanceof Position && anchorColumnOrActive instanceof Position) {
+ anchor = anchorLineOrAnchor
+ active = anchorColumnOrActive
+ }
+
+ if (!anchor || !active) {
+ throw new Error('Invalid arguments')
+ }
+
+ super(anchor, active)
+
+ this._anchor = anchor
+ this._active = active
+ }
+
+ get isReversed () {
+ return this._anchor === this._end
+ }
+
+ toJSON () {
+ return {
+ start: this.start,
+ end: this.end,
+ active: this.active,
+ anchor: this.anchor
+ }
+ }
+}
+
+export const EndOfLine = {
+ LF: 1,
+ CRLF: 2
+}
+
+export class TextEdit {
+ static isTextEdit (thing) {
+ if (thing instanceof TextEdit) {
+ return true
+ }
+ if (!thing) {
+ return false
+ }
+ return Range.isRange(thing) && typeof thing.newText === 'string'
+ }
+
+ static replace (range, newText) {
+ return new TextEdit(range, newText)
+ }
+
+ static insert (position, newText) {
+ return TextEdit.replace(new Range(position, position), newText)
+ }
+
+ static delete (range) {
+ return TextEdit.replace(range, '')
+ }
+
+ static setEndOfLine (eol) {
+ const ret = new TextEdit(new Range(new Position(0, 0), new Position(0, 0)), '')
+ ret.newEol = eol
+ return ret
+ }
+
+ get range () {
+ return this._range
+ }
+
+ set range (value) {
+ if (value && !Range.isRange(value)) {
+ throw new Error('range')
+ }
+ this._range = value
+ }
+
+ get newText () {
+ return this._newText || ''
+ }
+
+ set newText (value) {
+ if (value && typeof value !== 'string') {
+ throw new Error('newText')
+ }
+ this._newText = value
+ }
+
+ get newEol () {
+ return this._newEol
+ }
+
+ set newEol (value) {
+ if (value && typeof value !== 'number') {
+ throw new Error('newEol')
+ }
+ this._newEol = value
+ }
+
+ constructor (range, newText) {
+ this.range = range
+ this._newText = newText
+ }
+
+ toJSON () {
+ return {
+ range: this.range,
+ newText: this.newText,
+ newEol: this._newEol
+ }
+ }
+}
+
+export class WorkspaceEdit {
+ constructor () {
+ this._edits = []
+ }
+
+ renameFile (from, to, options) {
+ this._edits.push({ _type: 1, from, to, options })
+ }
+
+ createFile (uri, options) {
+ this._edits.push({ _type: 1, from: undefined, to: uri, options })
+ }
+
+ deleteFile (uri, options) {
+ this._edits.push({ _type: 1, from: uri, to: undefined, options })
+ }
+
+ replace (uri, range, newText) {
+ this._edits.push({ _type: 2, uri, edit: new TextEdit(range, newText) })
+ }
+
+ insert (resource, position, newText) {
+ this.replace(resource, new Range(position, position), newText)
+ }
+
+ delete (resource, range) {
+ this.replace(resource, range, '')
+ }
+
+ has (uri) {
+ for (const edit of this._edits) {
+ if (edit._type === 2 && edit.uri.toString() === uri.toString()) {
+ return true
+ }
+ }
+ return false
+ }
+
+ set (uri, edits) {
+ if (!edits) {
+ // remove all text edits for `uri`
+ for (let i = 0; i < this._edits.length; i++) {
+ const element = this._edits[i]
+ if (element._type === 2 && element.uri.toString() === uri.toString()) {
+ this._edits[i] = undefined // will be coalesced down below
+ }
+ }
+ // this._edits = coalesce(this._edits); TODO
+ } else {
+ // append edit to the end
+ for (const edit of edits) {
+ if (edit) {
+ this._edits.push({ _type: 2, uri, edit })
+ }
+ }
+ }
+ }
+
+ get (uri) {
+ const res = []
+ for (const candidate of this._edits) {
+ if (candidate._type === 2 && candidate.uri.toString() === uri.toString()) {
+ res.push(candidate.edit)
+ }
+ }
+ return res
+ }
+
+ entries () {
+ const textEdits = new Map()
+ for (const candidate of this._edits) {
+ if (candidate._type === 2) {
+ let textEdit = textEdits.get(candidate.uri.toString())
+ if (!textEdit) {
+ textEdit = [candidate.uri, []]
+ textEdits.set(candidate.uri.toString(), textEdit)
+ }
+ textEdit[1].push(candidate.edit)
+ }
+ }
+ return values(textEdits)
+ }
+
+ _allEntries () {
+ const res = []
+ for (const edit of this._edits) {
+ if (edit._type === 1) {
+ res.push([edit.from, edit.to, edit.options])
+ } else {
+ res.push([edit.uri, [edit.edit]])
+ }
+ }
+ return res
+ }
+
+ get size () {
+ return this.entries().length
+ }
+
+ toJSON () {
+ return this.entries()
+ }
+}
+
+export const TextEditorRevealType = {
+ Default: 0,
+ InCenter: 1,
+ InCenterIfOutsideViewport: 2,
+ AtTop: 3
+}
+
+export const TextEditorSelectionChangeKind = {
+ Keyboard: 1,
+ Mouse: 2,
+ Command: 3
+}
+
+export class SnippetString {
+ static isSnippetString (thing) {
+ if (thing instanceof SnippetString) {
+ return true
+ }
+ if (!thing) {
+ return false
+ }
+ return typeof thing.value === 'string'
+ }
+
+ static _escape (value) {
+ return value.replace(/\$|}|\\/g, '\\$&')
+ }
+
+ constructor (value) {
+ this._tabstop = 1
+ this.value = value || ''
+ }
+
+ appendText (string) {
+ this.value += SnippetString._escape(string)
+ return this
+ }
+
+ appendTabstop (number = this._tabstop++) {
+ this.value += '$'
+ this.value += number
+ return this
+ }
+
+ appendPlaceholder (value, number = this._tabstop++) {
+ if (typeof value === 'function') {
+ const nested = new SnippetString()
+ nested._tabstop = this._tabstop
+ value(nested)
+ this._tabstop = nested._tabstop
+ value = nested.value
+ } else {
+ value = SnippetString._escape(value)
+ }
+
+ this.value += '${'
+ this.value += number
+ this.value += ':'
+ this.value += value
+ this.value += '}'
+
+ return this
+ }
+
+ appendVariable (name, defaultValue) {
+ if (typeof defaultValue === 'function') {
+ const nested = new SnippetString()
+ nested._tabstop = this._tabstop
+ defaultValue(nested)
+ this._tabstop = nested._tabstop
+ defaultValue = nested.value
+ } else if (typeof defaultValue === 'string') {
+ defaultValue = defaultValue.replace(/\$|}/g, '\\$&')
+ }
+
+ this.value += '${'
+ this.value += name
+ if (defaultValue) {
+ this.value += ':'
+ this.value += defaultValue
+ }
+ this.value += '}'
+
+ return this
+ }
+}
diff --git a/frontend/src/layouts/AdminLayout.vue b/frontend/src/layouts/AdminLayout.vue
new file mode 100644
index 00000000..40eae23f
--- /dev/null
+++ b/frontend/src/layouts/AdminLayout.vue
@@ -0,0 +1,454 @@
+
+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 Wiki.js
+ q-toolbar.gt-sm.justify-center(style='height: 64px;', dark)
+ .text-overline.text-uppercase.text-grey {{ t('admin.adminArea') }}
+ q-badge.q-ml-sm(
+ label='beta'
+ color='pink'
+ outline
+ )
+ q-toolbar(style='height: 64px;', dark)
+ q-space
+ transition(name='syncing')
+ q-spinner-tail(
+ v-show='commonStore.routerLoading'
+ color='accent'
+ size='24px'
+ )
+ q-btn.q-ml-md(flat, dense, icon='las la-times-circle', :label='t(`common.actions.exit`)' color='pink', to='/')
+ q-btn.q-ml-md(flat, dense, icon='las la-language', :label='commonStore.locale' color='grey-4')
+ q-menu.translucent-menu(auto-close, anchor='bottom right', self='top right')
+ q-list(separator, padding)
+ q-item(
+ v-for='lang of adminStore.locales'
+ :key='lang.code'
+ clickable
+ @click='commonStore.setLocale(lang.code)'
+ )
+ q-item-section(side)
+ q-avatar(rounded, :color='lang.code === commonStore.locale ? `secondary` : `primary`', text-color='white', size='sm')
+ .text-caption.text-uppercase: strong {{ lang.language }}
+ q-item-section
+ q-item-label {{ lang.nativeName }}
+ q-item-label(caption) {{ lang.name }}
+ 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.q-pb-lg(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', v-if='userStore.can(`manage:sites`)')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-change-theme.svg')
+ q-item-section {{ t('admin.sites.title') }}
+ q-item-section(side)
+ q-badge(color='dark-3', :label='adminStore.sites.length')
+ template(v-if='siteSectionShown')
+ 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') }}
+ template(v-if='flagsStore.experimental')
+ 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 + `/blocks`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-rfid-tag.svg')
+ q-item-section {{ t('admin.blocks.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/editors`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)')
+ 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', v-if='userStore.can(`manage:sites`)')
+ 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', v-if='userStore.can(`manage:sites`)')
+ 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', disabled, v-if='flagsStore.experimental && (userStore.can(`manage:sites`) || userStore.can(`manage:navigation`))')
+ 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 + `/storage`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`)')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-ssd.svg')
+ q-item-section {{ t('admin.storage.title') }}
+ q-item-section(side)
+ //- TODO: Reflect site storage status
+ status-light(:color='true ? `positive` : `warning`', :pulse='false')
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/tags`', v-ripple, active-class='bg-primary text-white', disabled, v-if='flagsStore.experimental && (userStore.can(`manage:sites`))')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-tag.svg')
+ q-item-section {{ t('admin.tags.title') }}
+ q-item(:to='`/_admin/` + adminStore.currentSiteId + `/theme`', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:sites`) || userStore.can(`manage:theme`)')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-paint-roller.svg')
+ q-item-section {{ t('admin.theme.title') }}
+ template(v-if='usersSectionShown')
+ 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', v-if='userStore.can(`manage:system`)')
+ 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', v-if='userStore.can(`manage:groups`)')
+ 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='adminStore.info.groupsTotal')
+ q-item(to='/_admin/users', v-ripple, active-class='bg-primary text-white', v-if='userStore.can(`manage:users`)')
+ 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='adminStore.info.usersTotal')
+ template(v-if='userStore.can(`manage:system`)')
+ 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-section(side)
+ status-light(:color='adminStore.info.isApiEnabled ? `positive` : `negative`')
+ q-item(to='/_admin/audit', v-ripple, active-class='bg-primary text-white', disabled, v-if='flagsStore.experimental')
+ 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/icons', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-spring.svg')
+ q-item-section {{ t('admin.icons.title') }}
+ q-item(to='/_admin/instances', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-network.svg')
+ q-item-section {{ t('admin.instances.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-section(side)
+ status-light(:color='adminStore.info.isMailConfigured ? `positive` : `warning`', :pulse='!adminStore.info.isMailConfigured')
+ q-item(to='/_admin/metrics', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-graph.svg')
+ q-item-section {{ t('admin.metrics.title') }}
+ q-item-section(side)
+ status-light(:color='adminStore.info.isMetricsEnabled ? `positive` : `negative`')
+ q-item(to='/_admin/rendering', v-ripple, active-class='bg-primary text-white', v-if='flagsStore.experimental')
+ 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/scheduler', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-bot.svg')
+ q-item-section {{ t('admin.scheduler.title') }}
+ q-item-section(side)
+ status-light(:color='adminStore.info.isSchedulerHealthy ? `positive` : `warning`', :pulse='!adminStore.info.isSchedulerHealthy')
+ q-item(to='/_admin/search', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-find-and-replace.svg')
+ q-item-section {{ t('admin.search.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, v-if='flagsStore.experimental')
+ 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-section(side)
+ status-light(:color='adminStore.isVersionLatest ? `positive` : `warning`')
+ q-item(to='/_admin/terminal', v-ripple, active-class='bg-primary text-white')
+ q-item-section(avatar)
+ q-icon(name='img:/_assets/icons/fluent-linux-terminal.svg')
+ q-item-section {{ t('admin.terminal.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 }')
+ 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='overlays[adminStore.overlay]')
+ footer-nav.admin-footer(generic)
+
+
+
+
+
diff --git a/frontend/src/layouts/AuthLayout.vue b/frontend/src/layouts/AuthLayout.vue
new file mode 100644
index 00000000..4cb6cf4d
--- /dev/null
+++ b/frontend/src/layouts/AuthLayout.vue
@@ -0,0 +1,13 @@
+
+q-layout(view='hHh lpr lff')
+ q-page-container
+ router-view
+
+
+
+
+
diff --git a/frontend/src/layouts/MainLayout.vue b/frontend/src/layouts/MainLayout.vue
new file mode 100644
index 00000000..ecb97a5b
--- /dev/null
+++ b/frontend/src/layouts/MainLayout.vue
@@ -0,0 +1,275 @@
+
+q-layout(view='hHh Lpr lff')
+ header-nav
+ q-drawer.bg-sidebar(
+ :model-value='isSidebarShown'
+ :show-if-above='siteStore.theme.sidebarPosition !== `off`'
+ :width='isSidebarMini ? 56 : 255'
+ :side='siteStore.theme.sidebarPosition === `right` ? `right` : `left`'
+ )
+ .sidebar-mini.column.items-stretch(v-if='isSidebarMini')
+ q-btn.q-py-md(
+ flat
+ icon='las la-globe'
+ color='white'
+ aria-label='Switch Locale'
+ )
+ locale-selector-menu(anchor='top right' self='top left')
+ q-tooltip(anchor='center right' self='center left') Switch Locale
+ q-btn.q-py-md(
+ flat
+ icon='las la-sitemap'
+ color='white'
+ aria-label='Browse'
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') Browse
+ q-separator.q-my-sm(inset, dark)
+ q-btn.q-py-md(
+ flat
+ icon='las la-bookmark'
+ color='white'
+ aria-label='Bookmarks'
+ @click='notImplemented'
+ )
+ q-tooltip(anchor='center right' self='center left') Bookmarks
+ q-space
+ q-btn.q-py-xs(
+ flat
+ icon='las la-dharmachakra'
+ color='white'
+ aria-label='Edit Nav'
+ size='sm'
+ )
+ q-menu(
+ ref='navEditMenuMini'
+ anchor='top right'
+ self='bottom left'
+ )
+ nav-edit-menu(
+ :menu-hide-handler='navEditMenuMini.hide'
+ :update-position-handler='navEditMenuMini.updatePosition'
+ )
+ q-tooltip(anchor='center right' self='center left') Edit Nav
+ template(v-else)
+ .sidebar-actions.flex.items-stretch
+ q-btn.q-px-sm.col(
+ flat
+ dense
+ icon='las la-globe'
+ color='blue-7'
+ text-color='custom-color'
+ :label='commonStore.locale'
+ :aria-label='commonStore.locale'
+ size='sm'
+ )
+ locale-selector-menu(:offset='[-5, 5]')
+ q-separator(vertical)
+ q-btn.q-px-sm.col(
+ flat
+ dense
+ icon='las la-sitemap'
+ color='blue-7'
+ text-color='custom-color'
+ label='Browse'
+ aria-label='Browse'
+ size='sm'
+ @click='notImplemented'
+ )
+ nav-sidebar
+ q-bar.sidebar-footerbtns.text-white(
+ v-if='userStore.authenticated'
+ dense
+ )
+ q-btn.col(
+ icon='las la-dharmachakra'
+ label='Edit Nav'
+ flat
+ )
+ q-menu(
+ ref='navEditMenu'
+ anchor='top left'
+ self='bottom left'
+ :offset='[0, 10]'
+ )
+ nav-edit-menu(
+ :menu-hide-handler='navEditMenu.hide'
+ :update-position-handler='navEditMenu.updatePosition'
+ )
+ q-separator(vertical)
+ q-btn.col(
+ icon='las la-bookmark'
+ label='Bookmarks'
+ flat
+ @click='notImplemented'
+ )
+ 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'
+ )
+ main-overlay-dialog
+ footer-nav(v-if='!editorStore.isActive')
+
+
+
+
+
diff --git a/frontend/src/layouts/ProfileLayout.vue b/frontend/src/layouts/ProfileLayout.vue
new file mode 100644
index 00000000..ef3a726f
--- /dev/null
+++ b/frontend/src/layouts/ProfileLayout.vue
@@ -0,0 +1,330 @@
+
+q-layout(view='hHh Lpr lff')
+ header-nav
+ q-page-container.layout-profile
+ .layout-profile-card
+ .layout-profile-sd
+ q-list
+ template(v-for='navItem of sidenav' :key='navItem.key')
+ q-item(
+ v-if='!navItem.disabled || flagsStore.experimental'
+ clickable
+ :to='`/_profile/` + navItem.key'
+ active-class='is-active'
+ :disabled='navItem.disabled'
+ 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='`/_user/` + userStore.id'
+ )
+ q-item-section(side)
+ q-icon(name='las la-id-card')
+ q-item-section
+ q-item-label {{ t('profile.viewPublicProfile') }}
+ q-separator.q-my-sm(inset)
+ q-item(
+ clickable
+ v-ripple
+ @click='userStore.logout()'
+ )
+ q-item-section(side)
+ q-icon(name='las la-sign-out-alt', color='negative')
+ q-item-section
+ q-item-label.text-negative {{ t('common.header.logout') }}
+ router-view
+ main-overlay-dialog
+ footer-nav
+
+
+
+
+
diff --git a/frontend/src/main.js b/frontend/src/main.js
new file mode 100644
index 00000000..dfdb4750
--- /dev/null
+++ b/frontend/src/main.js
@@ -0,0 +1,80 @@
+import { createApp } from 'vue'
+import { Quasar, Dialog, Loading, LoadingBar, Meta, Notify } from 'quasar'
+import { initializeRouter } from './router'
+import { initializeStore } from './stores'
+import { initializeApi } from './boot/api'
+import { initializeComponents } from './boot/components'
+import { initializeEventBus } from './boot/eventbus'
+import { initializeExternals } from './boot/externals'
+import { initializeI18n } from './boot/i18n'
+import quasarIconSet from 'quasar/icon-set/mdi-v7'
+
+// Import icon libraries
+import '@quasar/extras/roboto-font/roboto-font.css'
+import '@mdi/font/css/materialdesignicons.css'
+import '@quasar/extras/line-awesome/line-awesome.css'
+
+// Import Quasar css
+import 'quasar/src/css/index.sass'
+import './css/app.scss'
+
+import RootApp from './App.vue'
+
+const router = initializeRouter()
+const store = initializeStore(router)
+
+const app = createApp(RootApp)
+app.use(store)
+app.use(router)
+
+initializeApi(store)
+initializeComponents(app)
+initializeEventBus()
+initializeExternals(router, store)
+initializeI18n(app, store)
+
+app.use(Quasar, {
+ plugins: {
+ Dialog,
+ Loading,
+ LoadingBar,
+ Meta,
+ Notify
+ },
+ iconSet: quasarIconSet,
+ config: {
+ brand: {
+ header: '#000',
+ sidebar: '#1976D2'
+ },
+ loading: {
+ delay: 500,
+ spinner: 'QSpinnerGrid',
+ spinnerSize: 32,
+ spinnerColor: 'white',
+ customClass: 'loading-darker'
+ },
+ loadingBar: {
+ color: 'primary',
+ size: '1px',
+ position: 'top'
+ },
+ notify: {
+ position: 'top',
+ progress: true,
+ color: 'green',
+ icon: 'las la-check',
+ actions: [
+ {
+ icon: 'las la-times',
+ color: 'white',
+ size: 'sm',
+ round: true,
+ handler: () => {}
+ }
+ ]
+ }
+ }
+})
+
+app.mount('#app')
diff --git a/frontend/src/pages/AdminAnalytics.vue b/frontend/src/pages/AdminAnalytics.vue
new file mode 100644
index 00000000..337960a6
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminApi.vue b/frontend/src/pages/AdminApi.vue
new file mode 100644
index 00000000..e77189f2
--- /dev/null
+++ b/frontend/src/pages/AdminApi.vue
@@ -0,0 +1,246 @@
+
+q-page.admin-api
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-rest-api-animated.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='state.enabled')
+ q-spinner-rings.q-mr-sm(color='green', size='md')
+ .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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/dev/api`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='refresh'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn.q-mr-sm(
+ unelevated
+ icon='las la-power-off'
+ :label='!state.enabled ? t(`admin.api.enableButton`) : t(`admin.api.disableButton`)'
+ :color='!state.enabled ? `positive` : `negative`'
+ @click='globalSwitch'
+ :loading='state.isToggleLoading'
+ :disabled='state.loading > 0'
+ )
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='t(`admin.api.newKeyButton`)'
+ color='primary'
+ @click='newKey'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12(v-if='state.keys.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.api.none') }}
+ .col-12(v-else)
+ q-card
+ q-list(separator)
+ q-item(v-for='key of state.keys', :key='key.id')
+ q-item-section(side)
+ q-icon(name='las la-key', :color='key.isRevoked ? `negative` : `positive`')
+ q-item-section
+ q-item-label {{key.name}}
+ q-item-label(caption) Ending in {{key.keyShort}}
+ q-item-label(caption) Created On: #[strong {{DateTime.fromISO(key.createdAt).toFormat('fff')}}]
+ q-item-label(caption) Expiration: #[strong(:style='key.isRevoked ? `text-decoration: line-through;` : ``') {{DateTime.fromISO(key.expiration).toFormat('fff')}}]
+ q-item-section(
+ v-if='key.isRevoked'
+ side
+ style='flex-direction: row; align-items: center;'
+ )
+ q-icon.q-mr-sm(
+ color='negative'
+ size='xs'
+ name='las la-exclamation-triangle'
+ )
+ .text-caption.text-negative {{t('admin.api.revoked')}}
+ q-tooltip(anchor='center left', self='center right') {{t('admin.api.revokedHint')}}
+ q-separator.q-ml-md(vertical)
+ q-item-section(side, style='flex-direction: row; align-items: center;')
+ q-btn.acrylic-btn(
+ :color='key.isRevoked ? `gray` : `red`'
+ icon='las la-ban'
+ flat
+ @click='revoke(key)'
+ :disable='key.isRevoked'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminAuth.vue b/frontend/src/pages/AdminAuth.vue
new file mode 100644
index 00000000..f7ea8db7
--- /dev/null
+++ b/frontend/src/pages/AdminAuth.vue
@@ -0,0 +1,671 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/auth`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-auto
+ q-card.rounded-borders.bg-dark
+ q-list(
+ style='min-width: 350px;'
+ padding
+ dark
+ )
+ q-item(
+ v-for='str of state.activeStrategies'
+ :key='str.id'
+ active-class='bg-primary text-white'
+ :active='state.selectedStrategy === str.id'
+ @click='state.selectedStrategy = str.id'
+ 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)
+ status-light(:color='str.isEnabled ? `positive` : `negative`', :pulse='str.isEnabled')
+ q-btn.q-mt-sm.full-width(
+ color='primary'
+ icon='las la-plus'
+ :label='t(`admin.auth.addStrategy`)'
+ v-if='flagsStore.experimental'
+ )
+ q-menu(auto-close, fit, max-width='300px')
+ q-list(separator)
+ q-item(
+ v-for='str of availableStrategies'
+ :key='str.key'
+ clickable
+ @click='addStrategy(str)'
+ )
+ q-item-section(avatar)
+ q-avatar(
+ rounded
+ color='dark'
+ text-color='white'
+ )
+ q-icon(
+ :name='`img:` + str.icon'
+ )
+ q-item-section
+ q-item-label: strong {{ str.title }}
+ q-item-label(caption, lines='2') {{str.description}}
+ .col
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{ t('admin.auth.info') }}
+ q-item
+ blueprint-icon(icon='information')
+ q-item-section
+ q-item-label {{ t(`admin.auth.infoName`) }}
+ q-item-label(caption) {{ t(`admin.auth.infoNameHint`) }}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.strategy.displayName'
+ dense
+ hide-bottom-space
+ :aria-label='t(`admin.auth.infoName`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='shutdown')
+ q-item-section
+ q-item-label {{ t(`admin.auth.enabled`) }}
+ q-item-label(caption) {{ t(`admin.auth.enabledHint`) }}
+ q-item-label.text-deep-orange(v-if='state.strategy.strategy.key === `local`', caption) {{ t(`admin.auth.enabledForced`) }}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.strategy.isEnabled'
+ :disable='state.strategy.strategy.key === `local`'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.auth.enabled`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='register')
+ q-item-section
+ q-item-label {{ t(`admin.auth.registration`) }}
+ q-item-label(caption) {{ state.strategy.strategy.key === `local` ? t(`admin.auth.registrationLocalHint`) : t(`admin.auth.registrationHint`) }}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.strategy.registration'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.auth.registration`)'
+ )
+ template(v-if='state.strategy.registration')
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='team')
+ q-item-section
+ q-item-label {{ t(`admin.auth.autoEnrollGroups`) }}
+ q-item-label(caption) {{ t(`admin.auth.autoEnrollGroupsHint`) }}
+ q-item-section
+ q-select(
+ outlined
+ :options='state.groups'
+ v-model='state.strategy.autoEnrollGroups'
+ multiple
+ map-options
+ emit-value
+ option-value='id'
+ option-label='name'
+ options-dense
+ dense
+ hide-bottom-space
+ :aria-label='t(`admin.users.groups`)'
+ :loading='state.loadingGroups'
+ )
+ template(#selected)
+ .text-caption(v-if='state.strategy.autoEnrollGroups?.length > 1')
+ i18n-t(keypath='admin.users.groupsSelected')
+ template(#count)
+ strong {{ state.strategy.autoEnrollGroups?.length }}
+ .text-caption(v-else-if='state.strategy.autoEnrollGroups?.length === 1')
+ i18n-t(keypath='admin.users.groupSelected')
+ template(#group)
+ strong {{ selectedGroupName }}
+ span(v-else)
+ template(#option='{ itemProps, opt, selected, toggleOption }')
+ q-item(
+ v-bind='itemProps'
+ )
+ 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-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='private')
+ q-item-section
+ q-item-label {{ t(`admin.auth.allowedEmailRegex`) }}
+ q-item-label(caption) {{ t(`admin.auth.allowedEmailRegexHint`) }}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.strategy.allowedEmailRegex'
+ dense
+ hide-bottom-space
+ :aria-label='t(`admin.auth.allowedEmailRegex`)'
+ prefix='/'
+ suffix='/'
+ )
+
+ //- -----------------------
+ //- Configuration
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{ t('admin.auth.strategyConfiguration') }}
+ q-banner.q-mt-md(
+ v-if='!state.strategy.config || Object.keys(state.strategy.config).length < 1'
+ rounded
+ :class='$q.dark.isActive ? `bg-dark-4 text-grey-5` : `bg-grey-2 text-grey-7`'
+ ): em {{ t('admin.auth.noConfigOption') }}
+ template(
+ v-for='(cfg, cfgKey, idx) in state.strategy.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='cfg.title'
+ :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` : (cfg.sensitive ? `password` : `input`)'
+ :aria-label='cfg.title'
+ :disable='cfg.readOnly'
+ )
+
+ //- -----------------------
+ //- References
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md(v-if='strategyRefs.length > 0')
+ q-card-section
+ .text-subtitle1 {{ t('admin.auth.configReference') }}
+ q-item(v-for='strRef of strategyRefs', :key='strRef.key')
+ blueprint-icon(:icon='strRef.icon', :hue-rotate='-45')
+ q-item-section
+ q-item-label {{ strRef.title }}
+ q-item-label(caption) {{ strRef.hint }}
+ q-item-section
+ q-input(
+ outlined
+ v-model='strRef.value'
+ dense
+ :aria-label='strRef.title'
+ readonly
+ )
+ //- -----------------------
+ //- Infobox
+ //- -----------------------
+ q-card.q-mt-md
+ q-card-section.text-center
+ q-img.rounded-borders(
+ :src='state.strategy.strategy.logo'
+ fit='contain'
+ no-spinner
+ style='height: 100px; max-width: 300px;'
+ )
+ .text-subtitle2.q-mt-sm {{ state.strategy.strategy.title }}
+ .text-caption.q-mt-sm {{ state.strategy.strategy.description }}
+ .text-caption.q-mt-sm: strong {{ state.strategy.strategy.vendor }}
+ .text-caption: a(:href='state.strategy.strategy.website', target='_blank', rel='noreferrer') {{state.strategy.strategy.website}}
+
+ .flex.q-mt-md
+ .text-caption.text-grey ID: {{ state.strategy.id }}
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-trash-alt'
+ flat
+ color='negative'
+ :disable='state.strategy.strategy.key === `local`'
+ label='Delete Strategy'
+ @click='deleteStrategy(state.strategy.id)'
+ )
+
+
+
diff --git a/frontend/src/pages/AdminBlocks.vue b/frontend/src/pages/AdminBlocks.vue
new file mode 100644
index 00000000..64c04678
--- /dev/null
+++ b/frontend/src/pages/AdminBlocks.vue
@@ -0,0 +1,236 @@
+
+q-page.admin-flags
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-rfid-tag.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.blocks.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.blocks.subtitle') }}
+ .col-auto.flex
+ template(v-if='flagsStore.experimental')
+ q-btn.q-mr-sm.acrylic-btn(
+ unelevated
+ icon='las la-plus'
+ :label='t(`admin.blocks.add`)'
+ color='primary'
+ @click='addBlock'
+ )
+ q-separator.q-mr-sm(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/editors`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='refresh'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card
+ q-list(separator)
+ q-item(v-for='block of state.blocks', :key='block.id')
+ blueprint-icon(:icon='block.isCustom ? `plugin` : block.icon')
+ q-item-section
+ q-item-label: strong {{ block.name }}
+ q-item-label(caption) {{ block.description }}
+ q-item-label.flex.items-center(caption)
+ q-chip.q-ma-none(square, dense, :color='$q.dark.isActive ? `pink-8` : `pink-1`', :text-color='$q.dark.isActive ? `white` : `pink-9`'): span.text-caption <block-{{ block.block }}>
+ q-separator.q-mx-sm.q-my-xs(vertical)
+ em.text-purple(v-if='block.isCustom') {{ t('admin.blocks.custom') }}
+ em.text-teal-7(v-else) {{ t('admin.blocks.builtin') }}
+ template(v-if='block.isCustom')
+ q-item-section(
+ side
+ )
+ q-btn(
+ icon='las la-trash'
+ :aria-label='t(`common.actions.delete`)'
+ color='negative'
+ outline
+ no-caps
+ padding='xs sm'
+ @click='deleteBlock(block.id)'
+ )
+ q-separator.q-ml-lg(vertical)
+ q-item-section(side)
+ q-toggle.q-pr-sm(
+ v-model='block.isEnabled'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :label='t(`admin.blocks.isEnabled`)'
+ :aria-label='t(`admin.blocks.isEnabled`)'
+ disable
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminComments.vue b/frontend/src/pages/AdminComments.vue
new file mode 100644
index 00000000..02f831d3
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminDashboard.vue b/frontend/src/pages/AdminDashboard.vue
new file mode 100644
index 00000000..e1e12d75
--- /dev/null
+++ b/frontend/src/pages/AdminDashboard.vue
@@ -0,0 +1,271 @@
+
+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-sm
+ .col-12.col-sm-6.col-lg-3
+ q-card
+ q-card-section.admin-dashboard-card
+ img(src='/_assets/icons/fluent-change-theme.svg')
+ div
+ strong {{ t('admin.sites.title') }}
+ span {{adminStore.sites.length}}
+ q-separator
+ q-card-actions(align='right')
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-plus-circle'
+ :label='t(`common.actions.new`)'
+ :disable='!userStore.can(`manage:sites`)'
+ @click='newSite'
+ )
+ q-separator.q-mx-sm(vertical)
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-sitemap'
+ :label='t(`common.actions.manage`)'
+ :disable='!userStore.can(`manage:sites`)'
+ to='/_admin/sites'
+ )
+ .col-12.col-sm-6.col-lg-3
+ q-card
+ q-card-section.admin-dashboard-card
+ img(src='/_assets/icons/fluent-people.svg')
+ div
+ strong {{ t('admin.groups.title') }}
+ span {{adminStore.info.groupsTotal}}
+ q-separator
+ q-card-actions(align='right')
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-plus-circle'
+ :label='t(`common.actions.new`)'
+ :disable='!userStore.can(`manage:users`)'
+ @click='newGroup'
+ )
+ q-separator.q-mx-sm(vertical)
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-users'
+ :label='t(`common.actions.manage`)'
+ :disable='!userStore.can(`manage:users`)'
+ to='/_admin/groups'
+ )
+ .col-12.col-sm-6.col-lg-3
+ q-card
+ q-card-section.admin-dashboard-card
+ img(src='/_assets/icons/fluent-account.svg')
+ div
+ strong {{ t('admin.users.title') }}
+ span {{adminStore.info.usersTotal}}
+ q-separator
+ q-card-actions(align='right')
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-user-plus'
+ :label='t(`common.actions.new`)'
+ :disable='!userStore.can(`manage:users`)'
+ @click='newUser'
+ )
+ q-separator.q-mx-sm(vertical)
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-user-friends'
+ :label='t(`common.actions.manage`)'
+ :disable='!userStore.can(`manage:users`)'
+ to='/_admin/users'
+ )
+ //- .col-12.col-sm-6.col-lg-3
+ //- q-card
+ //- q-card-section.admin-dashboard-card
+ //- img(src='/_assets/icons/fluent-tag.svg')
+ //- div
+ //- strong {{ t('admin.tags.title') }}
+ //- span {{adminStore.info.tagsTotal}}
+ //- q-separator
+ //- q-card-actions(align='right')
+ //- q-btn(
+ //- flat
+ //- color='primary'
+ //- icon='las la-tags'
+ //- :label='t(`common.actions.manage`)'
+ //- :disable='!userStore.can(`manage:sites`)'
+ //- :to='`/_admin/` + adminStore.currentSiteId + `/tags`'
+ //- )
+ .col-12.col-sm-6.col-lg-3
+ q-card
+ q-card-section.admin-dashboard-card
+ img(src='/_assets/icons/fluent-female-working-with-a-laptop.svg')
+ div
+ strong Logins
+ small {{ adminStore.info.loginsPastDay }} #[i / past 24h]
+ q-separator
+ q-card-actions(align='right')
+ q-btn(
+ flat
+ color='primary'
+ icon='las la-chart-area'
+ :label='t(`admin.analytics.title`)'
+ :disable='!flagsStore.experimental'
+ :to='`/_admin/` + adminStore.currentSiteId + `/analytics`'
+ )
+ //- .col-12.col-lg-9
+ //- q-card
+ //- q-card-section ---
+
+ .col-12
+ q-banner.bg-positive.text-white(
+ :class='adminStore.isVersionLatest ? `bg-positive` : `bg-warning`'
+ inline-actions
+ rounded
+ )
+ i.las.la-check.q-mr-sm
+ span.text-weight-medium(v-if='adminStore.isVersionLatest') Your Wiki.js server is running the latest version!
+ span.text-weight-medium(v-else) A new version of Wiki.js is available. Please update to the latest version.
+ template(#action, v-if='userStore.can(`manage:system`)')
+ q-btn(
+ flat
+ :label='t(`admin.system.checkForUpdates`)'
+ @click='checkForUpdates'
+ )
+ q-separator.q-mx-sm(vertical, dark)
+ q-btn(
+ flat
+ :label='t(`admin.system.title`)'
+ to='/_admin/system'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminEditors.vue b/frontend/src/pages/AdminEditors.vue
new file mode 100644
index 00000000..2d38d599
--- /dev/null
+++ b/frontend/src/pages/AdminEditors.vue
@@ -0,0 +1,309 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/editors`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='refresh'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card
+ q-list(separator)
+ template(v-for='editor of editors', :key='editor.id')
+ q-item(v-if='flagsStore.experimental || !editor.isDisabled')
+ blueprint-icon(:icon='editor.icon')
+ q-item-section
+ q-item-label: strong {{t(`admin.editors.` + editor.id + `Name`)}}
+ q-item-label(caption)
+ span {{t(`admin.editors.` + editor.id + `Description`)}}
+ q-item-label(caption, v-if='editor.useRendering')
+ em.text-purple {{ t('admin.editors.useRenderingPipeline') }}
+ template(v-if='editor.hasConfig')
+ 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'
+ @click='openConfig(editor.id)'
+ )
+ q-separator.q-ml-md(vertical)
+ q-item-section(side)
+ q-toggle.q-pr-sm(
+ v-model='state.config[editor.id]'
+ :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/frontend/src/pages/AdminExtensions.vue b/frontend/src/pages/AdminExtensions.vue
new file mode 100644
index 00000000..06d21832
--- /dev/null
+++ b/frontend/src/pages/AdminExtensions.vue
@@ -0,0 +1,229 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/extensions`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card
+ q-list(separator)
+ q-item(
+ v-for='ext of state.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/frontend/src/pages/AdminFlags.vue b/frontend/src/pages/AdminFlags.vue
new file mode 100644
index 00000000..bbf55fc7
--- /dev/null
+++ b/frontend/src/pages/AdminFlags.vue
@@ -0,0 +1,257 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/flags`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ q-card.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')
+ blueprint-icon(icon='flag-filled')
+ q-item-section
+ q-item-label {{t(`admin.flags.experimental.label`)}}
+ q-item-label(caption) {{t(`admin.flags.experimental.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.flags.experimental'
+ color='negative'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.flags.experimental.label`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='flag-filled')
+ q-item-section
+ q-item-label {{t(`admin.flags.authDebug.label`)}}
+ q-item-label(caption) {{t(`admin.flags.authDebug.hint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.flags.authDebug'
+ color='negative'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.flags.authDebug.label`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ 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='state.flags.sqlLog'
+ color='negative'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.flags.sqlLog.label`)'
+ )
+ q-card.q-py-sm.q-mt-md
+ q-item
+ blueprint-icon(icon='administrative-tools')
+ q-item-section
+ q-item-label {{t(`admin.flags.advanced.label`)}}
+ q-item-label(caption) {{t(`admin.flags.advanced.hint`)}}
+ q-item-section(avatar)
+ q-btn(
+ :label='t(`common.actions.edit`)'
+ unelevated
+ icon='las la-code'
+ color='primary'
+ text-color='white'
+ disabled
+ )
+
+ q-card.q-py-sm.q-mt-md
+ q-item
+ blueprint-icon(icon='key')
+ q-item-section
+ q-item-label {{t(`admin.flags.getTokenLabel`)}}
+ q-item-label(caption) {{t(`admin.flags.getTokenHint`)}}
+ q-item-section(avatar)
+ q-btn(
+ ref='copyTokenBtn'
+ :label='t(`common.actions.copy`)'
+ unelevated
+ icon='las la-clipboard'
+ color='primary'
+ text-color='white'
+ )
+
+ .col-12.col-lg-5.gt-md
+ .q-pa-md.text-center
+ img(src='/_assets/illustrations/undraw_settings.svg', style='width: 80%;')
+
+
+
+
+
diff --git a/frontend/src/pages/AdminGeneral.vue b/frontend/src/pages/AdminGeneral.vue
new file mode 100644
index 00000000..c3454d34
--- /dev/null
+++ b/frontend/src/pages/AdminGeneral.vue
@@ -0,0 +1,916 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/sites#general`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ //- -----------------------
+ //- Site Info
+ //- -----------------------
+ q-card.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='state.config.title'
+ dense
+ :rules='rulesTitle'
+ 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='state.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='state.config.hostname'
+ dense
+ :rules='rulesHostname'
+ hide-bottom-space
+ :aria-label='t(`admin.general.siteHostname`)'
+ )
+
+ //- -----------------------
+ //- Footer / Copyright
+ //- -----------------------
+ q-card.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='state.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='state.config.contentLicense'
+ :options='contentLicenses'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.general.contentLicense`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='subtitles')
+ q-item-section
+ q-item-label {{t(`admin.general.footerExtra`)}}
+ q-item-label(caption) {{t(`admin.general.footerExtraHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.config.footerExtra'
+ dense
+ :aria-label='t(`admin.general.footerExtra`)'
+ )
+
+ //- -----------------------
+ //- FEATURES
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.general.features')}}
+ q-item(tag='label')
+ blueprint-icon(icon='tree-structure')
+ q-item-section
+ q-item-label {{t(`admin.general.allowBrowse`)}}
+ q-item-label(caption) {{t(`admin.general.allowBrowseHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.features.browse'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.allowBrowse`)'
+ )
+ q-separator.q-my-sm(inset)
+ 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='state.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='state.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='state.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='state.config.features.ratingsMode'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='ratingsModes'
+ )
+ 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='state.config.features.search'
+ 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
+ blueprint-icon(icon='confusion')
+ q-item-section
+ q-item-label {{t(`admin.general.reasonForChange`)}}
+ q-item-label(caption) {{t(`admin.general.reasonForChangeHint`)}}
+ q-item-section(avatar)
+ q-btn-toggle(
+ v-model='state.config.features.reasonForChange'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='reasonForChangeModes'
+ )
+
+ //- -----------------------
+ //- Defaults
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md(v-if='state.config.defaults')
+ q-card-section
+ .text-subtitle1 {{t('admin.general.defaults')}}
+ q-item
+ blueprint-icon(icon='depth')
+ q-item-section
+ q-item-label {{t(`admin.general.defaultTocDepth`)}}
+ q-item-label(caption) {{t(`admin.general.defaultTocDepthHint`)}}
+ q-item-section.col-auto.q-pl-sm(style='min-width: 180px;')
+ .text-caption {{t('editor.props.tocMinMaxDepth')}} #[strong (H{{state.config.defaults.tocDepth.min}} → H{{state.config.defaults.tocDepth.max}})]
+ q-range(
+ v-model='state.config.defaults.tocDepth'
+ :min='1'
+ :max='6'
+ color='primary'
+ :left-label-value='`H` + state.config.defaults.tocDepth.min'
+ :right-label-value='`H` + state.config.defaults.tocDepth.max'
+ snap
+ label
+ markers
+ )
+
+ .col-12.col-lg-5
+ //- -----------------------
+ //- Logo
+ //- -----------------------
+ q-card.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, v-if='adminStore.currentSiteId')
+ q-avatar(
+ v-if='state.config.logoText'
+ size='34px'
+ square
+ )
+ img(:src='`/_site/` + adminStore.currentSiteId + `/logo?` + state.assetTimestamp')
+ img(
+ v-else
+ :src='`/_site/` + adminStore.currentSiteId + `/logo?` + state.assetTimestamp'
+ style='height: 34px;'
+ )
+ q-toolbar-title.text-h6(v-if='state.config.logoText') {{state.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='state.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(
+ v-if='adminStore.currentSiteId'
+ size='24px'
+ square
+ )
+ img(:src='`/_site/` + adminStore.currentSiteId + `/favicon?` + state.assetTimestamp')
+ .text-caption.q-ml-sm {{state.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...
+
+ //- -----------------------
+ //- Discovery
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.general.discovery')}}
+ q-item(tag='label')
+ blueprint-icon(icon='cellular-network')
+ q-item-section
+ q-item-label {{t(`admin.general.discoverable`)}}
+ q-item-label(caption) {{t(`admin.general.discoverableHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.discoverable'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.discoverable`)'
+ )
+
+ //- -----------------------
+ //- Uploads
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md(v-if='state.config.uploads')
+ q-card-section
+ .text-subtitle1 {{t('admin.general.uploads')}}
+ q-item
+ blueprint-icon(icon='merge-files')
+ q-item-section
+ q-item-label {{t(`admin.general.uploadConflictBehavior`)}}
+ q-item-label(caption) {{t(`admin.general.uploadConflictBehaviorHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='state.config.uploads.conflictBehavior'
+ :options='uploadConflictBehaviors'
+ option-value='value'
+ option-label='label'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :virtual-scroll-slice-size='1000'
+ :aria-label='t(`admin.general.uploadConflictBehavior`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='rename')
+ q-item-section
+ q-item-label {{t(`admin.general.uploadNormalizeFilename`)}}
+ q-item-label(caption) {{t(`admin.general.uploadNormalizeFilenameHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.uploads.normalizeFilename'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.uploadNormalizeFilename`)'
+ )
+
+ //- -----------------------
+ //- URL Handling
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.general.urlHandling')}}
+ q-item
+ blueprint-icon(icon='sort-by-follow-up-date')
+ q-item-section
+ q-item-label {{t(`admin.general.pageExtensions`)}}
+ q-item-label(caption) {{t(`admin.general.pageExtensionsHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.config.pageExtensions'
+ dense
+ :aria-label='t(`admin.general.pageExtensions`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='lowercase')
+ q-item-section
+ q-item-label {{t(`admin.general.pageCasing`)}}
+ q-item-label(caption) {{t(`admin.general.pageCasingHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.pageCasing'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.pageCasing`)'
+ )
+
+ //- -----------------------
+ //- SEO
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md(v-if='state.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='state.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='state.config.robots.follow'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.searchAllowFollow`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='genealogy')
+ q-item-section
+ q-item-label {{t(`admin.general.sitemap`)}}
+ q-item-label(caption) {{t(`admin.general.sitemapHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.sitemap'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.general.sitemap`)'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/pages/AdminGroups.vue b/frontend/src/pages/AdminGroups.vue
new file mode 100644
index 00000000..cb9327a4
--- /dev/null
+++ b/frontend/src/pages/AdminGroups.vue
@@ -0,0 +1,276 @@
+
+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='state.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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/groups`'
+ target='_blank'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ :loading='state.loading > 0'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ 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
+ q-table(
+ :rows='state.groups'
+ :columns='headers'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ :filter='state.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` : `negative`'
+ :disabled='props.row.isSystem'
+ @click='deleteGroup(props.row)'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminIcons.vue b/frontend/src/pages/AdminIcons.vue
new file mode 100644
index 00000000..4cca4f49
--- /dev/null
+++ b/frontend/src/pages/AdminIcons.vue
@@ -0,0 +1,254 @@
+
+q-page.admin-icons
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.admin-icons-icon.animated.fadeInLeft(src='/_assets/icons/fluent-spring.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.icons.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.icons.subtitle') }}
+ .col-auto
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/icons`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card
+ q-card-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.icons.warnLabel') }}
+ .text-caption.text-red-1 {{ t('admin.icons.warnHint') }}
+ q-list(separator)
+ q-item(v-for='pack of combinedPacks', :key='pack.key')
+ blueprint-icon(icon='small-icons', :hueRotate='30')
+ q-item-section
+ q-item-label: strong {{pack.label}}
+ q-item-label(caption, v-if='pack.isMandatory')
+ em {{t('admin.icons.mandatory')}}
+ template(v-if='pack.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-btn(
+ type='a'
+ icon='las la-external-link-square-alt'
+ :label='t(`admin.icons.reference`)'
+ color='indigo'
+ outline
+ no-caps
+ padding='xs md'
+ :href='pack.website'
+ target='_blank'
+ rel='noreferrer noopener'
+ )
+ q-separator.q-ml-md(vertical)
+ q-item-section(side)
+ q-toggle.q-pr-sm(
+ :modelValue='pack.isActive'
+ @update:model-value='newValue => setPackState(pack.key, newValue)'
+ :color='pack.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='pack.isMandatory'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/pages/AdminInstances.vue b/frontend/src/pages/AdminInstances.vue
new file mode 100644
index 00000000..d3c7ce8f
--- /dev/null
+++ b/frontend/src/pages/AdminInstances.vue
@@ -0,0 +1,212 @@
+
+q-page.admin-terminal
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-network.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.instances.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.instances.subtitle') }}
+ .col-auto.flex
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/instances`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card
+ q-table(
+ :rows='state.instances'
+ :columns='instancesHeaders'
+ row-key='name'
+ flat
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ )
+ template(v-slot:body-cell-icon='props')
+ q-td(:props='props')
+ q-icon(name='las la-server', color='positive', size='sm')
+ template(v-slot:body-cell-id='props')
+ q-td(:props='props')
+ strong {{props.value}}
+ div: small.text-grey: strong {{props.row.ip}}
+ div: small.text-grey {{props.row.dbUser}}
+ template(v-slot:body-cell-cons='props')
+ q-td(:props='props')
+ q-chip(
+ icon='las la-plug'
+ square
+ size='md'
+ color='blue'
+ text-color='white'
+ )
+ span.font-robotomono {{ props.value }}
+ template(v-slot:body-cell-subs='props')
+ q-td(:props='props')
+ q-chip(
+ icon='las la-broadcast-tower'
+ square
+ size='md'
+ color='green'
+ text-color='white'
+ )
+ small.text-uppercase {{ props.value }}
+ template(v-slot:body-cell-firstseen='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div: small.text-grey {{humanizeDate(props.row.dbFirstSeen)}}
+ template(v-slot:body-cell-lastseen='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div: small.text-grey {{humanizeDate(props.row.dbLastSeen)}}
+
+
+
diff --git a/frontend/src/pages/AdminLocale.vue b/frontend/src/pages/AdminLocale.vue
new file mode 100644
index 00000000..070b45d6
--- /dev/null
+++ b/frontend/src/pages/AdminLocale.vue
@@ -0,0 +1,267 @@
+
+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-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/localisation`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ //- -----------------------
+ //- Locale Options
+ //- -----------------------
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('admin.locale.settings')}}
+ q-item
+ blueprint-icon(icon='translation')
+ q-item-section
+ q-item-label {{t(`admin.locale.primary`)}}
+ q-item-label(caption) {{t(`admin.locale.primaryHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='state.primary'
+ :options='state.locales'
+ option-value='code'
+ option-label='name'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.locale.primary`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ blueprint-icon(icon='close-pane')
+ q-item-section
+ q-item-label {{t(`admin.locale.forcePrefix`)}}
+ q-item-label(caption) {{t(`admin.locale.forcePrefixHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.forcePrefix'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.locale.forcePrefixHint`)'
+ )
+
+ //- -----------------------
+ //- Active Locales
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.locale.active')}}
+ .text-caption(:class='$q.dark.isActive ? `text-grey-4` : `text-grey-7`') Select the locales that can be used on this site.
+
+ q-item(
+ v-for='lc of state.locales'
+ :key='lc.code'
+ :tag='lc.code !== state.selectedLocale ? `label` : null'
+ )
+ blueprint-icon(:text='lc.language')
+ q-item-section
+ q-item-label {{lc.nativeName}}
+ q-item-label(caption) {{lc.name}} ({{lc.code}})
+ q-item-section(avatar)
+ q-toggle(
+ :disable='lc.code === state.primary'
+ v-model='state.active'
+ :val='lc.code'
+ :color='lc.code === state.primary ? `secondary` : `primary`'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='lc.name'
+ )
+
+ .col-12.col-lg-5
+ .q-pa-md.text-center
+ img(src='/_assets/illustrations/undraw_world.svg', style='width: 80%;')
+
+
+
+
diff --git a/frontend/src/pages/AdminLogin.vue b/frontend/src/pages/AdminLogin.vue
new file mode 100644
index 00000000..8433d0d2
--- /dev/null
+++ b/frontend/src/pages/AdminLogin.vue
@@ -0,0 +1,407 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/auth`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Experience
+ //- -----------------------
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('admin.login.experience')}}
+ q-item
+ 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'
+ @click='uploadBg'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ 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='state.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')
+ 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='state.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='state.config.loginRedirect'
+ dense
+ :rules=`[
+ val => state.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='state.config.welcomeRedirect'
+ dense
+ :rules=`[
+ val => state.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='state.config.logoutRedirect'
+ dense
+ :rules=`[
+ val => state.invalidCharsRegex.test(val) || t('admin.login.logoutRedirectInvalidChars')
+ ]`
+ hide-bottom-space
+ :aria-label='t(`admin.login.logoutRedirect`)'
+ )
+
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Providers
+ //- -----------------------
+ q-card.q-pb-sm
+ q-card-section
+ .text-subtitle1 {{t('admin.login.providers')}}
+ q-card-section.admin-login-providers.q-pt-none
+ sortable(
+ class='q-list'
+ :list='state.providers'
+ item-key='id'
+ :options='sortableOptions'
+ @end='updateAuthPosition'
+ )
+ template(#item='{element}')
+ q-item
+ q-item-section(side)
+ q-icon.handle(name='mdi-drag-horizontal')
+ q-item-section(side)
+ q-icon(:name='`img:` + element.activeStrategy.strategy.icon')
+ q-item-section
+ q-item-label {{element.activeStrategy.displayName}}
+ q-item-label(caption) {{element.activeStrategy.strategy.title}}
+ q-item-section(side)
+ q-toggle(
+ v-model='element.isVisible'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ label='Visible'
+ :aria-label='element.activeStrategy.displayName'
+ )
+ 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/frontend/src/pages/AdminMail.vue b/frontend/src/pages/AdminMail.vue
new file mode 100644
index 00000000..6ad0f627
--- /dev/null
+++ b/frontend/src/pages/AdminMail.vue
@@ -0,0 +1,548 @@
+
+q-page.admin-mail
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-message-settings-animated.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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/mail`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ //- -----------------------
+ //- Configuration
+ //- -----------------------
+ q-card.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='state.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='state.config.senderEmail'
+ dense
+ :aria-label='t(`admin.mail.senderEmail`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='dns')
+ q-item-section
+ q-item-label {{t(`admin.mail.defaultBaseURL`)}}
+ q-item-label(caption) {{t(`admin.general.defaultBaseURLHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.config.defaultBaseURL'
+ dense
+ :aria-label='t(`admin.mail.defaultBaseURL`)'
+ )
+ //- -----------------------
+ //- SMTP
+ //- -----------------------
+ q-card.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='state.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='state.config.port'
+ dense
+ :aria-label='t(`admin.mail.smtpPort`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ 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='state.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')
+ 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='state.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='state.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='state.config.pass'
+ dense
+ :aria-label='t(`admin.mail.smtpPwd`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='server')
+ q-item-section
+ q-item-label {{t(`admin.mail.smtpName`)}}
+ q-item-label(caption) {{t(`admin.mail.smtpNameHint`)}}
+ q-item-section
+ q-input(
+ outlined
+ v-model='state.config.name'
+ dense
+ hide-bottom-space
+ :aria-label='t(`admin.mail.smtpName`)'
+ )
+ //- -----------------------
+ //- DKIM
+ //- -----------------------
+ q-card.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')
+ 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='state.config.useDKIM'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.mail.dkimUse`)'
+ )
+ template(v-if='state.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='state.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='state.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='state.config.dkimPrivateKey'
+ dense
+ :aria-label='t(`admin.mail.dkimPrivateKey`)'
+ type='textarea'
+ )
+
+ .col-12.col-lg-5
+ //- -----------------------
+ //- MAIL TEMPLATES
+ //- -----------------------
+ q-card.q-pb-sm.q-mb-md(v-if='flagStore.experimental')
+ 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='editTemplate(`welcome`)'
+ :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='editTemplate(`pwdreset`)'
+ :label='t(`common.actions.edit`)'
+ )
+ //- -----------------------
+ //- SMTP TEST
+ //- -----------------------
+ q-card.q-pb-sm
+ 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='state.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='state.testLoading'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/pages/AdminMetrics.vue b/frontend/src/pages/AdminMetrics.vue
new file mode 100644
index 00000000..6cfb36a4
--- /dev/null
+++ b/frontend/src/pages/AdminMetrics.vue
@@ -0,0 +1,188 @@
+
+q-page.admin-api
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-graph.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.metrics.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.metrics.subtitle') }}
+ .col
+ .flex.items-center
+ template(v-if='state.enabled')
+ q-spinner-rings.q-mr-sm(color='green', size='md')
+ .text-caption.text-green {{t('admin.metrics.enabled')}}
+ template(v-else)
+ q-spinner-rings.q-mr-sm(color='red', size='md')
+ .text-caption.text-red {{t('admin.metrics.disabled')}}
+ .col-auto
+ q-btn.q-mr-sm.q-ml-md.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/metrics`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='refresh'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn.q-mr-sm(
+ unelevated
+ icon='las la-power-off'
+ :label='!state.enabled ? t(`common.actions.activate`) : t(`common.actions.deactivate`)'
+ :color='!state.enabled ? `positive` : `negative`'
+ @click='globalSwitch'
+ :loading='state.isToggleLoading'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ 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
+ i18n-t(tag='span', keypath='admin.metrics.endpoint', scope='global')
+ template(#endpoint)
+ strong.font-robotomono /metrics
+ .text-caption {{ t('admin.metrics.endpointWarning') }}
+ q-card.rounded-borders.q-mt-md(
+ 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-key', size='sm')
+ q-card-section
+ i18n-t(tag='span', keypath='admin.metrics.auth', scope='global')
+ template(#headerName)
+ strong.font-robotomono Authorization
+ template(#tokenType)
+ strong.font-robotomono Bearer
+ template(#permission)
+ strong.font-robotomono read:metrics
+ .text-caption.font-robotomono Authorization: Bearer API-KEY-VALUE
+
+
+
+
+
diff --git a/frontend/src/pages/AdminNavigation.vue b/frontend/src/pages/AdminNavigation.vue
new file mode 100644
index 00000000..5ef04beb
--- /dev/null
+++ b/frontend/src/pages/AdminNavigation.vue
@@ -0,0 +1,619 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/navigation`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-auto
+ q-card.q-mt-sm {{t('admin.navigation.mode')}}
+
+ q-card.bg-dark.q-mt-sm
+ q-list(
+ style='min-width: 350px;'
+ padding
+ dark
+ )
+ q-item
+ q-item-section
+ q-select(
+ dark
+ outlined
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ options-dense
+ :label='t(`admin.navigation.mode`)'
+ :aria-label='t(`admin.navigation.mode`)'
+ )
+
+//- 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/frontend/src/pages/AdminPages.vue b/frontend/src/pages/AdminPages.vue
new file mode 100644
index 00000000..4bd5958c
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminPagesEdit.vue b/frontend/src/pages/AdminPagesEdit.vue
new file mode 100644
index 00000000..ef29082c
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminPagesVisualize.vue b/frontend/src/pages/AdminPagesVisualize.vue
new file mode 100644
index 00000000..e02cdaae
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminRendering.vue b/frontend/src/pages/AdminRendering.vue
new file mode 100644
index 00000000..165ddae0
--- /dev/null
+++ b/frontend/src/pages/AdminRendering.vue
@@ -0,0 +1,116 @@
+
+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='siteStore.docsBase + `/system/rendering`'
+ target='_blank'
+ type='a'
+ )
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='$t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+
+ .row.q-pa-md.q-col-gutter-md
+ .col-auto
+ q-card.rounded-borders.bg-dark
+ q-list(
+ style='min-width: 300px;'
+ padding
+ dark
+ )
+ q-item(
+ v-for='rdr of state.renderers'
+ :key='rdr.key'
+ active-class='bg-primary text-white'
+ :active='state.selectedRenderer === rdr.id'
+ @click='state.selectedRenderer = rdr.id'
+ clickable
+ )
+ q-item-section(side)
+ q-icon(:name='`img:` + rdr.icon')
+ q-item-section
+ q-item-label {{rdr.title}}
+ q-item-label(caption) {{rdr.description}}
+ q-item-section(side)
+ status-light(:color='rdr.isEnabled ? `positive` : `negative`', :pulse='rdr.isEnabled')
+ .col
+ .row.q-col-gutter-md
+ .col-12.col-lg
+
+
+
+
diff --git a/frontend/src/pages/AdminScheduler.vue b/frontend/src/pages/AdminScheduler.vue
new file mode 100644
index 00000000..27d23a1f
--- /dev/null
+++ b/frontend/src/pages/AdminScheduler.vue
@@ -0,0 +1,644 @@
+
+q-page.admin-terminal
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-bot-animated.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.scheduler.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.scheduler.subtitle') }}
+ .col-auto.flex
+ q-btn-toggle.q-mr-md(
+ v-model='state.displayMode'
+ push
+ no-caps
+ :disable='state.loading > 0'
+ :toggle-color='$q.dark.isActive ? `white` : `black`'
+ :toggle-text-color='$q.dark.isActive ? `black` : `white`'
+ :text-color='$q.dark.isActive ? `white` : `black`'
+ :color='$q.dark.isActive ? `dark-1` : `white`'
+ :options=`[
+ { label: t('admin.scheduler.schedule'), value: 'scheduled' },
+ { label: t('admin.scheduler.upcoming'), value: 'upcoming' },
+ { label: t('admin.scheduler.active'), value: 'active' },
+ { label: t('admin.scheduler.completed'), value: 'completed' },
+ { label: t('admin.scheduler.failed'), value: 'failed' },
+ ]`
+ )
+ q-separator.q-mr-md(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/scheduler`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ template(v-if='state.displayMode === `scheduled`')
+ q-card.rounded-borders(
+ v-if='state.scheduledJobs.length < 1'
+ 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.scheduler.scheduledNone') }}
+ q-card(v-else)
+ q-table(
+ :rows='state.scheduledJobs'
+ :columns='scheduledJobsHeaders'
+ row-key='name'
+ flat
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ )
+ template(v-slot:body-cell-id='props')
+ q-td(:props='props')
+ q-spinner-clock.q-mr-sm(
+ color='indigo'
+ size='xs'
+ )
+ template(v-slot:body-cell-task='props')
+ q-td(:props='props')
+ strong {{props.value}}
+ div: small.text-grey {{props.row.id}}
+ template(v-slot:body-cell-cron='props')
+ q-td(:props='props')
+ q-chip(
+ square
+ size='md'
+ color='blue'
+ text-color='white'
+ )
+ span.font-robotomono {{ props.value }}
+ template(v-slot:body-cell-type='props')
+ q-td(:props='props')
+ q-chip(
+ square
+ size='md'
+ dense
+ color='deep-orange'
+ text-color='white'
+ )
+ small.text-uppercase {{ props.value }}
+ template(v-slot:body-cell-created='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div: small.text-grey {{humanizeDate(props.row.createdAt)}}
+ template(v-slot:body-cell-updated='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div: small.text-grey {{humanizeDate(props.row.updatedAt)}}
+ template(v-else-if='state.displayMode === `upcoming`')
+ q-card.rounded-borders(
+ v-if='state.upcomingJobs.length < 1'
+ 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.scheduler.upcomingNone') }}
+ q-card(v-else)
+ q-table(
+ :rows='state.upcomingJobs'
+ :columns='upcomingJobsHeaders'
+ row-key='name'
+ flat
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ )
+ template(v-slot:body-cell-id='props')
+ q-td(:props='props')
+ q-icon(name='las la-clock', color='primary', size='sm')
+ template(v-slot:body-cell-task='props')
+ q-td(:props='props')
+ strong {{props.value}}
+ div: small.text-grey {{props.row.id}}
+ template(v-slot:body-cell-waituntil='props')
+ q-td(:props='props')
+ span {{ props.value }}
+ div: small.text-grey {{humanizeDate(props.row.waitUntil)}}
+ template(v-slot:body-cell-retries='props')
+ q-td(:props='props')
+ span #[strong {{props.value + 1}}] #[span.text-grey / {{props.row.maxRetries + 1}}]
+ template(v-slot:body-cell-useworker='props')
+ q-td(:props='props')
+ template(v-if='props.value')
+ q-icon(name='las la-microchip', color='brown', size='sm')
+ small.q-ml-xs.text-brown Worker
+ template(v-else)
+ q-icon(name='las la-leaf', color='teal', size='sm')
+ small.q-ml-xs.text-teal In-Process
+ template(v-slot:body-cell-date='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div
+ i18n-t.text-grey(keypath='admin.scheduler.createdBy', tag='small')
+ template(#instance)
+ strong {{props.row.createdBy}}
+ template(v-slot:body-cell-cancel='props')
+ q-td(:props='props')
+ q-btn.acrylic-btn.q-px-sm(
+ flat
+ icon='las la-window-close'
+ color='negative'
+ @click='cancelJob(props.row.id)'
+ )
+ q-tooltip(anchor='center left', self='center right') {{ t('admin.scheduler.cancelJob') }}
+ template(v-else)
+ q-card.rounded-borders(
+ v-if='state.jobs.length < 1'
+ 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.scheduler.' + state.displayMode + 'None') }}
+ q-card(v-else)
+ q-table(
+ :rows='state.jobs'
+ :columns='jobsHeaders'
+ row-key='name'
+ flat
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ )
+ template(v-slot:body-cell-id='props')
+ q-td(:props='props')
+ q-avatar(
+ v-if='props.row.state === `completed`'
+ icon='las la-check'
+ color='positive'
+ text-color='white'
+ size='sm'
+ rounded
+ )
+ q-avatar(
+ v-else-if='props.row.state === `failed`'
+ icon='las la-times'
+ color='negative'
+ text-color='white'
+ size='sm'
+ rounded
+ )
+ q-avatar(
+ v-else-if='props.row.state === `interrupted`'
+ icon='las la-square-full'
+ color='orange'
+ text-color='white'
+ size='sm'
+ rounded
+ )
+ q-circular-progress(
+ v-else-if='props.row.state === `active`'
+ indeterminate
+ size='sm'
+ :thickness='0.4'
+ color='blue'
+ track-color='blue-1'
+ center-color='blue-2'
+ )
+ template(v-slot:body-cell-task='props')
+ q-td(:props='props')
+ strong {{props.value}}
+ div: small.text-grey {{props.row.id}}
+ template(v-slot:body-cell-state='props')
+ q-td(:props='props')
+ template(v-if='props.value === `completed`')
+ i18n-t(keypath='admin.scheduler.completedIn', tag='span')
+ template(#duration)
+ strong {{humanizeDuration(props.row.startedAt, props.row.completedAt)}}
+ div: small.text-grey {{ humanizeDate(props.row.completedAt) }}
+ template(v-else-if='props.value === `active`')
+ em.text-grey {{ t('admin.scheduler.pending') }}
+ template(v-else)
+ strong.text-negative {{ props.value === 'failed' ? t('admin.scheduler.error') : t('admin.scheduler.interrupted') }}
+ div: small {{ props.row.lastErrorMessage }}
+ template(v-slot:body-cell-attempt='props')
+ q-td(:props='props')
+ span #[strong {{props.value}}] #[span.text-grey / {{props.row.maxRetries + 1}}]
+ template(v-slot:body-cell-useworker='props')
+ q-td(:props='props')
+ template(v-if='props.value')
+ q-icon(name='las la-microchip', color='brown', size='sm')
+ small.q-ml-xs.text-brown Worker
+ template(v-else)
+ q-icon(name='las la-leaf', color='teal', size='sm')
+ small.q-ml-xs.text-teal In-Process
+ template(v-slot:body-cell-date='props')
+ q-td(:props='props')
+ span {{props.value}}
+ div: small.text-grey {{humanizeDate(props.row.startedAt)}}
+ div
+ i18n-t.text-grey(keypath='admin.scheduler.createdBy', tag='small')
+ template(#instance)
+ strong {{props.row.executedBy}}
+ template(v-slot:body-cell-actions='props')
+ q-td(:props='props')
+ q-btn.acrylic-btn.q-px-sm(
+ v-if='props.row.state !== `active`'
+ flat
+ icon='las la-undo-alt'
+ color='orange'
+ @click='retryJob(props.row.id)'
+ :disable='props.row.state === `interrupted` || props.row.state === `failed` && props.row.attempt < props.row.maxRetries'
+ )
+ q-tooltip(anchor='center left', self='center right') {{ t('admin.scheduler.retryJob') }}
+
+
+
+
diff --git a/frontend/src/pages/AdminSearch.vue b/frontend/src/pages/AdminSearch.vue
new file mode 100644
index 00000000..1cab1cad
--- /dev/null
+++ b/frontend/src/pages/AdminSearch.vue
@@ -0,0 +1,241 @@
+
+q-page.admin-flags
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-find-and-replace.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.search.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.search.subtitle') }}
+ .col-auto.flex
+ q-btn.q-mr-sm.acrylic-btn(
+ flat
+ icon='mdi-database-refresh'
+ :label='t(`admin.searchRebuildIndex`)'
+ color='purple'
+ @click='rebuild'
+ :loading='state.rebuildLoading'
+ )
+ q-separator.q-mr-sm(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/search`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-7
+ q-card.q-py-sm
+ q-item(tag='label')
+ blueprint-icon(icon='search')
+ q-item-section
+ q-item-label {{t(`admin.search.highlighting`)}}
+ q-item-label(caption) {{t(`admin.search.highlightingHint`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.config.termHighlighting'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.search.highlighting`)'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon.self-start(icon='search')
+ q-item-section
+ q-item-label {{t(`admin.search.dictOverrides`)}}
+ q-no-ssr(:placeholder='t(`common.loading`)')
+ util-code-editor.admin-theme-cm.q-my-sm(
+ v-model='state.config.dictOverrides'
+ language='json'
+ :min-height='250'
+ )
+ q-item-label(caption)
+ i18n-t(keypath='admin.search.dictOverridesHint' tag='span')
+ span { "en": "english" }
+
+ .col-12.col-lg-5.gt-md
+ .q-pa-md.text-center
+ img(src='/_assets/illustrations/undraw_file_searching.svg', style='width: 80%;')
+
+
+
+
+
diff --git a/frontend/src/pages/AdminSecurity.vue b/frontend/src/pages/AdminSecurity.vue
new file mode 100644
index 00000000..10d2c28a
--- /dev/null
+++ b/frontend/src/pages/AdminSecurity.vue
@@ -0,0 +1,524 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/security`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12.col-lg-6
+ //- -----------------------
+ //- Security
+ //- -----------------------
+ q-card.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='state.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='state.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='state.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='state.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='state.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='state.config.trustProxy'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.security.trustProxy`)'
+ )
+ //- -----------------------
+ //- HSTS
+ //- -----------------------
+ q-card.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='state.config.enforceHsts'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.security.enforceHsts`)'
+ )
+ template(v-if='state.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='state.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.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='state.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='state.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='state.config.uploadScanSVG'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.security.scanSVG`)'
+ )
+
+ //- -----------------------
+ //- CORS
+ //- -----------------------
+ q-card.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='state.config.corsMode'
+ :options='corsModes'
+ option-value='value'
+ option-label='text'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.security.corsMode`)'
+ )
+ template(v-if='state.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='state.config.corsConfig'
+ dense
+ type='textarea'
+ :aria-label='t(`admin.security.corsHostnames`)'
+ )
+ template(v-else-if='state.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='state.config.corsConfig'
+ dense
+ :aria-label='t(`admin.security.corsRegex`)'
+ )
+
+ //- -----------------------
+ //- JWT
+ //- -----------------------
+ q-card.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='state.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='state.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='state.config.authJwtRenewablePeriod'
+ dense
+ :aria-label='t(`admin.security.tokenRenewalPeriod`)'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminSites.vue b/frontend/src/pages/AdminSites.vue
new file mode 100644
index 00000000..7ecaf70d
--- /dev/null
+++ b/frontend/src/pages/AdminSites.vue
@@ -0,0 +1,189 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/sites`'
+ target='_blank'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='refresh'
+ )
+ q-tooltip {{ t(`common.actions.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
+ q-list(separator)
+ q-item(
+ v-for='site of adminStore.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='negative'
+ @click='deleteSite(site)'
+ :aria-label='t(`common.actions.delete`)'
+ )
+
+
+
diff --git a/frontend/src/pages/AdminSsl.vue b/frontend/src/pages/AdminSsl.vue
new file mode 100644
index 00000000..453f55b5
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminStats.vue b/frontend/src/pages/AdminStats.vue
new file mode 100644
index 00000000..cbfd09a3
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminStorage.vue b/frontend/src/pages/AdminStorage.vue
new file mode 100644
index 00000000..df5e0ea1
--- /dev/null
+++ b/frontend/src/pages/AdminStorage.vue
@@ -0,0 +1,1262 @@
+
+q-page.admin-storage
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-ssd-animated.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='state.loading > 0'
+ color='accent'
+ size='sm'
+ )
+ q-btn-toggle.q-mr-md(
+ v-model='state.displayMode'
+ push
+ no-caps
+ :toggle-color='$q.dark.isActive ? `white` : `black`'
+ :toggle-text-color='$q.dark.isActive ? `black` : `white`'
+ :text-color='$q.dark.isActive ? `white` : `black`'
+ :color='$q.dark.isActive ? `dark-1` : `white`'
+ :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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/storage`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+
+ //- ==========================================
+ //- TARGETS
+ //- ==========================================
+
+ .row.q-pa-md.q-col-gutter-md(v-if='state.displayMode === `targets`')
+ .col-auto
+ q-card.rounded-borders.bg-dark
+ q-list(
+ style='min-width: 300px;'
+ padding
+ dark
+ )
+ q-item(
+ v-for='tgt of state.targets'
+ :key='tgt.key'
+ active-class='bg-primary text-white'
+ :active='state.selectedTarget === tgt.id'
+ :to='`/_admin/` + adminStore.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)
+ status-light(:color='tgt.isEnabled ? `positive` : `negative`', :pulse='tgt.isEnabled')
+ .col(v-if='state.target')
+ .row.q-col-gutter-md
+ .col-12.col-lg
+ //- -----------------------
+ //- Setup
+ //- -----------------------
+ q-card.q-pb-sm.q-mb-md(v-if='state.target.setup && state.target.setup.handler && state.target.setup.state !== `configured`')
+ q-card-section
+ .text-subtitle1 {{t('admin.storage.setup')}}
+ .text-body2.text-grey {{ t('admin.storage.setupHint') }}
+ template(v-if='state.target.setup.handler === `github` && state.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='state.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='state.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='state.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='state.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='state.setupCfg.action'
+ )
+ input(
+ type='hidden'
+ name='manifest'
+ :value='state.setupCfg.manifest'
+ )
+ q-btn(
+ unelevated
+ icon='las la-angle-double-right'
+ :label='t(`admin.storage.startSetup`)'
+ color='secondary'
+ @click='setupGitHub'
+ :loading='state.setupCfg.loading'
+ )
+ template(v-else-if='state.target.setup.handler === `github` && state.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='state.setupCfg.loading'
+ )
+ q-card.q-pb-sm.q-mb-md(v-if='state.target.setup && state.target.setup.handler && state.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`)'
+ )
+
+ //- -----------------------
+ //- Content Types
+ //- -----------------------
+ q-card.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='state.target.contentTypes.activeTypes'
+ :color='state.target.module === `db` ? `grey` : `primary`'
+ val='pages'
+ :aria-label='t(`admin.storage.contentTypePages`)'
+ :disable='state.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='state.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='state.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='state.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='state.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='state.target.module === `db`', caption) {{t(`admin.storage.contentTypeLargeFilesDBWarn`)}}
+ q-item-section(side)
+ q-input(
+ outlined
+ :label='t(`admin.storage.contentTypeLargeFilesThreshold`)'
+ v-model='state.target.contentTypes.largeThreshold'
+ style='min-width: 150px;'
+ dense
+ )
+
+ //- -----------------------
+ //- Content Delivery
+ //- -----------------------
+ q-card.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='state.target.assetDelivery.isStreamingSupported ? `label` : null')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='state.target.assetDelivery.streaming'
+ :color='state.target.module === `db` || !state.target.assetDelivery.isStreamingSupported ? `grey` : `primary`'
+ :aria-label='t(`admin.storage.contentTypePages`)'
+ :disable='state.target.module === `db` || !state.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='!state.target.assetDelivery.isStreamingSupported', caption) {{t(`admin.storage.assetStreamingNotSupported`)}}
+ q-item(:tag='state.target.assetDelivery.isDirectAccessSupported ? `label` : null')
+ q-item-section(avatar)
+ q-checkbox(
+ v-model='state.target.assetDelivery.directAccess'
+ :color='!state.target.assetDelivery.isDirectAccessSupported ? `grey` : `primary`'
+ :aria-label='t(`admin.storage.contentTypePages`)'
+ :disable='!state.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='!state.target.assetDelivery.isDirectAccessSupported', caption) {{t(`admin.storage.assetDirectAccessNotSupported`)}}
+
+ //- -----------------------
+ //- Configuration
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.storage.config')}}
+ q-banner.q-mt-md(
+ v-if='!state.target.config || Object.keys(state.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 state.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.q-pb-sm.q-mt-md(v-if='state.target.sync && Object.keys(state.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.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.storage.actions')}}
+ q-banner.q-mt-md(
+ v-if='!state.target.actions || state.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='!state.target.isEnabled'
+ rounded
+ :class='$q.dark.isActive ? `bg-negative text-white` : `bg-grey-2 text-grey-7`'
+ ) {{t('admin.storage.actionsInactiveWarn')}}
+
+ template(
+ v-if='state.target.isEnabled'
+ v-for='(act, idx) in state.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-12.col-lg-auto
+ //- -----------------------
+ //- Infobox
+ //- -----------------------
+ q-card.rounded-borders.q-pb-md(style='width: 300px;')
+ q-card-section
+ .text-subtitle1 {{state.target.title}}
+ q-img.q-mt-sm.rounded-borders(
+ :src='state.target.banner'
+ fit='cover'
+ no-spinner
+ )
+ .text-body2.q-mt-md {{state.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 {{state.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='state.target.website', target='_blank', rel='noreferrer') {{state.target.website}}
+
+ //- -----------------------
+ //- Status
+ //- -----------------------
+ q-card.rounded-borders.q-pb-md.q-mt-md(style='width: 300px;')
+ q-card-section
+ .text-subtitle1 {{ t('admin.storage.status') }}
+ template(v-if='state.target.module !== `db`')
+ 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='state.target.module === `db`', caption) {{t(`admin.storage.enabledForced`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.target.isEnabled'
+ :disable='state.target.module === `db` || isSetupNeeded'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.storage.enabled`)'
+ )
+ q-inner-loading(:showing='isSetupNeeded')
+ q-icon(name='las la-exclamation-triangle', size='sm', color='negative')
+ .text-body2.text-negative {{ t('admin.storage.setupRequired') }}
+ 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: 300px;')
+ q-card-section
+ .text-subtitle1 {{t(`admin.storage.versioning`)}}
+ .text-body2.text-grey {{t(`admin.storage.versioningHint`)}}
+ q-item(:tag='state.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='!state.target.versioning.isSupported', caption) {{t(`admin.storage.versioningNotSupported`)}}
+ q-item-label.text-deep-orange(v-if='state.target.versioning.isForceEnabled', caption) {{t(`admin.storage.versioningForceEnabled`)}}
+ q-item-section(avatar)
+ q-toggle(
+ v-model='state.target.versioning.enabled'
+ :disable='!state.target.versioning.isSupported || state.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='state.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='state.deliveryConfig'
+ :nodes='state.deliveryNodes'
+ :edges='state.deliveryEdges'
+ :paths='state.deliveryPaths'
+ :layouts='state.deliveryLayouts'
+ style='height: 600px; background-color: #FFF;'
+ )
+ 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='state.deliveryNodes[nodeId].icon && state.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='state.deliveryNodes[nodeId].icon'
+ )
+ text(
+ v-if='state.deliveryNodes[nodeId].icon && state.deliveryNodes[nodeId].iconText'
+ :class='state.deliveryNodes[nodeId].icon'
+ :font-size='22 * scale'
+ fill='#ffffff'
+ text-anchor='middle'
+ dominant-baseline='central'
+ v-html='state.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/frontend/src/pages/AdminSystem.vue b/frontend/src/pages/AdminSystem.vue
new file mode 100644
index 00000000..84171bc1
--- /dev/null
+++ b/frontend/src/pages/AdminSystem.vue
@@ -0,0 +1,383 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn.acrylic-btn(
+ ref='copySysInfoBtn'
+ flat
+ icon='mdi-clipboard-text-outline'
+ label='Copy System Info'
+ color='primary'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-6
+ //- -----------------------
+ //- WIKI.JS
+ //- -----------------------
+ q-card.q-pb-sm
+ 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) {{ state.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
+ .row.q-col-gutter-sm
+ .col
+ .text-caption.dark-value {{ state.info.latestVersion }}
+ .col-auto
+ q-btn.acrylic-btn(
+ flat
+ color='purple'
+ @click='checkForUpdates'
+ :label='t(`admin.system.checkUpdate`)'
+ )
+
+ //- -----------------------
+ //- CLIENT
+ //- -----------------------
+ q-no-ssr
+ q-card.q-mt-md.q-pb-sm
+ 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
+ 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) {{ state.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) {{ state.info.dbHost }}
+
+ //- -----------------------
+ //- HOST INFORMATION
+ //- -----------------------
+ q-card.q-mt-md.q-pb-sm
+ 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) {{ (state.info.platform === 'docker') ? 'Docker Container (Linux)' : state.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) {{ state.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) {{ state.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) {{ state.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) {{ state.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) {{ state.info.configFile }}
+
+
+
+
+
diff --git a/frontend/src/pages/AdminTags.vue b/frontend/src/pages/AdminTags.vue
new file mode 100644
index 00000000..1142671d
--- /dev/null
+++ b/frontend/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/frontend/src/pages/AdminTerminal.vue b/frontend/src/pages/AdminTerminal.vue
new file mode 100644
index 00000000..e39228b3
--- /dev/null
+++ b/frontend/src/pages/AdminTerminal.vue
@@ -0,0 +1,171 @@
+
+q-page.admin-terminal
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-linux-terminal-animated.svg')
+ .col.q-pl-md
+ .text-h5.text-primary.animated.fadeInLeft {{ t('admin.terminal.title') }}
+ .text-subtitle1.text-grey.animated.fadeInLeft.wait-p2s {{ t('admin.terminal.subtitle') }}
+ .col-auto.flex
+ q-btn.acrylic-btn.q-mr-sm(
+ v-if='!state.connected || state.connecting'
+ flat
+ icon='las la-link'
+ :label='t(`admin.terminal.connect`)'
+ color='positive'
+ @click='connect'
+ :loading='state.connecting'
+ :disabled='state.connecting'
+ )
+ q-btn.acrylic-btn.q-mr-sm(
+ v-else
+ flat
+ icon='las la-unlink'
+ :label='t(`admin.terminal.disconnect`)'
+ color='negative'
+ @click='disconnect'
+ )
+ q-btn.acrylic-btn.q-mr-md(
+ flat
+ icon='las la-ban'
+ :label='t(`admin.terminal.clear`)'
+ color='primary'
+ @click='clearTerminal'
+ )
+ q-separator.q-mr-md(vertical)
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/terminal`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card
+ .admin-terminal-term(ref='termDiv')
+
+
+
+
+
+
diff --git a/frontend/src/pages/AdminTheme.vue b/frontend/src/pages/AdminTheme.vue
new file mode 100644
index 00000000..77fec4db
--- /dev/null
+++ b/frontend/src/pages/AdminTheme.vue
@@ -0,0 +1,812 @@
+
+q-page.admin-theme
+ .row.q-pa-md.items-center
+ .col-auto
+ img.admin-icon.animated.fadeInLeft(src='/_assets/icons/fluent-paint-roller-animated.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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/theme`'
+ target='_blank'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn(
+ unelevated
+ icon='mdi-check'
+ :label='t(`common.actions.apply`)'
+ color='secondary'
+ @click='save'
+ :loading='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-6
+ //- -----------------------
+ //- Theme Options
+ //- -----------------------
+ q-card.q-pb-sm
+ q-card-section.flex.items-center
+ .text-subtitle1 {{t('admin.theme.appearance')}}
+ 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')
+ 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='state.config.dark'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.theme.darkMode`)'
+ )
+ template(v-for='cl 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 {{state.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: ` + state.config[`color` + startCase(cl)] + `;`'
+ text-color='white'
+ )
+ q-icon(name='las la-fill', size='xs', left)
+ span Pick...
+ q-menu
+ q-color(
+ v-model='state.config[`color` + startCase(cl)]'
+ )
+
+ //- -----------------------
+ //- Code Blocks
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section.flex.items-center
+ .text-subtitle1 {{t('admin.theme.codeBlocks')}}
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-redo-alt'
+ :label='t(`admin.theme.resetDefaults`)'
+ flat
+ size='sm'
+ color='pink'
+ @click='resetCodeBlocks'
+ )
+ q-item
+ blueprint-icon(icon='code')
+ q-item-section
+ q-item-label {{t(`admin.theme.codeBlocksAppearance`)}}
+ q-item-label(caption) {{t(`admin.theme.codeBlocksAppearanceHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='state.config.codeBlocksTheme'
+ :options='codeThemes'
+ emit-value
+ map-options
+ :virtual-scroll-slice-size='100'
+ :virtual-scroll-slice-ratio-before='2'
+ :virtual-scroll-slice-ratio-after='2'
+ dense
+ options-dense
+ :aria-label='t(`admin.theme.codeBlocksAppearance`)'
+ )
+
+ //- -----------------------
+ //- Theme Layout
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ q-card-section
+ .text-subtitle1 {{t('admin.theme.layout')}}
+ template(v-if='flagStore.experimental')
+ q-item
+ blueprint-icon(icon='width')
+ q-item-section
+ q-item-label {{t(`admin.theme.contentWidth`)}}
+ q-item-label(caption) {{t(`admin.theme.contentWidthHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.config.contentWidth'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='widthOptions'
+ )
+ q-separator.q-my-sm(inset)
+ 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='state.config.sidebarPosition'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='rightLeftOptions'
+ )
+ 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='state.config.tocPosition'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='rightLeftOptions'
+ )
+ q-separator.q-my-sm(inset)
+ q-item(tag='label')
+ 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='state.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')
+ 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='state.config.showPrintBtn'
+ color='primary'
+ checked-icon='las la-check'
+ unchecked-icon='las la-times'
+ :aria-label='t(`admin.theme.showPrintBtn`)'
+ )
+
+ .col-6
+ //- -----------------------
+ //- Fonts
+ //- -----------------------
+ q-card.q-pb-sm
+ q-card-section.flex.items-center
+ .text-subtitle1 {{t('admin.theme.fonts')}}
+ q-space
+ q-btn.acrylic-btn(
+ icon='las la-redo-alt'
+ :label='t(`admin.theme.resetDefaults`)'
+ flat
+ size='sm'
+ color='pink'
+ @click='resetFonts'
+ )
+ q-item
+ blueprint-icon(icon='fonts-app')
+ q-item-section
+ q-item-label {{t(`admin.theme.baseFont`)}}
+ q-item-label(caption) {{t(`admin.theme.baseFontHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='state.config.baseFont'
+ :options='fonts'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.theme.baseFont`)'
+ )
+ q-item
+ blueprint-icon(icon='fonts-app')
+ q-item-section
+ q-item-label {{t(`admin.theme.contentFont`)}}
+ q-item-label(caption) {{t(`admin.theme.contentFontHint`)}}
+ q-item-section
+ q-select(
+ outlined
+ v-model='state.config.contentFont'
+ :options='fonts'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.theme.contentFont`)'
+ )
+
+ //- -----------------------
+ //- Code Injection
+ //- -----------------------
+ q-card.q-pb-sm.q-mt-md
+ 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='state.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='state.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='state.config.injectBody'
+ language='html'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminUsers.vue b/frontend/src/pages/AdminUsers.vue
new file mode 100644
index 00000000..59fc3eb9
--- /dev/null
+++ b/frontend/src/pages/AdminUsers.vue
@@ -0,0 +1,334 @@
+
+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='state.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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/groups`'
+ target='_blank'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.q-mr-sm.acrylic-btn(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ :loading='state.loading > 0'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ q-btn.q-mr-sm(
+ icon='las la-user-cog'
+ unelevated
+ color='secondary'
+ :aria-label='t(`admin.users.defaults`)'
+ )
+ q-tooltip {{ t(`admin.users.defaults`) }}
+ user-defaults-menu
+ q-btn(
+ unelevated
+ icon='las la-plus'
+ :label='t(`admin.users.create`)'
+ color='primary'
+ @click='createUser'
+ :disabled='state.loading > 0'
+ )
+ q-separator(inset)
+ .row.q-pa-md.q-col-gutter-md
+ .col-12
+ q-card
+ q-table(
+ :rows='state.users'
+ :columns='headers'
+ row-key='id'
+ flat
+ hide-header
+ hide-bottom
+ :rows-per-page-options='[0]'
+ :loading='state.loading > 0'
+ )
+ template(#body-cell-id='props')
+ q-td(:props='props')
+ q-icon(name='las la-user', color='primary', size='sm')
+ template(#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(#body-cell-email='props')
+ q-td(:props='props')
+ em {{ props.value }}
+ template(#body-cell-date='props')
+ q-td(:props='props')
+ i18n-t.text-caption(keypath='admin.users.createdAt', tag='div')
+ template(#date)
+ strong {{ formattedDate(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(#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='negative'
+ @click='deleteUser(props.row)'
+ )
+ .flex.flex-center.q-mt-lg(v-if='state.totalPages > 1')
+ q-pagination(
+ v-model='state.currentPage'
+ :max='state.totalPages'
+ :max-pages='9'
+ boundary-numbers
+ direction-links
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminUtilities.vue b/frontend/src/pages/AdminUtilities.vue
new file mode 100644
index 00000000..b7e05e4b
--- /dev/null
+++ b/frontend/src/pages/AdminUtilities.vue
@@ -0,0 +1,205 @@
+
+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'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/admin/utilities`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-separator(inset)
+ .q-pa-md.q-gutter-md
+ q-card
+ q-list(separator)
+ q-item
+ blueprint-icon(icon='disconnected', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.disconnectWS`)}}
+ q-item-label(caption) {{t(`admin.utilities.disconnectWSHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ @click='disconnectWS'
+ :label='t(`common.actions.proceed`)'
+ )
+ q-item
+ blueprint-icon(icon='database-export', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.export`)}}
+ q-item-label(caption) {{t(`admin.utilities.exportHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ :label='t(`common.actions.proceed`)'
+ )
+ q-item
+ blueprint-icon(icon='datalake', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.flushCache`)}}
+ q-item-label(caption) {{t(`admin.utilities.flushCacheHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ :label='t(`common.actions.proceed`)'
+ )
+ q-item
+ blueprint-icon(icon='database-restore', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.import`)}}
+ q-item-label(caption) {{t(`admin.utilities.importHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ :label='t(`common.actions.proceed`)'
+ )
+ 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'
+ :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='state.purgeHistoryTimeframe'
+ style='min-width: 175px;'
+ emit-value
+ map-options
+ dense
+ :options='purgeHistoryTimeframes'
+ )
+ q-separator.q-ml-sm(vertical)
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ :label='t(`common.actions.proceed`)'
+ )
+ q-item
+ blueprint-icon(icon='rescan-document', :hue-rotate='45')
+ q-item-section
+ q-item-label {{t(`admin.utilities.scanPageProblems`)}}
+ q-item-label(caption) {{t(`admin.utilities.scanPageProblemsHint`)}}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-arrow-circle-right'
+ color='primary'
+ :label='t(`common.actions.proceed`)'
+ )
+
+
+
+
+
diff --git a/frontend/src/pages/AdminWebhooks.vue b/frontend/src/pages/AdminWebhooks.vue
new file mode 100644
index 00000000..a794f93a
--- /dev/null
+++ b/frontend/src/pages/AdminWebhooks.vue
@@ -0,0 +1,202 @@
+
+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-btn.q-mr-sm.acrylic-btn(
+ icon='las la-question-circle'
+ flat
+ color='grey'
+ :aria-label='t(`common.actions.viewDocs`)'
+ :href='siteStore.docsBase + `/system/webhooks`'
+ target='_blank'
+ type='a'
+ )
+ q-tooltip {{ t(`common.actions.viewDocs`) }}
+ q-btn.acrylic-btn.q-mr-sm(
+ icon='las la-redo-alt'
+ flat
+ color='secondary'
+ :loading='state.loading > 0'
+ :aria-label='t(`common.actions.refresh`)'
+ @click='load'
+ )
+ q-tooltip {{ t(`common.actions.refresh`) }}
+ 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='state.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 state.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/frontend/src/pages/ErrorGeneric.vue b/frontend/src/pages/ErrorGeneric.vue
new file mode 100644
index 00000000..b09b206e
--- /dev/null
+++ b/frontend/src/pages/ErrorGeneric.vue
@@ -0,0 +1,144 @@
+
+.errorpage
+ .errorpage-bg
+ .errorpage-content
+ .errorpage-code {{error.code}}
+ .errorpage-title {{error.title}}
+ .errorpage-hint {{error.hint}}
+ .errorpage-actions
+ q-btn(
+ v-if='error.showHomeBtn'
+ push
+ color='primary'
+ label='Go to home'
+ icon='las la-home'
+ to='/'
+ )
+ q-btn.q-ml-md(
+ v-if='error.showLoginBtn'
+ push
+ color='primary'
+ label='Login As...'
+ icon='las la-sign-in-alt'
+ to='/login'
+ )
+
+
+
+
+
+
diff --git a/frontend/src/pages/Index.vue b/frontend/src/pages/Index.vue
new file mode 100644
index 00000000..81885f4a
--- /dev/null
+++ b/frontend/src/pages/Index.vue
@@ -0,0 +1,499 @@
+
+q-page.column
+ .page-breadcrumbs.q-py-sm.q-px-md.row(
+ v-if='!editorStore.isActive'
+ )
+ .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 pageStore.breadcrumbs'
+ :key='brd.id'
+ :icon='brd.icon'
+ :label='brd.title'
+ :aria-label='brd.title'
+ :to='brd.path'
+ )
+ .col-auto.flex.items-center.justify-end
+ template(v-if='!pageStore.publishState === `draft`')
+ .text-caption.text-accent: strong Unpublished
+ q-separator.q-mx-sm(vertical)
+ .text-caption.text-grey-6 Last modified on #[strong {{lastModified}}]
+ page-header
+ .page-container.row.no-wrap.items-stretch(style='flex: 1 1 100%;')
+ .col(
+ :style='siteStore.theme.tocPosition === `left` ? `order: 2;` : `order: 1;`'
+ )
+ q-no-ssr(
+ v-if='editorStore.isActive'
+ )
+ component(:is='editorComponents[editorStore.editor]')
+ q-scroll-area.page-container-scrl(
+ v-else
+ :thumb-style='thumbStyle'
+ :bar-style='barStyle'
+ style='height: 100%;'
+ )
+ .q-pa-md
+ .page-contents(ref='pageContents', v-html='pageStore.render')
+ template(v-if='pageStore.relations && pageStore.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='siteStore.theme.tocPosition === `left` ? `order: 1;` : `order: 2;`'
+ )
+ template(v-if='pageStore.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.page-toc(
+ :nodes='pageStore.toc'
+ icon='las la-caret-right'
+ node-key='key'
+ dense
+ v-model:expanded='state.tocExpanded'
+ v-model:selected='state.tocSelected'
+ )
+ //- Tags
+ template(v-if='pageStore.showTags')
+ q-separator(v-if='pageStore.showToc')
+ .q-pa-md(
+ @mouseover='state.showTagsEditBtn = true'
+ @mouseleave='state.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='state.showTagsEditBtn'
+ size='sm'
+ padding='none xs'
+ icon='las la-pen'
+ color='deep-orange-9'
+ flat
+ label='Edit'
+ no-caps
+ @click='state.tagEditMode = !state.tagEditMode'
+ )
+ page-tags.q-mt-sm(:edit='state.tagEditMode')
+ template(v-if='siteStore.features.ratingsMode !== `off` && pageStore.allowRatings')
+ q-separator(v-if='pageStore.showToc || pageStore.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='siteStore.features.ratingsMode === `stars`'
+ v-model='state.currentRating'
+ icon='las la-star'
+ color='secondary'
+ size='sm'
+ )
+ .flex.items-center(v-else-if='siteStore.features.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-col
+
+ side-dialog
+
+
+
+
+
diff --git a/frontend/src/pages/Login.vue b/frontend/src/pages/Login.vue
new file mode 100644
index 00000000..02fd158f
--- /dev/null
+++ b/frontend/src/pages/Login.vue
@@ -0,0 +1,486 @@
+
+.auth
+ .auth-content
+ .auth-logo
+ img(:src='`/_site/logo`' :alt='siteStore.title')
+ h2.auth-site-title(v-if='siteStore.logoText') {{ siteStore.title }}
+ p.text-grey-7 Login to continue
+ auth-login-panel
+ .auth-bg(aria-hidden="true")
+ img(:src='`/_site/loginbg`' alt='')
+
+
+
+
+
diff --git a/frontend/src/pages/ProfileAuth.vue b/frontend/src/pages/ProfileAuth.vue
new file mode 100644
index 00000000..6c040333
--- /dev/null
+++ b/frontend/src/pages/ProfileAuth.vue
@@ -0,0 +1,418 @@
+
+q-page.q-py-md(:style-fn='pageStyle')
+ .text-header {{t('profile.auth')}}
+ .q-pa-md
+ .text-body2 {{ t('profile.authInfo') }}
+ q-list.q-mt-lg(
+ bordered
+ separator
+ )
+ q-item(
+ v-for='auth of state.authMethods'
+ :key='auth.id'
+ )
+ q-item-section(avatar)
+ q-avatar(
+ color='dark-5'
+ text-color='white'
+ rounded
+ )
+ q-icon(:name='`img:` + auth.strategyIcon')
+ q-item-section
+ strong {{auth.authName}}
+ template(v-if='auth.strategyKey === `local`')
+ q-item-section(v-if='auth.config.isTfaSetup', side)
+ q-btn(
+ icon='las la-fingerprint'
+ unelevated
+ :label='t(`profile.authDisableTfa`)'
+ color='negative'
+ @click='disableTfa(auth.authId)'
+ :disable='auth.config.isTfaRequired'
+ )
+ q-item-section(v-else, side)
+ q-btn(
+ icon='las la-fingerprint'
+ unelevated
+ :label='t(`profile.authSetTfa`)'
+ color='primary'
+ @click='setupTfa(auth.authId)'
+ )
+ q-item-section(side)
+ q-btn(
+ icon='las la-key'
+ unelevated
+ :label='t(`profile.authChangePassword`)'
+ color='primary'
+ @click='changePassword(auth.authId)'
+ )
+
+ .text-header.q-mt-md {{t('profile.passkeys')}}
+ .q-pa-md
+ .text-body2 {{ t('profile.passkeysIntro') }}
+ q-list.q-mt-lg(
+ v-if="state.passkeys?.length > 0"
+ bordered
+ separator
+ )
+ q-item(
+ v-for='pkey of state.passkeys'
+ :key='pkey.id'
+ )
+ q-item-section(avatar)
+ q-avatar(
+ color='secondary'
+ text-color='white'
+ rounded
+ )
+ q-icon(name='las la-key')
+ q-item-section
+ strong {{pkey.name}}
+ .text-caption {{ pkey.siteHostname }}
+ .text-caption.text-grey-7 {{ humanizeDate(pkey.createdAt) }}
+ q-item-section(side)
+ q-btn.acrylic-btn(
+ flat
+ icon='las la-trash'
+ :aria-label='t(`common.actions.delete`)'
+ color='negative'
+ @click='deactivatePasskey(pkey)'
+ )
+ .q-mt-md
+ q-btn(
+ icon='las la-plus'
+ unelevated
+ :label='t(`profile.passkeysAdd`)'
+ color='primary'
+ @click='setupPasskey'
+ )
+
+ q-inner-loading(:showing='state.loading > 0')
+
+
+
diff --git a/frontend/src/pages/ProfileAvatar.vue b/frontend/src/pages/ProfileAvatar.vue
new file mode 100644
index 00000000..6d2aea7c
--- /dev/null
+++ b/frontend/src/pages/ProfileAvatar.vue
@@ -0,0 +1,203 @@
+
+q-page.q-py-md(:style-fn='pageStyle')
+ .text-header {{t('profile.avatar')}}
+ .row.q-gutter-lg.q-mt-xl
+ .col.text-center
+ q-avatar.profile-avatar-circ(
+ size='180px'
+ :color='userStore.hasAvatar ? `dark-1` : `primary`'
+ text-color='white'
+ :class='userStore.hasAvatar ? `is-image` : ``'
+ )
+ img(
+ v-if='userStore.hasAvatar',
+ :src='`/_user/` + userStore.id + `/avatar?` + state.assetTimestamp'
+ )
+ q-icon(
+ v-else,
+ name='las la-user'
+ )
+ .col.self-center(v-if='canEdit')
+ .text-body1 {{ t('profile.avatarUploadTitle') }}
+ .text-caption {{ t('profile.avatarUploadHint') }}
+ .q-mt-md
+ q-btn(
+ icon='las la-upload'
+ unelevated
+ :label='t(`profile.uploadNewAvatar`)'
+ color='primary'
+ @click='uploadImage'
+ )
+ .q-mt-md
+ q-btn.q-mr-sm(
+ icon='las la-times'
+ outline
+ :label='t(`common.actions.clear`)'
+ color='primary'
+ @click='clearImage'
+ :disable='!userStore.hasAvatar'
+ )
+ .col.self-center(v-else)
+ .text-caption.text-negative {{ t('profile.avatarUploadDisabled') }}
+
+ q-inner-loading(:showing='state.loading > 0')
+
+
+
+
+
diff --git a/frontend/src/pages/ProfileGroups.vue b/frontend/src/pages/ProfileGroups.vue
new file mode 100644
index 00000000..bf195b85
--- /dev/null
+++ b/frontend/src/pages/ProfileGroups.vue
@@ -0,0 +1,114 @@
+
+q-page.q-py-md(:style-fn='pageStyle')
+ .text-header {{t('profile.groups')}}
+ .q-pa-md
+ .text-body2 {{ t('profile.groupsInfo') }}
+ q-list.q-mt-lg(
+ bordered
+ separator
+ )
+ q-item(
+ v-if='state.groups.length === 0 && state.loading < 1'
+ )
+ q-item-section
+ span.text-negative {{ t('profile.groupsNone') }}
+ q-item(
+ v-for='grp of state.groups'
+ :key='grp.id'
+ )
+ q-item-section(avatar)
+ q-avatar(
+ color='secondary'
+ text-color='white'
+ icon='las la-users'
+ rounded
+ )
+ q-item-section
+ strong {{grp.name}}
+
+ q-inner-loading(:showing='state.loading > 0')
+
+
+
diff --git a/frontend/src/pages/ProfileInfo.vue b/frontend/src/pages/ProfileInfo.vue
new file mode 100644
index 00000000..980e9c63
--- /dev/null
+++ b/frontend/src/pages/ProfileInfo.vue
@@ -0,0 +1,327 @@
+
+q-page.q-py-md(:style-fn='pageStyle')
+ .text-header {{t('profile.myInfo')}}
+ q-item(v-if='!canEdit')
+ 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-ban', size='sm')
+ q-card-section
+ span {{ t('profile.editDisabledTitle') }}
+ .text-caption.text-red-1 {{ t('profile.editDisabledDescription') }}
+ 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='state.config.name'
+ dense
+ hide-bottom-space
+ :aria-label='t(`profile.displayName`)'
+ :readonly='!canEdit'
+ )
+ 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='state.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='state.config.location'
+ dense
+ hide-bottom-space
+ :aria-label='t(`profile.location`)'
+ :readonly='!canEdit'
+ )
+ 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='state.config.jobTitle'
+ dense
+ hide-bottom-space
+ :aria-label='t(`profile.jobTitle`)'
+ :readonly='!canEdit'
+ )
+ 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='state.config.pronouns'
+ dense
+ hide-bottom-space
+ :aria-label='t(`profile.pronouns`)'
+ :readonly='!canEdit'
+ )
+ .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='state.config.timezone'
+ :options='timezones'
+ :virtual-scroll-slice-size='100'
+ :virtual-scroll-slice-ratio-before='2'
+ :virtual-scroll-slice-ratio-after='2'
+ 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='state.config.dateFormat'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`admin.general.defaultDateFormat`)'
+ :options='dateFormats'
+ )
+ 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='state.config.timeFormat'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='timeFormats'
+ )
+ q-separator.q-my-sm(inset)
+ q-item
+ blueprint-icon(icon='light-on')
+ q-item-section
+ q-item-label {{t(`profile.appearance`)}}
+ q-item-label(caption) {{t(`profile.appearanceHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.config.appearance'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='appearances'
+ )
+ .text-header.q-mt-lg {{t('profile.accessibility')}}
+ q-item
+ blueprint-icon(icon='visualy-impaired')
+ q-item-section
+ q-item-label {{t(`profile.cvd`)}}
+ q-item-label(caption) {{t(`profile.cvdHint`)}}
+ q-item-section.col-auto
+ q-btn-toggle(
+ v-model='state.config.cvd'
+ push
+ glossy
+ no-caps
+ toggle-color='primary'
+ :options='cvdChoices'
+ )
+ .actions-bar.q-mt-lg
+ q-btn(
+ icon='las la-check'
+ unelevated
+ :label='t(`common.actions.saveChanges`)'
+ color='secondary'
+ @click='save'
+ )
+
+
+
diff --git a/frontend/src/pages/Search.vue b/frontend/src/pages/Search.vue
new file mode 100644
index 00000000..121c76a6
--- /dev/null
+++ b/frontend/src/pages/Search.vue
@@ -0,0 +1,578 @@
+
+q-layout(view='hHh Lpr lff')
+ header-nav
+ q-page-container.layout-search
+ .layout-search-card
+ q-btn.layout-search-back(
+ icon='las la-arrow-circle-left'
+ color='white'
+ flat
+ round
+ @click='goBack'
+ )
+ q-tooltip(anchor='center left', self='center right') {{ t('common.actions.goback') }}
+ .layout-search-sd
+ .text-header {{ t('search.sortBy') }}
+ q-list(dense, padding)
+ q-item(
+ v-for='item of orderByOptions'
+ clickable
+ :active='item.value === state.params.orderBy'
+ @click='setOrderBy(item.value)'
+ )
+ q-item-section(side)
+ q-icon(:name='item.icon', :color='item.value === state.params.orderBy ? `primary` : ``')
+ q-item-section
+ q-item-label {{ item.label }}
+ q-item-section(
+ v-if='item.value === state.params.orderBy'
+ side
+ )
+ q-icon(
+ :name='state.params.orderByDirection === `desc` ? `mdi-transfer-down` : `mdi-transfer-up`'
+ size='sm'
+ color='primary'
+ )
+ .text-header {{ t('search.filters') }}
+ .q-pa-sm
+ q-input(
+ outlined
+ dense
+ :placeholder='t(`search.filterPath`)'
+ prefix='/'
+ v-model='state.params.filterPath'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-caret-square-right', size='xs')
+ q-select.q-mt-sm(
+ outlined
+ v-model='state.selectedTags'
+ :options='state.filteredTags'
+ dense
+ options-dense
+ use-input
+ use-chips
+ multiple
+ hide-dropdown-icon
+ :input-debounce='0'
+ @update:model-value='v => syncTags(v)'
+ @filter='filterTags'
+ :placeholder='state.selectedTags.length < 1 ? t(`search.filterTags`) : ``'
+ :loading='state.loading > 0'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-hashtag', size='xs')
+ template(v-slot:option='scope')
+ q-item(v-bind='scope.itemProps')
+ q-item-section(side)
+ q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)', size='sm')
+ q-item-section
+ q-item-label
+ span(v-html='scope.opt')
+ //- q-input.q-mt-sm(
+ //- outlined
+ //- dense
+ //- placeholder='Last updated...'
+ //- )
+ //- template(v-slot:prepend)
+ //- q-icon(name='las la-calendar', size='xs')
+ //- q-input.q-mt-sm(
+ //- outlined
+ //- dense
+ //- placeholder='Last edited by...'
+ //- )
+ //- template(v-slot:prepend)
+ //- q-icon(name='las la-user-edit', size='xs')
+ q-select.q-mt-sm(
+ outlined
+ v-model='state.params.filterLocale'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`search.filterLocale`)'
+ :options='siteStore.locales.active'
+ option-value='code'
+ option-label='name'
+ options-dense
+ multiple
+ :display-value='t(`search.filterLocaleDisplay`, { n: state.params.filterLocale.length > 0 ? state.params.filterLocale[0].toUpperCase() : state.params.filterLocale.length }, state.params.filterLocale.length)'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-language', size='xs')
+ template(v-slot:option='scope')
+ q-item(v-bind='scope.itemProps')
+ q-item-section(side)
+ q-checkbox(:model-value='scope.selected', @update:model-value='scope.toggleOption(scope.opt)')
+ q-item-section
+ q-item-label
+ span(v-html='scope.opt.name')
+ q-select.q-mt-sm(
+ outlined
+ v-model='state.params.filterEditor'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`search.filterEditor`)'
+ :options='editors'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-pen-nib', size='xs')
+ q-select.q-mt-sm(
+ outlined
+ v-model='state.params.filterPublishState'
+ emit-value
+ map-options
+ dense
+ :aria-label='t(`search.filterPublishState`)'
+ :options='publishStates'
+ )
+ template(v-slot:prepend)
+ q-icon(name='las la-traffic-light', size='xs')
+ q-page(:style-fn='pageStyle')
+ .text-header.flex
+ span {{t('search.results')}}
+ q-space
+ transition(name='slide-up', mode='out-in')
+ i18n-t(
+ v-if='!siteStore.searchIsLoading'
+ keypath='search.totalResults'
+ tag='span'
+ class='text-caption'
+ :plural='state.total'
+ )
+ strong {{ state.total }}
+ .q-pa-lg(v-if='state.results.length < 1')
+ i18n-t(keypath='search.noResults', tag='span', v-if='siteStore.search && siteStore.searchLastQuery')
+ strong {{ siteStore.searchLastQuery }}
+ span(v-else): em {{ t('search.emptyQuery') }}
+ q-list(separator)
+ q-item(
+ v-for='item of state.results'
+ clickable
+ :to='`/` + item.path'
+ )
+ q-item-section(avatar)
+ q-avatar(color='primary' text-color='white' rounded :icon='item.icon')
+ q-item-section
+ q-item-label {{ item.title }}
+ q-item-label(v-if='item.description', caption) {{ item.description }}
+ q-item-label.text-highlight(v-if='item.highlight', caption)
+ span(v-html='item.highlight')
+ q-item-section(side)
+ .flex.layout-search-itemtags
+ q-chip(
+ v-for='tag of item.tags'
+ square
+ color='secondary'
+ text-color='white'
+ icon='las la-hashtag'
+ size='sm'
+ ) {{ tag }}
+ .flex
+ .text-caption.q-mr-sm.text-grey /{{ item.path }}
+ .text-caption {{ humanizeDate(item.updatedAt) }}
+
+ q-inner-loading(:showing='state.loading > 0')
+ main-overlay-dialog
+ footer-nav
+
+
+
+
+
diff --git a/frontend/src/renderers/markdown.js b/frontend/src/renderers/markdown.js
new file mode 100644
index 00000000..4559e89f
--- /dev/null
+++ b/frontend/src/renderers/markdown.js
@@ -0,0 +1,186 @@
+import MarkdownIt from 'markdown-it'
+import mdAttrs from 'markdown-it-attrs'
+import mdDecorate from 'markdown-it-decorate'
+import { full as mdEmoji } from 'markdown-it-emoji'
+import mdTaskLists from 'markdown-it-task-lists'
+import mdExpandTabs from 'markdown-it-expand-tabs'
+import mdAbbr from 'markdown-it-abbr'
+import mdSup from 'markdown-it-sup'
+import mdSub from 'markdown-it-sub'
+import mdMark from 'markdown-it-mark'
+import mdMultiTable from 'markdown-it-multimd-table'
+import mdFootnote from 'markdown-it-footnote'
+import mdMdc from 'markdown-it-mdc'
+import katex from 'katex'
+import mdUnderline from './modules/markdown-it-underline'
+import mdImsize from './modules/markdown-it-imsize'
+import 'katex/dist/contrib/mhchem'
+import twemoji from 'twemoji'
+import plantuml from './modules/plantuml'
+import kroki from './modules/kroki.mjs'
+import katexHelper from './modules/katex'
+
+import hljs from 'highlight.js'
+
+import { escape, findLast, times } from 'lodash-es'
+
+const quoteStyles = {
+ chinese: '””‘’',
+ english: '“”‘’',
+ french: ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'],
+ german: '„“‚‘',
+ greek: '«»‘’',
+ japanese: '「」「」',
+ hungarian: '„”’’',
+ polish: '„”‚‘',
+ portuguese: '«»‘’',
+ russian: '«»„“',
+ spanish: '«»‘’',
+ swedish: '””’’'
+}
+
+export class MarkdownRenderer {
+ constructor (config = {}) {
+ this.md = new MarkdownIt({
+ html: config.allowHTML,
+ breaks: config.lineBreaks,
+ linkify: config.linkify,
+ typography: config.typographer,
+ quotes: quoteStyles[config.quotes] ?? quoteStyles.english,
+ highlight (str, lang) {
+ if (lang === 'diagram') {
+ return `${Buffer.from(str, 'base64').toString()}`
+ } else if (['mermaid', 'plantuml'].includes(lang)) {
+ return `${escape(str)}
`
+ } else {
+ const highlighted = lang ? hljs.highlight(str, { language: lang, ignoreIllegals: true }) : { value: str }
+ const lineCount = highlighted.value.match(/\n/g).length
+ const lineNums = lineCount > 1 ? `${times(lineCount, n => '').join('')}` : ''
+ return `${highlighted.value}${lineNums}
`
+ }
+ }
+ })
+ .use(mdMdc)
+ .use(mdAttrs, {
+ allowedAttributes: ['id', 'class', 'target']
+ })
+ .use(mdDecorate)
+ .use(mdEmoji)
+ .use(mdTaskLists, { label: false, labelAfter: false })
+ .use(mdExpandTabs, { tabWidth: config.tabWidth })
+ .use(mdAbbr)
+ .use(mdSup)
+ .use(mdSub)
+ .use(mdMark)
+ .use(mdFootnote)
+ .use(mdImsize)
+
+ if (config.underline) {
+ this.md.use(mdUnderline)
+ }
+
+ if (config.mdmultiTable) {
+ this.md.use(mdMultiTable, { multiline: true, rowspan: true, headerless: true })
+ }
+
+ // --------------------------------
+ // PLANTUML
+ // --------------------------------
+
+ if (config.plantuml) {
+ plantuml.init(this.md, { server: config.plantumlServerUrl })
+ }
+
+ // --------------------------------
+ // KROKI
+ // --------------------------------
+
+ if (config.kroki) {
+ kroki.init(this.md, { server: config.krokiServerUrl })
+ }
+
+ // --------------------------------
+ // KATEX
+ // --------------------------------
+
+ const macros = {}
+
+ // TODO: Add mhchem (needs esm conversion)
+ // Add \ce, \pu, and \tripledash to the KaTeX macros.
+ // katex.__defineMacro('\\ce', function (context) {
+ // return chemParse(context.consumeArgs(1)[0], 'ce')
+ // })
+ // katex.__defineMacro('\\pu', function (context) {
+ // return chemParse(context.consumeArgs(1)[0], 'pu')
+ // })
+
+ // Needed for \bond for the ~ forms
+ // Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not
+ // a mathematical minus, U+2212. So we need that extra 0.56.
+ katex.__defineMacro('\\tripledash', '{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu' + '\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}')
+ this.md.inline.ruler.after('escape', 'katex_inline', katexHelper.katexInline)
+ this.md.renderer.rules.katex_inline = (tokens, idx) => {
+ try {
+ return katex.renderToString(tokens[idx].content, {
+ displayMode: false, macros
+ })
+ } catch (err) {
+ console.warn(err)
+ return tokens[idx].content
+ }
+ }
+ this.md.block.ruler.after('blockquote', 'katex_block', katexHelper.katexBlock, {
+ alt: ['paragraph', 'reference', 'blockquote', 'list']
+ })
+ this.md.renderer.rules.katex_block = (tokens, idx) => {
+ try {
+ return '' + katex.renderToString(tokens[idx].content, {
+ displayMode: true, macros
+ }) + '
'
+ } catch (err) {
+ console.warn(err)
+ return tokens[idx].content
+ }
+ }
+
+ // --------------------------------
+ // TWEMOJI
+ // --------------------------------
+
+ this.md.renderer.rules.emoji = (token, idx) => {
+ return twemoji.parse(token[idx].content, {
+ callback (icon, opts) {
+ return `/_assets/svg/twemoji/${icon}.svg`
+ }
+ })
+ }
+
+ // --------------------------------
+ // Inject line numbers for preview scroll sync
+ // --------------------------------
+
+ this.linesMap = []
+ const injectLineNumbers = (tokens, idx, options, env, slf) => {
+ let line
+ if (tokens[idx].map && tokens[idx].level === 0) {
+ line = tokens[idx].map[0] + 1
+ tokens[idx].attrJoin('class', 'line')
+ tokens[idx].attrSet('data-line', String(line))
+ this.linesMap.push(line)
+ }
+ return slf.renderToken(tokens, idx, options, env, slf)
+ }
+ this.md.renderer.rules.paragraph_open = injectLineNumbers
+ this.md.renderer.rules.heading_open = injectLineNumbers
+ this.md.renderer.rules.blockquote_open = injectLineNumbers
+ }
+
+ render (src) {
+ this.linesMap = []
+ return this.md.render(src)
+ }
+
+ getClosestPreviewLine (line) {
+ return findLast(this.linesMap, n => n <= line)
+ }
+}
diff --git a/frontend/src/renderers/modules/katex.js b/frontend/src/renderers/modules/katex.js
new file mode 100644
index 00000000..74a096db
--- /dev/null
+++ b/frontend/src/renderers/modules/katex.js
@@ -0,0 +1,145 @@
+// Test if potential opening or closing delimieter
+// Assumes that there is a "$" at state.src[pos]
+function isValidDelim (state, pos) {
+ const max = state.posMax
+ let canOpen = true
+ let canClose = true
+
+ const prevChar = pos > 0 ? state.src.charCodeAt(pos - 1) : -1
+ const nextChar = pos + 1 <= max ? state.src.charCodeAt(pos + 1) : -1
+
+ // Check non-whitespace conditions for opening and closing, and
+ // check that closing delimeter isn't followed by a number
+ if (prevChar === 0x20/* " " */ || prevChar === 0x09/* \t */ ||
+ (nextChar >= 0x30/* "0" */ && nextChar <= 0x39/* "9" */)) {
+ canClose = false
+ }
+ if (nextChar === 0x20/* " " */ || nextChar === 0x09/* \t */) {
+ canOpen = false
+ }
+
+ return {
+ canOpen,
+ canClose
+ }
+}
+
+export default {
+ katexInline (state, silent) {
+ let match, token, res, pos
+
+ if (state.src[state.pos] !== '$') { return false }
+
+ res = isValidDelim(state, state.pos)
+ if (!res.canOpen) {
+ if (!silent) { state.pending += '$' }
+ state.pos += 1
+ return true
+ }
+
+ // First check for and bypass all properly escaped delimieters
+ // This loop will assume that the first leading backtick can not
+ // be the first character in state.src, which is known since
+ // we have found an opening delimieter already.
+ const start = state.pos + 1
+ match = start
+ while ((match = state.src.indexOf('$', match)) !== -1) {
+ // Found potential $, look for escapes, pos will point to
+ // first non escape when complete
+ pos = match - 1
+ while (state.src[pos] === '\\') { pos -= 1 }
+
+ // Even number of escapes, potential closing delimiter found
+ if (((match - pos) % 2) === 1) { break }
+ match += 1
+ }
+
+ // No closing delimter found. Consume $ and continue.
+ if (match === -1) {
+ if (!silent) { state.pending += '$' }
+ state.pos = start
+ return true
+ }
+
+ // Check if we have empty content, ie: $$. Do not parse.
+ if (match - start === 0) {
+ if (!silent) { state.pending += '$$' }
+ state.pos = start + 1
+ return true
+ }
+
+ // Check for valid closing delimiter
+ res = isValidDelim(state, match)
+ if (!res.canClose) {
+ if (!silent) { state.pending += '$' }
+ state.pos = start
+ return true
+ }
+
+ if (!silent) {
+ token = state.push('katex_inline', 'math', 0)
+ token.markup = '$'
+ token.content = state.src
+ // Extract the math part without the $
+ .slice(start, match)
+ // Escape the curly braces since they will be interpreted as
+ // attributes by markdown-it-attrs (the "curly_attributes"
+ // core rule)
+ .replaceAll('{', '{{')
+ .replaceAll('}', '}}')
+ }
+
+ state.pos = match + 1
+ return true
+ },
+
+ katexBlock (state, start, end, silent) {
+ let firstLine; let lastLine; let next; let lastPos; let found = false
+ let pos = state.bMarks[start] + state.tShift[start]
+ let max = state.eMarks[start]
+
+ if (pos + 2 > max) { return false }
+ if (state.src.slice(pos, pos + 2) !== '$$') { return false }
+
+ pos += 2
+ firstLine = state.src.slice(pos, max)
+
+ if (silent) { return true }
+ if (firstLine.trim().slice(-2) === '$$') {
+ // Single line expression
+ firstLine = firstLine.trim().slice(0, -2)
+ found = true
+ }
+
+ for (next = start; !found;) {
+ next++
+
+ if (next >= end) { break }
+
+ pos = state.bMarks[next] + state.tShift[next]
+ max = state.eMarks[next]
+
+ if (pos < max && state.tShift[next] < state.blkIndent) {
+ // non-empty line with negative indent should stop the list:
+ break
+ }
+
+ if (state.src.slice(pos, max).trim().slice(-2) === '$$') {
+ lastPos = state.src.slice(0, max).lastIndexOf('$$')
+ lastLine = state.src.slice(pos, lastPos)
+ found = true
+ }
+ }
+
+ state.line = next + 1
+
+ const token = state.push('katex_block', 'math', 0)
+ token.block = true
+ token.content = (firstLine && firstLine.trim() ? firstLine + '\n' : '') +
+ state.getLines(start + 1, next, state.tShift[start], true) +
+ (lastLine && lastLine.trim() ? lastLine : '')
+ token.map = [start, state.line]
+ token.markup = '$$'
+ return true
+ }
+}
diff --git a/frontend/src/renderers/modules/kroki.mjs b/frontend/src/renderers/modules/kroki.mjs
new file mode 100644
index 00000000..43e102bc
--- /dev/null
+++ b/frontend/src/renderers/modules/kroki.mjs
@@ -0,0 +1,143 @@
+import pako from 'pako'
+
+// ------------------------------------
+// Markdown - PlantUML Preprocessor
+// ------------------------------------
+
+export default {
+ init (mdinst, conf) {
+ mdinst.use((md, opts) => {
+ const openMarker = opts.openMarker || '```kroki'
+ const openChar = openMarker.charCodeAt(0)
+ const closeMarker = opts.closeMarker || '```'
+ const closeChar = closeMarker.charCodeAt(0)
+ const server = opts.server || 'https://kroki.io'
+
+ md.block.ruler.before('fence', 'kroki', (state, startLine, endLine, silent) => {
+ let nextLine
+ let markup
+ let params
+ let token
+ let i
+ let autoClosed = false
+ let start = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // Check out the first character quickly,
+ // this should filter out most of non-uml blocks
+ //
+ if (openChar !== state.src.charCodeAt(start)) { return false }
+
+ // Check out the rest of the marker string
+ //
+ for (i = 0; i < openMarker.length; ++i) {
+ if (openMarker[i] !== state.src[start + i]) { return false }
+ }
+
+ markup = state.src.slice(start, start + i)
+ params = state.src.slice(start + i, max)
+
+ // Since start is found, we can report success here in validation mode
+ //
+ if (silent) { return true }
+
+ // Search for the end of the block
+ //
+ nextLine = startLine
+
+ for (;;) {
+ nextLine++
+ if (nextLine >= endLine) {
+ // unclosed block should be autoclosed by end of document.
+ // also block seems to be autoclosed by end of parent
+ break
+ }
+
+ start = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+
+ if (start < max && state.sCount[nextLine] < state.blkIndent) {
+ // non-empty line with negative indent should stop the list:
+ // - ```
+ // test
+ break
+ }
+
+ if (closeChar !== state.src.charCodeAt(start)) {
+ // didn't find the closing fence
+ continue
+ }
+
+ if (state.sCount[nextLine] > state.sCount[startLine]) {
+ // closing fence should not be indented with respect of opening fence
+ continue
+ }
+
+ let closeMarkerMatched = true
+ for (i = 0; i < closeMarker.length; ++i) {
+ if (closeMarker[i] !== state.src[start + i]) {
+ closeMarkerMatched = false
+ break
+ }
+ }
+
+ if (!closeMarkerMatched) {
+ continue
+ }
+
+ // make sure tail has spaces only
+ if (state.skipSpaces(start + i) < max) {
+ continue
+ }
+
+ // found!
+ autoClosed = true
+ break
+ }
+
+ let contents = state.src
+ .split('\n')
+ .slice(startLine + 1, nextLine)
+ .join('\n')
+
+ // We generate a token list for the alt property, to mimic what the image parser does.
+ let altToken = []
+ // Remove leading space if any.
+ let alt = params ? params.slice(1) : 'uml diagram'
+ state.md.inline.parse(
+ alt,
+ state.md,
+ state.env,
+ altToken
+ )
+
+ let firstlf = contents.indexOf('\n')
+ if (firstlf === -1) firstlf = undefined
+ let diagramType = contents.substring(0, firstlf)
+ contents = contents.substring(firstlf + 1)
+
+ const result = pako.deflate(contents).toString('base64').replace(/\+/g, '-').replace(/\//g, '_')
+
+ token = state.push('kroki', 'img', 0)
+ // alt is constructed from children. No point in populating it here.
+ token.attrs = [ [ 'src', `${server}/${diagramType}/svg/${result}` ], [ 'alt', '' ], ['class', 'uml-diagram prefetch-candidate'] ]
+ token.block = true
+ token.children = altToken
+ token.info = params
+ token.map = [ startLine, nextLine ]
+ token.markup = markup
+
+ state.line = nextLine + (autoClosed ? 1 : 0)
+
+ return true
+ }, {
+ alt: [ 'paragraph', 'reference', 'blockquote', 'list' ]
+ })
+ md.renderer.rules.kroki = md.renderer.rules.image
+ }, {
+ openMarker: conf.openMarker,
+ closeMarker: conf.closeMarker,
+ server: conf.server
+ })
+ }
+}
diff --git a/frontend/src/renderers/modules/markdown-it-imsize.js b/frontend/src/renderers/modules/markdown-it-imsize.js
new file mode 100644
index 00000000..6b6b30e6
--- /dev/null
+++ b/frontend/src/renderers/modules/markdown-it-imsize.js
@@ -0,0 +1,259 @@
+// Adapted from markdown-it-imsize plugin by @tatsy
+// Original source https://github.com/tatsy/markdown-it-imsize/blob/master/lib/index.js
+
+function renderImSize (state, silent) {
+ let attrs
+ let code
+ let label
+ let pos
+ let ref
+ let res
+ let title
+ let width = ''
+ let height = ''
+ let token
+ let tokens
+ let start
+ let href = ''
+ const oldPos = state.pos
+ const max = state.posMax
+
+ if (state.src.charCodeAt(state.pos) !== 0x21/* ! */) { return false }
+ if (state.src.charCodeAt(state.pos + 1) !== 0x5B/* [ */) { return false }
+
+ const labelStart = state.pos + 2
+ const labelEnd = state.md.helpers.parseLinkLabel(state, state.pos + 1, false)
+
+ // parser failed to find ']', so it's not a valid link
+ if (labelEnd < 0) { return false }
+
+ pos = labelEnd + 1
+ if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
+ //
+ // Inline link
+ //
+
+ // [link]( "title" )
+ // ^^ skipping these spaces
+ pos++
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (code !== 0x20 && code !== 0x0A) { break }
+ }
+ if (pos >= max) { return false }
+
+ // [link]( "title" )
+ // ^^^^^^ parsing link destination
+ start = pos
+ res = state.md.helpers.parseLinkDestination(state.src, pos, state.posMax)
+ if (res.ok) {
+ href = state.md.normalizeLink(res.str)
+ if (state.md.validateLink(href)) {
+ pos = res.pos
+ } else {
+ href = ''
+ }
+ }
+
+ // [link]( "title" )
+ // ^^ skipping these spaces
+ start = pos
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (code !== 0x20 && code !== 0x0A) { break }
+ }
+
+ // [link]( "title" )
+ // ^^^^^^^ parsing link title
+ res = state.md.helpers.parseLinkTitle(state.src, pos, state.posMax)
+ if (pos < max && start !== pos && res.ok) {
+ title = res.str
+ pos = res.pos
+
+ // [link]( "title" )
+ // ^^ skipping these spaces
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (code !== 0x20 && code !== 0x0A) { break }
+ }
+ } else {
+ title = ''
+ }
+
+ // [link]( "title" =WxH )
+ // ^^^^ parsing image size
+ if (pos - 1 >= 0) {
+ code = state.src.charCodeAt(pos - 1)
+
+ // there must be at least one white spaces
+ // between previous field and the size
+ if (code === 0x20) {
+ res = parseImageSize(state.src, pos, state.posMax)
+ if (res.ok) {
+ width = res.width
+ height = res.height
+ pos = res.pos
+
+ // [link]( "title" =WxH )
+ // ^^ skipping these spaces
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (code !== 0x20 && code !== 0x0A) { break }
+ }
+ }
+ }
+ }
+
+ if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
+ state.pos = oldPos
+ return false
+ }
+ pos++
+ } else {
+ //
+ // Link reference
+ //
+ if (typeof state.env.references === 'undefined') { return false }
+
+ // [foo] [bar]
+ // ^^ optional whitespace (can include newlines)
+ for (; pos < max; pos++) {
+ code = state.src.charCodeAt(pos)
+ if (code !== 0x20 && code !== 0x0A) { break }
+ }
+
+ if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
+ start = pos + 1
+ pos = state.md.helpers.parseLinkLabel(state, pos)
+ if (pos >= 0) {
+ label = state.src.slice(start, pos++)
+ } else {
+ pos = labelEnd + 1
+ }
+ } else {
+ pos = labelEnd + 1
+ }
+
+ // covers label === '' and label === undefined
+ // (collapsed reference link and shortcut reference link respectively)
+ if (!label) { label = state.src.slice(labelStart, labelEnd) }
+
+ ref = state.env.references[state.md.utils.normalizeReference(label)]
+ if (!ref) {
+ state.pos = oldPos
+ return false
+ }
+ href = ref.href
+ title = ref.title
+ }
+
+ //
+ // We found the end of the link, and know for a fact it's a valid link;
+ // so all that's left to do is to call tokenizer.
+ //
+ if (!silent) {
+ state.pos = labelStart
+ state.posMax = labelEnd
+
+ const newState = new state.md.inline.State(
+ state.src.slice(labelStart, labelEnd),
+ state.md,
+ state.env,
+ tokens = []
+ )
+ newState.md.inline.tokenize(newState)
+
+ token = state.push('image', 'img', 0)
+ token.attrs = attrs = [['src', href],
+ ['alt', '']]
+ token.children = tokens
+ if (title) {
+ attrs.push(['title', title])
+ }
+
+ if (width !== '') {
+ attrs.push(['width', width])
+ }
+
+ if (height !== '') {
+ attrs.push(['height', height])
+ }
+ }
+
+ state.pos = pos
+ state.posMax = max
+ return true
+}
+
+function parseNextNumber (str, pos, max) {
+ let code
+ const start = pos
+ const result = {
+ ok: false,
+ pos,
+ value: ''
+ }
+
+ code = str.charCodeAt(pos)
+
+ while ((pos < max && (code >= 0x30 /* 0 */ && code <= 0x39 /* 9 */)) || code === 0x25 /* % */) {
+ code = str.charCodeAt(++pos)
+ }
+
+ result.ok = true
+ result.pos = pos
+ result.value = str.slice(start, pos)
+
+ return result
+}
+
+function parseImageSize (str, pos, max) {
+ let code
+ const result = {
+ ok: false,
+ pos: 0,
+ width: '',
+ height: ''
+ }
+
+ if (pos >= max) { return result }
+
+ code = str.charCodeAt(pos)
+
+ if (code !== 0x3d /* = */) { return result }
+
+ pos++
+
+ // size must follow = without any white spaces as follows
+ // (1) =300x200
+ // (2) =300x
+ // (3) =x200
+ code = str.charCodeAt(pos)
+ if (code !== 0x78 /* x */ && (code < 0x30 || code > 0x39) /* [0-9] */) {
+ return result
+ }
+
+ // parse width
+ const resultW = parseNextNumber(str, pos, max)
+ pos = resultW.pos
+
+ // next charactor must be 'x'
+ code = str.charCodeAt(pos)
+ if (code !== 0x78 /* x */) { return result }
+
+ pos++
+
+ // parse height
+ const resultH = parseNextNumber(str, pos, max)
+ pos = resultH.pos
+
+ result.width = resultW.value
+ result.height = resultH.value
+ result.pos = pos
+ result.ok = true
+ return result
+}
+
+export default (md) => {
+ md.inline.ruler.before('emphasis', 'image', renderImSize)
+}
diff --git a/frontend/src/renderers/modules/markdown-it-underline.js b/frontend/src/renderers/modules/markdown-it-underline.js
new file mode 100644
index 00000000..b898d1ce
--- /dev/null
+++ b/frontend/src/renderers/modules/markdown-it-underline.js
@@ -0,0 +1,12 @@
+function renderEm (tokens, idx, opts, env, slf) {
+ const token = tokens[idx]
+ if (token.markup === '_') {
+ token.tag = 'u'
+ }
+ return slf.renderToken(tokens, idx, opts)
+}
+
+export default (md) => {
+ md.renderer.rules.em_open = renderEm
+ md.renderer.rules.em_close = renderEm
+}
diff --git a/frontend/src/renderers/modules/plantuml.js b/frontend/src/renderers/modules/plantuml.js
new file mode 100644
index 00000000..9d5e3ea5
--- /dev/null
+++ b/frontend/src/renderers/modules/plantuml.js
@@ -0,0 +1,187 @@
+import pako from 'pako'
+
+// ------------------------------------
+// Markdown - PlantUML Preprocessor
+// ------------------------------------
+
+export default {
+ init (mdinst, conf) {
+ mdinst.use((md, opts) => {
+ const openMarker = opts.openMarker || '```plantuml'
+ const openChar = openMarker.charCodeAt(0)
+ const closeMarker = opts.closeMarker || '```'
+ const closeChar = closeMarker.charCodeAt(0)
+ const imageFormat = opts.imageFormat || 'svg'
+ const server = opts.server || 'https://plantuml.requarks.io'
+
+ md.block.ruler.before('fence', 'uml_diagram', (state, startLine, endLine, silent) => {
+ let nextLine
+ let i
+ let autoClosed = false
+ let start = state.bMarks[startLine] + state.tShift[startLine]
+ let max = state.eMarks[startLine]
+
+ // Check out the first character quickly,
+ // this should filter out most of non-uml blocks
+ //
+ if (openChar !== state.src.charCodeAt(start)) { return false }
+
+ // Check out the rest of the marker string
+ //
+ for (i = 0; i < openMarker.length; ++i) {
+ if (openMarker[i] !== state.src[start + i]) { return false }
+ }
+
+ const markup = state.src.slice(start, start + i)
+ const params = state.src.slice(start + i, max)
+
+ // Since start is found, we can report success here in validation mode
+ //
+ if (silent) { return true }
+
+ // Search for the end of the block
+ //
+ nextLine = startLine
+
+ for (;;) {
+ nextLine++
+ if (nextLine >= endLine) {
+ // unclosed block should be autoclosed by end of document.
+ // also block seems to be autoclosed by end of parent
+ break
+ }
+
+ start = state.bMarks[nextLine] + state.tShift[nextLine]
+ max = state.eMarks[nextLine]
+
+ if (start < max && state.sCount[nextLine] < state.blkIndent) {
+ // non-empty line with negative indent should stop the list:
+ // - ```
+ // test
+ break
+ }
+
+ if (closeChar !== state.src.charCodeAt(start)) {
+ // didn't find the closing fence
+ continue
+ }
+
+ if (state.sCount[nextLine] > state.sCount[startLine]) {
+ // closing fence should not be indented with respect of opening fence
+ continue
+ }
+
+ let closeMarkerMatched = true
+ for (i = 0; i < closeMarker.length; ++i) {
+ if (closeMarker[i] !== state.src[start + i]) {
+ closeMarkerMatched = false
+ break
+ }
+ }
+
+ if (!closeMarkerMatched) {
+ continue
+ }
+
+ // make sure tail has spaces only
+ if (state.skipSpaces(start + i) < max) {
+ continue
+ }
+
+ // found!
+ autoClosed = true
+ break
+ }
+
+ const contents = state.src
+ .split('\n')
+ .slice(startLine + 1, nextLine)
+ .join('\n')
+
+ // We generate a token list for the alt property, to mimic what the image parser does.
+ const altToken = []
+ // Remove leading space if any.
+ const alt = params ? params.slice(1) : 'uml diagram'
+ state.md.inline.parse(
+ alt,
+ state.md,
+ state.env,
+ altToken
+ )
+
+ const zippedCode = encode64(pako.deflate('@startuml\n' + contents + '\n@enduml', { to: 'string' }))
+
+ const token = state.push('uml_diagram', 'img', 0)
+ // alt is constructed from children. No point in populating it here.
+ token.attrs = [['src', `${server}/${imageFormat}/${zippedCode}`], ['alt', ''], ['class', 'uml-diagram']]
+ token.block = true
+ token.children = altToken
+ token.info = params
+ token.map = [startLine, nextLine]
+ token.markup = markup
+
+ state.line = nextLine + (autoClosed ? 1 : 0)
+
+ return true
+ }, {
+ alt: ['paragraph', 'reference', 'blockquote', 'list']
+ })
+ md.renderer.rules.uml_diagram = md.renderer.rules.image
+ }, {
+ openMarker: conf.openMarker,
+ closeMarker: conf.closeMarker,
+ imageFormat: conf.imageFormat,
+ server: conf.server
+ })
+ }
+}
+
+function encode64 (data) {
+ let r = ''
+ for (let i = 0; i < data.length; i += 3) {
+ if (i + 2 === data.length) {
+ r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), 0)
+ } else if (i + 1 === data.length) {
+ r += append3bytes(data.charCodeAt(i), 0, 0)
+ } else {
+ r += append3bytes(data.charCodeAt(i), data.charCodeAt(i + 1), data.charCodeAt(i + 2))
+ }
+ }
+ return r
+}
+
+function append3bytes (b1, b2, b3) {
+ const c1 = b1 >> 2
+ const c2 = ((b1 & 0x3) << 4) | (b2 >> 4)
+ const c3 = ((b2 & 0xF) << 2) | (b3 >> 6)
+ const c4 = b3 & 0x3F
+ let r = ''
+ r += encode6bit(c1 & 0x3F)
+ r += encode6bit(c2 & 0x3F)
+ r += encode6bit(c3 & 0x3F)
+ r += encode6bit(c4 & 0x3F)
+ return r
+}
+
+function encode6bit (raw) {
+ let b = raw
+ if (b < 10) {
+ return String.fromCharCode(48 + b)
+ }
+ b -= 10
+ if (b < 26) {
+ return String.fromCharCode(65 + b)
+ }
+ b -= 26
+ if (b < 26) {
+ return String.fromCharCode(97 + b)
+ }
+ b -= 26
+ if (b === 0) {
+ return '-'
+ }
+ if (b === 1) {
+ return '_'
+ }
+ return '?'
+}
diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js
new file mode 100644
index 00000000..fb552660
--- /dev/null
+++ b/frontend/src/router/index.js
@@ -0,0 +1,16 @@
+import { createRouter, createMemoryHistory, createWebHistory } from 'vue-router'
+import routes from './routes'
+
+export function initializeRouter () {
+ const createHistory = import.meta.env.SSR
+ ? createMemoryHistory
+ : createWebHistory
+
+ const router = createRouter({
+ scrollBehavior: () => ({ left: 0, top: 0 }),
+ routes,
+ history: createHistory(import.meta.env.BASE_URL)
+ })
+
+ return router
+}
diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js
new file mode 100644
index 00000000..0cf5d40a
--- /dev/null
+++ b/frontend/src/router/routes.js
@@ -0,0 +1,118 @@
+import { usePageStore } from '@/stores/page'
+
+const routes = [
+ {
+ path: '/login',
+ component: () => import('@/layouts/AuthLayout.vue'),
+ children: [
+ { path: '', component: () => import('@/pages/Login.vue') }
+ ]
+ },
+ {
+ path: '/a/:alias',
+ component: () => import('@/layouts/MainLayout.vue'),
+ beforeEnter: async (to, from) => {
+ const pageStore = usePageStore()
+ try {
+ const pathPath = await pageStore.pageAlias(to.params.alias)
+ return `/${pathPath}`
+ } catch (err) {
+ return '/_error/notfound'
+ }
+ }
+ },
+ {
+ path: '/_profile',
+ component: () => import('@/layouts/ProfileLayout.vue'),
+ children: [
+ { path: '', redirect: '/_profile/info' },
+ { path: 'info', component: () => import('@/pages/ProfileInfo.vue') },
+ { path: 'avatar', component: () => import('@/pages/ProfileAvatar.vue') },
+ { path: 'auth', component: () => import('@/pages/ProfileAuth.vue') },
+ { path: 'groups', component: () => import('@/pages/ProfileGroups.vue') }
+ ]
+ },
+ {
+ path: '/_search',
+ component: () => import('@/pages/Search.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/blocks', component: () => import('@/pages/AdminBlocks.vue') },
+ { path: ':siteid/editors', component: () => import('@/pages/AdminEditors.vue') },
+ { path: ':siteid/locale', component: () => import('@/pages/AdminLocale.vue') },
+ { path: ':siteid/login', component: () => import('@/pages/AdminLogin.vue') },
+ { path: ':siteid/navigation', component: () => import('@/pages/AdminNavigation.vue') },
+ { path: ':siteid/storage/:id?', component: () => import('@/pages/AdminStorage.vue') },
+ { path: ':siteid/theme', component: () => import('@/pages/AdminTheme.vue') },
+ // -> Users
+ { path: 'auth', component: () => import('@/pages/AdminAuth.vue') },
+ { path: 'groups/:id?/:section?', component: () => import('@/pages/AdminGroups.vue') },
+ { path: 'users/:id?/:section?', component: () => import('@/pages/AdminUsers.vue') },
+ // -> System
+ { path: 'api', component: () => import('@/pages/AdminApi.vue') },
+ { path: 'extensions', component: () => import('@/pages/AdminExtensions.vue') },
+ { path: 'icons', component: () => import('@/pages/AdminIcons.vue') },
+ { path: 'instances', component: () => import('@/pages/AdminInstances.vue') },
+ { path: 'mail', component: () => import('@/pages/AdminMail.vue') },
+ { path: 'metrics', component: () => import('@/pages/AdminMetrics.vue') },
+ { path: 'rendering', component: () => import('@/pages/AdminRendering.vue') },
+ { path: 'scheduler', component: () => import('@/pages/AdminScheduler.vue') },
+ { path: 'search', component: () => import('@/pages/AdminSearch.vue') },
+ { path: 'security', component: () => import('@/pages/AdminSecurity.vue') },
+ { path: 'system', component: () => import('@/pages/AdminSystem.vue') },
+ { path: 'terminal', component: () => import('@/pages/AdminTerminal.vue') },
+ { path: 'utilities', component: () => import('@/pages/AdminUtilities.vue') },
+ { path: 'webhooks', component: () => import('@/pages/AdminWebhooks.vue') },
+ { path: 'flags', component: () => import('@/pages/AdminFlags.vue') }
+ ]
+ },
+ {
+ path: '/_error/:action?',
+ component: () => import('@/pages/ErrorGeneric.vue')
+ },
+ // {
+ // path: '/_unknown-site',
+ // component: () => import('../pages/UnknownSite.vue')
+ // },
+
+ // --------------------------------
+ // CREATE
+ // --------------------------------
+ {
+ path: '/_create/:editor?',
+ component: () => import('../layouts/MainLayout.vue'),
+ children: [
+ { path: '', component: () => import('../pages/Index.vue') }
+ ]
+ },
+ // --------------------------------
+ // EDIT
+ // --------------------------------
+ {
+ path: '/_edit/:pagePath?',
+ component: () => import('../layouts/MainLayout.vue'),
+ children: [
+ { path: '', component: () => import('../pages/Index.vue') }
+ ]
+ },
+ // -----------------------
+ // STANDARD PAGE CATCH-ALL
+ // -----------------------
+ {
+ path: '/:catchAll(.*)*',
+ component: () => import('../layouts/MainLayout.vue'),
+ children: [
+ { path: '', component: () => import('../pages/Index.vue') }
+ ]
+ }
+]
+
+export default routes
diff --git a/frontend/src/stores/admin.js b/frontend/src/stores/admin.js
new file mode 100644
index 00000000..fd368209
--- /dev/null
+++ b/frontend/src/stores/admin.js
@@ -0,0 +1,105 @@
+import { defineStore } from 'pinia'
+
+import { clone, cloneDeep, sortBy } from 'lodash-es'
+import semverGte from 'semver/functions/gte'
+
+/* global APOLLO_CLIENT */
+
+export const useAdminStore = defineStore('admin', {
+ state: () => ({
+ currentSiteId: null,
+ info: {
+ currentVersion: 'n/a',
+ latestVersion: 'n/a',
+ groupsTotal: 0,
+ pagesTotal: 0,
+ tagsTotal: 0,
+ usersTotal: 0,
+ loginsPastDay: 0,
+ isApiEnabled: false,
+ isMailConfigured: false,
+ isSchedulerHealthy: false
+ },
+ overlay: null,
+ overlayOpts: {},
+ sites: [],
+ locales: [
+ { code: 'en', name: 'English' }
+ ]
+ }),
+ getters: {
+ isVersionLatest: (state) => {
+ if (!state.info.currentVersion || !state.info.latestVersion || state.info.currentVersion === 'n/a' || state.info.latestVersion === 'n/a') {
+ return false
+ }
+ return semverGte(state.info.currentVersion, state.info.latestVersion)
+ }
+ },
+ actions: {
+ async fetchLocales () {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query getAdminLocales {
+ locales {
+ code
+ language
+ name
+ nativeName
+ }
+ }
+ `
+ })
+ this.locales = sortBy(cloneDeep(resp?.data?.locales ?? []), ['nativeName', 'name'])
+ },
+ async fetchInfo () {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query getAdminInfo {
+ apiState
+ metricsState
+ systemInfo {
+ groupsTotal
+ tagsTotal
+ usersTotal
+ loginsPastDay
+ currentVersion
+ latestVersion
+ isMailConfigured
+ isSchedulerHealthy
+ }
+ }
+ `,
+ fetchPolicy: 'network-only'
+ })
+ this.info.groupsTotal = clone(resp?.data?.systemInfo?.groupsTotal ?? 0)
+ this.info.tagsTotal = clone(resp?.data?.systemInfo?.tagsTotal ?? 0)
+ this.info.usersTotal = clone(resp?.data?.systemInfo?.usersTotal ?? 0)
+ this.info.loginsPastDay = clone(resp?.data?.systemInfo?.loginsPastDay ?? 0)
+ this.info.currentVersion = clone(resp?.data?.systemInfo?.currentVersion ?? 'n/a')
+ this.info.latestVersion = clone(resp?.data?.systemInfo?.latestVersion ?? 'n/a')
+ this.info.isApiEnabled = clone(resp?.data?.apiState ?? false)
+ this.info.isMetricsEnabled = clone(resp?.data?.metricsState ?? false)
+ this.info.isMailConfigured = clone(resp?.data?.systemInfo?.isMailConfigured ?? false)
+ this.info.isSchedulerHealthy = clone(resp?.data?.systemInfo?.isSchedulerHealthy ?? false)
+ },
+ async fetchSites () {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query getSites {
+ sites {
+ id
+ hostname
+ isEnabled
+ title
+ }
+ }
+ `,
+ fetchPolicy: 'network-only'
+ })
+ this.sites = cloneDeep(resp?.data?.sites ?? [])
+ if (!this.currentSiteId) {
+ this.currentSiteId = this.sites[0].id
+ }
+ }
+ }
+})
diff --git a/frontend/src/stores/common.js b/frontend/src/stores/common.js
new file mode 100644
index 00000000..158cc746
--- /dev/null
+++ b/frontend/src/stores/common.js
@@ -0,0 +1,41 @@
+import { defineStore } from 'pinia'
+
+import { difference } from 'lodash-es'
+
+export const useCommonStore = defineStore('common', {
+ state: () => ({
+ routerLoading: false,
+ locale: localStorage.getItem('locale') || 'en',
+ desiredLocale: localStorage.getItem('locale'),
+ blocksLoaded: []
+ }),
+ getters: {},
+ actions: {
+ async fetchLocaleStrings (locale) {
+ try {
+ return API_CLIENT.get(`locales/${locale}/strings`)
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ },
+ setLocale (locale) {
+ this.$patch({
+ locale,
+ desiredLocale: locale
+ })
+ localStorage.setItem('locale', locale)
+ },
+ async loadBlocks (blocks = []) {
+ const toLoad = difference(blocks, this.blocksLoaded)
+ for (const block of toLoad) {
+ try {
+ await import(/* @vite-ignore */ `/_blocks/${block}.js`)
+ this.blocksLoaded.push(block)
+ } catch (err) {
+ console.warn(`Failed to load ${block}: ${err.message}`)
+ }
+ }
+ }
+ }
+})
diff --git a/frontend/src/stores/editor.js b/frontend/src/stores/editor.js
new file mode 100644
index 00000000..7b3caaa4
--- /dev/null
+++ b/frontend/src/stores/editor.js
@@ -0,0 +1,120 @@
+import { defineStore } from 'pinia'
+
+import { clone } from 'lodash-es'
+import { v4 as uuid } from 'uuid'
+
+import { useSiteStore } from './site'
+
+const imgMimeExt = {
+ 'image/jpeg': 'jpg',
+ 'image/gif': 'gif',
+ 'image/png': 'png',
+ 'image/webp': 'webp',
+ 'image/svg+xml': 'svg',
+ 'image/tiff': 'tif'
+}
+
+export const useEditorStore = defineStore('editor', {
+ state: () => ({
+ isActive: false,
+ editor: '',
+ originPageId: '',
+ mode: 'edit',
+ activeModal: '',
+ activeModalData: null,
+ hideSideNav: false,
+ media: {
+ folderTree: [],
+ currentFolderId: 0,
+ currentFileId: null
+ },
+ checkoutDateActive: '',
+ lastSaveTimestamp: null,
+ lastChangeTimestamp: null,
+ editors: {},
+ configIsLoaded: false,
+ reasonForChange: '',
+ ignoreRouteChange: false,
+ pendingAssets: []
+ }),
+ getters: {
+ hasPendingChanges: (state) => {
+ return state.lastSaveTimestamp && state.lastSaveTimestamp !== state.lastChangeTimestamp
+ }
+ },
+ actions: {
+ addPendingAsset (data) {
+ const blobUrl = URL.createObjectURL(data)
+ if (data instanceof File) {
+ this.pendingAssets.push({
+ id: uuid(),
+ kind: 'file',
+ file: data,
+ fileName: data.name,
+ blobUrl
+ })
+ } else {
+ const fileId = uuid()
+ const fileName = `${fileId}.${imgMimeExt[data.type] || 'dat'}`
+ this.pendingAssets.push({
+ id: fileId,
+ kind: 'blob',
+ file: new File(data, fileName, { type: data.type }),
+ fileName,
+ blobUrl
+ })
+ }
+ return blobUrl
+ },
+ async fetchConfigs () {
+ const siteStore = useSiteStore()
+ try {
+ if (!siteStore.id) {
+ throw new Error('Cannot fetch editors config: Missing Site ID')
+ }
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query fetchEditorConfigs (
+ $id: UUID!
+ ) {
+ siteById(
+ id: $id
+ ) {
+ id
+ editors {
+ asciidoc {
+ isActive
+ config
+ }
+ markdown {
+ isActive
+ config
+ }
+ wysiwyg {
+ isActive
+ config
+ }
+ }
+ }
+ }
+ `,
+ variables: {
+ id: siteStore.id
+ },
+ fetchPolicy: 'network-only'
+ })
+ this.$patch({
+ editors: {
+ asciidoc: resp?.data?.siteById?.editors?.asciidoc?.config,
+ markdown: resp?.data?.siteById?.editors?.markdown?.config,
+ wysiwyg: resp?.data?.siteById?.editors?.wysiwyg?.config
+ },
+ configIsLoaded: true
+ })
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ }
+ }
+})
diff --git a/frontend/src/stores/flags.js b/frontend/src/stores/flags.js
new file mode 100644
index 00000000..ccab18af
--- /dev/null
+++ b/frontend/src/stores/flags.js
@@ -0,0 +1,28 @@
+import { defineStore } from 'pinia'
+
+
+export const useFlagsStore = defineStore('flags', {
+ state: () => ({
+ loaded: false,
+ experimental: false
+ }),
+ getters: {},
+ actions: {
+ async load () {
+ try {
+ const systemFlags = await API_CLIENT.get('system/flags')
+ if (systemFlags) {
+ this.$patch({
+ ...systemFlags,
+ loaded: true
+ })
+ } else {
+ throw new Error('Could not fetch system flags.')
+ }
+ } catch (err) {
+ console.warn(err.message)
+ throw err
+ }
+ }
+ }
+})
diff --git a/frontend/src/stores/index.js b/frontend/src/stores/index.js
new file mode 100644
index 00000000..e44b46d1
--- /dev/null
+++ b/frontend/src/stores/index.js
@@ -0,0 +1,15 @@
+import { createPinia } from 'pinia'
+import { markRaw } from 'vue'
+
+export function initializeStore (router) {
+ const pinia = createPinia()
+
+ // You can add Pinia plugins here
+ // pinia.use(SomePiniaPlugin)
+
+ pinia.use(({ store }) => {
+ store.router = markRaw(router)
+ })
+
+ return pinia
+}
diff --git a/frontend/src/stores/page.js b/frontend/src/stores/page.js
new file mode 100644
index 00000000..cc6a7811
--- /dev/null
+++ b/frontend/src/stores/page.js
@@ -0,0 +1,683 @@
+import { defineStore } from 'pinia'
+
+import { cloneDeep, dropRight, initial, last, pick, transform } from 'lodash-es'
+import { DateTime } from 'luxon'
+
+import { useSiteStore } from './site'
+import { useEditorStore } from './editor'
+
+const pagePropsFragment = `
+ fragment PageRead on Page {
+ alias
+ allowComments
+ allowContributions
+ allowRatings
+ contentType
+ createdAt
+ description
+ editor
+ icon
+ id
+ isBrowsable
+ isSearchable
+ locale
+ navigationId
+ navigationMode
+ password
+ path
+ publishEndDate
+ publishStartDate
+ publishState
+ relations {
+ id
+ position
+ label
+ caption
+ icon
+ target
+ }
+ render
+ scriptJsLoad
+ scriptJsUnload
+ scriptCss
+ showSidebar
+ showTags
+ showToc
+ tags
+ title
+ toc
+ tocDepth {
+ min
+ max
+ }
+ updatedAt
+ }
+`
+const gqlQueries = {
+ pageById: `
+ query loadPage (
+ $id: UUID!
+ ) {
+ pageById(
+ id: $id
+ ) {
+ ...PageRead
+ }
+ }
+ ${pagePropsFragment}
+ `,
+ pageByPath: `
+ query loadPage (
+ $siteId: UUID!
+ $path: String!
+ ) {
+ pageByPath(
+ siteId: $siteId
+ path: $path
+ ) {
+ ...PageRead
+ }
+ }
+ ${pagePropsFragment}
+ `,
+ pageByIdWithContent: `
+ query loadPageWithContent (
+ $id: UUID!
+ ) {
+ pageById(
+ id: $id
+ ) {
+ ...PageRead,
+ content
+ }
+ }
+ ${pagePropsFragment}
+ `,
+ pageByPathWithContent: `
+ query loadPageWithContent (
+ $siteId: UUID!
+ $path: String!
+ ) {
+ pageByPath(
+ siteId: $siteId
+ path: $path
+ ) {
+ ...PageRead,
+ content
+ }
+ }
+ ${pagePropsFragment}
+ `
+}
+
+const gqlMutations = {
+ createPage: `
+ mutation createPage (
+ $alias: String
+ $allowComments: Boolean
+ $allowContributions: Boolean
+ $allowRatings: Boolean
+ $content: String!
+ $description: String!
+ $editor: String!
+ $icon: String
+ $isBrowsable: Boolean
+ $isSearchable: Boolean
+ $locale: String!
+ $path: String!
+ $publishState: PagePublishState!
+ $publishEndDate: Date
+ $publishStartDate: Date
+ $relations: [PageRelationInput!]
+ $scriptCss: String
+ $scriptJsLoad: String
+ $scriptJsUnload: String
+ $showSidebar: Boolean
+ $showTags: Boolean
+ $showToc: Boolean
+ $siteId: UUID!
+ $tags: [String!]
+ $title: String!
+ $tocDepth: PageTocDepthInput
+ ) {
+ createPage (
+ alias: $alias
+ allowComments: $allowComments
+ allowContributions: $allowContributions
+ allowRatings: $allowRatings
+ content: $content
+ description: $description
+ editor: $editor
+ icon: $icon
+ isBrowsable: $isBrowsable
+ isSearchable: $isSearchable
+ locale: $locale
+ path: $path
+ publishState: $publishState
+ publishEndDate: $publishEndDate
+ publishStartDate: $publishStartDate
+ relations: $relations
+ scriptCss: $scriptCss
+ scriptJsLoad: $scriptJsLoad
+ scriptJsUnload: $scriptJsUnload
+ showSidebar: $showSidebar
+ showTags: $showTags
+ showToc: $showToc
+ siteId: $siteId
+ tags: $tags
+ title: $title
+ tocDepth: $tocDepth
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ page {
+ ...PageRead
+ }
+ }
+ }
+ ${pagePropsFragment}
+ `
+}
+
+export const usePageStore = defineStore('page', {
+ state: () => ({
+ alias: '',
+ allowComments: false,
+ allowContributions: true,
+ allowRatings: true,
+ authorId: 0,
+ authorName: '',
+ commentsCount: 0,
+ content: '',
+ createdAt: '',
+ description: '',
+ editor: '',
+ icon: 'las la-file-alt',
+ id: '',
+ isBrowsable: true,
+ isSearchable: true,
+ locale: 'en',
+ navigationId: null,
+ navigationMode: 'inherit',
+ password: '',
+ path: '',
+ publishEndDate: '',
+ publishStartDate: '',
+ publishState: '',
+ relations: [],
+ render: '',
+ scriptJsLoad: '',
+ scriptJsUnload: '',
+ scriptCss: '',
+ showSidebar: true,
+ showTags: true,
+ showToc: true,
+ tags: [],
+ title: '',
+ toc: [],
+ tocDepth: {
+ min: 1,
+ max: 2
+ },
+ updatedAt: ''
+ }),
+ getters: {
+ breadcrumbs: (state) => {
+ const siteStore = useSiteStore()
+ const pathPrefix = siteStore.useLocales ? `/${state.locale}` : ''
+ return transform(state.path.split('/'), (result, value, key) => {
+ result.push({
+ id: key,
+ title: value,
+ icon: 'las la-file-alt',
+ locale: 'en',
+ path: (last(result)?.path || pathPrefix) + `/${value}`
+ })
+ }, [])
+ },
+ folderPath: (state) => {
+ return initial(state.path.split('/')).join('/')
+ },
+ isHome: (state) => {
+ return ['', 'home'].includes(state.path)
+ }
+ },
+ actions: {
+ /**
+ * PAGE - LOAD
+ */
+ async pageLoad ({ path, id, withContent = false }) {
+ const editorStore = useEditorStore()
+ const siteStore = useSiteStore()
+ try {
+ const pageData = await API_CLIENT.get(`sites/${siteStore.id}/pages/${id ?? fastHash(path)}`, {
+ searchParams: {
+ withContent
+ }
+ }).json()
+ if (!pageData?.id) {
+ throw new Error('ERR_PAGE_NOT_FOUND')
+ }
+ // Update page store
+ this.$patch({
+ ...pageData,
+ relations: pageData.relations.map(r => pick(r, ['id', 'position', 'label', 'caption', 'icon', 'target'])),
+ tocDepth: pick(pageData.tocDepth, ['min', 'max'])
+ })
+ // Update editor state timestamps
+ const curDate = DateTime.utc()
+ editorStore.$patch({
+ lastChangeTimestamp: curDate,
+ lastSaveTimestamp: curDate
+ })
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ },
+ /**
+ * PAGE - GET PATH FROM ALIAS
+ */
+ async pageAlias (alias) {
+ const siteStore = useSiteStore()
+ try {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query fetchPathFromAlias (
+ $siteId: UUID!
+ $alias: String!
+ ) {
+ pathFromAlias (
+ siteId: $siteId
+ alias: $alias
+ ) {
+ id
+ path
+ }
+ }
+ `,
+ variables: { siteId: siteStore.id, alias },
+ fetchPolicy: 'cache-first'
+ })
+ const pagePath = cloneDeep(resp?.data?.pathFromAlias)
+ if (!pagePath?.id) {
+ throw new Error('ERR_PAGE_NOT_FOUND')
+ }
+ return pagePath.path
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ },
+ /**
+ * PAGE - CREATE
+ */
+ async pageCreate ({ editor, locale, path, basePath, title = '', description = '', content = '', fromNavigate = false } = {}) {
+ const editorStore = useEditorStore()
+
+ // -> Load editor config
+ if (!editorStore.configIsLoaded) {
+ await editorStore.fetchConfigs()
+ }
+
+ // -> Path normalization
+ if (path?.startsWith('/')) {
+ path = path.substring(1)
+ }
+ if (basePath?.startsWith('/')) {
+ basePath = basePath.substring(1)
+ }
+ if (basePath?.endsWith('/')) {
+ basePath = basePath.substring(0, basePath.length - 1)
+ }
+
+ // -> Redirect if not at /_create path
+ if (!this.router.currentRoute.value.path.startsWith('/_create/') && !fromNavigate) {
+ editorStore.$patch({ ignoreRouteChange: true })
+ this.router.push(`/_create/${editor}`)
+ }
+
+ // -> Init editor
+ editorStore.$patch({
+ originPageId: editorStore.isActive ? editorStore.originPageId : this.id, // Don't replace if already in edit mode
+ isActive: true,
+ mode: 'create',
+ editor
+ })
+
+ // -> Default Page Path
+ let newPath = path
+ if (!path && path !== '') {
+ const parentPath = basePath || basePath === '' ? basePath : dropRight(this.path.split('/'), 1).join('/')
+ newPath = parentPath ? `${parentPath}/new-page` : 'new-page'
+ }
+
+ // -> Set Default Page Data
+ this.$patch({
+ id: 0,
+ locale: locale || this.locale,
+ path: newPath,
+ title: title ?? '',
+ description: description ?? '',
+ icon: 'las la-file-alt',
+ alias: '',
+ publishState: 'published',
+ relations: [],
+ tags: [],
+ content: content ?? '',
+ render: '',
+ isBrowsable: true,
+ isSearchable: true,
+ mode: 'edit'
+ })
+ },
+ /**
+ * PAGE - DUPLICATE
+ */
+ async pageDuplicate ({ sourecePageId, title, path }) {
+ try {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query loadPageSource (
+ $id: UUID!
+ ) {
+ pageById(
+ id: $id
+ ) {
+ id
+ content
+ contentType
+ description
+ editor
+ }
+ }
+ `,
+ variables: {
+ id: sourecePageId ?? pageStore.id
+ },
+ fetchPolicy: 'network-only'
+ })
+ const pageData = cloneDeep(resp?.data?.pageById ?? {})
+ if (!pageData?.id) {
+ throw new Error('ERR_PAGE_NOT_FOUND')
+ }
+ this.pageCreate({
+ editor: pageData.editor,
+ title,
+ path,
+ content: pageData.content,
+ description: pageData.description
+ })
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ },
+ /**
+ * PAGE - EDIT
+ */
+ async pageEdit ({ path, id, fromNavigate = false } = {}) {
+ const editorStore = useEditorStore()
+
+ const loadArgs = {
+ withContent: true
+ }
+
+ if (id) {
+ loadArgs.id = id
+ } else if (path) {
+ loadArgs.path = path
+ } else {
+ loadArgs.id = this.id
+ }
+
+ await this.pageLoad(loadArgs)
+
+ if (!editorStore.configIsLoaded) {
+ await editorStore.fetchConfigs()
+ }
+
+ editorStore.$patch({
+ isActive: true,
+ mode: 'edit',
+ editor: this.editor
+ })
+ },
+ /**
+ * PAGE - MOVE
+ */
+ async pageMove ({ id, title, path } = {}) {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: `
+ mutation movePage (
+ $id: UUID!
+ $destinationLocale: String!
+ $destinationPath: String!
+ $title: String
+ ) {
+ movePage (
+ id: $id
+ destinationLocale: $destinationLocale
+ destinationPath: $destinationPath
+ title: $title
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ variables: {
+ id,
+ destinationLocale: this.locale,
+ destinationPath: path,
+ title
+ }
+ })
+ const result = resp?.data?.movePage?.operation ?? {}
+ if (!result.succeeded) {
+ throw new Error(result.message)
+ } else {
+ this.router.replace(`/${path}`)
+ }
+ },
+ /**
+ * PAGE - Rename
+ */
+ async pageRename ({ id, title } = {}) {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: `
+ mutation renamePage (
+ $id: UUID!
+ $patch: PageUpdateInput!
+ ) {
+ updatePage (
+ id: $id
+ patch: $patch
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ variables: {
+ id: id,
+ patch: {
+ title
+ }
+ }
+ })
+ const result = resp?.data?.updatePage?.operation ?? {}
+ if (!result.succeeded) {
+ throw new Error(result.message)
+ }
+
+ // Update page store
+ if (id === this.id) {
+ this.$patch({ title })
+ }
+ },
+ /**
+ * PAGE SAVE
+ */
+ async pageSave () {
+ const editorStore = useEditorStore()
+ const siteStore = useSiteStore()
+ try {
+ if (editorStore.mode === 'create') {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: gqlMutations.createPage,
+ variables: {
+ ...pick(this, [
+ 'alias',
+ 'allowComments',
+ 'allowContributions',
+ 'allowRatings',
+ 'content',
+ 'description',
+ 'icon',
+ 'isBrowsable',
+ 'isSearchable',
+ 'locale',
+ 'password',
+ 'path',
+ 'publishEndDate',
+ 'publishStartDate',
+ 'publishState',
+ 'relations',
+ 'scriptJsLoad',
+ 'scriptJsUnload',
+ 'scriptCss',
+ 'showSidebar',
+ 'showTags',
+ 'showToc',
+ 'tags',
+ 'title',
+ 'tocDepth'
+ ]),
+ editor: editorStore.editor,
+ siteId: siteStore.id
+ }
+ })
+ const result = resp?.data?.createPage?.operation ?? {}
+ if (!result.succeeded) {
+ throw new Error(result.message)
+ }
+ const pageData = cloneDeep(resp.data.createPage.page ?? {})
+ if (!pageData?.id) {
+ throw new Error('ERR_CREATED_PAGE_NOT_FOUND')
+ }
+ // Update page store
+ this.$patch({
+ ...pageData,
+ relations: pageData.relations.map(r => pick(r, ['id', 'position', 'label', 'caption', 'icon', 'target'])),
+ tocDepth: pick(pageData.tocDepth, ['min', 'max'])
+ })
+
+ editorStore.$patch({
+ mode: 'edit'
+ })
+
+ this.router.replace(`/${this.path}`)
+ } else {
+ const resp = await APOLLO_CLIENT.mutate({
+ mutation: `
+ mutation savePage (
+ $id: UUID!
+ $patch: PageUpdateInput!
+ ) {
+ updatePage (
+ id: $id
+ patch: $patch
+ ) {
+ operation {
+ succeeded
+ message
+ }
+ }
+ }
+ `,
+ variables: {
+ id: this.id,
+ patch: {
+ ...pick(this, [
+ 'alias',
+ 'allowComments',
+ 'allowContributions',
+ 'allowRatings',
+ 'content',
+ 'description',
+ 'icon',
+ 'isBrowsable',
+ 'isSearchable',
+ 'password',
+ 'publishEndDate',
+ 'publishStartDate',
+ 'publishState',
+ 'relations',
+ 'scriptJsLoad',
+ 'scriptJsUnload',
+ 'scriptCss',
+ 'showSidebar',
+ 'showTags',
+ 'showToc',
+ 'tags',
+ 'title',
+ 'tocDepth'
+ ]),
+ reasonForChange: editorStore.reasonForChange
+ }
+ }
+ })
+ const result = resp?.data?.updatePage?.operation ?? {}
+ if (!result.succeeded) {
+ throw new Error(result.message)
+ }
+ }
+ // Update editor state timestamps
+ const curDate = DateTime.utc()
+ editorStore.$patch({
+ lastChangeTimestamp: curDate,
+ lastSaveTimestamp: curDate,
+ reasonForChange: ''
+ })
+ } catch (err) {
+ console.warn(err)
+ throw err
+ }
+ },
+ async cancelPageEdit () {
+ const editorStore = useEditorStore()
+ await this.pageLoad({ id: editorStore.originPageId ? editorStore.originPageId : this.id })
+ this.router.replace(`/${this.path}`)
+ },
+ generateToc () {
+
+ }
+ }
+})
+
+/**
+ * Fast, non-cryptographic 53-bit hash to encode page paths.
+ * Returns a URL-safe hex string.
+ */
+function fastHash (str, seed = 0) {
+ let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed
+ for (let i = 0, ch; i < str.length; i++) {
+ ch = str.charCodeAt(i)
+ h1 = Math.imul(h1 ^ ch, 2654435761)
+ h2 = Math.imul(h2 ^ ch, 1597334677)
+ }
+ h1 = Math.imul(h1 ^ (h1 >>> 16), 2246822507)
+ h1 ^= Math.imul(h2 ^ (h2 >>> 13), 3266489909)
+ h2 = Math.imul(h2 ^ (h2 >>> 16), 2246822507)
+ h2 ^= Math.imul(h1 ^ (h1 >>> 13), 3266489909)
+
+ // Convert to a 16-character hexadecimal string
+ return (4294967296 * (2097151 & h2) + (h1 >>> 0)).toString(16)
+}
diff --git a/frontend/src/stores/site.js b/frontend/src/stores/site.js
new file mode 100644
index 00000000..568a703d
--- /dev/null
+++ b/frontend/src/stores/site.js
@@ -0,0 +1,220 @@
+import { defineStore } from 'pinia'
+
+import { clone, sortBy } from 'lodash-es'
+
+import { useUserStore } from './user'
+
+export const useSiteStore = defineStore('site', {
+ state: () => ({
+ id: null,
+ hostname: '',
+ company: '',
+ contentLicense: '',
+ footerExtra: '',
+ dark: false,
+ title: '',
+ description: '',
+ logoText: true,
+ search: '',
+ searchLastQuery: '',
+ searchIsLoading: false,
+ printView: false,
+ pageDataTemplates: [],
+ showSideNav: true,
+ showSidebar: true,
+ overlay: null,
+ overlayOpts: {},
+ features: {
+ profile: false,
+ ratingsMode: 'off',
+ reasonForChange: 'required',
+ search: false
+ },
+ editors: {
+ asciidoc: false,
+ markdown: false,
+ wysiwyg: false
+ },
+ locales: {
+ primary: 'en',
+ active: [{
+ code: 'en',
+ language: 'en',
+ name: 'English',
+ nativeName: 'English'
+ }]
+ },
+ tags: [],
+ tagsLoaded: false,
+ theme: {
+ dark: false,
+ injectCSS: '',
+ injectHead: '',
+ injectBody: '',
+ colorPrimary: '#1976D2',
+ colorSecondary: '#02C39A',
+ colorAccent: '#f03a47',
+ colorHeader: '#000',
+ colorSidebar: '#1976D2',
+ codeBlocksTheme: '',
+ sidebarPosition: 'left',
+ tocPosition: 'right',
+ showSharingMenu: true,
+ showPrintBtn: true
+ },
+ sideDialogShown: false,
+ sideDialogComponent: '',
+ docsBase: 'https://beta.js.wiki/docs',
+ nav: {
+ currentId: null,
+ items: []
+ }
+ }),
+ getters: {
+ overlayIsShown: (state) => Boolean(state.overlay),
+ sideNavIsDisabled: (state) => Boolean(state.theme.sidebarPosition === 'off'),
+ scrollStyle: (state) => {
+ const userStore = useUserStore()
+ let isDark = false
+ if (userStore.appearance === 'site') {
+ isDark = state.theme.dark
+ } else if (userStore.appearance === 'dark') {
+ isDark = true
+ }
+ return {
+ thumb: {
+ right: '2px',
+ borderRadius: '5px',
+ backgroundColor: isDark ? '#FFF' : '#000',
+ width: '5px',
+ opacity: isDark ? 0.25 : 0.15
+ },
+ bar: {
+ backgroundColor: isDark ? '#000' : '#FAFAFA',
+ width: '9px',
+ opacity: isDark ? 0.25 : 1
+ }
+ }
+ },
+ useLocales: (state) => {
+ return state.locales?.active?.length > 1
+ }
+ },
+ actions: {
+ openFileManager (opts) {
+ this.$patch({
+ overlay: 'FileManager',
+ overlayOpts: {
+ insertMode: opts?.insertMode ?? false
+ }
+ })
+ },
+ async loadSite (hostname) {
+ try {
+ const siteInfo = await API_CLIENT.get(`sites/${hostname}`).json()
+ if (siteInfo) {
+ this.$patch({
+ id: clone(siteInfo.id),
+ hostname: clone(siteInfo.hostname),
+ title: clone(siteInfo.title),
+ description: clone(siteInfo.description),
+ logoText: clone(siteInfo.logoText),
+ company: clone(siteInfo.company),
+ contentLicense: clone(siteInfo.contentLicense),
+ footerExtra: clone(siteInfo.footerExtra),
+ features: {
+ ...this.features,
+ ...clone(siteInfo.features)
+ },
+ editors: {
+ asciidoc: clone(siteInfo.editors.asciidoc.isActive),
+ markdown: clone(siteInfo.editors.markdown.isActive),
+ wysiwyg: clone(siteInfo.editors.wysiwyg.isActive)
+ },
+ locales: {
+ primary: clone(siteInfo.locales.primary),
+ active: sortBy(clone(siteInfo.locales.active), ['nativeName', 'name'])
+ },
+ tags: [],
+ tagsLoaded: false,
+ theme: {
+ ...this.theme,
+ ...clone(siteInfo.theme)
+ }
+ })
+ } else {
+ throw new Error('Invalid Site')
+ }
+ } catch (err) {
+ console.warn(err)
+ console.warn(err.networkError?.result ?? err.message)
+ throw err
+ }
+ },
+ async fetchTags (forceRefresh = false) {
+ if (this.tagsLoaded && !forceRefresh) { return }
+ try {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query getSiteTags ($siteId: UUID!) {
+ tags (
+ siteId: $siteId
+ ) {
+ tag
+ usageCount
+ }
+ }
+ `,
+ variables: {
+ siteId: this.id
+ }
+ })
+ this.$patch({
+ tags: resp.data.tags ?? [],
+ tagsLoaded: true
+ })
+ } catch (err) {
+ console.warn(err.networkError?.result ?? err.message)
+ throw err
+ }
+ },
+ async fetchNavigation (id) {
+ try {
+ const resp = await APOLLO_CLIENT.query({
+ query: `
+ query getNavigationItems ($id: UUID!) {
+ navigationById (
+ id: $id
+ ) {
+ id
+ type
+ label
+ icon
+ target
+ openInNewWindow
+ children {
+ id
+ type
+ label
+ icon
+ target
+ openInNewWindow
+ }
+ }
+ }
+ `,
+ variables: { id }
+ })
+ this.$patch({
+ nav: {
+ currentId: id,
+ items: resp?.data?.navigationById ?? []
+ }
+ })
+ } catch (err) {
+ console.warn(err.networkError?.result ?? err.message)
+ throw err
+ }
+ }
+ }
+})
diff --git a/frontend/src/stores/store-flag.d.ts b/frontend/src/stores/store-flag.d.ts
new file mode 100644
index 00000000..7677175b
--- /dev/null
+++ b/frontend/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/frontend/src/stores/user.js b/frontend/src/stores/user.js
new file mode 100644
index 00000000..2b3c3592
--- /dev/null
+++ b/frontend/src/stores/user.js
@@ -0,0 +1,201 @@
+import { defineStore } from 'pinia'
+import { jwtDecode } from 'jwt-decode'
+import Cookies from 'js-cookie'
+
+import { DateTime } from 'luxon'
+import { getAccessibleColor } from '@/helpers/accessibility'
+
+import { useSiteStore } from './site'
+
+export const useUserStore = defineStore('user', {
+ state: () => ({
+ id: '10000000-0000-4000-8000-000000000001',
+ email: '',
+ name: '',
+ hasAvatar: false,
+ localeCode: '',
+ timezone: '',
+ dateFormat: 'YYYY-MM-DD',
+ timeFormat: '12h',
+ appearance: 'site',
+ cvd: 'none',
+ permissions: [],
+ pagePermissions: [],
+ iat: 0,
+ exp: null,
+ authenticated: false,
+ token: Cookies.get('jwt'),
+ profileLoaded: false
+ }),
+ getters: {
+ preferredDateFormat: (state) => {
+ if (!state.dateFormat) {
+ return 'D'
+ } else {
+ return state.dateFormat.replaceAll('Y', 'y').replaceAll('D', 'd')
+ }
+ },
+ preferredTimeFormat: (state) => {
+ return state.timeFormat === '24h' ? 'T' : 't'
+ }
+ },
+ actions: {
+ isTokenValid (offset) {
+ return this.exp && this.exp > (offset ? DateTime.now().plus(offset) : DateTime.now())
+ },
+ loadToken () {
+ if (!this.token) { return }
+ try {
+ const jwtData = jwtDecode(this.token)
+ this.id = jwtData.id
+ this.email = jwtData.email
+ this.iat = jwtData.iat
+ this.exp = DateTime.fromSeconds(jwtData.exp, { zone: 'utc' })
+ if (this.exp > DateTime.utc()) {
+ this.authenticated = true
+ } else {
+ console.info('Token has expired and will be refreshed on next query.')
+ }
+ } catch (err) {
+ console.warn('Failed to parse JWT. Invalid or malformed.')
+ }
+ },
+ async refreshToken () {
+ try {
+ const respRaw = await APOLLO_CLIENT.mutate({
+ context: {
+ skipAuth: true
+ },
+ mutation: `
+ mutation refreshToken (
+ $token: String!
+ ) {
+ refreshToken(token: $token) {
+ operation {
+ succeeded
+ message
+ }
+ jwt
+ }
+ }
+ `,
+ variables: {
+ token: this.token
+ }
+ })
+ const resp = respRaw?.data?.refreshToken ?? {}
+ if (!resp.operation?.succeeded) {
+ throw new Error(resp.operation?.message || 'Failed to refresh token.')
+ }
+ Cookies.set('jwt', resp.jwt, { expires: 365, path: '/', sameSite: 'Lax' })
+ this.token = resp.jwt
+ this.loadToken()
+ return true
+ } catch (err) {
+ console.warn(err)
+ return false
+ }
+ },
+ async refreshProfile () {
+ if (!this.authenticated || !this.id) {
+ return
+ }
+ try {
+ const respRaw = await APOLLO_CLIENT.query({
+ query: `
+ query refreshProfile (
+ $id: UUID!
+ ) {
+ userById(id: $id) {
+ id
+ name
+ email
+ hasAvatar
+ meta
+ prefs
+ lastLoginAt
+ groups {
+ id
+ name
+ }
+ }
+ userPermissions
+ }
+ `,
+ variables: {
+ id: this.id
+ }
+ })
+ const resp = respRaw?.data?.userById
+ if (!resp || resp.id !== this.id) {
+ throw new Error('Failed to fetch user profile!')
+ }
+ this.name = resp.name || 'Unknown User'
+ this.email = resp.email
+ this.hasAvatar = resp.hasAvatar ?? false
+ this.location = resp.meta.location || ''
+ this.jobTitle = resp.meta.jobTitle || ''
+ this.pronouns = resp.meta.pronouns || ''
+ this.timezone = resp.prefs.timezone || Intl.DateTimeFormat().resolvedOptions().timeZone || ''
+ this.dateFormat = resp.prefs.dateFormat || ''
+ this.timeFormat = resp.prefs.timeFormat || '12h'
+ this.appearance = resp.prefs.appearance || 'site'
+ this.cvd = resp.prefs.cvd || 'none'
+ this.permissions = respRaw.data.userPermissions || []
+ this.profileLoaded = true
+ } catch (err) {
+ console.warn(err)
+ }
+ },
+ logout () {
+ Cookies.remove('jwt', { path: '/' })
+ this.$patch({
+ id: '10000000-0000-4000-8000-000000000001',
+ email: '',
+ name: '',
+ hasAvatar: false,
+ localeCode: '',
+ timezone: '',
+ dateFormat: 'YYYY-MM-DD',
+ timeFormat: '12h',
+ appearance: 'site',
+ cvd: 'none',
+ permissions: [],
+ iat: 0,
+ exp: null,
+ authenticated: false,
+ token: '',
+ profileLoaded: false
+ })
+ EVENT_BUS.emit('logout')
+ },
+ getAccessibleColor (base, hexBase) {
+ return getAccessibleColor(base, hexBase, this.cvd)
+ },
+ can (permission) {
+ if (this.permissions.includes('manage:system') || this.permissions.includes(permission) || this.pagePermissions.includes(permission)) {
+ return true
+ }
+ return false
+ },
+ async fetchPagePermissions (path) {
+ if (path.startsWith('/_')) {
+ this.pagePermissions = []
+ return
+ }
+ const siteStore = useSiteStore()
+ try {
+ this.pagePermissions = await API_CLIENT.post(`sites/${siteStore.id}/pages/userPermissions`, {
+ json: {
+ path
+ }
+ }).json()
+ } catch (err) {
+ console.warn(`Failed to fetch page permissions at path ${path}!`)
+ }
+ },
+ formatDateTime (t, date) {
+ return (typeof date === 'string' ? DateTime.fromISO(date) : date).toFormat(t('common.datetime', { date: this.preferredDateFormat, time: this.preferredTimeFormat }))
+ }
+ }
+})
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
new file mode 100644
index 00000000..71399ae0
--- /dev/null
+++ b/frontend/vite.config.js
@@ -0,0 +1,87 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue'
+import yaml from 'js-yaml'
+import fs from 'node:fs'
+import { fileURLToPath } from 'node:url'
+import { quasar, transformAssetUrls } from '@quasar/vite-plugin'
+import vueDevTools from 'vite-plugin-vue-devtools'
+
+// https://vitejs.dev/config/
+export default defineConfig(({ mode }) => {
+ const userConfig = mode === 'development' ? {
+ dev: { port: 3001, hmrClientPort: 3001 },
+ ...yaml.load(fs.readFileSync(fileURLToPath(new URL('../config.yml', import.meta.url)), 'utf8'))
+ } : {}
+
+ return {
+ build: {
+ assetsDir: '_assets',
+ chunkSizeWarningLimit: 5000,
+ dynamicImportVarsOptions: {
+ warnOnError: true,
+ include: ['!/_blocks/**']
+ },
+ outDir: '../assets',
+ target: 'es2022',
+ ...(mode === 'production') && {
+ rollupOptions: {
+ output: {
+ manualChunks (id) {
+ if (id.includes('lodash')) {
+ return 'lodash'
+ // } else if (id.includes('quasar')) {
+ // return 'quasar'
+ } else if (id.includes('pages/Admin')) {
+ return 'admin'
+ } else if (id.includes('pages/Profile')) {
+ return 'profile'
+ }
+ }
+ }
+ }
+ }
+ },
+ optimizeDeps: {
+ include: [
+ 'prosemirror-state',
+ 'prosemirror-transform',
+ 'prosemirror-model',
+ 'prosemirror-view'
+ ]
+ },
+ plugins: [
+ vue({
+ template: { transformAssetUrls }
+ }),
+ quasar({
+ autoImportComponentCase: 'kebab',
+ sassVariables: '@/css/_theme.scss'
+ }),
+ vueDevTools()
+ ],
+ resolve: {
+ alias: {
+ '@': fileURLToPath(new URL('./src', import.meta.url))
+ }
+ },
+ server: {
+ // https: true
+ open: false, // opens browser window automatically
+ host: '0.0.0.0',
+ allowedHosts: true,
+ port: userConfig.dev?.port,
+ proxy: ['_api', '_blocks', '_site', '_thumb', '_user'].reduce((result, key) => {
+ result[`/${key}`] = {
+ target: {
+ host: '127.0.0.1',
+ port: userConfig.port
+ }
+ }
+ return result
+ }, {}),
+ hmr: {
+ clientPort: userConfig.dev?.hmrClientPort
+ }
+ }
+ }
+})