Compare commits

..

No commits in common. "41add4fc6a05dfb4e52bc9d0c6c4a2f1e5df73ef" and "651ce2080e3d724c1f6df04810445bd69baf7237" have entirely different histories.

27 changed files with 488 additions and 517 deletions

View file

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

View file

@ -1 +0,0 @@
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.28.2",
"@babel/runtime": "7.27.1",
"@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.28.0",
"@babel/eslint-parser": "7.28.0",
"@babel/plugin-transform-runtime": "7.28.0",
"@babel/preset-env": "7.28.0",
"@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/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.1",
"chai": "5.2.0",
"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.33.0",
"eslint": "9.26.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.2",
"nightwatch": "3.12.1",
"playwright": "1.52.0",
"postcss": "8.5.6",
"postcss": "8.5.3",
"postcss-html": "^1.5.0",
"postcss-scss": "^4.0.6",
"sass": "1.89.2",

View file

@ -0,0 +1,27 @@
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,6 +8,9 @@ export default {
'Text',
'Icon',
'Border',
'Button',
'RichContent',
'Attachment',
'PollGraph'
],
defaultRules: [

View file

@ -0,0 +1,48 @@
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,7 +4,11 @@ export default {
validInnerComponents: [
'Text',
'Icon',
'Border'
'Input',
'Border',
'ButtonUnstyled',
'Badge',
'Avatar'
],
states: {
hover: ':is(:hover, :focus-visible, :has(:focus-visible)):not(.disabled)',

View file

@ -2,7 +2,18 @@ export default {
name: 'MobileDrawer',
selector: '.mobile-drawer',
validInnerComponents: [
'MenuItem'
'Text',
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'Input',
'PanelHeader',
'MenuItem',
'Notification',
'Alert',
'UserCard'
],
defaultRules: [
{
@ -10,6 +21,21 @@ 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,7 +6,12 @@ export default {
'Link',
'Icon',
'Border',
'Button',
'ButtonUnstyled',
'RichContent',
'Input',
'Avatar',
'Attachment',
'PollGraph'
],
defaultRules: []

View file

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

View file

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

View file

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

View file

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

View file

@ -0,0 +1,20 @@
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,10 +32,7 @@ const EmojiTab = {
newPackName: '',
deleteModalVisible: false,
remotePackInstance: '',
remotePackDownloadAs: '',
remotePackURL: '',
remotePackFile: null
remotePackDownloadAs: ''
}
},
@ -223,7 +220,7 @@ const EmojiTab = {
.then(data => data.json())
.then(resp => {
if (resp === 'ok') {
this.$refs.downloadPackPopover.hidePopover()
this.$refs.dlPackPopover.hidePopover()
return this.refreshPackList()
} else {
@ -235,47 +232,6 @@ 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,64 +62,6 @@
</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>
@ -298,12 +240,12 @@
v-if="pack.remote !== undefined"
class="button button-default btn"
type="button"
@click="$refs.downloadPackPopover.showPopover"
@click="$refs.dlPackPopover.showPopover"
>
{{ $t('admin_dash.emoji.download_pack') }}
<Popover
ref="downloadPackPopover"
ref="dlPackPopover"
trigger="click"
placement="bottom"
bound-to-selector=".emoji-tab"

View file

@ -9,9 +9,23 @@ 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,7 +5,6 @@ export default {
'Link',
'Text',
'Icon',
// Optimization: don't put heavy components unless needed
'Button',
'ButtonUnstyled',
'Input',

View file

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

View file

@ -2,10 +2,40 @@ export default {
name: 'UserCard',
selector: '.user-card',
notEditable: true,
validInnerComponents: [
'Text',
'Link',
'Icon',
'Button',
'ButtonUnstyled',
'Input',
'RichContent',
'Alert'
],
defaultRules: [
{
directives: {
'--profileTint': 'color | $alpha(--background 1)'
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
}
}
]

View file

@ -3,18 +3,16 @@
<div class="user-card-inner">
<div class="user-info">
<div class="user-identity">
<div class="header-overlay">
<div class="banner-image">
<img
:src="bannerImgSrc"
:class="{ 'hide-bio': hideBio }"
>
</div>
<div
class="banner-overlay"
<div class="banner-image">
<img
:src="bannerImgSrc"
:class="{ 'hide-bio': hideBio }"
/>
>
</div>
<div
class="banner-overlay"
:class="{ 'hide-bio': hideBio }"
/>
<a
v-if="avatarAction === 'zoom'"
class="user-info-avatar -link"
@ -460,8 +458,7 @@
>
<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,7 +122,6 @@ 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}`
@ -225,9 +224,6 @@ 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]);
}
}
@ -241,17 +237,6 @@ 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
@ -1947,18 +1932,6 @@ 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),
@ -2087,7 +2060,6 @@ const apiService = {
getCaptcha,
updateProfileImages,
updateProfile,
updateProfileJSON,
importMutes,
importBlocks,
importFollows,
@ -2165,7 +2137,6 @@ 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: true
debug
})
getCssRules(themes3.eager, debug).forEach(rule => {

View file

@ -244,6 +244,9 @@ 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,6 +33,7 @@ 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
.updateProfileJSON({ params })
.updateProfile({ params })
.then((user) => {
this.setServerSideStorage(user)
this.dirty = false

584
yarn.lock

File diff suppressed because it is too large Load diff