fix: various UI fixes

This commit is contained in:
NGPixel
2026-08-25 00:49:59 -04:00
parent 7682e839c8
commit f537f04ecc
36 changed files with 272 additions and 105 deletions
+4 -1
View File
@@ -105,6 +105,9 @@ async function routes(app: FastifyInstance) {
dbHost: {
type: 'string'
},
dbVersion: {
type: 'string'
},
groupsTotal: {
type: 'number'
},
@@ -160,7 +163,7 @@ async function routes(app: FastifyInstance) {
type: 'string'
},
tagsTotal: {
type: 'string'
type: 'number'
},
upgradeCapable: {
type: 'boolean'
+12
View File
@@ -235,6 +235,18 @@ export class BlockIndexElement extends LitElement {
font-weight: normal;
pointer-events: none;
}
/*
On a dark row the writing takes the app's lightened brand shade, the same one the interface
gives a heading over a dark surface: primary is a mid-tone picked to read on white and is
too dim against #161b22. --color-primary-light is a mix of --q-primary, so a re-themed
site's own hue comes with it.
On .text rather than on the anchor, so it is the title alone that lightens: the icon and the
arrow are drawn with currentColor precisely so they keep the link's normal primary.
*/
:host([dark]) .text {
color: var(--color-primary-light);
}
/*
The page's own icon. Sized in em so it keeps its place beside writing at whatever size the
+11 -2
View File
@@ -114,7 +114,7 @@ Content of the second tab.
}
.tab:hover:not(.is-active) {
background-color: rgb(255 255 255 / 0.5);
color: var(--tabs-active-fg);
color: var(--tabs-active-label);
}
:host([dark]) .tab:hover:not(.is-active) {
background-color: rgb(255 255 255 / 0.05);
@@ -130,7 +130,7 @@ Content of the second tab.
border-bottom-color: var(--tabs-panel-bg);
background-color: var(--tabs-panel-bg);
background-image: none;
color: var(--tabs-active-fg);
color: var(--tabs-active-label);
}
.tab svg {
@@ -170,12 +170,21 @@ Content of the second tab.
--tabs-strip-bg: linear-gradient(to bottom, #fdfdfd, #eeeeee);
--tabs-inactive-fg: #424242;
--tabs-active-fg: var(--q-primary, #1976d2);
/*
The label of the tab being pointed at or opened. The active foreground, except on dark,
where writing in a mid-tone picked to read on white is too dim -- the strip and the panel
both sit near #1b212a. The edge that marks the open tab and the focus ring keep the plain
brand colour: they are shapes rather than writing, and read at that weight.
*/
--tabs-active-label: var(--tabs-active-fg);
--tabs-panel-bg: #fff;
}
:host([dark]) {
--tabs-border: rgba(255, 255, 255, 0.15);
--tabs-strip-bg: linear-gradient(to bottom, #1b212a, #12161d);
--tabs-inactive-fg: rgba(255, 255, 255, 0.7);
/* -> A mix of --q-primary, so a re-themed site's own hue comes with it. See block-index. */
--tabs-active-label: var(--color-primary-light);
--tabs-panel-bg: #1e232a;
}
`
+39 -12
View File
@@ -440,21 +440,27 @@ const HEADER_ICONS = [
]
/*
How the preview follows the caret: the line being edited goes to the TOP of the pane.
How the preview follows the caret: the line being edited is put a SHORT WAY down the pane.
`start` rather than `nearest`, which was tried and is wrong here -- `nearest` leaves a line alone as
long as it is visible anywhere, so a line sitting on the last row of the pane stays there, with what is
being written pinned to the bottom edge and nothing after it in view.
Not at its top, which is what this replaces. The top is exact, and that is the problem with it -- it
scrolls away everything written just above the caret, which is the paragraph the next one is being
written against, so the author is left editing at the edge of a pane with no context on one side of
it. Expressed as a fraction of the pane rather than a fixed inset so that a preview half the height
gives up half as much of itself to the lines already written.
Asking for the top on every caret move costs nothing when the caret stays put: the element is already
there, so the browser computes the same offset and there is no movement. What used to make this thrash
was the pane losing its scroll position to the re-render -- see `processContent` -- and animating up
from the top of the document each time, not the alignment asked for here.
`nearest` is not the answer either, at any offset: it leaves a line alone as long as it is visible
anywhere, so a line sitting on the last row of the pane stays there, with what is being written pinned
to the bottom edge and nothing after it in view. The position is computed and applied on every caret
move, and costs nothing when the caret stays put -- the arithmetic lands on the offset the pane is
already scrolled to and nothing moves. What used to make this thrash was the pane losing its scroll
position to the re-render -- see `processContent` -- and animating up from the top of the document each
time, not the alignment asked for here.
`inline: 'nearest'` only so that a wide block -- a table, a diagram -- is never scrolled sideways as a
side effect of following the caret down the page.
Scrolling the container rather than asking an element to bring itself into view also means a wide block
-- a table, a diagram -- is never scrolled sideways as a side effect of following the caret down the
page.
*/
const SYNC_SCROLL = { behavior: 'smooth', block: 'start', inline: 'nearest' }
const PREVIEW_CONTEXT_ABOVE = 0.2
/**
* Whether the window is wide enough to open the preview beside the source.
@@ -1162,6 +1168,24 @@ function previewAnchorFor(container, line) {
return best ?? panel
}
/**
* Scroll the preview so that `el` sits `PREVIEW_CONTEXT_ABOVE` of the way down the pane.
*
* Measured through `getBoundingClientRect` rather than `offsetTop`, because what is wanted is the
* distance to the SCROLL container and `offsetTop` answers to the nearest positioned ancestor -- which
* inside rendered page content is whatever a block happened to put around it.
*
* A negative result is clamped by `scrollTo`, so an anchor near the top of the document puts the
* document's own top in view rather than leaving a gap above it.
*/
function scrollPreviewTo(container, el) {
const offset = el.getBoundingClientRect().top - container.getBoundingClientRect().top
container.scrollTo({
top: container.scrollTop + offset - container.clientHeight * PREVIEW_CONTEXT_ABOVE,
behavior: 'smooth'
})
}
function processContent(newContent) {
/*
A render that throws must not become a render that is empty.
@@ -1569,7 +1593,10 @@ onMounted(async () => {
container.scrollTo({ top: 0, behavior: 'smooth' })
return
}
previewAnchorFor(container, currentLine)?.scrollIntoView(SYNC_SCROLL)
const anchor = previewAnchorFor(container, currentLine)
if (anchor) {
scrollPreviewTo(container, anchor)
}
}, 500)
)
@@ -252,7 +252,7 @@
<w-card-section class="alt-card pb-6" id="refCardSocial">
<div class="w-section-header">{{ t('editor.props.social') }}</div>
<w-form class="gap-4 pt-2">
<div>
<div v-if="flagsStore.experimental">
<w-toggle
v-model="pageStore.allowComments"
dense
@@ -270,7 +270,7 @@
checked-icon="la:check"
unchecked-icon="la:times" />
</div>
<div>
<div v-if="flagsStore.experimental">
<w-toggle
v-model="pageStore.allowRatings"
dense
@@ -352,6 +352,7 @@ import { useI18n } from 'vue-i18n'
import { computed, nextTick, onMounted, reactive, ref, watch } from 'vue'
import { useEditorStore } from '@/stores/editor'
import { useFlagsStore } from '@/stores/flags'
import { usePageStore } from '@/stores/page'
import { useSiteStore } from '@/stores/site'
@@ -364,6 +365,7 @@ import PageTags from './PageTags.vue'
// STORES
const editorStore = useEditorStore()
const flagsStore = useFlagsStore()
const pageStore = usePageStore()
const siteStore = useSiteStore()
+101 -8
View File
@@ -1,6 +1,14 @@
<template>
<nav class="page-toc" aria-label="Table of contents">
<ul class="page-toc-list">
<ul
class="page-toc-list"
:class="{ 'page-toc-list--animated': markerAnimated }"
ref="listEl"
:style="{
'--page-toc-marker-y': `${marker.y}px`,
'--page-toc-marker-h': `${marker.h}px`,
'--page-toc-marker-opacity': marker.visible ? 1 : 0
}">
<li
v-for="item of visibleItems"
:key="item.key"
@@ -24,7 +32,7 @@
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, watch } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from 'vue'
import { isVisible, scrollToAnchor } from '@/helpers/anchors'
import { flattenToc } from '@/helpers/toc'
@@ -90,6 +98,30 @@ const TAB_TAG = 'BLOCK-TAB'
let spyFrame = null
let spySuspendedUntil = 0
// DATA
const listEl = ref(null)
/*
Where the marker sits on the rail, in pixels down from the top of the list, and how tall it is.
Measured rather than expressed in CSS because the marker is one element for the whole list: that is
what lets it travel from one heading to the next instead of being switched off one row and on
another, and a pseudo-element on the active row can only ever do the latter.
*/
const marker = reactive({ y: 0, h: 0, visible: false })
/*
Whether the marker may animate yet. Off for its first placement, so that a reader arriving partway
down a page is not shown it sliding from the top of the list -- there is no move to follow there,
only the list settling.
*/
const markerAnimated = ref(false)
/* The inset the marker keeps at each end of a row, so it marks the label rather than the gap too. */
const MARKER_INSET = 2
let markerObserver = null
// COMPUTED
/**
@@ -201,6 +233,35 @@ function syncSpy() {
}
}
/**
* Put the marker over the active row.
*
* Offsets are read off the row rather than computed from the list, since every depth has its own
* type size and therefore its own height. `offsetTop` is relative to the list, which is the nearest
* positioned ancestor and so the marker's own containing block.
*/
function syncMarker() {
const list = listEl.value
const active = list?.querySelector('.page-toc-item--active')
if (!active) {
// -> Nothing marked: fade out where it stands, rather than travelling to the top of the list
marker.visible = false
return
}
marker.y = active.offsetTop + MARKER_INSET
marker.h = Math.max(active.offsetHeight - MARKER_INSET * 2, 0)
marker.visible = true
if (!markerAnimated.value) {
// -> A frame after the first placement, so the browser paints it there before the transition
// exists to animate away from it
requestAnimationFrame(() => {
markerAnimated.value = true
})
}
}
/** Scroll fires far more often than the marker can move; one read per frame is enough. */
function queueSpy() {
if (spyFrame !== null) {
@@ -217,6 +278,12 @@ function queueSpy() {
// -> A new render means new heading positions, and possibly a different set of them
watch(() => props.nodes, queueSpy)
// -> After the class the marker is measured from has been applied
watch([() => props.selected, visibleItems], async () => {
await nextTick()
syncMarker()
})
// MOUNTED
onMounted(() => {
@@ -228,6 +295,15 @@ onMounted(() => {
window.addEventListener('scroll', queueSpy, { capture: true, passive: true })
window.addEventListener('resize', queueSpy, { passive: true })
queueSpy()
/*
The rows move for reasons no event reports: the sidebar narrows at 1400px and long labels wrap
onto another line, which changes the height of the row the marker is on and the offset of every
row below it. Watching the list itself catches all of that, resizes included.
*/
markerObserver = new ResizeObserver(syncMarker)
markerObserver.observe(listEl.value)
syncMarker()
})
onBeforeUnmount(() => {
@@ -236,6 +312,7 @@ onBeforeUnmount(() => {
if (spyFrame !== null) {
cancelAnimationFrame(spyFrame)
}
markerObserver?.disconnect()
})
</script>
@@ -293,18 +370,33 @@ onBeforeUnmount(() => {
}
/*
The active marker, drawn ON the rail rather than beside it: `left: 0` is the item's own border
box, which starts at the rail whatever the indentation, so every depth marks the same line.
The active marker, drawn ON the rail rather than beside it: `left: 0` is the list's own border
box, which is where the rail is, so every depth marks the same line whatever its indentation.
One element for the whole list rather than a pseudo on the active row, so that moving to the next
heading is a slide down the rail instead of the marker being switched off one row and on another.
Where it goes cannot be said in CSS -- each depth has its own type size and so its own height --
so `syncMarker` measures the row and hands the two numbers over as custom properties.
*/
&-item--active::before {
&-list::after {
content: '';
position: absolute;
top: 2px;
bottom: 2px;
top: 0;
left: 0;
width: 2px;
height: var(--page-toc-marker-h, 0);
border-radius: 1px;
background-color: var(--color-primary);
opacity: var(--page-toc-marker-opacity, 0);
transform: translateY(var(--page-toc-marker-y, 0));
}
/* -> The class arrives a frame after the first placement, so the marker does not travel to it */
&-list--animated::after {
transition:
transform 0.25s var(--ease-standard),
height 0.25s var(--ease-standard),
opacity 0.15s var(--ease-standard);
}
&-link {
@@ -367,7 +459,8 @@ onBeforeUnmount(() => {
}
@media (prefers-reduced-motion: reduce) {
&-link {
&-link,
&-list--animated::after {
transition-duration: 0.01ms;
}
}
+14
View File
@@ -733,6 +733,20 @@
color: var(--color-primary-light);
}
/*
The heading at the top of every admin page. Only the colour lives here -- the size stays a
`text-h5` utility on the element -- because the one thing every copy of that header needs and
cannot express inline is the dark-mode shade: `primary` is picked to read on white and at
#1976d2 over `dark-5` it is too dim for a heading, the same problem `.w-section-header` has.
*/
.admin-page-title {
color: var(--color-primary);
}
body.body--dark .admin-page-title {
color: var(--color-primary-light);
}
.w-unstyled {
appearance: none;
background-color: transparent;
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-rest-api-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.api.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.api.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.api.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-inspection-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.approval.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.approval.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.approval.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-security-lock.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.auth.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.auth.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.auth.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-plugin.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.blocks.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.blocks.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.blocks.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-apps-tab-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.dashboard.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.dashboard.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.dashboard.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-cashbook.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.editors.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.editors.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.editors.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-module.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">
<div class="text-h5 admin-page-title animated fadeInLeft">
{{ t('admin.extensions.title') }}
</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-windsock-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.flags.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.flags.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.flags.subtitle') }}
</div>
+55 -49
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-web.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.general.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.general.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.general.subtitle') }}
</div>
@@ -185,19 +185,21 @@
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
<w-item tag="label">
<blueprint-icon icon="discussion-forum" />
<w-item-section>
<w-item-label>{{ t(`admin.general.allowComments`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.allowCommentsHint`) }}</w-item-label>
</w-item-section>
<w-item-section avatar>
<w-toggle
v-model="state.config.features.comments"
:aria-label="t(`admin.general.allowComments`)" />
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
<template v-if="flagsStore.experimental">
<w-item tag="label">
<blueprint-icon icon="discussion-forum" />
<w-item-section>
<w-item-label>{{ t(`admin.general.allowComments`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.allowCommentsHint`) }}</w-item-label>
</w-item-section>
<w-item-section avatar>
<w-toggle
v-model="state.config.features.comments"
:aria-label="t(`admin.general.allowComments`)" />
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
</template>
<w-item tag="label">
<blueprint-icon icon="administrator-male" />
<w-item-section>
@@ -211,23 +213,25 @@
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
<w-item>
<blueprint-icon icon="star-half-empty" />
<w-item-section>
<w-item-label>{{ t(`admin.general.allowRatings`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.allowRatingsHint`) }}</w-item-label>
</w-item-section>
<w-item-section class="flex-none">
<w-btn-toggle
v-model="state.config.features.ratingsMode"
push
glossy
no-caps
toggle-color="primary"
:options="ratingsModes" />
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
<template v-if="flagsStore.experimental">
<w-item>
<blueprint-icon icon="star-half-empty" />
<w-item-section>
<w-item-label>{{ t(`admin.general.allowRatings`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.allowRatingsHint`) }}</w-item-label>
</w-item-section>
<w-item-section class="flex-none">
<w-btn-toggle
v-model="state.config.features.ratingsMode"
push
glossy
no-caps
toggle-color="primary"
:options="ratingsModes" />
</w-item-section>
</w-item>
<w-separator class="my-2" inset />
</template>
<w-item tag="label">
<blueprint-icon icon="search" />
<w-item-section>
@@ -290,6 +294,24 @@
</w-item-section>
</w-item>
</w-card>
<!-- ----------------------- -->
<!-- Discovery -->
<!-- ----------------------- -->
<w-card class="pb-2 mt-4">
<w-card-header>{{ t('admin.general.discovery') }}</w-card-header>
<w-item tag="label">
<blueprint-icon icon="cellular-network" />
<w-item-section>
<w-item-label>{{ t(`admin.general.discoverable`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.discoverableHint`) }}</w-item-label>
</w-item-section>
<w-item-section avatar>
<w-toggle
v-model="state.config.discoverable"
:aria-label="t(`admin.general.discoverable`)" />
</w-item-section>
</w-item>
</w-card>
</div>
<div class="col-span-12 lg:col-span-5">
<!-- ----------------------- -->
@@ -464,24 +486,6 @@
</w-item>
</w-card>
<!-- ----------------------- -->
<!-- Discovery -->
<!-- ----------------------- -->
<w-card class="pb-2 mt-4">
<w-card-header>{{ t('admin.general.discovery') }}</w-card-header>
<w-item tag="label">
<blueprint-icon icon="cellular-network" />
<w-item-section>
<w-item-label>{{ t(`admin.general.discoverable`) }}</w-item-label>
<w-item-label caption>{{ t(`admin.general.discoverableHint`) }}</w-item-label>
</w-item-section>
<w-item-section avatar>
<w-toggle
v-model="state.config.discoverable"
:aria-label="t(`admin.general.discoverable`)" />
</w-item-section>
</w-item>
</w-card>
<!-- ----------------------- -->
<!-- Uploads -->
<!-- ----------------------- -->
<w-card class="pb-2 mt-4" v-if="state.config.uploads">
@@ -585,6 +589,7 @@ import { notify } from '@/composables/notify'
import { loading } from '@/composables/loading'
import { useAdminStore } from '@/stores/admin'
import { useFlagsStore } from '@/stores/flags'
import { useSiteStore } from '@/stores/site'
import UtilCodeEditor from '@/components/UtilCodeEditor.vue'
@@ -601,6 +606,7 @@ import { toMerged } from 'es-toolkit/object'
// STORES
const adminStore = useAdminStore()
const flagsStore = useFlagsStore()
const siteStore = useSiteStore()
// I18N
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-people.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.groups.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.groups.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.groups.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-spring.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.icons.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.icons.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.icons.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-network-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.instances.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.instances.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.instances.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-language.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.locale.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.locale.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.locale.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-bunch-of-keys-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.login.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.login.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.login.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-message-settings-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.mail.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.mail.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.mail.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-graph.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.metrics.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.metrics.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.metrics.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-tree-structure.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">
<div class="text-h5 admin-page-title animated fadeInLeft">
{{ t('admin.navigation.title') }}
</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-rich-text-converter.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">
<div class="text-h5 admin-page-title animated fadeInLeft">
{{ $t('admin.rendering.title') }}
</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-bot-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.scheduler.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.scheduler.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.scheduler.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-find-and-replace-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.search.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.search.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.search.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-protect.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.security.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.security.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.security.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-change-theme.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.sites.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.sites.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.sites.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-ssd-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.storage.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.storage.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.storage.subtitle') }}
</div>
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-processor.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.system.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.system.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.system.subtitle') }}
</div>
+3 -2
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-linux-terminal-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.terminal.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.terminal.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.terminal.subtitle') }}
</div>
@@ -15,7 +15,8 @@
<div class="flex-none flex items-center">
<div v-if="state.connected" class="mr-4 text-right leading-tight">
<div class="text-xs text-grey">{{ t('admin.terminal.instance') }}</div>
<div class="flex items-center justify-end gap-1.5 font-mono text-sm">
<div
class="flex items-center justify-end gap-1.5 font-mono text-sm text-grey-9 dark:text-grey-3">
<status-light class="admin-terminal-dot" color="positive" pulse />
{{ state.instance }}
</div>
+3 -3
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-paint-roller-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.theme.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.theme.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.theme.subtitle') }}
</div>
@@ -217,7 +217,7 @@
<!-- ----------------------- -->
<!-- Fonts -->
<!-- ----------------------- -->
<w-card class="pb-2">
<w-card class="pb-2" v-if="flagStore.experimental">
<w-card-header>
{{ t('admin.theme.fonts') }}
<template #action>
@@ -269,7 +269,7 @@
<!-- ----------------------- -->
<!-- Code Injection -->
<!-- ----------------------- -->
<w-card class="pb-2 mt-4">
<w-card :class="['pb-2', { 'mt-4': flagStore.experimental }]">
<w-card-header>{{ t('admin.theme.codeInjection') }}</w-card-header>
<w-item>
<blueprint-icon icon="css" />
+1 -1
View File
@@ -5,7 +5,7 @@
<img class="admin-icon animated fadeInLeft" src="/_assets/icons/fluent-account.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.users.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.users.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.users.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-swiss-army-knife-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.utilities.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.utilities.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.utilities.subtitle') }}
</div>
+1 -1
View File
@@ -7,7 +7,7 @@
src="/_assets/icons/fluent-lightning-bolt-animated.svg" />
</div>
<div class="min-w-0 flex-1 pl-4">
<div class="text-h5 text-primary animated fadeInLeft">{{ t('admin.webhooks.title') }}</div>
<div class="text-h5 admin-page-title animated fadeInLeft">{{ t('admin.webhooks.title') }}</div>
<div class="text-subtitle1 text-grey animated fadeInLeft wait-p2s">
{{ t('admin.webhooks.subtitle') }}
</div>