Skip to content

Commit 93b9da9

Browse files
committed
Updating viewport type and documentation
1 parent e514e6c commit 93b9da9

6 files changed

Lines changed: 342 additions & 180 deletions

File tree

specification/draft/apps.mdx

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -461,13 +461,18 @@ interface HostContext {
461461
displayMode?: "inline" | "fullscreen" | "pip";
462462
/** Display modes the host supports */
463463
availableDisplayModes?: string[];
464-
/** Current and maximum dimensions available to the UI */
465-
viewport?: {
466-
width: number;
467-
height: number;
468-
maxHeight?: number;
469-
maxWidth?: number;
470-
};
464+
/**
465+
* Viewport dimensions available to the UI.
466+
*
467+
* The viewport has two independent dimension pairs:
468+
* - Height: Either `height` (fixed) or `maxHeight` (flexible), never both
469+
* - Width: Either `width` (fixed) or `maxWidth` (flexible), never both
470+
*
471+
* Fixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.
472+
* Flexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified.
473+
*/
474+
viewport?: ({ height: number } | { maxHeight?: number }) &
475+
({ width: number } | { maxWidth?: number });
471476
/** User's language/region preference (BCP 47, e.g., "en-US") */
472477
locale?: string;
473478
/** User's timezone (IANA, e.g., "America/New_York") */
@@ -516,12 +521,76 @@ Example:
516521
}
517522
},
518523
"displayMode": "inline",
519-
"viewport": { "width": 400, "height": 300 }
524+
"viewport": { "width": 400, "maxHeight": 600 }
520525
}
521526
}
522527
}
523528
```
524529

530+
### Viewport and Sizing
531+
532+
The `viewport` field in `HostContext` communicates sizing constraints between host and app. Each dimension (height and width) operates independently and can be either **fixed** or **flexible**.
533+
534+
#### Viewport Modes
535+
536+
| Mode | Viewport Field | Meaning |
537+
|------|---------------|---------|
538+
| Fixed | `height` or `width` | Host controls the size. App should fill the available space. |
539+
| Flexible | `maxHeight` or `maxWidth` | App controls the size, up to the specified maximum. |
540+
| Unbounded | Field omitted | App controls the size with no limit. |
541+
542+
These modes can be combined independently. For example, a host might specify a fixed width but flexible height, allowing the app to grow vertically based on content.
543+
544+
#### App Behavior
545+
546+
Apps should check the viewport configuration and apply appropriate CSS:
547+
548+
```typescript
549+
// In the app's initialization
550+
const viewport = hostContext.viewport;
551+
552+
if (viewport) {
553+
// Handle height
554+
if ("height" in viewport) {
555+
// Fixed height: fill the container
556+
document.body.style.height = "100%";
557+
} else if (viewport.maxHeight) {
558+
// Flexible with max: let content determine size, up to max
559+
document.body.style.maxHeight = `${viewport.maxHeight}px`;
560+
}
561+
// If neither, height is unbounded
562+
563+
// Handle width
564+
if ("width" in viewport) {
565+
// Fixed width: fill the container
566+
document.body.style.width = "100%";
567+
} else if (viewport.maxWidth) {
568+
// Flexible with max: let content determine size, up to max
569+
document.body.style.maxWidth = `${viewport.maxWidth}px`;
570+
}
571+
// If neither, width is unbounded
572+
}
573+
```
574+
575+
#### Host Behavior
576+
577+
When using flexible dimensions (no fixed `height` or `width`), hosts MUST listen for `ui/notifications/size-changed` notifications from the app and update the iframe dimensions accordingly:
578+
579+
```typescript
580+
// Host listens for size changes from the app
581+
bridge.onsizechange = ({ width, height }) => {
582+
// Update iframe to match app's content size
583+
if (width != null) {
584+
iframe.style.width = `${width}px`;
585+
}
586+
if (height != null) {
587+
iframe.style.height = `${height}px`;
588+
}
589+
};
590+
```
591+
592+
Apps using the SDK automatically send size-changed notifications via ResizeObserver when `autoResize` is enabled (the default). The notifications are debounced and only sent when dimensions actually change.
593+
525594
### Theming
526595

527596
Hosts can optionally pass CSS custom properties via `HostContext.styles.variables` for visual cohesion with the host environment.

src/app-bridge.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ describe("App <-> AppBridge integration", () => {
113113
const testHostContext = {
114114
theme: "dark" as const,
115115
locale: "en-US",
116-
viewport: { width: 800, height: 600 },
116+
viewport: { width: 800, maxHeight: 600 },
117117
};
118118
const newBridge = new AppBridge(
119119
createMockClient() as Client,
@@ -337,7 +337,7 @@ describe("App <-> AppBridge integration", () => {
337337
const initialContext = {
338338
theme: "light" as const,
339339
locale: "en-US",
340-
viewport: { width: 800, height: 600 },
340+
viewport: { width: 800, maxHeight: 600 },
341341
};
342342
const newBridge = new AppBridge(
343343
createMockClient() as Client,
@@ -356,7 +356,7 @@ describe("App <-> AppBridge integration", () => {
356356

357357
// Send another partial update: only viewport changes
358358
newBridge.sendHostContextChange({
359-
viewport: { width: 1024, height: 768 },
359+
viewport: { width: 1024, maxHeight: 768 },
360360
});
361361
await flush();
362362

@@ -367,7 +367,7 @@ describe("App <-> AppBridge integration", () => {
367367
const context = newApp.getHostContext();
368368
expect(context?.theme).toBe("dark");
369369
expect(context?.locale).toBe("en-US");
370-
expect(context?.viewport).toEqual({ width: 1024, height: 768 });
370+
expect(context?.viewport).toEqual({ width: 1024, maxHeight: 768 });
371371

372372
await newAppTransport.close();
373373
await newBridgeTransport.close();

src/app-bridge.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ export class AppBridge extends Protocol<
10081008
* ```typescript
10091009
* bridge.setHostContext({
10101010
* theme: "dark",
1011-
* viewport: { width: 800, height: 600 }
1011+
* viewport: { width: 800, maxHeight: 600 }
10121012
* });
10131013
* ```
10141014
*

src/generated/schema.json

Lines changed: 141 additions & 60 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)