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

View File

@@ -25,23 +25,6 @@
'tesseract.js', // Requires code changes 'tesseract.js', // Requires code changes
'react-hotkeys', // 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 // react-router: Requires manual upgrade
'history', 'history',
'react-router-dom', 'react-router-dom',

View File

@@ -49,7 +49,7 @@ jobs:
public/assets public/assets
public/packs public/packs
public/packs-test public/packs-test
tmp/cache/webpacker tmp/cache/vite
key: ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }} key: ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
restore-keys: | restore-keys: |
${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }} ${{ matrix.mode }}-assets-${{ github.head_ref || github.ref_name }}-${{ github.sha }}
@@ -63,7 +63,7 @@ jobs:
- name: Archive asset artifacts - name: Archive asset artifacts
run: | 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 - uses: actions/upload-artifact@v4
if: matrix.mode == 'test' if: matrix.mode == 'test'

3
.gitignore vendored
View File

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

View File

@@ -18,10 +18,6 @@
!/log/.keep !/log/.keep
/tmp /tmp
/coverage /coverage
/public/system
/public/assets
/public/packs
/public/packs-test
.env .env
.env.production .env.production
.env.development .env.development
@@ -60,6 +56,7 @@ docker-compose.override.yml
/public/packs /public/packs
/public/packs-test /public/packs-test
/public/system /public/system
/public/vite*
# Ignore emoji map file # Ignore emoji map file
/app/javascript/mastodon/features/emoji/emoji_map.json /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 'twitter-text', '~> 3.1.0'
gem 'tzinfo-data', '~> 1.2023' gem 'tzinfo-data', '~> 1.2023'
gem 'webauthn', '~> 3.0' gem 'webauthn', '~> 3.0'
gem 'webpacker', '~> 5.4'
gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913' gem 'webpush', github: 'mastodon/webpush', ref: '9631ac63045cfabddacc69fc06e919b4c13eb913'
gem 'json-ld' gem 'json-ld'
@@ -230,3 +229,5 @@ gem 'rubyzip', '~> 2.3'
gem 'hcaptcha', '~> 7.1' gem 'hcaptcha', '~> 7.1'
gem 'mail', '~> 2.8' gem 'mail', '~> 2.8'
gem 'vite_rails', '~> 3.0.19'

View File

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

View File

@@ -1,4 +1,4 @@
web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb web: env PORT=3000 RAILS_ENV=development bundle exec puma -C config/puma.rb
sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq sidekiq: env PORT=3000 RAILS_ENV=development bundle exec sidekiq
stream: env PORT=4000 yarn workspace @mastodon/streaming start 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 def current_skin
@current_skin ||= begin @current_skin ||= begin
skins = Themes.instance.skins_for(current_flavour) 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
end end

View File

@@ -248,22 +248,8 @@ module ApplicationHelper
end end
# glitch-soc addition to handle the multiple flavors # glitch-soc addition to handle the multiple flavors
def preload_locale_pack def flavoured_vite_typescript_tag(pack_name, **)
supported_locales = Themes.instance.flavour(current_flavour)['locales'] vite_typescript_tag("#{Themes.instance.flavour(current_flavour)['pack_directory'].delete_prefix('app/javascript/')}/#{pack_name}", **)
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 })
end end
private private

View File

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

View File

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

View File

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

View File

@@ -1,11 +1,6 @@
import './public-path'; import { loadLocale } from 'mastodon/locales';
import main from 'mastodon/main'; import main from 'mastodon/main';
import { loadPolyfills } from 'mastodon/polyfills';
import { start } from '../mastodon/common';
import { loadLocale } from '../mastodon/locales';
import { loadPolyfills } from '../mastodon/polyfills';
start();
loadPolyfills() loadPolyfills()
.then(loadLocale) .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 { createRoot } from 'react-dom/client';
import { afterInitialRender } from 'mastodon/hooks/useRenderSignal'; import { afterInitialRender } from 'mastodon/hooks/useRenderSignal';
import { start } from '../mastodon/common';
import { Status } from '../mastodon/features/standalone/status'; import { Status } from '../mastodon/features/standalone/status';
import { loadPolyfills } from '../mastodon/polyfills'; import { loadPolyfills } from '../mastodon/polyfills';
import ready from '../mastodon/ready'; import ready from '../mastodon/ready';
start();
function loaded() { function loaded() {
const mountNode = document.getElementById('mastodon-status'); const mountNode = document.getElementById('mastodon-status');

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,8 +1,6 @@
import Rails from '@rails/ujs'; import Rails from '@rails/ujs';
export function start() { export function start() {
require.context('@/images/', true, /\.(jpg|png|svg)$/);
try { try {
Rails.start(); Rails.start();
} catch { } 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 StarBorderIcon from '@/material-icons/400-24px/star.svg?react';
import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react'; import VisibilityIcon from '@/material-icons/400-24px/visibility.svg?react';
import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react'; import RepeatActiveIcon from '@/svg-icons/repeat_active.svg?react';
import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg'; import RepeatDisabledIcon from '@/svg-icons/repeat_disabled.svg?react';
import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg'; import RepeatPrivateIcon from '@/svg-icons/repeat_private.svg?react';
import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react'; import RepeatPrivateActiveIcon from '@/svg-icons/repeat_private_active.svg?react';
import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context'; import { identityContextPropShape, withIdentity } from 'flavours/glitch/identity_context';
import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions'; 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 { createRoot } from 'react-dom/client';
import Rails from '@rails/ujs'; import Rails from '@rails/ujs';
@@ -273,7 +272,7 @@ async function mountReactComponent(element: Element) {
); );
const { default: Component } = (await import( const { default: Component } = (await import(
`@/flavours/glitch/components/admin/${componentName}` `@/flavours/glitch/components/admin/${componentName}.jsx`
)) as { default: React.ComponentType }; )) as { default: React.ComponentType };
const root = createRoot(element); 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 { loadLocale } from 'flavours/glitch/locales';
import main from 'flavours/glitch/main'; import main from 'flavours/glitch/main';
import { loadPolyfills } from 'flavours/glitch/polyfills'; import { loadPolyfills } from 'flavours/glitch/polyfills';
start();
loadPolyfills() loadPolyfills()
.then(loadLocale) .then(loadLocale)
.then(main) .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 { createRoot } from 'react-dom/client';
import '@/entrypoints/public-path';
import { start } from 'flavours/glitch/common';
import { Status } from 'flavours/glitch/features/standalone/status'; import { Status } from 'flavours/glitch/features/standalone/status';
import { afterInitialRender } from 'flavours/glitch/hooks/useRenderSignal'; import { afterInitialRender } from 'flavours/glitch/hooks/useRenderSignal';
import { loadPolyfills } from 'flavours/glitch/polyfills'; import { loadPolyfills } from 'flavours/glitch/polyfills';
import ready from 'flavours/glitch/ready'; import ready from 'flavours/glitch/ready';
start();
function loaded() { function loaded() {
const mountNode = document.getElementById('mastodon-status'); const mountNode = document.getElementById('mastodon-status');

View File

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

View File

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

View File

@@ -1,4 +1,3 @@
import '@/entrypoints/public-path';
import axios from 'axios'; import axios from 'axios';
import ready from 'flavours/glitch/ready'; 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 { useSpring, animated } from '@react-spring/web';
import Textarea from 'react-textarea-autosize'; import Textarea from 'react-textarea-autosize';
import { length } from 'stringz'; 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 { showAlertForError } from 'flavours/glitch/actions/alerts';
import { uploadThumbnail } from 'flavours/glitch/actions/compose'; import { uploadThumbnail } from 'flavours/glitch/actions/compose';
@@ -350,9 +346,15 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
fetchTesseract() fetchTesseract()
.then(async ({ createWorker }) => { .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, { const worker = await createWorker('eng', 1, {
workerPath: tesseractWorkerPath as string, workerPath: tesseractWorkerPath.default,
corePath: tesseractCorePath as string, corePath: tesseractCorePath.default,
langPath: `${assetHost}/ocr/lang-data`, langPath: `${assetHost}/ocr/lang-data`,
cacheMethod: 'write', cacheMethod: 'write',
}); });
@@ -501,5 +503,4 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
); );
}, },
); );
AltTextModal.displayName = 'AltTextModal'; AltTextModal.displayName = 'AltTextModal';

View File

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

View File

@@ -3,9 +3,13 @@
// emojiIndex.search functionality. // emojiIndex.search functionality.
import type { BaseEmoji } from 'emoji-mart'; import type { BaseEmoji } from 'emoji-mart';
import type { Emoji } from 'emoji-mart/dist-es/utils/data'; 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'; import { unicodeToUnifiedName } from './unicode_to_unified_name';
type Emojis = Record< type Emojis = Record<

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,6 +5,13 @@ import { isLocaleLoaded, setLocale } from './global_locale';
const localeLoadingSemaphore = new Semaphore(1); 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() { export async function loadLocale() {
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing -- we want to match empty strings
const locale = document.querySelector<HTMLElement>('html')?.lang || 'en'; 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 the locale is already set, then do nothing
if (isLocaleLoaded()) return; if (isLocaleLoaded()) return;
const upstreamLocaleData = await import( // If there is no locale file, then fallback to english
/* webpackMode: "lazy" */ const upstreamLocaleFile = Object.hasOwn(upstreamLocaleFiles, `@/mastodon/locales/${locale}.json`)
/* webpackChunkName: "locales/vanilla/[request]" */ ? upstreamLocaleFiles[`/mastodon/locales/${locale}.json`]
/* webpackInclude: /\.json$/ */ : upstreamLocaleFiles['/mastodon/locales/en.json'];
/* webpackPreload: true */
`mastodon/locales/${locale}.json`
) as LocaleData['messages'];
const localeData = await import( if (!upstreamLocaleFile) throw new Error('Could not load the upstream locale JSON file');
/* webpackMode: "lazy" */
/* webpackChunkName: "locales/glitch/[request]" */ const { default: upstreamLocaleData } = await upstreamLocaleFile();
/* webpackInclude: /\.json$/ */
/* webpackPreload: true */ // If there is no locale file, then fallback to english
`flavours/glitch/locales/${locale}.json` const localeFile = Object.hasOwn(localeFiles, `./${locale}.json`)
) as LocaleData['messages']; ? 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 }); 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 ready from 'flavours/glitch/ready';
import { store } from 'flavours/glitch/store'; import { store } from 'flavours/glitch/store';
import { isProduction } from './utils/environment'; import { isProduction, isDevelopment } from './utils/environment';
/**
* @returns {Promise<void>}
*/
function main() { function main() {
perf.start('main()'); perf.start('main()');
return ready(async () => { return ready(async () => {
const mountNode = document.getElementById('mastodon'); 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); const root = createRoot(mountNode);
root.render(<Mastodon {...props} />); root.render(<Mastodon {...props} />);
@@ -25,8 +27,10 @@ function main() {
if (isProduction() && me && 'serviceWorker' in navigator) { if (isProduction() && me && 'serviceWorker' in navigator) {
const { Workbox } = await import('workbox-window'); const { Workbox } = await import('workbox-window');
const wb = new Workbox('/sw.js'); const wb = new Workbox(
/** @type {ServiceWorkerRegistration} */ isDevelopment() ? '/packs-dev/dev-sw.js?dev-sw' : '/sw.js',
{ type: 'module', scope: '/' },
);
let registration; let registration;
try { try {
@@ -35,8 +39,14 @@ function main() {
console.error(err); console.error(err);
} }
if (registration && 'Notification' in window && Notification.permission === 'granted') { if (
const registerPushNotifications = await import('flavours/glitch/actions/push_notifications'); registration &&
'Notification' in window &&
Notification.permission === 'granted'
) {
const registerPushNotifications = await import(
'flavours/glitch/actions/push_notifications'
);
store.dispatch(registerPushNotifications.register()); store.dispatch(registerPushNotifications.register());
} }
@@ -46,4 +56,5 @@ function main() {
}); });
} }
// eslint-disable-next-line import/no-default-export
export default main; export default main;

View File

@@ -1,24 +1,11 @@
// //
// Tools for performance debugging, only enabled in development mode. // Tools for performance debugging, only enabled in development mode.
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output. // 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 * as marky from 'marky';
import { isDevelopment } from './utils/environment'; 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) { export function start(name) {
if (isDevelopment()) { if (isDevelopment()) {
marky.mark(name); marky.mark(name);

View File

@@ -2,10 +2,13 @@
// If there are no polyfills, then this is just Promise.resolve() which means // 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). // 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'; import { loadIntlPolyfills } from './intl';
function importExtraPolyfills() { function importExtraPolyfills() {
return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills'); return import('./extra_polyfills');
} }
export function loadPolyfills() { export function loadPolyfills() {

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,7 +1,11 @@
export function isDevelopment() { 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() { 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'; import Rails from '@rails/ujs';
export function start() { export function start() {
require.context('../images/', true, /\.(jpg|png|svg)$/);
try { try {
Rails.start(); Rails.start();
} catch { } catch {

View File

@@ -9,7 +9,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; 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 BookmarkBorderIcon from '@/material-icons/400-24px/bookmark.svg?react';
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react'; import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
import RepeatIcon from '@/material-icons/400-24px/repeat.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 { useSpring, animated } from '@react-spring/web';
import Textarea from 'react-textarea-autosize'; import Textarea from 'react-textarea-autosize';
import { length } from 'stringz'; 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 { showAlertForError } from 'mastodon/actions/alerts';
import { uploadThumbnail } from 'mastodon/actions/compose'; import { uploadThumbnail } from 'mastodon/actions/compose';
@@ -350,9 +346,15 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
fetchTesseract() fetchTesseract()
.then(async ({ createWorker }) => { .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, { const worker = await createWorker('eng', 1, {
workerPath: tesseractWorkerPath as string, workerPath: tesseractWorkerPath.default,
corePath: tesseractCorePath as string, corePath: tesseractCorePath.default,
langPath: `${assetHost}/ocr/lang-data`, langPath: `${assetHost}/ocr/lang-data`,
cacheMethod: 'write', cacheMethod: 'write',
}); });
@@ -501,5 +503,4 @@ export const AltTextModal = forwardRef<ModalRef, Props & Partial<RestoreProps>>(
); );
}, },
); );
AltTextModal.displayName = 'AltTextModal'; AltTextModal.displayName = 'AltTextModal';

View File

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

View File

@@ -3,9 +3,13 @@
// emojiIndex.search functionality. // emojiIndex.search functionality.
import type { BaseEmoji } from 'emoji-mart'; import type { BaseEmoji } from 'emoji-mart';
import type { Emoji } from 'emoji-mart/dist-es/utils/data'; 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'; import { unicodeToUnifiedName } from './unicode_to_unified_name';
type Emojis = Record< type Emojis = Record<

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,24 +1,11 @@
// //
// Tools for performance debugging, only enabled in development mode. // Tools for performance debugging, only enabled in development mode.
// Open up Chrome Dev Tools, then Timeline, then User Timing to see output. // 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 * as marky from 'marky';
import { isDevelopment } from './utils/environment'; 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) { export function start(name) {
if (isDevelopment()) { if (isDevelopment()) {
marky.mark(name); marky.mark(name);

View File

@@ -2,10 +2,13 @@
// If there are no polyfills, then this is just Promise.resolve() which means // 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). // 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'; import { loadIntlPolyfills } from './intl';
function importExtraPolyfills() { function importExtraPolyfills() {
return import(/* webpackChunkName: "extra_polyfills" */ './extra_polyfills'); return import('./extra_polyfills');
} }
export function loadPolyfills() { export function loadPolyfills() {

View File

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

View File

@@ -1,5 +1,4 @@
import { ExpirationPlugin } from 'workbox-expiration'; import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing'; import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies'; import { CacheFirst } from 'workbox-strategies';
@@ -15,10 +14,9 @@ function fetchRoot() {
return fetch('/', { credentials: 'include', redirect: 'manual' }); return fetch('/', { credentials: 'include', redirect: 'manual' });
} }
precacheAndRoute(self.__WB_MANIFEST);
registerRoute( registerRoute(
/locale_.*\.js$/, /intl\/.*\.js$/,
new CacheFirst({ new CacheFirst({
cacheName: `${CACHE_NAME_PREFIX}locales`, cacheName: `${CACHE_NAME_PREFIX}locales`,
plugins: [ 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 { IntlMessageFormat } from 'intl-messageformat';
import { unescape } from 'lodash'; import { unescape } from 'lodash';
// see config/vite/plugins/sw-locales
import locales from './web_push_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 MAX_NOTIFICATIONS = 5;
const GROUP_TAG = 'tag'; const GROUP_TAG = 'tag';

View File

@@ -1,7 +1,11 @@
export function isDevelopment() { 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() { 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 { body {
accent-color: #6364ff; accent-color: #6364ff;
@@ -259,7 +259,7 @@ table + p {
.email-header-td { .email-header-td {
padding: 16px 32px; padding: 16px 32px;
background-color: #1b001f; 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-position: left top;
background-repeat: repeat; background-repeat: repeat;
} }
@@ -426,7 +426,7 @@ table + p {
// Body content // Body content
.email-body-td { .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-position: left top;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
@@ -922,7 +922,7 @@ table + p {
// Extra content on light purple background // Extra content on light purple background
.email-extra-wave { .email-extra-wave {
height: 42px; 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-position: bottom center;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
@@ -930,7 +930,7 @@ table + p {
.email-extra-td { .email-extra-td {
padding: 32px 32px 24px; padding: 32px 32px 24px;
background-color: #f0f0ff; 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 { .email-column-td {
padding-top: 8px; padding-top: 8px;

View File

@@ -1,6 +1,6 @@
@font-face { @font-face {
font-family: Inter; 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'); format('woff2-variations');
font-weight: 100 900; font-weight: 100 900;
font-style: normal; font-style: normal;

View File

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

View File

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

View File

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

View File

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

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