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;
......@@ -438,7 +438,7 @@
}
}
// ---------------------------------
// ---------------------------------
// TASK LISTS
// ---------------------------------
......@@ -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
......
......@@ -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.features,
...clone(siteInfo.features)
}
this.editors = {
asciidoc: clone(siteInfo.editors.asciidoc.isActive),
markdown: clone(siteInfo.editors.markdown.isActive),
wysiwyg: clone(siteInfo.editors.wysiwyg.isActive)
}
this.theme = {
...this.theme,
...clone(siteInfo.theme)
}
this.$patch({
id: clone(siteInfo.id),
hostname: clone(siteInfo.hostname),
title: clone(siteInfo.title),
description: clone(siteInfo.description),
logoText: clone(siteInfo.logoText),
company: clone(siteInfo.company),
contentLicense: clone(siteInfo.contentLicense),
footerExtra: clone(siteInfo.footerExtra),
features: {
...this.features,
...clone(siteInfo.features)
},
editors: {
asciidoc: clone(siteInfo.editors.asciidoc.isActive),
markdown: clone(siteInfo.editors.markdown.isActive),
wysiwyg: clone(siteInfo.editors.wysiwyg.isActive)
},
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