+
+ {{ ' - ' }}
+
+
{{ $t('general.generic_error_details') }}
+
+ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 06fbf45f9..e2946b162 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -1,7 +1,7 @@
# This file is a template, and might need editing before it works on your project.
# Official framework image. Look for the different tagged releases at:
# https://hub.docker.com/r/library/node/tags/
-image: node:18
+image: node:20
stages:
- check-changelog
@@ -73,7 +73,7 @@ test:
e2e-pleroma:
stage: test
- image: mcr.microsoft.com/playwright:v1.57.0-jammy
+ image: mcr.microsoft.com/playwright:v1.61.0-jammy
services:
- name: postgres:15-alpine
alias: db
@@ -157,7 +157,7 @@ e2e-pleroma:
variables:
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
FF_NETWORK_PER_BUILD: "true"
- PLEROMA_IMAGE: git.pleroma.social:5050/pleroma/pleroma:stable
+ PLEROMA_IMAGE: git.pleroma.social/pleroma/pleroma:stable
POSTGRES_USER: pleroma
POSTGRES_PASSWORD: pleroma
POSTGRES_DB: pleroma
@@ -175,7 +175,7 @@ e2e-pleroma:
NOTIFY_EMAIL: $E2E_ADMIN_EMAIL
VITE_PROXY_TARGET: http://pleroma:4000
VITE_PROXY_ORIGIN: http://localhost:4000
- E2E_BASE_URL: http://localhost:8080
+ E2E_BASE_URL: http://localhost:8099
script:
- npm install -g yarn@1.22.22
- yarn --frozen-lockfile
diff --git a/.node-version b/.node-version
index 08b7109d0..5bd681170 100644
--- a/.node-version
+++ b/.node-version
@@ -1 +1 @@
-18.20.8
+20.19.0
diff --git a/.woodpecker/build.yaml b/.woodpecker/build.yaml
index 059fdb1ff..af0bb98e3 100644
--- a/.woodpecker/build.yaml
+++ b/.woodpecker/build.yaml
@@ -16,7 +16,7 @@ labels:
steps:
build:
- image: docker.io/node:18-alpine
+ image: docker.io/node:20-alpine
commands:
- apk add --no-cache zip git
- yarn --frozen-lockfile
diff --git a/.woodpecker/lint.yaml b/.woodpecker/lint.yaml
index 237135ee9..257887338 100644
--- a/.woodpecker/lint.yaml
+++ b/.woodpecker/lint.yaml
@@ -9,7 +9,7 @@ when:
steps:
install-depends:
image: &node-image
- docker.io/node:18-alpine
+ docker.io/node:20-alpine
commands:
- yarn --frozen-lockfile
diff --git a/.woodpecker/test-e2e.yaml b/.woodpecker/test-e2e.yaml
index c0bf103cd..2a7d1511d 100644
--- a/.woodpecker/test-e2e.yaml
+++ b/.woodpecker/test-e2e.yaml
@@ -25,12 +25,12 @@ variables:
steps:
test:
- image: mcr.microsoft.com/playwright:v1.57.0-jammy
+ image: mcr.microsoft.com/playwright:v1.61.0-jammy
entrypoint: *script_file_entrypoint
environment:
APT_CACHE_DIR: apt-cache
DEBIAN_FRONTEND: noninteractive
- E2E_BASE_URL: http://localhost:8080
+ E2E_BASE_URL: http://localhost:8099
FF_NETWORK_PER_BUILD: "true"
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: "1"
VITE_PROXY_ORIGIN: "http://pleroma:4000"
diff --git a/.woodpecker/test.yaml b/.woodpecker/test.yaml
index acc48aacc..89f6f2f93 100644
--- a/.woodpecker/test.yaml
+++ b/.woodpecker/test.yaml
@@ -25,7 +25,7 @@ variables:
steps:
test:
- image: mcr.microsoft.com/playwright:v1.57.0-jammy
+ image: mcr.microsoft.com/playwright:v1.61.0-jammy
environment:
APT_CACHE_DIR: apt-cache
DEBIAN_FRONTEND: noninteractive
diff --git a/biome.json b/biome.json
index 6a464a0e5..5ef6f79ee 100644
--- a/biome.json
+++ b/biome.json
@@ -46,6 +46,7 @@
"noUnusedLabels": "error",
"noUnusedPrivateClassMembers": "error",
"noUnusedVariables": "error",
+ "noUnusedImports": "error",
"useIsNan": "error",
"useValidForDirection": "error",
"useValidTypeof": "error",
diff --git a/build/check-versions.mjs b/build/check-versions.mjs
index 8c5968a30..c22004b00 100644
--- a/build/check-versions.mjs
+++ b/build/check-versions.mjs
@@ -36,7 +36,6 @@ export default function () {
const warning = warnings[i]
console.warn(' ' + warning)
}
- console.warn()
process.exit(1)
}
}
diff --git a/build/sw_plugin.js b/build/sw_plugin.js
index 03c5978d7..2f0a4819d 100644
--- a/build/sw_plugin.js
+++ b/build/sw_plugin.js
@@ -1,7 +1,6 @@
-import { readFile } from 'node:fs/promises'
import { dirname, resolve } from 'node:path'
import { fileURLToPath } from 'node:url'
-import * as esbuild from 'esbuild'
+import { exactRegex } from '@rolldown/pluginutils'
import { build } from 'vite'
import {
@@ -15,106 +14,6 @@ const getSWMessagesAsText = async () => {
}
const projectRoot = dirname(dirname(fileURLToPath(import.meta.url)))
-const swEnvName = 'virtual:pleroma-fe/service_worker_env'
-const swEnvNameResolved = '\0' + swEnvName
-const getDevSwEnv = () => `self.serviceWorkerOption = { assets: [] };`
-const getProdSwEnv = ({ assets }) =>
- `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };`
-
-export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => {
- const swFullSrc = resolve(projectRoot, swSrc)
- const esbuildAlias = {}
- Object.entries(alias).forEach(([source, dest]) => {
- esbuildAlias[source] = dest.startsWith('/') ? projectRoot + dest : dest
- })
-
- return {
- name: 'dev-sw-plugin',
- apply: 'serve',
- configResolved() {
- /* no-op */
- },
- resolveId(id) {
- const name = id.startsWith('/') ? id.slice(1) : id
- if (name === swDest) {
- return swFullSrc
- } else if (name === swEnvName) {
- return swEnvNameResolved
- }
- return null
- },
- async load(id) {
- if (id === swFullSrc) {
- return readFile(swFullSrc, 'utf-8')
- } else if (id === swEnvNameResolved) {
- return getDevSwEnv()
- }
- return null
- },
- /**
- * vite does not bundle the service worker
- * during dev, and firefox does not support ESM as service worker
- * https://bugzilla.mozilla.org/show_bug.cgi?id=1360870
- */
- async transform(code, id) {
- if (id === swFullSrc && transformSW) {
- const res = await esbuild.build({
- entryPoints: [swSrc],
- bundle: true,
- write: false,
- outfile: 'sw-pleroma.js',
- alias: esbuildAlias,
- plugins: [
- {
- name: 'vite-like-root-resolve',
- setup(b) {
- b.onResolve({ filter: new RegExp(/^\//) }, (args) => ({
- path: resolve(projectRoot, args.path.slice(1)),
- }))
- },
- },
- {
- name: 'sw-messages',
- setup(b) {
- b.onResolve(
- { filter: new RegExp('^' + swMessagesName + '$') },
- (args) => ({
- path: args.path,
- namespace: 'sw-messages',
- }),
- )
- b.onLoad(
- { filter: /.*/, namespace: 'sw-messages' },
- async () => ({
- contents: await getSWMessagesAsText(),
- }),
- )
- },
- },
- {
- name: 'sw-env',
- setup(b) {
- b.onResolve(
- { filter: new RegExp('^' + swEnvName + '$') },
- (args) => ({
- path: args.path,
- namespace: 'sw-env',
- }),
- )
- b.onLoad({ filter: /.*/, namespace: 'sw-env' }, () => ({
- contents: getDevSwEnv(),
- }))
- },
- },
- ],
- })
- const text = res.outputFiles[0].text
- return text
- }
- },
- }
-}
-
// Idea taken from
// https://github.com/vite-pwa/vite-plugin-pwa/blob/main/src/plugins/build.ts
// rollup does not support compiling to iife if we want to code-split;
@@ -122,12 +21,17 @@ export const devSwPlugin = ({ swSrc, swDest, transformSW, alias }) => {
// Run another vite build just for the service worker targeting iife at
// the end of the build.
export const buildSwPlugin = ({ swSrc, swDest }) => {
+ const swFullSrc = resolve(projectRoot, swSrc)
+ const swEnvName = 'virtual:pleroma-fe/service_worker_env'
+ const swEnvNameResolved = '\0' + swEnvName
+
let config
+
return {
name: 'build-sw-plugin',
enforce: 'post',
- apply: 'build',
configResolved(resolvedConfig) {
+ resolvedConfig
config = {
define: resolvedConfig.define,
resolve: resolvedConfig.resolve,
@@ -135,15 +39,16 @@ export const buildSwPlugin = ({ swSrc, swDest }) => {
publicDir: false,
build: {
...resolvedConfig.build,
- lib: {
- entry: swSrc,
- formats: ['iife'],
- name: 'sw_pleroma',
- },
emptyOutDir: false,
- rollupOptions: {
+ rolldownOptions: {
+ input: {
+ main: swSrc,
+ },
+ context: 'self',
output: {
entryFileNames: swDest,
+ codeSplitting: false,
+ format: 'iife',
},
},
},
@@ -157,29 +62,64 @@ export const buildSwPlugin = ({ swSrc, swDest }) => {
const assets = Object.keys(bundle)
.filter((name) => !/\.map$/.test(name))
.map((name) => '/' + name)
+
config.plugins.push({
name: 'build-sw-env-plugin',
- resolveId(id) {
- if (id === swEnvName) {
- return swEnvNameResolved
- }
- return null
+ mode: 'production',
+ resolveId: {
+ filter: { id: exactRegex(swEnvName) },
+ handler: () => swEnvNameResolved,
},
- load(id) {
- if (id === swEnvNameResolved) {
- return getProdSwEnv({ assets })
- }
- return null
+ load: {
+ filter: { id: exactRegex(swEnvNameResolved) },
+ handler() {
+ return `self.serviceWorkerOption = { assets: ${JSON.stringify(assets)} };`
+ },
},
})
},
},
+ resolveId: {
+ filter: { id: new RegExp(swDest) },
+ handler() {
+ return swFullSrc
+ },
+ },
+ load: {
+ filter: { id: new RegExp(swFullSrc) },
+ async handler() {
+ config.plugins.push({
+ name: 'dummy-sw-env',
+ mode: 'development',
+ resolveId: {
+ filter: { id: exactRegex(swEnvName) },
+ handler: () => swEnvNameResolved,
+ },
+ load: {
+ filter: { id: exactRegex(swEnvNameResolved) },
+ handler: () => 'self.serviceWorkerOption = { assets: [] }',
+ },
+ })
+
+ try {
+ const swBundle = await build(config)
+ return swBundle.output[0]
+ } catch (e) {
+ console.error('Error building ServiceWorker:', e)
+ }
+ },
+ },
closeBundle: {
order: 'post',
sequential: true,
async handler() {
+ if (process.env.VITEST) return
console.info('Building service worker for production')
- await build(config)
+ try {
+ await build(config)
+ } catch (e) {
+ console.error('Error building ServiceWorker:', e)
+ }
},
},
}
diff --git a/changelog.d/api-refactor.skip b/changelog.d/api-refactor.skip
new file mode 100644
index 000000000..e69de29bb
diff --git a/changelog.d/fast.change b/changelog.d/fast.change
new file mode 100644
index 000000000..1f0a89092
--- /dev/null
+++ b/changelog.d/fast.change
@@ -0,0 +1 @@
+Migrated to Vite 8 and optimized our imports, more stuff is loaded on-demand, reducing the initial load time and transfer size
diff --git a/changelog.d/minor.change b/changelog.d/minor.change
index a234ad9c2..979d36955 100644
--- a/changelog.d/minor.change
+++ b/changelog.d/minor.change
@@ -4,3 +4,4 @@ reply/quote now is a radio group and wraps, fixes overflow on languages where la
personal note input is now bigger
moved "edit pinned" to the bottom for status action buttons.
dots status action button drops down instead of up to avoid hiding the action buttons
+improved attachment description (alt text) input and display
diff --git a/changelog.d/minor.fix b/changelog.d/minor.fix
index 69d7b11fe..420836364 100644
--- a/changelog.d/minor.fix
+++ b/changelog.d/minor.fix
@@ -9,3 +9,4 @@ second language input not having header
post form's bottom left buttons not showing their toggled state
some font overrides not working
popovers opening outside of window's boundaries
+occasional blank page when showing new posts
diff --git a/changelog.d/more-fixes.skip b/changelog.d/more-fixes.skip
new file mode 100644
index 000000000..e69de29bb
diff --git a/changelog.d/profilebg.add b/changelog.d/profilebg.add
new file mode 100644
index 000000000..a2c79074a
--- /dev/null
+++ b/changelog.d/profilebg.add
@@ -0,0 +1 @@
+displaying other user's backgrounds (if supported by BE)
diff --git a/changelog.d/reply-quote-config.fix b/changelog.d/reply-quote-config.fix
new file mode 100644
index 000000000..b6ac4e5e9
--- /dev/null
+++ b/changelog.d/reply-quote-config.fix
@@ -0,0 +1 @@
+Fix reply form crash when quote-reply settings are unavailable
diff --git a/changelog.d/user-management.add b/changelog.d/user-management.add
new file mode 100644
index 000000000..ccd217f19
--- /dev/null
+++ b/changelog.d/user-management.add
@@ -0,0 +1 @@
+User administration + post scope/sensitivity admin change support
diff --git a/docker-compose.e2e.yml b/docker-compose.e2e.yml
index 75a4979a1..c740e69c4 100644
--- a/docker-compose.e2e.yml
+++ b/docker-compose.e2e.yml
@@ -12,7 +12,7 @@ services:
retries: 30
pleroma:
- image: ${PLEROMA_IMAGE:-git.pleroma.social:5050/pleroma/pleroma:stable}
+ image: ${PLEROMA_IMAGE:-git.pleroma.social/pleroma/pleroma:stable}
environment:
DB_USER: pleroma
DB_PASS: pleroma
@@ -39,6 +39,8 @@ services:
interval: 5s
timeout: 3s
retries: 60
+ ports:
+ - 4000:4000
e2e:
build:
@@ -51,7 +53,7 @@ services:
CI: "1"
VITE_PROXY_TARGET: http://pleroma:4000
VITE_PROXY_ORIGIN: http://localhost:4000
- E2E_BASE_URL: http://localhost:8080
+ E2E_BASE_URL: http://localhost:8099
E2E_ADMIN_USERNAME: ${E2E_ADMIN_USERNAME:-admin}
E2E_ADMIN_PASSWORD: ${E2E_ADMIN_PASSWORD:-adminadmin}
command: ["yarn", "e2e:pw"]
diff --git a/docker/e2e/Dockerfile.e2e b/docker/e2e/Dockerfile.e2e
index e84359ceb..7e3fbfbf1 100644
--- a/docker/e2e/Dockerfile.e2e
+++ b/docker/e2e/Dockerfile.e2e
@@ -1,4 +1,4 @@
-FROM mcr.microsoft.com/playwright:v1.57.0-jammy
+FROM mcr.microsoft.com/playwright:v1.61.0-jammy
WORKDIR /app
diff --git a/package.json b/package.json
index 85e79a1d3..f594d7a99 100644
--- a/package.json
+++ b/package.json
@@ -43,7 +43,7 @@
"localforage": "1.10.0",
"parse-link-header": "2.0.0",
"phoenix": "1.8.1",
- "pinia": "^3.0.0",
+ "pinia": "^3.0.4",
"punycode.js": "2.3.1",
"qrcode": "1.5.4",
"querystring-es3": "0.2.1",
@@ -65,10 +65,12 @@
"@biomejs/biome": "2.3.11",
"@pinia/testing": "1.0.3",
"@ungap/event-target": "0.2.4",
- "@vitejs/plugin-vue": "^5.2.1",
- "@vitejs/plugin-vue-jsx": "^4.1.1",
- "@vitest/browser": "^3.0.7",
- "@vitest/ui": "^3.0.7",
+ "@vitejs/devtools": "^0.3.1",
+ "@vitejs/plugin-vue": "^6.0.7",
+ "@vitejs/plugin-vue-jsx": "^5.1.5",
+ "@vitest/browser-playwright": "^4.1.7",
+ "@vitest/browser": "^4.1.7",
+ "@vitest/ui": "^4.1.7",
"@vue/babel-helper-vue-jsx-merge-props": "1.4.0",
"@vue/babel-plugin-jsx": "1.5.0",
"@vue/compiler-sfc": "3.5.22",
@@ -94,13 +96,14 @@
"http-proxy-middleware": "3.0.5",
"iso-639-1": "3.1.5",
"lodash": "4.17.21",
- "msw": "2.10.5",
+ "msw": "2.14.6",
"nightwatch": "3.12.2",
- "playwright": "1.57.0",
+ "oxc": "^1.0.1",
+ "playwright": "1.61.0",
"postcss": "8.5.6",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
- "sass": "1.93.2",
+ "sass-embedded": "^1.100.0",
"selenium-server": "3.141.59",
"semver": "7.7.3",
"serve-static": "2.2.0",
@@ -113,10 +116,10 @@
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-recommended-vue": "^1.6.0",
"stylelint-config-standard": "38.0.0",
- "vite": "^6.1.0",
- "vite-plugin-eslint2": "^5.0.3",
- "vite-plugin-stylelint": "^6.0.0",
- "vitest": "^3.0.7",
+ "vite": "^8.0.0",
+ "vite-plugin-eslint2": "^5.1.0",
+ "vite-plugin-stylelint": "^6.1.0",
+ "vitest": "^4.1.7",
"vue-eslint-parser": "10.2.0"
},
"type": "module",
diff --git a/src/App.js b/src/App.js
index 9fb4d32e5..934b96713 100644
--- a/src/App.js
+++ b/src/App.js
@@ -2,22 +2,15 @@ import { throttle } from 'lodash'
import { mapState } from 'pinia'
import { defineAsyncComponent } from 'vue'
-import DesktopNav from './components/desktop_nav/desktop_nav.vue'
-import EditStatusModal from './components/edit_status_modal/edit_status_modal.vue'
-import FeaturesPanel from './components/features_panel/features_panel.vue'
-import GlobalNoticeList from './components/global_notice_list/global_notice_list.vue'
-import InstanceSpecificPanel from './components/instance_specific_panel/instance_specific_panel.vue'
-import MediaModal from './components/media_modal/media_modal.vue'
-import MobileNav from './components/mobile_nav/mobile_nav.vue'
-import MobilePostStatusButton from './components/mobile_post_status_button/mobile_post_status_button.vue'
-import NavPanel from './components/nav_panel/nav_panel.vue'
-import PostStatusModal from './components/post_status_modal/post_status_modal.vue'
-import ShoutPanel from './components/shout_panel/shout_panel.vue'
-import SideDrawer from './components/side_drawer/side_drawer.vue'
-import StatusHistoryModal from './components/status_history_modal/status_history_modal.vue'
-import UserPanel from './components/user_panel/user_panel.vue'
-import UserReportingModal from './components/user_reporting_modal/user_reporting_modal.vue'
-import WhoToFollowPanel from './components/who_to_follow_panel/who_to_follow_panel.vue'
+import DesktopNav from 'src/components/desktop_nav/desktop_nav.vue'
+import FeaturesPanel from 'src/components/features_panel/features_panel.vue'
+import GlobalError from 'src/components/global_error/global_error.vue'
+import GlobalNoticeList from 'src/components/global_notice_list/global_notice_list.vue'
+import InstanceSpecificPanel from 'src/components/instance_specific_panel/instance_specific_panel.vue'
+import MobileNav from 'src/components/mobile_nav/mobile_nav.vue'
+import MobilePostStatusButton from 'src/components/mobile_post_status_button/mobile_post_status_button.vue'
+import NavPanel from 'src/components/nav_panel/nav_panel.vue'
+import UserPanel from 'src/components/user_panel/user_panel.vue'
import { getOrCreateServiceWorker } from './services/sw/sw'
import { windowHeight, windowWidth } from './services/window_utils/window_utils'
@@ -29,9 +22,6 @@ import { useInterfaceStore } from 'src/stores/interface.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useShoutStore } from 'src/stores/shout.js'
-import messages from 'src/i18n/messages'
-import localeService from 'src/services/locale/locale.service.js'
-
// Helper to unwrap reactive proxies
window.toValue = (x) => JSON.parse(JSON.stringify(x))
@@ -41,27 +31,45 @@ export default {
UserPanel,
NavPanel,
Notifications: defineAsyncComponent(
- () => import('./components/notifications/notifications.vue'),
+ () => import('src/components/notifications/notifications.vue'),
),
InstanceSpecificPanel,
FeaturesPanel,
- WhoToFollowPanel,
- ShoutPanel,
- MediaModal,
- SideDrawer,
+ WhoToFollowPanel: defineAsyncComponent(
+ () =>
+ import('src/components/who_to_follow_panel/who_to_follow_panel.vue'),
+ ),
+ ShoutPanel: defineAsyncComponent(
+ () => import('src/components/shout_panel/shout_panel.vue'),
+ ),
+ MediaModal: defineAsyncComponent(
+ () => import('src/components/media_modal/media_modal.vue'),
+ ),
MobilePostStatusButton,
MobileNav,
DesktopNav,
SettingsModal: defineAsyncComponent(
- () => import('./components/settings_modal/settings_modal.vue'),
+ () => import('src/components/settings_modal/settings_modal.vue'),
),
UpdateNotification: defineAsyncComponent(
- () => import('./components/update_notification/update_notification.vue'),
+ () =>
+ import('src/components/update_notification/update_notification.vue'),
),
- UserReportingModal,
- PostStatusModal,
- EditStatusModal,
- StatusHistoryModal,
+ PostStatusModal: defineAsyncComponent(
+ () => import('src/components/post_status_modal/post_status_modal.vue'),
+ ),
+ UserReportingModal: defineAsyncComponent(
+ () =>
+ import('src/components/user_reporting_modal/user_reporting_modal.vue'),
+ ),
+ EditStatusModal: defineAsyncComponent(
+ () => import('src/components/edit_status_modal/edit_status_modal.vue'),
+ ),
+ StatusHistoryModal: defineAsyncComponent(
+ () =>
+ import('src/components/status_history_modal/status_history_modal.vue'),
+ ),
+ GlobalError,
GlobalNoticeList,
},
data: () => ({
@@ -149,13 +157,23 @@ export default {
userBackground() {
return this.currentUser.background_image
},
+ foreignProfileBackground() {
+ return (
+ useMergedConfigStore().mergedConfig.allowForeignUserBackground &&
+ useInterfaceStore().foreignProfileBackground
+ )
+ },
instanceBackground() {
return useMergedConfigStore().mergedConfig.hideInstanceWallpaper
? null
: this.instanceBackgroundUrl
},
background() {
- return this.userBackground || this.instanceBackground
+ return (
+ this.foreignProfileBackground ||
+ this.userBackground ||
+ this.instanceBackground
+ )
},
bgStyle() {
if (this.background) {
diff --git a/src/App.scss b/src/App.scss
index f556a7d9d..0de5a3577 100644
--- a/src/App.scss
+++ b/src/App.scss
@@ -50,7 +50,7 @@ body {
// have a cursor/pointer to operate them
@media (any-pointer: fine) {
* {
- scrollbar-color: var(--fg) transparent;
+ scrollbar-color: var(--icon) transparent;
&::-webkit-scrollbar {
background: transparent;
@@ -130,7 +130,7 @@ body {
}
// Body should have background to scrollbar otherwise it will use white (body color?)
html {
- scrollbar-color: var(--fg) var(--wallpaper);
+ scrollbar-color: var(--icon) var(--wallpaper);
background: var(--wallpaper);
}
}
@@ -144,6 +144,25 @@ h4 {
margin: 0;
}
+code {
+ background: var(--background);
+ border: 1px solid var(--border);
+ border-radius: var(--roundness);
+ padding: 0 0.2em;
+
+ & pre,
+ pre & {
+ display: block;
+ overflow-x: auto;
+ padding: 0.2em;
+ }
+
+ &.pre {
+ white-space: pre;
+ display: block;
+ }
+}
+
.iconLetter {
display: inline-block;
text-align: center;
@@ -200,6 +219,7 @@ nav {
background-color: var(--wallpaper);
background-image: var(--body-background-image);
background-position: 50%;
+ transition: background-image 1s;
}
.underlay {
@@ -410,6 +430,14 @@ nav {
button:not(.button-default) {
color: var(--text);
font-size: 100%;
+ text-align: initial;
+ padding: 0;
+ background: none;
+ border: none;
+ outline: none;
+ display: inline;
+ font-family: inherit;
+ line-height: unset;
}
&.disabled {
@@ -417,45 +445,6 @@ nav {
}
}
-.list-item {
- border-color: var(--border);
- border-style: solid;
- border-width: 0;
- border-top-width: 1px;
-
- &.-active,
- &:hover {
- border-top-width: 1px;
- border-bottom-width: 1px;
- }
-
- &.-active + &,
- &:hover + & {
- border-top-width: 0;
- }
-
- &:hover + .menu-item-collapsible:not(.-expanded) + &,
- &.-active + .menu-item-collapsible:not(.-expanded) + & {
- border-top-width: 0;
- }
-
- &[aria-expanded="true"] {
- border-bottom-width: 1px;
- }
-
- &:first-child {
- border-top-right-radius: var(--roundness);
- border-top-left-radius: var(--roundness);
- border-top-width: 0;
- }
-
- &:last-child {
- border-bottom-right-radius: var(--roundness);
- border-bottom-left-radius: var(--roundness);
- border-bottom-width: 0;
- }
-}
-
.menu-item,
.list-item {
display: block;
@@ -474,22 +463,6 @@ nav {
--__line-height: 1.5em;
--__horizontal-gap: 0.75em;
--__vertical-gap: 0.5em;
-
- &.-non-interactive {
- cursor: auto;
- }
-
- a,
- button:not(.button-default) {
- text-align: initial;
- padding: 0;
- background: none;
- border: none;
- outline: none;
- display: inline;
- font-family: inherit;
- line-height: unset;
- }
}
.button-unstyled {
@@ -815,9 +788,11 @@ option {
align-items: baseline;
line-height: 1.5;
+ p,
span {
display: block;
flex: 1 1 auto;
+ margin: 0;
}
.dismiss {
diff --git a/src/App.vue b/src/App.vue
index f432d8bc4..84a6f7d3d 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -28,10 +28,10 @@
>
- {{ localDescription }} -
+ />- {{ localDescription }} -
+ />
+
+ {{ ' - ' }}
+
+
+ {{ content }}
+ +
+
- {{ $t('user_card.expire_in') }}
-
-
+
+
ebin
', - favourites_count: 0, - fields: [], - followers_count: 1, - following: true, - follows_you: true, - friends_count: 1, - id: '60717', - is_local: false, - locked: false, - name: 'Spurdo :ebin:', - name_html: 'Spurdo