@mpgd/target-config 0.11.0 → 0.12.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.
- package/dist/effective.js +3 -0
- package/dist/runtime.js +20 -7
- package/dist/viewport.d.ts +46 -0
- package/dist/viewport.js +90 -0
- package/package.json +4 -4
- package/targets.json +4 -4
package/dist/effective.js
CHANGED
|
@@ -126,6 +126,9 @@ function effectiveProductReason(runtime, targetEnabled, product, platformProduct
|
|
|
126
126
|
if (runtime === 'verse8-web' && product.grant.type === 'resource') {
|
|
127
127
|
return 'capability-unsupported';
|
|
128
128
|
}
|
|
129
|
+
if (runtime === 'microsoft-store-pwa' && product.type !== 'consumable') {
|
|
130
|
+
return 'capability-unsupported';
|
|
131
|
+
}
|
|
129
132
|
return effectiveItemReason(targetEnabled, platformProductId);
|
|
130
133
|
}
|
|
131
134
|
function createEffectiveAdPlacementConfig(target, config, placement) {
|
package/dist/runtime.js
CHANGED
|
@@ -111,6 +111,7 @@ export function applyTargetConfigToCapabilities(capabilities, config) {
|
|
|
111
111
|
rewardedAds: capabilities.rewardedAds && config.features.rewardedAds,
|
|
112
112
|
interstitialAds: capabilities.interstitialAds && config.features.interstitialAds,
|
|
113
113
|
nativeLeaderboard: capabilities.nativeLeaderboard && config.features.leaderboard,
|
|
114
|
+
remoteLeaderboard: capabilities.remoteLeaderboard && config.features.leaderboard,
|
|
114
115
|
localizedContent: capabilities.localizedContent && config.features.localization,
|
|
115
116
|
};
|
|
116
117
|
}
|
|
@@ -206,6 +207,13 @@ export function withTargetAvailability(gateway, config, options = {}) {
|
|
|
206
207
|
};
|
|
207
208
|
const notifications = notificationsAvailable ? gatewayNotifications : undefined;
|
|
208
209
|
const isIapAvailable = async () => (availabilityConfig.features.iap && (await getGatewayCapabilities()).nativeIap);
|
|
210
|
+
const isLeaderboardAvailable = async () => {
|
|
211
|
+
if (!availabilityConfig.features.leaderboard) {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
const capabilities = await getGatewayCapabilities();
|
|
215
|
+
return capabilities.nativeLeaderboard || capabilities.remoteLeaderboard;
|
|
216
|
+
};
|
|
209
217
|
const isAdPlacementAllowed = (placementId, expectedType) => {
|
|
210
218
|
const actualType = options.resolveAdPlacementType?.(placementId);
|
|
211
219
|
if (actualType !== undefined && actualType !== expectedType) {
|
|
@@ -303,16 +311,21 @@ export function withTargetAvailability(gateway, config, options = {}) {
|
|
|
303
311
|
return gateway.ads.showInterstitial(input);
|
|
304
312
|
},
|
|
305
313
|
},
|
|
306
|
-
leaderboard:
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
async submitScore() {
|
|
314
|
+
leaderboard: {
|
|
315
|
+
async submitScore(input) {
|
|
316
|
+
if (!await isLeaderboardAvailable()) {
|
|
310
317
|
return {
|
|
311
318
|
submitted: false,
|
|
312
319
|
};
|
|
313
|
-
}
|
|
314
|
-
|
|
320
|
+
}
|
|
321
|
+
return gateway.leaderboard.submitScore(input);
|
|
315
322
|
},
|
|
323
|
+
async open(input) {
|
|
324
|
+
if (await isLeaderboardAvailable()) {
|
|
325
|
+
await gateway.leaderboard.open(input);
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
},
|
|
316
329
|
};
|
|
317
330
|
}
|
|
318
331
|
function resolveAvailabilityConfig(config, effectiveConfig) {
|
|
@@ -335,7 +348,7 @@ function isFeatureCapabilitySupported(feature, capabilities) {
|
|
|
335
348
|
case 'interstitialAds':
|
|
336
349
|
return capabilities.interstitialAds;
|
|
337
350
|
case 'leaderboard':
|
|
338
|
-
return capabilities.nativeLeaderboard;
|
|
351
|
+
return capabilities.nativeLeaderboard || capabilities.remoteLeaderboard;
|
|
339
352
|
case 'localization':
|
|
340
353
|
return capabilities.localizedContent;
|
|
341
354
|
}
|
package/dist/viewport.d.ts
CHANGED
|
@@ -15,6 +15,10 @@ export type TargetViewportMeasurementSource = 'container' | 'visual-viewport' |
|
|
|
15
15
|
export type TargetViewportControlPlacement = 'bottom' | 'side';
|
|
16
16
|
/** Recommended placement for secondary game panels. */
|
|
17
17
|
export type TargetViewportPanelPlacement = 'below' | 'side' | 'drawer';
|
|
18
|
+
/** Adaptive shell arrangement for a game surface and its surrounding UI. */
|
|
19
|
+
export type TargetViewportCompositionMode = 'side-rails' | 'bottom-controls' | 'compact-portrait';
|
|
20
|
+
/** Optional expanded-screen arrangement requested by a game. */
|
|
21
|
+
export type TargetViewportExpandedLayout = 'none' | 'side-rails';
|
|
18
22
|
/** Width thresholds used to classify target viewports. */
|
|
19
23
|
export interface TargetViewportBreakpoints {
|
|
20
24
|
readonly compactMaxWidth: number;
|
|
@@ -109,6 +113,36 @@ export interface TargetViewportSnapshot extends TargetViewportPlan {
|
|
|
109
113
|
readonly safeArea: TargetViewportSafeArea;
|
|
110
114
|
}
|
|
111
115
|
/**
|
|
116
|
+
* Input for resolving a concrete adaptive game shell from an existing viewport
|
|
117
|
+
* snapshot. The aspect ratio is expressed as width divided by height.
|
|
118
|
+
*/
|
|
119
|
+
export interface TargetViewportCompositionInput {
|
|
120
|
+
readonly viewport: Pick<TargetViewportSnapshot, 'layout' | 'safeArea'>;
|
|
121
|
+
readonly gameAspectRatio: number;
|
|
122
|
+
readonly expandedLayout?: TargetViewportExpandedLayout;
|
|
123
|
+
readonly minRailWidth?: number;
|
|
124
|
+
}
|
|
125
|
+
interface TargetViewportCompositionBounds {
|
|
126
|
+
readonly contentBounds: TargetViewportBounds;
|
|
127
|
+
readonly gameBounds: TargetViewportBounds;
|
|
128
|
+
}
|
|
129
|
+
/** Concrete safe-area-relative geometry for an adaptive game shell. */
|
|
130
|
+
export type TargetViewportComposition = TargetViewportCompositionBounds & ({
|
|
131
|
+
readonly mode: 'side-rails';
|
|
132
|
+
readonly leftRailBounds: TargetViewportBounds;
|
|
133
|
+
readonly rightRailBounds: TargetViewportBounds;
|
|
134
|
+
readonly primaryControls: 'side';
|
|
135
|
+
readonly secondaryPanels: 'side';
|
|
136
|
+
} | {
|
|
137
|
+
readonly mode: 'bottom-controls';
|
|
138
|
+
readonly primaryControls: 'bottom';
|
|
139
|
+
readonly secondaryPanels: 'below';
|
|
140
|
+
} | {
|
|
141
|
+
readonly mode: 'compact-portrait';
|
|
142
|
+
readonly primaryControls: 'bottom';
|
|
143
|
+
readonly secondaryPanels: 'drawer';
|
|
144
|
+
});
|
|
145
|
+
/**
|
|
112
146
|
* The minimal browser style surface needed to read CSS safe-area custom
|
|
113
147
|
* properties. Keeping this structural avoids a runtime DOM dependency in the
|
|
114
148
|
* target-config package.
|
|
@@ -151,6 +185,8 @@ export declare const defaultTargetViewportSafeAreaCssVariables: {
|
|
|
151
185
|
readonly bottom: '--mpgd-safe-area-bottom';
|
|
152
186
|
readonly left: '--mpgd-safe-area-left';
|
|
153
187
|
};
|
|
188
|
+
/** Default minimum width for each expanded-layout side rail. */
|
|
189
|
+
export declare const defaultTargetViewportCompositionMinRailWidth = 160;
|
|
154
190
|
/** Map a configured target runtime to its viewport host shell. */
|
|
155
191
|
export declare function targetViewportShellForRuntime(runtime: TargetRuntimeKind): TargetViewportShell;
|
|
156
192
|
/** Resolve a viewport shell from the runtime field of a target config. */
|
|
@@ -171,6 +207,15 @@ export declare function resolveTargetViewportPlan(input: TargetViewportInput, br
|
|
|
171
207
|
*/
|
|
172
208
|
export declare function resolveTargetViewportSnapshot(input: TargetViewportSnapshotInput, breakpoints?: TargetViewportBreakpoints): TargetViewportSnapshot;
|
|
173
209
|
/**
|
|
210
|
+
* Resolve an adaptive game shell inside the snapshot's safe content bounds.
|
|
211
|
+
*
|
|
212
|
+
* A landscape viewport uses side rails only when the requested game aspect and
|
|
213
|
+
* both minimum rails fit at full content height. Portrait viewports remain
|
|
214
|
+
* full-bleed so phone shells do not inherit desktop letterboxing. Intermediate
|
|
215
|
+
* layouts keep the full game bounds and move persistent controls to the bottom.
|
|
216
|
+
*/
|
|
217
|
+
export declare function resolveTargetViewportComposition(input: TargetViewportCompositionInput): TargetViewportComposition;
|
|
218
|
+
/**
|
|
174
219
|
* Resolve safe-area insets and content bounds for a given layout.
|
|
175
220
|
*
|
|
176
221
|
* Insets are clamped sequentially (top before bottom, left before right) so
|
|
@@ -185,3 +230,4 @@ export declare function resolveTargetViewportSafeArea(layout: Pick<TargetViewpor
|
|
|
185
230
|
* the same full-viewport layout as before.
|
|
186
231
|
*/
|
|
187
232
|
export declare function readTargetViewportSafeAreaInsets(style: TargetViewportComputedStyle, variables?: TargetViewportSafeAreaCssVariables): TargetViewportSafeAreaInsets;
|
|
233
|
+
export {};
|
package/dist/viewport.js
CHANGED
|
@@ -26,6 +26,8 @@ export const defaultTargetViewportSafeAreaCssVariables = {
|
|
|
26
26
|
bottom: '--mpgd-safe-area-bottom',
|
|
27
27
|
left: '--mpgd-safe-area-left',
|
|
28
28
|
};
|
|
29
|
+
/** Default minimum width for each expanded-layout side rail. */
|
|
30
|
+
export const defaultTargetViewportCompositionMinRailWidth = 160;
|
|
29
31
|
const targetViewportOrientationPolicyModeDescriptors = {
|
|
30
32
|
responsive: {
|
|
31
33
|
preferredOrientation: undefined,
|
|
@@ -147,6 +149,80 @@ export function resolveTargetViewportSnapshot(input, breakpoints = defaultTarget
|
|
|
147
149
|
safeArea: resolveTargetViewportSafeArea(plan.layout, input.safeAreaInsets),
|
|
148
150
|
};
|
|
149
151
|
}
|
|
152
|
+
/**
|
|
153
|
+
* Resolve an adaptive game shell inside the snapshot's safe content bounds.
|
|
154
|
+
*
|
|
155
|
+
* A landscape viewport uses side rails only when the requested game aspect and
|
|
156
|
+
* both minimum rails fit at full content height. Portrait viewports remain
|
|
157
|
+
* full-bleed so phone shells do not inherit desktop letterboxing. Intermediate
|
|
158
|
+
* layouts keep the full game bounds and move persistent controls to the bottom.
|
|
159
|
+
*/
|
|
160
|
+
export function resolveTargetViewportComposition(input) {
|
|
161
|
+
const gameAspectRatio = normalizeViewportAspectRatio(input.gameAspectRatio);
|
|
162
|
+
const minRailWidth = normalizeViewportMinimum(input.minRailWidth ?? defaultTargetViewportCompositionMinRailWidth, 'minRailWidth');
|
|
163
|
+
const contentBounds = { ...input.viewport.safeArea.contentBounds };
|
|
164
|
+
const portrait = input.viewport.layout.orientation === 'portrait';
|
|
165
|
+
if (portrait) {
|
|
166
|
+
if (input.viewport.layout.sizeClass === 'compact') {
|
|
167
|
+
return {
|
|
168
|
+
mode: 'compact-portrait',
|
|
169
|
+
contentBounds,
|
|
170
|
+
gameBounds: contentBounds,
|
|
171
|
+
primaryControls: 'bottom',
|
|
172
|
+
secondaryPanels: 'drawer',
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
mode: 'bottom-controls',
|
|
177
|
+
contentBounds,
|
|
178
|
+
gameBounds: contentBounds,
|
|
179
|
+
primaryControls: 'bottom',
|
|
180
|
+
secondaryPanels: 'below',
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
const requestedSideRails = input.expandedLayout === 'side-rails';
|
|
184
|
+
const minimumGameWidth = contentBounds.width > 0 && contentBounds.height > 0 ? 1 : 0;
|
|
185
|
+
const desiredGameWidth = Math.max(minimumGameWidth, Math.round(contentBounds.height * gameAspectRatio));
|
|
186
|
+
const fittedGameWidth = Math.min(contentBounds.width, desiredGameWidth);
|
|
187
|
+
const availableRailWidth = (contentBounds.width - fittedGameWidth) / 2;
|
|
188
|
+
if (requestedSideRails &&
|
|
189
|
+
input.viewport.layout.sizeClass === 'expanded' &&
|
|
190
|
+
desiredGameWidth <= contentBounds.width &&
|
|
191
|
+
availableRailWidth >= minRailWidth) {
|
|
192
|
+
const gameBounds = {
|
|
193
|
+
x: contentBounds.x + availableRailWidth,
|
|
194
|
+
y: contentBounds.y,
|
|
195
|
+
width: fittedGameWidth,
|
|
196
|
+
height: contentBounds.height,
|
|
197
|
+
};
|
|
198
|
+
return {
|
|
199
|
+
mode: 'side-rails',
|
|
200
|
+
contentBounds,
|
|
201
|
+
gameBounds,
|
|
202
|
+
leftRailBounds: {
|
|
203
|
+
x: contentBounds.x,
|
|
204
|
+
y: contentBounds.y,
|
|
205
|
+
width: availableRailWidth,
|
|
206
|
+
height: contentBounds.height,
|
|
207
|
+
},
|
|
208
|
+
rightRailBounds: {
|
|
209
|
+
x: gameBounds.x + gameBounds.width,
|
|
210
|
+
y: contentBounds.y,
|
|
211
|
+
width: availableRailWidth,
|
|
212
|
+
height: contentBounds.height,
|
|
213
|
+
},
|
|
214
|
+
primaryControls: 'side',
|
|
215
|
+
secondaryPanels: 'side',
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
mode: 'bottom-controls',
|
|
220
|
+
contentBounds,
|
|
221
|
+
gameBounds: contentBounds,
|
|
222
|
+
primaryControls: 'bottom',
|
|
223
|
+
secondaryPanels: 'below',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
150
226
|
/**
|
|
151
227
|
* Resolve safe-area insets and content bounds for a given layout.
|
|
152
228
|
*
|
|
@@ -246,6 +322,20 @@ function normalizeViewportInset(value, name) {
|
|
|
246
322
|
}
|
|
247
323
|
return Math.round(value);
|
|
248
324
|
}
|
|
325
|
+
/** Validate a non-negative CSS-pixel minimum without discarding fractions. */
|
|
326
|
+
function normalizeViewportMinimum(value, name) {
|
|
327
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
328
|
+
throw new Error(`Viewport ${name} must be a non-negative finite number.`);
|
|
329
|
+
}
|
|
330
|
+
return value;
|
|
331
|
+
}
|
|
332
|
+
/** Validate a finite positive width-to-height aspect ratio. */
|
|
333
|
+
function normalizeViewportAspectRatio(value) {
|
|
334
|
+
if (!Number.isFinite(value) || value <= 0) {
|
|
335
|
+
throw new Error('Viewport gameAspectRatio must be a positive finite number.');
|
|
336
|
+
}
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
249
339
|
/** Parse a non-negative CSS pixel custom-property value. */
|
|
250
340
|
function readTargetViewportCssPixels(style, property) {
|
|
251
341
|
const value = style.getPropertyValue(property).trim();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mpgd/target-config",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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.
|
|
41
|
-
"@mpgd/i18n": "0.5.
|
|
42
|
-
"@mpgd/platform": "0.
|
|
40
|
+
"@mpgd/catalog": "0.6.0",
|
|
41
|
+
"@mpgd/i18n": "0.5.5",
|
|
42
|
+
"@mpgd/platform": "0.9.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"ttsc": "0.18.4",
|
package/targets.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "2026-
|
|
2
|
+
"version": "2026-08-12",
|
|
3
3
|
"targets": {
|
|
4
4
|
"web-preview": {
|
|
5
5
|
"runtime": "web-preview",
|
|
@@ -47,10 +47,10 @@
|
|
|
47
47
|
"microsoft-store": {
|
|
48
48
|
"runtime": "microsoft-store-pwa",
|
|
49
49
|
"features": {
|
|
50
|
-
"iap":
|
|
50
|
+
"iap": true,
|
|
51
51
|
"rewardedAds": false,
|
|
52
52
|
"interstitialAds": false,
|
|
53
|
-
"leaderboard":
|
|
53
|
+
"leaderboard": true,
|
|
54
54
|
"localization": true
|
|
55
55
|
},
|
|
56
56
|
"capabilities": {
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"fallbackLocale": "en"
|
|
62
62
|
},
|
|
63
63
|
"monetization": {
|
|
64
|
-
"iap":
|
|
64
|
+
"iap": true,
|
|
65
65
|
"rewardedAds": false,
|
|
66
66
|
"interstitialAds": false
|
|
67
67
|
},
|