Unverified Commit 3da8fb41 authored by NGPixel's avatar NGPixel

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

parent 80b1cbff
......@@ -168,7 +168,7 @@ export async function up (knex) {
table.boolean('isRTL').notNullable().defaultTo(false)
table.string('name').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('updatedAt').notNullable().defaultTo(knex.fn.now())
})
......@@ -606,6 +606,7 @@ export async function up (knex) {
},
theme: {
dark: false,
codeBlocksTheme: 'github-dark',
colorPrimary: '#1976D2',
colorSecondary: '#02C39A',
colorAccent: '#FF9800',
......
......@@ -187,6 +187,31 @@ export default {
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
*/
......
......@@ -41,12 +41,6 @@ extend type Query {
password: String
): Page
tags: [PageTag]!
searchTags(
query: String!
): [String]!
pageTree(
path: String
parent: Int
......@@ -59,6 +53,17 @@ extend type Query {
locale: String!
): [PageLinkItem]
pathFromAlias(
siteId: UUID!
alias: String!
): PageAliasPath
searchTags(
query: String!
): [String]!
tags: [PageTag]!
checkConflicts(
id: Int!
checkoutDate: Date!
......@@ -71,6 +76,7 @@ extend type Query {
extend type Mutation {
createPage(
alias: String
allowComments: Boolean
allowContributions: Boolean
allowRatings: Boolean
......@@ -165,6 +171,7 @@ type PageMigrationResponse {
}
type Page {
alias: String
allowComments: Boolean
allowContributions: Boolean
allowRatings: Boolean
......@@ -329,6 +336,7 @@ input PageRelationInput {
}
input PageUpdateInput {
alias: String
allowComments: Boolean
allowContributions: Boolean
allowRatings: Boolean
......@@ -355,6 +363,11 @@ input PageUpdateInput {
tocDepth: PageTocDepthInput
}
type PageAliasPath {
id: UUID
path: String
}
type PageTocDepth {
min: Int
max: Int
......
......@@ -118,6 +118,7 @@ type SiteEditor {
type SiteTheme {
dark: Boolean
codeBlocksTheme: String
colorPrimary: String
colorSecondary: String
colorAccent: String
......@@ -210,6 +211,7 @@ input SiteDefaultsInput {
input SiteThemeInput {
dark: Boolean
codeBlocksTheme: String
colorPrimary: String
colorSecondary: String
colorAccent: String
......
This source diff could not be displayed because it is too large. You can view the blob instead.
[
{
"code": "en",
"name": "English",
"nativeName": "English",
"rtl": false,
"completeness": 100
}
]
......@@ -19,7 +19,7 @@ export class Locale extends Model {
nativeName: {type: 'string'},
createdAt: {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'
const pageRegex = /^[a-zA0-90-9-_/]*$/
const aliasRegex = /^[a-zA0-90-9-_]*$/
const frontmatterRegex = {
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) {
// -> Validate site
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
......@@ -262,13 +263,31 @@ export class Page extends Model {
locale: opts.locale,
path: opts.path
})) {
throw new WIKI.Error.PageDeleteForbidden()
throw new Error('ERR_FORBIDDEN')
}
// -> 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) {
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
......@@ -302,6 +321,7 @@ export class Page extends Model {
// -> Create page
const page = await WIKI.db.pages.query().insert({
alias: opts.alias,
authorId: opts.user.id,
content: opts.content,
creatorId: opts.user.id,
......@@ -449,6 +469,24 @@ export class Page extends Model {
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) {
patch.content = opts.patch.content
historyData.affectedFields.push('content')
......
......@@ -91,6 +91,7 @@ export class Site extends Model {
},
theme: {
dark: false,
codeBlocksTheme: 'github-dark',
colorPrimary: '#1976D2',
colorSecondary: '#02C39A',
colorAccent: '#FF9800',
......
title: Git
title: Local Git
icon: '/_assets/icons/ultraviolet-git.svg'
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!
......
......@@ -34,10 +34,10 @@
"node": ">=18.0"
},
"dependencies": {
"@azure/storage-blob": "12.13.0",
"@azure/storage-blob": "12.14.0",
"@exlinc/keycloak-passport": "1.0.2",
"@graphql-tools/schema": "9.0.17",
"@graphql-tools/utils": "9.2.1",
"@graphql-tools/schema": "10.0.0",
"@graphql-tools/utils": "10.0.0",
"@joplin/turndown-plugin-gfm": "1.0.47",
"@root/csr": "0.8.1",
"@root/keypairs": "0.10.3",
......@@ -48,7 +48,7 @@
"apollo-server": "3.6.7",
"apollo-server-express": "3.6.7",
"auto-load": "3.0.4",
"aws-sdk": "2.1353.0",
"aws-sdk": "2.1381.0",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"chalk": "5.2.0",
......@@ -66,34 +66,34 @@
"custom-error-instance": "2.1.2",
"dependency-graph": "0.11.0",
"diff": "5.1.0",
"diff2html": "3.4.34",
"dompurify": "3.0.1",
"diff2html": "3.4.35",
"dompurify": "3.0.3",
"dotize": "0.3.0",
"emoji-regex": "10.2.1",
"eventemitter2": "6.4.9",
"express": "4.18.2",
"express-brute": "1.0.1",
"express-session": "1.17.3",
"file-type": "18.2.1",
"file-type": "18.4.0",
"filesize": "10.0.7",
"fs-extra": "11.1.1",
"getos": "3.2.1",
"graphql": "16.6.0",
"graphql-list-fields": "2.0.2",
"graphql-rate-limit-directive": "2.0.3",
"graphql-tools": "8.3.19",
"graphql-tools": "9.0.0",
"graphql-upload": "16.0.2",
"he": "1.2.0",
"highlight.js": "11.7.0",
"i18next": "22.4.14",
"highlight.js": "11.8.0",
"i18next": "22.5.0",
"i18next-node-fs-backend": "2.1.3",
"image-size": "1.0.2",
"js-base64": "3.7.5",
"js-binary": "1.2.0",
"js-yaml": "4.1.0",
"jsdom": "21.1.1",
"jsdom": "22.0.0",
"jsonwebtoken": "9.0.0",
"katex": "0.16.4",
"katex": "0.16.7",
"klaw": "4.1.0",
"knex": "2.4.2",
"lodash": "4.17.21",
......@@ -108,7 +108,7 @@
"markdown-it-footnote": "3.0.3",
"markdown-it-imsize": "2.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-sup": "1.0.0",
"markdown-it-task-lists": "2.1.1",
......@@ -119,11 +119,11 @@
"nanoid": "4.0.2",
"node-2fa": "2.0.3",
"node-cache": "5.1.2",
"nodemailer": "6.9.1",
"nodemailer": "6.9.2",
"objection": "3.0.1",
"passport": "0.6.0",
"passport-auth0": "1.4.3",
"passport-azure-ad": "4.3.4",
"passport-azure-ad": "4.3.5",
"passport-cas": "0.1.1",
"passport-discord": "0.1.4",
"passport-dropbox-oauth2": "1.1.0",
......@@ -142,14 +142,14 @@
"passport-slack-oauth2": "1.1.1",
"passport-twitch-strategy": "2.2.0",
"pem-jwk": "2.0.0",
"pg": "8.10.0",
"pg": "8.11.0",
"pg-hstore": "2.3.4",
"pg-pubsub": "0.8.1",
"pg-query-stream": "4.4.0",
"pg-query-stream": "4.5.0",
"pg-tsquery": "8.4.1",
"poolifier": "2.4.4",
"poolifier": "2.4.14",
"punycode": "2.3.0",
"puppeteer-core": "19.8.5",
"puppeteer-core": "20.2.1",
"qr-image": "3.2.0",
"rate-limiter-flexible": "2.4.1",
"remove-markdown": "0.5.0",
......@@ -158,12 +158,12 @@
"safe-regex": "2.1.1",
"sanitize-filename": "1.6.3",
"scim-query-filter-parser": "2.0.4",
"semver": "7.3.8",
"semver": "7.5.1",
"serve-favicon": "2.5.0",
"sharp": "0.32.0",
"simple-git": "3.17.0",
"sharp": "0.32.1",
"simple-git": "3.18.0",
"socket.io": "4.6.1",
"ssh2": "1.11.0",
"ssh2": "1.13.0",
"ssh2-promise": "1.0.3",
"striptags": "3.2.0",
"tar-fs": "2.1.1",
......@@ -173,10 +173,10 @@
"uuid": "9.0.0",
"validate.js": "0.13.1",
"xss": "1.0.14",
"yargs": "17.7.1"
"yargs": "17.7.2"
},
"devDependencies": {
"eslint": "8.38.0",
"eslint": "8.41.0",
"eslint-config-requarks": "1.0.7",
"eslint-config-standard": "17.0.0",
"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 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>` : ''
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>`
}
}
})
......
......@@ -13,37 +13,37 @@
"ncu-u": "ncu -u -x codemirror,codemirror-asciidoc"
},
"dependencies": {
"@apollo/client": "3.7.11",
"@apollo/client": "3.7.14",
"@lezer/common": "1.0.2",
"@mdi/font": "7.2.96",
"@quasar/extras": "1.16.2",
"@tiptap/core": "2.0.2",
"@tiptap/extension-code-block": "2.0.2",
"@tiptap/extension-code-block-lowlight": "2.0.2",
"@tiptap/extension-color": "2.0.2",
"@tiptap/extension-dropcursor": "2.0.2",
"@tiptap/extension-font-family": "2.0.2",
"@tiptap/extension-gapcursor": "2.0.2",
"@tiptap/extension-hard-break": "2.0.2",
"@tiptap/extension-highlight": "2.0.2",
"@tiptap/extension-history": "2.0.2",
"@tiptap/extension-image": "2.0.2",
"@tiptap/extension-mention": "2.0.2",
"@tiptap/extension-placeholder": "2.0.2",
"@tiptap/extension-table": "2.0.2",
"@tiptap/extension-table-cell": "2.0.2",
"@tiptap/extension-table-header": "2.0.2",
"@tiptap/extension-table-row": "2.0.2",
"@tiptap/extension-task-item": "2.0.2",
"@tiptap/extension-task-list": "2.0.2",
"@tiptap/extension-text-align": "2.0.2",
"@tiptap/extension-text-style": "2.0.2",
"@tiptap/extension-typography": "2.0.2",
"@tiptap/pm": "2.0.2",
"@tiptap/starter-kit": "2.0.2",
"@tiptap/vue-3": "2.0.2",
"@quasar/extras": "1.16.4",
"@tiptap/core": "2.0.3",
"@tiptap/extension-code-block": "2.0.3",
"@tiptap/extension-code-block-lowlight": "2.0.3",
"@tiptap/extension-color": "2.0.3",
"@tiptap/extension-dropcursor": "2.0.3",
"@tiptap/extension-font-family": "2.0.3",
"@tiptap/extension-gapcursor": "2.0.3",
"@tiptap/extension-hard-break": "2.0.3",
"@tiptap/extension-highlight": "2.0.3",
"@tiptap/extension-history": "2.0.3",
"@tiptap/extension-image": "2.0.3",
"@tiptap/extension-mention": "2.0.3",
"@tiptap/extension-placeholder": "2.0.3",
"@tiptap/extension-table": "2.0.3",
"@tiptap/extension-table-cell": "2.0.3",
"@tiptap/extension-table-header": "2.0.3",
"@tiptap/extension-table-row": "2.0.3",
"@tiptap/extension-task-item": "2.0.3",
"@tiptap/extension-task-list": "2.0.3",
"@tiptap/extension-text-align": "2.0.3",
"@tiptap/extension-text-style": "2.0.3",
"@tiptap/extension-typography": "2.0.3",
"@tiptap/pm": "2.0.3",
"@tiptap/starter-kit": "2.0.3",
"@tiptap/vue-3": "2.0.3",
"apollo-upload-client": "17.0.0",
"browser-fs-access": "0.33.0",
"browser-fs-access": "0.34.0",
"clipboard": "2.0.11",
"codemirror": "5.65.11",
"codemirror-asciidoc": "1.0.4",
......@@ -53,12 +53,12 @@
"fuse.js": "6.6.2",
"graphql": "16.6.0",
"graphql-tag": "2.12.6",
"highlight.js": "11.7.0",
"js-cookie": "3.0.1",
"highlight.js": "11.8.0",
"js-cookie": "3.0.5",
"jwt-decode": "3.1.2",
"katex": "0.16.4",
"katex": "0.16.7",
"lodash-es": "4.17.21",
"lowlight": "2.8.1",
"lowlight": "2.9.0",
"luxon": "3.3.0",
"markdown-it": "13.0.1",
"markdown-it-abbr": "1.0.4",
......@@ -69,34 +69,34 @@
"markdown-it-footnote": "3.0.3",
"markdown-it-imsize": "2.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-sup": "1.0.0",
"markdown-it-task-lists": "2.1.1",
"mitt": "3.0.0",
"monaco-editor": "0.37.1",
"monaco-editor": "0.38.0",
"pako": "2.1.0",
"pinia": "2.0.33",
"prosemirror-commands": "1.5.1",
"prosemirror-history": "1.3.0",
"prosemirror-keymap": "1.2.1",
"prosemirror-model": "1.19.0",
"prosemirror-schema-list": "1.2.2",
"prosemirror-state": "1.4.2",
"prosemirror-transform": "1.7.1",
"prosemirror-view": "1.30.2",
"pinia": "2.1.3",
"prosemirror-commands": "1.5.2",
"prosemirror-history": "1.3.2",
"prosemirror-keymap": "1.2.2",
"prosemirror-model": "1.19.1",
"prosemirror-schema-list": "1.2.3",
"prosemirror-state": "1.4.3",
"prosemirror-transform": "1.7.2",
"prosemirror-view": "1.31.3",
"pug": "3.0.2",
"quasar": "2.11.10",
"quasar": "2.12.0",
"slugify": "1.6.6",
"socket.io-client": "4.6.1",
"tabulator-tables": "5.4.4",
"tippy.js": "6.3.7",
"twemoji": "14.0.2",
"uuid": "9.0.0",
"v-network-graph": "0.9.1",
"vue": "3.2.47",
"v-network-graph": "0.9.3",
"vue": "3.3.4",
"vue-i18n": "9.2.2",
"vue-router": "4.1.6",
"vue-router": "4.2.1",
"vue3-otp-input": "0.4.1",
"vuedraggable": "4.1.0",
"xterm": "5.1.0",
......@@ -104,17 +104,17 @@
},
"devDependencies": {
"@intlify/unplugin-vue-i18n": "0.10.0",
"@quasar/app-vite": "1.2.1",
"@types/lodash": "4.14.192",
"@volar/vue-language-plugin-pug": "1.2.0",
"@quasar/app-vite": "1.4.3",
"@types/lodash": "4.14.194",
"@volar/vue-language-plugin-pug": "1.6.5",
"autoprefixer": "10.4.14",
"browserlist": "latest",
"eslint": "8.37.0",
"eslint": "8.41.0",
"eslint-config-standard": "17.0.0",
"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-vue": "9.10.0"
"eslint-plugin-vue": "9.13.0"
},
"engines": {
"node": ">= 18.0",
......
<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>
\ No newline at end of file
......@@ -75,7 +75,7 @@ module.exports = configure(function (/* ctx */) {
vueRouterMode: 'history', // available values: 'hash', 'history'
// vueRouterBase,
// vueDevtools,
// vueOptionsAPI: true,
vueOptionsAPI: false,
rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
......@@ -90,28 +90,21 @@ module.exports = configure(function (/* ctx */) {
extendViteConf (viteConf) {
viteConf.build.assetsDir = '_assets'
// viteConf.resolve.alias.vue = '/workspace/ux/node_modules/vue/dist/vue.esm-bundler.js'
// viteConf.build.rollupOptions = {
// ...viteConf.build.rollupOptions ?? {},
// external: [
// /^\/_site\//
// ]
// }
viteConf.build.rollupOptions = {
...viteConf.build.rollupOptions ?? {},
output: {
manualChunks: {
lodash: ['lodash-es', 'lodash'],
quasar: ['quasar', 'quasar/src/components']
}
}
}
viteConf.optimizeDeps.include = [
'prosemirror-state',
'prosemirror-transform',
'prosemirror-model',
'prosemirror-view'
]
// viteConf.build.rollupOptions = {
// external: ['monaco-editor'],
// output: {
// globals: {
// 'monaco-editor': 'monaco-editor'
// }
// }
// }
},
// viteVuePluginOptions: {},
......@@ -139,7 +132,8 @@ module.exports = configure(function (/* ctx */) {
},
hmr: {
clientPort: userConfig.dev.hmrClientPort
}
},
vueDevtools: true
},
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
......
......@@ -55,12 +55,15 @@ watch(() => userStore.cvd, () => {
// THEME
function applyTheme () {
async function applyTheme () {
// -> Dark Mode
if (userStore.appearance === 'site') {
$q.dark.set(siteStore.theme.dark)
} else {
$q.dark.set(userStore.appearance === 'dark')
}
// -> CSS Vars
setCssVar('primary', userStore.getAccessibleColor('primary', siteStore.theme.colorPrimary))
setCssVar('secondary', userStore.getAccessibleColor('secondary', siteStore.theme.colorSecondary))
setCssVar('accent', userStore.getAccessibleColor('accent', siteStore.theme.colorAccent))
......@@ -68,6 +71,21 @@ function applyTheme () {
setCssVar('sidebar', userStore.getAccessibleColor('sidebar', siteStore.theme.colorSidebar))
setCssVar('positive', userStore.getAccessibleColor('positive', '#02C39A'))
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
......
......@@ -92,6 +92,7 @@
)
q-tooltip Bookmark Page
q-btn.q-ml-md(
v-if='siteStore.theme.showSharingMenu'
flat
dense
icon='las la-share-alt'
......@@ -101,6 +102,7 @@
q-tooltip Share
social-sharing-menu
q-btn.q-ml-md(
v-if='siteStore.theme.showPrintBtn'
flat
dense
icon='las la-print'
......@@ -297,7 +299,8 @@ async function saveChangesCommit (closeAfter = false) {
} catch (err) {
$q.notify({
type: 'negative',
message: 'Failed to save page changes.'
message: 'Failed to save page changes.',
caption: err.message
})
}
$q.loading.hide()
......
......@@ -62,6 +62,13 @@ q-card.page-properties-dialog
name='las la-icons'
color='primary'
)
q-input(
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
......
......@@ -15,7 +15,7 @@
// ---------------------------------
a {
color: $blue-8;
color: var(--q-primary);
&.is-internal-link.is-invalid-page {
color: $red-8;
......@@ -514,22 +514,17 @@
// }
pre.codeblock {
border: none;
border: 1px solid rgba(0,0,0,.2);
border-radius: 5px;
box-shadow: initial;
background-color: $dark-5;
padding: 1rem;
margin: 1rem 0;
overflow: auto;
@at-root .body--dark & {
background-color: $dark-5;
}
> code {
background-color: transparent;
// background-color: transparent;
padding: 0;
color: #FFF;
// color: #FFF;
box-shadow: initial;
display: block;
font-size: .85rem;
......@@ -591,5 +586,3 @@
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.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.",
......@@ -1503,6 +1506,7 @@
"editor.pageRel.title": "Add Page Relation",
"editor.pageRel.titleEdit": "Edit Page Relation",
"editor.pageScripts.title": "Page Scripts",
"editor.props.alias": "Alias",
"editor.props.allowComments": "Allow Comments",
"editor.props.allowCommentsHint": "Enable commenting abilities on this page.",
"editor.props.allowContributions": "Allow Contributions",
......
......@@ -3,8 +3,9 @@ q-layout(view='hHh Lpr lff')
header-nav
q-drawer.bg-sidebar(
:modelValue='isSidebarShown'
show-if-above
:show-if-above='siteStore.theme.sidebarPosition !== `off`'
:width='255'
:side='siteStore.theme.sidebarPosition === `right` ? `right` : `left`'
)
.sidebar-actions.flex.items-stretch
q-btn.q-px-sm.col(
......@@ -144,7 +145,7 @@ const barStyle = {
// COMPUTED
const isSidebarShown = computed(() => {
return siteStore.showSideNav && !(editorStore.isActive && editorStore.hideSideNav)
return siteStore.showSideNav && !siteStore.sideNavIsDisabled && !(editorStore.isActive && editorStore.hideSideNav)
})
// METHODS
......
......@@ -92,11 +92,47 @@ q-page.admin-theme
)
//- -----------------------
//- 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
......@@ -274,6 +310,7 @@ import { setCssVar, useMeta, useQuasar } from 'quasar'
import { onMounted, reactive, watch } from 'vue'
import { useAdminStore } from 'src/stores/admin'
import { useFlagsStore } from 'src/stores/flags'
import { useSiteStore } from 'src/stores/site'
import UtilCodeEditor from '../components/UtilCodeEditor.vue'
......@@ -285,6 +322,7 @@ const $q = useQuasar()
// STORES
const adminStore = useAdminStore()
const flagStore = useFlagsStore()
const siteStore = useSiteStore()
// I18N
......@@ -311,6 +349,7 @@ const state = reactive({
colorAccent: '#f03a47',
colorHeader: '#000',
colorSidebar: '#1976D2',
codeBlocksTheme: '',
contentWidth: 'full',
sidebarPosition: 'left',
tocPosition: 'right',
......@@ -350,6 +389,256 @@ const fonts = [
{ 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
watch(() => adminStore.currentSiteId, (newValue) => {
......@@ -372,6 +661,10 @@ function resetFonts () {
state.config.contentFont = 'roboto'
}
function resetCodeBlocks () {
state.config.codeBlocksTheme = 'github-dark'
}
async function load () {
state.loading++
$q.loading.show()
......@@ -386,6 +679,7 @@ async function load () {
) {
theme {
baseFont
codeBlocksTheme
contentFont
colorPrimary
colorSecondary
......@@ -429,6 +723,7 @@ async function save () {
try {
const patchTheme = {
dark: state.config.dark,
codeBlocksTheme: state.config.codeBlocksTheme,
colorPrimary: state.config.colorPrimary,
colorSecondary: state.config.colorSecondary,
colorAccent: state.config.colorAccent,
......
......@@ -29,7 +29,9 @@ q-page.column
.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='order: 1;')
.col(
:style='siteStore.theme.tocPosition === `left` ? `order: 2;` : `order: 1;`'
)
q-no-ssr(
v-if='editorStore.isActive'
)
......@@ -84,7 +86,7 @@ q-page.column
.text-caption {{rel.caption}}
.page-sidebar(
v-if='showSidebar'
style='order: 2;'
:style='siteStore.theme.tocPosition === `left` ? `order: 1;` : `order: 2;`'
)
template(v-if='pageStore.showToc')
//- TOC
......@@ -242,7 +244,7 @@ const barStyle = {
// 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(() => {
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 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>` : ''
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>`
}
}
})
......
import { usePageStore } from 'src/stores/page'
const routes = [
{
......@@ -16,6 +17,19 @@ const routes = [
]
},
{
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: [
......
......@@ -8,6 +8,7 @@ import { useEditorStore } from './editor'
const pagePropsFragment = gql`
fragment PageRead on Page {
alias
allowComments
allowContributions
allowRatings
......@@ -112,6 +113,7 @@ const gqlQueries = {
const gqlMutations = {
createPage: gql`
mutation createPage (
$alias: String
$allowComments: Boolean
$allowContributions: Boolean
$allowRatings: Boolean
......@@ -138,6 +140,7 @@ const gqlMutations = {
$tocDepth: PageTocDepthInput
) {
createPage (
alias: $alias
allowComments: $allowComments
allowContributions: $allowContributions
allowRatings: $allowRatings
......@@ -178,6 +181,7 @@ const gqlMutations = {
export const usePageStore = defineStore('page', {
state: () => ({
alias: '',
allowComments: false,
allowContributions: true,
allowRatings: true,
......@@ -273,6 +277,40 @@ export const usePageStore = defineStore('page', {
}
},
/**
* 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
*/
async pageCreate ({ editor, locale, path, title = '', description = '', content = '' }) {
......@@ -305,6 +343,7 @@ export const usePageStore = defineStore('page', {
title: title ?? '',
description: description ?? '',
icon: 'las la-file-alt',
alias: '',
publishState: 'published',
relations: [],
tags: [],
......@@ -345,6 +384,7 @@ export const usePageStore = defineStore('page', {
mutation: gqlMutations.createPage,
variables: {
...pick(this, [
'alias',
'allowComments',
'allowContributions',
'allowRatings',
......@@ -415,6 +455,7 @@ export const usePageStore = defineStore('page', {
id: this.id,
patch: {
...pick(this, [
'alias',
'allowComments',
'allowContributions',
'allowRatings',
......
......@@ -47,6 +47,7 @@ export const useSiteStore = defineStore('site', {
colorAccent: '#f03a47',
colorHeader: '#000',
colorSidebar: '#1976D2',
codeBlocksTheme: '',
sidebarPosition: 'left',
tocPosition: 'right',
showSharingMenu: true,
......@@ -69,7 +70,8 @@ export const useSiteStore = defineStore('site', {
docsBase: 'https://next.js.wiki/docs'
}),
getters: {
overlayIsShown: (state) => Boolean(state.overlay)
overlayIsShown: (state) => Boolean(state.overlay),
sideNavIsDisabled: (state) => Boolean(state.theme.sidebarPosition === 'off')
},
actions: {
openFileManager (opts) {
......@@ -121,6 +123,7 @@ export const useSiteStore = defineStore('site', {
colorAccent
colorHeader
colorSidebar
codeBlocksTheme
sidebarPosition
tocPosition
showSharingMenu
......@@ -137,27 +140,29 @@ export const useSiteStore = defineStore('site', {
})
const siteInfo = resp.data.siteByHostname
if (siteInfo) {
this.id = clone(siteInfo.id)
this.hostname = clone(siteInfo.hostname)
this.title = clone(siteInfo.title)
this.description = clone(siteInfo.description)
this.logoText = clone(siteInfo.logoText)
this.company = clone(siteInfo.company)
this.contentLicense = clone(siteInfo.contentLicense)
this.footerExtra = clone(siteInfo.footerExtra)
this.features = {
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)
}
this.editors = {
},
editors: {
asciidoc: clone(siteInfo.editors.asciidoc.isActive),
markdown: clone(siteInfo.editors.markdown.isActive),
wysiwyg: clone(siteInfo.editors.wysiwyg.isActive)
}
this.theme = {
},
theme: {
...this.theme,
...clone(siteInfo.theme)
}
})
} else {
throw new Error('Invalid Site')
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment