Compare commits

...

26 Commits

Author SHA1 Message Date
Thomas Steiner
533b07f207
Merge c1d09c8d6e into 14cb5ff881 2025-09-03 20:05:42 +00:00
Claire
14cb5ff881
Add compatibility hack for GoToSocial interaction policies (#36004)
Some checks are pending
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
2025-09-03 19:35:43 +00:00
diondiondion
bc952ebde9
Add new plain (text-only) button variant (#36002)
Some checks failed
Check i18n / check-i18n (push) Waiting to run
CodeQL / Analyze (javascript) (push) Waiting to run
CodeQL / Analyze (ruby) (push) Waiting to run
Check formatting / lint (push) Waiting to run
Ruby Linting / lint (push) Waiting to run
Historical data migration test / test (14-alpine) (push) Waiting to run
Historical data migration test / test (15-alpine) (push) Waiting to run
Historical data migration test / test (16-alpine) (push) Waiting to run
Historical data migration test / test (17-alpine) (push) Waiting to run
Ruby Testing / build (production) (push) Waiting to run
Ruby Testing / build (test) (push) Waiting to run
Ruby Testing / test (.ruby-version) (push) Blocked by required conditions
Ruby Testing / test (3.2) (push) Blocked by required conditions
Ruby Testing / test (3.3) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (.ruby-version) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.2) (push) Blocked by required conditions
Ruby Testing / ImageMagick tests (3.3) (push) Blocked by required conditions
Ruby Testing / End to End testing (.ruby-version) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.2) (push) Blocked by required conditions
Ruby Testing / End to End testing (3.3) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, docker.elastic.co/elasticsearch/elasticsearch:8.10.2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (.ruby-version, opensearchproject/opensearch:2) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.2, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Ruby Testing / Elastic Search integration testing (3.3, docker.elastic.co/elasticsearch/elasticsearch:7.17.13) (push) Blocked by required conditions
Chromatic / Run Chromatic (push) Has been cancelled
CSS Linting / lint (push) Has been cancelled
JavaScript Linting / lint (push) Has been cancelled
JavaScript Testing / test (push) Has been cancelled
2025-09-03 12:34:29 +00:00
renovate[bot]
c1542643f5
fix(deps): update dependency sass to v1.92.0 (#36001)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-09-03 12:23:00 +00:00
Thomas Steiner
c1d09c8d6e
Merge branch 'main' into compose-language-detection 2025-08-21 18:25:36 +02:00
Thomas Steiner
eed4c3df5b
Merge branch 'main' into compose-language-detection 2025-08-20 09:51:21 +02:00
Thomas Steiner
5579122edc
Merge branch 'main' into compose-language-detection 2025-08-08 01:50:20 +02:00
Thomas Steiner
c1cd312146
Merge branch 'main' into compose-language-detection 2025-07-23 22:22:57 +05:30
Thomas Steiner
069b91a293
Merge branch 'main' into compose-language-detection 2025-07-18 14:32:07 +02:00
Thomas Steiner
d4a5718c8e
Merge branch 'main' into compose-language-detection 2025-07-17 18:02:53 +02:00
Thomas Steiner
97ce80b222
Merge branch 'main' into compose-language-detection 2025-07-14 13:35:30 +02:00
Thomas Steiner
e3ec61ab10 Merge branch 'compose-language-detection' of github.com:tomayac/mastodon into compose-language-detection 2025-07-14 13:35:12 +02:00
Thomas Steiner
8bde7fe8b9 Rename file, as it's lande, not laude 2025-07-14 13:34:41 +02:00
Thomas Steiner
796bcc701d
Merge branch 'main' into compose-language-detection 2025-07-11 19:29:01 +02:00
Thomas Steiner
9c9442ab84 Remove unused imports 2025-07-11 19:17:52 +02:00
Thomas Steiner
25b0143a12 Don't auto-change the language 2025-07-11 19:14:25 +02:00
Thomas Steiner
8f2834d02a Merge branch 'compose-language-detection' of github.com:tomayac/mastodon into compose-language-detection 2025-07-11 17:41:12 +02:00
Thomas Steiner
d59b6a7e12 Log debug 2025-07-11 17:40:09 +02:00
Thomas Steiner
4122b4f26b
Merge branch 'main' into compose-language-detection 2025-07-11 17:31:15 +02:00
Thomas Steiner
cdc956bba3 Merge branch 'compose-language-detection' of github.com:tomayac/mastodon into compose-language-detection 2025-07-11 17:27:11 +02:00
Thomas Steiner
bac259debf Restructure to use laude or LanguageDetector 2025-07-11 17:26:38 +02:00
Thomas Steiner
3a232d9626
Merge branch 'main' into compose-language-detection 2025-07-11 12:47:47 +02:00
Thomas Steiner
3e1e6762aa Debounce language detection 2025-07-11 08:37:44 +02:00
Thomas Steiner
0733590c3b Remove log 2025-06-21 13:28:29 +02:00
Thomas Steiner
c50cf3b544 Use the UI language as default compose language 2025-06-21 13:27:03 +02:00
Thomas Steiner
429ab8d210 Add language detection to compose widget 2025-06-21 13:19:04 +02:00
10 changed files with 188 additions and 27 deletions

View File

@ -8,6 +8,7 @@ const meta = {
component: Button,
args: {
secondary: false,
plain: false,
compact: false,
dangerous: false,
disabled: false,
@ -57,6 +58,14 @@ export const Secondary: Story = {
play: buttonTest,
};
export const Plain: Story = {
args: {
plain: true,
children: 'Plain button',
},
play: buttonTest,
};
export const Compact: Story = {
args: {
compact: true,
@ -101,6 +110,14 @@ export const SecondaryDisabled: Story = {
play: disabledButtonTest,
};
export const PlainDisabled: Story = {
args: {
...Plain.args,
disabled: true,
},
play: disabledButtonTest,
};
const loadingButtonTest: Story['play'] = async ({
args,
canvas,

View File

@ -9,6 +9,7 @@ interface BaseProps
extends Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, 'children'> {
block?: boolean;
secondary?: boolean;
plain?: boolean;
compact?: boolean;
dangerous?: boolean;
loading?: boolean;
@ -35,6 +36,7 @@ export const Button: React.FC<Props> = ({
disabled,
block,
secondary,
plain,
compact,
dangerous,
loading,
@ -62,6 +64,7 @@ export const Button: React.FC<Props> = ({
<button
className={classNames('button', className, {
'button-secondary': secondary,
'button--plain': plain,
'button--compact': compact,
'button--block': block,
'button--dangerous': dangerous,

View File

@ -20,7 +20,7 @@ import { languages as preloadedLanguages } from 'mastodon/initial_state';
import type { RootState } from 'mastodon/store';
import { useAppSelector, useAppDispatch } from 'mastodon/store';
import { debouncedGuess } from '../util/language_detection';
import { debouncedGuess, countLetters } from '../util/language_detection';
const messages = defineMessages({
changeLanguage: {
@ -375,12 +375,25 @@ export const LanguageDropdown: React.FC = () => {
);
useEffect(() => {
if (text.length > 20) {
debouncedGuess(text, setGuess);
let canceled = false;
if (countLetters(text) >= 5) {
debouncedGuess(text)
.then((lang) => {
if (!canceled) {
setGuess(lang ?? '');
}
})
.catch(() => {
setGuess('');
});
} else {
debouncedGuess.cancel();
setGuess('');
}
return () => {
canceled = true;
};
}, [text, setGuess]);
return (

View File

@ -0,0 +1,5 @@
export declare const debouncedGuess: (
text: string,
) => Promise<string | undefined>;
export declare const countLetters: (text: string) => number;

View File

@ -1,7 +1,5 @@
import lande from 'lande';
import { debounce } from 'lodash';
import { urlRegex } from './url_regex';
const languageDetectorInGlobalThis = 'LanguageDetector' in globalThis;
let languageDetectorSupportedAndReady = languageDetectorInGlobalThis && await globalThis.LanguageDetector.availability() === 'available';
const ISO_639_MAP = {
afr: 'af', // Afrikaans
@ -56,21 +54,23 @@ const ISO_639_MAP = {
vie: 'vi', // Vietnamese
};
const guessLanguage = (text) => {
text = text
.replace(urlRegex, '')
.replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '');
if (text.length > 20) {
const [lang, confidence] = lande(text)[0];
if (confidence > 0.8)
return ISO_639_MAP[lang];
}
return '';
const countLetters = (text) => {
const segmenter = new Intl.Segmenter('und', { granularity: 'grapheme' })
const letters = [...segmenter.segment(text)]
return letters.length
};
export const debouncedGuess = debounce((text, setGuess) => {
setGuess(guessLanguage(text));
}, 500, { maxWait: 1500, leading: true, trailing: true });
let module;
// If the API is supported, but the model not loaded yet…
if (languageDetectorInGlobalThis) {
if (!languageDetectorSupportedAndReady) {
// …trigger the model download
globalThis.LanguageDetector.create();
}
module = await import('./language_detection_with_languagedetector');
} else {
module = await import('./language_detection_with_lande');
}
const debouncedGuess = module.debouncedGuess;
export { debouncedGuess, countLetters, ISO_639_MAP };

View File

@ -0,0 +1,38 @@
import lande from 'lande';
import { debounce } from 'lodash';
import { countLetters, ISO_639_MAP } from './language_detection';
import { urlRegex } from './url_regex';
const guessLanguage = (text) => {
text = text
.replace(urlRegex, '')
.replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '');
if (countLetters(text) > 20) {
const [lang, confidence] = lande(text)[0];
if (confidence > 0.8)
return ISO_639_MAP[lang];
}
return '';
};
const debouncedGuess = (() => {
let resolver = null;
const debounced = debounce((text) => {
const result = guessLanguage(text);
if (resolver) {
resolver(result);
resolver = null;
}
}, 500, { maxWait: 1500, leading: true, trailing: true });
return (text) => new Promise((resolve) => {
resolver = resolve;
debounced(text);
});
})();
export { debouncedGuess };

View File

@ -0,0 +1,41 @@
import { debounce } from 'lodash';
import { urlRegex } from './url_regex';
const guessLanguage = async (text) => {
text = text
.replace(urlRegex, '')
.replace(/(^|[^/\w])@(([a-z0-9_]+)@[a-z0-9.-]+[a-z0-9]+)/ig, '');
try {
const languageDetector = await globalThis.LanguageDetector.create();
let {detectedLanguage, confidence} = (await languageDetector.detect(text))[0];
if (confidence > 0.8) {
detectedLanguage = detectedLanguage.split('-')[0];
return detectedLanguage;
}
} catch {
return '';
}
return '';
};
const debouncedGuess = (() => {
let resolver = null;
const debounced = debounce((text) => {
const result = guessLanguage(text);
if (resolver) {
resolver(result);
resolver = null;
}
}, 500, { maxWait: 1500, leading: true, trailing: true });
return (text) => new Promise((resolve) => {
resolver = resolve;
debounced(text);
});
})();
export { debouncedGuess };

View File

@ -201,6 +201,41 @@
}
}
&.button--plain {
color: $highlight-text-color;
background: transparent;
padding: 6px;
// The button has no outline, so we use negative margin to
// visually align its label with its surroundings while maintaining
// a generous click target
margin-inline: -6px;
border: 1px solid transparent;
&:active,
&:focus,
&:hover {
border-color: transparent;
color: lighten($highlight-text-color, 4%);
background-color: transparent;
text-decoration: none;
}
&:disabled,
&.disabled {
opacity: 0.7;
border-color: transparent;
color: $ui-button-disabled-color;
&:active,
&:focus,
&:hover {
border-color: transparent;
color: $ui-button-disabled-color;
}
}
}
&.button-tertiary {
background: transparent;
padding: 6px 17px;

View File

@ -232,6 +232,15 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
canQuote: {
automaticApproval: approved_uris,
},
canReply: {
always: 'https://www.w3.org/ns/activitystreams#Public',
},
canLike: {
always: 'https://www.w3.org/ns/activitystreams#Public',
},
canAnnounce: {
always: 'https://www.w3.org/ns/activitystreams#Public',
},
}
end

View File

@ -11957,8 +11957,8 @@ __metadata:
linkType: hard
"sass@npm:^1.62.1":
version: 1.91.0
resolution: "sass@npm:1.91.0"
version: 1.92.0
resolution: "sass@npm:1.92.0"
dependencies:
"@parcel/watcher": "npm:^2.4.1"
chokidar: "npm:^4.0.0"
@ -11969,7 +11969,7 @@ __metadata:
optional: true
bin:
sass: sass.js
checksum: 10c0/5be1c98f7a618cb5f90b62f63d2aa0f78f9bf369c93ec7cd9880752a26b0ead19aa63cc341e8a26ce6c74d080baa5705f1685dff52fe6a3f28a7828ae50182b6
checksum: 10c0/bdff9fa6988620e2a81962efdd016e3894d19934cfadc105cf41db767f59dd47afd8ff32840e612ef700cb67e19d9e83c108f1724eb8f0bef56c4877dbe6f14d
languageName: node
linkType: hard