Skip to content

Commit c47b970

Browse files
committed
Updating viewport type and documentation
1 parent c3e9d4d commit c47b970

File tree

6 files changed

+266
-102
lines changed

6 files changed

+266
-102
lines changed

specification/draft/apps.mdx

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -456,13 +456,18 @@ interface HostContext {
456456
displayMode?: "inline" | "fullscreen" | "pip";
457457
/** Display modes the host supports */
458458
availableDisplayModes?: string[];
459-
/** Current and maximum dimensions available to the UI */
460-
viewport?: {
461-
width: number;
462-
height: number;
463-
maxHeight?: number;
464-
maxWidth?: number;
465-
};
459+
/**
460+
* Viewport dimensions available to the UI.
461+
*
462+
* The viewport has two independent dimension pairs:
463+
* - Height: Either `height` (fixed) or `maxHeight` (flexible), never both
464+
* - Width: Either `width` (fixed) or `maxWidth` (flexible), never both
465+
*
466+
* Fixed dimensions (height/width): The host controls the size. Set height: 100% (recommended) or use the pixel value directly.
467+
* Flexible dimensions (maxHeight/maxWidth or undefined): The app controls the size, up to the max if specified.
468+
*/
469+
viewport?: ({ height: number } | { maxHeight?: number }) &
470+
({ width: number } | { maxWidth?: number });
466471
/** User's language/region preference (BCP 47, e.g., "en-US") */
467472
locale?: string;
468473
/** User's timezone (IANA, e.g., "America/New_York") */
@@ -508,12 +513,76 @@ Example:
508513
}
509514
},
510515
"displayMode": "inline",
511-
"viewport": { "width": 400, "height": 300 }
516+
"viewport": { "width": 400, "maxHeight": 600 }
512517
}
513518
}
514519
}
515520
```
516521

522+
### Viewport and Sizing
523+
524+
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**.
525+
526+
#### Viewport Modes
527+
528+
| Mode | Viewport Field | Meaning |
529+
|------|---------------|---------|
530+
| Fixed | `height` or `width` | Host controls the size. App should fill the available space. |
531+
| Flexible | `maxHeight` or `maxWidth` | App controls the size, up to the specified maximum. |
532+
| Unbounded | Field omitted | App controls the size with no limit. |
533+
534+
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.
535+
536+
#### App Behavior
537+
538+
Apps should check the viewport configuration and apply appropriate CSS:
539+
540+
```typescript
541+
// In the app's initialization
542+
const viewport = hostContext.viewport;
543+
544+
if (viewport) {
545+
// Handle height
546+
if ("height" in viewport) {
547+
// Fixed height: fill the container
548+
document.body.style.height = "100%";
549+
} else if (viewport.maxHeight) {
550+
// Flexible with max: let content determine size, up to max
551+
document.body.style.maxHeight = `${viewport.maxHeight}px`;
552+
}
553+
// If neither, height is unbounded
554+
555+
// Handle width
556+
if ("width" in viewport) {
557+
// Fixed width: fill the container
558+
document.body.style.width = "100%";
559+
} else if (viewport.maxWidth) {
560+
// Flexible with max: let content determine size, up to max
561+
document.body.style.maxWidth = `${viewport.maxWidth}px`;
562+
}
563+
// If neither, width is unbounded
564+
}
565+
```
566+
567+
#### Host Behavior
568+
569+
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:
570+
571+
```typescript
572+
// Host listens for size changes from the app
573+
bridge.onsizechange = ({ width, height }) => {
574+
// Update iframe to match app's content size
575+
if (width != null) {
576+
iframe.style.width = `${width}px`;
577+
}
578+
if (height != null) {
579+
iframe.style.height = `${height}px`;
580+
}
581+
};
582+
```
583+
584+
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.
585+
517586
### Theming
518587

519588
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)