Skip to content

feat(ui-kit): new chat, contacts, groups and call log - #367

Open
purpshell wants to merge 21 commits into
mainfrom
feat/ui-kit-new-chat-contacts-groups-20261027
Open

purpshell wants to merge 21 commits into
mainfrom
feat/ui-kit-new-chat-contacts-groups-20261027

Conversation

@purpshell

@purpshell purpshell commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Related source stack

Backend native view: Stack 957.

Backend order: #877 → #878. This SDK PR targets main; those are API contract dependencies, not Git parents. Consolidation predecessor: SDK #365, owned separately.

Summary

Add a real contacts directory, Start New Chat, group creation/member/settings controls and an API-backed call log to framework-neutral controllers, React and native Web Components. All six server SDKs gain Number call-history reads and the new client-token actions.

Verification

At current remote head 85eb685af2238a41f20beb0bca14aa0dca65f448, native Go/Python/PHP/.NET/Rust CI passed. Both JavaScript CI jobs failed formatting on 37 files. Accepted-main integration, the genuine formatting repair and refreshed source/package/snippet qualification remain unfinished; the local successor has not been pushed. No fresh pass is claimed for it.

Release status

Open source candidate; the Ready flag does not establish merge qualification. The carried #365 consolidation remains a separate predecessor owned by its existing source owner. The client-action API depends on polymorfa/polymorfa#877 (migration identity held); the regional call route is in #878 and still needs regional activation. Accepted API/coverage pins are unchanged. Missing or mismatched snippet recipes retain cURL fallback. No registry publication, API acceptance or customer availability is claimed.

@coderabbitai

coderabbitai Bot commented Oct 28, 2026 •

Copy link
Copy Markdown

Warning

Review limit reached

The included review limit has been reached and this organization has disabled usage-based review continuation. Wait for reviews to reset or ask a billing admin to change After included review limits.

  • Ask an admin to enable usage-based reviews

Open in CodeRabbit

Reviews can continue after your included limit without a manual trigger. An admin must approve usage-based billing.

Next included review available in 28 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available. Your 64 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

Learn how review limits work.

Review configuration:

Run configuration

  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Team
  • Run ID: 08c0e0bd-f8ce-4870-9afd-615a852378bb
Commits

Reviewing files that changed from the base of the PR and between a6fc6ea and 43d9f2e.

Files ignored due to path filters: 1

  • packages/python/uv.lock is excluded by !**/*.lock
Files selected for processing: 99

  • CHANGELOG.md
  • contracts/doc-snippets.json
  • packages/calls/src/api.ts
  • packages/calls/src/client.ts
  • packages/calls/src/lifecycle.ts
  • packages/calls/test/lifecycle.test.ts
  • packages/calls/test/social.test.ts
  • packages/dotnet/README.md
  • packages/dotnet/src/Polymorfa.Sdk/Calls.cs
  • packages/dotnet/src/Polymorfa.Sdk/MessagingModels.cs
  • packages/dotnet/tests/Polymorfa.Sdk.Tests/AccountTests.cs
  • packages/dotnet/tests/Polymorfa.Sdk.Tests/Program.cs
  • packages/dotnet/tests/Polymorfa.Sdk.Tests/ProjectPairingTests.cs
  • packages/go/README.md
  • packages/go/client.go
  • packages/go/operations_test.go
  • packages/go/session_calls.go
  • packages/go/session_calls_test.go
  • packages/nextjs/src/handler.ts
  • packages/nextjs/src/messaging-token-mint.ts
  • packages/nextjs/test/handler.test.ts
  • packages/nextjs/test/nextjs.test.ts
  • packages/php/README.md
  • packages/php/src/ClientTokenModels.php
  • packages/php/src/CursorPage.php
  • packages/php/src/Resources/Calls.php
  • packages/php/tests/client_tokens.php
  • packages/php/tests/messaging_complete.php
  • packages/python/README.md
  • packages/python/src/polymorfa/client_tokens.py
  • packages/python/src/polymorfa/messaging.py
  • packages/python/src/polymorfa/models.py
  • packages/python/tests/test_client_tokens.py
  • packages/python/tests/test_system_quicklinks_webhooks.py
  • packages/rust/README.md
  • packages/rust/src/calls.rs
  • packages/rust/src/connections.rs
  • packages/rust/src/lib.rs
  • packages/rust/src/session_calls.rs
  • packages/rust/tests/calls.rs
  • packages/rust/tests/connections.rs
  • packages/typescript/README.md
  • packages/typescript/src/index.ts
  • packages/typescript/src/messaging/calls.ts
  • packages/typescript/src/messaging/types.ts
  • packages/typescript/test/messaging.test.ts
  • packages/ui-kit/README.md
  • packages/ui-kit/docs/browser.md
  • packages/ui-kit/docs/react.md
  • packages/ui-kit/docs/web-components.md
  • packages/ui-kit/src/appearance/chat.ts
  • packages/ui-kit/src/appearance/locale-pt-br.ts
  • packages/ui-kit/src/appearance/locale.ts
  • packages/ui-kit/src/appearance/slots.ts
  • packages/ui-kit/src/appearance/styles.ts
  • packages/ui-kit/src/browser/call-log/index.ts
  • packages/ui-kit/src/browser/calls/client.ts
  • packages/ui-kit/src/browser/calls/socket.ts
  • packages/ui-kit/src/browser/chat/conversation.ts
  • packages/ui-kit/src/browser/chat/index.ts
  • packages/ui-kit/src/browser/contacts/directory.ts
  • packages/ui-kit/src/browser/contacts/groups.ts
  • packages/ui-kit/src/browser/contacts/index.ts
  • packages/ui-kit/src/browser/contacts/new-chat.ts
  • packages/ui-kit/src/browser/dropin/inbox.ts
  • packages/ui-kit/src/browser/dropin/permissions.ts
  • packages/ui-kit/src/browser/index.ts
  • packages/ui-kit/src/browser/messaging/account.ts
  • packages/ui-kit/src/browser/messaging/client.ts
  • packages/ui-kit/src/browser/messaging/index.ts
  • packages/ui-kit/src/browser/messaging/types.ts
  • packages/ui-kit/src/react/call-log.tsx
  • packages/ui-kit/src/react/call-number-picker.tsx
  • packages/ui-kit/src/react/calls.tsx
  • packages/ui-kit/src/react/components.tsx
  • packages/ui-kit/src/react/dropin.tsx
  • packages/ui-kit/src/react/index.ts
  • packages/ui-kit/src/react/new-chat.tsx
  • packages/ui-kit/src/store/conversation-source.ts
  • packages/ui-kit/src/store/inbox-source.ts
  • packages/ui-kit/src/store/reducers.ts
  • packages/ui-kit/src/web-components/call-log.ts
  • packages/ui-kit/src/web-components/calls.ts
  • packages/ui-kit/src/web-components/chat.ts
  • packages/ui-kit/src/web-components/client.ts
  • packages/ui-kit/src/web-components/contacts.ts
  • packages/ui-kit/src/web-components/dropin.ts
  • packages/ui-kit/src/web-components/index.ts
  • packages/ui-kit/test/browser/account-resources.test.ts
  • packages/ui-kit/test/browser/call-log.test.ts
  • packages/ui-kit/test/browser/calls-client.test.ts
  • packages/ui-kit/test/browser/contacts.test.ts
  • packages/ui-kit/test/react/call-log.test.tsx
  • packages/ui-kit/test/react/calls.test.tsx
  • packages/ui-kit/test/react/new-chat.test.tsx
  • packages/ui-kit/test/web-components/call-log.test.ts
  • packages/ui-kit/test/web-components/contacts.test.ts
  • packages/ui-kit/tsconfig.build.json
  • scripts/doc-snippets/recipes.mjs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

purpshell and others added 15 commits October 28, 2026 05:40
… when it cannot

The Inbox's layout rules used unnamed container queries. The thread pane
is a container too, so the rule that shows Back read the thread's width
and Back appeared beside the list in the two-pane layout. The Inbox's
container is now named and its rules query it.

The list and the thread sit side by side from 560px of Inbox width
(was 700px); the list's minimum is 240px, leaving the thread 320px.
Back has a visible border where it shows.

(cherry picked from commit 9b965da)
Replace @polymorfa/browser, @polymorfa/react, @polymorfa/elements,
@polymorfa/ui, @polymorfa/store and @polymorfa/devtools with one package,
@polymorfa/ui-kit, using subpath exports: the root, /internal, /react,
/web-components (+ /chat, /templates, /calls), /appearance, /store
(+ /store/react) and /devtools (+ /devtools/production).

Sources and tests move without behavior changes. definePolymorfa becomes
defineUiKit (DefineUiKitOptions) with no alias; the pmfa- prefix stays.
react and react-dom are optional peers used only by the React subpaths.
Examples, native host bridges, READMEs, the architecture doc and the
changelog use the new imports. Browser call reports name @polymorfa/ui-kit.
… toggle state

Session client tokens accept the two group actions in all six server SDKs,
the drop-in handler and the UI Kit grant vocabulary. Customer tokens cannot
carry them. The handler forwards them on session grants.

Call toggles named their next action while also reporting aria-pressed, so
a camera that was off read as 'Turn camera on, pressed'. They now carry only
the action label. CallNumberPicker renders through the shared shell with the
callNumberPicker slot, theme classes and bundled styles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Add framework-neutral controllers for a searchable contacts directory, the
new-chat flow and one WhatsApp group, with API sources that use the client
token (read_contact, groups_read, groups_manage). React gets NewChat,
ContactListView and GroupDetails; Web Components get <pmfa-new-chat> and
<pmfa-group-details>. The Inbox opens new chats as drafts until their first
message, and group threads show who wrote each run of messages.

Relayed group messages no longer rename the conversation after their
sender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… React and elements

Also fixes two Inbox defects the tests exposed: without a provider client the
Inbox offered a call button that then threw, and API group defaults were
offered with no client to call them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…an invite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CallLogController groups consecutive calls as WhatsApp does and filters
missed calls. React CallLog, <pmfa-call-log> and an Inbox Chats/Calls switch
render it. Sources: the handler's history/calls route (read_calls), the
store's reduced call.* events. The handler no longer relays call events
without a peer to grants limited to conversations, and the store keeps a
caller's number when later call events carry only the ID.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ContactListView no longer takes the internal slot resolver: it takes
className and classNames like every component and adds the stylesheet,
theme and contactList slot. The directory-backed ContactList and
<pmfa-contact-list> render the account's Contacts list on their own. React
contact rows had lost the pmfa-contact-list class and rendered unstyled.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
All six SDKs read the new peer field on call records and accept the
read_calls client-token action. The UI Kit call log reads
GET /messaging/{session}/calls through client.messaging().calls by default.
The handler history/calls route and the event-derived call log sources are
removed; read_calls is forwarded to the API on session grants.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… Kit

All six SDKs, the drop-in handler and the UI Kit accept the new session
actions (conversations, labels, contact blocking, the account profile and
privacy, channels, catalog reads and presence) and let Customer-scoped tokens
carry every action except mcp. The UI Kit browser client gains chats,
labels, profile, channels and catalog resources, presence.set, contact
block/unblock/info and base64 group pictures. Calls clients take
clientTokenSession so a Customer token names the number it calls from.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The message field was 41.2px beside 40px buttons, and below 600px the
buttons grew to 44px while the field stayed at 41px. In ChatWindow the
emoji and attachment buttons were 40px next to a 44px field and Send.

The composer now has one control size (40px, 44px below 600px and in
ChatWindow). The field's vertical padding is derived from that size and
its own line height, so one line matches the buttons at any text size;
more lines still grow upward from the shared bottom edge.
GET /messaging/{session}/calls returns each call with the other party
in peer. Records are kept and served in the number's data region, so
caller identity is only on this route: Platform call records go back
to peerRef, which is what the API returns.

- TypeScript and Python: calls.list
- Go: SessionCalls().List
- .NET: Calls.ListAsync
- PHP: calls->list
- Rust: calls().list

Each has a native wire test.
The session calls list added Go and Rust SDK source files after the
snippets were bound to the UI Kit consolidation. Re-bind sourceCommit to
a3f6b51 and regenerate.
@purpshell
purpshell force-pushed the feat/ui-kit-new-chat-contacts-groups-20261027 branch from 503df7f to 85eb685 Compare October 28, 2026 04:29
@purpshell
purpshell marked this pull request as ready for review October 31, 2026 04:46
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 31, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-31T04:50:30.238162Z 85eb685 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-31T04:49:22.637922Z 85eb685 Draft marked ready
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85eb685af2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +382 to +383
get(target, property, receiver) {
if (property !== "request") return Reflect.get(target, property, receiver);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Bind forwarded transport members to the original instance

When createBrowserCalls receives clientTokenSession, this proxy forwards getters and methods with the proxy as their receiver. BrowserTransport.baseUrl and BrowserTransport.token() access private fields, so both throw a TypeError; CallsSignalingClient uses these members to establish the lifecycle socket. This prevents Customer-scoped browser calls from connecting. Read getters against the original transport and bind forwarded methods to it, rather than forwarding the proxy receiver.

Useful? React with 👍 / 👎.

Comment on lines +453 to +457
const newChat = useOwnedController<NewChatController>(
controller,
() =>
new NewChatController({
contacts: contacts ?? createApiContactSource(client!),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Wait for the initial grant before initializing NewChat

When <NewChat/> mounts directly inside <PolymorfaProvider tokenEndpoint="…">, the initial grant has not arrived yet. Its startup effect immediately loads the API contacts source, whose client.can("read_contact") check rejects before token acquisition, leaving the directory in a permission-error state even after the grant arrives. Additionally, canCreate is initially false and useOwnedController retains that first controller, so an eventual groups_manage grant never enables group creation. Defer API-backed initialization until the grant is available, or update and reload the owned controller when it arrives.

Useful? React with 👍 / 👎.

Comment thread packages/ui-kit/src/react/call-log.tsx Outdated
Comment on lines +192 to +196
) : snapshot.groups.length === 0 ? (
<p className="pmfa-newchat-state">
{text(configuration, snapshot.filter === "missed" ? "callLog.noMissed" : "callLog.empty")}
</p>
) : (

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep pagination available when the missed filter has no matches

If the first page contains no missed calls but has a next cursor, choosing Missed renders this empty state and removes the Load more button, which exists only in the nonempty-list branch. CallLogController.setFilter() filters only loaded entries, so older missed calls cannot be reached from this view and the UI incorrectly reports no missed calls. The web-component renderer has the same branching issue. Keep pagination available when hasMore is true, or fetch additional matching pages before declaring the filtered log empty.

Useful? React with 👍 / 👎.

shell.dataset.contactOpen = "";
children.push(info);
} else delete shell.dataset.contactOpen;
shell.replaceChildren(...children);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve connected panels during inbox rerenders

When an inbox activity update arrives while the new-chat panel is open, this unconditional replaceChildren removes and reinserts the same pmfa-new-chat element. Its disconnectedCallback() disposes the owned NewChatController, and reconnection creates a fresh one, discarding the search, selected group members and group name, or aborting an in-flight group creation. The call-log and group-details panels likewise dispose their owned controllers on these parent rerenders. Avoid disconnecting unchanged child elements when updating the inbox shell.

Useful? React with 👍 / 👎.

): Promise<boolean> {
this.assertActive();
const current = this.getSnapshot();
if (current.pending !== undefined || !this.canManage) return false;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Allow invite-code reads without group-management permission

For a grant with groups_read but no groups_manage, GroupDetails creates a controller with canManage: false yet still displays the Show invite button. Clicking it calls showInviteCode(), which goes through this guard and silently returns false without reading anything. The API source explicitly authorizes invite-code retrieval with groups_read, so this blocks an otherwise permitted read in both React and web-component group details. Separate read operations from the management guard, or exempt inviteCode while retaining the pending-operation check.

Useful? React with 👍 / 👎.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant