@limrun/ui 0.9.0-rc.14 → 0.9.0-rc.15
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/components/remote-control.d.ts +125 -1
- package/dist/core/device-install/apple/provisioning.d.ts +55 -4
- package/dist/core/device-install/operations/limbuild-client.d.ts +15 -1
- package/dist/core/device-install/storage/browser-storage.d.ts +9 -5
- package/dist/core/device-install/types.d.ts +4 -1
- package/dist/device-install/index.cjs +1 -1
- package/dist/device-install/index.js +70 -64
- package/dist/device-install/react.cjs +1 -1
- package/dist/device-install/react.js +1 -1
- package/dist/device-install-dialog-DY35un0b.js +9 -0
- package/dist/device-install-dialog-chNLeiiL.mjs +2000 -0
- package/dist/device-install-dialog.css +1 -1
- package/dist/hooks/use-device-install.d.ts +5 -1
- package/dist/index.cjs +1 -1
- package/dist/index.js +1286 -1116
- package/dist/use-device-install-BIrl0v-k.js +31 -0
- package/dist/{use-device-install-sDVvby1V.mjs → use-device-install-LGfEdqyM.mjs} +4388 -4271
- package/package.json +4 -2
- package/src/components/device-install/device-install-dialog.css +29 -0
- package/src/components/device-install/device-install-dialog.tsx +91 -30
- package/src/components/remote-control.tsx +535 -5
- package/src/core/device-install/apple/provisioning.test.ts +84 -0
- package/src/core/device-install/apple/provisioning.ts +91 -7
- package/src/core/device-install/operations/limbuild-client.ts +32 -2
- package/src/core/device-install/storage/browser-storage.ts +29 -14
- package/src/core/device-install/types.ts +5 -1
- package/src/hooks/use-device-install.ts +135 -59
- package/dist/device-install-dialog-CjH25hnN.js +0 -2
- package/dist/device-install-dialog-W5Xv9kWL.mjs +0 -443
- package/dist/use-device-install-Y1u6vIBB.js +0 -31
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@limrun/ui",
|
|
3
|
-
"version": "0.9.0-rc.
|
|
3
|
+
"version": "0.9.0-rc.15",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"access": "public"
|
|
6
6
|
},
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
"license": "MIT",
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@types/node-forge": "^1.3.14",
|
|
45
|
+
"@types/qrcode": "^1.5.6",
|
|
45
46
|
"@types/react": "^19.1.12",
|
|
46
47
|
"@types/react-dom": "^19.1.9",
|
|
47
48
|
"@vitejs/plugin-react-swc": "^4.0.1",
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
"dependencies": {
|
|
58
59
|
"@foxt/js-srp": "^0.0.3-patch2",
|
|
59
60
|
"clsx": "^2.1.1",
|
|
60
|
-
"node-forge": "^1.4.0"
|
|
61
|
+
"node-forge": "^1.4.0",
|
|
62
|
+
"qrcode": "^1.5.4"
|
|
61
63
|
}
|
|
62
64
|
}
|
|
@@ -197,6 +197,10 @@
|
|
|
197
197
|
padding: 12px;
|
|
198
198
|
}
|
|
199
199
|
|
|
200
|
+
.lr-device-install__section-panel h4 {
|
|
201
|
+
margin: 0 0 4px;
|
|
202
|
+
}
|
|
203
|
+
|
|
200
204
|
.lr-device-install__grid {
|
|
201
205
|
display: grid;
|
|
202
206
|
gap: 10px;
|
|
@@ -234,6 +238,31 @@
|
|
|
234
238
|
font-size: 13px;
|
|
235
239
|
}
|
|
236
240
|
|
|
241
|
+
.lr-device-install__ota-panel {
|
|
242
|
+
justify-items: start;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.lr-device-install__qr {
|
|
246
|
+
border: 1px solid #e5e7eb;
|
|
247
|
+
border-radius: 12px;
|
|
248
|
+
height: 240px;
|
|
249
|
+
width: 240px;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.lr-device-install__qr-placeholder {
|
|
253
|
+
align-items: center;
|
|
254
|
+
background: #f9fafb;
|
|
255
|
+
border: 1px dashed #d1d5db;
|
|
256
|
+
border-radius: 12px;
|
|
257
|
+
color: #6b7280;
|
|
258
|
+
display: flex;
|
|
259
|
+
height: 240px;
|
|
260
|
+
justify-content: center;
|
|
261
|
+
padding: 16px;
|
|
262
|
+
text-align: center;
|
|
263
|
+
width: 240px;
|
|
264
|
+
}
|
|
265
|
+
|
|
237
266
|
.lr-device-install__checklist {
|
|
238
267
|
border: 1px solid #e5e7eb;
|
|
239
268
|
border-radius: 10px;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useEffect, useId, useState, type ChangeEvent, type ReactNode } from 'react';
|
|
2
2
|
import { clsx } from 'clsx';
|
|
3
|
+
import QRCode from 'qrcode';
|
|
3
4
|
import { useDeviceInstall, type UseDeviceInstallOptions } from '../../hooks/use-device-install';
|
|
4
5
|
import type { DeviceInstallStep, DeviceInstallStepStatus } from '../../core/device-install';
|
|
5
6
|
import './device-install-dialog.css';
|
|
@@ -17,7 +18,7 @@ const steps: Array<{ id: DeviceInstallStep; title: string; description: string }
|
|
|
17
18
|
{
|
|
18
19
|
id: 'build',
|
|
19
20
|
title: 'Build for device',
|
|
20
|
-
description: 'Start the signed iPhone build
|
|
21
|
+
description: 'Start the signed iPhone build for the selected signing mode.',
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
24
|
id: 'connect',
|
|
@@ -27,7 +28,7 @@ const steps: Array<{ id: DeviceInstallStep; title: string; description: string }
|
|
|
27
28
|
{
|
|
28
29
|
id: 'install',
|
|
29
30
|
title: 'Start installation',
|
|
30
|
-
description: '
|
|
31
|
+
description: 'Install the last successful device build.',
|
|
31
32
|
},
|
|
32
33
|
];
|
|
33
34
|
|
|
@@ -43,13 +44,31 @@ export function DeviceInstallDialog({
|
|
|
43
44
|
const [appleAccountName, setAppleAccountName] = useState('');
|
|
44
45
|
const [applePassword, setApplePassword] = useState('');
|
|
45
46
|
const [appleTwoFactorCode, setAppleTwoFactorCode] = useState('');
|
|
47
|
+
const [otaQRCode, setOTAQRCode] = useState<string>();
|
|
46
48
|
const dialogTitleId = useId();
|
|
47
49
|
const deviceInstall = useDeviceInstall(hookOptions);
|
|
50
|
+
const visibleSteps = steps.filter((step) => deviceInstall.signingMode === 'development' || step.id !== 'connect');
|
|
48
51
|
|
|
49
52
|
useEffect(() => {
|
|
50
53
|
setOpenStep(deviceInstall.currentStep);
|
|
51
54
|
}, [deviceInstall.currentStep]);
|
|
52
55
|
|
|
56
|
+
useEffect(() => {
|
|
57
|
+
let cancelled = false;
|
|
58
|
+
if (!deviceInstall.otaInstall?.landingUrl) {
|
|
59
|
+
setOTAQRCode(undefined);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
void QRCode.toDataURL(deviceInstall.otaInstall.landingUrl, { margin: 1, width: 240 }).then((dataUrl) => {
|
|
63
|
+
if (!cancelled) {
|
|
64
|
+
setOTAQRCode(dataUrl);
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
cancelled = true;
|
|
69
|
+
};
|
|
70
|
+
}, [deviceInstall.otaInstall?.landingUrl]);
|
|
71
|
+
|
|
53
72
|
const updateSigningFiles = (field: 'certificateFile' | 'provisioningProfileFile', event: ChangeEvent<HTMLInputElement>) => {
|
|
54
73
|
deviceInstall.setSigningFiles({
|
|
55
74
|
[field]: event.currentTarget.files?.[0],
|
|
@@ -78,7 +97,7 @@ export function DeviceInstallDialog({
|
|
|
78
97
|
<header className="lr-device-install__header">
|
|
79
98
|
<div>
|
|
80
99
|
<h2 id={dialogTitleId}>Install to a real iPhone</h2>
|
|
81
|
-
<p>Prepare signing, build for the registered device,
|
|
100
|
+
<p>Prepare signing, build for the registered device, then install with QR or WebUSB based on signing mode.</p>
|
|
82
101
|
</div>
|
|
83
102
|
<button type="button" className="lr-device-install__icon-button" onClick={() => setOpen(false)}>
|
|
84
103
|
Close
|
|
@@ -88,7 +107,7 @@ export function DeviceInstallDialog({
|
|
|
88
107
|
{deviceInstall.error && <div className="lr-device-install__error">{deviceInstall.error}</div>}
|
|
89
108
|
|
|
90
109
|
<div className="lr-device-install__steps">
|
|
91
|
-
{
|
|
110
|
+
{visibleSteps.map((step, index) => (
|
|
92
111
|
<StepCard
|
|
93
112
|
key={step.id}
|
|
94
113
|
index={index + 1}
|
|
@@ -100,6 +119,31 @@ export function DeviceInstallDialog({
|
|
|
100
119
|
>
|
|
101
120
|
{step.id === 'signing' && (
|
|
102
121
|
<div className="lr-device-install__step-body">
|
|
122
|
+
<div className="lr-device-install__choice-grid">
|
|
123
|
+
<button
|
|
124
|
+
type="button"
|
|
125
|
+
className={clsx(
|
|
126
|
+
'lr-device-install__choice',
|
|
127
|
+
deviceInstall.signingMode === 'development' && 'lr-device-install__choice--active',
|
|
128
|
+
)}
|
|
129
|
+
onClick={() => deviceInstall.setSigningMode('development')}
|
|
130
|
+
>
|
|
131
|
+
<strong>Development + WebUSB</strong>
|
|
132
|
+
<span>Use Apple Development signing and install by pairing the iPhone with this browser.</span>
|
|
133
|
+
</button>
|
|
134
|
+
<button
|
|
135
|
+
type="button"
|
|
136
|
+
className={clsx(
|
|
137
|
+
'lr-device-install__choice',
|
|
138
|
+
deviceInstall.signingMode === 'adhoc' && 'lr-device-install__choice--active',
|
|
139
|
+
)}
|
|
140
|
+
onClick={() => deviceInstall.setSigningMode('adhoc')}
|
|
141
|
+
>
|
|
142
|
+
<strong>Ad Hoc + QR</strong>
|
|
143
|
+
<span>Use registered-device Ad Hoc signing and install from iPhone Camera or Safari.</span>
|
|
144
|
+
</button>
|
|
145
|
+
</div>
|
|
146
|
+
|
|
103
147
|
<div className="lr-device-install__choice-grid">
|
|
104
148
|
<button
|
|
105
149
|
type="button"
|
|
@@ -148,18 +192,6 @@ export function DeviceInstallDialog({
|
|
|
148
192
|
onChange={(event) => setApplePassword(event.currentTarget.value)}
|
|
149
193
|
/>
|
|
150
194
|
</label>
|
|
151
|
-
{!deviceInstall.hasReusableAppleCertificate && (
|
|
152
|
-
<label className="lr-device-install__field">
|
|
153
|
-
<span>Generated .p12 password</span>
|
|
154
|
-
<input
|
|
155
|
-
type="password"
|
|
156
|
-
placeholder="Used when exporting Apple certificate"
|
|
157
|
-
onChange={(event) =>
|
|
158
|
-
deviceInstall.setSigningFiles({ certificatePassword: event.currentTarget.value })
|
|
159
|
-
}
|
|
160
|
-
/>
|
|
161
|
-
</label>
|
|
162
|
-
)}
|
|
163
195
|
</div>
|
|
164
196
|
<div className="lr-device-install__actions">
|
|
165
197
|
<button
|
|
@@ -275,7 +307,9 @@ export function DeviceInstallDialog({
|
|
|
275
307
|
>
|
|
276
308
|
{deviceInstall.appleSigningStatus === 'preparing-assets'
|
|
277
309
|
? 'Preparing signing assets...'
|
|
278
|
-
:
|
|
310
|
+
: deviceInstall.signingMode === 'adhoc'
|
|
311
|
+
? 'Generate Ad Hoc signing assets'
|
|
312
|
+
: 'Generate development signing assets'}
|
|
279
313
|
</button>
|
|
280
314
|
</div>
|
|
281
315
|
)}
|
|
@@ -300,10 +334,10 @@ export function DeviceInstallDialog({
|
|
|
300
334
|
/>
|
|
301
335
|
</label>
|
|
302
336
|
<label className="lr-device-install__field">
|
|
303
|
-
<span>Uploaded .p12 password</span>
|
|
337
|
+
<span>Uploaded .p12 password (optional)</span>
|
|
304
338
|
<input
|
|
305
339
|
type="password"
|
|
306
|
-
placeholder="
|
|
340
|
+
placeholder="Leave empty if the certificate has no password"
|
|
307
341
|
onChange={(event) =>
|
|
308
342
|
deviceInstall.setSigningFiles({ certificatePassword: event.currentTarget.value })
|
|
309
343
|
}
|
|
@@ -405,17 +439,44 @@ export function DeviceInstallDialog({
|
|
|
405
439
|
|
|
406
440
|
{step.id === 'install' && (
|
|
407
441
|
<div className="lr-device-install__step-body">
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
442
|
+
{deviceInstall.signingMode === 'adhoc' ? (
|
|
443
|
+
<div className="lr-device-install__section-panel lr-device-install__ota-panel">
|
|
444
|
+
<div>
|
|
445
|
+
<h4>Install with iPhone Camera</h4>
|
|
446
|
+
<p className="lr-device-install__hint">
|
|
447
|
+
Scan this QR code on a registered iPhone to open an HTTPS install page in Safari.
|
|
448
|
+
</p>
|
|
449
|
+
</div>
|
|
450
|
+
{otaQRCode ? (
|
|
451
|
+
<img className="lr-device-install__qr" src={otaQRCode} alt="Ad Hoc install QR code" />
|
|
452
|
+
) : (
|
|
453
|
+
<div className="lr-device-install__qr-placeholder">Build an Ad Hoc IPA to generate a QR code.</div>
|
|
454
|
+
)}
|
|
455
|
+
{deviceInstall.otaInstall && (
|
|
456
|
+
<label className="lr-device-install__field">
|
|
457
|
+
<span>Install page link</span>
|
|
458
|
+
<input readOnly value={deviceInstall.otaInstall.landingUrl} />
|
|
459
|
+
</label>
|
|
460
|
+
)}
|
|
461
|
+
<p className="lr-device-install__hint">
|
|
462
|
+
Requires HTTPS and an Ad Hoc profile containing this device UDID.
|
|
463
|
+
</p>
|
|
464
|
+
</div>
|
|
465
|
+
) : (
|
|
466
|
+
<>
|
|
467
|
+
<button
|
|
468
|
+
type="button"
|
|
469
|
+
className="lr-device-install__primary"
|
|
470
|
+
disabled={disabled || !deviceInstall.canInstall}
|
|
471
|
+
onClick={() => void deviceInstall.startInstallation()}
|
|
472
|
+
>
|
|
473
|
+
{deviceInstall.busyAction === 'install' ? 'Installing...' : 'Install last build'}
|
|
474
|
+
</button>
|
|
475
|
+
<button type="button" className="lr-device-install__secondary" onClick={deviceInstall.stopRelay}>
|
|
476
|
+
Stop relay
|
|
477
|
+
</button>
|
|
478
|
+
</>
|
|
479
|
+
)}
|
|
419
480
|
</div>
|
|
420
481
|
)}
|
|
421
482
|
</StepCard>
|