Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions data/onPostBuild/transpileMdxToMarkdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
removeScriptTags,
removeAnchorTags,
removeJsxComments,
convertMethodSignatureToCode,
convertImagePathsToGitHub,
convertDocsLinksToMarkdown,
convertJsxLinkProps,
Expand Down Expand Up @@ -306,6 +307,50 @@ import Baz from 'qux';
});
});

describe('convertMethodSignatureToCode', () => {
it('should convert simple MethodSignature to inline code', () => {
const input = '<MethodSignature>rooms.get(name, options)</MethodSignature>';
const output = convertMethodSignatureToCode(input);
expect(output).toBe('`rooms.get(name, options)`');
});

it('should convert template literal MethodSignature to inline code', () => {
const input = '<MethodSignature>{`rooms.get<RoomOptions>(name, options)`}</MethodSignature>';
const output = convertMethodSignatureToCode(input);
expect(output).toBe('`rooms.get<RoomOptions>(name, options)`');
});

it('should handle MethodSignature with angle brackets in template literal', () => {
const input = '<MethodSignature>{`Map<string, Room>`}</MethodSignature>';
const output = convertMethodSignatureToCode(input);
expect(output).toBe('`Map<string, Room>`');
});

it('should handle multiple MethodSignatures', () => {
const input = `## Method A
<MethodSignature>methodA()</MethodSignature>

## Method B
<MethodSignature>{\`methodB<T>()\`}</MethodSignature>`;
const output = convertMethodSignatureToCode(input);
expect(output).toContain('`methodA()`');
expect(output).toContain('`methodB<T>()`');
expect(output).not.toContain('<MethodSignature>');
});

it('should preserve MethodSignature in code blocks', () => {
const input = '```jsx\n<MethodSignature>preserve this</MethodSignature>\n```';
const output = convertMethodSignatureToCode(input);
expect(output).toContain('<MethodSignature>preserve this</MethodSignature>');
});

it('should handle surrounding content', () => {
const input = 'Before\n\n<MethodSignature>method()</MethodSignature>\n\nAfter';
const output = convertMethodSignatureToCode(input);
expect(output).toBe('Before\n\n`method()`\n\nAfter');
});
});

describe('convertImagePathsToGitHub', () => {
const githubBase = 'https://raw.githubusercontent.com/ably/docs/main/src';

Expand Down
34 changes: 27 additions & 7 deletions data/onPostBuild/transpileMdxToMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,22 @@ function removeJsxComments(content: string): string {
return transformNonCodeBlocks(content, (text) => text.replace(/\{\/\*[\s\S]*?\*\/\}/g, ''));
}

/**
* Convert MethodSignature components to inline code
* Handles both simple content and template literal syntax
* <MethodSignature>content</MethodSignature> → `content`
* <MethodSignature>{`content`}</MethodSignature> → `content`
*/
function convertMethodSignatureToCode(content: string): string {
return transformNonCodeBlocks(content, (text) =>
text
// Template literal syntax: <MethodSignature>{`content`}</MethodSignature>
.replace(/<MethodSignature>\{`([^`]*)`\}<\/MethodSignature>/g, '`$1`')
// Simple syntax: <MethodSignature>content</MethodSignature>
.replace(/<MethodSignature>([^<{]+)<\/MethodSignature>/g, '`$1`'),
);
}

/**
* Convert image paths to GitHub raw URLs
* Handles relative (../), absolute (/images/), and direct (images/) paths
Expand Down Expand Up @@ -456,25 +472,28 @@ function transformMdxToMarkdown(
// Stage 5: Remove JSX comments
content = removeJsxComments(content);

// Stage 6: Strip hidden attribute from tables (makes them visible in markdown)
// Stage 6: Convert MethodSignature components to inline code
content = convertMethodSignatureToCode(content);

// Stage 7: Strip hidden attribute from tables (makes them visible in markdown)
content = stripHiddenFromTables(content);

// Stage 7: Convert image paths to GitHub URLs
// Stage 8: Convert image paths to GitHub URLs
content = convertImagePathsToGitHub(content);

// Stage 8: Convert relative URLs to absolute URLs
// Stage 9: Convert relative URLs to absolute URLs
content = convertRelativeUrls(content, siteUrl);

// Stage 9: Convert quoted /docs/ URLs to markdown links (for JSX props like link: '/docs/...')
// Stage 10: Convert quoted /docs/ URLs to markdown links (for JSX props like link: '/docs/...')
content = convertJsxLinkProps(content, siteUrl);

// Stage 10: Convert /docs/ links to .md extension and remove ?lang= params
// Stage 11: Convert /docs/ links to .md extension and remove ?lang= params
content = convertDocsLinksToMarkdown(content);

// Stage 11: Replace template variables
// Stage 12: Replace template variables
content = replaceTemplateVariables(content);

// Stage 12: Prepend title as markdown heading
// Stage 13: Prepend title as markdown heading
const finalContent = `# ${title}\n\n${intro ? `${intro}\n\n` : ''}${content}`;

return { content: finalContent, title, intro };
Expand Down Expand Up @@ -592,6 +611,7 @@ export {
removeScriptTags,
removeAnchorTags,
removeJsxComments,
convertMethodSignatureToCode,
stripHiddenFromTables,
convertImagePathsToGitHub,
convertDocsLinksToMarkdown,
Expand Down
2 changes: 2 additions & 0 deletions src/components/Layout/MDXWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { Table, NestedTableProvider } from './mdx/NestedTable';
import { Tiles } from './mdx/tiles';
import { PageHeader } from './mdx/PageHeader';
import Admonition from './mdx/Admonition';
import { MethodSignature } from './mdx/MethodSignature';

import { Frontmatter, PageContextType } from './Layout';
import { ActivePage } from './utils/nav';
Expand Down Expand Up @@ -285,6 +286,7 @@ const MDXWrapper: React.FC<MDXWrapperProps> = ({ children, pageContext, location
th: Table.Head,
td: Table.Cell,
Tiles,
MethodSignature,
}}
>
<PageHeader title={title} intro={intro} />
Expand Down
38 changes: 38 additions & 0 deletions src/components/Layout/mdx/MethodSignature.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React from 'react';
import cn from '@ably/ui/core/utils/cn';

interface MethodSignatureProps {
children: React.ReactNode;
className?: string;
}

/**
* A component for displaying method signatures prominently in API reference docs.
* Uses orange styling consistent with NestedTable property names.
* Features an L-shaped connector line linking to the parent header.
*
* Note: Must be placed immediately after an h2/h3 heading with standard margins.
* The connector line positioning assumes this layout relationship.
*
* Usage in MDX:
* <MethodSignature>rooms.get(name, options)</MethodSignature>
*
* For signatures containing special characters like < > { }, use a template literal:
* <MethodSignature>{`rooms.get<RoomOptions>(name, options)`}</MethodSignature>
*/
export const MethodSignature: React.FC<MethodSignatureProps> = ({ children, className }) => {
return (
<div className="relative pl-3 my-4">
{/* L-shaped connector line */}
<div className="absolute left-0 -top-8 h-[calc(50%+2rem)] w-3 border-l border-b border-orange-300 dark:border-orange-900 rounded-bl-md" />
<code
className={cn(
'bg-orange-100 dark:bg-orange-1000 border border-orange-300 dark:border-orange-900 px-3 py-1.5 rounded text-sm font-mono text-neutral-1000 dark:text-neutral-300 inline-block',
className,
)}
>
{children}
</code>
</div>
);
};
137 changes: 137 additions & 0 deletions src/pages/docs/chat/api/chat-client.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
---
title: ChatClient
meta_description: "API reference for the ChatClient class in the Ably Chat JavaScript SDK."
meta_keywords: "Ably Chat SDK, JavaScript, ChatClient API, constructor, dispose, rooms, connection, clientId, realtime"
---

The `ChatClient` class is the main entry point for using the Ably Chat SDK. It provides access to chat rooms, connection management, and the underlying Ably Realtime client.

## Properties

The `ChatClient` interface has the following properties:

<Table id='ChatClientProperties'>

| Property | Description | Type |
| --- | --- | --- |
| rooms | The rooms object, used to get or create chat room instances. | [Rooms](/docs/chat/api/javascript/rooms) |
| connection | The connection object, used to monitor the connection status with Ably. | [Connection](/docs/chat/api/javascript/connection) |
| clientId | The client ID configured on the underlying Ably Realtime client. Used to identify the current user. May be `undefined` until authenticated with a token. | String or Undefined |
| realtime | The underlying Ably Realtime client instance. | Ably.Realtime |
| clientOptions | The resolved configuration options with defaults applied. | <Table id='ChatClientOptions'/> |

</Table>

<Table id='ChatClientOptions' hidden>

| Property | Required | Description | Type |
| --- | --- | --- | --- |
| logLevel | Optional | The logging level to use. Default: `LogLevel.Error`. | <Table id='LogLevel'/> |
| logHandler | Optional | A custom log handler function that receives log messages from the SDK. | <Table id='LogHandler'/> |

</Table>

<Table id='LogLevel' hidden>

| Value | Description |
| --- | --- |
| Trace | Log all messages. |
| Debug | Log debug, info, warn, and error messages. |
| Info | Log info, warn, and error messages. |
| Warn | Log warn and error messages. |
| Error | Log error messages only. |
| Silent | Disable logging. |

</Table>

<Table id='LogHandler' hidden>

| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| message | Required | The log message. | String |
| level | Required | The severity level of the log message. | <Table id='LogLevel'/> |
| context | Optional | Additional contextual data associated with the log entry. | <Table id='LogContext'/> |

</Table>

<Table id='LogContext' hidden>

`LogContext` is a type alias for `Record<string, any>`. It represents an object of key-value pairs that can be used to provide additional context to a log message.

</Table>

## Create a new chat client <a id="constructor"/>

<MethodSignature>{`new ChatClient(realtime: Realtime, clientOptions?: ChatClientOptions)`}</MethodSignature>

Create a new `ChatClient` instance by passing an Ably Realtime client and optional configuration options.

<Code>
```javascript
import * as Ably from 'ably';
import { ChatClient } from '@ably/chat';

const realtimeClient = new Ably.Realtime({
key: 'your-api-key',
clientId: 'user-123'
});

const chatClient = new ChatClient(realtimeClient, clientOptions);
```
</Code>

### Parameters

The `ChatClient()` constructor takes the following parameters:

<Table id='ConstructorParameters'>

| Parameter | Required | Description | Type |
| --- | --- | --- | --- |
| realtime | Required | An instance of the Ably Realtime client, configured with your API key and a `clientId`. The `clientId` is required for all chat operations. | Ably.Realtime |
| clientOptions | Optional | Configuration options for the Chat client. | <Table id='ChatClientOptions'/> |

</Table>

## Dispose of the chat client <a id="dispose"/>

<MethodSignature>{`chatClient.dispose(): Promise<void>`}</MethodSignature>

Disposes of the ChatClient instance and releases all resources, including all chat rooms. After calling this method, the ChatClient instance is no longer usable.

<Code>
```javascript
await chatClient.dispose();
```
</Code>

### Returns

`Promise<void>` - A promise that resolves when the client has been disposed.

## Example

<Code>
```javascript
import * as Ably from 'ably';
import { ChatClient, LogLevel } from '@ably/chat';

const realtimeClient = new Ably.Realtime({
key: 'your-api-key',
clientId: 'user-123'
});

const chatClient = new ChatClient(realtimeClient, {
logLevel: LogLevel.Debug
});

// Access rooms
const room = await chatClient.rooms.get('my-room');

// Check connection status
console.log(chatClient.connection.status);

// Get current user ID
console.log(chatClient.clientId);
```
</Code>