Merge commit 'c4f47adb49663f24ee80d9174ab24431a8c165c0' into glitch-soc/merge-upstream

Too many conflicts to list, because of webpack → vite migration.
This commit is contained in:
Claire
2025-05-24 12:37:51 +02:00
159 changed files with 2373 additions and 7784 deletions

View File

@@ -9,6 +9,7 @@ services:
environment:
RAILS_ENV: development
NODE_ENV: development
VITE_RUBY_HOST: 0.0.0.0
BIND: 0.0.0.0
BOOTSNAP_CACHE_DIR: /tmp
REDIS_HOST: redis
@@ -22,11 +23,12 @@ services:
ES_PORT: '9200'
LIBRE_TRANSLATE_ENDPOINT: http://libretranslate:5000
LOCAL_DOMAIN: ${LOCAL_DOMAIN:-localhost:3000}
VITE_DEV_SERVER_PUBLIC: ${VITE_DEV_SERVER_PUBLIC:-localhost:3036}
# Overrides default command so things don't shut down after the process ends.
command: sleep infinity
ports:
- '3000:3000'
- '3035:3035'
- '3036:3036'
- '4000:4000'
networks:
- external_network

View File

@@ -25,23 +25,6 @@
'tesseract.js', // Requires code changes
'react-hotkeys', // Requires code changes
// Requires Webpacker upgrade or replacement
'@svgr/webpack',
'@types/webpack',
'babel-loader',
'compression-webpack-plugin',
'css-loader',
'imports-loader',
'mini-css-extract-plugin',
'postcss-loader',
'sass-loader',
'terser-webpack-plugin',
'webpack',
'webpack-assets-manifest',
'webpack-bundle-analyzer',
'webpack-dev-server',
'webpack-cli',
// react-router: Requires manual upgrade
'history',
'react-router-dom',

View File

@@ -49,7 +49,7 @@ jobs:
public/assets
public/packs
public/packs-test
tmp/cache/webpacker
tmp/cache/vite
key: ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
restore-keys: |
${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
@@ -63,7 +63,7 @@ jobs:
- name: Archive asset artifacts
run: |
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs*
tar --exclude={"*.br","*.gz"} -zcf artifacts.tar.gz public/assets public/packs* tmp/cache/vite/last-build*.json
- uses: actions/upload-artifact@v4
if: matrix.mode == 'test'

3
.gitignore vendored
View File

@@ -21,10 +21,11 @@
/public/system
/public/assets
/public/packs
/public/packs-dev
/public/packs-test
.env
.env.production
/node_modules/
node_modules/
/build/
# Ignore Vagrant files

View File

@@ -18,10 +18,6 @@
!/log/.keep
/tmp
/coverage
/public/system
/public/assets
/public/packs
/public/packs-test
.env
.env.production
.env.development
@@ -60,6 +56,7 @@ docker-compose.override.yml
/public/packs
/public/packs-test
/public/system
/public/vite*
# Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json

View File

@@ -95,7 +95,6 @@ gem 'tty-prompt', '~> 0.23', require: false
gem 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023'
gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
gem 'json-ld'
@@ -230,3 +229,5 @@ gem 'rubyzip', '~> 2.3'
gem 'hcaptcha', '~> 7.1'
gem 'mail', '~> 2.8'
gem 'vite_rails', '~> 3.0.19'

View File

@@ -203,6 +203,7 @@ GEM
railties (>= 5)
dotenv (3.1.8)
drb (2.2.1)
dry-cli (1.2.0)
elasticsearch (7.17.11)
elasticsearch-api (= 7.17.11)
elasticsearch-transport (= 7.17.11)
@@ -806,7 +807,6 @@ GEM
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
semantic_range (3.1.0)
shoulda-matchers (6.5.0)
activesupport (>= 5.2.0)
sidekiq (6.5.12)
@@ -892,6 +892,15 @@ GEM
validate_url (1.0.15)
activemodel (>= 3.0.0)
public_suffix
vite_rails (3.0.19)
railties (>= 5.1, < 9)
vite_ruby (~> 3.0, >= 3.2.2)
vite_ruby (3.9.2)
dry-cli (>= 0.7, < 2)
logger (~> 1.6)
mutex_m
rack-proxy (~> 0.6, >= 0.6.1)
zeitwerk (~> 2.2)
warden (1.2.9)
rack (>= 2.0.9)
webauthn (3.4.0)
@@ -910,11 +919,6 @@ GEM
addressable (>= 2.8.0)
crack (>= 0.3.2)
hashdiff (>= 0.4.0, < 2.0.0)
webpacker (5.4.4)
activesupport (>= 5.2)
rack-proxy (>= 0.6.1)
railties (>= 5.2)
semantic_range (>= 2.3.0)
webrick (1.9.1)
websocket (1.2.11)
websocket-driver (0.7.7)
@@ -1078,9 +1082,9 @@ DEPENDENCIES
tty-prompt (~> 0.23)
twitter-text (~> 3.1.0)
tzinfo-data (~> 1.2023)
vite_rails (~> 3.0.19)
webauthn (~> 3.0)
webmock (~> 3.18)
webpacker (~> 5.4)
webpush!
xorcist (~> 1.1)

View File

@@ -1,4 +1,4 @@
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
stream: env PORT=4000 yarn workspace @mastodon/streaming start
webpack: bin/webpack-dev-server
vite: yarn dev

View File

@@ -12,7 +12,7 @@ module ThemingConcern
def current_skin
@current_skin ||= begin
skins = Themes.instance.skins_for(current_flavour)
[current_user&.setting_skin, Setting.skin, 'system', 'default'].find { |skin| skins.include?(skin) }
[current_user&.setting_skin, Setting.skin, 'system', 'application'].find { |skin| skins.include?(skin) }
end
end

View File

@@ -248,22 +248,8 @@ module ApplicationHelper
end
# glitch-soc addition to handle the multiple flavors
def preload_locale_pack
supported_locales = Themes.instance.flavour(current_flavour)['locales']
preload_pack_asset "locales/#{current_flavour}/#{I18n.locale}-json.js" if supported_locales.include?(I18n.locale.to_s)
end
def flavoured_javascript_pack_tag(pack_name, **)
javascript_pack_tag("flavours/#{current_flavour}/#{pack_name}", **)
end
def flavoured_stylesheet_pack_tag(pack_name, **)
stylesheet_pack_tag("flavours/#{current_flavour}/#{pack_name}", **)
end
def preload_signed_in_js_packs
preload_files = Themes.instance.flavour(current_flavour)&.fetch('signed_in_preload', nil) || []
safe_join(preload_files.map { |entry| preload_pack_asset entry })
def flavoured_vite_typescript_tag(pack_name, **)
vite_typescript_tag("#{Themes.instance.flavour(current_flavour)['pack_directory'].delete_prefix('app/javascript/')}/#{pack_name}", **)
end
private

View File

@@ -4,7 +4,7 @@ module RoutingHelper
extend ActiveSupport::Concern
include ActionView::Helpers::AssetTagHelper
include Webpacker::Helper
include ViteRails::TagHelpers
included do
include Rails.application.routes.url_helpers
@@ -25,7 +25,7 @@ module RoutingHelper
end
def frontend_asset_path(source, **)
asset_pack_path("media/#{source}", **)
vite_asset_path(source, **)
end
def frontend_asset_url(source, **)

View File

@@ -6,11 +6,13 @@ module ThemeHelper
if theme == 'system'
''.html_safe.tap do |tags|
tags << stylesheet_pack_tag("skins/#{flavour}/mastodon-light", media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
tags << stylesheet_pack_tag("skins/#{flavour}/default", media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
tags << vite_stylesheet_tag("skins/#{flavour}/mastodon-light.scss", media: 'not all and (prefers-color-scheme: dark)', crossorigin: 'anonymous')
tags << vite_stylesheet_tag("skins/#{flavour}/application.scss", media: '(prefers-color-scheme: dark)', crossorigin: 'anonymous')
end
elsif theme == 'default'
vite_stylesheet_tag "skins/#{flavour}/application.scss", media: 'all', crossorigin: 'anonymous'
else
stylesheet_pack_tag "skins/#{flavour}/#{theme}", media: 'all', crossorigin: 'anonymous'
vite_stylesheet_tag "skins/#{flavour}/#{theme}.scss", media: 'all', crossorigin: 'anonymous'
end
end

View File

@@ -1,4 +1,3 @@
import './public-path';
import { createRoot } from 'react-dom/client';
import Rails from '@rails/ujs';
@@ -273,7 +272,7 @@ async function mountReactComponent(element: Element) {
);
const { default: Component } = (await import(
`@/mastodon/components/admin/${componentName}`
`@/mastodon/components/admin/${componentName}.jsx`
)) as { default: React.ComponentType };
const root = createRoot(element);

View File

@@ -1,11 +1,6 @@
import './public-path';
import { loadLocale } from 'mastodon/locales';
import main from 'mastodon/main';
import { start } from '../mastodon/common';
import { loadLocale } from '../mastodon/locales';
import { loadPolyfills } from '../mastodon/polyfills';
start();
import { loadPolyfills } from 'mastodon/polyfills';
loadPolyfills()
.then(loadLocale)

View File

@@ -1,4 +0,0 @@
/* This file is a hack to have something more reliable than the upstream `common` tag
that is implicitly generated as the common chunk through webpack's `splitChunks` config */
import './public-path';

View File

@@ -0,0 +1,3 @@
import { start } from 'mastodon/common';
start();

View File

@@ -1,15 +1,11 @@
import './public-path';
import { createRoot } from 'react-dom/client';
import { afterInitialRender } from 'mastodon/hooks/useRenderSignal';
import { start } from '../mastodon/common';
import { Status } from '../mastodon/features/standalone/status';
import { loadPolyfills } from '../mastodon/polyfills';
import ready from '../mastodon/ready';
start();
function loaded() {
const mountNode = document.getElementById('mastodon-status');

View File

@@ -1,4 +1,3 @@
import './public-path';
import ready from '../mastodon/ready';
ready(() => {

View File

@@ -1,4 +0,0 @@
/* Placeholder file to have `inert.scss` compiled by Webpack
This is used by the `wicg-inert` polyfill */
import '../styles/inert.scss';

View File

@@ -1,3 +0,0 @@
import '../styles/mailer.scss';
require.context('../icons');

View File

@@ -1,23 +0,0 @@
// Dynamically set webpack's loading path depending on a meta header, in order
// to share the same assets regardless of instance configuration.
// See https://webpack.js.org/guides/public-path/#on-the-fly
function removeOuterSlashes(string: string) {
return string.replace(/^\/*/, '').replace(/\/*$/, '');
}
function formatPublicPath(host = '', path = '') {
let formattedHost = removeOuterSlashes(host);
if (formattedHost && !/^http/i.test(formattedHost)) {
formattedHost = `//${formattedHost}`;
}
const formattedPath = removeOuterSlashes(path);
return `${formattedHost}/${formattedPath}/`;
}
const cdnHost = document.querySelector<HTMLMetaElement>('meta[name=cdn-host]');
__webpack_public_path__ = formatPublicPath(
cdnHost ? cdnHost.content : '',
process.env.PUBLIC_OUTPUT_PATH,
);

View File

@@ -1,7 +1,5 @@
import { createRoot } from 'react-dom/client';
import './public-path';
import { IntlMessageFormat } from 'intl-messageformat';
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
import { defineMessages } from 'react-intl';
@@ -10,7 +8,6 @@ import Rails from '@rails/ujs';
import axios from 'axios';
import { throttle } from 'lodash';
import { start } from '../mastodon/common';
import { timeAgoString } from '../mastodon/components/relative_timestamp';
import emojify from '../mastodon/features/emoji/emoji';
import loadKeyboardExtensions from '../mastodon/load_keyboard_extensions';
@@ -20,8 +17,6 @@ import ready from '../mastodon/ready';
import 'cocoon-js-vanilla';
start();
const messages = defineMessages({
usernameTaken: {
id: 'username.taken',
@@ -153,9 +148,7 @@ function loaded() {
const reactComponents = document.querySelectorAll('[data-component]');
if (reactComponents.length > 0) {
import(
/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container'
)
import('../mastodon/containers/media_container')
.then(({ default: MediaContainer }) => {
reactComponents.forEach((component) => {
Array.from(component.children).forEach((child) => {

View File

@@ -8,8 +8,6 @@ and performs no other task.
*/
import './public-path';
import axios from 'axios';
interface JRDLink {

View File

@@ -1,13 +1,9 @@
import './public-path';
import { createRoot } from 'react-dom/client';
import { start } from '../mastodon/common';
import ComposeContainer from '../mastodon/containers/compose_container';
import { loadPolyfills } from '../mastodon/polyfills';
import ready from '../mastodon/ready';
start();
function loaded() {
const mountNode = document.getElementById('mastodon-compose');

View File

@@ -1,4 +1,3 @@
import './public-path';
import axios from 'axios';
import ready from '../mastodon/ready';

View File

@@ -1,8 +1,6 @@
import Rails from '@rails/ujs';
export function start() {
require.context('@/images/', true, /\.(jpg|png|svg)$/);
try {
Rails.start();
} catch {

View File

@@ -18,8 +18,8 @@ import StarIcon from '@/material-icons/400-24px/star-fill.svg?react';
import StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';

View File

@@ -1,4 +1,3 @@
import '@/entrypoints/public-path';
import { createRoot } from 'react-dom/client';
import Rails from '@rails/ujs';
@@ -273,7 +272,7 @@ async function mountReactComponent(element: Element) {
);
const { default: Component } = (await import(
`@/flavours/glitch/components/admin/${componentName}`
`@/flavours/glitch/components/admin/${componentName}.jsx`
)) as { default: React.ComponentType };
const root = createRoot(element);

View File

@@ -1,12 +1,7 @@
import '@/entrypoints/public-path';
import { start } from 'flavours/glitch/common';
import { loadLocale } from 'flavours/glitch/locales';
import main from 'flavours/glitch/main';
import { loadPolyfills } from 'flavours/glitch/polyfills';
start();
loadPolyfills()
.then(loadLocale)
.then(main)

View File

@@ -1,7 +0,0 @@
/* This file is a hack to have something more reliable than the upstream `common` tag
that is implicitly generated as the common chunk through webpack's `splitChunks` config */
import '@/entrypoints/public-path';
// This is a hack to ensures that webpack compiles our images.
require.context('../images', true);

View File

@@ -0,0 +1,3 @@
import { start } from 'flavours/glitch/common';
start();

View File

@@ -1,15 +1,10 @@
import { createRoot } from 'react-dom/client';
import '@/entrypoints/public-path';
import { start } from 'flavours/glitch/common';
import { Status } from 'flavours/glitch/features/standalone/status';
import { afterInitialRender } from 'flavours/glitch/hooks/useRenderSignal';
import { loadPolyfills } from 'flavours/glitch/polyfills';
import ready from 'flavours/glitch/ready';
start();
function loaded() {
const mountNode = document.getElementById('mastodon-status');

View File

@@ -1,4 +1,3 @@
import '@/entrypoints/public-path';
import ready from 'flavours/glitch/ready';
ready(() => {

View File

@@ -1,4 +0,0 @@
/* Placeholder file to have `inert.scss` compiled by Webpack
This is used by the `wicg-inert` polyfill */
import '@/styles/inert.scss';

View File

@@ -1,3 +0,0 @@
import '@/styles/mailer.scss';
require.context('@/icons');

View File

@@ -1,7 +1,5 @@
import { createRoot } from 'react-dom/client';
import '@/entrypoints/public-path';
import { IntlMessageFormat } from 'intl-messageformat';
import type { MessageDescriptor, PrimitiveType } from 'react-intl';
import { defineMessages } from 'react-intl';
@@ -10,7 +8,6 @@ import Rails from '@rails/ujs';
import axios from 'axios';
import { throttle } from 'lodash';
import { start } from 'flavours/glitch/common';
import { timeAgoString } from 'flavours/glitch/components/relative_timestamp';
import emojify from 'flavours/glitch/features/emoji/emoji';
import loadKeyboardExtensions from 'flavours/glitch/load_keyboard_extensions';
@@ -20,8 +17,6 @@ import ready from 'flavours/glitch/ready';
import 'cocoon-js-vanilla';
start();
const messages = defineMessages({
usernameTaken: {
id: 'username.taken',
@@ -153,9 +148,7 @@ function loaded() {
const reactComponents = document.querySelectorAll('[data-component]');
if (reactComponents.length > 0) {
import(
/* webpackChunkName: "containers/media_container" */ 'flavours/glitch/containers/media_container'
)
import('flavours/glitch/containers/media_container')
.then(({ default: MediaContainer }) => {
reactComponents.forEach((component) => {
Array.from(component.children).forEach((child) => {

View File

@@ -8,8 +8,6 @@ and performs no other task.
*/
import '@/entrypoints/public-path';
import axios from 'axios';
interface JRDLink {

View File

@@ -1,13 +1,9 @@
import '@/entrypoints/public-path';
import { createRoot } from 'react-dom/client';
import { start } from 'flavours/glitch/common';
import ComposeContainer from 'flavours/glitch/containers/compose_container';
import { loadPolyfills } from 'flavours/glitch/polyfills';
import ready from 'flavours/glitch/ready';
start();
function loaded() {
const mountNode = document.getElementById('mastodon-compose');

View File

@@ -1,4 +1,3 @@
import '@/entrypoints/public-path';
import axios from 'axios';
import ready from 'flavours/glitch/ready';

View File

@@ -15,10 +15,6 @@ import type { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { useSpring, animated } from '@react-spring/web';
import Textarea from 'react-textarea-autosize';
import { length } from 'stringz';
// eslint-disable-next-line import/extensions
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
// eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
import { showAlertForError } from 'flavours/glitch/actions/alerts';
import { uploadThumbnail } from 'flavours/glitch/actions/compose';
@@ -350,9 +346,15 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
fetchTesseract()
.then(async ({ createWorker }) => {
const [tesseractWorkerPath, tesseractCorePath] = await Promise.all([
// eslint-disable-next-line import/extensions
import('tesseract.js/dist/worker.min.js?url'),
// eslint-disable-next-line import/no-extraneous-dependencies
import('tesseract.js-core/tesseract-core.wasm.js?url'),
]);
const worker = await createWorker('eng', 1, {
workerPath: tesseractWorkerPath as string,
corePath: tesseractCorePath as string,
workerPath: tesseractWorkerPath.default,
corePath: tesseractCorePath.default,
langPath: `${assetHost}/ocr/lang-data`,
cacheMethod: 'write',
});
@@ -501,5 +503,4 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
);
},
);
AltTextModal.displayName = 'AltTextModal';

View File

@@ -9,28 +9,27 @@
// to ensure that the prevaled file is regenerated by Babel
// version: 4
const { NimbleEmojiIndex } = require('emoji-mart');
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
import { NimbleEmojiIndex } from 'emoji-mart';
import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data';
let data = require('./emoji_data.json');
const emojiMap = require('./emoji_map.json');
const { unicodeToFilename } = require('./unicode_to_filename');
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
import data from './emoji_data.json';
import emojiMap from './emoji_map.json';
import { unicodeToFilename } from './unicode_to_filename';
import { unicodeToUnifiedName } from './unicode_to_unified_name';
emojiMartUncompress(data);
const emojiMartData = data;
const emojiIndex = new NimbleEmojiIndex(emojiMartData);
const excluded = ['®', '©', '™'];
const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿'];
const shortcodeMap = {};
const excluded = ['®', '©', '™'];
const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿'];
const shortcodeMap = {};
const shortCodesToEmojiData = {};
const emojisWithoutShortCodes = [];
Object.keys(emojiIndex.emojis).forEach(key => {
Object.keys(emojiIndex.emojis).forEach((key) => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
@@ -41,22 +40,22 @@ Object.keys(emojiIndex.emojis).forEach(key => {
shortcodeMap[emoji.native] = emoji.id;
});
const stripModifiers = unicode => {
skinTones.forEach(tone => {
const stripModifiers = (unicode) => {
skinTones.forEach((tone) => {
unicode = unicode.replace(tone, '');
});
return unicode;
};
Object.keys(emojiMap).forEach(key => {
Object.keys(emojiMap).forEach((key) => {
if (excluded.includes(key)) {
delete emojiMap[key];
return;
}
const normalizedKey = stripModifiers(key);
let shortcode = shortcodeMap[normalizedKey];
let shortcode = shortcodeMap[normalizedKey];
if (!shortcode) {
shortcode = shortcodeMap[normalizedKey + '\uFE0F'];
@@ -82,7 +81,7 @@ Object.keys(emojiMap).forEach(key => {
}
});
Object.keys(emojiIndex.emojis).forEach(key => {
Object.keys(emojiIndex.emojis).forEach((key) => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
@@ -94,9 +93,11 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let { short_names, search, unified } = emojiMartData.emojis[key];
if (short_names[0] !== key) {
throw new Error('The compressor expects the first short_code to be the ' +
'key. It may need to be rewritten if the emoji change such that this ' +
'is no longer the case.');
throw new Error(
'The compressor expects the first short_code to be the ' +
'key. It may need to be rewritten if the emoji change such that this ' +
'is no longer the case.',
);
}
short_names = short_names.slice(1); // first short name can be inferred from the key
@@ -117,20 +118,22 @@ Object.keys(emojiIndex.emojis).forEach(key => {
// JSON.parse/stringify is to emulate what @preval is doing and avoid any
// inconsistent behavior in dev mode
module.exports = JSON.parse(JSON.stringify([
shortCodesToEmojiData,
/*
* The property `skins` is not found in the current context.
* This could potentially lead to issues when interacting with modules or data structures
* that expect the presence of `skins` property.
* Currently, no definitions or references to `skins` property can be found in:
* - {@link node_modules/emoji-mart/dist/utils/data.js}
* - {@link node_modules/emoji-mart/data/all.json}
* - {@link app/javascript/mastodon/features/emoji/emoji_compressed.d.ts#Skins}
* Future refactorings or updates should consider adding definitions or handling for `skins` property.
*/
emojiMartData.skins,
emojiMartData.categories,
emojiMartData.aliases,
emojisWithoutShortCodes
]));
export default JSON.parse(
JSON.stringify([
shortCodesToEmojiData,
/*
* The property `skins` is not found in the current context.
* This could potentially lead to issues when interacting with modules or data structures
* that expect the presence of `skins` property.
* Currently, no definitions or references to `skins` property can be found in:
* - {@link node_modules/emoji-mart/dist/utils/data.js}
* - {@link node_modules/emoji-mart/data/all.json}
* - {@link app/javascript/flavours/glitch/features/emoji/emoji_compressed.d.ts#Skins}
* Future refactorings or updates should consider adding definitions or handling for `skins` property.
*/
emojiMartData.skins,
emojiMartData.categories,
emojiMartData.aliases,
emojisWithoutShortCodes,
]),
);

View File

@@ -3,9 +3,13 @@
// emojiIndex.search functionality.
import type { BaseEmoji } from 'emoji-mart';
import type { Emoji } from 'emoji-mart/dist-es/utils/data';
// eslint-disable-next-line import/no-unresolved
import emojiCompressed from 'virtual:mastodon-emoji-compressed';
import type {
Search,
ShortCodesToEmojiData,
} from 'virtual:mastodon-emoji-compressed';
import type { Search, ShortCodesToEmojiData } from './emoji_compressed';
import emojiCompressed from './emoji_compressed';
import { unicodeToUnifiedName } from './unicode_to_unified_name';
type Emojis = Record<

View File

@@ -2,11 +2,13 @@
// (i.e. the svg filename) and a shortCode intended to be shown
// as a "title" attribute in an HTML element (aka tooltip).
// eslint-disable-next-line import/no-unresolved
import emojiCompressed from 'virtual:mastodon-emoji-compressed';
import type {
FilenameData,
ShortCodesToEmojiDataKey,
} from './emoji_compressed';
import emojiCompressed from './emoji_compressed';
} from 'virtual:mastodon-emoji-compressed';
import { unicodeToFilename } from './unicode_to_filename';
type UnicodeMapping = Record<

View File

@@ -1,6 +1,6 @@
// taken from:
// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
exports.unicodeToFilename = (str) => {
export const unicodeToFilename = (str) => {
let result = '';
let charCode = 0;
let p = 0;

View File

@@ -6,7 +6,7 @@ function padLeft(str, num) {
return str;
}
exports.unicodeToUnifiedName = (str) => {
export const unicodeToUnifiedName = (str) => {
let output = '';
for (let i = 0; i < str.length; i += 2) {

View File

@@ -1,243 +1,243 @@
export function EmojiPicker () {
return import(/* webpackChunkName: "flavours/glitch/async/emoji_picker" */'../../emoji/emoji_picker');
return import('../../emoji/emoji_picker');
}
export function Compose () {
return import(/* webpackChunkName: "flavours/glitch/async/compose" */'../../compose');
return import('../../compose');
}
export function Notifications () {
return import(/* webpackChunkName: "flavours/glitch/async/notifications" */'../../notifications_v2');
return import('../../notifications_v2');
}
export function HomeTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/home_timeline" */'../../home_timeline');
return import('../../home_timeline');
}
export function PublicTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/public_timeline" */'../../public_timeline');
return import('../../public_timeline');
}
export function CommunityTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/community_timeline" */'../../community_timeline');
return import('../../community_timeline');
}
export function Firehose () {
return import(/* webpackChunkName: "flavours/glitch/async/firehose" */'../../firehose');
return import('../../firehose');
}
export function HashtagTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/hashtag_timeline" */'../../hashtag_timeline');
return import('../../hashtag_timeline');
}
export function ListTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/list_timeline" */'../../list_timeline');
return import('../../list_timeline');
}
export function Lists () {
return import(/* webpackChunkName: "flavours/glitch/async/lists" */'../../lists');
return import('../../lists');
}
export function DirectTimeline() {
return import(/* webpackChunkName: "flavours/glitch/async/direct_timeline" */'../../direct_timeline');
return import('../../direct_timeline');
}
export function Status () {
return import(/* webpackChunkName: "flavours/glitch/async/status" */'../../status');
return import('../../status');
}
export function GettingStarted () {
return import(/* webpackChunkName: "flavours/glitch/async/getting_started" */'../../getting_started');
return import('../../getting_started');
}
export function GettingStartedMisc () {
return import(/* webpackChunkName: "flavours/glitch/async/getting_started_misc" */'../../getting_started_misc');
return import('../../getting_started_misc');
}
export function KeyboardShortcuts () {
return import(/* webpackChunkName: "flavours/glitch/async/keyboard_shortcuts" */'../../keyboard_shortcuts');
return import('../../keyboard_shortcuts');
}
export function PinnedStatuses () {
return import(/* webpackChunkName: "flavours/glitch/async/pinned_statuses" */'../../pinned_statuses');
return import('../../pinned_statuses');
}
export function AccountTimeline () {
return import(/* webpackChunkName: "flavours/glitch/async/account_timeline" */'../../account_timeline');
return import('../../account_timeline');
}
export function AccountGallery () {
return import(/* webpackChunkName: "flavours/glitch/async/account_gallery" */'../../account_gallery');
return import('../../account_gallery');
}
export function AccountFeatured() {
return import(/* webpackChunkName: "flavours/glitch/async/account_featured" */'../../account_featured');
return import('../../account_featured');
}
export function Followers () {
return import(/* webpackChunkName: "flavours/glitch/async/followers" */'../../followers');
return import('../../followers');
}
export function Following () {
return import(/* webpackChunkName: "flavours/glitch/async/following" */'../../following');
return import('../../following');
}
export function Reblogs () {
return import(/* webpackChunkName: "flavours/glitch/async/reblogs" */'../../reblogs');
return import('../../reblogs');
}
export function Favourites () {
return import(/* webpackChunkName: "flavours/glitch/async/favourites" */'../../favourites');
return import('../../favourites');
}
export function FollowRequests () {
return import(/* webpackChunkName: "flavours/glitch/async/follow_requests" */'../../follow_requests');
return import('../../follow_requests');
}
export function FavouritedStatuses () {
return import(/* webpackChunkName: "flavours/glitch/async/favourited_statuses" */'../../favourited_statuses');
return import('../../favourited_statuses');
}
export function FollowedTags () {
return import(/* webpackChunkName: "flavours/glitch/async/followed_tags" */'../../followed_tags');
return import('../../followed_tags');
}
export function BookmarkedStatuses () {
return import(/* webpackChunkName: "flavours/glitch/async/bookmarked_statuses" */'../../bookmarked_statuses');
return import('../../bookmarked_statuses');
}
export function Blocks () {
return import(/* webpackChunkName: "flavours/glitch/async/blocks" */'../../blocks');
return import('../../blocks');
}
export function DomainBlocks () {
return import(/* webpackChunkName: "flavours/glitch/async/domain_blocks" */'../../domain_blocks');
return import('../../domain_blocks');
}
export function Mutes () {
return import(/* webpackChunkName: "flavours/glitch/async/mutes" */'../../mutes');
return import('../../mutes');
}
export function MuteModal () {
return import(/* webpackChunkName: "flavours/glitch/async/mute_modal" */'../components/mute_modal');
return import('../components/mute_modal');
}
export function BlockModal () {
return import(/* webpackChunkName: "flavours/glitch/async/block_modal" */'../components/block_modal');
return import('../components/block_modal');
}
export function DomainBlockModal () {
return import(/* webpackChunkName: "flavours/glitch/async/modals/domain_block_modal" */'../components/domain_block_modal');
return import('../components/domain_block_modal');
}
export function ReportModal () {
return import(/* webpackChunkName: "flavours/glitch/async/report_modal" */'../components/report_modal');
return import('../components/report_modal');
}
export function SettingsModal () {
return import(/* webpackChunkName: "flavours/glitch/async/settings_modal" */'../../local_settings');
return import('../../local_settings');
}
export function IgnoreNotificationsModal () {
return import(/* webpackChunkName: "flavours/glitch/async/ignore_notifications_modal" */'../components/ignore_notifications_modal');
return import('../components/ignore_notifications_modal');
}
export function MediaGallery () {
return import(/* webpackChunkName: "flavours/glitch/async/media_gallery" */'../../../components/media_gallery');
return import('../../../components/media_gallery');
}
export function Video () {
return import(/* webpackChunkName: "flavours/glitch/async/video" */'../../video');
return import('../../video');
}
export function EmbedModal () {
return import(/* webpackChunkName: "flavours/glitch/async/embed_modal" */'../components/embed_modal');
return import('../components/embed_modal');
}
export function ListAdder () {
return import(/* webpackChunkName: "features/glitch/async/list_adder" */'../../list_adder');
return import('../../list_adder');
}
export function Tesseract () {
return import(/*webpackChunkName: "tesseract" */'tesseract.js');
return import('tesseract.js');
}
export function Audio () {
return import(/* webpackChunkName: "features/glitch/async/audio" */'../../audio');
return import('../../audio');
}
export function Directory () {
return import(/* webpackChunkName: "features/glitch/async/directory" */'../../directory');
return import('../../directory');
}
export function OnboardingProfile () {
return import(/* webpackChunkName: "features/glitch/async/onboarding" */'../../onboarding/profile');
return import('../../onboarding/profile');
}
export function OnboardingFollows () {
return import(/* webpackChunkName: "features/glitch/async/onboarding" */'../../onboarding/follows');
return import('../../onboarding/follows');
}
export function CompareHistoryModal () {
return import(/*webpackChunkName: "flavours/glitch/async/compare_history_modal" */'../components/compare_history_modal');
return import('../components/compare_history_modal');
}
export function Explore () {
return import(/* webpackChunkName: "flavours/glitch/async/explore" */'../../explore');
return import('../../explore');
}
export function Search () {
return import(/* webpackChunkName: "features/glitch/async/explore" */'../../search');
return import('../../search');
}
export function FilterModal () {
return import(/*webpackChunkName: "flavours/glitch/async/filter_modal" */'../components/filter_modal');
return import('../components/filter_modal');
}
export function InteractionModal () {
return import(/*webpackChunkName: "flavours/glitch/async/modals/interaction_modal" */'../../interaction_modal');
return import('../../interaction_modal');
}
export function SubscribedLanguagesModal () {
return import(/*webpackChunkName: "flavours/glitch/async/modals/subscribed_languages_modal" */'../../subscribed_languages_modal');
return import('../../subscribed_languages_modal');
}
export function ClosedRegistrationsModal () {
return import(/*webpackChunkName: "flavours/glitch/async/modals/closed_registrations_modal" */'../../closed_registrations_modal');
return import('../../closed_registrations_modal');
}
export function About () {
return import(/*webpackChunkName: "features/glitch/async/about" */'../../about');
return import('../../about');
}
export function PrivacyPolicy () {
return import(/*webpackChunkName: "features/glitch/async/privacy_policy" */'../../privacy_policy');
return import('../../privacy_policy');
}
export function TermsOfService () {
return import(/*webpackChunkName: "features/glitch/async/terms_of_service" */'../../terms_of_service');
return import('../../terms_of_service');
}
export function NotificationRequests () {
return import(/*webpackChunkName: "features/glitch/notifications/requests" */'../../notifications/requests');
return import('../../notifications/requests');
}
export function NotificationRequest () {
return import(/*webpackChunkName: "features/glitch/notifications/request" */'../../notifications/request');
return import('../../notifications/request');
}
export function LinkTimeline () {
return import(/*webpackChunkName: "features/glitch/link_timeline" */'../../link_timeline');
return import('../../link_timeline');
}
export function AnnualReportModal () {
return import(/*webpackChunkName: "flavours/glitch/async/modals/annual_report_modal" */'../components/annual_report_modal');
return import('../components/annual_report_modal');
}
export function ListEdit () {
return import(/*webpackChunkName: "flavours/glitch/async/lists" */'../../lists/new');
return import('../../lists/new');
}
export function ListMembers () {
return import(/* webpackChunkName: "flavours/glitch/async/lists" */'../../lists/members');
return import('../../lists/members');
}

View File

@@ -3,7 +3,7 @@
// can at least log in using KaiOS devices).
function importArrowKeyNavigation() {
return import(/* webpackChunkName: "arrow-key-navigation" */ 'arrow-key-navigation');
return import('arrow-key-navigation');
}
export default function loadKeyboardExtensions() {

View File

@@ -5,6 +5,13 @@ import { isLocaleLoaded, setLocale } from './global_locale';
const localeLoadingSemaphore = new Semaphore(1);
const upstreamLocaleFiles = import.meta.glob<{ default: LocaleData['messages'] }>([
'@/mastodon/locales/*.json',
]);
const localeFiles = import.meta.glob<{ default: LocaleData['messages'] }>([
'./*.json',
]);
export async function loadLocale() {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en';
@@ -17,21 +24,23 @@ export async function loadLocale() {
// if the locale is already set, then do nothing
if (isLocaleLoaded()) return;
const upstreamLocaleData = await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "locales/vanilla/[request]" */
/* webpackInclude: /\.json$/ */
/* webpackPreload: true */
`mastodon/locales/${locale}.json`
) as LocaleData['messages'];
// If there is no locale file, then fallback to english
const upstreamLocaleFile = Object.hasOwn(upstreamLocaleFiles, `@/mastodon/locales/${locale}.json`)
? upstreamLocaleFiles[`/mastodon/locales/${locale}.json`]
: upstreamLocaleFiles['/mastodon/locales/en.json'];
const localeData = await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "locales/glitch/[request]" */
/* webpackInclude: /\.json$/ */
/* webpackPreload: true */
`flavours/glitch/locales/${locale}.json`
) as LocaleData['messages'];
if (!upstreamLocaleFile) throw new Error('Could not load the upstream locale JSON file');
const { default: upstreamLocaleData } = await upstreamLocaleFile();
// If there is no locale file, then fallback to english
const localeFile = Object.hasOwn(localeFiles, `./${locale}.json`)
? localeFiles[`./${locale}.json`]
: localeFiles['./en.json'];
if (!localeFile) throw new Error('Could not load the locale JSON file');
const { default: localeData } = await localeFile();
setLocale({ messages: { ...upstreamLocaleData, ...localeData }, locale });
});

View File

@@ -7,17 +7,19 @@ import * as perf from 'flavours/glitch/performance';
import ready from 'flavours/glitch/ready';
import { store } from 'flavours/glitch/store';
import { isProduction } from './utils/environment';
import { isProduction, isDevelopment } from './utils/environment';
/**
* @returns {Promise<void>}
*/
function main() {
perf.start('main()');
return ready(async () => {
const mountNode = document.getElementById('mastodon');
const props = JSON.parse(mountNode.getAttribute('data-props'));
if (!mountNode) {
throw new Error('Mount node not found');
}
const props = JSON.parse(
mountNode.getAttribute('data-props') ?? '{}',
) as Record<string, unknown>;
const root = createRoot(mountNode);
root.render(<Mastodon {...props} />);
@@ -25,8 +27,10 @@ function main() {
if (isProduction() && me && 'serviceWorker' in navigator) {
const { Workbox } = await import('workbox-window');
const wb = new Workbox('/sw.js');
/** @type {ServiceWorkerRegistration} */
const wb = new Workbox(
isDevelopment() ? '/packs-dev/dev-sw.js?dev-sw' : '/sw.js',
{ type: 'module', scope: '/' },
);
let registration;
try {
@@ -35,8 +39,14 @@ function main() {
console.error(err);
}
if (registration && 'Notification' in window && Notification.permission === 'granted') {
const registerPushNotifications = await import('flavours/glitch/actions/push_notifications');
if (
registration &&
'Notification' in window &&
Notification.permission === 'granted'
) {
const registerPushNotifications = await import(
'flavours/glitch/actions/push_notifications'
);
store.dispatch(registerPushNotifications.register());
}
@@ -46,4 +56,5 @@ function main() {
});
}
// eslint-disable-next-line import/no-default-export
export default main;

View File

@@ -1,24 +1,11 @@
//
// Tools for performance debugging, only enabled in development mode.
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output.
// Also see config/webpack/loaders/mark.js for the webpack loader marks.
import * as marky from 'marky';
import { isDevelopment } from './utils/environment';
if (isDevelopment()) {
if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) {
// Increase Firefox's performance entry limit; otherwise it's capped to 150.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
performance.setResourceTimingBufferSize(Infinity);
}
// allows us to easily do e.g. ReactPerf.printWasted() while debugging
//window.ReactPerf = require('react-addons-perf');
//window.ReactPerf.start();
}
export function start(name) {
if (isDevelopment()) {
marky.mark(name);

View File

@@ -2,10 +2,13 @@
// If there are no polyfills, then this is just Promise.resolve() which means
// it will execute in the same tick of the event loop (i.e. near-instant).
// eslint-disable-next-line import/extensions -- This file is virtual so it thinks it has an extension
import 'vite/modulepreload-polyfill';
import { loadIntlPolyfills } from './intl';
function importExtraPolyfills() {
return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills');
return import('./extra_polyfills');
}
export function loadPolyfills() {

View File

@@ -54,11 +54,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
return;
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-pluralrules/polyfill-force');
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force'
);
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`
`../../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
);
}
@@ -70,11 +68,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
// }
// // Load the polyfill 1st BEFORE loading data
// await import(
// /* webpackChunkName: "i18n-relativetimeformat-polyfill" */
// '@formatjs/intl-relativetimeformat/polyfill-force'
// );
// await import(
// /* webpackChunkName: "i18n-relativetimeformat-polyfill-[request]" */
// `@formatjs/intl-relativetimeformat/locale-data/${unsupportedLocale}`
// );
// }

View File

@@ -1451,8 +1451,8 @@ a.sparkline {
inset-inline-start: 10px;
border-radius: 4px;
background:
url('~images/warning-stripes.svg') repeat-y left,
url('~images/warning-stripes.svg') repeat-y right,
url('@/images/warning-stripes.svg') repeat-y left,
url('@/images/warning-stripes.svg') repeat-y right,
var(--background-color);
}

View File

@@ -2,8 +2,8 @@
@use 'mixins';
@use 'variables';
@use 'css_variables';
@use 'styles/fonts/roboto';
@use 'styles/fonts/roboto-mono';
@use '@/styles/fonts/roboto';
@use '@/styles/fonts/roboto-mono';
@use 'reset';
@use 'basics';

View File

@@ -641,7 +641,7 @@ body > [data-popper-placement] {
align-items: stretch;
&__border {
background: url('~images/warning-stripes.svg') repeat-y;
background: url('@/images/warning-stripes.svg') repeat-y;
width: 5px;
flex: 0 0 auto;

View File

@@ -394,7 +394,7 @@ code {
max-width: 100%;
height: auto;
border-radius: var(--avatar-border-radius);
background: url('images/void.png');
background: url('@/images/void.png');
&[src$='missing.png'] {
visibility: hidden;

View File

@@ -44,7 +44,7 @@
content: '';
width: 24px;
height: 20px;
mask-image: url('~images/quote.svg');
mask-image: url('@/images/quote.svg');
background-color: var(--rich-text-decorations-color);
position: absolute;
inset-inline-start: 0;

View File

@@ -1,7 +1,11 @@
export function isDevelopment() {
return process.env.NODE_ENV === 'development';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'development';
else return import.meta.env.DEV;
}
export function isProduction() {
return process.env.NODE_ENV === 'production';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'production';
else return import.meta.env.PROD;
}

View File

@@ -1,8 +1,6 @@
import Rails from '@rails/ujs';
export function start() {
require.context('../images/', true, /\.(jpg|png|svg)$/);
try {
Rails.start();
} catch {

View File

@@ -9,7 +9,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg';
import BookmarkIcon from '@/material-icons/400-24px/bookmark-fill.svg?react';
import BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react';

View File

@@ -15,10 +15,6 @@ import type { List as ImmutableList, Map as ImmutableMap } from 'immutable';
import { useSpring, animated } from '@react-spring/web';
import Textarea from 'react-textarea-autosize';
import { length } from 'stringz';
// eslint-disable-next-line import/extensions
import tesseractWorkerPath from 'tesseract.js/dist/worker.min.js';
// eslint-disable-next-line import/no-extraneous-dependencies
import tesseractCorePath from 'tesseract.js-core/tesseract-core.wasm.js';
import { showAlertForError } from 'mastodon/actions/alerts';
import { uploadThumbnail } from 'mastodon/actions/compose';
@@ -350,9 +346,15 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
fetchTesseract()
.then(async ({ createWorker }) => {
const [tesseractWorkerPath, tesseractCorePath] = await Promise.all([
// eslint-disable-next-line import/extensions
import('tesseract.js/dist/worker.min.js?url'),
// eslint-disable-next-line import/no-extraneous-dependencies
import('tesseract.js-core/tesseract-core.wasm.js?url'),
]);
const worker = await createWorker('eng', 1, {
workerPath: tesseractWorkerPath as string,
corePath: tesseractCorePath as string,
workerPath: tesseractWorkerPath.default,
corePath: tesseractCorePath.default,
langPath: `${assetHost}/ocr/lang-data`,
cacheMethod: 'write',
});
@@ -501,5 +503,4 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
);
},
);
AltTextModal.displayName = 'AltTextModal';

View File

@@ -9,28 +9,27 @@
// to ensure that the prevaled file is regenerated by Babel
// version: 4
const { NimbleEmojiIndex } = require('emoji-mart');
const { uncompress: emojiMartUncompress } = require('emoji-mart/dist/utils/data');
import { NimbleEmojiIndex } from 'emoji-mart';
import { uncompress as emojiMartUncompress } from 'emoji-mart/dist/utils/data';
let data = require('./emoji_data.json');
const emojiMap = require('./emoji_map.json');
const { unicodeToFilename } = require('./unicode_to_filename');
const { unicodeToUnifiedName } = require('./unicode_to_unified_name');
import data from './emoji_data.json';
import emojiMap from './emoji_map.json';
import { unicodeToFilename } from './unicode_to_filename';
import { unicodeToUnifiedName } from './unicode_to_unified_name';
emojiMartUncompress(data);
const emojiMartData = data;
const emojiIndex = new NimbleEmojiIndex(emojiMartData);
const excluded = ['®', '©', '™'];
const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿'];
const shortcodeMap = {};
const excluded = ['®', '©', '™'];
const skinTones = ['🏻', '🏼', '🏽', '🏾', '🏿'];
const shortcodeMap = {};
const shortCodesToEmojiData = {};
const emojisWithoutShortCodes = [];
Object.keys(emojiIndex.emojis).forEach(key => {
Object.keys(emojiIndex.emojis).forEach((key) => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
@@ -41,22 +40,22 @@ Object.keys(emojiIndex.emojis).forEach(key => {
shortcodeMap[emoji.native] = emoji.id;
});
const stripModifiers = unicode => {
skinTones.forEach(tone => {
const stripModifiers = (unicode) => {
skinTones.forEach((tone) => {
unicode = unicode.replace(tone, '');
});
return unicode;
};
Object.keys(emojiMap).forEach(key => {
Object.keys(emojiMap).forEach((key) => {
if (excluded.includes(key)) {
delete emojiMap[key];
return;
}
const normalizedKey = stripModifiers(key);
let shortcode = shortcodeMap[normalizedKey];
let shortcode = shortcodeMap[normalizedKey];
if (!shortcode) {
shortcode = shortcodeMap[normalizedKey + '\uFE0F'];
@@ -82,7 +81,7 @@ Object.keys(emojiMap).forEach(key => {
}
});
Object.keys(emojiIndex.emojis).forEach(key => {
Object.keys(emojiIndex.emojis).forEach((key) => {
let emoji = emojiIndex.emojis[key];
// Emojis with skin tone modifiers are stored like this
@@ -94,9 +93,11 @@ Object.keys(emojiIndex.emojis).forEach(key => {
let { short_names, search, unified } = emojiMartData.emojis[key];
if (short_names[0] !== key) {
throw new Error('The compressor expects the first short_code to be the ' +
'key. It may need to be rewritten if the emoji change such that this ' +
'is no longer the case.');
throw new Error(
'The compressor expects the first short_code to be the ' +
'key. It may need to be rewritten if the emoji change such that this ' +
'is no longer the case.',
);
}
short_names = short_names.slice(1); // first short name can be inferred from the key
@@ -117,20 +118,22 @@ Object.keys(emojiIndex.emojis).forEach(key => {
// JSON.parse/stringify is to emulate what @preval is doing and avoid any
// inconsistent behavior in dev mode
module.exports = JSON.parse(JSON.stringify([
shortCodesToEmojiData,
/*
* The property `skins` is not found in the current context.
* This could potentially lead to issues when interacting with modules or data structures
* that expect the presence of `skins` property.
* Currently, no definitions or references to `skins` property can be found in:
* - {@link node_modules/emoji-mart/dist/utils/data.js}
* - {@link node_modules/emoji-mart/data/all.json}
* - {@link app/javascript/flavours/glitch/features/emoji/emoji_compressed.d.ts#Skins}
* Future refactorings or updates should consider adding definitions or handling for `skins` property.
*/
emojiMartData.skins,
emojiMartData.categories,
emojiMartData.aliases,
emojisWithoutShortCodes
]));
export default JSON.parse(
JSON.stringify([
shortCodesToEmojiData,
/*
* The property `skins` is not found in the current context.
* This could potentially lead to issues when interacting with modules or data structures
* that expect the presence of `skins` property.
* Currently, no definitions or references to `skins` property can be found in:
* - {@link node_modules/emoji-mart/dist/utils/data.js}
* - {@link node_modules/emoji-mart/data/all.json}
* - {@link app/javascript/mastodon/features/emoji/emoji_compressed.d.ts#Skins}
* Future refactorings or updates should consider adding definitions or handling for `skins` property.
*/
emojiMartData.skins,
emojiMartData.categories,
emojiMartData.aliases,
emojisWithoutShortCodes,
]),
);

View File

@@ -3,9 +3,13 @@
// emojiIndex.search functionality.
import type { BaseEmoji } from 'emoji-mart';
import type { Emoji } from 'emoji-mart/dist-es/utils/data';
// eslint-disable-next-line import/no-unresolved
import emojiCompressed from 'virtual:mastodon-emoji-compressed';
import type {
Search,
ShortCodesToEmojiData,
} from 'virtual:mastodon-emoji-compressed';
import type { Search, ShortCodesToEmojiData } from './emoji_compressed';
import emojiCompressed from './emoji_compressed';
import { unicodeToUnifiedName } from './unicode_to_unified_name';
type Emojis = Record<

View File

@@ -2,11 +2,13 @@
// (i.e. the svg filename) and a shortCode intended to be shown
// as a "title" attribute in an HTML element (aka tooltip).
// eslint-disable-next-line import/no-unresolved
import emojiCompressed from 'virtual:mastodon-emoji-compressed';
import type {
FilenameData,
ShortCodesToEmojiDataKey,
} from './emoji_compressed';
import emojiCompressed from './emoji_compressed';
} from 'virtual:mastodon-emoji-compressed';
import { unicodeToFilename } from './unicode_to_filename';
type UnicodeMapping = Record<

View File

@@ -1,6 +1,6 @@
// taken from:
// https://github.com/twitter/twemoji/blob/47732c7/twemoji-generator.js#L848-L866
exports.unicodeToFilename = (str) => {
export const unicodeToFilename = (str) => {
let result = '';
let charCode = 0;
let p = 0;

View File

@@ -6,7 +6,7 @@ function padLeft(str, num) {
return str;
}
exports.unicodeToUnifiedName = (str) => {
export const unicodeToUnifiedName = (str) => {
let output = '';
for (let i = 0; i < str.length; i += 2) {

View File

@@ -1,235 +1,235 @@
export function EmojiPicker () {
return import(/* webpackChunkName: "emoji_picker" */'../../emoji/emoji_picker');
return import('../../emoji/emoji_picker');
}
export function Compose () {
return import(/* webpackChunkName: "features/compose" */'../../compose');
return import('../../compose');
}
export function Notifications () {
return import(/* webpackChunkName: "features/notifications" */'../../notifications_v2');
return import('../../notifications_v2');
}
export function HomeTimeline () {
return import(/* webpackChunkName: "features/home_timeline" */'../../home_timeline');
return import('../../home_timeline');
}
export function PublicTimeline () {
return import(/* webpackChunkName: "features/public_timeline" */'../../public_timeline');
return import('../../public_timeline');
}
export function CommunityTimeline () {
return import(/* webpackChunkName: "features/community_timeline" */'../../community_timeline');
return import('../../community_timeline');
}
export function Firehose () {
return import(/* webpackChunkName: "features/firehose" */'../../firehose');
return import('../../firehose');
}
export function HashtagTimeline () {
return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline');
return import('../../hashtag_timeline');
}
export function DirectTimeline() {
return import(/* webpackChunkName: "features/direct_timeline" */'../../direct_timeline');
return import('../../direct_timeline');
}
export function ListTimeline () {
return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline');
return import('../../list_timeline');
}
export function Lists () {
return import(/* webpackChunkName: "features/lists" */'../../lists');
return import('../../lists');
}
export function Status () {
return import(/* webpackChunkName: "features/status" */'../../status');
return import('../../status');
}
export function GettingStarted () {
return import(/* webpackChunkName: "features/getting_started" */'../../getting_started');
return import('../../getting_started');
}
export function KeyboardShortcuts () {
return import(/* webpackChunkName: "features/keyboard_shortcuts" */'../../keyboard_shortcuts');
return import('../../keyboard_shortcuts');
}
export function PinnedStatuses () {
return import(/* webpackChunkName: "features/pinned_statuses" */'../../pinned_statuses');
return import('../../pinned_statuses');
}
export function AccountTimeline () {
return import(/* webpackChunkName: "features/account_timeline" */'../../account_timeline');
return import('../../account_timeline');
}
export function AccountGallery () {
return import(/* webpackChunkName: "features/account_gallery" */'../../account_gallery');
return import('../../account_gallery');
}
export function AccountFeatured() {
return import(/* webpackChunkName: "features/account_featured" */'../../account_featured');
return import('../../account_featured');
}
export function Followers () {
return import(/* webpackChunkName: "features/followers" */'../../followers');
return import('../../followers');
}
export function Following () {
return import(/* webpackChunkName: "features/following" */'../../following');
return import('../../following');
}
export function Reblogs () {
return import(/* webpackChunkName: "features/reblogs" */'../../reblogs');
return import('../../reblogs');
}
export function Favourites () {
return import(/* webpackChunkName: "features/favourites" */'../../favourites');
return import('../../favourites');
}
export function FollowRequests () {
return import(/* webpackChunkName: "features/follow_requests" */'../../follow_requests');
return import('../../follow_requests');
}
export function FavouritedStatuses () {
return import(/* webpackChunkName: "features/favourited_statuses" */'../../favourited_statuses');
return import('../../favourited_statuses');
}
export function FollowedTags () {
return import(/* webpackChunkName: "features/followed_tags" */'../../followed_tags');
return import('../../followed_tags');
}
export function BookmarkedStatuses () {
return import(/* webpackChunkName: "features/bookmarked_statuses" */'../../bookmarked_statuses');
return import('../../bookmarked_statuses');
}
export function Blocks () {
return import(/* webpackChunkName: "features/blocks" */'../../blocks');
return import('../../blocks');
}
export function DomainBlocks () {
return import(/* webpackChunkName: "features/domain_blocks" */'../../domain_blocks');
return import('../../domain_blocks');
}
export function Mutes () {
return import(/* webpackChunkName: "features/mutes" */'../../mutes');
return import('../../mutes');
}
export function MuteModal () {
return import(/* webpackChunkName: "modals/mute_modal" */'../components/mute_modal');
return import('../components/mute_modal');
}
export function BlockModal () {
return import(/* webpackChunkName: "modals/block_modal" */'../components/block_modal');
return import('../components/block_modal');
}
export function DomainBlockModal () {
return import(/* webpackChunkName: "modals/domain_block_modal" */'../components/domain_block_modal');
return import('../components/domain_block_modal');
}
export function ReportModal () {
return import(/* webpackChunkName: "modals/report_modal" */'../components/report_modal');
return import('../components/report_modal');
}
export function IgnoreNotificationsModal () {
return import(/* webpackChunkName: "modals/domain_block_modal" */'../components/ignore_notifications_modal');
return import('../components/ignore_notifications_modal');
}
export function MediaGallery () {
return import(/* webpackChunkName: "status/media_gallery" */'../../../components/media_gallery');
return import('../../../components/media_gallery');
}
export function Video () {
return import(/* webpackChunkName: "features/video" */'../../video');
return import('../../video');
}
export function EmbedModal () {
return import(/* webpackChunkName: "modals/embed_modal" */'../components/embed_modal');
return import('../components/embed_modal');
}
export function ListAdder () {
return import(/*webpackChunkName: "features/list_adder" */'../../list_adder');
return import('../../list_adder');
}
export function Tesseract () {
return import(/*webpackChunkName: "tesseract" */'tesseract.js');
return import('tesseract.js');
}
export function Audio () {
return import(/* webpackChunkName: "features/audio" */'../../audio');
return import('../../audio');
}
export function Directory () {
return import(/* webpackChunkName: "features/directory" */'../../directory');
return import('../../directory');
}
export function OnboardingProfile () {
return import(/* webpackChunkName: "features/onboarding" */'../../onboarding/profile');
return import('../../onboarding/profile');
}
export function OnboardingFollows () {
return import(/* webpackChunkName: "features/onboarding" */'../../onboarding/follows');
return import('../../onboarding/follows');
}
export function CompareHistoryModal () {
return import(/*webpackChunkName: "modals/compare_history_modal" */'../components/compare_history_modal');
return import('../components/compare_history_modal');
}
export function Explore () {
return import(/* webpackChunkName: "features/explore" */'../../explore');
return import('../../explore');
}
export function Search () {
return import(/* webpackChunkName: "features/explore" */'../../search');
return import('../../search');
}
export function FilterModal () {
return import(/*webpackChunkName: "modals/filter_modal" */'../components/filter_modal');
return import('../components/filter_modal');
}
export function InteractionModal () {
return import(/*webpackChunkName: "modals/interaction_modal" */'../../interaction_modal');
return import('../../interaction_modal');
}
export function SubscribedLanguagesModal () {
return import(/*webpackChunkName: "modals/subscribed_languages_modal" */'../../subscribed_languages_modal');
return import('../../subscribed_languages_modal');
}
export function ClosedRegistrationsModal () {
return import(/*webpackChunkName: "modals/closed_registrations_modal" */'../../closed_registrations_modal');
return import('../../closed_registrations_modal');
}
export function About () {
return import(/*webpackChunkName: "features/about" */'../../about');
return import('../../about');
}
export function PrivacyPolicy () {
return import(/*webpackChunkName: "features/privacy_policy" */'../../privacy_policy');
return import('../../privacy_policy');
}
export function TermsOfService () {
return import(/*webpackChunkName: "features/terms_of_service" */'../../terms_of_service');
return import('../../terms_of_service');
}
export function NotificationRequests () {
return import(/*webpackChunkName: "features/notifications/requests" */'../../notifications/requests');
return import('../../notifications/requests');
}
export function NotificationRequest () {
return import(/*webpackChunkName: "features/notifications/request" */'../../notifications/request');
return import('../../notifications/request');
}
export function LinkTimeline () {
return import(/*webpackChunkName: "features/link_timeline" */'../../link_timeline');
return import('../../link_timeline');
}
export function AnnualReportModal () {
return import(/*webpackChunkName: "modals/annual_report_modal" */'../components/annual_report_modal');
return import('../components/annual_report_modal');
}
export function ListEdit () {
return import(/*webpackChunkName: "features/lists" */'../../lists/new');
return import('../../lists/new');
}
export function ListMembers () {
return import(/* webpackChunkName: "features/lists" */'../../lists/members');
return import('../../lists/members');
}

View File

@@ -3,7 +3,7 @@
// can at least log in using KaiOS devices).
function importArrowKeyNavigation() {
return import(/* webpackChunkName: "arrow-key-navigation" */ 'arrow-key-navigation');
return import('arrow-key-navigation');
}
export default function loadKeyboardExtensions() {

View File

@@ -5,6 +5,10 @@ import { isLocaleLoaded, setLocale } from './global_locale';
const localeLoadingSemaphore = new Semaphore(1);
const localeFiles = import.meta.glob<{ default: LocaleData['messages'] }>([
'./*.json',
]);
export async function loadLocale() {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en';
@@ -17,13 +21,14 @@ export async function loadLocale() {
// if the locale is already set, then do nothing
if (isLocaleLoaded()) return;
const localeData = (await import(
/* webpackMode: "lazy" */
/* webpackChunkName: "locales/vanilla/[request]" */
/* webpackInclude: /\.json$/ */
/* webpackPreload: true */
`mastodon/locales/${locale}.json`
)) as LocaleData['messages'];
// If there is no locale file, then fallback to english
const localeFile = Object.hasOwn(localeFiles, `./${locale}.json`)
? localeFiles[`./${locale}.json`]
: localeFiles['./en.json'];
if (!localeFile) throw new Error('Could not load the locale JSON file');
const { default: localeData } = await localeFile();
setLocale({ messages: localeData, locale });
});

View File

@@ -7,17 +7,19 @@ import * as perf from 'mastodon/performance';
import ready from 'mastodon/ready';
import { store } from 'mastodon/store';
import { isProduction } from './utils/environment';
import { isProduction, isDevelopment } from './utils/environment';
/**
* @returns {Promise<void>}
*/
function main() {
perf.start('main()');
return ready(async () => {
const mountNode = document.getElementById('mastodon');
const props = JSON.parse(mountNode.getAttribute('data-props'));
if (!mountNode) {
throw new Error('Mount node not found');
}
const props = JSON.parse(
mountNode.getAttribute('data-props') ?? '{}',
) as Record<string, unknown>;
const root = createRoot(mountNode);
root.render(<Mastodon {...props} />);
@@ -25,8 +27,10 @@ function main() {
if (isProduction() && me && 'serviceWorker' in navigator) {
const { Workbox } = await import('workbox-window');
const wb = new Workbox('/sw.js');
/** @type {ServiceWorkerRegistration} */
const wb = new Workbox(
isDevelopment() ? '/packs-dev/dev-sw.js?dev-sw' : '/sw.js',
{ type: 'module', scope: '/' },
);
let registration;
try {
@@ -35,8 +39,14 @@ function main() {
console.error(err);
}
if (registration && 'Notification' in window && Notification.permission === 'granted') {
const registerPushNotifications = await import('mastodon/actions/push_notifications');
if (
registration &&
'Notification' in window &&
Notification.permission === 'granted'
) {
const registerPushNotifications = await import(
'mastodon/actions/push_notifications'
);
store.dispatch(registerPushNotifications.register());
}
@@ -46,4 +56,5 @@ function main() {
});
}
// eslint-disable-next-line import/no-default-export
export default main;

View File

@@ -1,24 +1,11 @@
//
// Tools for performance debugging, only enabled in development mode.
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output.
// Also see config/webpack/loaders/mark.js for the webpack loader marks.
import * as marky from 'marky';
import { isDevelopment } from './utils/environment';
if (isDevelopment()) {
if (typeof performance !== 'undefined' && performance.setResourceTimingBufferSize) {
// Increase Firefox's performance entry limit; otherwise it's capped to 150.
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1331135
performance.setResourceTimingBufferSize(Infinity);
}
// allows us to easily do e.g. ReactPerf.printWasted() while debugging
//window.ReactPerf = require('react-addons-perf');
//window.ReactPerf.start();
}
export function start(name) {
if (isDevelopment()) {
marky.mark(name);

View File

@@ -2,10 +2,13 @@
// If there are no polyfills, then this is just Promise.resolve() which means
// it will execute in the same tick of the event loop (i.e. near-instant).
// eslint-disable-next-line import/extensions -- This file is virtual so it thinks it has an extension
import 'vite/modulepreload-polyfill';
import { loadIntlPolyfills } from './intl';
function importExtraPolyfills() {
return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills');
return import('./extra_polyfills');
}
export function loadPolyfills() {

View File

@@ -54,11 +54,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
return;
}
// Load the polyfill 1st BEFORE loading data
await import('@formatjs/intl-pluralrules/polyfill-force');
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill" */ '@formatjs/intl-pluralrules/polyfill-force'
);
await import(
/* webpackChunkName: "i18n-pluralrules-polyfill-[request]" */ `@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}`
`../../../../node_modules/@formatjs/intl-pluralrules/locale-data/${unsupportedLocale}.js`
);
}
@@ -70,11 +68,9 @@ async function loadIntlPluralRulesPolyfills(locale: string) {
// }
// // Load the polyfill 1st BEFORE loading data
// await import(
// /* webpackChunkName: "i18n-relativetimeformat-polyfill" */
// '@formatjs/intl-relativetimeformat/polyfill-force'
// );
// await import(
// /* webpackChunkName: "i18n-relativetimeformat-polyfill-[request]" */
// `@formatjs/intl-relativetimeformat/locale-data/${unsupportedLocale}`
// );
// }

View File

@@ -1,5 +1,4 @@
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
@@ -15,10 +14,9 @@ function fetchRoot() {
return fetch('/', { credentials: 'include', redirect: 'manual' });
}
precacheAndRoute(self.__WB_MANIFEST);
registerRoute(
/locale_.*\.js$/,
/intl\/.*\.js$/,
new CacheFirst({
cacheName: `${CACHE_NAME_PREFIX}locales`,
plugins: [

View File

@@ -1,41 +0,0 @@
/* @preval */
const fs = require('fs');
const path = require('path');
const { defineMessages } = require('react-intl');
const messages = defineMessages({
mentioned_you: { id: 'notification.mentioned_you', defaultMessage: '{name} mentioned you' },
});
const filtered = {};
const filenames = fs.readdirSync(path.resolve(__dirname, '../locales'));
filenames.forEach(filename => {
if (!filename.match(/\.json$/)) return;
const content = fs.readFileSync(path.resolve(__dirname, `../locales/${filename}`), 'utf-8');
const full = JSON.parse(content);
const locale = filename.split('.')[0];
filtered[locale] = {
'notification.favourite': full['notification.favourite'] || '',
'notification.follow': full['notification.follow'] || '',
'notification.follow_request': full['notification.follow_request'] || '',
'notification.mention': full[messages.mentioned_you.id] || '',
'notification.reblog': full['notification.reblog'] || '',
'notification.poll': full['notification.poll'] || '',
'notification.status': full['notification.status'] || '',
'notification.update': full['notification.update'] || '',
'notification.admin.sign_up': full['notification.admin.sign_up'] || '',
'status.show_more': full['status.show_more'] || '',
'status.reblog': full['status.reblog'] || '',
'status.favourite': full['status.favourite'] || '',
'notifications.group': full['notifications.group'] || '',
};
});
module.exports = JSON.parse(JSON.stringify(filtered));

View File

@@ -1,8 +1,10 @@
import { IntlMessageFormat } from 'intl-messageformat';
import { unescape } from 'lodash';
import locales from './web_push_locales';
// see config/vite/plugins/sw-locales
// it needs to be updated when new locale keys are used in this file
// eslint-disable-next-line import/no-unresolved
import locales from "virtual:mastodon-sw-locales";
const MAX_NOTIFICATIONS = 5;
const GROUP_TAG = 'tag';

View File

@@ -1,7 +1,11 @@
export function isDevelopment() {
return process.env.NODE_ENV === 'development';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'development';
else return import.meta.env.DEV;
}
export function isProduction() {
return process.env.NODE_ENV === 'production';
if (typeof process !== 'undefined')
return process.env.NODE_ENV === 'production';
else return import.meta.env.PROD;
}

View File

@@ -0,0 +1 @@
@use '@/flavours/glitch/styles/application';

View File

@@ -0,0 +1 @@
@use '@/flavours/glitch/styles/contrast';

View File

@@ -1 +0,0 @@
@use 'flavours/glitch/styles/contrast';

View File

@@ -1 +0,0 @@
@use 'flavours/glitch/styles/application';

View File

@@ -0,0 +1 @@
@use '@/flavours/glitch/styles/mastodon-light';

View File

@@ -1 +0,0 @@
@use 'flavours/glitch/styles/mastodon-light';

View File

@@ -0,0 +1 @@
@use '@/styles/application';

View File

@@ -0,0 +1 @@
@use '@/styles/contrast';

View File

@@ -1 +0,0 @@
@use 'styles/contrast';

View File

@@ -1 +0,0 @@
@use 'styles/application';

View File

@@ -0,0 +1 @@
@use '@/styles/mastodon-light';

View File

@@ -1 +0,0 @@
@use 'styles/mastodon-light';

View File

@@ -0,0 +1 @@
@use '@/styles/win95';

View File

@@ -1 +0,0 @@
@use 'styles/win95';

View File

@@ -1,4 +1,4 @@
@use 'fonts/inter';
@use '../fonts/inter';
body {
accent-color: #6364ff;
@@ -259,7 +259,7 @@ table + p {
.email-header-td {
padding: 16px 32px;
background-color: #1b001f;
background-image: url('../images/mailer-new/common/header-bg-start.png');
background-image: url('../../images/mailer-new/common/header-bg-start.png');
background-position: left top;
background-repeat: repeat;
}
@@ -426,7 +426,7 @@ table + p {
// Body content
.email-body-td {
background-image: url('../images/mailer-new/common/header-bg-end.png');
background-image: url('../../images/mailer-new/common/header-bg-end.png');
background-position: left top;
background-repeat: no-repeat;
}
@@ -922,7 +922,7 @@ table + p {
// Extra content on light purple background
.email-extra-wave {
height: 42px;
background-image: url('../images/mailer-new/welcome/purple-extra-soft-wave.png');
background-image: url('../../images/mailer-new/welcome/purple-extra-soft-wave.png');
background-position: bottom center;
background-repeat: no-repeat;
}
@@ -930,7 +930,7 @@ table + p {
.email-extra-td {
padding: 32px 32px 24px;
background-color: #f0f0ff;
background-image: url('../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes
background-image: url('../../images/mailer-new/welcome/purple-extra-soft-spacer.png'); // Using an image to maintain the color even in forced dark modes
.email-column-td {
padding-top: 8px;

View File

@@ -1,6 +1,6 @@
@font-face {
font-family: Inter;
src: url('../fonts/inter/inter-variable-font-slnt-wght.woff2')
src: url('../../fonts/inter/inter-variable-font-slnt-wght.woff2')
format('woff2-variations');
font-weight: 100 900;
font-style: normal;

View File

@@ -2,10 +2,10 @@
font-family: mastodon-font-monospace;
src:
local('Roboto Mono'),
url('~fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
url('~fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'),
url('~fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
url('~fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular')
url('@/fonts/roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
url('@/fonts/roboto-mono/robotomono-regular-webfont.woff') format('woff'),
url('@/fonts/roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
url('@/fonts/roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular')
format('svg');
font-weight: 400;
font-display: swap;

View File

@@ -2,10 +2,10 @@
font-family: mastodon-font-sans-serif;
src:
local('Roboto Italic'),
url('~fonts/roboto/roboto-italic-webfont.woff2') format('woff2'),
url('~fonts/roboto/roboto-italic-webfont.woff') format('woff'),
url('~fonts/roboto/roboto-italic-webfont.ttf') format('truetype'),
url('~fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont')
url('@/fonts/roboto/roboto-italic-webfont.woff2') format('woff2'),
url('@/fonts/roboto/roboto-italic-webfont.woff') format('woff'),
url('@/fonts/roboto/roboto-italic-webfont.ttf') format('truetype'),
url('@/fonts/roboto/roboto-italic-webfont.svg#roboto-italic-webfont')
format('svg');
font-weight: normal;
font-display: swap;
@@ -16,10 +16,10 @@
font-family: mastodon-font-sans-serif;
src:
local('Roboto Bold'),
url('~fonts/roboto/roboto-bold-webfont.woff2') format('woff2'),
url('~fonts/roboto/roboto-bold-webfont.woff') format('woff'),
url('~fonts/roboto/roboto-bold-webfont.ttf') format('truetype'),
url('~fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont')
url('@/fonts/roboto/roboto-bold-webfont.woff2') format('woff2'),
url('@/fonts/roboto/roboto-bold-webfont.woff') format('woff'),
url('@/fonts/roboto/roboto-bold-webfont.ttf') format('truetype'),
url('@/fonts/roboto/roboto-bold-webfont.svg#roboto-bold-webfont')
format('svg');
font-weight: bold;
font-display: swap;
@@ -30,10 +30,10 @@
font-family: mastodon-font-sans-serif;
src:
local('Roboto Medium'),
url('~fonts/roboto/roboto-medium-webfont.woff2') format('woff2'),
url('~fonts/roboto/roboto-medium-webfont.woff') format('woff'),
url('~fonts/roboto/roboto-medium-webfont.ttf') format('truetype'),
url('~fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont')
url('@/fonts/roboto/roboto-medium-webfont.woff2') format('woff2'),
url('@/fonts/roboto/roboto-medium-webfont.woff') format('woff'),
url('@/fonts/roboto/roboto-medium-webfont.ttf') format('truetype'),
url('@/fonts/roboto/roboto-medium-webfont.svg#roboto-medium-webfont')
format('svg');
font-weight: 500;
font-display: swap;
@@ -44,10 +44,10 @@
font-family: mastodon-font-sans-serif;
src:
local('Roboto'),
url('~fonts/roboto/roboto-regular-webfont.woff2') format('woff2'),
url('~fonts/roboto/roboto-regular-webfont.woff') format('woff'),
url('~fonts/roboto/roboto-regular-webfont.ttf') format('truetype'),
url('~fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont')
url('@/fonts/roboto/roboto-regular-webfont.woff2') format('woff2'),
url('@/fonts/roboto/roboto-regular-webfont.woff') format('woff'),
url('@/fonts/roboto/roboto-regular-webfont.ttf') format('truetype'),
url('@/fonts/roboto/roboto-regular-webfont.svg#roboto-regular-webfont')
format('svg');
font-weight: normal;
font-display: swap;

View File

@@ -1446,8 +1446,8 @@ a.sparkline {
inset-inline-start: 10px;
border-radius: 4px;
background:
url('~images/warning-stripes.svg') repeat-y left,
url('~images/warning-stripes.svg') repeat-y right,
url('@/images/warning-stripes.svg') repeat-y left,
url('@/images/warning-stripes.svg') repeat-y right,
var(--background-color);
}

View File

@@ -641,7 +641,7 @@ body > [data-popper-placement] {
align-items: stretch;
&__border {
background: url('~images/warning-stripes.svg') repeat-y;
background: url('@/images/warning-stripes.svg') repeat-y;
width: 5px;
flex: 0 0 auto;

Some files were not shown because too many files have changed in this diff Show More