diff --git a/package-lock.json b/package-lock.json
index e2bec3ec..d8843901 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,6 +9,7 @@
"version": "0.1.0",
"dependencies": {
"@formatjs/intl-localematcher": "~0.5.4",
+ "@formatjs/intl-segmenter": "~11.5.5",
"@formkit/auto-animate": "~0.8.1",
"@github/text-expander-element": "~2.6.1",
"@iconify-icons/mingcute": "~1.2.9",
@@ -33,8 +34,7 @@
"react-intersection-observer": "~9.8.1",
"react-quick-pinch-zoom": "~5.1.0",
"react-router-dom": "6.6.2",
- "runes2": "~1.1.4",
- "string-length": "5.0.1",
+ "string-length": "6.0.0",
"swiped-events": "~1.1.9",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
@@ -2924,6 +2924,15 @@
"node": ">=12"
}
},
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "1.18.2",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-1.18.2.tgz",
+ "integrity": "sha512-+QoPW4csYALsQIl8GbN14igZzDbuwzcpWrku9nyMXlaqAlwRBgl5V+p0vWMGFqHOw37czNXaP/lEk4wbLgcmtA==",
+ "dependencies": {
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@formatjs/intl-localematcher": {
"version": "0.5.4",
"resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.5.4.tgz",
@@ -2932,6 +2941,16 @@
"tslib": "^2.4.0"
}
},
+ "node_modules/@formatjs/intl-segmenter": {
+ "version": "11.5.5",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-segmenter/-/intl-segmenter-11.5.5.tgz",
+ "integrity": "sha512-mMbJKFGzwYJBcwfL9EfqFje75Ce5WPar5rSi7wWvFtBPFY2Zi1cWIss7FSm2MNNM9l1BycBAsBQuXFt+Hd+0tQ==",
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "1.18.2",
+ "@formatjs/intl-localematcher": "0.5.4",
+ "tslib": "^2.4.0"
+ }
+ },
"node_modules/@formkit/auto-animate": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.1.tgz",
@@ -3967,15 +3986,6 @@
"tslib": "^2.0.3"
}
},
- "node_modules/char-regex": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-2.0.1.tgz",
- "integrity": "sha512-oSvEeo6ZUD7NepqAat3RqoucZ5SeqLJgOvVIwkafu6IP3V0pO38s/ypdVUmDDK6qIIHNlYHJAKX9E7R7HoKElw==",
- "license": "MIT",
- "engines": {
- "node": ">=12.20"
- }
- },
"node_modules/color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -7116,11 +7126,6 @@
"queue-microtask": "^1.2.2"
}
},
- "node_modules/runes2": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/runes2/-/runes2-1.1.4.tgz",
- "integrity": "sha512-LNPnEDPOOU4ehF71m5JoQyzT2yxwD6ZreFJ7MxZUAoMKNMY1XrAo60H1CUoX5ncSm0rIuKlqn9JZNRrRkNou2g=="
- },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -7262,16 +7267,14 @@
"license": "MIT"
},
"node_modules/string-length": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/string-length/-/string-length-5.0.1.tgz",
- "integrity": "sha512-9Ep08KAMUn0OadnVaBuRdE2l615CQ508kr0XMadjClfYpdCyvrbFp6Taebo8yyxokQ4viUd/xPPUA4FGgUa0ow==",
- "license": "MIT",
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/string-length/-/string-length-6.0.0.tgz",
+ "integrity": "sha512-1U361pxZHEQ+FeSjzqRpV+cu2vTzYeWeafXFLykiFlv4Vc0n3njgU8HrMbyik5uwm77naWMuVG8fhEF+Ovb1Kg==",
"dependencies": {
- "char-regex": "^2.0.0",
- "strip-ansi": "^7.0.1"
+ "strip-ansi": "^7.1.0"
},
"engines": {
- "node": ">=12.20"
+ "node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
diff --git a/package.json b/package.json
index 07d037c8..f4117a77 100644
--- a/package.json
+++ b/package.json
@@ -11,6 +11,7 @@
},
"dependencies": {
"@formatjs/intl-localematcher": "~0.5.4",
+ "@formatjs/intl-segmenter": "~11.5.5",
"@formkit/auto-animate": "~0.8.1",
"@github/text-expander-element": "~2.6.1",
"@iconify-icons/mingcute": "~1.2.9",
@@ -35,8 +36,7 @@
"react-intersection-observer": "~9.8.1",
"react-quick-pinch-zoom": "~5.1.0",
"react-router-dom": "6.6.2",
- "runes2": "~1.1.4",
- "string-length": "5.0.1",
+ "string-length": "6.0.0",
"swiped-events": "~1.1.9",
"toastify-js": "~1.12.0",
"uid": "~2.0.2",
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index c075790f..001b0f9c 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -6,7 +6,6 @@ import { deepEqual } from 'fast-equals';
import { forwardRef } from 'preact/compat';
import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
import { useHotkeys } from 'react-hotkeys-hook';
-import { substring } from 'runes2';
import stringLength from 'string-length';
import { uid } from 'uid/single';
import { useDebouncedCallback, useThrottledCallback } from 'use-debounce';
@@ -131,6 +130,7 @@ const SCAN_RE = new RegExp(
'g',
);
+const segmenter = new Intl.Segmenter();
function highlightText(text, { maxCharacters = Infinity }) {
// Accept text string, return formatted HTML string
// Escape all HTML special characters
@@ -143,19 +143,25 @@ function highlightText(text, { maxCharacters = Infinity }) {
// Exceeded characters limit
const { composerCharacterCount } = states;
- let leftoverHTML = '';
if (composerCharacterCount > maxCharacters) {
- // NOTE: runes2 substring considers surrogate pairs
- // const leftoverCount = composerCharacterCount - maxCharacters;
// Highlight exceeded characters
- leftoverHTML =
- '' +
- // html.slice(-leftoverCount) +
- substring(html, maxCharacters) +
- '';
- // html = html.slice(0, -leftoverCount);
- html = substring(html, 0, maxCharacters);
- return html + leftoverHTML;
+ let withinLimitHTML = '',
+ exceedLimitHTML = '';
+ const htmlSegments = segmenter.segment(html);
+ for (const { segment, index } of htmlSegments) {
+ if (index < maxCharacters) {
+ withinLimitHTML += segment;
+ } else {
+ exceedLimitHTML += segment;
+ }
+ }
+ if (exceedLimitHTML) {
+ exceedLimitHTML =
+ '' +
+ exceedLimitHTML +
+ '';
+ }
+ return withinLimitHTML + exceedLimitHTML;
}
return html
diff --git a/src/main.jsx b/src/main.jsx
index 9c21e63f..19a84c69 100644
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -2,6 +2,9 @@ import './index.css';
import './cloak-mode.css';
+// Polyfill needed for Firefox < 122
+// https://bugzilla.mozilla.org/show_bug.cgi?id=1423593
+import '@formatjs/intl-segmenter/polyfill';
import { render } from 'preact';
import { HashRouter } from 'react-router-dom';