Compare commits

...

31 commits

Author SHA1 Message Date
Henry Jameson
41add4fc6a Merge branch 'themes-3-1' into shigusegubu-themes3 2025-08-10 23:39:49 +03:00
Henry Jameson
eef6f6d0e2 Merge remote-tracking branch 'origin/develop' into shigusegubu-themes3 2025-08-10 23:39:43 +03:00
HJ
d62393bf6b Merge branch 'emoji-pack-upload' into 'develop'
Add a way to upload new packs from URL/ZIP file

See merge request pleroma/pleroma-fe!1999
2025-08-10 20:37:46 +00:00
HJ
1d3b271e7c Apply 1 suggestion(s) to 1 file(s) 2025-08-10 20:30:24 +00:00
Henry Jameson
6e5da62233 changelog 2025-08-10 23:25:42 +03:00
HJ
b80035cbb0 Apply 1 suggestion(s) to 1 file(s) 2025-08-10 20:23:09 +00:00
HJ
047dda5525 Apply 2 suggestion(s) to 1 file(s) 2025-08-10 20:22:39 +00:00
HJ
e82de98892 Merge branch 'renovate/eslint-monorepo' into 'develop'
Update dependency eslint to v9.33.0

See merge request pleroma/pleroma-fe!2172
2025-08-10 20:20:37 +00:00
HJ
cf4aa692e3 Merge branch 'renovate/babel-monorepo' into 'develop'
Update babel monorepo

See merge request pleroma/pleroma-fe!2174
2025-08-10 20:20:29 +00:00
HJ
8f16da2f6f Merge branch 'renovate/nightwatch-3.x' into 'develop'
Update dependency nightwatch to v3.12.2

See merge request pleroma/pleroma-fe!2181
2025-08-10 20:19:51 +00:00
HJ
1f53c8bb07 Merge branch 'renovate/postcss-8.x' into 'develop'
Update dependency postcss to v8.5.6

See merge request pleroma/pleroma-fe!2183
2025-08-10 20:19:33 +00:00
HJ
2830b55d41 Merge branch 'renovate/stylelint-config-recommended-vue-1.x-lockfile' into 'develop'
Update dependency stylelint-config-recommended-vue to v1.6.1

See merge request pleroma/pleroma-fe!2184
2025-08-10 20:19:20 +00:00
HJ
f86cc5d8b5 Merge branch 'renovate/chai-5.x' into 'develop'
Update dependency chai to v5.2.1

See merge request pleroma/pleroma-fe!2208
2025-08-10 20:19:00 +00:00
Henry Jameson
8b8975adb2 even even more weight loss 2025-08-10 23:16:29 +03:00
Henry Jameson
2aabaeb5c6 notification fixes 2025-08-10 23:06:33 +03:00
Henry Jameson
67f606a3b0 user-card fixes 2025-08-10 23:03:50 +03:00
Henry Jameson
9440d35266 even more weight loss 2025-08-10 22:49:09 +03:00
Henry Jameson
6341747ec9 more weight reduction 2025-08-10 22:38:31 +03:00
Henry Jameson
9ec2ff409d panel-header avatar is now hard-coded in user-card 2025-08-10 21:35:59 +03:00
Henry Jameson
4ff257be57 only put heavy components if truly needed 2025-08-10 21:35:19 +03:00
HJ
5e77a0a23d Merge branch 'profile-edit' into 'develop'
Profile edit overhaul

See merge request pleroma/pleroma-fe!2205
2025-08-10 16:41:28 +00:00
Henry Jameson
370a7f8291 lint 2025-08-10 19:37:17 +03:00
Henry Jameson
0f51550802 lint 2025-08-10 17:55:54 +03:00
Henry Jameson
700e096dd4 fix sss 2025-08-10 17:42:37 +03:00
Pleroma Renovate Bot
d879b6f6eb Update dependency eslint to v9.33.0 2025-08-09 09:04:58 +00:00
Pleroma Renovate Bot
8d141cbeab Update dependency chai to v5.2.1 2025-08-08 08:53:03 +00:00
Ekaterina Vaartis
18110d6821 Add a way to upload new packs from URL/ZIP file 2025-08-06 21:52:33 +03:00
Pleroma Renovate Bot
0e56f8f103 Update babel monorepo 2025-07-25 08:52:58 +00:00
Pleroma Renovate Bot
8483268cb3 Update dependency stylelint-config-recommended-vue to v1.6.1 2025-06-27 09:04:51 +00:00
Pleroma Renovate Bot
230e61235d Update dependency postcss to v8.5.6 2025-06-27 09:04:41 +00:00
Pleroma Renovate Bot
4146c071ce Update dependency nightwatch to v3.12.2 2025-06-26 09:04:43 +00:00
27 changed files with 517 additions and 488 deletions

View file

@ -0,0 +1 @@
Added a way to upload new packs from a URL or ZIP file via the admin-fe

View file

@ -0,0 +1 @@
Reduced time taken processing theme by half

View file

@ -17,7 +17,7 @@
"lint-fix": "eslint --fix src test/unit/specs test/e2e/specs"
},
"dependencies": {
"@babel/runtime": "7.27.1",
"@babel/runtime": "7.28.2",
"@chenfengyuan/vue-qrcode": "2.0.0",
"@fortawesome/fontawesome-svg-core": "6.7.2",
"@fortawesome/free-regular-svg-icons": "6.7.2",
@ -54,10 +54,10 @@
"vuex": "4.1.0"
},
"devDependencies": {
"@babel/core": "7.27.1",
"@babel/eslint-parser": "7.27.1",
"@babel/plugin-transform-runtime": "7.27.1",
"@babel/preset-env": "7.27.2",
"@babel/core": "7.28.0",
"@babel/eslint-parser": "7.28.0",
"@babel/plugin-transform-runtime": "7.28.0",
"@babel/preset-env": "7.28.0",
"@babel/register": "7.27.1",
"@ungap/event-target": "0.2.4",
"@vitejs/plugin-vue": "^5.2.1",
@ -70,13 +70,13 @@
"@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.21",
"babel-plugin-lodash": "3.3.4",
"chai": "5.2.0",
"chai": "5.2.1",
"chalk": "5.4.1",
"chromedriver": "135.0.4",
"connect-history-api-fallback": "2.0.0",
"cross-spawn": "7.0.6",
"custom-event-polyfill": "1.0.7",
"eslint": "9.26.0",
"eslint": "9.33.0",
"vue-eslint-parser": "10.1.3",
"eslint-config-standard": "17.1.0",
"eslint-formatter-friendly": "7.0.0",
@ -91,9 +91,9 @@
"iso-639-1": "3.1.5",
"lodash": "4.17.21",
"msw": "2.10.2",
"nightwatch": "3.12.1",
"nightwatch": "3.12.2",
"playwright": "1.52.0",
"postcss": "8.5.3",
"postcss": "8.5.6",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
"sass": "1.89.2",

View file

@ -1,27 +0,0 @@
export default {
name: 'Attachment',
selector: '.Attachment',
notEditable: true,
validInnerComponents: [
'Border',
'Button',
'Input'
],
defaultRules: [
{
directives: {
roundness: 3
}
},
{
component: 'Button',
parent: {
component: 'Attachment'
},
directives: {
background: '#FFFFFF',
opacity: 0.5
}
}
]
}

View file

@ -8,9 +8,6 @@ export default {
'Text',
'Icon',
'Border',
'Button',
'RichContent',
'Attachment',
'PollGraph'
],
defaultRules: [

View file

@ -1,48 +0,0 @@
export default {
name: 'ListItem',
selector: '.list-item',
states: {
active: '.-active',
hover: ':is(:hover, :focus-visible, :has(:focus-visible)):not(.-non-interactive)'
},
validInnerComponents: [
'Text',
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'RichContent',
'Input',
'Avatar'
],
defaultRules: [
{
directives: {
background: '--bg',
opacity: 0
}
},
{
state: ['active'],
directives: {
background: '--inheritedBackground, 10',
opacity: 1
}
},
{
state: ['hover'],
directives: {
background: '--inheritedBackground, 10',
opacity: 1
}
},
{
state: ['hover', 'active'],
directives: {
background: '--inheritedBackground, 20',
opacity: 1
}
}
]
}

View file

@ -4,11 +4,7 @@ export default {
validInnerComponents: [
'Text',
'Icon',
'Input',
'Border',
'ButtonUnstyled',
'Badge',
'Avatar'
'Border'
],
states: {
hover: ':is(:hover, :focus-visible, :has(:focus-visible)):not(.disabled)',

View file

@ -2,18 +2,7 @@ export default {
name: 'MobileDrawer',
selector: '.mobile-drawer',
validInnerComponents: [
'Text',
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'Input',
'PanelHeader',
'MenuItem',
'Notification',
'Alert',
'UserCard'
'MenuItem'
],
defaultRules: [
{
@ -21,21 +10,6 @@ export default {
background: '--bg',
backgroundNoCssColor: 'yes'
}
},
{
component: 'PanelHeader',
parent: { component: 'MobileDrawer' },
directives: {
background: '--fg',
shadow: [{
x: 0,
y: 0,
blur: 4,
spread: 0,
color: '#000000',
alpha: 0.6
}]
}
}
]
}

View file

@ -6,12 +6,7 @@ export default {
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'RichContent',
'Input',
'Avatar',
'Attachment',
'PollGraph'
],
defaultRules: []

View file

@ -6,29 +6,17 @@ export default {
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'Input',
'PanelHeader',
'MenuItem',
'Post',
'Notification',
'Alert',
'UserCard',
'Chat',
'Attachment',
'Tab',
'ListItem'
'MenuItem'
],
validInnerComponentsLite: [
'Text',
'Link',
'Icon',
'Border',
'Button',
'Input',
'PanelHeader',
'Alert'
'PanelHeader'
],
defaultRules: [
{

View file

@ -7,9 +7,7 @@ export default {
'Icon',
'Button',
'ButtonUnstyled',
'Badge',
'Alert',
'Avatar'
'Alert'
],
defaultRules: [
{

View file

@ -6,16 +6,7 @@ export default {
modal: '.modal'
},
validInnerComponents: [
'Text',
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'Input',
'MenuItem',
'Post',
'UserCard'
'MenuItem'
],
defaultRules: [
{

View file

@ -2,6 +2,7 @@
font-family: var(--font);
&.-faint {
color: var(--text);
/* stylelint-disable declaration-no-important */
--text: var(--textFaint) !important;
--link: var(--linkFaint) !important;

View file

@ -1,20 +0,0 @@
export default {
name: 'RichContent',
selector: '.RichContent',
notEditable: true,
transparent: true,
validInnerComponents: [
'Text',
'FunText',
'Link'
],
defaultRules: [
{
directives: {
'--font': 'generic | inherit',
'--monoFont': 'generic | monospace',
textNoCssColor: 'yes'
}
}
]
}

View file

@ -32,7 +32,10 @@ const EmojiTab = {
newPackName: '',
deleteModalVisible: false,
remotePackInstance: '',
remotePackDownloadAs: ''
remotePackDownloadAs: '',
remotePackURL: '',
remotePackFile: null
}
},
@ -220,7 +223,7 @@ const EmojiTab = {
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.dlPackPopover.hidePopover()
this.$refs.downloadPackPopover.hidePopover()
return this.refreshPackList()
} else {
@ -232,6 +235,47 @@ const EmojiTab = {
this.remotePackDownloadAs = ''
})
},
downloadRemoteURLPack () {
this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({
url: this.remotePackURL, packName: this.newPackName
})
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
}
}).then(() => {
this.packName = this.newPackName
this.newPackName = ''
this.remotePackURL = ''
})
},
downloadRemoteFilePack () {
this.$store.state.api.backendInteractor.downloadRemoteEmojiPackZIP({
file: this.remotePackFile[0], packName: this.newPackName
})
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.additionalRemotePopover.hidePopover()
return this.refreshPackList()
} else {
this.displayError(resp.error)
return Promise.reject(resp)
}
}).then(() => {
this.packName = this.newPackName
this.newPackName = ''
this.remotePackURL = ''
})
},
displayError (msg) {
useInterfaceStore().pushGlobalNotice({
messageKey: 'admin_dash.emoji.error',

View file

@ -62,6 +62,64 @@
</template>
</Popover>
</button>
<button
class="button button-default emoji-panel-additional-actions"
@click="$refs.additionalRemotePopover.showPopover"
>
<FAIcon
icon="chevron-down"
/>
<Popover
ref="additionalRemotePopover"
popover-class="emoji-tab-edit-popover popover-default"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"
:bound-to="{ x: 'container' }"
:offset="{ y: 5 }"
>
<template #content>
<div class="emoji-tab-popover-input">
<h3>{{ $t('admin_dash.emoji.new_pack_name') }}</h3>
<input
v-model="newPackName"
:placeholder="$t('admin_dash.emoji.new_pack_name')"
class="input"
>
<h3>Import pack from URL</h3>
<input
v-model="remotePackURL"
class="input"
placeholder="Pack .zip URL"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackURL.trim() === ''"
@click="downloadRemoteURLPack"
>
Import
</button>
<h3>Import pack from a file</h3>
<input
type="file"
accept="application/zip"
class="emoji-tab-popover-file input"
@change="remotePackFile = $event.target.files"
>
<button
class="button button-default btn emoji-tab-popover-button"
type="button"
:disabled="newPackName.trim() === '' || remotePackFile === null || remotePackFile.length === 0"
@click="downloadRemoteFilePack"
>
Import
</button>
</div>
</template>
</Popover>
</button>
</li>
<h3>{{ $t('admin_dash.emoji.emoji_packs') }}</h3>
@ -240,12 +298,12 @@
v-if="pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="$refs.dlPackPopover.showPopover"
@click="$refs.downloadPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.download_pack') }}
<Popover
ref="dlPackPopover"
ref="downloadPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"

View file

@ -9,23 +9,9 @@ export default {
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'RichContent',
'Input',
'Avatar',
'Attachment',
'PollGraph'
],
validInnerComponentsLite: [
'Text',
'Link',
'Icon',
'Border',
'ButtonUnstyled',
'RichContent',
'Avatar'
],
defaultRules: [
{
directives: {

View file

@ -5,6 +5,7 @@ export default {
'Link',
'Text',
'Icon',
// Optimization: don't put heavy components unless needed
'Button',
'ButtonUnstyled',
'Input',

View file

@ -111,15 +111,20 @@
}
}
.banner-overlay,
.banner-image {
.header-overlay {
position: absolute;
inset: 0;
right: -1.2em;
left: -1.2em;
top: -1.4em;
padding: 0;
mask: linear-gradient(to top, transparent 0, white 5em) bottom no-repeat;
}
.banner-overlay,
.banner-image {
position: absolute;
inset: 0;
padding: 0;
border-top-left-radius: calc(var(--roundness) - 1px);
border-top-right-radius: calc(var(--roundness) - 1px);
}
@ -136,6 +141,7 @@
.banner-overlay {
background-color: var(--profileTint);
opacity: 0.5;
pointer-events: none; // let user copy bg url
z-index: -1;
}
@ -423,11 +429,6 @@
--emoji-size: 1.8em;
img {
object-fit: contain;
vertical-align: middle;
}
.user-profile-field-add,
.user-profile-field {
display: flex;

View file

@ -2,40 +2,10 @@ export default {
name: 'UserCard',
selector: '.user-card',
notEditable: true,
validInnerComponents: [
'Text',
'Link',
'Icon',
'Button',
'ButtonUnstyled',
'Input',
'RichContent',
'Alert'
],
defaultRules: [
{
directives: {
background: '--bg',
opacity: 0,
roundness: 3,
shadow: [{
x: 1,
y: 1,
blur: 4,
spread: 0,
color: '#000000',
alpha: 0.6
}],
'--profileTint': 'color | $alpha(--background 0.5)'
}
},
{
parent: {
component: 'UserCard'
},
component: 'RichContent',
directives: {
opacity: 0
'--profileTint': 'color | $alpha(--background 1)'
}
}
]

View file

@ -3,16 +3,18 @@
<div class="user-card-inner">
<div class="user-info">
<div class="user-identity">
<div class="banner-image">
<img
:src="bannerImgSrc"
<div class="header-overlay">
<div class="banner-image">
<img
:src="bannerImgSrc"
:class="{ 'hide-bio': hideBio }"
>
</div>
<div
class="banner-overlay"
:class="{ 'hide-bio': hideBio }"
>
/>
</div>
<div
class="banner-overlay"
:class="{ 'hide-bio': hideBio }"
/>
<a
v-if="avatarAction === 'zoom'"
class="user-info-avatar -link"
@ -458,7 +460,8 @@
>
<template #default="inputProps">
<input
v-model="newFields[i].name" :placeholder="$t('settings.profile_fields.name')"
v-model="newFields[i].name"
:placeholder="$t('settings.profile_fields.name')"
v-bind="propsToNative(inputProps)"
class="input"
>

View file

@ -122,6 +122,7 @@ const PLEROMA_EMOJI_IMPORT_FS_URL = '/api/pleroma/emoji/packs/import'
const PLEROMA_EMOJI_PACKS_URL = (page, pageSize) => `/api/v1/pleroma/emoji/packs?page=${page}&page_size=${pageSize}`
const PLEROMA_EMOJI_PACK_URL = (name) => `/api/v1/pleroma/emoji/pack?name=${name}`
const PLEROMA_EMOJI_PACKS_DL_REMOTE_URL = '/api/v1/pleroma/emoji/packs/download'
const PLEROMA_EMOJI_PACKS_DL_REMOTE_ZIP_URL = '/api/v1/pleroma/emoji/packs/download_zip'
const PLEROMA_EMOJI_PACKS_LS_REMOTE_URL =
(url, page, pageSize) => `/api/v1/pleroma/emoji/packs/remote?url=${url}&page=${page}&page_size=${pageSize}`
const PLEROMA_EMOJI_UPDATE_FILE_URL = (name) => `/api/v1/pleroma/emoji/packs/files?name=${name}`
@ -224,6 +225,9 @@ const updateProfile = ({ credentials, params }) => {
formData.append(name + `[${i}][value]`, param.value)
})
} else {
if (typeof params[name] === 'object') {
console.warning('Object detected in updateProfile API call. This will not work, use updateProfileJSON instead.')
}
formData.append(name, params[name]);
}
}
@ -237,6 +241,17 @@ const updateProfile = ({ credentials, params }) => {
.then((data) => parseUser(data))
}
const updateProfileJSON = ({ credentials, params }) => {
return promisedRequest({
url: MASTODON_PROFILE_UPDATE_URL,
credentials,
payload: params ,
method: 'PATCH'
})
.then((data) => data.json())
.then((data) => parseUser(data))
}
// Params needed:
// nickname
// email
@ -1932,6 +1947,18 @@ const downloadRemoteEmojiPack = ({ instance, packName, as }) => {
)
}
const downloadRemoteEmojiPackZIP = ({ url, packName, file }) => {
const data = new FormData()
if (file) data.set('file', file)
if (url) data.set('url', url)
data.set('name', packName)
return fetch(
PLEROMA_EMOJI_PACKS_DL_REMOTE_ZIP_URL,
{ method: 'POST', body: data }
)
}
const saveEmojiPackMetadata = ({ name, newData }) => {
return fetch(
PLEROMA_EMOJI_PACK_URL(name),
@ -2060,6 +2087,7 @@ const apiService = {
getCaptcha,
updateProfileImages,
updateProfile,
updateProfileJSON,
importMutes,
importBlocks,
importFollows,
@ -2137,6 +2165,7 @@ const apiService = {
deleteEmojiFile,
listRemoteEmojiPacks,
downloadRemoteEmojiPack,
downloadRemoteEmojiPackZIP,
fetchBookmarkFolders,
createBookmarkFolder,
updateBookmarkFolder,

View file

@ -86,7 +86,7 @@ export const generateTheme = (inputRuleset, callbacks, debug) => {
const themes3 = init({
inputRuleset,
debug
debug: true
})
getCssRules(themes3.eager, debug).forEach(rule => {

View file

@ -244,9 +244,6 @@ export const convertTheme2To3 = (data) => {
case 'tooltip':
rule.component = 'Popover'
break
case 'attachment':
rule.component = 'Attachment'
break
case 'ChatMessage':
rule.component = 'Button'
break

View file

@ -33,7 +33,6 @@ const components = {
Icon: null,
Border: null,
PanelHeader: null,
Attachment: null,
Panel: null,
Chat: null,
ChatMessage: null,

View file

@ -508,7 +508,7 @@ export const useServerSideStorageStore = defineStore('serverSideStorage', {
this.updateCache({ username: window.vuex.state.users.currentUser.fqn })
const params = { pleroma_settings_store: { 'pleroma-fe': this.cache } }
window.vuex.state.api.backendInteractor
.updateProfile({ params })
.updateProfileJSON({ params })
.then((user) => {
this.setServerSideStorage(user)
this.dirty = false

584
yarn.lock

File diff suppressed because it is too large Load diff