-
-
updateProperty(axis, value)"
+ />
+
-
+ {{ shadow?.name ?? $t('settings.style.shadows.shadow_id', { value: index }) }}
+
+
-
-
-
-
-
+
+
-
+
diff --git a/src/i18n/en.json b/src/i18n/en.json
index cc55a6d0..74cddea8 100644
--- a/src/i18n/en.json
+++ b/src/i18n/en.json
@@ -876,6 +876,9 @@
"component": "Component",
"override": "Override",
"shadow_id": "Shadow #{value}",
+ "offset": "Shadow offset",
+ "light_grid": "Use light checkerboard",
+ "name": "Name",
"blur": "Blur",
"spread": "Spread",
"inset": "Inset",
@@ -883,6 +886,7 @@
"filter_hint": {
"always_drop_shadow": "Warning, this shadow always uses {0} when browser supports it.",
"drop_shadow_syntax": "{0} does not support {1} parameter and {2} keyword.",
+ "avatar_inset_short": "Separate inset shadow",
"avatar_inset": "Please note that combining both inset and non-inset shadows on avatars might give unexpected results with transparent avatars.",
"spread_zero": "Shadows with spread > 0 will appear as if it was set to zero",
"inset_classic": "Inset shadows will be using {0}"
@@ -1408,6 +1412,18 @@
"unicode_domain_indicator": {
"tooltip": "This domain contains non-ascii characters."
},
+ "splash": {
+ "loading": "Loading...",
+ "theme": "Applying theme, please wait warmly...",
+ "instance": "Getting instance info...",
+ "settings": "Applying settings...",
+ "almost": "Reticulating splines...",
+ "fun_1": "Drink more water",
+ "fun_2": "Take it easy!",
+ "fun_3": "Suya...",
+ "fun_4": "My Pleroma machine is full power!",
+ "error": "Something went wrong"
+ },
"bookmark_folders": {
"select_folder": "Select bookmark folder",
"creating_folder": "Creating bookmark folder",
diff --git a/src/i18n/nan-TW.json b/src/i18n/nan-TW.json
index 782a75f5..44a693dd 100644
--- a/src/i18n/nan-TW.json
+++ b/src/i18n/nan-TW.json
@@ -190,7 +190,8 @@
"mobile_notifications_close": "關掉通知",
"announcements": "公告",
"search": "Tshuē",
- "mobile_notifications_mark_as_seen": "Lóng 標做有讀"
+ "mobile_notifications_mark_as_seen": "Lóng 標做有讀",
+ "quotes": "引用"
},
"notifications": {
"broken_favorite": "狀態毋知影,leh tshiau-tshuē……",
@@ -212,7 +213,8 @@
"unread_follow_requests": "{num}ê新ê跟tuè請求",
"configuration_tip": "用{theSettings},lí通自訂siánn物佇tsia顯示。{dismiss}",
"configuration_tip_settings": "設定",
- "configuration_tip_dismiss": "Mài koh顯示"
+ "configuration_tip_dismiss": "Mài koh顯示",
+ "subscribed_status": "有發送ê"
},
"polls": {
"add_poll": "開投票",
@@ -252,7 +254,8 @@
},
"load_all_hint": "載入頭前 {saneAmount} ê 繪文字,規个攏載入效能可能 ē khah 食力。",
"load_all": "Kā {emojiAmount} ê 繪文字攏載入",
- "regional_indicator": "地區指引 {letter}"
+ "regional_indicator": "地區指引 {letter}",
+ "hide_custom_emoji": "Khàm掉自訂ê繪文字"
},
"errors": {
"storage_unavailable": "Pleroma buē-tàng the̍h 著瀏覽器儲存 ê。Lí ê 登入狀態抑是局部設定 buē 儲存,mā 凡勢 tú 著意料外 ê 問題。拍開 cookie 看māi。"
@@ -263,7 +266,8 @@
"emoji_reactions": "繪文字 ê 反應",
"reports": "檢舉",
"moves": "用者 ê 移民",
- "load_older": "載入 koh khah 早 ê 互動"
+ "load_older": "載入 koh khah 早 ê 互動",
+ "statuses": "訂ê"
},
"post_status": {
"edit_status": "編輯狀態",
@@ -935,7 +939,34 @@
"notification_extra_chats": "顯示bô讀ê開講",
"notification_extra_announcements": "顯示bô讀ê公告",
"notification_extra_follow_requests": "顯示新ê跟tuè請求",
- "notification_extra_tip": "顯示自訂其他通知ê撇步"
+ "notification_extra_tip": "顯示自訂其他通知ê撇步",
+ "confirm_new_setting": "Lí敢確認新ê設定?",
+ "text_size_tip": "用 {0} 做絕對值,{1} ē根據瀏覽器ê標準文字sài-suh放大縮小。",
+ "theme_debug": "佇處理透明ê時,顯示背景主題ia̋n-jín 所假使ê(DEBUG)",
+ "units": {
+ "time": {
+ "s": "秒鐘",
+ "m": "分鐘",
+ "h": "點鐘",
+ "d": "工"
+ }
+ },
+ "actor_type": "Tsit ê口座是:",
+ "actor_type_Person": "一般ê用者",
+ "actor_type_description": "標記lí ê口座做群組,ē hōo自動轉送提起伊ê狀態。",
+ "actor_type_Group": "群組",
+ "actor_type_Service": "機器lâng",
+ "appearance": "外觀",
+ "confirm_new_question": "Tse看起來kám好?設定ē佇10秒鐘後改轉去。",
+ "revert": "改轉去",
+ "confirm": "確認",
+ "text_size": "文字kap界面ê sài-suh",
+ "text_size_tip2": "毋是 {0} ê值可能ē破壞一寡物件kap主題",
+ "emoji_size": "繪文字ê sài-suh",
+ "navbar_size": "頂 liâu-á êsài-suh",
+ "panel_header_size": "面pang標題ê sài-suh",
+ "visual_tweaks": "細細ê外觀調整",
+ "scale_and_layout": "界面ê sài-suh kap排列"
},
"status": {
"favorites": "收藏",
@@ -1001,7 +1032,7 @@
"show_only_conversation_under_this": "Kan-ta顯示tsit ê狀態ê回應",
"status_history": "狀態ê歷史",
"reaction_count_label": "{num}ê lâng用表情反應",
- "hide_quote": "Khàm條引用ê狀態",
+ "hide_quote": "Khàm掉引用ê狀態",
"display_quote": "顯示引用ê狀態",
"invisible_quote": "引用ê狀態bē當用:{link}",
"more_actions": "佇tsit ê狀態ê其他動作"
diff --git a/src/main.js b/src/main.js
index 60f4d06f..bcfc80b0 100644
--- a/src/main.js
+++ b/src/main.js
@@ -58,56 +58,76 @@ const persistedStateOptions = {
};
(async () => {
- let storageError = false
- const plugins = [pushNotifications]
+ const isFox = Math.floor(Math.random() * 2) > 0 ? '_fox' : ''
+
+ const splashError = (i18n, e) => {
+ const throbber = document.querySelector('#throbber')
+ throbber.addEventListener('animationend', () => {
+ document.querySelector('#mascot').src = `/static/pleromatan_orz${isFox}.png`
+ })
+ throbber.classList.add('dead')
+ document.querySelector('#status').textContent = i18n.global.t('splash.error')
+ console.error('PleromaFE failed to initialize: ', e)
+ }
+
try {
- const persistedState = await createPersistedState(persistedStateOptions)
- plugins.push(persistedState)
- } catch (e) {
- console.error(e)
- storageError = true
- }
- const store = createStore({
- modules: {
- i18n: {
- getters: {
- i18n: () => i18n.global
- }
+ let storageError
+ const plugins = [pushNotifications]
+ try {
+ const persistedState = await createPersistedState(persistedStateOptions)
+ plugins.push(persistedState)
+ } catch (e) {
+ console.error('Storage error', e)
+ storageError = e
+ }
+ document.querySelector('#mascot').src = `/static/pleromatan_apology${isFox}.png`
+ document.querySelector('#status').removeAttribute('class')
+ document.querySelector('#status').textContent = i18n.global.t('splash.loading')
+ document.querySelector('#splash-credit').textContent = i18n.global.t('update.art_by', { linkToArtist: 'pipivovott' })
+ const store = createStore({
+ modules: {
+ i18n: {
+ getters: {
+ i18n: () => i18n.global
+ }
+ },
+ interface: interfaceModule,
+ instance: instanceModule,
+ // TODO refactor users/statuses modules, they depend on each other
+ users: usersModule,
+ statuses: statusesModule,
+ notifications: notificationsModule,
+ lists: listsModule,
+ api: apiModule,
+ config: configModule,
+ profileConfig: profileConfigModule,
+ serverSideStorage: serverSideStorageModule,
+ adminSettings: adminSettingsModule,
+ shout: shoutModule,
+ oauth: oauthModule,
+ authFlow: authFlowModule,
+ mediaViewer: mediaViewerModule,
+ oauthTokens: oauthTokensModule,
+ reports: reportsModule,
+ polls: pollsModule,
+ postStatus: postStatusModule,
+ editStatus: editStatusModule,
+ statusHistory: statusHistoryModule,
+ chats: chatsModule,
+ announcements: announcementsModule,
+ bookmarkFolders: bookmarkFoldersModule
},
- interface: interfaceModule,
- instance: instanceModule,
- // TODO refactor users/statuses modules, they depend on each other
- users: usersModule,
- statuses: statusesModule,
- notifications: notificationsModule,
- lists: listsModule,
- api: apiModule,
- config: configModule,
- profileConfig: profileConfigModule,
- serverSideStorage: serverSideStorageModule,
- adminSettings: adminSettingsModule,
- shout: shoutModule,
- oauth: oauthModule,
- authFlow: authFlowModule,
- mediaViewer: mediaViewerModule,
- oauthTokens: oauthTokensModule,
- reports: reportsModule,
- polls: pollsModule,
- postStatus: postStatusModule,
- editStatus: editStatusModule,
- statusHistory: statusHistoryModule,
- chats: chatsModule,
- announcements: announcementsModule,
- bookmarkFolders: bookmarkFoldersModule
- },
- plugins,
- strict: false // Socket modifies itself, let's ignore this for now.
- // strict: process.env.NODE_ENV !== 'production'
- })
- if (storageError) {
- store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
+ plugins,
+ strict: false // Socket modifies itself, let's ignore this for now.
+ // strict: process.env.NODE_ENV !== 'production'
+ })
+ if (storageError) {
+ store.dispatch('pushGlobalNotice', { messageKey: 'errors.storage_unavailable', level: 'error' })
+ }
+ return await afterStoreSetup({ store, i18n })
+ } catch (e) {
+ splashError(i18n, e)
}
- afterStoreSetup({ store, i18n })
})()
// These are inlined by webpack's DefinePlugin
diff --git a/src/services/style_setter/style_setter.js b/src/services/style_setter/style_setter.js
index e54a95bf..c1603f39 100644
--- a/src/services/style_setter/style_setter.js
+++ b/src/services/style_setter/style_setter.js
@@ -43,16 +43,16 @@ const adoptStyleSheets = (styles) => {
// is nothing to do here.
}
-export const generateTheme = async (inputRuleset, callbacks, debug) => {
+export const generateTheme = (inputRuleset, callbacks, debug) => {
const {
onNewRule = (rule, isLazy) => {},
onLazyFinished = () => {},
onEagerFinished = () => {}
} = callbacks
- // Assuming that "worst case scenario background" is panel background since it's the most likely one
const themes3 = init({
inputRuleset,
+ // Assuming that "worst case scenario background" is panel background since it's the most likely one
ultimateBackgroundColor: inputRuleset[0].directives['--bg'].split('|')[1].trim(),
debug
})
@@ -146,11 +146,11 @@ export const tryLoadCache = () => {
}
}
-export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
+export const applyTheme = (input, onFinish = (data) => {}, debug) => {
const eagerStyles = createStyleSheet(EAGER_STYLE_ID)
const lazyStyles = createStyleSheet(LAZY_STYLE_ID)
- const { lazyProcessFunc } = await generateTheme(
+ const { lazyProcessFunc } = generateTheme(
input,
{
onNewRule (rule, isLazy) {
@@ -169,15 +169,22 @@ export const applyTheme = async (input, onFinish = (data) => {}, debug) => {
adoptStyleSheets([eagerStyles, lazyStyles])
const cache = { engineChecksum: getEngineChecksum(), data: [eagerStyles.rules, lazyStyles.rules] }
onFinish(cache)
- localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
+ try {
+ localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
+ } catch (e) {
+ localStorage.removeItem('pleroma-fe-theme-cache')
+ try {
+ localStorage.setItem('pleroma-fe-theme-cache', JSON.stringify(cache))
+ } catch (e) {
+ console.warn('cannot save cache!', e)
+ }
+ }
}
},
debug
)
setTimeout(lazyProcessFunc, 0)
-
- return Promise.resolve()
}
const extractStyleConfig = ({
@@ -222,7 +229,7 @@ const extractStyleConfig = ({
const defaultStyleConfig = extractStyleConfig(defaultState)
-export const applyConfig = (input) => {
+export const applyConfig = (input, i18n) => {
const config = extractStyleConfig(input)
if (config === defaultStyleConfig) {
@@ -230,8 +237,6 @@ export const applyConfig = (input) => {
}
const head = document.head
- const body = document.body
- body.classList.add('hidden')
const rules = Object
.entries(config)
@@ -252,8 +257,6 @@ export const applyConfig = (input) => {
--roundness: var(--forcedRoundness) !important;
}`, 'index-max')
}
-
- body.classList.remove('hidden')
}
export const getThemes = () => {
diff --git a/src/services/theme_data/theme_data.service.js b/src/services/theme_data/theme_data.service.js
index 2dddfa04..ef7ec645 100644
--- a/src/services/theme_data/theme_data.service.js
+++ b/src/services/theme_data/theme_data.service.js
@@ -452,7 +452,7 @@ export const getCssShadow = (input, usesDropShadow) => {
]).join(' ')).join(', ')
}
-const getCssShadowFilter = (input) => {
+export const getCssShadowFilter = (input) => {
if (input.length === 0) {
return 'none'
}
diff --git a/src/services/theme_data/theme_data_3.service.js b/src/services/theme_data/theme_data_3.service.js
index 39c8b74f..3c2f8a63 100644
--- a/src/services/theme_data/theme_data_3.service.js
+++ b/src/services/theme_data/theme_data_3.service.js
@@ -182,7 +182,7 @@ export const init = ({
const rulesetUnsorted = [
...Object.values(components)
- .map(c => (c.defaultRules || []).map(r => ({ component: c.name, ...r, source: 'Built-in' })))
+ .map(c => (c.defaultRules || []).map(r => ({ source: 'Built-in', component: c.name, ...r })))
.reduce((acc, arr) => [...acc, ...arr], []),
...inputRuleset
].map(rule => {
@@ -198,18 +198,33 @@ export const init = ({
const ruleset = rulesetUnsorted
.map((data, index) => ({ data, index }))
- .sort(({ data: a, index: ai }, { data: b, index: bi }) => {
+ .toSorted(({ data: a, index: ai }, { data: b, index: bi }) => {
const parentsA = unroll(a).length
const parentsB = unroll(b).length
- if (parentsA === parentsB) {
- if (a.component === 'Text') return -1
- if (b.component === 'Text') return 1
+ let aScore = 0
+ let bScore = 0
+
+ aScore += parentsA * 1000
+ bScore += parentsB * 1000
+
+ aScore += a.variant !== 'normal' ? 100 : 0
+ bScore += b.variant !== 'normal' ? 100 : 0
+
+ aScore += a.state.filter(x => x !== 'normal').length * 1000
+ bScore += b.state.filter(x => x !== 'normal').length * 1000
+
+ aScore += a.component === 'Text' ? 1 : 0
+ bScore += b.component === 'Text' ? 1 : 0
+
+ // Debug
+ a.specifityScore = aScore
+ b.specifityScore = bScore
+
+ if (aScore === bScore) {
return ai - bi
}
- if (parentsA === 0 && parentsB !== 0) return -1
- if (parentsB === 0 && parentsA !== 0) return 1
- return parentsA - parentsB
+ return aScore - bScore
})
.map(({ data }) => data)
@@ -235,7 +250,10 @@ export const init = ({
// Inheriting all of the applicable rules
const existingRules = ruleset.filter(findRules(combination))
- const computedDirectives = existingRules.map(r => r.directives).reduce((acc, directives) => ({ ...acc, ...directives }), {})
+ const computedDirectives =
+ existingRules
+ .map(r => r.directives)
+ .reduce((acc, directives) => ({ ...acc, ...directives }), {})
const computedRule = {
...combination,
directives: computedDirectives
diff --git a/static/pleromatan_apology.png b/static/pleromatan_apology.png
new file mode 100644
index 00000000..36ad7aeb
Binary files /dev/null and b/static/pleromatan_apology.png differ
diff --git a/static/pleromatan_apology_fox.png b/static/pleromatan_apology_fox.png
new file mode 100644
index 00000000..17f87694
Binary files /dev/null and b/static/pleromatan_apology_fox.png differ
diff --git a/static/pleromatan_orz.png b/static/pleromatan_orz.png
new file mode 100644
index 00000000..aa54411f
Binary files /dev/null and b/static/pleromatan_orz.png differ
diff --git a/static/pleromatan_orz_fox.png b/static/pleromatan_orz_fox.png
new file mode 100644
index 00000000..936e42f1
Binary files /dev/null and b/static/pleromatan_orz_fox.png differ