diff --git a/src/components/compose.css b/src/components/compose.css
index 4870063c..5ae798cb 100644
--- a/src/components/compose.css
+++ b/src/components/compose.css
@@ -619,3 +619,86 @@
#custom-emojis-sheet .custom-emojis-list button:is(:hover, :focus) img {
transform: scale(1.5);
}
+
+.compose-field-container {
+ display: grid !important;
+
+ &.debug {
+ grid-template-columns: 1fr 1fr;
+ }
+
+ > * {
+ grid-area: 1 / 1 / 2 / 2;
+ }
+
+ .compose-highlight {
+ user-drag: none;
+ user-select: none;
+ pointer-events: none;
+ touch-action: none;
+ padding: 8px;
+ color: transparent;
+ background-color: transparent;
+ border: 2px solid transparent;
+ line-height: 1.4;
+ overflow: auto;
+ unicode-bidi: plaintext;
+ -webkit-rtl-ordering: logical;
+ rtl-ordering: logical;
+ overflow-wrap: break-word;
+ white-space: pre-wrap;
+ min-height: 5em;
+ max-height: 50vh;
+
+ /* Follow textarea styles */
+ @media (min-width: 40em) {
+ max-height: 65vh;
+ }
+ @media (width < 30em) {
+ margin-inline: calc(-1 * var(--form-padding-inline));
+ width: 100vw !important;
+ max-width: 100vw;
+ border: 0;
+ }
+
+ mark {
+ color: inherit;
+ }
+
+ .compose-highlight-url,
+ .compose-highlight-hashtag {
+ background-color: transparent;
+ text-decoration: underline;
+ text-decoration-color: var(--link-faded-color);
+ text-decoration-thickness: 2px;
+ text-underline-offset: 2px;
+ }
+ .compose-highlight-mention,
+ .compose-highlight-emoji-shortcode,
+ .compose-highlight-exceeded {
+ mix-blend-mode: multiply;
+ border-radius: 4px;
+ box-shadow: 0 0 0 1px;
+ }
+ .compose-highlight-mention {
+ background-color: var(--orange-light-bg-color);
+ box-shadow-color: var(--orange-light-bg-color);
+ }
+ .compose-highlight-emoji-shortcode {
+ background-color: var(--bg-faded-color);
+ box-shadow-color: var(--bg-faded-color);
+ }
+ .compose-highlight-exceeded {
+ background-color: var(--red-bg-color);
+ box-shadow-color: var(--red-bg-color);
+ }
+
+ @media (prefers-color-scheme: dark) {
+ .compose-highlight-mention,
+ .compose-highlight-emoji-shortcode,
+ .compose-highlight-exceeded {
+ mix-blend-mode: screen;
+ }
+ }
+ }
+}
diff --git a/src/components/compose.jsx b/src/components/compose.jsx
index 18480db7..0fe06102 100644
--- a/src/components/compose.jsx
+++ b/src/components/compose.jsx
@@ -104,6 +104,55 @@ function countableText(inputText) {
.replace(usernameRegex, '$1@$3');
}
+// https://github.com/mastodon/mastodon/blob/c03bd2a238741a012aa4b98dc4902d6cf948ab63/app/models/account.rb#L69
+const USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i;
+const MENTION_RE = new RegExp(
+ `(? maxCharacters) {
+ const leftoverCount = composerCharacterCount - maxCharacters;
+ leftoverHTML = html.slice(-leftoverCount);
+ html = html.slice(0, -leftoverCount);
+ // Highlight exceeded characters
+ leftoverHTML = leftoverHTML.replace(
+ new RegExp(`(.{${leftoverCount}})$`),
+ '$1',
+ );
+ }
+
+ html = html
+ .replace(urlRegexObj, '$2$3') // URLs
+ .replace(MENTION_RE, '$&') // Mentions
+ .replace(HASHTAG_RE, '#$1') // Hashtags
+ .replace(
+ SCAN_RE,
+ '$&',
+ ); // Emoji shortcodes
+
+ return html + leftoverHTML;
+}
+
function Compose({
onClose,
replyToStatus,
@@ -1387,6 +1436,11 @@ const Textarea = forwardRef((props, ref) => {
handleCommited = (e) => {
const { input } = e.detail;
setText(input.value);
+ // fire input event
+ if (ref.current) {
+ const event = new Event('input', { bubbles: true });
+ ref.current.dispatchEvent(event);
+ }
};
textExpanderRef.current.addEventListener(
@@ -1413,8 +1467,14 @@ const Textarea = forwardRef((props, ref) => {
};
}, []);
+ const composeHighlightRef = useRef();
+
return (
-
+
);
diff --git a/src/index.css b/src/index.css
index ab41185d..b6cdb5cd 100644
--- a/src/index.css
+++ b/src/index.css
@@ -18,7 +18,13 @@
--purple-color: blueviolet;
--green-color: darkgreen;
--orange-color: darkorange;
+ --orange-light-bg-color: color-mix(
+ in srgb,
+ var(--orange-color) 20%,
+ transparent
+ );
--red-color: orangered;
+ --red-bg-color: color-mix(in lch, var(--red-color) 40%, transparent);
--bg-color: #fff;
--bg-faded-color: #f0f2f5;
--bg-blur-color: #fff9;