feat: page alias, theme layout options, code block themes

pull/6775/head
NGPixel 2 years ago
parent 80b1cbff5c
commit 3da8fb417c
No known key found for this signature in database
GPG Key ID: B755FB6870B30F63

@ -168,7 +168,7 @@ export async function up (knex) {
table.boolean('isRTL').notNullable().defaultTo(false) table.boolean('isRTL').notNullable().defaultTo(false)
table.string('name').notNullable() table.string('name').notNullable()
table.string('nativeName').notNullable() table.string('nativeName').notNullable()
table.integer('availability').notNullable().defaultTo(0) table.integer('completeness').notNullable().defaultTo(0)
table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now()) table.timestamp('createdAt').notNullable().defaultTo(knex.fn.now())
table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now()) table.timestamp('updatedAt').notNullable().defaultTo(knex.fn.now())
}) })
@ -606,6 +606,7 @@ export async function up (knex) {
}, },
theme: { theme: {
dark: false, dark: false,
codeBlocksTheme: 'github-dark',
colorPrimary: '#1976D2', colorPrimary: '#1976D2',
colorSecondary: '#02C39A', colorSecondary: '#02C39A',
colorAccent: '#FF9800', colorAccent: '#FF9800',

@ -187,6 +187,31 @@ export default {
throw new Error('ERR_PAGE_NOT_FOUND') throw new Error('ERR_PAGE_NOT_FOUND')
} }
}, },
/**
* FETCH PATH FROM ALIAS
*/
async pathFromAlias (obj, args, context, info) {
const alias = args.alias?.trim()
if (!alias) {
throw new Error('ERR_ALIAS_MISSING')
}
if (!WIKI.sites[args.siteId]) {
throw new Error('ERR_INVALID_SITE_ID')
}
const page = await WIKI.db.pages.query().findOne({
alias: args.alias,
siteId: args.siteId
}).select('id', 'path', 'localeCode')
if (!page) {
throw new Error('ERR_ALIAS_NOT_FOUND')
}
return {
id: page.id,
path: WIKI.sites[args.siteId].config.localeNamespacing ? `${page.localeCode}/${page.path}` : page.path
}
},
/** /**
* FETCH TAGS * FETCH TAGS
*/ */

@ -41,12 +41,6 @@ extend type Query {
password: String password: String
): Page ): Page
tags: [PageTag]!
searchTags(
query: String!
): [String]!
pageTree( pageTree(
path: String path: String
parent: Int parent: Int
@ -59,6 +53,17 @@ extend type Query {
locale: String! locale: String!
): [PageLinkItem] ): [PageLinkItem]
pathFromAlias(
siteId: UUID!
alias: String!
): PageAliasPath
searchTags(
query: String!
): [String]!
tags: [PageTag]!
checkConflicts( checkConflicts(
id: Int! id: Int!
checkoutDate: Date! checkoutDate: Date!
@ -71,6 +76,7 @@ extend type Query {
extend type Mutation { extend type Mutation {
createPage( createPage(
alias: String
allowComments: Boolean allowComments: Boolean
allowContributions: Boolean allowContributions: Boolean
allowRatings: Boolean allowRatings: Boolean
@ -165,6 +171,7 @@ type PageMigrationResponse {
} }
type Page { type Page {
alias: String
allowComments: Boolean allowComments: Boolean
allowContributions: Boolean allowContributions: Boolean
allowRatings: Boolean allowRatings: Boolean
@ -329,6 +336,7 @@ input PageRelationInput {
} }
input PageUpdateInput { input PageUpdateInput {
alias: String
allowComments: Boolean allowComments: Boolean
allowContributions: Boolean allowContributions: Boolean
allowRatings: Boolean allowRatings: Boolean
@ -355,6 +363,11 @@ input PageUpdateInput {
tocDepth: PageTocDepthInput tocDepth: PageTocDepthInput
} }
type PageAliasPath {
id: UUID
path: String
}
type PageTocDepth { type PageTocDepth {
min: Int min: Int
max: Int max: Int

@ -118,6 +118,7 @@ type SiteEditor {
type SiteTheme { type SiteTheme {
dark: Boolean dark: Boolean
codeBlocksTheme: String
colorPrimary: String colorPrimary: String
colorSecondary: String colorSecondary: String
colorAccent: String colorAccent: String
@ -210,6 +211,7 @@ input SiteDefaultsInput {
input SiteThemeInput { input SiteThemeInput {
dark: Boolean dark: Boolean
codeBlocksTheme: String
colorPrimary: String colorPrimary: String
colorSecondary: String colorSecondary: String
colorAccent: String colorAccent: String

File diff suppressed because it is too large Load Diff

@ -0,0 +1,9 @@
[
{
"code": "en",
"name": "English",
"nativeName": "English",
"rtl": false,
"completeness": 100
}
]

@ -19,7 +19,7 @@ export class Locale extends Model {
nativeName: {type: 'string'}, nativeName: {type: 'string'},
createdAt: {type: 'string'}, createdAt: {type: 'string'},
updatedAt: {type: 'string'}, updatedAt: {type: 'string'},
availability: {type: 'integer'} completeness: {type: 'integer'}
} }
} }
} }

@ -19,6 +19,7 @@ import { Tag } from './tags.mjs'
import { User } from './users.mjs' import { User } from './users.mjs'
const pageRegex = /^[a-zA0-90-9-_/]*$/ const pageRegex = /^[a-zA0-90-9-_/]*$/
const aliasRegex = /^[a-zA0-90-9-_]*$/
const frontmatterRegex = { const frontmatterRegex = {
html: /^(<!-{2}(?:\n|\r)([\w\W]+?)(?:\n|\r)-{2}>)?(?:\n|\r)*([\w\W]*)*/, html: /^(<!-{2}(?:\n|\r)([\w\W]+?)(?:\n|\r)-{2}>)?(?:\n|\r)*([\w\W]*)*/,
@ -236,7 +237,7 @@ export class Page extends Model {
static async createPage(opts) { static async createPage(opts) {
// -> Validate site // -> Validate site
if (!WIKI.sites[opts.siteId]) { if (!WIKI.sites[opts.siteId]) {
throw new WIKI.Error.Custom('InvalidSiteId', 'Site ID is invalid.') throw new Error('ERR_INVALID_SITE_ID')
} }
// -> Remove trailing slash // -> Remove trailing slash
@ -262,13 +263,31 @@ export class Page extends Model {
locale: opts.locale, locale: opts.locale,
path: opts.path path: opts.path
})) { })) {
throw new WIKI.Error.PageDeleteForbidden() throw new Error('ERR_FORBIDDEN')
} }
// -> Check for duplicate // -> Check for duplicate
const dupCheck = await WIKI.db.pages.query().select('id').where('localeCode', opts.locale).where('path', opts.path).first() const dupCheck = await WIKI.db.pages.query().findOne({
siteId: opts.siteId,
localeCode: opts.locale,
path: opts.path
}).select('id')
if (dupCheck) { if (dupCheck) {
throw new WIKI.Error.PageDuplicateCreate() throw new Error('ERR_PAGE_DUPLICATE_PATH')
}
// -> Check for alias
if (opts.alias) {
if (!aliasRegex.test(opts.alias)) {
throw new Error('ERR_PAGE_INVALID_ALIAS')
}
const dupAliasCheck = await WIKI.db.pages.query().findOne({
siteId: opts.siteId,
alias: opts.alias
}).select('id')
if (dupAliasCheck) {
throw new Error('ERR_PAGE_DUPLICATE_ALIAS')
}
} }
// -> Check for empty content // -> Check for empty content
@ -302,6 +321,7 @@ export class Page extends Model {
// -> Create page // -> Create page
const page = await WIKI.db.pages.query().insert({ const page = await WIKI.db.pages.query().insert({
alias: opts.alias,
authorId: opts.user.id, authorId: opts.user.id,
content: opts.content, content: opts.content,
creatorId: opts.user.id, creatorId: opts.user.id,
@ -449,6 +469,24 @@ export class Page extends Model {
historyData.affectedFields.push('icon') historyData.affectedFields.push('icon')
} }
if ('alias' in opts.patch) {
patch.alias = opts.patch.alias.trim()
historyData.affectedFields.push('alias')
if (patch.alias.length > 255) {
throw new Error('ERR_PAGE_ALIAS_TOO_LONG')
} else if (!aliasRegex.test(patch.alias)) {
throw new Error('ERR_PAGE_INVALID_ALIAS')
}
const dupAliasCheck = await WIKI.db.pages.query().where({
siteId: ogPage.siteId,
alias: patch.alias
}).andWhereNot('id', ogPage.id).select('id').first()
if (dupAliasCheck) {
throw new Error('ERR_PAGE_DUPLICATE_ALIAS')
}
}
if ('content' in opts.patch) { if ('content' in opts.patch) {
patch.content = opts.patch.content patch.content = opts.patch.content
historyData.affectedFields.push('content') historyData.affectedFields.push('content')

@ -91,6 +91,7 @@ export class Site extends Model {
}, },
theme: { theme: {
dark: false, dark: false,
codeBlocksTheme: 'github-dark',
colorPrimary: '#1976D2', colorPrimary: '#1976D2',
colorSecondary: '#02C39A', colorSecondary: '#02C39A',
colorAccent: '#FF9800', colorAccent: '#FF9800',

@ -1,4 +1,4 @@
title: Git title: Local Git
icon: '/_assets/icons/ultraviolet-git.svg' icon: '/_assets/icons/ultraviolet-git.svg'
banner: '/_assets/storage/git.jpg' banner: '/_assets/storage/git.jpg'
description: Git is a version control system for tracking changes in computer files and coordinating work on those files among multiple people. If using GitHub, use the GitHub module instead! description: Git is a version control system for tracking changes in computer files and coordinating work on those files among multiple people. If using GitHub, use the GitHub module instead!

File diff suppressed because it is too large Load Diff

@ -34,10 +34,10 @@
"node": ">=18.0" "node": ">=18.0"
}, },
"dependencies": { "dependencies": {
"@azure/storage-blob": "12.13.0", "@azure/storage-blob": "12.14.0",
"@exlinc/keycloak-passport": "1.0.2", "@exlinc/keycloak-passport": "1.0.2",
"@graphql-tools/schema": "9.0.17", "@graphql-tools/schema": "10.0.0",
"@graphql-tools/utils": "9.2.1", "@graphql-tools/utils": "10.0.0",
"@joplin/turndown-plugin-gfm": "1.0.47", "@joplin/turndown-plugin-gfm": "1.0.47",
"@root/csr": "0.8.1", "@root/csr": "0.8.1",
"@root/keypairs": "0.10.3", "@root/keypairs": "0.10.3",
@ -48,7 +48,7 @@
"apollo-server": "3.6.7", "apollo-server": "3.6.7",
"apollo-server-express": "3.6.7", "apollo-server-express": "3.6.7",
"auto-load": "3.0.4", "auto-load": "3.0.4",
"aws-sdk": "2.1353.0", "aws-sdk": "2.1381.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"body-parser": "1.20.2", "body-parser": "1.20.2",
"chalk": "5.2.0", "chalk": "5.2.0",
@ -66,34 +66,34 @@
"custom-error-instance": "2.1.2", "custom-error-instance": "2.1.2",
"dependency-graph": "0.11.0", "dependency-graph": "0.11.0",
"diff": "5.1.0", "diff": "5.1.0",
"diff2html": "3.4.34", "diff2html": "3.4.35",
"dompurify": "3.0.1", "dompurify": "3.0.3",
"dotize": "0.3.0", "dotize": "0.3.0",
"emoji-regex": "10.2.1", "emoji-regex": "10.2.1",
"eventemitter2": "6.4.9", "eventemitter2": "6.4.9",
"express": "4.18.2", "express": "4.18.2",
"express-brute": "1.0.1", "express-brute": "1.0.1",
"express-session": "1.17.3", "express-session": "1.17.3",
"file-type": "18.2.1", "file-type": "18.4.0",
"filesize": "10.0.7", "filesize": "10.0.7",
"fs-extra": "11.1.1", "fs-extra": "11.1.1",
"getos": "3.2.1", "getos": "3.2.1",
"graphql": "16.6.0", "graphql": "16.6.0",
"graphql-list-fields": "2.0.2", "graphql-list-fields": "2.0.2",
"graphql-rate-limit-directive": "2.0.3", "graphql-rate-limit-directive": "2.0.3",
"graphql-tools": "8.3.19", "graphql-tools": "9.0.0",
"graphql-upload": "16.0.2", "graphql-upload": "16.0.2",
"he": "1.2.0", "he": "1.2.0",
"highlight.js": "11.7.0", "highlight.js": "11.8.0",
"i18next": "22.4.14", "i18next": "22.5.0",
"i18next-node-fs-backend": "2.1.3", "i18next-node-fs-backend": "2.1.3",
"image-size": "1.0.2", "image-size": "1.0.2",
"js-base64": "3.7.5", "js-base64": "3.7.5",
"js-binary": "1.2.0", "js-binary": "1.2.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"jsdom": "21.1.1", "jsdom": "22.0.0",
"jsonwebtoken": "9.0.0", "jsonwebtoken": "9.0.0",
"katex": "0.16.4", "katex": "0.16.7",
"klaw": "4.1.0", "klaw": "4.1.0",
"knex": "2.4.2", "knex": "2.4.2",
"lodash": "4.17.21", "lodash": "4.17.21",
@ -108,7 +108,7 @@
"markdown-it-footnote": "3.0.3", "markdown-it-footnote": "3.0.3",
"markdown-it-imsize": "2.0.1", "markdown-it-imsize": "2.0.1",
"markdown-it-mark": "3.0.1", "markdown-it-mark": "3.0.1",
"markdown-it-multimd-table": "4.2.1", "markdown-it-multimd-table": "4.2.2",
"markdown-it-sub": "1.0.0", "markdown-it-sub": "1.0.0",
"markdown-it-sup": "1.0.0", "markdown-it-sup": "1.0.0",
"markdown-it-task-lists": "2.1.1", "markdown-it-task-lists": "2.1.1",
@ -119,11 +119,11 @@
"nanoid": "4.0.2", "nanoid": "4.0.2",
"node-2fa": "2.0.3", "node-2fa": "2.0.3",
"node-cache": "5.1.2", "node-cache": "5.1.2",
"nodemailer": "6.9.1", "nodemailer": "6.9.2",
"objection": "3.0.1", "objection": "3.0.1",
"passport": "0.6.0", "passport": "0.6.0",
"passport-auth0": "1.4.3", "passport-auth0": "1.4.3",
"passport-azure-ad": "4.3.4", "passport-azure-ad": "4.3.5",
"passport-cas": "0.1.1", "passport-cas": "0.1.1",
"passport-discord": "0.1.4", "passport-discord": "0.1.4",
"passport-dropbox-oauth2": "1.1.0", "passport-dropbox-oauth2": "1.1.0",
@ -142,14 +142,14 @@
"passport-slack-oauth2": "1.1.1", "passport-slack-oauth2": "1.1.1",
"passport-twitch-strategy": "2.2.0", "passport-twitch-strategy": "2.2.0",
"pem-jwk": "2.0.0", "pem-jwk": "2.0.0",
"pg": "8.10.0", "pg": "8.11.0",
"pg-hstore": "2.3.4", "pg-hstore": "2.3.4",
"pg-pubsub": "0.8.1", "pg-pubsub": "0.8.1",
"pg-query-stream": "4.4.0", "pg-query-stream": "4.5.0",
"pg-tsquery": "8.4.1", "pg-tsquery": "8.4.1",
"poolifier": "2.4.4", "poolifier": "2.4.14",
"punycode": "2.3.0", "punycode": "2.3.0",
"puppeteer-core": "19.8.5", "puppeteer-core": "20.2.1",
"qr-image": "3.2.0", "qr-image": "3.2.0",
"rate-limiter-flexible": "2.4.1", "rate-limiter-flexible": "2.4.1",
"remove-markdown": "0.5.0", "remove-markdown": "0.5.0",
@ -158,12 +158,12 @@
"safe-regex": "2.1.1", "safe-regex": "2.1.1",
"sanitize-filename": "1.6.3", "sanitize-filename": "1.6.3",
"scim-query-filter-parser": "2.0.4", "scim-query-filter-parser": "2.0.4",
"semver": "7.3.8", "semver": "7.5.1",
"serve-favicon": "2.5.0", "serve-favicon": "2.5.0",
"sharp": "0.32.0", "sharp": "0.32.1",
"simple-git": "3.17.0", "simple-git": "3.18.0",
"socket.io": "4.6.1", "socket.io": "4.6.1",
"ssh2": "1.11.0", "ssh2": "1.13.0",
"ssh2-promise": "1.0.3", "ssh2-promise": "1.0.3",
"striptags": "3.2.0", "striptags": "3.2.0",
"tar-fs": "2.1.1", "tar-fs": "2.1.1",
@ -173,10 +173,10 @@
"uuid": "9.0.0", "uuid": "9.0.0",
"validate.js": "0.13.1", "validate.js": "0.13.1",
"xss": "1.0.14", "xss": "1.0.14",
"yargs": "17.7.1" "yargs": "17.7.2"
}, },
"devDependencies": { "devDependencies": {
"eslint": "8.38.0", "eslint": "8.41.0",
"eslint-config-requarks": "1.0.7", "eslint-config-requarks": "1.0.7",
"eslint-config-standard": "17.0.0", "eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.27.5",

@ -54,7 +54,7 @@ export async function render (input, config) {
const highlighted = lang ? hljs.highlight(str, { language: lang, ignoreIllegals: true }) : { value: str } const highlighted = lang ? hljs.highlight(str, { language: lang, ignoreIllegals: true }) : { value: str }
const lineCount = highlighted.value.match(/\n/g).length const lineCount = highlighted.value.match(/\n/g).length
const lineNums = lineCount > 1 ? `<span aria-hidden="true" class="line-numbers-rows">${times(lineCount, n => '<span></span>').join('')}</span>` : '' const lineNums = lineCount > 1 ? `<span aria-hidden="true" class="line-numbers-rows">${times(lineCount, n => '<span></span>').join('')}</span>` : ''
return `<pre class="codeblock ${lineCount > 1 && 'line-numbers'}"><code class="language-${lang}">${highlighted.value}${lineNums}</code></pre>` return `<pre class="codeblock hljs ${lineCount > 1 && 'line-numbers'}"><code class="language-${lang}">${highlighted.value}${lineNums}</code></pre>`
} }
} }
}) })

1444
ux/package-lock.json generated

File diff suppressed because it is too large Load Diff

@ -13,37 +13,37 @@
"ncu-u": "ncu -u -x codemirror,codemirror-asciidoc" "ncu-u": "ncu -u -x codemirror,codemirror-asciidoc"
}, },
"dependencies": { "dependencies": {
"@apollo/client": "3.7.11", "@apollo/client": "3.7.14",
"@lezer/common": "1.0.2", "@lezer/common": "1.0.2",
"@mdi/font": "7.2.96", "@mdi/font": "7.2.96",
"@quasar/extras": "1.16.2", "@quasar/extras": "1.16.4",
"@tiptap/core": "2.0.2", "@tiptap/core": "2.0.3",
"@tiptap/extension-code-block": "2.0.2", "@tiptap/extension-code-block": "2.0.3",
"@tiptap/extension-code-block-lowlight": "2.0.2", "@tiptap/extension-code-block-lowlight": "2.0.3",
"@tiptap/extension-color": "2.0.2", "@tiptap/extension-color": "2.0.3",
"@tiptap/extension-dropcursor": "2.0.2", "@tiptap/extension-dropcursor": "2.0.3",
"@tiptap/extension-font-family": "2.0.2", "@tiptap/extension-font-family": "2.0.3",
"@tiptap/extension-gapcursor": "2.0.2", "@tiptap/extension-gapcursor": "2.0.3",
"@tiptap/extension-hard-break": "2.0.2", "@tiptap/extension-hard-break": "2.0.3",
"@tiptap/extension-highlight": "2.0.2", "@tiptap/extension-highlight": "2.0.3",
"@tiptap/extension-history": "2.0.2", "@tiptap/extension-history": "2.0.3",
"@tiptap/extension-image": "2.0.2", "@tiptap/extension-image": "2.0.3",
"@tiptap/extension-mention": "2.0.2", "@tiptap/extension-mention": "2.0.3",
"@tiptap/extension-placeholder": "2.0.2", "@tiptap/extension-placeholder": "2.0.3",
"@tiptap/extension-table": "2.0.2", "@tiptap/extension-table": "2.0.3",
"@tiptap/extension-table-cell": "2.0.2", "@tiptap/extension-table-cell": "2.0.3",
"@tiptap/extension-table-header": "2.0.2", "@tiptap/extension-table-header": "2.0.3",
"@tiptap/extension-table-row": "2.0.2", "@tiptap/extension-table-row": "2.0.3",
"@tiptap/extension-task-item": "2.0.2", "@tiptap/extension-task-item": "2.0.3",
"@tiptap/extension-task-list": "2.0.2", "@tiptap/extension-task-list": "2.0.3",
"@tiptap/extension-text-align": "2.0.2", "@tiptap/extension-text-align": "2.0.3",
"@tiptap/extension-text-style": "2.0.2", "@tiptap/extension-text-style": "2.0.3",
"@tiptap/extension-typography": "2.0.2", "@tiptap/extension-typography": "2.0.3",
"@tiptap/pm": "2.0.2", "@tiptap/pm": "2.0.3",
"@tiptap/starter-kit": "2.0.2", "@tiptap/starter-kit": "2.0.3",
"@tiptap/vue-3": "2.0.2", "@tiptap/vue-3": "2.0.3",
"apollo-upload-client": "17.0.0", "apollo-upload-client": "17.0.0",
"browser-fs-access": "0.33.0", "browser-fs-access": "0.34.0",
"clipboard": "2.0.11", "clipboard": "2.0.11",
"codemirror": "5.65.11", "codemirror": "5.65.11",
"codemirror-asciidoc": "1.0.4", "codemirror-asciidoc": "1.0.4",
@ -53,12 +53,12 @@
"fuse.js": "6.6.2", "fuse.js": "6.6.2",
"graphql": "16.6.0", "graphql": "16.6.0",
"graphql-tag": "2.12.6", "graphql-tag": "2.12.6",
"highlight.js": "11.7.0", "highlight.js": "11.8.0",
"js-cookie": "3.0.1", "js-cookie": "3.0.5",
"jwt-decode": "3.1.2", "jwt-decode": "3.1.2",
"katex": "0.16.4", "katex": "0.16.7",
"lodash-es": "4.17.21", "lodash-es": "4.17.21",
"lowlight": "2.8.1", "lowlight": "2.9.0",
"luxon": "3.3.0", "luxon": "3.3.0",
"markdown-it": "13.0.1", "markdown-it": "13.0.1",
"markdown-it-abbr": "1.0.4", "markdown-it-abbr": "1.0.4",
@ -69,34 +69,34 @@
"markdown-it-footnote": "3.0.3", "markdown-it-footnote": "3.0.3",
"markdown-it-imsize": "2.0.1", "markdown-it-imsize": "2.0.1",
"markdown-it-mark": "3.0.1", "markdown-it-mark": "3.0.1",
"markdown-it-multimd-table": "4.2.1", "markdown-it-multimd-table": "4.2.2",
"markdown-it-sub": "1.0.0", "markdown-it-sub": "1.0.0",
"markdown-it-sup": "1.0.0", "markdown-it-sup": "1.0.0",
"markdown-it-task-lists": "2.1.1", "markdown-it-task-lists": "2.1.1",
"mitt": "3.0.0", "mitt": "3.0.0",
"monaco-editor": "0.37.1", "monaco-editor": "0.38.0",
"pako": "2.1.0", "pako": "2.1.0",
"pinia": "2.0.33", "pinia": "2.1.3",
"prosemirror-commands": "1.5.1", "prosemirror-commands": "1.5.2",
"prosemirror-history": "1.3.0", "prosemirror-history": "1.3.2",
"prosemirror-keymap": "1.2.1", "prosemirror-keymap": "1.2.2",
"prosemirror-model": "1.19.0", "prosemirror-model": "1.19.1",
"prosemirror-schema-list": "1.2.2", "prosemirror-schema-list": "1.2.3",
"prosemirror-state": "1.4.2", "prosemirror-state": "1.4.3",
"prosemirror-transform": "1.7.1", "prosemirror-transform": "1.7.2",
"prosemirror-view": "1.30.2", "prosemirror-view": "1.31.3",
"pug": "3.0.2", "pug": "3.0.2",
"quasar": "2.11.10", "quasar": "2.12.0",
"slugify": "1.6.6", "slugify": "1.6.6",
"socket.io-client": "4.6.1", "socket.io-client": "4.6.1",
"tabulator-tables": "5.4.4", "tabulator-tables": "5.4.4",
"tippy.js": "6.3.7", "tippy.js": "6.3.7",
"twemoji": "14.0.2", "twemoji": "14.0.2",
"uuid": "9.0.0", "uuid": "9.0.0",
"v-network-graph": "0.9.1", "v-network-graph": "0.9.3",
"vue": "3.2.47", "vue": "3.3.4",
"vue-i18n": "9.2.2", "vue-i18n": "9.2.2",
"vue-router": "4.1.6", "vue-router": "4.2.1",
"vue3-otp-input": "0.4.1", "vue3-otp-input": "0.4.1",
"vuedraggable": "4.1.0", "vuedraggable": "4.1.0",
"xterm": "5.1.0", "xterm": "5.1.0",
@ -104,17 +104,17 @@
}, },
"devDependencies": { "devDependencies": {
"@intlify/unplugin-vue-i18n": "0.10.0", "@intlify/unplugin-vue-i18n": "0.10.0",
"@quasar/app-vite": "1.2.1", "@quasar/app-vite": "1.4.3",
"@types/lodash": "4.14.192", "@types/lodash": "4.14.194",
"@volar/vue-language-plugin-pug": "1.2.0", "@volar/vue-language-plugin-pug": "1.6.5",
"autoprefixer": "10.4.14", "autoprefixer": "10.4.14",
"browserlist": "latest", "browserlist": "latest",
"eslint": "8.37.0", "eslint": "8.41.0",
"eslint-config-standard": "17.0.0", "eslint-config-standard": "17.0.0",
"eslint-plugin-import": "2.27.5", "eslint-plugin-import": "2.27.5",
"eslint-plugin-n": "15.7.0", "eslint-plugin-n": "16.0.0",
"eslint-plugin-promise": "6.1.1", "eslint-plugin-promise": "6.1.1",
"eslint-plugin-vue": "9.10.0" "eslint-plugin-vue": "9.13.0"
}, },
"engines": { "engines": {
"node": ">= 18.0", "node": ">= 18.0",

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 40 40" width="80px" height="80px"><path fill="none" stroke="#4788c7" stroke-linecap="round" stroke-miterlimit="10" stroke-width="2" d="M10 11L1.75 20 10 29M30 11L38.25 20 30 29M15 35L25 5"/></svg>

After

Width:  |  Height:  |  Size: 250 B

@ -75,7 +75,7 @@ module.exports = configure(function (/* ctx */) {
vueRouterMode: 'history', // available values: 'hash', 'history' vueRouterMode: 'history', // available values: 'hash', 'history'
// vueRouterBase, // vueRouterBase,
// vueDevtools, // vueDevtools,
// vueOptionsAPI: true, vueOptionsAPI: false,
rebuildCache: true, // rebuilds Vite/linter/etc cache on startup rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
@ -90,28 +90,21 @@ module.exports = configure(function (/* ctx */) {
extendViteConf (viteConf) { extendViteConf (viteConf) {
viteConf.build.assetsDir = '_assets' viteConf.build.assetsDir = '_assets'
// viteConf.resolve.alias.vue = '/workspace/ux/node_modules/vue/dist/vue.esm-bundler.js' viteConf.build.rollupOptions = {
// viteConf.build.rollupOptions = { ...viteConf.build.rollupOptions ?? {},
// ...viteConf.build.rollupOptions ?? {}, output: {
// external: [ manualChunks: {
// /^\/_site\// lodash: ['lodash-es', 'lodash'],
// ] quasar: ['quasar', 'quasar/src/components']
// } }
}
}
viteConf.optimizeDeps.include = [ viteConf.optimizeDeps.include = [
'prosemirror-state', 'prosemirror-state',
'prosemirror-transform', 'prosemirror-transform',
'prosemirror-model', 'prosemirror-model',
'prosemirror-view' 'prosemirror-view'
] ]
// viteConf.build.rollupOptions = {
// external: ['monaco-editor'],
// output: {
// globals: {
// 'monaco-editor': 'monaco-editor'
// }
// }
// }
}, },
// viteVuePluginOptions: {}, // viteVuePluginOptions: {},
@ -139,7 +132,8 @@ module.exports = configure(function (/* ctx */) {
}, },
hmr: { hmr: {
clientPort: userConfig.dev.hmrClientPort clientPort: userConfig.dev.hmrClientPort
} },
vueDevtools: true
}, },
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework // https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework

@ -55,12 +55,15 @@ watch(() => userStore.cvd, () => {
// THEME // THEME
function applyTheme () { async function applyTheme () {
// -> Dark Mode
if (userStore.appearance === 'site') { if (userStore.appearance === 'site') {
$q.dark.set(siteStore.theme.dark) $q.dark.set(siteStore.theme.dark)
} else { } else {
$q.dark.set(userStore.appearance === 'dark') $q.dark.set(userStore.appearance === 'dark')
} }
// -> CSS Vars
setCssVar('primary', userStore.getAccessibleColor('primary', siteStore.theme.colorPrimary)) setCssVar('primary', userStore.getAccessibleColor('primary', siteStore.theme.colorPrimary))
setCssVar('secondary', userStore.getAccessibleColor('secondary', siteStore.theme.colorSecondary)) setCssVar('secondary', userStore.getAccessibleColor('secondary', siteStore.theme.colorSecondary))
setCssVar('accent', userStore.getAccessibleColor('accent', siteStore.theme.colorAccent)) setCssVar('accent', userStore.getAccessibleColor('accent', siteStore.theme.colorAccent))
@ -68,6 +71,21 @@ function applyTheme () {
setCssVar('sidebar', userStore.getAccessibleColor('sidebar', siteStore.theme.colorSidebar)) setCssVar('sidebar', userStore.getAccessibleColor('sidebar', siteStore.theme.colorSidebar))
setCssVar('positive', userStore.getAccessibleColor('positive', '#02C39A')) setCssVar('positive', userStore.getAccessibleColor('positive', '#02C39A'))
setCssVar('negative', userStore.getAccessibleColor('negative', '#f03a47')) setCssVar('negative', userStore.getAccessibleColor('negative', '#f03a47'))
// -> Highlight.js Theme
if (siteStore.theme.codeBlocksTheme) {
const desiredHljsTheme = userStore.cvd !== 'none' ? 'github' : siteStore.theme.codeBlocksTheme
const hljsStyleEl = document.querySelector('#hljs-theme')
if (hljsStyleEl) {
hljsStyleEl.remove()
}
const newHljsStyleEl = document.createElement('style')
newHljsStyleEl.id = 'hljs-theme'
newHljsStyleEl.innerHTML = (await import(`../node_modules/highlight.js/styles/${desiredHljsTheme}.css`)).default
document.head.appendChild(newHljsStyleEl)
}
} }
// INIT SITE STORE // INIT SITE STORE

@ -92,6 +92,7 @@
) )
q-tooltip Bookmark Page q-tooltip Bookmark Page
q-btn.q-ml-md( q-btn.q-ml-md(
v-if='siteStore.theme.showSharingMenu'
flat flat
dense dense
icon='las la-share-alt' icon='las la-share-alt'
@ -101,6 +102,7 @@
q-tooltip Share q-tooltip Share
social-sharing-menu social-sharing-menu
q-btn.q-ml-md( q-btn.q-ml-md(
v-if='siteStore.theme.showPrintBtn'
flat flat
dense dense
icon='las la-print' icon='las la-print'
@ -297,7 +299,8 @@ async function saveChangesCommit (closeAfter = false) {
} catch (err) { } catch (err) {
$q.notify({ $q.notify({
type: 'negative', type: 'negative',
message: 'Failed to save page changes.' message: 'Failed to save page changes.',
caption: err.message
}) })
} }
$q.loading.hide() $q.loading.hide()

@ -62,6 +62,13 @@ q-card.page-properties-dialog
name='las la-icons' name='las la-icons'
color='primary' color='primary'
) )
q-input(
v-model='pageStore.alias'
:label='t(`editor.props.alias`)'
outlined
dense
prefix='/a/'
)
q-card-section.alt-card(id='refCardPublishState') 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')}} .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 q-form.q-gutter-md

@ -15,7 +15,7 @@
// --------------------------------- // ---------------------------------
a { a {
color: $blue-8; color: var(--q-primary);
&.is-internal-link.is-invalid-page { &.is-internal-link.is-invalid-page {
color: $red-8; color: $red-8;
@ -438,7 +438,7 @@
} }
} }
// --------------------------------- // ---------------------------------
// TASK LISTS // TASK LISTS
// --------------------------------- // ---------------------------------
@ -514,22 +514,17 @@
// } // }
pre.codeblock { pre.codeblock {
border: none; border: 1px solid rgba(0,0,0,.2);
border-radius: 5px; border-radius: 5px;
box-shadow: initial; box-shadow: initial;
background-color: $dark-5;
padding: 1rem; padding: 1rem;
margin: 1rem 0; margin: 1rem 0;
overflow: auto; overflow: auto;
@at-root .body--dark & {
background-color: $dark-5;
}
> code { > code {
background-color: transparent; // background-color: transparent;
padding: 0; padding: 0;
color: #FFF; // color: #FFF;
box-shadow: initial; box-shadow: initial;
display: block; display: block;
font-size: .85rem; font-size: .85rem;
@ -591,5 +586,3 @@
padding: 5px; padding: 5px;
} }
} }
@import 'highlight.js/styles/atom-one-dark-reasonable.css';

@ -815,6 +815,9 @@
"admin.theme.baseFontHint": "The font used across the site for the interface.", "admin.theme.baseFontHint": "The font used across the site for the interface.",
"admin.theme.bodyHtmlInjection": "Body HTML Injection", "admin.theme.bodyHtmlInjection": "Body HTML Injection",
"admin.theme.bodyHtmlInjectionHint": "HTML code to be injected just before the closing body tag.", "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.codeInjection": "Code Injection",
"admin.theme.contentFont": "Content Font", "admin.theme.contentFont": "Content Font",
"admin.theme.contentFontHint": "The font used specifically for page content.", "admin.theme.contentFontHint": "The font used specifically for page content.",
@ -1503,6 +1506,7 @@
"editor.pageRel.title": "Add Page Relation", "editor.pageRel.title": "Add Page Relation",
"editor.pageRel.titleEdit": "Edit Page Relation", "editor.pageRel.titleEdit": "Edit Page Relation",
"editor.pageScripts.title": "Page Scripts", "editor.pageScripts.title": "Page Scripts",
"editor.props.alias": "Alias",
"editor.props.allowComments": "Allow Comments", "editor.props.allowComments": "Allow Comments",
"editor.props.allowCommentsHint": "Enable commenting abilities on this page.", "editor.props.allowCommentsHint": "Enable commenting abilities on this page.",
"editor.props.allowContributions": "Allow Contributions", "editor.props.allowContributions": "Allow Contributions",

@ -3,8 +3,9 @@ q-layout(view='hHh Lpr lff')
header-nav header-nav
q-drawer.bg-sidebar( q-drawer.bg-sidebar(
:modelValue='isSidebarShown' :modelValue='isSidebarShown'
show-if-above :show-if-above='siteStore.theme.sidebarPosition !== `off`'
:width='255' :width='255'
:side='siteStore.theme.sidebarPosition === `right` ? `right` : `left`'
) )
.sidebar-actions.flex.items-stretch .sidebar-actions.flex.items-stretch
q-btn.q-px-sm.col( q-btn.q-px-sm.col(
@ -144,7 +145,7 @@ const barStyle = {
// COMPUTED // COMPUTED
const isSidebarShown = computed(() => { const isSidebarShown = computed(() => {
return siteStore.showSideNav && !(editorStore.isActive && editorStore.hideSideNav) return siteStore.showSideNav && !siteStore.sideNavIsDisabled && !(editorStore.isActive && editorStore.hideSideNav)
}) })
// METHODS // METHODS

@ -91,27 +91,63 @@ q-page.admin-theme
v-model='state.config[`color` + startCase(cl)]' 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 //- Theme Layout
//- ----------------------- //- -----------------------
q-card.q-pb-sm.q-mt-md q-card.q-pb-sm.q-mt-md
q-card-section q-card-section
.text-subtitle1 {{t('admin.theme.layout')}} .text-subtitle1 {{t('admin.theme.layout')}}
q-item template(v-if='flagStore.experimental')
blueprint-icon(icon='width') q-item
q-item-section blueprint-icon(icon='width')
q-item-label {{t(`admin.theme.contentWidth`)}} q-item-section
q-item-label(caption) {{t(`admin.theme.contentWidthHint`)}} q-item-label {{t(`admin.theme.contentWidth`)}}
q-item-section.col-auto q-item-label(caption) {{t(`admin.theme.contentWidthHint`)}}
q-btn-toggle( q-item-section.col-auto
v-model='state.config.contentWidth' q-btn-toggle(
push v-model='state.config.contentWidth'
glossy push
no-caps glossy
toggle-color='primary' no-caps
:options='widthOptions' toggle-color='primary'
) :options='widthOptions'
q-separator.q-my-sm(inset) )
q-separator.q-my-sm(inset)
q-item q-item
blueprint-icon(icon='right-navigation-toolbar') blueprint-icon(icon='right-navigation-toolbar')
q-item-section q-item-section
@ -274,6 +310,7 @@ import { setCssVar, useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue' import { onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin' import { useAdminStore } from 'src/stores/admin'
import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site' import { useSiteStore } from 'src/stores/site'
import UtilCodeEditor from '../components/UtilCodeEditor.vue' import UtilCodeEditor from '../components/UtilCodeEditor.vue'
@ -285,6 +322,7 @@ const $q = useQuasar()
// STORES // STORES
const adminStore = useAdminStore() const adminStore = useAdminStore()
const flagStore = useFlagsStore()
const siteStore = useSiteStore() const siteStore = useSiteStore()
// I18N // I18N
@ -311,6 +349,7 @@ const state = reactive({
colorAccent: '#f03a47', colorAccent: '#f03a47',
colorHeader: '#000', colorHeader: '#000',
colorSidebar: '#1976D2', colorSidebar: '#1976D2',
codeBlocksTheme: '',
contentWidth: 'full', contentWidth: 'full',
sidebarPosition: 'left', sidebarPosition: 'left',
tocPosition: 'right', tocPosition: 'right',
@ -350,6 +389,256 @@ const fonts = [
{ label: 'User System Defaults', value: 'user' } { label: 'User System Defaults', value: 'user' }
] ]
const codeThemes = [
{ label: 'A 11 Y Dark', value: 'a11y-dark' },
{ label: 'A 11 Y Light', value: 'a11y-light' },
{ label: 'Agate', value: 'agate' },
{ label: 'An Old Hope', value: 'an-old-hope' },
{ label: 'Androidstudio', value: 'androidstudio' },
{ label: 'Arduino Light', value: 'arduino-light' },
{ label: 'Arta', value: 'arta' },
{ label: 'Ascetic', value: 'ascetic' },
{ label: 'Atom One Dark', value: 'atom-one-dark' },
{ label: 'Atom One Dark Reasonable', value: 'atom-one-dark-reasonable' },
{ label: 'Atom One Light', value: 'atom-one-light' },
{ label: 'Base16 / 3024', value: 'base16/3024' },
{ label: 'Base16 / Apathy', value: 'base16/apathy' },
{ label: 'Base16 / Apprentice', value: 'base16/apprentice' },
{ label: 'Base16 / Ashes', value: 'base16/ashes' },
{ label: 'Base16 / Atelier Cave', value: 'base16/atelier-cave' },
{ label: 'Base16 / Atelier Cave Light', value: 'base16/atelier-cave-light' },
{ label: 'Base16 / Atelier Dune', value: 'base16/atelier-dune' },
{ label: 'Base16 / Atelier Dune Light', value: 'base16/atelier-dune-light' },
{ label: 'Base16 / Atelier Estuary', value: 'base16/atelier-estuary' },
{ label: 'Base16 / Atelier Estuary Light', value: 'base16/atelier-estuary-light' },
{ label: 'Base16 / Atelier Forest', value: 'base16/atelier-forest' },
{ label: 'Base16 / Atelier Forest Light', value: 'base16/atelier-forest-light' },
{ label: 'Base16 / Atelier Heath', value: 'base16/atelier-heath' },
{ label: 'Base16 / Atelier Heath Light', value: 'base16/atelier-heath-light' },
{ label: 'Base16 / Atelier Lakeside', value: 'base16/atelier-lakeside' },
{ label: 'Base16 / Atelier Lakeside Light', value: 'base16/atelier-lakeside-light' },
{ label: 'Base16 / Atelier Plateau', value: 'base16/atelier-plateau' },
{ label: 'Base16 / Atelier Plateau Light', value: 'base16/atelier-plateau-light' },
{ label: 'Base16 / Atelier Savanna', value: 'base16/atelier-savanna' },
{ label: 'Base16 / Atelier Savanna Light', value: 'base16/atelier-savanna-light' },
{ label: 'Base16 / Atelier Seaside', value: 'base16/atelier-seaside' },
{ label: 'Base16 / Atelier Seaside Light', value: 'base16/atelier-seaside-light' },
{ label: 'Base16 / Atelier Sulphurpool', value: 'base16/atelier-sulphurpool' },
{ label: 'Base16 / Atelier Sulphurpool Light', value: 'base16/atelier-sulphurpool-light' },
{ label: 'Base16 / Atlas', value: 'base16/atlas' },
{ label: 'Base16 / Bespin', value: 'base16/bespin' },
{ label: 'Base16 / Black Metal', value: 'base16/black-metal' },
{ label: 'Base16 / Black Metal Bathory', value: 'base16/black-metal-bathory' },
{ label: 'Base16 / Black Metal Burzum', value: 'base16/black-metal-burzum' },
{ label: 'Base16 / Black Metal Dark Funeral', value: 'base16/black-metal-dark-funeral' },
{ label: 'Base16 / Black Metal Gorgoroth', value: 'base16/black-metal-gorgoroth' },
{ label: 'Base16 / Black Metal Immortal', value: 'base16/black-metal-immortal' },
{ label: 'Base16 / Black Metal Khold', value: 'base16/black-metal-khold' },
{ label: 'Base16 / Black Metal Marduk', value: 'base16/black-metal-marduk' },
{ label: 'Base16 / Black Metal Mayhem', value: 'base16/black-metal-mayhem' },
{ label: 'Base16 / Black Metal Nile', value: 'base16/black-metal-nile' },
{ label: 'Base16 / Black Metal Venom', value: 'base16/black-metal-venom' },
{ label: 'Base16 / Brewer', value: 'base16/brewer' },
{ label: 'Base16 / Bright', value: 'base16/bright' },
{ label: 'Base16 / Brogrammer', value: 'base16/brogrammer' },
{ label: 'Base16 / Brush Trees', value: 'base16/brush-trees' },
{ label: 'Base16 / Brush Trees Dark', value: 'base16/brush-trees-dark' },
{ label: 'Base16 / Chalk', value: 'base16/chalk' },
{ label: 'Base16 / Circus', value: 'base16/circus' },
{ label: 'Base16 / Classic Dark', value: 'base16/classic-dark' },
{ label: 'Base16 / Classic Light', value: 'base16/classic-light' },
{ label: 'Base16 / Codeschool', value: 'base16/codeschool' },
{ label: 'Base16 / Colors', value: 'base16/colors' },
{ label: 'Base16 / Cupcake', value: 'base16/cupcake' },
{ label: 'Base16 / Cupertino', value: 'base16/cupertino' },
{ label: 'Base16 / Danqing', value: 'base16/danqing' },
{ label: 'Base16 / Darcula', value: 'base16/darcula' },
{ label: 'Base16 / Dark Violet', value: 'base16/dark-violet' },
{ label: 'Base16 / Darkmoss', value: 'base16/darkmoss' },
{ label: 'Base16 / Darktooth', value: 'base16/darktooth' },
{ label: 'Base16 / Decaf', value: 'base16/decaf' },
{ label: 'Base16 / Default Dark', value: 'base16/default-dark' },
{ label: 'Base16 / Default Light', value: 'base16/default-light' },
{ label: 'Base16 / Dirtysea', value: 'base16/dirtysea' },
{ label: 'Base16 / Dracula', value: 'base16/dracula' },
{ label: 'Base16 / Edge Dark', value: 'base16/edge-dark' },
{ label: 'Base16 / Edge Light', value: 'base16/edge-light' },
{ label: 'Base16 / Eighties', value: 'base16/eighties' },
{ label: 'Base16 / Embers', value: 'base16/embers' },
{ label: 'Base16 / Equilibrium Dark', value: 'base16/equilibrium-dark' },
{ label: 'Base16 / Equilibrium Gray Dark', value: 'base16/equilibrium-gray-dark' },
{ label: 'Base16 / Equilibrium Gray Light', value: 'base16/equilibrium-gray-light' },
{ label: 'Base16 / Equilibrium Light', value: 'base16/equilibrium-light' },
{ label: 'Base16 / Espresso', value: 'base16/espresso' },
{ label: 'Base16 / Eva', value: 'base16/eva' },
{ label: 'Base16 / Eva Dim', value: 'base16/eva-dim' },
{ label: 'Base16 / Flat', value: 'base16/flat' },
{ label: 'Base16 / Framer', value: 'base16/framer' },
{ label: 'Base16 / Fruit Soda', value: 'base16/fruit-soda' },
{ label: 'Base16 / Gigavolt', value: 'base16/gigavolt' },
{ label: 'Base16 / Github', value: 'base16/github' },
{ label: 'Base16 / Google Dark', value: 'base16/google-dark' },
{ label: 'Base16 / Google Light', value: 'base16/google-light' },
{ label: 'Base16 / Grayscale Dark', value: 'base16/grayscale-dark' },
{ label: 'Base16 / Grayscale Light', value: 'base16/grayscale-light' },
{ label: 'Base16 / Green Screen', value: 'base16/green-screen' },
{ label: 'Base16 / Gruvbox Dark Hard', value: 'base16/gruvbox-dark-hard' },
{ label: 'Base16 / Gruvbox Dark Medium', value: 'base16/gruvbox-dark-medium' },
{ label: 'Base16 / Gruvbox Dark Pale', value: 'base16/gruvbox-dark-pale' },
{ label: 'Base16 / Gruvbox Dark Soft', value: 'base16/gruvbox-dark-soft' },
{ label: 'Base16 / Gruvbox Light Hard', value: 'base16/gruvbox-light-hard' },
{ label: 'Base16 / Gruvbox Light Medium', value: 'base16/gruvbox-light-medium' },
{ label: 'Base16 / Gruvbox Light Soft', value: 'base16/gruvbox-light-soft' },
{ label: 'Base16 / Hardcore', value: 'base16/hardcore' },
{ label: 'Base16 / Harmonic 16 Dark', value: 'base16/harmonic16-dark' },
{ label: 'Base16 / Harmonic 16 Light', value: 'base16/harmonic16-light' },
{ label: 'Base16 / Heetch Dark', value: 'base16/heetch-dark' },
{ label: 'Base16 / Heetch Light', value: 'base16/heetch-light' },
{ label: 'Base16 / Helios', value: 'base16/helios' },
{ label: 'Base16 / Hopscotch', value: 'base16/hopscotch' },
{ label: 'Base16 / Horizon Dark', value: 'base16/horizon-dark' },
{ label: 'Base16 / Horizon Light', value: 'base16/horizon-light' },
{ label: 'Base16 / Humanoid Dark', value: 'base16/humanoid-dark' },
{ label: 'Base16 / Humanoid Light', value: 'base16/humanoid-light' },
{ label: 'Base16 / Ia Dark', value: 'base16/ia-dark' },
{ label: 'Base16 / Ia Light', value: 'base16/ia-light' },
{ label: 'Base16 / Icy Dark', value: 'base16/icy-dark' },
{ label: 'Base16 / Ir Black', value: 'base16/ir-black' },
{ label: 'Base16 / Isotope', value: 'base16/isotope' },
{ label: 'Base16 / Kimber', value: 'base16/kimber' },
{ label: 'Base16 / London Tube', value: 'base16/london-tube' },
{ label: 'Base16 / Macintosh', value: 'base16/macintosh' },
{ label: 'Base16 / Marrakesh', value: 'base16/marrakesh' },
{ label: 'Base16 / Materia', value: 'base16/materia' },
{ label: 'Base16 / Material', value: 'base16/material' },
{ label: 'Base16 / Material Darker', value: 'base16/material-darker' },
{ label: 'Base16 / Material Lighter', value: 'base16/material-lighter' },
{ label: 'Base16 / Material Palenight', value: 'base16/material-palenight' },
{ label: 'Base16 / Material Vivid', value: 'base16/material-vivid' },
{ label: 'Base16 / Mellow Purple', value: 'base16/mellow-purple' },
{ label: 'Base16 / Mexico Light', value: 'base16/mexico-light' },
{ label: 'Base16 / Mocha', value: 'base16/mocha' },
{ label: 'Base16 / Monokai', value: 'base16/monokai' },
{ label: 'Base16 / Nebula', value: 'base16/nebula' },
{ label: 'Base16 / Nord', value: 'base16/nord' },
{ label: 'Base16 / Nova', value: 'base16/nova' },
{ label: 'Base16 / Ocean', value: 'base16/ocean' },
{ label: 'Base16 / Oceanicnext', value: 'base16/oceanicnext' },
{ label: 'Base16 / One Light', value: 'base16/one-light' },
{ label: 'Base16 / Onedark', value: 'base16/onedark' },
{ label: 'Base16 / Outrun Dark', value: 'base16/outrun-dark' },
{ label: 'Base16 / Papercolor Dark', value: 'base16/papercolor-dark' },
{ label: 'Base16 / Papercolor Light', value: 'base16/papercolor-light' },
{ label: 'Base16 / Paraiso', value: 'base16/paraiso' },
{ label: 'Base16 / Pasque', value: 'base16/pasque' },
{ label: 'Base16 / Phd', value: 'base16/phd' },
{ label: 'Base16 / Pico', value: 'base16/pico' },
{ label: 'Base16 / Pop', value: 'base16/pop' },
{ label: 'Base16 / Porple', value: 'base16/porple' },
{ label: 'Base16 / Qualia', value: 'base16/qualia' },
{ label: 'Base16 / Railscasts', value: 'base16/railscasts' },
{ label: 'Base16 / Rebecca', value: 'base16/rebecca' },
{ label: 'Base16 / Ros Pine', value: 'base16/ros-pine' },
{ label: 'Base16 / Ros Pine Dawn', value: 'base16/ros-pine-dawn' },
{ label: 'Base16 / Ros Pine Moon', value: 'base16/ros-pine-moon' },
{ label: 'Base16 / Sagelight', value: 'base16/sagelight' },
{ label: 'Base16 / Sandcastle', value: 'base16/sandcastle' },
{ label: 'Base16 / Seti Ui', value: 'base16/seti-ui' },
{ label: 'Base16 / Shapeshifter', value: 'base16/shapeshifter' },
{ label: 'Base16 / Silk Dark', value: 'base16/silk-dark' },
{ label: 'Base16 / Silk Light', value: 'base16/silk-light' },
{ label: 'Base16 / Snazzy', value: 'base16/snazzy' },
{ label: 'Base16 / Solar Flare', value: 'base16/solar-flare' },
{ label: 'Base16 / Solar Flare Light', value: 'base16/solar-flare-light' },
{ label: 'Base16 / Solarized Dark', value: 'base16/solarized-dark' },
{ label: 'Base16 / Solarized Light', value: 'base16/solarized-light' },
{ label: 'Base16 / Spacemacs', value: 'base16/spacemacs' },
{ label: 'Base16 / Summercamp', value: 'base16/summercamp' },
{ label: 'Base16 / Summerfruit Dark', value: 'base16/summerfruit-dark' },
{ label: 'Base16 / Summerfruit Light', value: 'base16/summerfruit-light' },
{ label: 'Base16 / Synth Midnight Terminal Dark', value: 'base16/synth-midnight-terminal-dark' },
{ label: 'Base16 / Synth Midnight Terminal Light', value: 'base16/synth-midnight-terminal-light' },
{ label: 'Base16 / Tango', value: 'base16/tango' },
{ label: 'Base16 / Tender', value: 'base16/tender' },
{ label: 'Base16 / Tomorrow', value: 'base16/tomorrow' },
{ label: 'Base16 / Tomorrow Night', value: 'base16/tomorrow-night' },
{ label: 'Base16 / Twilight', value: 'base16/twilight' },
{ label: 'Base16 / Unikitty Dark', value: 'base16/unikitty-dark' },
{ label: 'Base16 / Unikitty Light', value: 'base16/unikitty-light' },
{ label: 'Base16 / Vulcan', value: 'base16/vulcan' },
{ label: 'Base16 / Windows 10', value: 'base16/windows-10' },
{ label: 'Base16 / Windows 10 Light', value: 'base16/windows-10-light' },
{ label: 'Base16 / Windows 95', value: 'base16/windows-95' },
{ label: 'Base16 / Windows 95 Light', value: 'base16/windows-95-light' },
{ label: 'Base16 / Windows High Contrast', value: 'base16/windows-high-contrast' },
{ label: 'Base16 / Windows High Contrast Light', value: 'base16/windows-high-contrast-light' },
{ label: 'Base16 / Windows Nt', value: 'base16/windows-nt' },
{ label: 'Base16 / Windows Nt Light', value: 'base16/windows-nt-light' },
{ label: 'Base16 / Woodland', value: 'base16/woodland' },
{ label: 'Base16 / Xcode Dusk', value: 'base16/xcode-dusk' },
{ label: 'Base16 / Zenburn', value: 'base16/zenburn' },
{ label: 'Brown Paper', value: 'brown-paper' },
{ label: 'Codepen Embed', value: 'codepen-embed' },
{ label: 'Color Brewer', value: 'color-brewer' },
{ label: 'Dark', value: 'dark' },
{ label: 'Devibeans', value: 'devibeans' },
{ label: 'Docco', value: 'docco' },
{ label: 'Far', value: 'far' },
{ label: 'Felipec', value: 'felipec' },
{ label: 'Foundation', value: 'foundation' },
{ label: 'Github', value: 'github' },
{ label: 'Github Dark', value: 'github-dark' },
{ label: 'Github Dark Dimmed', value: 'github-dark-dimmed' },
{ label: 'Gml', value: 'gml' },
{ label: 'Googlecode', value: 'googlecode' },
{ label: 'Gradient Dark', value: 'gradient-dark' },
{ label: 'Gradient Light', value: 'gradient-light' },
{ label: 'Grayscale', value: 'grayscale' },
{ label: 'Hybrid', value: 'hybrid' },
{ label: 'Idea', value: 'idea' },
{ label: 'Intellij Light', value: 'intellij-light' },
{ label: 'Ir Black', value: 'ir-black' },
{ label: 'Isbl Editor Dark', value: 'isbl-editor-dark' },
{ label: 'Isbl Editor Light', value: 'isbl-editor-light' },
{ label: 'Kimbie Dark', value: 'kimbie-dark' },
{ label: 'Kimbie Light', value: 'kimbie-light' },
{ label: 'Lightfair', value: 'lightfair' },
{ label: 'Lioshi', value: 'lioshi' },
{ label: 'Magula', value: 'magula' },
{ label: 'Mono Blue', value: 'mono-blue' },
{ label: 'Monokai', value: 'monokai' },
{ label: 'Monokai Sublime', value: 'monokai-sublime' },
{ label: 'Night Owl', value: 'night-owl' },
{ label: 'Nnfx Dark', value: 'nnfx-dark' },
{ label: 'Nnfx Light', value: 'nnfx-light' },
{ label: 'Nord', value: 'nord' },
{ label: 'Obsidian', value: 'obsidian' },
{ label: 'Panda Syntax Dark', value: 'panda-syntax-dark' },
{ label: 'Panda Syntax Light', value: 'panda-syntax-light' },
{ label: 'Paraiso Dark', value: 'paraiso-dark' },
{ label: 'Paraiso Light', value: 'paraiso-light' },
{ label: 'Pojoaque', value: 'pojoaque' },
{ label: 'Purebasic', value: 'purebasic' },
{ label: 'Qtcreator Dark', value: 'qtcreator-dark' },
{ label: 'Qtcreator Light', value: 'qtcreator-light' },
{ label: 'Rainbow', value: 'rainbow' },
{ label: 'Routeros', value: 'routeros' },
{ label: 'School Book', value: 'school-book' },
{ label: 'Shades Of Purple', value: 'shades-of-purple' },
{ label: 'Srcery', value: 'srcery' },
{ label: 'Stackoverflow Dark', value: 'stackoverflow-dark' },
{ label: 'Stackoverflow Light', value: 'stackoverflow-light' },
{ label: 'Sunburst', value: 'sunburst' },
{ label: 'Tokyo Night Dark', value: 'tokyo-night-dark' },
{ label: 'Tokyo Night Light', value: 'tokyo-night-light' },
{ label: 'Tomorrow Night Blue', value: 'tomorrow-night-blue' },
{ label: 'Tomorrow Night Bright', value: 'tomorrow-night-bright' },
{ label: 'Vs', value: 'vs' },
{ label: 'Vs 2015', value: 'vs2015' },
{ label: 'Xcode', value: 'xcode' },
{ label: 'Xt 256', value: 'xt256' }
]
// WATCHERS // WATCHERS
watch(() => adminStore.currentSiteId, (newValue) => { watch(() => adminStore.currentSiteId, (newValue) => {
@ -372,6 +661,10 @@ function resetFonts () {
state.config.contentFont = 'roboto' state.config.contentFont = 'roboto'
} }
function resetCodeBlocks () {
state.config.codeBlocksTheme = 'github-dark'
}
async function load () { async function load () {
state.loading++ state.loading++
$q.loading.show() $q.loading.show()
@ -386,6 +679,7 @@ async function load () {
) { ) {
theme { theme {
baseFont baseFont
codeBlocksTheme
contentFont contentFont
colorPrimary colorPrimary
colorSecondary colorSecondary
@ -429,6 +723,7 @@ async function save () {
try { try {
const patchTheme = { const patchTheme = {
dark: state.config.dark, dark: state.config.dark,
codeBlocksTheme: state.config.codeBlocksTheme,
colorPrimary: state.config.colorPrimary, colorPrimary: state.config.colorPrimary,
colorSecondary: state.config.colorSecondary, colorSecondary: state.config.colorSecondary,
colorAccent: state.config.colorAccent, colorAccent: state.config.colorAccent,

@ -29,7 +29,9 @@ q-page.column
.text-caption.text-grey-6 Last modified on #[strong {{lastModified}}] .text-caption.text-grey-6 Last modified on #[strong {{lastModified}}]
page-header page-header
.page-container.row.no-wrap.items-stretch(style='flex: 1 1 100%;') .page-container.row.no-wrap.items-stretch(style='flex: 1 1 100%;')
.col(style='order: 1;') .col(
:style='siteStore.theme.tocPosition === `left` ? `order: 2;` : `order: 1;`'
)
q-no-ssr( q-no-ssr(
v-if='editorStore.isActive' v-if='editorStore.isActive'
) )
@ -84,7 +86,7 @@ q-page.column
.text-caption {{rel.caption}} .text-caption {{rel.caption}}
.page-sidebar( .page-sidebar(
v-if='showSidebar' v-if='showSidebar'
style='order: 2;' :style='siteStore.theme.tocPosition === `left` ? `order: 1;` : `order: 2;`'
) )
template(v-if='pageStore.showToc') template(v-if='pageStore.showToc')
//- TOC //- TOC
@ -242,7 +244,7 @@ const barStyle = {
// COMPUTED // COMPUTED
const showSidebar = computed(() => { const showSidebar = computed(() => {
return pageStore.showSidebar && siteStore.showSidebar && !editorStore.isActive return pageStore.showSidebar && siteStore.showSidebar && siteStore.theme.tocPosition !== 'off' && !editorStore.isActive
}) })
const relationsLeft = computed(() => { const relationsLeft = computed(() => {
return pageStore.relations ? pageStore.relations.filter(r => r.position === 'left') : [] return pageStore.relations ? pageStore.relations.filter(r => r.position === 'left') : []

@ -55,7 +55,7 @@ export class MarkdownRenderer {
const highlighted = lang ? hljs.highlight(str, { language: lang, ignoreIllegals: true }) : { value: str } const highlighted = lang ? hljs.highlight(str, { language: lang, ignoreIllegals: true }) : { value: str }
const lineCount = highlighted.value.match(/\n/g).length const lineCount = highlighted.value.match(/\n/g).length
const lineNums = lineCount > 1 ? `<span aria-hidden="true" class="line-numbers-rows">${times(lineCount, n => '<span></span>').join('')}</span>` : '' const lineNums = lineCount > 1 ? `<span aria-hidden="true" class="line-numbers-rows">${times(lineCount, n => '<span></span>').join('')}</span>` : ''
return `<pre class="codeblock ${lineCount > 1 && 'line-numbers'}"><code class="language-${lang}">${highlighted.value}${lineNums}</code></pre>` return `<pre class="codeblock hljs ${lineCount > 1 && 'line-numbers'}"><code class="language-${lang}">${highlighted.value}${lineNums}</code></pre>`
} }
} }
}) })

@ -1,3 +1,4 @@
import { usePageStore } from 'src/stores/page'
const routes = [ const routes = [
{ {
@ -15,6 +16,19 @@ const routes = [
{ path: '', component: () => import('pages/Login.vue') } { 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', path: '/_profile',
component: () => import('layouts/ProfileLayout.vue'), component: () => import('layouts/ProfileLayout.vue'),

@ -8,6 +8,7 @@ import { useEditorStore } from './editor'
const pagePropsFragment = gql` const pagePropsFragment = gql`
fragment PageRead on Page { fragment PageRead on Page {
alias
allowComments allowComments
allowContributions allowContributions
allowRatings allowRatings
@ -112,6 +113,7 @@ const gqlQueries = {
const gqlMutations = { const gqlMutations = {
createPage: gql` createPage: gql`
mutation createPage ( mutation createPage (
$alias: String
$allowComments: Boolean $allowComments: Boolean
$allowContributions: Boolean $allowContributions: Boolean
$allowRatings: Boolean $allowRatings: Boolean
@ -138,6 +140,7 @@ const gqlMutations = {
$tocDepth: PageTocDepthInput $tocDepth: PageTocDepthInput
) { ) {
createPage ( createPage (
alias: $alias
allowComments: $allowComments allowComments: $allowComments
allowContributions: $allowContributions allowContributions: $allowContributions
allowRatings: $allowRatings allowRatings: $allowRatings
@ -178,6 +181,7 @@ const gqlMutations = {
export const usePageStore = defineStore('page', { export const usePageStore = defineStore('page', {
state: () => ({ state: () => ({
alias: '',
allowComments: false, allowComments: false,
allowContributions: true, allowContributions: true,
allowRatings: true, allowRatings: true,
@ -272,6 +276,40 @@ export const usePageStore = defineStore('page', {
throw err throw err
} }
}, },
/**
* PAGE - GET PATH FROM ALIAS
*/
async pageAlias (alias) {
const siteStore = useSiteStore()
try {
const resp = await APOLLO_CLIENT.query({
query: gql`
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 * PAGE - CREATE
*/ */
@ -305,6 +343,7 @@ export const usePageStore = defineStore('page', {
title: title ?? '', title: title ?? '',
description: description ?? '', description: description ?? '',
icon: 'las la-file-alt', icon: 'las la-file-alt',
alias: '',
publishState: 'published', publishState: 'published',
relations: [], relations: [],
tags: [], tags: [],
@ -345,6 +384,7 @@ export const usePageStore = defineStore('page', {
mutation: gqlMutations.createPage, mutation: gqlMutations.createPage,
variables: { variables: {
...pick(this, [ ...pick(this, [
'alias',
'allowComments', 'allowComments',
'allowContributions', 'allowContributions',
'allowRatings', 'allowRatings',
@ -415,6 +455,7 @@ export const usePageStore = defineStore('page', {
id: this.id, id: this.id,
patch: { patch: {
...pick(this, [ ...pick(this, [
'alias',
'allowComments', 'allowComments',
'allowContributions', 'allowContributions',
'allowRatings', 'allowRatings',

@ -47,6 +47,7 @@ export const useSiteStore = defineStore('site', {
colorAccent: '#f03a47', colorAccent: '#f03a47',
colorHeader: '#000', colorHeader: '#000',
colorSidebar: '#1976D2', colorSidebar: '#1976D2',
codeBlocksTheme: '',
sidebarPosition: 'left', sidebarPosition: 'left',
tocPosition: 'right', tocPosition: 'right',
showSharingMenu: true, showSharingMenu: true,
@ -69,7 +70,8 @@ export const useSiteStore = defineStore('site', {
docsBase: 'https://next.js.wiki/docs' docsBase: 'https://next.js.wiki/docs'
}), }),
getters: { getters: {
overlayIsShown: (state) => Boolean(state.overlay) overlayIsShown: (state) => Boolean(state.overlay),
sideNavIsDisabled: (state) => Boolean(state.theme.sidebarPosition === 'off')
}, },
actions: { actions: {
openFileManager (opts) { openFileManager (opts) {
@ -121,6 +123,7 @@ export const useSiteStore = defineStore('site', {
colorAccent colorAccent
colorHeader colorHeader
colorSidebar colorSidebar
codeBlocksTheme
sidebarPosition sidebarPosition
tocPosition tocPosition
showSharingMenu showSharingMenu
@ -137,27 +140,29 @@ export const useSiteStore = defineStore('site', {
}) })
const siteInfo = resp.data.siteByHostname const siteInfo = resp.data.siteByHostname
if (siteInfo) { if (siteInfo) {
this.id = clone(siteInfo.id) this.$patch({
this.hostname = clone(siteInfo.hostname) id: clone(siteInfo.id),
this.title = clone(siteInfo.title) hostname: clone(siteInfo.hostname),
this.description = clone(siteInfo.description) title: clone(siteInfo.title),
this.logoText = clone(siteInfo.logoText) description: clone(siteInfo.description),
this.company = clone(siteInfo.company) logoText: clone(siteInfo.logoText),
this.contentLicense = clone(siteInfo.contentLicense) company: clone(siteInfo.company),
this.footerExtra = clone(siteInfo.footerExtra) contentLicense: clone(siteInfo.contentLicense),
this.features = { footerExtra: clone(siteInfo.footerExtra),
...this.features, features: {
...clone(siteInfo.features) ...this.features,
} ...clone(siteInfo.features)
this.editors = { },
asciidoc: clone(siteInfo.editors.asciidoc.isActive), editors: {
markdown: clone(siteInfo.editors.markdown.isActive), asciidoc: clone(siteInfo.editors.asciidoc.isActive),
wysiwyg: clone(siteInfo.editors.wysiwyg.isActive) markdown: clone(siteInfo.editors.markdown.isActive),
} wysiwyg: clone(siteInfo.editors.wysiwyg.isActive)
this.theme = { },
...this.theme, theme: {
...clone(siteInfo.theme) ...this.theme,
} ...clone(siteInfo.theme)
}
})
} else { } else {
throw new Error('Invalid Site') throw new Error('Invalid Site')
} }

Loading…
Cancel
Save