Skip to content

Commit 3e16a3e

Browse files
committed
Separate viewport and containerDimensions
1 parent 2eb8e31 commit 3e16a3e

File tree

6 files changed

+320
-110
lines changed

6 files changed

+320
-110
lines changed

specification/draft/apps.mdx

Lines changed: 37 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -456,16 +456,20 @@ 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. */
459+
/** Container dimensions for the iframe. Specify either width or maxWidth, and either height or maxHeight. */
460+
containerDimensions?: (
461+
| { height: number } // If specified, container is fixed at this height
462+
| { maxHeight?: number } // Otherwise, container height is determined by the UI height, up to this maximum height (if defined)
463+
) & (
464+
| { width: number } // If specified, container is fixed at this width
465+
| { maxWidth?: number } // Otherwise, container width is determined by the UI width, up to this maximum width (if defined)
466+
);
467+
/** Host window viewport dimensions */
460468
viewport?: {
461-
/** Viewport width (if fixed). Only pass width or maxWidth, not both. */
469+
/** Window viewport width in pixels. */
462470
width?: number;
463-
/** Viewport height (if fixed). Only pass height or maxHeight, not both. */
471+
/** Window viewport height in pixels. */
464472
height?: number;
465-
/** Maximum available viewport width in pixels (if constrained). */
466-
maxWidth?: number;
467-
/** Maximum available viewport height in pixels (if constrained). */
468-
maxHeight?: number;
469473
};
470474
/** User's language/region preference (BCP 47, e.g., "en-US") */
471475
locale?: string;
@@ -512,20 +516,25 @@ Example:
512516
}
513517
},
514518
"displayMode": "inline",
515-
"viewport": { "width": 400, "maxHeight": 600 }
519+
"containerDimensions": { "width": 400, "maxHeight": 600 }
520+
"viewport": { "width": 1920, "height": 1080 },
516521
}
517522
}
518523
}
519524
```
520525

521-
### Viewport and Sizing
526+
### Viewport and Dimensions
522527

523-
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**.
528+
The `HostContext` provides two separate fields for sizing information:
524529

525-
#### Viewport Modes
530+
- **`containerDimensions`**: The dimensions of the container that holds the app. This controls the actual space the app occupies within the host. Each dimension (height and width) operates independently and can be either **fixed** or **flexible**.
526531

527-
| Mode | Viewport Field | Meaning |
528-
|------|---------------|---------|
532+
- **`viewport`**: The host window's dimensions (e.g., `window.innerWidth` and `window.innerHeight`). Apps can use this to make responsive layout decisions based on the overall screen size.
533+
534+
#### Dimension Modes
535+
536+
| Mode | Dimensions Field | Meaning |
537+
|------|-----------------|---------|
529538
| Fixed | `height` or `width` | Host controls the size. App should fill the available space. |
530539
| Flexible | `maxHeight` or `maxWidth` | App controls the size, up to the specified maximum. |
531540
| Unbounded | Field omitted | App controls the size with no limit. |
@@ -534,33 +543,39 @@ These modes can be combined independently. For example, a host might specify a f
534543

535544
#### App Behavior
536545

537-
Apps should check the viewport configuration and apply appropriate CSS:
546+
Apps should check the containerDimensions configuration and apply appropriate CSS:
538547

539548
```typescript
540549
// In the app's initialization
541-
const viewport = hostContext.viewport;
550+
const containerDimensions = hostContext.containerDimensions;
542551

543-
if (viewport) {
552+
if (containerDimensions) {
544553
// Handle height
545-
if ("height" in viewport) {
554+
if ("height" in containerDimensions) {
546555
// Fixed height: fill the container
547556
document.documentElement.style.height = "100vh";
548-
} else if (viewport.maxHeight) {
557+
} else if ("maxHeight" in containerDimensions && containerDimensions.maxHeight) {
549558
// Flexible with max: let content determine size, up to max
550-
document.documentElement.style.maxHeight = `${viewport.maxHeight}px`;
559+
document.documentElement.style.maxHeight = `${containerDimensions.maxHeight}px`;
551560
}
552561
// If neither, height is unbounded
553562

554563
// Handle width
555-
if ("width" in viewport) {
564+
if ("width" in containerDimensions) {
556565
// Fixed width: fill the container
557566
document.documentElement.style.width = "100vw";
558-
} else if (viewport.maxWidth) {
567+
} else if ("maxWidth" in containerDimensions && containerDimensions.maxWidth) {
559568
// Flexible with max: let content determine size, up to max
560-
document.documentElement.style.maxWidth = `${viewport.maxWidth}px`;
569+
document.documentElement.style.maxWidth = `${containerDimensions.maxWidth}px`;
561570
}
562571
// If neither, width is unbounded
563572
}
573+
574+
// Apps can also use viewport for additional data to make responsive layout decisions
575+
const viewport = hostContext.viewport;
576+
if (viewport?.width && viewport.width < 768) {
577+
// Apply mobile-friendly layout
578+
}
564579
```
565580

566581
#### Host Behavior

src/app-bridge.test.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,8 @@ describe("App <-> AppBridge integration", () => {
113113
const testHostContext = {
114114
theme: "dark" as const,
115115
locale: "en-US",
116-
viewport: { width: 800, height: 600, maxHeight: 600 },
116+
viewport: { width: 800, height: 600 },
117+
containerDimensions: { width: 800, maxHeight: 600 },
117118
};
118119
const newBridge = new AppBridge(
119120
createMockClient() as Client,
@@ -337,7 +338,8 @@ describe("App <-> AppBridge integration", () => {
337338
const initialContext = {
338339
theme: "light" as const,
339340
locale: "en-US",
340-
viewport: { width: 800, height: 600, maxHeight: 600 },
341+
viewport: { width: 800, height: 600 },
342+
containerDimensions: { width: 800, maxHeight: 600 },
341343
};
342344
const newBridge = new AppBridge(
343345
createMockClient() as Client,
@@ -354,22 +356,26 @@ describe("App <-> AppBridge integration", () => {
354356
newBridge.sendHostContextChange({ theme: "dark" });
355357
await flush();
356358

357-
// Send another partial update: only viewport changes
359+
// Send another partial update: only viewport and containerDimensions change
358360
newBridge.sendHostContextChange({
359-
viewport: { width: 1024, height: 768, maxHeight: 768 },
361+
viewport: { width: 1024, height: 768 },
362+
containerDimensions: { width: 1024, maxHeight: 768 },
360363
});
361364
await flush();
362365

363366
// getHostContext should have accumulated all updates:
364367
// - locale from initial (unchanged)
365368
// - theme from first partial update
366-
// - viewport from second partial update
369+
// - viewport and containerDimensions from second partial update
367370
const context = newApp.getHostContext();
368371
expect(context?.theme).toBe("dark");
369372
expect(context?.locale).toBe("en-US");
370373
expect(context?.viewport).toEqual({
371374
width: 1024,
372375
height: 768,
376+
});
377+
expect(context?.containerDimensions).toEqual({
378+
width: 1024,
373379
maxHeight: 768,
374380
});
375381

src/app-bridge.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,8 @@ export class AppBridge extends Protocol<
10081008
* ```typescript
10091009
* bridge.setHostContext({
10101010
* theme: "dark",
1011-
* viewport: { width: 800, maxHeight: 600 }
1011+
* viewport: { width: 800, height: 600 },
1012+
* containerDimensions: { maxHeight: 600, width: 800 }
10121013
* });
10131014
* ```
10141015
*

0 commit comments

Comments
 (0)