diff --git a/client/themes/splynx/components/image-modal.vue b/client/themes/splynx/components/image-modal.vue
new file mode 100644
index 00000000..38c589ee
--- /dev/null
+++ b/client/themes/splynx/components/image-modal.vue
@@ -0,0 +1,59 @@
+
+ v-dialog(v-model="dialog", :max-width="maxWidth", transition="dialog-bottom-transition")
+ v-card
+ v-toolbar(flat, dense, dark, color="white")
+ v-spacer
+ v-btn(icon, dark, @click="dialog = false")
+ v-icon(color="black") mdi-close
+ v-card-text
+ v-img(:src="photoUrl", :alt="photoAlt", height="700px", contain)
+ v-card-actions
+ v-spacer
+ v-btn(color="dark darken-1", text, @click="close") Close
+
+
+
diff --git a/client/themes/splynx/components/nav-header.vue b/client/themes/splynx/components/nav-header.vue
new file mode 100644
index 00000000..4da91d2d
--- /dev/null
+++ b/client/themes/splynx/components/nav-header.vue
@@ -0,0 +1,571 @@
+
+ v-app-bar.nav-header(color='black', dark, app, :clipped-left='!$vuetify.rtl', :clipped-right='$vuetify.rtl', fixed, flat, :extended='searchIsShown && $vuetify.breakpoint.smAndDown')
+ v-toolbar(color='deep-purple', flat, slot='extension', v-if='searchIsShown && $vuetify.breakpoint.smAndDown')
+ v-text-field(
+ ref='searchFieldMobile'
+ v-model='search'
+ clearable
+ background-color='deep-purple'
+ color='white'
+ :label='$t(`common:header.search`)'
+ single-line
+ solo
+ flat
+ hide-details
+ prepend-inner-icon='mdi-magnify'
+ :loading='searchIsLoading'
+ @keyup.enter='searchEnter'
+ autocomplete='none'
+ )
+ v-layout(row)
+ v-flex(xs5, md4)
+ v-toolbar.nav-header-inner(color='black', dark, flat, :class='$vuetify.rtl ? `pr-3` : `pl-3`')
+ v-avatar(tile, size='34', @click='goHome')
+ v-img.org-logo(:src='logoUrl')
+ //- v-menu(open-on-hover, offset-y, bottom, left, min-width='250', transition='slide-y-transition')
+ //- template(v-slot:activator='{ on }')
+ //- v-app-bar-nav-icon.btn-animate-app(v-on='on', :class='$vuetify.rtl ? `mx-0` : ``')
+ //- v-icon mdi-menu
+ //- v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
+ //- v-list-item.pl-4(href='/')
+ //- v-list-item-avatar(size='24'): v-icon(color='blue') mdi-home
+ //- v-list-item-title.body-2 {{$t('common:header.home')}}
+ //- v-list-item.pl-4(@click='')
+ //- v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-file-tree
+ //- v-list-item-content
+ //- v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.siteMap')}}
+ //- v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon
+ //- v-list-item.pl-4(href='/t')
+ //- v-list-item-avatar(size='24'): v-icon(color='teal') mdi-tag-multiple
+ //- v-list-item-title.body-2 {{$t('common:header.browseTags')}}
+ //- v-list-item.pl-4(@click='assets')
+ //- v-list-item-avatar(size='24'): v-icon(color='grey lighten-2') mdi-folder-multiple-image
+ //- v-list-item-content
+ //- v-list-item-title.body-2.grey--text.text--ligten-2 {{$t('common:header.imagesFiles')}}
+ //- v-list-item-subtitle.overline.grey--text.text--lighten-2 Coming soon
+ v-toolbar-title(:class='{ "mx-3": $vuetify.breakpoint.mdAndUp, "mx-1": $vuetify.breakpoint.smAndDown }')
+ span.subheading {{title}}
+ v-flex(md4, v-if='$vuetify.breakpoint.mdAndUp')
+ v-toolbar.nav-header-inner(color='black', dark, flat)
+ slot(name='mid')
+ transition(name='navHeaderSearch', v-if='searchIsShown')
+ v-text-field(
+ ref='searchField',
+ v-if='searchIsShown && $vuetify.breakpoint.mdAndUp',
+ v-model='search',
+ color='white',
+ :label='$t(`common:header.search`)',
+ single-line,
+ solo
+ flat
+ rounded
+ hide-details,
+ prepend-inner-icon='mdi-magnify',
+ :loading='searchIsLoading',
+ @keyup.enter='searchEnter'
+ @keyup.esc='searchClose'
+ @focus='searchFocus'
+ @blur='searchBlur'
+ @keyup.down='searchMove(`down`)'
+ @keyup.up='searchMove(`up`)'
+ autocomplete='none'
+ )
+ v-tooltip(bottom)
+ template(v-slot:activator='{ on }')
+ v-btn.ml-2.mr-0(icon, v-on='on', href='/t', :aria-label='$t(`common:header.browseTags`)')
+ v-icon(color='grey') mdi-tag-multiple
+ span {{$t('common:header.browseTags')}}
+ v-flex(xs7, md4)
+ v-toolbar.nav-header-inner.pr-4(color='black', dark, flat)
+ v-spacer
+ .navHeaderLoading.mr-3
+ v-progress-circular(indeterminate, color='blue', :size='22', :width='2' v-show='isLoading')
+
+ slot(name='actions')
+
+ //- (mobile) SEARCH TOGGLE
+
+ v-btn(
+ v-if='!hideSearch && $vuetify.breakpoint.smAndDown'
+ @click='searchToggle'
+ icon
+ )
+ v-icon(color='grey') mdi-magnify
+
+ //- LANGUAGES
+
+ template(v-if='mode === `view` && locales.length > 0')
+ v-menu(offset-y, bottom, transition='slide-y-transition', max-height='320px', min-width='210px', left)
+ template(v-slot:activator='{ on: menu, attrs }')
+ v-tooltip(bottom)
+ template(v-slot:activator='{ on: tooltip }')
+ v-btn(
+ icon
+ v-bind='attrs'
+ v-on='{ ...menu, ...tooltip }'
+ :class='$vuetify.rtl ? `ml-3` : ``'
+ tile
+ height='64'
+ :aria-label='$t(`common:header.language`)'
+ )
+ v-icon(color='grey') mdi-web
+ span {{$t('common:header.language')}}
+ v-list(nav)
+ template(v-for='(lc, idx) of locales')
+ v-list-item(@click='changeLocale(lc)')
+ v-list-item-action(style='min-width:auto;'): v-chip(:color='lc.code === locale ? `blue` : `grey`', small, label, dark) {{lc.code.toUpperCase()}}
+ v-list-item-title {{lc.name}}
+ v-divider(vertical)
+
+ //- PAGE ACTIONS
+
+ template(v-if='hasAnyPagePermissions && path && mode !== `edit`')
+ v-menu(offset-y, bottom, transition='slide-y-transition', left)
+ template(v-slot:activator='{ on: menu, attrs }')
+ v-tooltip(bottom)
+ template(v-slot:activator='{ on: tooltip }')
+ v-btn(
+ icon
+ v-bind='attrs'
+ v-on='{ ...menu, ...tooltip }'
+ :class='$vuetify.rtl ? `ml-3` : ``'
+ tile
+ height='64'
+ :aria-label='$t(`common:header.pageActions`)'
+ )
+ v-icon(color='grey') mdi-file-document-edit-outline
+ span {{$t('common:header.pageActions')}}
+ v-list(nav, :light='!$vuetify.theme.dark', :dark='$vuetify.theme.dark', :class='$vuetify.theme.dark ? `grey darken-4` : ``')
+ .overline.pa-4.grey--text {{$t('common:header.currentPage')}}
+ v-list-item.pl-4(@click='pageView', v-if='mode !== `view`')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-outline
+ v-list-item-title.body-2 {{$t('common:header.view')}}
+ v-list-item.pl-4(@click='pageEdit', v-if='mode !== `edit` && hasWritePagesPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-file-document-edit-outline
+ v-list-item-title.body-2 {{$t('common:header.edit')}}
+ v-list-item.pl-4(@click='pageHistory', v-if='mode !== `history` && hasReadHistoryPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-history
+ v-list-item-content
+ v-list-item-title.body-2 {{$t('common:header.history')}}
+ v-list-item.pl-4(@click='pageSource', v-if='mode !== `source` && hasReadSourcePermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-code-tags
+ v-list-item-title.body-2 {{$t('common:header.viewSource')}}
+ v-list-item.pl-4(@click='pageConvert', v-if='hasWritePagesPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-lightning-bolt
+ v-list-item-title.body-2 {{$t('common:header.convert')}}
+ v-list-item.pl-4(@click='pageDuplicate', v-if='hasWritePagesPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-duplicate
+ v-list-item-title.body-2 {{$t('common:header.duplicate')}}
+ v-list-item.pl-4(@click='pageMove', v-if='hasManagePagesPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='indigo') mdi-content-save-move-outline
+ v-list-item-content
+ v-list-item-title.body-2 {{$t('common:header.move')}}
+ v-list-item.pl-4(@click='pageDelete', v-if='hasDeletePagesPermission')
+ v-list-item-avatar(size='24', tile): v-icon(color='red darken-2') mdi-trash-can-outline
+ v-list-item-title.body-2 {{$t('common:header.delete')}}
+ v-divider(vertical)
+
+ //- NEW PAGE
+
+ template(v-if='hasNewPagePermission && path && mode !== `edit`')
+ v-tooltip(bottom)
+ template(v-slot:activator='{ on }')
+ v-btn(icon, tile, height='64', v-on='on', @click='pageNew', :aria-label='$t(`common:header.newPage`)')
+ v-icon(color='grey') mdi-text-box-plus-outline
+ span {{$t('common:header.newPage')}}
+ v-divider(vertical)
+
+ //- ADMIN
+
+ template(v-if='isAuthenticated && isAdmin')
+ v-tooltip(bottom, v-if='mode !== `admin`')
+ template(v-slot:activator='{ on }')
+ v-btn(icon, tile, height='64', v-on='on', href='/a', :aria-label='$t(`common:header.admin`)')
+ v-icon(color='grey') mdi-cog
+ span {{$t('common:header.admin')}}
+ v-btn(v-else, text, tile, height='64', href='/', :aria-label='$t(`common:actions.exit`)')
+ v-icon(left, color='grey') mdi-exit-to-app
+ span {{$t('common:actions.exit')}}
+ v-divider(vertical)
+
+ //- ACCOUNT
+
+ v-menu(v-if='isAuthenticated', offset-y, bottom, min-width='300', transition='slide-y-transition', left)
+ template(v-slot:activator='{ on: menu, attrs }')
+ v-tooltip(bottom)
+ template(v-slot:activator='{ on: tooltip }')
+ v-btn(
+ icon
+ v-bind='attrs'
+ v-on='{ ...menu, ...tooltip }'
+ :class='$vuetify.rtl ? `ml-0` : ``'
+ tile
+ height='64'
+ :aria-label='$t(`common:header.account`)'
+ )
+ v-icon(v-if='picture.kind === `initials`', color='grey') mdi-account-circle
+ v-avatar(v-else-if='picture.kind === `image`', :size='34')
+ v-img(:src='picture.url')
+ span {{$t('common:header.account')}}
+ v-list(nav)
+ v-list-item.py-3.grey(:class='$vuetify.theme.dark ? `darken-4-l5` : `lighten-5`')
+ v-list-item-avatar
+ v-avatar.blue(v-if='picture.kind === `initials`', :size='40')
+ span.white--text.subheading {{picture.initials}}
+ v-avatar(v-else-if='picture.kind === `image`', :size='40')
+ v-img(:src='picture.url')
+ v-list-item-content
+ v-list-item-title {{name}}
+ v-list-item-subtitle {{email}}
+ //- v-list-item(href='/w', disabled)
+ //- v-list-item-action: v-icon(color='blue') mdi-view-compact-outline
+ //- v-list-item-content
+ //- v-list-item-title {{$t('common:header.myWiki')}}
+ //- v-list-item-subtitle.overline Coming soon
+ v-list-item(href='/p')
+ v-list-item-action: v-icon(color='blue-grey') mdi-face-profile
+ v-list-item-content
+ v-list-item-title(:class='$vuetify.theme.dark ? `blue-grey--text text--lighten-3` : `blue-grey--text`') {{$t('common:header.profile')}}
+ v-list-item(@click='logout')
+ v-list-item-action: v-icon(color='red') mdi-logout
+ v-list-item-title.red--text {{$t('common:header.logout')}}
+
+ v-tooltip(v-else, left)
+ template(v-slot:activator='{ on }')
+ v-btn(icon, v-on='on', color='grey darken-3', href='/login', :aria-label='$t(`common:header.login`)')
+ v-icon(color='grey') mdi-account-circle
+ span {{$t('common:header.login')}}
+
+ page-selector(mode='create', v-model='newPageModal', :open-handler='pageNewCreate', :locale='locale')
+ page-selector(mode='move', v-model='movePageModal', :open-handler='pageMoveRename', :path='path', :locale='locale')
+ page-selector(mode='create', v-model='duplicateOpts.modal', :open-handler='pageDuplicateHandle', :path='duplicateOpts.path', :locale='duplicateOpts.locale')
+ page-delete(v-model='deletePageModal', v-if='path && path.length')
+ page-convert(v-model='convertPageModal', v-if='path && path.length')
+
+ .nav-header-dev(v-if='isDevMode')
+ v-icon mdi-alert
+ div
+ .overline DEVELOPMENT VERSION
+ .overline This code base is NOT for production use!
+
+
+
+
+
diff --git a/client/themes/splynx/components/page.vue b/client/themes/splynx/components/page.vue
index 718cd421..e18d916f 100644
--- a/client/themes/splynx/components/page.vue
+++ b/client/themes/splynx/components/page.vue
@@ -331,12 +331,15 @@
:aria-label='$t(`common:actions.returnToTop`)'
)
v-icon mdi-arrow-up
+ image-modal(ref="imageModal")