Merge branch 'small-fixes-and-improvements' into shigusegubu-themes3

This commit is contained in:
Henry Jameson 2026-05-05 17:12:27 +03:00
commit 5c2ed98f60
29 changed files with 244 additions and 123 deletions

View file

@ -787,6 +787,19 @@ option {
padding: 0 0.25em;
border-radius: var(--roundness);
border: 1px solid var(--border);
&.-dismissible {
display: flex;
padding-left: 0.5em;
margin: 0;
align-items: baseline;
line-height: 2;
span {
display: block;
flex: 1 0 auto;
}
}
}
.faint {

View file

@ -1,3 +1,5 @@
import { mapState } from 'pinia'
import FeaturesPanel from '../features_panel/features_panel.vue'
import InstanceSpecificPanel from '../instance_specific_panel/instance_specific_panel.vue'
import MRFTransparencyPanel from '../mrf_transparency_panel/mrf_transparency_panel.vue'
@ -7,6 +9,9 @@ import TermsOfServicePanel from '../terms_of_service_panel/terms_of_service_pane
import { useInstanceStore } from 'src/stores/instance.js'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
const pleromaFeCommitUrl =
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
const About = {
components: {
InstanceSpecificPanel,
@ -19,6 +24,10 @@ const About = {
showFeaturesPanel() {
return useInstanceStore().instanceIdentity.showFeaturesPanel
},
frontendVersionLink() {
return pleromaFeCommitUrl + this.frontendVersion
},
...mapState(useInstanceStore, ['backendVersion', 'backendRepository', 'frontendVersion']),
showInstanceSpecificPanel() {
return (
useInstanceStore().instanceIdentity.showInstanceSpecificPanel &&

View file

@ -1,11 +1,45 @@
<template>
<div class="column-inner">
<div class="About column-inner">
<instance-specific-panel v-if="showInstanceSpecificPanel" />
<staff-panel />
<terms-of-service-panel />
<MRFTransparencyPanel />
<features-panel v-if="showFeaturesPanel" />
<div class="panel panel-default">
<div class="panel-heading">
<div class="title">{{ $t('settings.version.title') }}</div>
</div>
<div class="panel-body">
<dl>
<dt>{{ $t('settings.version.backend_version') }}</dt>
<dd>
<a
:href="backendRepository"
target="_blank"
>
{{ backendVersion }}
</a>
</dd>
<dt>{{ $t('settings.version.frontend_version') }}</dt>
<dd>
<a
:href="frontendVersionLink"
target="_blank"
>
{{ frontendVersion }}
</a>
</dd>
</dl>
</div>
</div>
</div>
</template>
<script src="./about.js"></script>
<style>
.About {
dl {
padding-left: 1em;
}
}
</style>

View file

@ -21,6 +21,9 @@ const ConfirmModal = {
confirmText: {
type: String,
},
confirmDanger: {
type: Boolean,
},
},
emits: ['cancelled', 'accepted'],
computed: {},

View file

@ -14,6 +14,7 @@
<slot name="footerLeft" />
<button
class="btn button-default"
:class="{ '-danger': confirmDanger }"
@click.prevent="onAccept"
v-text="confirmText"
/>

View file

@ -80,6 +80,7 @@
<confirm-modal
v-if="showingConfirmLogout"
:title="$t('login.logout_confirm_title')"
:confirm-danger="true"
:confirm-text="$t('login.logout_confirm_accept_button')"
:cancel-text="$t('login.logout_confirm_cancel_button')"
@accepted="doLogout"

View file

@ -109,6 +109,7 @@
<confirm-modal
v-if="showingConfirmLogout"
:title="$t('login.logout_confirm_title')"
:confirm-danger="true"
:confirm-text="$t('login.logout_confirm_accept_button')"
:cancel-text="$t('login.logout_confirm_cancel_button')"
@accepted="doLogout"

View file

@ -373,14 +373,20 @@ const PostStatusForm = {
quotable() {
return this.quotingAvailable && this.replyTo
},
quoteThreadToggled() {
return this.newStatus.hasQuote && this.newStatus.quote.thread
quoteThreadToggled: {
get() {
return this.newStatus.hasQuote && this.newStatus.quote.thread
},
set(value) {
this.newStatus.hasQuote = value
this.newStatus.quote.thread = value
this.newStatus.quote.id = value ? this.replyTo : ''
}
},
defaultQuotable() {
if (
!this.quotingAvailable ||
!this.isReply ||
!this.$store.getters.mergedConfig.quoteReply
!this.isReply
) {
return false
}
@ -868,11 +874,6 @@ const PostStatusForm = {
quote.url = ''
quote.thread = quotable
},
setQuoteThread(v) {
this.newStatus.hasQuote = v
this.newStatus.quote.thread = v
this.newStatus.quote.id = v ? this.replyTo : ''
},
clearQuoteForm() {
if (this.$refs.quoteForm) {
this.$refs.quoteForm.clear()

View file

@ -42,6 +42,7 @@
.form-bottom-left {
display: flex;
gap: 1.5em;
margin-right: 1em;
button {
padding: 0.5em;
@ -89,9 +90,10 @@
}
.reply-or-quote-selector {
flex: 1 0 auto;
flex-wrap: wrap;
margin-bottom: 0.5em;
display: grid;
gap: 1em;
display: flex;
grid-template-columns: 1fr 1fr;
}
@ -144,10 +146,7 @@
justify-content: right;
}
.media-upload-icon,
.poll-icon,
.quote-icon,
.emoji-icon {
.bottom-left-button {
font-size: 1.85em;
line-height: 1.1;
flex: 1;

View file

@ -88,7 +88,7 @@
</div>
<div
v-if="!disablePreview"
class="preview-heading faint"
class="preview-heading"
>
<a
class="preview-toggle faint"
@ -110,34 +110,23 @@
<div
v-if="quotable"
role="radiogroup"
class="btn-group reply-or-quote-selector"
class="reply-or-quote-selector"
>
<button
:id="`reply-or-quote-option-${randomSeed}-reply`"
class="btn button-default reply-or-quote-option"
:class="{ toggled: !quoteThreadToggled }"
tabindex="0"
<Checkbox
role="radio"
:disabled="quoteFormVisible"
:aria-labelledby="`reply-or-quote-option-${randomSeed}-reply`"
:aria-checked="!newStatus.quote.thread"
@click="setQuoteThread(false)"
:radio="true"
:model-value="!quoteThreadToggled"
@update:model-value="e => quoteThreadToggled = !e"
>
{{ $t('post_status.reply_option') }}
</button>
<button
:id="`reply-or-quote-option-${randomSeed}-quote`"
class="btn button-default reply-or-quote-option"
:class="{ toggled: quoteThreadToggled }"
tabindex="0"
role="radio"
</Checkbox>
<Checkbox
v-model="quoteThreadToggled"
:radio="true"
:disabled="quoteFormVisible"
:aria-labelledby="`reply-or-quote-option-${randomSeed}-quote`"
:aria-checked="newStatus.quote.thread"
@click="setQuoteThread(true)"
>
{{ $t('post_status.quote_option') }}
</button>
</Checkbox>
</div>
</div>
<div
@ -292,7 +281,7 @@
<div class="form-bottom-left">
<media-upload
ref="mediaUpload"
class="media-upload-icon"
class="bottom-left-button media-upload-icon"
:drop-files="dropFiles"
:disabled="uploadFileLimitReached"
@uploading="startedUploadingFiles"
@ -302,8 +291,8 @@
/>
<button
v-if="pollsAvailable"
class="poll-icon button-unstyled"
:class="{ selected: pollFormVisible }"
class="bottom-left-button poll-icon button-unstyled"
:class="{ toggled: pollFormVisible }"
:title="$t('polls.add_poll')"
@click="togglePollForm"
>
@ -311,9 +300,9 @@
</button>
<button
v-if="quotingAvailable"
class="quote-icon button-unstyled"
class="bottom-left-button quote-icon button-unstyled"
:disabled="newStatus.quote.thread"
:class="{ selected: quoteFormVisible }"
:class="{ toggled: quoteFormVisible }"
:title="$t('tool_tip.add_quote')"
@click="toggleQuoteForm"
>
@ -389,9 +378,11 @@
</div>
<div
v-if="error"
class="alert error"
class="alert error -dismissible"
>
Error: {{ error }}
<span>
{{ error }}
</span>
<button
class="button-unstyled"
@click="clearError"

View file

@ -24,9 +24,6 @@
</ul>
<h3>{{ $t('admin_dash.federation.activitypub') }}</h3>
<ul class="setting-list">
<li>
<BooleanSetting path=":pleroma.:instance.:allow_relay" />
</li>
<li>
<BooleanSetting path=":pleroma.:activitypub.:unfollow_blocked" />
</li>

View file

@ -67,6 +67,7 @@ const AppearanceTab = {
})),
backgroundUploading: false,
background: null,
backgroundError: null,
backgroundPreview: null,
}
},
@ -474,6 +475,9 @@ const AppearanceTab = {
resetUploadedBackground() {
this.backgroundPreview = null
},
clearBackgroundError() {
this.backgroundError = null
},
submitBackground(background) {
if (!this.backgroundPreview && background !== '') {
return
@ -486,8 +490,11 @@ const AppearanceTab = {
this.$store.commit('addNewUsers', [data])
this.$store.commit('setCurrentUser', data)
this.backgroundPreview = null
this.backgroundError = null
})
.catch((e) => {
this.backgroundError = e
})
.catch(this.displayUploadError)
.finally(() => {
this.backgroundUploading = false
})

View file

@ -208,6 +208,23 @@
{{ $t('settings.reset') }}
</button>
</div>
<div
v-if="backgroundError"
class="alert error -dismissible"
>
<span>
{{ backgroundError }}
</span>
<button
class="button-unstyled"
@click="clearBackgroundError"
>
<FAIcon
class="fa-scale-110 fa-old-padding"
icon="times"
/>
</button>
</div>
<button
v-if="!isDefaultBackground"
class="btn button-default reset-button"
@ -246,6 +263,11 @@
{{ $t('settings.hide_wallpaper') }}
</BooleanSetting>
</li>
<li>
<BooleanSetting path="compactProfiles">
{{ $t('settings.compact_profiles') }}
</BooleanSetting>
</li>
</ul>
</div>
</div>

View file

@ -1,3 +1,4 @@
import { mapState } from 'pinia'
import BooleanSetting from '../helpers/boolean_setting.vue'
import SharedComputedObject from '../helpers/shared_computed_object.js'
@ -9,14 +10,6 @@ const pleromaFeCommitUrl =
'https://git.pleroma.social/pleroma/pleroma-fe/commit/'
const VersionTab = {
data() {
const instance = useInstanceStore()
return {
backendVersion: instance.backendVersion,
backendRepository: instance.backendRepository,
frontendVersion: instance.frontendVersion,
}
},
components: {
BooleanSetting,
},
@ -24,6 +17,7 @@ const VersionTab = {
frontendVersionLink() {
return pleromaFeCommitUrl + this.frontendVersion
},
...mapState(useInstanceStore, ['backendVersion', 'backendRepository', 'frontendVersion']),
...SharedComputedObject(),
},
methods: {

View file

@ -9,6 +9,7 @@
class="lang-selector"
@update="val => language = val"
/>
<h5>{{ $t('settings.email_language') }}</h5>
<interface-language-switcher
v-model="emailLanguage"
class="lang-selector"

View file

@ -509,22 +509,14 @@ export default {
}
},
setCustomTheme() {
useInterfaceStore().setThemeV2({
customTheme: {
ignore: true,
themeFileVersion: this.selectedVersion,
themeEngineVersion: CURRENT_VERSION,
...this.previewTheme,
},
customThemeSource: {
themeFileVersion: this.selectedVersion,
themeEngineVersion: CURRENT_VERSION,
shadows: this.shadowsLocal,
fonts: this.fontsLocal,
opacity: this.currentOpacity,
colors: this.currentColors,
radii: this.currentRadii,
},
useInterfaceStore().setTheme({
themeFileVersion: this.selectedVersion,
themeEngineVersion: CURRENT_VERSION,
shadows: this.shadowsLocal,
fonts: this.fontsLocal,
opacity: this.currentOpacity,
colors: this.currentColors,
radii: this.currentRadii,
})
},
updatePreviewColors() {

View file

@ -152,6 +152,7 @@ const Status = {
'simpleTree',
'showOtherRepliesAsButton',
'dive',
'ignoreMute',
'controlledThreadDisplayStatus',
'controlledToggleThreadDisplay',
@ -345,6 +346,7 @@ const Status = {
}
},
muted() {
if (this.ignoreMute) return false
if (this.statusoid.user.id === this.currentUser.id) return false
return !this.unmuted && !this.shouldNotMute && this.muteReasons.length > 0
},
@ -366,6 +368,7 @@ const Status = {
)
},
shouldNotMute() {
if (this.ignoreMute) return true
if (this.isFocused) return true
const { status } = this
const { reblog } = status

View file

@ -23,6 +23,7 @@
.status-container {
display: flex;
padding: var(--status-margin);
gap: var(--status-margin);
> * {
min-width: 0;
@ -50,12 +51,11 @@
}
.left-side {
margin-right: var(--status-margin);
flex: 0 0 auto;
}
.right-side {
flex: 1;
min-width: 0;
flex: 1 1 auto;
}
.usercard {
@ -230,29 +230,47 @@
}
.repeat-info {
display: flex;
align-items: center;
padding: 0.4em var(--status-margin);
.repeat-icon {
color: var(--cGreen);
.repeater-avatar {
flex: 0 0 1.5em;
border-radius: var(--roundness);
margin-left: 2em; // 3.5 (poster avatar size) - 1.5 (repeater avatar size)
width: 1.5em;
height: 1.5em;
}
}
.repeater-avatar {
border-radius: var(--roundness);
margin-left: 2em; // 3.5 (poster avatar size) - 1.5 (repeater avatar size)
width: 1.5em;
height: 1.5em;
}
.right-side {
display: flex;
flex: 1 1 auto;
overflow-x: hidden;
text-overflow: ellipsis;
margin-right: 0;
gap: 0.5em;
.repeater-name {
text-overflow: ellipsis;
margin-right: 0;
.repeater-name {
flex: 0 1 auto;
margin: 0;
}
.emoji {
width: 1em;
height: 1em;
vertical-align: middle;
object-fit: contain;
.repeat-label {
white-space: nowrap;
flex: 0 0 auto;
.repeat-icon {
vertical-align: middle;
color: var(--cGreen);
}
}
.emoji {
width: 1em;
height: 1em;
vertical-align: middle;
object-fit: contain;
}
}
}
@ -371,21 +389,4 @@
text-decoration: underline;
}
}
@media all and (width <= 800px) {
.repeater-avatar {
margin-left: 20px;
}
.post-avatar {
width: 40px;
height: 40px;
// TODO define those other way somehow?
&.-compact {
width: 32px;
height: 32px;
}
}
}
}

View file

@ -88,13 +88,14 @@
:to="retweeterProfileLink"
>{{ retweeter }}</router-link>
</bdi>
{{ ' ' }}
<FAIcon
icon="retweet"
class="repeat-icon"
:title="$t('tool_tip.repeat')"
/>
{{ $t('timeline.repeated') }}
<div class="repeat-label">
<FAIcon
icon="retweet"
class="repeat-icon"
:title="$t('tool_tip.repeat')"
/>
{{ $t('timeline.repeated') }}
</div>
</div>
</div>

View file

@ -16,6 +16,7 @@
:is-preview="true"
:statusoid="status"
:compact="true"
:ignore-mute="true"
/>
<div
v-else-if="error"

View file

@ -62,6 +62,15 @@ library.add(
faClockRotateLeft,
)
const KNOWN_TAGS = new Set([
'mrf_tag:media-force-nsfw',
'mrf_tag:media-strip',
'mrf_tag:force-unlisted',
'mrf_tag:sandbox',
'mrf_tag:disable-remote-subscription',
'mrf_tag:disable-any-subscription'
])
export default {
props: {
// Enables all the options for profile editing, used in settings -> profile tab
@ -105,6 +114,12 @@ export default {
type: Boolean,
default: false,
},
// Disable forced 3:1 aspect ratio
compact: {
required: false,
type: Boolean,
default: false,
}
},
components: {
DialogModal,
@ -388,6 +403,9 @@ export default {
...mapState(useMergedConfigStore, ['mergedConfig']),
},
methods: {
isKnownTag(tag) {
return KNOWN_TAGS.has(tag)
},
muteUser() {
this.$refs.timedMuteDialog.optionallyPrompt()
},

View file

@ -165,12 +165,15 @@
.user-identity {
position: relative;
aspect-ratio: 3;
min-height: 6em;
display: flex;
align-items: flex-end;
container: user-card / inline-size;
&:not(.-compact) {
aspect-ratio: 3;
}
> * {
min-width: 0;
}

View file

@ -2,7 +2,10 @@
<div class="user-card">
<div class="user-card-inner">
<div class="user-info">
<div class="user-identity">
<div
class="user-identity"
:class="{ '-compact': compact }"
>
<div class="header-overlay">
<div class="banner-image">
<img
@ -212,6 +215,12 @@
>
{{ $t('user_card.group') }}
</span>
<span
v-for="tag in user.tags"
class="alert warning user-role"
>
{{ isKnownTag ? $t('user_card.tags.' + tag) : tag }}
</span>
</template>
</div>
</div>

View file

@ -8,7 +8,7 @@
v-model="localNote"
class="input note-text"
:class="{ unstyled: !editing }"
rows="1"
rows="3"
:placeholder="$t('user_card.note_blank_click')"
@focus="startEditing"
@blur="finalizeEditing"

View file

@ -12,6 +12,7 @@
<template #content="{close}">
<UserCard
class="user-popover"
:compact="true"
:show-close="true"
:show-expand="true"
:user-id="userId"
@ -37,7 +38,6 @@
}
.user-identity {
aspect-ratio: unset;
min-width: calc(min(30em, 98vw));
}

View file

@ -1,4 +1,5 @@
import get from 'lodash/get'
import { mapState } from 'pinia'
import RichContent from 'src/components/rich_content/rich_content.jsx'
import TabSwitcher from 'src/components/tab_switcher/tab_switcher.jsx'
@ -9,6 +10,7 @@ import List from '../list/list.vue'
import Timeline from '../timeline/timeline.vue'
import UserCard from '../user_card/user_card.vue'
import { useMergedConfigStore } from 'src/stores/merged_config.js'
import { useInstanceStore } from 'src/stores/instance.js'
import { useInstanceCapabilitiesStore } from 'src/stores/instance_capabilities.js'
@ -94,6 +96,9 @@ const UserProfile = {
!this.user.hide_favorites)
)
},
compactProfiles() {
return useMergedConfigStore().mergedConfig.compactProfiles
},
},
methods: {
setFooterRef(el) {

View file

@ -9,6 +9,7 @@
:user-id="userId"
:switcher="true"
:selected="timeline.viewing"
:compact="compactProfiles"
avatar-action="zoom"
:has-note-editor="true"
/>

View file

@ -1141,7 +1141,8 @@
"hard_reset_value_tooltip": "Remove setting from storage, forcing use of default value",
"cache": "Cache",
"clear_asset_cache": "Clear asset cache",
"clear_emoji_cache": "Clear emoji cache"
"clear_emoji_cache": "Clear emoji cache",
"compact_profiles": "Reduce profile height on user pages"
},
"admin_dash": {
"window_title": "Administration",
@ -1737,7 +1738,15 @@
},
"personal_note": "Personal note",
"note_blank_click": "Click to add note",
"highlight_header": "Highlight user's posts and mentions"
"highlight_header": "Highlight user's posts and mentions",
"tags": {
"mrf_tag:media-force-nsfw": "Mark as sensitive",
"mrf_tag:media-strip": "Remove attachments",
"mrf_tag:force-unlisted": "Force unlisted",
"mrf_tag:sandbox": "Remove from public timelines",
"mrf_tag:disable-remote-subscription": "Reject non-local follow requests",
"mrf_tag:disable-any-subscription": "Reject any follow requests"
}
},
"user_profile": {
"timeline_title": "User timeline",

View file

@ -542,6 +542,10 @@ export const INSTANCE_DEFAULT_CONFIG_DEFINITIONS = {
required: true,
default: 'none',
},
compactProfiles: {
description: 'Reduce profile height on user pages',
default: false,
},
}
export const INSTANCE_DEFAULT_CONFIG = convertDefinitions(
INSTANCE_DEFAULT_CONFIG_DEFINITIONS,