diff --git a/packages/iios-messaging-ui/src/index.ts b/packages/iios-messaging-ui/src/index.ts
index 466ef9e..1275866 100644
--- a/packages/iios-messaging-ui/src/index.ts
+++ b/packages/iios-messaging-ui/src/index.ts
@@ -10,7 +10,7 @@ export { useChannels } from './hooks/use-channels';
export { useMembers } from './hooks/use-members';
export { isOwnMessage } from './types';
// Message-body markup (bold/italic/strike/code/links + mentions) as a safe ReactNode tree.
-export { renderRichText, safeHref } from './rich-text';
+export { renderRichText, safeHref, stripMarkup } from './rich-text';
// Rendered UI. Pair with the './styles.css' export (or override the --miu-* tokens).
export { Messenger } from './components/messenger';
diff --git a/packages/iios-messaging-ui/src/rich-text.test.tsx b/packages/iios-messaging-ui/src/rich-text.test.tsx
index 46d3802..6fb6691 100644
--- a/packages/iios-messaging-ui/src/rich-text.test.tsx
+++ b/packages/iios-messaging-ui/src/rich-text.test.tsx
@@ -1,6 +1,6 @@
import { describe, it, expect } from 'vitest';
import { render, screen } from '@testing-library/react';
-import { renderRichText, safeHref } from './rich-text';
+import { renderRichText, safeHref, stripMarkup } from './rich-text';
function show(text: string, names: string[] = []) {
render({renderRichText(text, names)}
);
@@ -75,3 +75,21 @@ describe('renderRichText', () => {
expect(show('just a normal message').textContent).toBe('just a normal message');
});
});
+
+describe('stripMarkup (one-line previews)', () => {
+ it('drops the markers so a preview never shows ~crazy~', () => {
+ expect(stripMarkup('~crazy~')).toBe('crazy');
+ expect(stripMarkup('*bold* and _italic_')).toBe('bold and italic');
+ });
+
+ it('unwraps code and flattens a fenced block', () => {
+ expect(stripMarkup('run `npm ci` now')).toBe('run npm ci now');
+ expect(stripMarkup('```\nconst a = 1;\n```')).toBe('const a = 1;');
+ });
+
+ it('handles nesting and leaves ordinary text (and identifiers) alone', () => {
+ expect(stripMarkup('*bold with _italic_*')).toBe('bold with italic');
+ expect(stripMarkup('snake_case_name and 5 * 3')).toBe('snake_case_name and 5 * 3');
+ expect(stripMarkup('plain preview')).toBe('plain preview');
+ });
+});
diff --git a/packages/iios-messaging-ui/src/rich-text.tsx b/packages/iios-messaging-ui/src/rich-text.tsx
index f0a94bd..b760f21 100644
--- a/packages/iios-messaging-ui/src/rich-text.tsx
+++ b/packages/iios-messaging-ui/src/rich-text.tsx
@@ -71,6 +71,25 @@ function firstSpan(text: string): { start: number; end: number; rule: Rule; inne
return best;
}
+/**
+ * The same text with its formatting markers removed, for places that show a one-line plain-text
+ * preview (conversation list, notifications) where `~crazy~` must read as "crazy" — you cannot
+ * render nodes into those, so the markers have to come off rather than be styled.
+ */
+export function stripMarkup(text: string): string {
+ const noCode = text
+ .replace(/```([\s\S]*?)```/g, (_m, code: string) => code.trim())
+ .replace(/`([^`\n]+)`/g, '$1');
+ return stripSpans(noCode);
+}
+
+/** Recursively drop *bold* / _italic_ / ~strike~ markers, honouring the same word-boundary rule. */
+function stripSpans(text: string): string {
+ const span = firstSpan(text);
+ if (!span) return text;
+ return text.slice(0, span.start) + stripSpans(span.inner) + stripSpans(text.slice(span.end));
+}
+
/** Linkify + mention-highlight a run of text that carries no other markup. */
function plain(text: string, memberNames: string[], keyed: () => string): ReactNode[] {
const out: ReactNode[] = [];
diff --git a/packages/iios-messaging-ui/src/styles.css b/packages/iios-messaging-ui/src/styles.css
index 2c8922e..0340cf0 100644
--- a/packages/iios-messaging-ui/src/styles.css
+++ b/packages/iios-messaging-ui/src/styles.css
@@ -568,8 +568,14 @@ button.miu-attach-dl:hover {
color: var(--miu-accent);
text-decoration: underline;
}
-.miu-msg.is-mine .miu-bubble .miu-link,
-.miu-msg.is-mine .miu-bubble .miu-code {
+.miu-msg.is-mine .miu-bubble .miu-link {
+ color: var(--miu-accent-text);
+}
+/* On your own (accent-filled) bubble the panel background would sit dark-on-dark against the dark
+ accent text — tint the bubble instead so the chip reads on any accent colour. */
+.miu-msg.is-mine .miu-bubble .miu-code,
+.miu-msg.is-mine .miu-bubble .miu-code-block {
+ background: rgba(0, 0, 0, 0.16);
color: var(--miu-accent-text);
}
.miu-send {