The chat composer reused .miu-textarea, which the INBOX MAIL composer already
owned further down the stylesheet with `resize: vertical; min-height: 90px`.
Equal specificity, later rule wins — so the mail styling applied to the chat box:
90px tall with a resize grabber, and every height fix in 0.1.10–0.1.12 was
silently overridden. That is why the box never changed.
The composer now uses its own .miu-composer-box; the inbox rule is untouched.
Adds a regression test asserting the composer does not carry .miu-textarea.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0.1.11 left it at 40px with line-height 1.45, so the line (20.3px) overflowed
the 20px content box — taller than the <input> it replaced, and liable to show a
scrollbar on a single line.
Collapsed height is now one token, --miu-composer-h: 38px, shared by the
textarea, attach and send so they cannot drift apart again. Padding tightened to
8px (from .miu-input's 9px) with line-height 1.4, so a 14px line is 19.6 + 16 + 2
= 37.6px and fits exactly — matching the original single-line input.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The textarea swap made the composer row tall and dragged the controls with it.
Three causes, all fixed:
- No box-sizing anywhere in the stylesheet, so `min-height: 38px` on a padded,
bordered textarea rendered ~58px (content-box adds 18px padding + 2px border
on top). The textarea is now border-box with a 40px min-height — the same
height the old single-line input had.
- .miu-composer-row is display:flex with no align-items, so it defaulted to
`stretch` and the buttons — neither of which declared a height — grew to the
row. Now align-items: flex-end, so controls stay pinned to the bottom while
the box grows upward (WhatsApp behaviour), with an explicit 40px on both.
The send height is scoped to .miu-composer so modal/settings buttons keep
their own sizing.
- The auto-grow effect set height = scrollHeight, which under content-box
double-counted padding on every keystroke. It now compensates for the border
explicitly, correct under border-box.
Bumped to 0.1.11. SDK suite: 17 files / 98 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Two bugs from the formatting work, both visible in the CRM messenger:
1. Inline code / code blocks were invisible in your OWN bubbles. `.miu-code` sets
background: --miu-panel-2 (#1d1d26) while the is-mine override set only the
colour to --miu-accent-text (#1a1206) — near-black on near-black, ~1.03:1
contrast. The chip now tints the accent bubble (rgba(0,0,0,.16)) instead of
using the panel colour, so it reads against any accent.
2. The conversation list showed the raw last message, so a strikethrough message
previewed as "~crazy~". Adds stripMarkup() — markers off, code unwrapped,
nesting handled, honouring the same word-boundary rule so snake_case_name and
"5 * 3" survive — and uses it for the preview line.
Bumped to 0.1.10. SDK suite: 17 files / 98 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Composer becomes a <textarea> so the PLATFORM supplies text services: red
spellcheck squiggles, right-click suggestions / add-to-dictionary, and mobile
autocorrect (spellCheck + autoCorrect + autoCapitalize). Nothing shipped for it.
Previously a single-line <input>, which Firefox does not spellcheck by default
(layout.spellcheckDefault=1 checks multi-line only) and which could not hold a
multi-line message at all. Enter sends, Shift+Enter (and IME composition) makes a
newline, and the box grows with content.
WhatsApp-style markup: *bold*, _italic_, ~strike~, `code`, ```fenced blocks```,
plus bare URLs. Cmd/Ctrl+B/I/E and a small toolbar wrap the selection in markers.
Messages stay PLAIN TEXT on the wire, so stored history and older clients are
unaffected — formatting is purely a render concern.
renderRichText() returns a ReactNode tree and never uses dangerouslySetInnerHTML,
so message text cannot inject markup; links are restricted to http/https/mailto
(safeHref) to close the javascript: vector. Code is tokenized first and its
contents stay literal; markers require word boundaries so snake_case_name and
"5 * 3" are not mangled.
Bumped to 0.1.9. SDK suite: 17 files / 95 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase C of channel-roster import. Adds the optional addMembers(threadId, userIds)
adapter seam (+ BulkAddResult) and, in ConversationSettings, a '#' mode on the
existing Add-people box that lists the channels you belong to — public AND
private, since the source list is your own membership-scoped conversation list.
Picking a channel STAGES the import (reads its roster, diffs against who is
already here) and shows a confirm — 'Add 9 people from #design? (3 already here)'
— so an administrative action never fires on a stray click. The result line
reports added / already-a-member / failed. Absent addMembers => the affordance is
hidden entirely and '#' is just a search string.
Bumped to 0.1.8. SDK suite: 15 files / 78 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add optional MessagingAdapter.setFocus(threadId) and subscribeActivity(cb)
so hosts can report the foregrounded thread (backend suppresses push for it)
and drive live conversation-list refresh on any thread's activity. Messenger
wires focus/blur presence + activity-driven refetch. Published as 0.1.7.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A host can pass focusThreadId to select/open a specific conversation — used by the
CRM's global search to jump to the exact thread. Mail switches to Open + selects the
matching item; messenger selects the thread. (Source for the already-published 0.1.6.)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Every message now shows a Slack-style time (clock today, then 'Yesterday',
weekday, else a date; full timestamp on hover).
- New ConversationSettings panel for groups AND channels (public + private):
rename, member list, add people (from the directory), remove, and leave.
Opened from a new thread header gear; DMs show no gear. Adapter gains
addMember/removeMember/renameConversation (optional, OPA still gates writes).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Extend the messaging Attachment type with optional storage fields so an adapter's
upload() can return a sendable reference (the socket needs contentRef/mimeType/
sizeBytes to persist the message part) while keeping the display url. Enables
messenger attachments end-to-end in host adapters.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Mail reader: .miu-mail-msg gets flex:0 0 auto so cards keep their natural height
in the scrolling column instead of being compressed + clipped by overflow:hidden.
- Reaction/emoji picker renders through a PopoverPortal to <body> (positioned +
themed) so it floats above the message list instead of being cut by the scroll
container's overflow. 72 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Picking a file now shows an immediate chip with the filename + a spinner while
the bytes upload (mail compose, mail reply, and the messenger composer), instead
of only appearing once upload finishes. Respects prefers-reduced-motion.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Attachment chips in the mail reader are now clickable: new InboxAdapter
downloadAttachment() resolves a short-lived URL, opened in a new tab.
- Mail reader scrolls again: convert the .miu-detail/.miu-mail height:100%
chain to flex fill so a long thread scrolls within the pane instead of
being clipped. MockInboxAdapter implements download. Bump to 0.1.1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A host wrapper like .view{position:relative;z-index:1} creates a stacking context
that traps the modal's fixed overlay below a sibling sticky header, no matter its
z-index. Render the modal through a ModalPortal (createPortal to document.body) so
it escapes the host stacking context + overflow entirely; the portal root copies
the SDK theme tokens from the live surface (synchronously, no unstyled paint) and
carries dark defaults as a fallback. Adds react-dom peer dep. 67 tests green.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Implements MessagingAdapter over @insignia/iios-kernel-client (browser → IIOS,
token-in): listConversations/openThread/history/send/subscribe/typing/markRead
/react, mapping kernel Message/ThreadSummary/annotations onto the SDK's neutral
types. currentActorId comes from the host's session (senderId space), never
inferred from history — the exact bug the conformance suite kills.
- lives in adapters/ (transport boundary intact — core stays transport-free)
- ships from the ./adapters/kernel-client subpath (kernel-client is an optional
peer dep; excluded from the main bundle)
- connectKernelAdapter({serviceUrl, token, currentUserId}) convenience
- passes the shared adapter conformance suite (12 tests) over the real
MessageSocket facade with only the socket.io layer faked
- media/attachments deferred (kernel-client has no presign yet)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>