@webspatial/builder 1.0.4 → 1.0.5

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.
@@ -7,6 +7,7 @@ exports.configScope = configScope;
7
7
  exports.configDisplay = configDisplay;
8
8
  exports.configMainScene = configMainScene;
9
9
  exports.configDeeplink = configDeeplink;
10
+ const sceneUtils_1 = require("../utils/sceneUtils");
10
11
  const utils_1 = require("../utils/utils");
11
12
  const validate_1 = require("./validate");
12
13
  const path_1 = require("path");
@@ -139,38 +140,74 @@ function configDisplay(manifestJson) {
139
140
  manifestJson.display = display;
140
141
  }
141
142
  function configMainScene(manifestJson) {
142
- var _a, _b;
143
- const resizabilities = ['minWidth', 'minHeight', 'maxWidth', 'maxHeight'];
143
+ var _a, _b, _c, _d, _e, _f;
144
144
  let mainScene = {
145
- defaultSize: {
146
- width: 1280,
147
- height: 1280,
148
- },
145
+ ...sceneUtils_1.defaultSceneConfig,
149
146
  resizability: {},
147
+ type: 'window',
148
+ worldScaling: 'automatic',
149
+ worldAlignment: 'automatic',
150
+ baseplateVisibility: 'automatic',
150
151
  };
152
+ // set type
153
+ if (((_a = manifestJson.xr_main_scene) === null || _a === void 0 ? void 0 : _a.type) == 'volume') {
154
+ mainScene.type = 'volume';
155
+ }
156
+ // set default value by type
157
+ if (mainScene.type == 'volume') {
158
+ mainScene = {
159
+ ...mainScene,
160
+ ...sceneUtils_1.defaultSceneConfigVolume,
161
+ };
162
+ }
151
163
  let hasResizability = false;
152
164
  if (manifestJson.xr_main_scene &&
153
165
  typeof manifestJson.xr_main_scene === 'object') {
154
- mainScene.defaultSize.width =
155
- Number((_a = manifestJson.xr_main_scene.default_size) === null || _a === void 0 ? void 0 : _a.width) > 0
156
- ? manifestJson.xr_main_scene.default_size.width
157
- : 1280;
158
- mainScene.defaultSize.height =
159
- Number((_b = manifestJson.xr_main_scene.default_size) === null || _b === void 0 ? void 0 : _b.height) > 0
160
- ? manifestJson.xr_main_scene.default_size.height
161
- : 1280;
166
+ let defaultSizeKeys = ['width', 'height', 'depth'];
167
+ for (let k of defaultSizeKeys) {
168
+ if ((0, sceneUtils_1.isValidSceneUnit)((_b = manifestJson.xr_main_scene.default_size) === null || _b === void 0 ? void 0 : _b[k])) {
169
+ ;
170
+ mainScene.defaultSize[k] = (0, sceneUtils_1.formatToNumber)((_c = manifestJson.xr_main_scene.default_size) === null || _c === void 0 ? void 0 : _c[k], mainScene.type === 'window' ? 'px' : 'm', mainScene.type === 'window' ? 'px' : 'm');
171
+ }
172
+ else {
173
+ console.warn('invalid defaultSize type');
174
+ }
175
+ }
162
176
  if (typeof manifestJson.xr_main_scene.resizability === 'object') {
177
+ const resizabilities = ['minWidth', 'minHeight', 'maxWidth', 'maxHeight'];
163
178
  for (var i = 0; i < resizabilities.length; i++) {
164
- if (manifestJson.xr_main_scene.resizability[resizabilities[i]] >= 0) {
179
+ if ((0, sceneUtils_1.isValidSceneUnit)(manifestJson.xr_main_scene.resizability[resizabilities[i]])) {
165
180
  hasResizability = true;
166
- mainScene.resizability[resizabilities[i]] =
167
- manifestJson.xr_main_scene.resizability[resizabilities[i]];
181
+ //@ts-ignore
182
+ mainScene.resizability[resizabilities[i]] = (0, sceneUtils_1.formatToNumber)(manifestJson.xr_main_scene.resizability[resizabilities[i]], 'px', mainScene.type === 'window' ? 'px' : 'm');
183
+ }
184
+ else {
185
+ console.warn('invalid resizability type');
168
186
  }
169
187
  }
170
188
  }
171
189
  }
172
190
  if (!hasResizability) {
173
- mainScene.resizability = null;
191
+ mainScene.resizability = undefined;
192
+ }
193
+ if ((0, sceneUtils_1.isValidWorldScalingType)((_d = manifestJson.xr_main_scene) === null || _d === void 0 ? void 0 : _d.worldScaling)) {
194
+ mainScene.worldScaling = manifestJson.xr_main_scene.worldScaling;
195
+ }
196
+ else {
197
+ console.warn('invalid worldScaling type');
198
+ }
199
+ if ((0, sceneUtils_1.isValidWorldAlignmentType)((_e = manifestJson.xr_main_scene) === null || _e === void 0 ? void 0 : _e.worldAlignment)) {
200
+ mainScene.worldAlignment = manifestJson.xr_main_scene.worldAlignment;
201
+ }
202
+ else {
203
+ console.warn('invalid worldAlignment type');
204
+ }
205
+ if ((0, sceneUtils_1.isValidBaseplateVisibilityType)((_f = manifestJson.xr_main_scene) === null || _f === void 0 ? void 0 : _f.baseplateVisibility)) {
206
+ mainScene.baseplateVisibility =
207
+ manifestJson.xr_main_scene.baseplateVisibility;
208
+ }
209
+ else {
210
+ console.warn('invalid baseplateVisibility type');
174
211
  }
175
212
  manifestJson.xr_main_scene = mainScene;
176
213
  }
@@ -0,0 +1,40 @@
1
+ export interface SpatialSceneCreationOptions {
2
+ defaultSize?: {
3
+ width: number | string;
4
+ height: number | string;
5
+ depth?: number | string;
6
+ };
7
+ resizability?: {
8
+ minWidth?: number | string;
9
+ minHeight?: number | string;
10
+ maxWidth?: number | string;
11
+ maxHeight?: number | string;
12
+ };
13
+ worldScaling?: WorldScalingType;
14
+ worldAlignment?: WorldAlignmentType;
15
+ baseplateVisibility?: BaseplateVisibilityType;
16
+ }
17
+ export declare const BaseplateVisibilityValues: readonly ["automatic", "visible", "hidden"];
18
+ export type BaseplateVisibilityType = (typeof BaseplateVisibilityValues)[number];
19
+ export declare function isValidBaseplateVisibilityType(type: string): Boolean;
20
+ export declare const WorldScalingValues: readonly ["automatic", "dynamic"];
21
+ export type WorldScalingType = (typeof WorldScalingValues)[number];
22
+ export declare function isValidWorldScalingType(type: string): Boolean;
23
+ export declare const WorldAlignmentValues: readonly ["adaptive", "automatic", "gravityAligned"];
24
+ export type WorldAlignmentType = (typeof WorldAlignmentValues)[number];
25
+ export declare function isValidWorldAlignmentType(type: string): Boolean;
26
+ export declare const SpatialSceneValues: readonly ["window", "volume"];
27
+ export type SpatialSceneType = (typeof SpatialSceneValues)[number];
28
+ export declare function isValidSpatialSceneType(type: string): Boolean;
29
+ export type SpatialSceneCreationOptionsInternal = SpatialSceneCreationOptions & {
30
+ type: SpatialSceneType;
31
+ };
32
+ export declare function formatToNumber(str: string | number, targetUnit: 'px' | 'm', defaultUnit: 'px' | 'm'): number;
33
+ export declare function formatSceneConfig(config: SpatialSceneCreationOptions, sceneType: SpatialSceneType): SpatialSceneCreationOptions;
34
+ /**
35
+ * check px,m and number, number must be >= 0
36
+ *
37
+ * */
38
+ export declare function isValidSceneUnit(val: string | number): boolean;
39
+ export declare const defaultSceneConfig: SpatialSceneCreationOptions;
40
+ export declare const defaultSceneConfigVolume: SpatialSceneCreationOptions;
@@ -0,0 +1,158 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultSceneConfigVolume = exports.defaultSceneConfig = exports.SpatialSceneValues = exports.WorldAlignmentValues = exports.WorldScalingValues = exports.BaseplateVisibilityValues = void 0;
4
+ exports.isValidBaseplateVisibilityType = isValidBaseplateVisibilityType;
5
+ exports.isValidWorldScalingType = isValidWorldScalingType;
6
+ exports.isValidWorldAlignmentType = isValidWorldAlignmentType;
7
+ exports.isValidSpatialSceneType = isValidSpatialSceneType;
8
+ exports.formatToNumber = formatToNumber;
9
+ exports.formatSceneConfig = formatSceneConfig;
10
+ exports.isValidSceneUnit = isValidSceneUnit;
11
+ exports.BaseplateVisibilityValues = [
12
+ 'automatic',
13
+ 'visible',
14
+ 'hidden',
15
+ ];
16
+ function isValidBaseplateVisibilityType(type) {
17
+ return exports.BaseplateVisibilityValues.includes(type);
18
+ }
19
+ exports.WorldScalingValues = ['automatic', 'dynamic'];
20
+ function isValidWorldScalingType(type) {
21
+ return exports.WorldScalingValues.includes(type);
22
+ }
23
+ exports.WorldAlignmentValues = [
24
+ 'adaptive',
25
+ 'automatic',
26
+ 'gravityAligned',
27
+ ];
28
+ function isValidWorldAlignmentType(type) {
29
+ return exports.WorldAlignmentValues.includes(type);
30
+ }
31
+ exports.SpatialSceneValues = ['window', 'volume'];
32
+ function isValidSpatialSceneType(type) {
33
+ return exports.SpatialSceneValues.includes(type);
34
+ }
35
+ function pxToMeter(px) {
36
+ return px / 1360;
37
+ }
38
+ function meterToPx(meter) {
39
+ return meter * 1360;
40
+ }
41
+ function formatToNumber(str, targetUnit, defaultUnit) {
42
+ if (typeof str === 'number') {
43
+ if ((defaultUnit === 'px' && targetUnit === 'px') ||
44
+ (defaultUnit === 'm' && targetUnit === 'm')) {
45
+ return str;
46
+ }
47
+ // unit not match target
48
+ if (defaultUnit === 'px' && targetUnit === 'm') {
49
+ return pxToMeter(str);
50
+ }
51
+ else if (defaultUnit === 'm' && targetUnit === 'px') {
52
+ return meterToPx(str);
53
+ }
54
+ // fallback
55
+ return str;
56
+ }
57
+ if (targetUnit === 'm') {
58
+ if (str.endsWith('m')) {
59
+ // 1m
60
+ return Number(str.slice(0, -1));
61
+ }
62
+ else if (str.endsWith('px')) {
63
+ // 100px
64
+ return pxToMeter(Number(str.slice(0, -2)));
65
+ }
66
+ else {
67
+ throw new Error('formatToNumber: invalid str');
68
+ }
69
+ }
70
+ else if (targetUnit === 'px') {
71
+ if (str.endsWith('px')) {
72
+ // 100px
73
+ return Number(str.slice(0, -2));
74
+ }
75
+ else if (str.endsWith('m')) {
76
+ // 1m
77
+ return meterToPx(Number(str.slice(0, -1)));
78
+ }
79
+ else {
80
+ throw new Error('formatToNumber: invalid str');
81
+ }
82
+ }
83
+ else {
84
+ throw new Error('formatToNumber: invalid targetUnit');
85
+ }
86
+ }
87
+ function formatSceneConfig(config, sceneType) {
88
+ // defaultSize and resizability's width/height/depth can be 100 or "100px" or "1m"
89
+ // expect:
90
+ // resizability should format into px
91
+ // defaultSize should format into px if window
92
+ // defaultSize should format into m if volume
93
+ const isWindow = sceneType === 'window';
94
+ // const isVolume = sceneType === 'volume'
95
+ // format resizability
96
+ if (config.resizability) {
97
+ const iterKeys = Object.keys(config.resizability);
98
+ for (let k of iterKeys) {
99
+ if (config.resizability[k]) {
100
+ ;
101
+ config.resizability[k] = formatToNumber(config.resizability[k], 'px', isWindow ? 'px' : 'm');
102
+ }
103
+ }
104
+ }
105
+ // format defaultSize
106
+ if (config.defaultSize) {
107
+ const iterKeys = Object.keys(config.defaultSize);
108
+ for (let k of iterKeys) {
109
+ if (config.defaultSize[k]) {
110
+ ;
111
+ config.defaultSize[k] = formatToNumber(config.defaultSize[k], isWindow ? 'px' : 'm', isWindow ? 'px' : 'm');
112
+ }
113
+ }
114
+ }
115
+ return config;
116
+ }
117
+ /**
118
+ * check px,m and number, number must be >= 0
119
+ *
120
+ * */
121
+ function isValidSceneUnit(val) {
122
+ // only support number or string with unit px or m
123
+ // rpx cm mm not allowed
124
+ if (typeof val === 'number') {
125
+ return val >= 0;
126
+ }
127
+ if (typeof val === 'string') {
128
+ if (val.endsWith('px')) {
129
+ // check if number
130
+ if (isNaN(Number(val.slice(0, -2)))) {
131
+ return false;
132
+ }
133
+ return Number(val.slice(0, -2)) >= 0;
134
+ }
135
+ if (val.endsWith('m')) {
136
+ // check if number
137
+ if (isNaN(Number(val.slice(0, -1)))) {
138
+ return false;
139
+ }
140
+ return Number(val.slice(0, -1)) >= 0;
141
+ }
142
+ }
143
+ return false;
144
+ }
145
+ exports.defaultSceneConfig = {
146
+ defaultSize: {
147
+ width: 1280,
148
+ height: 720,
149
+ depth: 0,
150
+ },
151
+ };
152
+ exports.defaultSceneConfigVolume = {
153
+ defaultSize: {
154
+ width: 0.94,
155
+ height: 0.94,
156
+ depth: 0.94,
157
+ },
158
+ };
@@ -1 +1 @@
1
- export declare const manifestSwiftTemplate = "\nimport Foundation\n\nvar pwaManager = PWAManager()\n\nstruct PWAManager: Codable {\n var isLocal: Bool = false\n var start_url: String = \"START_URL\"\n var scope: String = \"SCOPE\"\n var id: String = \"AppID\"\n\n var name: String = \"AppName\"\n var short_name: String = \"name\"\n var description: String = \"Description\"\n\n var display: PWADisplayMode = .minimal\n var display_override: [PWADisplayMode] = []\n var protocol_handlers: [PWAProtocol] = [PWAProtocol(protocolValue: \"\", url: \"\")]\n var mainScene: WindowContainerOptions = .init(\n defaultSize: .init(\n width: SceneWidth,\n height: SceneHeight\n ),\n resizability: SceneResizability\n )\n private var version: String = \"PACKAGE_VERSION\"\n\n mutating func _init() {\n let urlType = start_url.split(separator: \"://\").first\n if !(urlType == \"http\" || urlType == \"https\") {\n if scope == \"\" || scope == \"/\" {\n scope = \"./\"\n }\n let startUrl = Bundle.main.url(forResource: start_url, withExtension: \"\", subdirectory: \"\")\n start_url = startUrl!.absoluteString\n scope = URL(string: (scope.starts(with: \"/\") ? \".\" : \"./\") + scope, relativeTo: Bundle.main.executableURL)!.absoluteString\n isLocal = true\n }\n\n if display_override.count > 0 {\n display = display_override[0]\n }\n\n for i in 0 ... protocol_handlers.count - 1 {\n let item = protocol_handlers[i]\n protocol_handlers[i].updateUrl(scope + item.url)\n }\n }\n\n func checkInScope(url: String) -> Bool {\n return url.starts(with: scope)\n }\n\n // web+spatial://test\n func checkInDeeplink(url: String) -> String {\n var linkUrl: String = url\n for item in protocol_handlers {\n if linkUrl.starts(with: item.protocolValue) {\n let queryString: String = linkUrl.replacingOccurrences(of: item.protocolValue, with: \"\").addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!\n linkUrl = item.url.replacingOccurrences(of: \"%s\", with: item.protocolValue + queryString)\n }\n }\n logger.debug(linkUrl)\n return linkUrl\n }\n\n func getLocalResourceURL(url: String) -> String {\n let path = String(url.split(separator: \"file://\").first!.split(separator: \"?\").first!)\n let newUrl = URL(string: url)\n let fileManager = FileManager.default\n if fileManager.fileExists(atPath: newUrl!.path) {\n return url\n }\n var resource: String = Bundle.main.url(forResource: newUrl?.path, withExtension: \"\", subdirectory: \"\")?.absoluteString ?? \"\"\n if resource == \"\" {\n resource = Bundle.main.url(forResource: \"static-web\" + path, withExtension: \"\", subdirectory: \"\")?.absoluteString ?? \"\"\n }\n if resource == \"\" {\n return url\n }\n if newUrl?.query() != nil {\n resource += \"?\" + (newUrl?.query())!\n }\n if newUrl?.fragment() != nil {\n resource += \"#\" + (newUrl?.fragment())!\n }\n return resource\n }\n \n func getVersion() -> String {\n return version\n }\n}\n\nenum PWADisplayMode: Codable {\n case minimal\n case standalone\n case fullscreen\n}\n\nstruct PWAProtocol: Codable {\n var protocolValue: String = \"\"\n var url: String = \"\"\n\n mutating func updateUrl(_ str: String) {\n url = str\n }\n}\n\n";
1
+ export declare const manifestSwiftTemplate = "\nimport Foundation\n\nvar pwaManager = PWAManager()\n\nstruct PWAManager: Codable {\n var isLocal: Bool = false\n var start_url: String = \"START_URL\"\n var scope: String = \"SCOPE\"\n var id: String = \"AppID\"\n\n var name: String = \"AppName\"\n var short_name: String = \"name\"\n var description: String = \"Description\"\n\n var display: PWADisplayMode = .minimal\n var display_override: [PWADisplayMode] = []\n var protocol_handlers: [PWAProtocol] = [PWAProtocol(protocolValue: \"\", url: \"\")]\n var mainScene: XSceneOptionsJSB = .init(\n defaultSize: .init(\n width: SceneWidth,\n height: SceneHeight,\n depth: SceneDepth\n ),\n type: .SceneType,\n resizability: SceneResizability,\n worldScaling: .WorldScaling,\n worldAlignment: .WorldAlignment,\n baseplateVisibility: .BaseplateVisibility\n )\n private var version: String = \"PACKAGE_VERSION\"\n\n mutating func _init() {\n let urlType = start_url.split(separator: \"://\").first\n if !(urlType == \"http\" || urlType == \"https\") {\n if scope == \"\" || scope == \"/\" {\n scope = \"./\"\n }\n let startUrl = Bundle.main.url(forResource: start_url, withExtension: \"\", subdirectory: \"\")\n start_url = startUrl!.absoluteString\n scope = URL(string: (scope.starts(with: \"/\") ? \".\" : \"./\") + scope, relativeTo: Bundle.main.executableURL)!.absoluteString\n isLocal = true\n }\n\n if display_override.count > 0 {\n display = display_override[0]\n }\n\n for i in 0 ... protocol_handlers.count - 1 {\n let item = protocol_handlers[i]\n protocol_handlers[i].updateUrl(scope + item.url)\n }\n }\n\n func checkInScope(url: String) -> Bool {\n return url.starts(with: scope)\n }\n\n // web+spatial://test\n func checkInDeeplink(url: String) -> String {\n var linkUrl: String = url\n for item in protocol_handlers {\n if linkUrl.starts(with: item.protocolValue) {\n let queryString: String = linkUrl.replacingOccurrences(of: item.protocolValue, with: \"\").addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!\n linkUrl = item.url.replacingOccurrences(of: \"%s\", with: item.protocolValue + queryString)\n }\n }\n logger.debug(linkUrl)\n return linkUrl\n }\n\n func getLocalResourceURL(url: String) -> String {\n let path = String(url.split(separator: \"file://\").first!.split(separator: \"?\").first!)\n let newUrl = URL(string: url)\n let fileManager = FileManager.default\n if fileManager.fileExists(atPath: newUrl!.path) {\n return url\n }\n var resource: String = Bundle.main.url(forResource: newUrl?.path, withExtension: \"\", subdirectory: \"\")?.absoluteString ?? \"\"\n if resource == \"\" {\n resource = Bundle.main.url(forResource: \"static-web\" + path, withExtension: \"\", subdirectory: \"\")?.absoluteString ?? \"\"\n }\n if resource == \"\" {\n return url\n }\n if newUrl?.query() != nil {\n resource += \"?\" + (newUrl?.query())!\n }\n if newUrl?.fragment() != nil {\n resource += \"#\" + (newUrl?.fragment())!\n }\n return resource\n }\n \n func getVersion() -> String {\n return version\n }\n}\n\nenum PWADisplayMode: Codable {\n case minimal\n case standalone\n case fullscreen\n}\n\nstruct PWAProtocol: Codable {\n var protocolValue: String = \"\"\n var url: String = \"\"\n\n mutating func updateUrl(_ str: String) {\n url = str\n }\n}\n\n";
@@ -19,12 +19,17 @@ struct PWAManager: Codable {
19
19
  var display: PWADisplayMode = .minimal
20
20
  var display_override: [PWADisplayMode] = []
21
21
  var protocol_handlers: [PWAProtocol] = [PWAProtocol(protocolValue: "", url: "")]
22
- var mainScene: WindowContainerOptions = .init(
22
+ var mainScene: XSceneOptionsJSB = .init(
23
23
  defaultSize: .init(
24
24
  width: SceneWidth,
25
- height: SceneHeight
25
+ height: SceneHeight,
26
+ depth: SceneDepth
26
27
  ),
27
- resizability: SceneResizability
28
+ type: .SceneType,
29
+ resizability: SceneResizability,
30
+ worldScaling: .WorldScaling,
31
+ worldAlignment: .WorldAlignment,
32
+ baseplateVisibility: .BaseplateVisibility
28
33
  )
29
34
  private var version: String = "PACKAGE_VERSION"
30
35
 
@@ -6,6 +6,8 @@ export default class XcodeProject {
6
6
  private static updateExportOptions;
7
7
  private static bindManifestInfo;
8
8
  private static updateVersion;
9
+ private static writeInfoPlist;
10
+ private static updateSceneType;
9
11
  private static updateDeeplink;
10
12
  private static modifySwift;
11
13
  }
@@ -50,7 +50,7 @@ const infoPlistXML = `<?xml version="1.0" encoding="UTF-8"?>
50
50
  <key>UIApplicationSceneManifest</key>
51
51
  <dict>
52
52
  <key>UIApplicationPreferredDefaultSceneSessionRole</key>
53
- <string>UIWindowSceneSessionRoleApplication</string>
53
+ <string>SCENE_SESSION_ROLE</string>
54
54
  <key>UIApplicationSupportsMultipleScenes</key>
55
55
  <true/>
56
56
  <key>UISceneConfigurations</key>
@@ -190,30 +190,44 @@ class XcodeProject {
190
190
  fs.writeFileSync((0, path_1.join)(resource_1.PROJECT_BUILD_DIRECTORY, 'ExportOptions.plist'), useExportOptionsXML);
191
191
  }
192
192
  static async bindManifestInfo(xcodeProject, manifest, isDev = false) {
193
- var _a;
193
+ var _a, _b;
194
194
  xcodeProject.updateProductName(manifest.name);
195
195
  // set PRODUCT_BUNDLE_IDENTIFIER need ""
196
196
  if (manifest.id) {
197
197
  xcodeProject.updateBuildProperty('PRODUCT_BUNDLE_IDENTIFIER', `"${manifest.id}"`);
198
198
  }
199
- this.updateDeeplink((_a = manifest.protocol_handlers) !== null && _a !== void 0 ? _a : []);
199
+ let tempInfoPlistXML = infoPlistXML.slice(); // clone
200
+ tempInfoPlistXML = this.updateDeeplink((_a = manifest.protocol_handlers) !== null && _a !== void 0 ? _a : [], tempInfoPlistXML);
201
+ tempInfoPlistXML = this.updateSceneType((_b = manifest.xr_main_scene) === null || _b === void 0 ? void 0 : _b.type, tempInfoPlistXML);
202
+ this.writeInfoPlist(tempInfoPlistXML);
200
203
  await this.modifySwift(manifest);
201
204
  }
202
205
  static updateVersion(xcodeProject, version) {
203
206
  xcodeProject.updateBuildProperty('CURRENT_PROJECT_VERSION', version);
204
207
  }
205
- static updateDeeplink(deeplinks) {
206
- let infoPlistPath = (0, path_1.join)(resource_1.PROJECT_DIRECTORY, './web-spatial/Info.plist');
208
+ static writeInfoPlist(infoPlistXML) {
209
+ const infoPlistPath = (0, path_1.join)(resource_1.PROJECT_DIRECTORY, './web-spatial/Info.plist');
210
+ fs.writeFileSync(infoPlistPath, infoPlistXML);
211
+ }
212
+ static updateSceneType(sceneType, infoPlistXML) {
213
+ let sceneSessionRole = 'UIWindowSceneSessionRoleApplication';
214
+ if (sceneType === 'volume') {
215
+ sceneSessionRole = 'UIWindowSceneSessionRoleVolumetricApplication';
216
+ }
217
+ infoPlistXML = infoPlistXML.replace('SCENE_SESSION_ROLE', sceneSessionRole);
218
+ return infoPlistXML;
219
+ }
220
+ static updateDeeplink(deeplinks, infoPlistXML) {
207
221
  let deeplinkString = '';
208
222
  for (let i = 0; i < deeplinks.length; i++) {
209
223
  deeplinkString += `<string>${deeplinks[i].protocol}</string>`;
210
224
  }
211
225
  const newInfoPlist = infoPlistXML.replace('DEEPLINK', deeplinkString);
212
- fs.writeFileSync(infoPlistPath, newInfoPlist);
226
+ return newInfoPlist;
213
227
  }
214
228
  static async modifySwift(manifest) {
215
229
  var _a;
216
- const manifestSwiftPath = (0, path_1.join)(resource_1.PROJECT_DIRECTORY, './web-spatial/libs/webView/manifest.swift');
230
+ const manifestSwiftPath = (0, path_1.join)(resource_1.PROJECT_DIRECTORY, './web-spatial/manifest.swift');
217
231
  const xcodePackageJsonPath = (0, path_1.join)(resource_1.PROJECT_DIRECTORY, 'package.json');
218
232
  const packageJson = await (0, load_1.loadJsonFromDisk)(xcodePackageJsonPath);
219
233
  let manifestSwift = manifestSwiftTemplate_1.manifestSwiftTemplate;
@@ -233,6 +247,7 @@ class XcodeProject {
233
247
  }
234
248
  manifestSwift = manifestSwift.replace('SceneWidth', manifest.xr_main_scene.defaultSize.width);
235
249
  manifestSwift = manifestSwift.replace('SceneHeight', manifest.xr_main_scene.defaultSize.height);
250
+ manifestSwift = manifestSwift.replace('SceneDepth', manifest.xr_main_scene.defaultSize.depth);
236
251
  let res = 'nil';
237
252
  const resizabilities = ['minWidth', 'minHeight', 'maxWidth', 'maxHeight'];
238
253
  if (manifest.xr_main_scene.resizability) {
@@ -246,6 +261,10 @@ class XcodeProject {
246
261
  res += ')';
247
262
  }
248
263
  manifestSwift = manifestSwift.replace('SceneResizability', res);
264
+ manifestSwift = manifestSwift.replace('SceneType', manifest.xr_main_scene.type);
265
+ manifestSwift = manifestSwift.replace('WorldScaling', manifest.xr_main_scene.worldScaling);
266
+ manifestSwift = manifestSwift.replace('WorldAlignment', manifest.xr_main_scene.worldAlignment);
267
+ manifestSwift = manifestSwift.replace('BaseplateVisibility', manifest.xr_main_scene.baseplateVisibility);
249
268
  fs.writeFileSync(manifestSwiftPath, manifestSwift, 'utf-8');
250
269
  }
251
270
  }
@@ -32,6 +32,7 @@ export default class Xcrun {
32
32
  private static buildTestApp;
33
33
  private static terminateApp;
34
34
  private static installApp;
35
+ private static uninstallApp;
35
36
  private static launchApp;
36
37
  private static parseDestinationDevices;
37
38
  private static parseSupportDevices;
@@ -111,7 +111,7 @@ class Xcrun {
111
111
  // launch visionOS simulator
112
112
  this.launchSimulator(device);
113
113
  try {
114
- this.terminateApp(device.deviceId, appInfo.id);
114
+ this.uninstallApp(device.deviceId, appInfo.id);
115
115
  }
116
116
  catch (_a) { }
117
117
  // install app
@@ -182,6 +182,9 @@ class Xcrun {
182
182
  cmd.install(deviceId, appFile);
183
183
  (0, child_process_1.execSync)(cmd.line);
184
184
  }
185
+ static uninstallApp(deviceId, appId) {
186
+ (0, child_process_1.execSync)(new XcrunCMD().simctl().uninstall(deviceId, appId).line);
187
+ }
185
188
  static launchApp(deviceId, bundleId) {
186
189
  (0, child_process_1.execSync)(new XcrunCMD().simctl().launch(deviceId, bundleId).line);
187
190
  }
@@ -283,15 +286,22 @@ class Xcrun {
283
286
  /*
284
287
  * device info like:
285
288
  * Apple Vision Pro (8C7AD003-4039-478F-9F94-938876D57817) (Shutdown)
289
+ * Apple Vision Pro (xxx) (8C7AD003-4039-478F-9F94-938876D57817) (Shutdown)
286
290
  */
287
291
  static parseDeviceInfo(device) {
288
- const info = device.split('(');
289
- const deviceInfo = {
290
- name: info[0].trim(),
291
- deviceId: info[1].split(')')[0].trim(),
292
- state: info[2].split(')')[0].trim(),
292
+ var _a;
293
+ const uuidRegex = /[A-F0-9]{8}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{4}-[A-F0-9]{12}/i;
294
+ const uuidMatch = (_a = device.match(uuidRegex)) !== null && _a !== void 0 ? _a : ["()"];
295
+ const deviceId = uuidMatch[0];
296
+ const name = device.substring(0, device.indexOf(deviceId) - 1).trim();
297
+ const statePart = device.substring(device.indexOf(deviceId) + deviceId.length).trim();
298
+ const stateMatch = statePart.match(/\(([^)]+)\)/);
299
+ const state = stateMatch ? stateMatch[1].trim() : 'Unknown';
300
+ return {
301
+ name: name,
302
+ deviceId: deviceId,
303
+ state: state
293
304
  };
294
- return deviceInfo;
295
305
  }
296
306
  // Try to find an available simulator, if not, create one and save the running record for the next time direct use.
297
307
  static findSimulator(deviceId) {
@@ -433,6 +443,10 @@ class XcrunCMD {
433
443
  this.line += ` install "${device}" "${path}"`;
434
444
  return this;
435
445
  }
446
+ uninstall(device, packName) {
447
+ this.line += ` uninstall "${device}" "${packName}"`;
448
+ return this;
449
+ }
436
450
  launch(device, packName) {
437
451
  this.line += ` launch "${device}" "${packName}"`;
438
452
  return this;
@@ -0,0 +1,95 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft-07/schema",
3
+ "title": "Schema for PWA Manifest",
4
+ "description": "Schema for PWA Manifest",
5
+ "type": "object",
6
+ "properties": {
7
+ "name": {
8
+ "type": "string"
9
+ },
10
+ "start_url": {
11
+ "type": "string"
12
+ },
13
+ "display": {
14
+ "type": "string"
15
+ },
16
+ "xr_main_scene": {
17
+ "type": "object",
18
+ "properties": {
19
+ "type": {
20
+ "type": "string",
21
+ "enum": ["window", "volume"]
22
+ },
23
+ "default_size": {
24
+ "type": "object",
25
+ "properties": {
26
+ "width": {
27
+ "type": ["number", "string"]
28
+ },
29
+ "height": {
30
+ "type": ["number", "string"]
31
+ },
32
+ "depth": {
33
+ "type": ["number", "string"]
34
+ }
35
+ },
36
+ "required": ["height", "width"]
37
+ },
38
+ "resizability": {
39
+ "type": "object",
40
+ "properties": {
41
+ "minWidth": {
42
+ "type": ["number", "string"]
43
+ },
44
+ "maxWidth": {
45
+ "type": ["number", "string"]
46
+ },
47
+ "minHeight": {
48
+ "type": ["number", "string"]
49
+ },
50
+ "maxHeight": {
51
+ "type": ["number", "string"]
52
+ }
53
+ },
54
+ "required": []
55
+ },
56
+ "worldScaling": {
57
+ "enum": ["automatic", "dynamic"],
58
+ "type": "string"
59
+ },
60
+ "worldAlignment": {
61
+ "enum": ["adaptive", "automatic", "gravityAligned"],
62
+ "type": "string"
63
+ },
64
+ "baseplateVisibility": {
65
+ "enum": ["visible", "hidden", "automatic"],
66
+ "type": "string"
67
+ }
68
+ },
69
+ "required": []
70
+ },
71
+ "icons": {
72
+ "type": "array",
73
+ "items": {
74
+ "type": "object",
75
+ "properties": {
76
+ "src": {
77
+ "type": "string"
78
+ },
79
+ "sizes": {
80
+ "type": "string"
81
+ },
82
+ "type": {
83
+ "type": "string"
84
+ },
85
+ "purpose": {
86
+ "type": "string"
87
+ }
88
+ },
89
+ "required": ["purpose", "sizes", "src", "type"]
90
+ },
91
+ "minItems": 0
92
+ }
93
+ },
94
+ "required": ["display", "icons", "name", "start_url"]
95
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webspatial/builder",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "description": "Client CLI tool to Generate XRApp project for Apple Vision Pro",
5
5
  "type": "commonjs",
6
6
  "engines": {
@@ -19,12 +19,13 @@
19
19
  "dist/assets",
20
20
  "dist/index.d.ts",
21
21
  "dist/index.js",
22
+ "dist/schema.json",
22
23
  "bin",
23
24
  "package.json",
24
25
  "README.md"
25
26
  ],
26
27
  "author": "",
27
- "license": "ISC",
28
+ "license": "MIT",
28
29
  "dependencies": {
29
30
  "@resvg/resvg-js": "^2.6.2",
30
31
  "cli-progress": "^3.8.2",
@@ -40,7 +41,7 @@
40
41
  "sharp": "^0.33.5",
41
42
  "valid-url": "^1.0.9",
42
43
  "xcode": "^3.0.1",
43
- "@webspatial/platform-visionos": "^1.0.4"
44
+ "@webspatial/platform-visionos": "^1.0.5"
44
45
  },
45
46
  "devDependencies": {
46
47
  "@rollup/plugin-terser": "^0.4.3",
@@ -74,7 +75,7 @@
74
75
  "build": "tsc && npm run copy-assets",
75
76
  "build1": "rollup -c ./rollup.config.js",
76
77
  "build2": "tsc && npm run copy-assets",
77
- "copy-assets": "cp -R src/assets/ dist/assets/",
78
+ "copy-assets": "cp -R src/assets/ dist/assets/ && cp -R src/schema.json dist/",
78
79
  "clean": "rm -rf dist",
79
80
  "lint": "eslint \"src/**/*.{js,ts}\""
80
81
  }