<template lang="pug"> q-layout(view='hHh lpR fFf', container) q-header.card-header.q-px-md.q-py-sm q-icon(name='img:/_assets/icons/ultraviolet-markdown.svg', left, size='md') span {{t(`admin.editors.markdownName`)}} q-space q-btn.q-mr-sm( flat rounded color='white' :aria-label='t(`common.actions.refresh`)' icon='las la-question-circle' :href='siteStore.docsBase + `/admin/editors/markdown`' target='_blank' type='a' ) q-btn-group(push) q-btn( push color='grey-6' text-color='white' :aria-label='t(`common.actions.refresh`)' icon='las la-redo-alt' @click='load' :loading='state.loading > 0' ) q-tooltip(anchor='center left', self='center right') {{t(`common.actions.refresh`)}} q-btn( push color='white' text-color='grey-7' :label='t(`common.actions.cancel`)' :aria-label='t(`common.actions.cancel`)' icon='las la-times' @click='close' ) q-btn( push color='positive' text-color='white' :label='t(`common.actions.save`)' :aria-label='t(`common.actions.save`)' icon='las la-check' @click='save' :disabled='state.loading > 0' ) q-page-container q-page.q-pa-md(style='max-width: 1200px; margin: 0 auto;') q-card.shadow-1.q-pb-sm q-card-section .text-subtitle1 {{t('admin.editors.markdown.general')}} q-item(tag='label') blueprint-icon(icon='html') q-item-section q-item-label {{t(`admin.editors.markdown.allowHTML`)}} q-item-label(caption) {{t(`admin.editors.markdown.allowHTMLHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.allowHTML' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.allowHTML`)' ) q-separator.q-my-sm(inset) q-item(tag='label') blueprint-icon(icon='link') q-item-section q-item-label {{t(`admin.editors.markdown.linkify`)}} q-item-label(caption) {{t(`admin.editors.markdown.linkifyHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.linkify' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.linkify`)' ) q-separator.q-my-sm(inset) q-item(tag='label') blueprint-icon(icon='enter-key') q-item-section q-item-label {{t(`admin.editors.markdown.lineBreaks`)}} q-item-label(caption) {{t(`admin.editors.markdown.lineBreaksHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.lineBreaks' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.lineBreaks`)' ) q-separator.q-my-sm(inset) q-item blueprint-icon(icon='width') q-item-section q-item-label {{t(`admin.editors.markdown.tabWidth`)}} q-item-label(caption) {{t(`admin.editors.markdown.tabWidthHint`)}} q-item-section(side) q-input( type='number' min='1' max='8' style='width: 100px;' outlined v-model='state.config.tabWidth' dense :aria-label='t(`admin.editors.markdown.tabWidth`)' ) q-separator.q-my-sm(inset) q-item blueprint-icon(icon='sigma') q-item-section q-item-label {{t(`admin.editors.markdown.latexEngine`)}} q-item-label(caption) {{t(`admin.editors.markdown.latexEngineHint`)}} q-item-section.col-auto q-btn-toggle( v-model='state.config.latexEngine' push glossy no-caps toggle-color='primary' :options='latexEngines' ) q-separator.q-my-sm(inset) q-item blueprint-icon(icon='data-sheet') q-item-section q-item-label {{t(`admin.editors.markdown.multimdTable`)}} q-item-label(caption) {{t(`admin.editors.markdown.multimdTableHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.multimdTable' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.multimdTable`)' ) q-separator.q-my-sm(inset) q-item(tag='label') blueprint-icon(icon='asterisk') q-item-section q-item-label {{t(`admin.editors.markdown.typographer`)}} q-item-label(caption) {{t(`admin.editors.markdown.typographerHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.typographer' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.typographer`)' ) template(v-if='state.config.typographer') q-separator.q-my-sm(inset) q-item(tag='label') blueprint-icon(icon='quote-left') q-item-section q-item-label {{t(`admin.editors.markdown.quotes`)}} q-item-label(caption) {{t(`admin.editors.markdown.quotesHint`)}} q-item-section(avatar) q-select( style='width: 200px;' outlined v-model='state.config.quotes' :options='quoteStyles' emit-value map-options dense options-dense :aria-label='t(`admin.editors.markdown.quotes`)' ) q-separator.q-my-sm(inset) q-item(tag='label') blueprint-icon(icon='underline') q-item-section q-item-label {{t(`admin.editors.markdown.underline`)}} q-item-label(caption) {{t(`admin.editors.markdown.underlineHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.underline' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.underline`)' ) q-card.shadow-1.q-pb-sm.q-mt-md q-card-section .text-subtitle1 {{t('admin.editors.markdown.plantuml')}} q-item(tag='label') blueprint-icon(icon='workflow') q-item-section q-item-label {{t(`admin.editors.markdown.plantuml`)}} q-item-label(caption) {{t(`admin.editors.markdown.plantumlHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.plantuml' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.plantuml`)' ) template(v-if='state.config.plantuml') q-separator.q-my-sm(inset) q-item blueprint-icon(icon='website') q-item-section q-item-label {{t(`admin.editors.markdown.plantumlServerUrl`)}} q-item-label(caption) {{t(`admin.editors.markdown.plantumlServerUrlHint`)}} q-item-section(side) q-input( style='width: 450px;' outlined v-model='state.config.plantumlServerUrl' dense :aria-label='t(`admin.editors.markdown.plantumlServerUrl`)' ) q-card.shadow-1.q-pb-sm.q-mt-md q-card-section .text-subtitle1 {{t('admin.editors.markdown.kroki')}} q-item(tag='label') blueprint-icon(icon='workflow') q-item-section q-item-label {{t(`admin.editors.markdown.kroki`)}} q-item-label(caption) {{t(`admin.editors.markdown.krokiHint`)}} q-item-section(avatar) q-toggle( v-model='state.config.kroki' color='primary' checked-icon='las la-check' unchecked-icon='las la-times' :aria-label='t(`admin.editors.markdown.kroki`)' ) template(v-if='state.config.kroki') q-separator.q-my-sm(inset) q-item blueprint-icon(icon='website') q-item-section q-item-label {{t(`admin.editors.markdown.krokiServerUrl`)}} q-item-label(caption) {{t(`admin.editors.markdown.krokiServerUrlHint`)}} q-item-section(side) q-input( style='width: 450px;' outlined v-model='state.config.krokiServerUrl' dense :aria-label='t(`admin.editors.markdown.krokiServerUrl`)' ) q-inner-loading(:showing='state.loading > 0') q-spinner(color='accent', size='lg') </template> <script setup> import { useI18n } from 'vue-i18n' import { useQuasar } from 'quasar' import { onMounted, reactive } from 'vue' import gql from 'graphql-tag' import { cloneDeep } from 'lodash-es' import { useAdminStore } from 'src/stores/admin' import { useEditorStore } from 'src/stores/editor' import { useSiteStore } from 'src/stores/site' // QUASAR const $q = useQuasar() // STORES const adminStore = useAdminStore() const editorStore = useEditorStore() const siteStore = useSiteStore() // I18N const { t } = useI18n() // DATA const state = reactive({ config: { allowHTML: false, linkify: false, lineBreaks: false, typographer: false, quotes: 'english', underline: false, tabWidth: 2, latexEngine: 'katex', multimdTable: false, plantuml: false, plantumlServerUrl: 'https://', kroki: false, krokiServerUrl: 'https://' }, loading: 0 }) const latexEngines = [ { value: 'katex', label: 'KaTeX' }, { value: 'mathjax', label: 'Mathjax' } ] const quoteStyles = [ { value: 'chinese', label: 'Chinese' }, { value: 'english', label: 'English' }, { value: 'french', label: 'French' }, { value: 'german', label: 'German' }, { value: 'greek', label: 'Greek' }, { value: 'japanese', label: 'Japanese' }, { value: 'hungarian', label: 'Hungarian' }, { value: 'polish', label: 'Polish' }, { value: 'portuguese', label: 'Portuguese' }, { value: 'russian', label: 'Russian' }, { value: 'spanish', label: 'Spanish' }, { value: 'swedish', label: 'Swedish' } ] // METHODS function close () { adminStore.$patch({ overlay: '' }) } async function load () { state.loading++ $q.loading.show() try { const resp = await APOLLO_CLIENT.query({ query: gql` query getEditorsState ( $siteId: UUID! ) { siteById ( id: $siteId ) { id editors { markdown { config } } } }`, variables: { siteId: adminStore.currentSiteId }, fetchPolicy: 'network-only' }) state.config = cloneDeep(resp?.data?.siteById?.editors?.markdown?.config) } catch (err) { $q.notify({ type: 'negative', message: 'Failed to fetch markdown editor configuration.' }) } $q.loading.hide() state.loading-- } async function save () { state.loading++ try { const respRaw = await APOLLO_CLIENT.mutate({ mutation: gql` mutation saveEditorState ( $id: UUID! $patch: SiteUpdateInput! ) { updateSite ( id: $id, patch: $patch ) { operation { succeeded slug message } } } `, variables: { id: adminStore.currentSiteId, patch: { editors: { markdown: { config: state.config } } } } }) if (respRaw?.data?.updateSite?.operation?.succeeded) { $q.notify({ type: 'positive', message: t('admin.editors.markdown.saveSuccess') }) editorStore.$patch({ configIsLoaded: false }) close() } else { throw new Error(respRaw?.data?.updateSite?.operation?.message || 'An unexpected error occured.') } } catch (err) { $q.notify({ type: 'negative', message: 'Failed to save Markdown editor config', caption: err.message }) } state.loading-- } onMounted(() => { load() }) </script>