@mpgd/target-config 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,16 +1,26 @@
1
1
  import type { TargetConfig, TargetRuntimeKind } from './runtime.js';
2
+ /** The physical orientation derived from a viewport measurement. */
2
3
  export type TargetViewportOrientation = 'portrait' | 'landscape';
4
+ /** How a game prefers or constrains viewport orientation. */
3
5
  export type TargetViewportOrientationPolicyMode = 'responsive' | 'prefer-landscape' | 'prefer-portrait' | 'lock-landscape' | 'lock-portrait';
6
+ /** Presentation behavior when the measured orientation does not match policy. */
4
7
  export type TargetViewportOrientationMismatchBehavior = 'continue' | 'letterbox' | 'show-rotate-prompt';
8
+ /** Responsive width class used by target-independent game layout. */
5
9
  export type TargetViewportSizeClass = 'compact' | 'medium' | 'expanded';
10
+ /** Browser or embedded host shell that owns the game surface. */
6
11
  export type TargetViewportShell = 'browser' | 'mobile-webview' | 'embedded-webview';
12
+ /** Browser measurement surface used to construct the viewport snapshot. */
7
13
  export type TargetViewportMeasurementSource = 'container' | 'visual-viewport' | 'window' | 'unknown';
14
+ /** Recommended placement for primary game controls. */
8
15
  export type TargetViewportControlPlacement = 'bottom' | 'side';
16
+ /** Recommended placement for secondary game panels. */
9
17
  export type TargetViewportPanelPlacement = 'below' | 'side' | 'drawer';
18
+ /** Width thresholds used to classify target viewports. */
10
19
  export interface TargetViewportBreakpoints {
11
20
  readonly compactMaxWidth: number;
12
21
  readonly expandedMinWidth: number;
13
22
  }
23
+ /** Raw viewport measurement and optional target context. */
14
24
  export interface TargetViewportInput {
15
25
  readonly width: number;
16
26
  readonly height: number;
@@ -18,6 +28,32 @@ export interface TargetViewportInput {
18
28
  readonly source?: TargetViewportMeasurementSource;
19
29
  readonly orientationPolicy?: TargetViewportOrientationPolicy;
20
30
  }
31
+ /**
32
+ * Insets expressed in CSS pixels between a viewport edge and content that can
33
+ * safely receive persistent controls. A target adapter or browser host owns
34
+ * measuring these values; the viewport helpers only normalize their use.
35
+ */
36
+ export interface TargetViewportSafeAreaInsets {
37
+ readonly top: number;
38
+ readonly right: number;
39
+ readonly bottom: number;
40
+ readonly left: number;
41
+ }
42
+ /** A rectangular region expressed in the coordinate space of a viewport. */
43
+ export interface TargetViewportBounds {
44
+ readonly x: number;
45
+ readonly y: number;
46
+ readonly width: number;
47
+ readonly height: number;
48
+ }
49
+ /**
50
+ * An optional safe-area extension for an otherwise ordinary viewport
51
+ * measurement. Omitted values are treated as zero.
52
+ */
53
+ export interface TargetViewportSnapshotInput extends TargetViewportInput {
54
+ readonly safeAreaInsets?: Partial<TargetViewportSafeAreaInsets>;
55
+ }
56
+ /** Normalized viewport geometry and responsive classification. */
21
57
  export interface TargetViewportLayout {
22
58
  readonly width: number;
23
59
  readonly height: number;
@@ -29,15 +65,18 @@ export interface TargetViewportLayout {
29
65
  readonly shell: TargetViewportShell;
30
66
  readonly source: TargetViewportMeasurementSource;
31
67
  }
68
+ /** Target-independent control and panel placement recommendations. */
32
69
  export interface TargetViewportRecommendation {
33
70
  readonly primaryControls: TargetViewportControlPlacement;
34
71
  readonly secondaryPanels: TargetViewportPanelPlacement;
35
72
  readonly safeAreaAware: boolean;
36
73
  }
74
+ /** A game's orientation preference and mismatch behavior. */
37
75
  export interface TargetViewportOrientationPolicy {
38
76
  readonly mode: TargetViewportOrientationPolicyMode;
39
77
  readonly mismatchBehavior?: TargetViewportOrientationMismatchBehavior;
40
78
  }
79
+ /** Resolved orientation state for the current viewport and policy. */
41
80
  export interface TargetViewportOrientationPlan {
42
81
  readonly mode: TargetViewportOrientationPolicyMode;
43
82
  readonly preferredOrientation?: TargetViewportOrientation;
@@ -47,23 +86,102 @@ export interface TargetViewportOrientationPlan {
47
86
  readonly shouldLetterbox: boolean;
48
87
  readonly shouldShowRotatePrompt: boolean;
49
88
  }
89
+ /** Complete responsive layout plan without concrete safe-area geometry. */
50
90
  export interface TargetViewportPlan {
51
91
  readonly layout: TargetViewportLayout;
52
92
  readonly recommendation: TargetViewportRecommendation;
53
93
  readonly orientation: TargetViewportOrientationPlan;
54
94
  }
95
+ /**
96
+ * The effective safe-area insets and the remaining rectangle for a game board
97
+ * or persistent HUD. Insets are clamped to the measured viewport so a malformed
98
+ * host measurement cannot produce a negative-sized content region.
99
+ */
100
+ export interface TargetViewportSafeArea {
101
+ readonly insets: TargetViewportSafeAreaInsets;
102
+ readonly contentBounds: TargetViewportBounds;
103
+ }
104
+ /**
105
+ * A viewport plan enriched with concrete safe-area geometry. It is assignable
106
+ * to `TargetViewportPlan`, so existing scene code can adopt it incrementally.
107
+ */
108
+ export interface TargetViewportSnapshot extends TargetViewportPlan {
109
+ readonly safeArea: TargetViewportSafeArea;
110
+ }
111
+ /**
112
+ * The minimal browser style surface needed to read CSS safe-area custom
113
+ * properties. Keeping this structural avoids a runtime DOM dependency in the
114
+ * target-config package.
115
+ */
116
+ export interface TargetViewportComputedStyle {
117
+ getPropertyValue(property: string): string;
118
+ }
119
+ /** CSS custom-property names used to expose host safe-area insets. */
120
+ export interface TargetViewportSafeAreaCssVariables {
121
+ readonly top: string;
122
+ readonly right: string;
123
+ readonly bottom: string;
124
+ readonly left: string;
125
+ }
126
+ /** Default responsive breakpoints shared by generated game starters. */
55
127
  export declare const defaultTargetViewportBreakpoints: {
56
128
  readonly compactMaxWidth: 599;
57
129
  readonly expandedMinWidth: 900;
58
130
  };
131
+ /** Default policy that keeps games responsive in every orientation. */
59
132
  export declare const defaultTargetViewportOrientationPolicy: {
60
133
  readonly mode: 'responsive';
61
134
  readonly mismatchBehavior: 'continue';
62
135
  };
136
+ /** Zero-inset fallback for hosts without safe-area support. */
137
+ export declare const defaultTargetViewportSafeAreaInsets: {
138
+ readonly top: 0;
139
+ readonly right: 0;
140
+ readonly bottom: 0;
141
+ readonly left: 0;
142
+ };
143
+ /**
144
+ * CSS variables emitted by the Phaser starter. They deliberately mirror the
145
+ * platform `safe-area-inset-*` environment variables while remaining readable
146
+ * from a Phaser/TypeScript layout pass.
147
+ */
148
+ export declare const defaultTargetViewportSafeAreaCssVariables: {
149
+ readonly top: '--mpgd-safe-area-top';
150
+ readonly right: '--mpgd-safe-area-right';
151
+ readonly bottom: '--mpgd-safe-area-bottom';
152
+ readonly left: '--mpgd-safe-area-left';
153
+ };
154
+ /** Map a configured target runtime to its viewport host shell. */
63
155
  export declare function targetViewportShellForRuntime(runtime: TargetRuntimeKind): TargetViewportShell;
156
+ /** Resolve a viewport shell from the runtime field of a target config. */
64
157
  export declare function targetViewportShellForConfig(config: Pick<TargetConfig, 'runtime'>): TargetViewportShell;
158
+ /** Normalize viewport input into responsive layout geometry. */
65
159
  export declare function resolveTargetViewportLayout(input: TargetViewportInput, breakpoints?: TargetViewportBreakpoints): TargetViewportLayout;
160
+ /** Classify a viewport width against normalized responsive breakpoints. */
66
161
  export declare function resolveTargetViewportSizeClass(width: number, breakpoints?: TargetViewportBreakpoints): TargetViewportSizeClass;
162
+ /** Recommend primary-control and secondary-panel placement for a layout. */
67
163
  export declare function resolveTargetViewportRecommendation(layout: TargetViewportLayout): TargetViewportRecommendation;
164
+ /** Resolve orientation mismatch behavior for a measured layout. */
68
165
  export declare function resolveTargetViewportOrientationPlan(layout: Pick<TargetViewportLayout, 'orientation'>, policy?: TargetViewportOrientationPolicy): TargetViewportOrientationPlan;
166
+ /** Build a responsive viewport plan from raw input and breakpoints. */
69
167
  export declare function resolveTargetViewportPlan(input: TargetViewportInput, breakpoints?: TargetViewportBreakpoints): TargetViewportPlan;
168
+ /**
169
+ * Build a target viewport plan together with safe-area geometry for games that
170
+ * render persistent DOM controls or position Phaser HUD elements themselves.
171
+ */
172
+ export declare function resolveTargetViewportSnapshot(input: TargetViewportSnapshotInput, breakpoints?: TargetViewportBreakpoints): TargetViewportSnapshot;
173
+ /**
174
+ * Resolve safe-area insets and content bounds for a given layout.
175
+ *
176
+ * Insets are clamped sequentially (top before bottom, left before right) so
177
+ * malformed host measurements cannot produce a negative-sized content region.
178
+ * When opposing insets exceed a viewport dimension, the first-processed side
179
+ * (top or left) takes priority.
180
+ */
181
+ export declare function resolveTargetViewportSafeArea(layout: Pick<TargetViewportLayout, 'width' | 'height'>, insets?: Partial<TargetViewportSafeAreaInsets>): TargetViewportSafeArea;
182
+ /**
183
+ * Read the starter's computed CSS variables. Unknown or non-pixel values are
184
+ * intentionally treated as zero so browser shells without CSS env support keep
185
+ * the same full-viewport layout as before.
186
+ */
187
+ export declare function readTargetViewportSafeAreaInsets(style: TargetViewportComputedStyle, variables?: TargetViewportSafeAreaCssVariables): TargetViewportSafeAreaInsets;
package/dist/viewport.js CHANGED
@@ -1,11 +1,31 @@
1
+ /** Default responsive breakpoints shared by generated game starters. */
1
2
  export const defaultTargetViewportBreakpoints = {
2
3
  compactMaxWidth: 599,
3
4
  expandedMinWidth: 900,
4
5
  };
6
+ /** Default policy that keeps games responsive in every orientation. */
5
7
  export const defaultTargetViewportOrientationPolicy = {
6
8
  mode: 'responsive',
7
9
  mismatchBehavior: 'continue',
8
10
  };
11
+ /** Zero-inset fallback for hosts without safe-area support. */
12
+ export const defaultTargetViewportSafeAreaInsets = {
13
+ top: 0,
14
+ right: 0,
15
+ bottom: 0,
16
+ left: 0,
17
+ };
18
+ /**
19
+ * CSS variables emitted by the Phaser starter. They deliberately mirror the
20
+ * platform `safe-area-inset-*` environment variables while remaining readable
21
+ * from a Phaser/TypeScript layout pass.
22
+ */
23
+ export const defaultTargetViewportSafeAreaCssVariables = {
24
+ top: '--mpgd-safe-area-top',
25
+ right: '--mpgd-safe-area-right',
26
+ bottom: '--mpgd-safe-area-bottom',
27
+ left: '--mpgd-safe-area-left',
28
+ };
9
29
  const targetViewportOrientationPolicyModeDescriptors = {
10
30
  responsive: {
11
31
  preferredOrientation: undefined,
@@ -33,6 +53,7 @@ const targetViewportOrientationPolicyModeDescriptors = {
33
53
  isLocked: true,
34
54
  },
35
55
  };
56
+ /** Map a configured target runtime to its viewport host shell. */
36
57
  export function targetViewportShellForRuntime(runtime) {
37
58
  switch (runtime) {
38
59
  case 'capacitor-android':
@@ -49,9 +70,11 @@ export function targetViewportShellForRuntime(runtime) {
49
70
  const exhaustive = runtime;
50
71
  return exhaustive;
51
72
  }
73
+ /** Resolve a viewport shell from the runtime field of a target config. */
52
74
  export function targetViewportShellForConfig(config) {
53
75
  return targetViewportShellForRuntime(config.runtime);
54
76
  }
77
+ /** Normalize viewport input into responsive layout geometry. */
55
78
  export function resolveTargetViewportLayout(input, breakpoints = defaultTargetViewportBreakpoints) {
56
79
  const normalizedBreakpoints = normalizeTargetViewportBreakpoints(breakpoints);
57
80
  const width = normalizeViewportDimension(input.width, 'width');
@@ -69,9 +92,11 @@ export function resolveTargetViewportLayout(input, breakpoints = defaultTargetVi
69
92
  source: input.source ?? 'unknown',
70
93
  };
71
94
  }
95
+ /** Classify a viewport width against normalized responsive breakpoints. */
72
96
  export function resolveTargetViewportSizeClass(width, breakpoints = defaultTargetViewportBreakpoints) {
73
97
  return classifyTargetViewportSize(normalizeViewportDimension(width, 'width'), normalizeTargetViewportBreakpoints(breakpoints));
74
98
  }
99
+ /** Recommend primary-control and secondary-panel placement for a layout. */
75
100
  export function resolveTargetViewportRecommendation(layout) {
76
101
  const narrowOrPortrait = layout.sizeClass === 'compact' || layout.orientation === 'portrait';
77
102
  const browserSafeAreaRisk = layout.shell === 'browser' &&
@@ -83,6 +108,7 @@ export function resolveTargetViewportRecommendation(layout) {
83
108
  safeAreaAware: layout.shell !== 'browser' || layout.sizeClass === 'compact' || browserSafeAreaRisk,
84
109
  };
85
110
  }
111
+ /** Resolve orientation mismatch behavior for a measured layout. */
86
112
  export function resolveTargetViewportOrientationPlan(layout, policy = defaultTargetViewportOrientationPolicy) {
87
113
  const normalizedPolicy = normalizeTargetViewportOrientationPolicy(policy);
88
114
  const descriptor = targetViewportOrientationPolicyModeDescriptors[normalizedPolicy.mode];
@@ -100,6 +126,7 @@ export function resolveTargetViewportOrientationPlan(layout, policy = defaultTar
100
126
  shouldShowRotatePrompt: isMismatch && mismatchBehavior === 'show-rotate-prompt',
101
127
  };
102
128
  }
129
+ /** Build a responsive viewport plan from raw input and breakpoints. */
103
130
  export function resolveTargetViewportPlan(input, breakpoints = defaultTargetViewportBreakpoints) {
104
131
  const layout = resolveTargetViewportLayout(input, breakpoints);
105
132
  return {
@@ -108,6 +135,60 @@ export function resolveTargetViewportPlan(input, breakpoints = defaultTargetView
108
135
  orientation: resolveTargetViewportOrientationPlan(layout, input.orientationPolicy),
109
136
  };
110
137
  }
138
+ /**
139
+ * Build a target viewport plan together with safe-area geometry for games that
140
+ * render persistent DOM controls or position Phaser HUD elements themselves.
141
+ */
142
+ export function resolveTargetViewportSnapshot(input, breakpoints = defaultTargetViewportBreakpoints) {
143
+ const plan = resolveTargetViewportPlan(input, breakpoints);
144
+ return {
145
+ ...plan,
146
+ safeArea: resolveTargetViewportSafeArea(plan.layout, input.safeAreaInsets),
147
+ };
148
+ }
149
+ /**
150
+ * Resolve safe-area insets and content bounds for a given layout.
151
+ *
152
+ * Insets are clamped sequentially (top before bottom, left before right) so
153
+ * malformed host measurements cannot produce a negative-sized content region.
154
+ * When opposing insets exceed a viewport dimension, the first-processed side
155
+ * (top or left) takes priority.
156
+ */
157
+ export function resolveTargetViewportSafeArea(layout, insets) {
158
+ const requested = normalizeTargetViewportSafeAreaInsets(insets);
159
+ const top = Math.min(requested.top, layout.height);
160
+ const bottom = Math.min(requested.bottom, layout.height - top);
161
+ const left = Math.min(requested.left, layout.width);
162
+ const right = Math.min(requested.right, layout.width - left);
163
+ return {
164
+ insets: {
165
+ top,
166
+ right,
167
+ bottom,
168
+ left,
169
+ },
170
+ contentBounds: {
171
+ x: left,
172
+ y: top,
173
+ width: layout.width - left - right,
174
+ height: layout.height - top - bottom,
175
+ },
176
+ };
177
+ }
178
+ /**
179
+ * Read the starter's computed CSS variables. Unknown or non-pixel values are
180
+ * intentionally treated as zero so browser shells without CSS env support keep
181
+ * the same full-viewport layout as before.
182
+ */
183
+ export function readTargetViewportSafeAreaInsets(style, variables = defaultTargetViewportSafeAreaCssVariables) {
184
+ return {
185
+ top: readTargetViewportCssPixels(style, variables.top),
186
+ right: readTargetViewportCssPixels(style, variables.right),
187
+ bottom: readTargetViewportCssPixels(style, variables.bottom),
188
+ left: readTargetViewportCssPixels(style, variables.left),
189
+ };
190
+ }
191
+ /** Classify a normalized width against normalized breakpoints. */
111
192
  function classifyTargetViewportSize(width, breakpoints) {
112
193
  if (width <= breakpoints.compactMaxWidth) {
113
194
  return 'compact';
@@ -117,6 +198,7 @@ function classifyTargetViewportSize(width, breakpoints) {
117
198
  }
118
199
  return 'expanded';
119
200
  }
201
+ /** Choose a secondary-panel placement for the resolved layout. */
120
202
  function resolveTargetViewportPanelPlacement(layout) {
121
203
  if (layout.sizeClass === 'compact') {
122
204
  return 'drawer';
@@ -126,6 +208,7 @@ function resolveTargetViewportPanelPlacement(layout) {
126
208
  }
127
209
  return 'side';
128
210
  }
211
+ /** Fill the policy mismatch behavior from its orientation mode. */
129
212
  function normalizeTargetViewportOrientationPolicy(policy) {
130
213
  const descriptor = targetViewportOrientationPolicyModeDescriptors[policy.mode];
131
214
  return {
@@ -134,6 +217,7 @@ function normalizeTargetViewportOrientationPolicy(policy) {
134
217
  (descriptor.isLocked ? 'show-rotate-prompt' : 'continue'),
135
218
  };
136
219
  }
220
+ /** Validate and normalize responsive breakpoint dimensions. */
137
221
  function normalizeTargetViewportBreakpoints(breakpoints) {
138
222
  const compactMaxWidth = normalizeViewportDimension(breakpoints.compactMaxWidth, 'compactMaxWidth');
139
223
  const expandedMinWidth = normalizeViewportDimension(breakpoints.expandedMinWidth, 'expandedMinWidth');
@@ -145,6 +229,29 @@ function normalizeTargetViewportBreakpoints(breakpoints) {
145
229
  expandedMinWidth,
146
230
  };
147
231
  }
232
+ /** Validate every supplied inset and fill omitted sides with zero. */
233
+ function normalizeTargetViewportSafeAreaInsets(insets) {
234
+ return {
235
+ top: normalizeViewportInset(insets?.top ?? 0, 'top'),
236
+ right: normalizeViewportInset(insets?.right ?? 0, 'right'),
237
+ bottom: normalizeViewportInset(insets?.bottom ?? 0, 'bottom'),
238
+ left: normalizeViewportInset(insets?.left ?? 0, 'left'),
239
+ };
240
+ }
241
+ /** Normalize a non-negative safe-area inset to whole CSS pixels. */
242
+ function normalizeViewportInset(value, name) {
243
+ if (!Number.isFinite(value) || value < 0) {
244
+ throw new Error(`Viewport safe-area inset ${name} must be a non-negative finite number.`);
245
+ }
246
+ return Math.round(value);
247
+ }
248
+ /** Parse a non-negative CSS pixel custom-property value. */
249
+ function readTargetViewportCssPixels(style, property) {
250
+ const value = style.getPropertyValue(property).trim();
251
+ const match = /^(\d+(?:\.\d+)?)px$/u.exec(value);
252
+ return match === null ? 0 : Math.round(Number(match[1]));
253
+ }
254
+ /** Normalize a positive viewport dimension to at least one CSS pixel. */
148
255
  function normalizeViewportDimension(value, name) {
149
256
  if (!Number.isFinite(value) || value <= 0) {
150
257
  throw new Error(`Viewport ${name} must be a positive finite number.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mpgd/target-config",
3
- "version": "0.9.0",
3
+ "version": "0.10.0",
4
4
  "description": "Target-specific platform feature availability and effective config helpers for mpgd.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -37,9 +37,9 @@
37
37
  ],
38
38
  "dependencies": {
39
39
  "typia": "13.0.2",
40
- "@mpgd/catalog": "0.5.0",
41
- "@mpgd/i18n": "0.5.3",
42
- "@mpgd/platform": "0.7.0"
40
+ "@mpgd/catalog": "0.5.1",
41
+ "@mpgd/i18n": "0.5.4",
42
+ "@mpgd/platform": "0.8.0"
43
43
  },
44
44
  "devDependencies": {
45
45
  "ttsc": "0.18.4",