@ponydev/dsh-phone 0.3.1 → 0.4.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/AGENTS.md +11 -2
- package/CHANGELOG.md +25 -2
- package/CONTRIBUTING.md +4 -3
- package/README.md +29 -18
- package/cordis.patch.yml +4 -0
- package/docs/DESIGN.md +26 -8
- package/lib/adb.js +2 -2
- package/lib/client.js +363 -5
- package/lib/hos.js +1144 -0
- package/lib/index.js +171 -13
- package/lib/ios.js +327 -0
- package/package.json +11 -7
- package/docs/mockups/device.png +0 -0
- package/docs/mockups/list.png +0 -0
- package/docs/mockups/mockup.html +0 -384
- package/docs/screenshots/01-devices.png +0 -0
- package/docs/screenshots/02-preview.png +0 -0
- package/docs/screenshots/03-overview.png +0 -0
package/lib/client.js
CHANGED
|
@@ -52,6 +52,24 @@ window.__ModuleLoader__.load({
|
|
|
52
52
|
diagnostics: 'adb 诊断',
|
|
53
53
|
quality: '显示清晰度',
|
|
54
54
|
immersiveOn: '沉浸模式',
|
|
55
|
+
addDevice: '添加设备',
|
|
56
|
+
connectAndroid: '连接 Android 设备',
|
|
57
|
+
createIos: '创建 iOS 模拟器',
|
|
58
|
+
createHos: '创建鸿蒙模拟器',
|
|
59
|
+
hosImage: '系统镜像',
|
|
60
|
+
hosStarting: '正在启动鸿蒙模拟器…',
|
|
61
|
+
stopHos: '关闭鸿蒙模拟器',
|
|
62
|
+
deleteHos: '删除鸿蒙模拟器',
|
|
63
|
+
iosName: '名称',
|
|
64
|
+
iosDeviceType: '机型',
|
|
65
|
+
iosRuntime: '系统版本',
|
|
66
|
+
create: '创建',
|
|
67
|
+
booting: '正在启动模拟器…',
|
|
68
|
+
simulator: '模拟器',
|
|
69
|
+
shutdownSimulator: '关闭模拟器',
|
|
70
|
+
deleteSimulator: '删除模拟器',
|
|
71
|
+
confirmDeleteSimulator: '确认删除?',
|
|
72
|
+
idbHint: 'iOS 输入/实时画面需要 idb',
|
|
55
73
|
immersiveOff: '显示操作栏',
|
|
56
74
|
qualityLow: '流畅',
|
|
57
75
|
qualityMedium: '标准',
|
|
@@ -102,6 +120,24 @@ window.__ModuleLoader__.load({
|
|
|
102
120
|
diagnostics: 'adb diagnostics',
|
|
103
121
|
quality: 'Stream quality',
|
|
104
122
|
immersiveOn: 'Immersive mode',
|
|
123
|
+
addDevice: 'Add device',
|
|
124
|
+
connectAndroid: 'Connect Android device',
|
|
125
|
+
createIos: 'Create iOS simulator',
|
|
126
|
+
createHos: 'Create HarmonyOS emulator',
|
|
127
|
+
hosImage: 'System image',
|
|
128
|
+
hosStarting: 'Starting HarmonyOS emulator…',
|
|
129
|
+
stopHos: 'Shut down HarmonyOS emulator',
|
|
130
|
+
deleteHos: 'Delete HarmonyOS emulator',
|
|
131
|
+
iosName: 'Name',
|
|
132
|
+
iosDeviceType: 'Device type',
|
|
133
|
+
iosRuntime: 'Runtime',
|
|
134
|
+
create: 'Create',
|
|
135
|
+
booting: 'Booting simulator…',
|
|
136
|
+
simulator: 'Simulator',
|
|
137
|
+
shutdownSimulator: 'Shut down simulator',
|
|
138
|
+
deleteSimulator: 'Delete simulator',
|
|
139
|
+
confirmDeleteSimulator: 'Confirm delete?',
|
|
140
|
+
idbHint: 'iOS input/streaming requires idb',
|
|
105
141
|
immersiveOff: 'Show controls',
|
|
106
142
|
qualityLow: 'Low',
|
|
107
143
|
qualityMedium: 'Medium',
|
|
@@ -187,6 +223,8 @@ window.__ModuleLoader__.load({
|
|
|
187
223
|
.dph-field{display:flex;gap:8px}
|
|
188
224
|
.dph-input{flex:1;height:32px;border:1px solid var(--dsw-alias-border-l2,#dededf);border-radius:8px;padding:0 10px;font:11.5px ui-monospace,SFMono-Regular,Menlo,monospace;color:var(--dsw-alias-label-primary,#17171a);background:var(--dsw-alias-bg-layer-1,#fff);outline:none}
|
|
189
225
|
.dph-input:focus{border-color:var(--dsw-alias-brand-primary,#3b6cff)}
|
|
226
|
+
.dph-select-input{appearance:none;-webkit-appearance:none;padding-right:24px}
|
|
227
|
+
.dph-label{display:block;font-size:11px;color:var(--dsw-alias-label-tertiary,#9a9aa1);margin:10px 0 4px}
|
|
190
228
|
.dph-primary{height:32px;padding:0 14px;border:0;border-radius:8px;background:var(--dsw-alias-label-primary,#17171a);color:var(--dsw-alias-bg-layer-1,#fff);font-size:12px;cursor:pointer}
|
|
191
229
|
.dph-primary:disabled{opacity:.5;cursor:default}
|
|
192
230
|
.dph-err{color:var(--dsw-alias-state-error-primary,#dc2626);font-size:11.5px;margin-top:8px;word-break:break-word}
|
|
@@ -395,6 +433,17 @@ window.__ModuleLoader__.load({
|
|
|
395
433
|
screenInfo: (serial, options) => unwrap('screen-info', { serial, fresh: Boolean(options && options.fresh) }),
|
|
396
434
|
ping: (serial) => unwrap('ping', { serial }),
|
|
397
435
|
targets: () => unwrap('targets'),
|
|
436
|
+
iosTypes: () => unwrap('ios/types'),
|
|
437
|
+
iosRuntimes: () => unwrap('ios/runtimes'),
|
|
438
|
+
iosCreate: (payload) => unwrap('ios/create', payload),
|
|
439
|
+
iosBoot: (serial) => unwrap('ios/boot', { serial }),
|
|
440
|
+
iosShutdown: (serial) => unwrap('ios/shutdown', { serial }),
|
|
441
|
+
iosDelete: (serial) => unwrap('ios/delete', { serial }),
|
|
442
|
+
hosImages: (deviceType) => unwrap('hos/images', { deviceType }),
|
|
443
|
+
hosCreate: (payload) => unwrap('hos/create', payload),
|
|
444
|
+
hosStart: (serial) => unwrap('hos/start', { serial }),
|
|
445
|
+
hosStop: (serial) => unwrap('hos/stop', { serial }),
|
|
446
|
+
hosDelete: (serial) => unwrap('hos/delete', { serial }),
|
|
398
447
|
removeTarget: (address) => unwrap('targets/remove', { address }),
|
|
399
448
|
shot: async (serial) => {
|
|
400
449
|
const res = await unwrap('shot', { serial })
|
|
@@ -437,7 +486,7 @@ window.__ModuleLoader__.load({
|
|
|
437
486
|
}
|
|
438
487
|
|
|
439
488
|
function kindForAddress(address) {
|
|
440
|
-
if (
|
|
489
|
+
if (/\.ts\.net/.test(address)) return 'tailscale'
|
|
441
490
|
if (/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\./.test(address)) return 'tailscale'
|
|
442
491
|
if (address.includes(':')) return 'wifi'
|
|
443
492
|
return 'usb'
|
|
@@ -462,6 +511,7 @@ window.__ModuleLoader__.load({
|
|
|
462
511
|
if (kind === 'tailscale') return 'Tailscale'
|
|
463
512
|
if (kind === 'wifi') return 'Wi-Fi'
|
|
464
513
|
if (kind === 'emulator') return 'emulator'
|
|
514
|
+
if (kind === 'simulator') return 'Simulator'
|
|
465
515
|
return 'USB'
|
|
466
516
|
}
|
|
467
517
|
|
|
@@ -1104,8 +1154,56 @@ window.__ModuleLoader__.load({
|
|
|
1104
1154
|
}
|
|
1105
1155
|
})
|
|
1106
1156
|
const [disconnectArmed, setDisconnectArmed] = useState(false)
|
|
1157
|
+
const [addMenuOpen, setAddMenuOpen] = useState(false)
|
|
1158
|
+
const [iosCreateOpen, setIosCreateOpen] = useState(false)
|
|
1159
|
+
const [iosTypes, setIosTypes] = useState([])
|
|
1160
|
+
const [iosRuntimes, setIosRuntimes] = useState([])
|
|
1161
|
+
const [iosForm, setIosForm] = useState({ name: '', deviceType: '', runtime: '' })
|
|
1162
|
+
const [iosBusy, setIosBusy] = useState(false)
|
|
1163
|
+
const [simDeleteArmed, setSimDeleteArmed] = useState(false)
|
|
1164
|
+
const [hosCreateOpen, setHosCreateOpen] = useState(false)
|
|
1165
|
+
const [hosImages, setHosImages] = useState([])
|
|
1166
|
+
const [hosForm, setHosForm] = useState({ name: '', osVersion: '' })
|
|
1167
|
+
const [hosBusy, setHosBusy] = useState(false)
|
|
1107
1168
|
const autoSelected = useRef(false)
|
|
1108
1169
|
const { devices, error, reload } = useDeviceList(visible, nonce)
|
|
1170
|
+
useEffect(() => {
|
|
1171
|
+
if (!hosCreateOpen) return undefined
|
|
1172
|
+
let alive = true
|
|
1173
|
+
api
|
|
1174
|
+
.hosImages('phone')
|
|
1175
|
+
.catch(() => ({ images: [] }))
|
|
1176
|
+
.then((value) => {
|
|
1177
|
+
if (!alive) return
|
|
1178
|
+
const images = (Array.isArray(value.images) ? value.images : []).filter((item) => item.downloaded)
|
|
1179
|
+
setHosImages(images)
|
|
1180
|
+
setHosForm((prev) => ({ name: prev.name, osVersion: prev.osVersion || (images[0] && images[0].osVersion) || '' }))
|
|
1181
|
+
})
|
|
1182
|
+
return () => {
|
|
1183
|
+
alive = false
|
|
1184
|
+
}
|
|
1185
|
+
}, [hosCreateOpen])
|
|
1186
|
+
|
|
1187
|
+
useEffect(() => {
|
|
1188
|
+
if (!iosCreateOpen) return undefined
|
|
1189
|
+
let alive = true
|
|
1190
|
+
Promise.all([api.iosTypes().catch(() => ({ types: [] })), api.iosRuntimes().catch(() => ({ runtimes: [] }))]).then(([t, r]) => {
|
|
1191
|
+
if (!alive) return
|
|
1192
|
+
const types = Array.isArray(t.types) ? t.types : []
|
|
1193
|
+
const runtimes = Array.isArray(r.runtimes) ? r.runtimes : []
|
|
1194
|
+
setIosTypes(types)
|
|
1195
|
+
setIosRuntimes(runtimes)
|
|
1196
|
+
setIosForm((prev) => ({
|
|
1197
|
+
name: prev.name,
|
|
1198
|
+
deviceType: prev.deviceType || (types[0] && types[0].identifier) || '',
|
|
1199
|
+
runtime: prev.runtime || (runtimes[0] && runtimes[0].identifier) || '',
|
|
1200
|
+
}))
|
|
1201
|
+
})
|
|
1202
|
+
return () => {
|
|
1203
|
+
alive = false
|
|
1204
|
+
}
|
|
1205
|
+
}, [iosCreateOpen])
|
|
1206
|
+
|
|
1109
1207
|
const quality = selected && QUALITY_ORDER.includes(qualityMap[selected]) ? qualityMap[selected] : 'medium'
|
|
1110
1208
|
const setQualityForSelected = (next) => {
|
|
1111
1209
|
if (!selected) return
|
|
@@ -1161,6 +1259,8 @@ window.__ModuleLoader__.load({
|
|
|
1161
1259
|
const device = rows ? rows.find((item) => item.serial === selected) || null : null
|
|
1162
1260
|
const tabId = tab && tab.id ? tab.id : null
|
|
1163
1261
|
const adbMissing = health && health.adb === null
|
|
1262
|
+
const iosReady = Boolean(health && health.ios && health.ios.available)
|
|
1263
|
+
const hosReady = Boolean(health && health.harmony && health.harmony.emulator && health.harmony.emulator.available)
|
|
1164
1264
|
|
|
1165
1265
|
const connectTo = async (address) => {
|
|
1166
1266
|
setConnecting(true)
|
|
@@ -1218,6 +1318,30 @@ window.__ModuleLoader__.load({
|
|
|
1218
1318
|
})
|
|
1219
1319
|
return
|
|
1220
1320
|
}
|
|
1321
|
+
if (picked && picked.transport === 'emulator' && picked.state !== 'device') {
|
|
1322
|
+
preview.setToast(t('hosStarting'))
|
|
1323
|
+
api
|
|
1324
|
+
.hosStart(picked.serial)
|
|
1325
|
+
.then(() => {
|
|
1326
|
+
rememberDevice(picked.serial)
|
|
1327
|
+
setNonce((value) => value + 1)
|
|
1328
|
+
setSelected(picked.serial)
|
|
1329
|
+
})
|
|
1330
|
+
.catch((err) => preview.setToast(err && err.message ? err.message : 'start failed'))
|
|
1331
|
+
return
|
|
1332
|
+
}
|
|
1333
|
+
if (picked && picked.kind === 'simulator' && picked.state !== 'device') {
|
|
1334
|
+
preview.setToast(t('booting'))
|
|
1335
|
+
api
|
|
1336
|
+
.iosBoot(picked.serial)
|
|
1337
|
+
.then(() => {
|
|
1338
|
+
rememberDevice(picked.serial)
|
|
1339
|
+
setNonce((value) => value + 1)
|
|
1340
|
+
setSelected(picked.serial)
|
|
1341
|
+
})
|
|
1342
|
+
.catch((err) => preview.setToast(err && err.message ? err.message : 'boot failed'))
|
|
1343
|
+
return
|
|
1344
|
+
}
|
|
1221
1345
|
rememberDevice(picked.serial)
|
|
1222
1346
|
setSelected(picked.serial)
|
|
1223
1347
|
}
|
|
@@ -1303,6 +1427,44 @@ window.__ModuleLoader__.load({
|
|
|
1303
1427
|
},
|
|
1304
1428
|
},
|
|
1305
1429
|
{ id: 'copy', icon: 'copy', label: t('copySerial'), run: copySerial },
|
|
1430
|
+
...(device && device.kind === 'simulator'
|
|
1431
|
+
? [
|
|
1432
|
+
{
|
|
1433
|
+
id: 'shutdown-sim',
|
|
1434
|
+
icon: 'power',
|
|
1435
|
+
label: t('shutdownSimulator'),
|
|
1436
|
+
run: () => {
|
|
1437
|
+
api
|
|
1438
|
+
.iosShutdown(device.serial)
|
|
1439
|
+
.catch(() => {})
|
|
1440
|
+
.then(() => {
|
|
1441
|
+
setSelected(null)
|
|
1442
|
+
setNonce((value) => value + 1)
|
|
1443
|
+
})
|
|
1444
|
+
},
|
|
1445
|
+
},
|
|
1446
|
+
{
|
|
1447
|
+
id: 'delete-sim',
|
|
1448
|
+
icon: 'unlink',
|
|
1449
|
+
label: simDeleteArmed ? t('confirmDeleteSimulator') : t('deleteSimulator'),
|
|
1450
|
+
run: () => {
|
|
1451
|
+
if (!simDeleteArmed) {
|
|
1452
|
+
setSimDeleteArmed(true)
|
|
1453
|
+
setTimeout(() => setSimDeleteArmed(false), 3000)
|
|
1454
|
+
return
|
|
1455
|
+
}
|
|
1456
|
+
setSimDeleteArmed(false)
|
|
1457
|
+
api
|
|
1458
|
+
.iosDelete(device.serial)
|
|
1459
|
+
.catch((err) => preview.setToast(err && err.message ? err.message : 'delete failed'))
|
|
1460
|
+
.then(() => {
|
|
1461
|
+
setSelected(null)
|
|
1462
|
+
setNonce((value) => value + 1)
|
|
1463
|
+
})
|
|
1464
|
+
},
|
|
1465
|
+
},
|
|
1466
|
+
]
|
|
1467
|
+
: []),
|
|
1306
1468
|
{
|
|
1307
1469
|
id: 'list',
|
|
1308
1470
|
icon: 'back',
|
|
@@ -1347,7 +1509,7 @@ window.__ModuleLoader__.load({
|
|
|
1347
1509
|
className: 'dph-link',
|
|
1348
1510
|
onClick: () => {
|
|
1349
1511
|
setMenuOpen(false)
|
|
1350
|
-
|
|
1512
|
+
setAddMenuOpen(true)
|
|
1351
1513
|
},
|
|
1352
1514
|
},
|
|
1353
1515
|
'+ ' + t('add'),
|
|
@@ -1406,7 +1568,7 @@ window.__ModuleLoader__.load({
|
|
|
1406
1568
|
h('input', {
|
|
1407
1569
|
className: 'dph-input',
|
|
1408
1570
|
value: address,
|
|
1409
|
-
placeholder: '
|
|
1571
|
+
placeholder: '192.0.2.53:5555',
|
|
1410
1572
|
onChange: (event) => setAddress(event.target.value),
|
|
1411
1573
|
autoFocus: true,
|
|
1412
1574
|
spellCheck: false,
|
|
@@ -1588,7 +1750,7 @@ window.__ModuleLoader__.load({
|
|
|
1588
1750
|
{ className: 'dph-empty' },
|
|
1589
1751
|
h('div', { className: 'dph-empty-icon' }, h(Icon, { name: 'phone', size: 30 })),
|
|
1590
1752
|
h('div', { className: 'dph-empty-title' }, t('empty')),
|
|
1591
|
-
h('button', { type: 'button', className: 'dph-primary', onClick: () =>
|
|
1753
|
+
h('button', { type: 'button', className: 'dph-primary', onClick: () => setAddMenuOpen(true) }, '+ ' + t('addDevice')),
|
|
1592
1754
|
)
|
|
1593
1755
|
: h(DeviceRows, { t, devices: rows, selected, onSelect })
|
|
1594
1756
|
|
|
@@ -1613,7 +1775,7 @@ window.__ModuleLoader__.load({
|
|
|
1613
1775
|
),
|
|
1614
1776
|
h(
|
|
1615
1777
|
'button',
|
|
1616
|
-
{ key: 'add', type: 'button', className: 'dph-ibtn', title: t('
|
|
1778
|
+
{ key: 'add', type: 'button', className: 'dph-ibtn', title: t('addDevice'), onClick: () => setAddMenuOpen(true) },
|
|
1617
1779
|
h(Icon, { name: 'plus' }),
|
|
1618
1780
|
),
|
|
1619
1781
|
]
|
|
@@ -1646,6 +1808,199 @@ window.__ModuleLoader__.load({
|
|
|
1646
1808
|
)
|
|
1647
1809
|
: null
|
|
1648
1810
|
|
|
1811
|
+
const hosCreateSheet = hosCreateOpen
|
|
1812
|
+
? h(
|
|
1813
|
+
'div',
|
|
1814
|
+
null,
|
|
1815
|
+
h('div', {
|
|
1816
|
+
className: 'dph-backdrop',
|
|
1817
|
+
onClick: () => {
|
|
1818
|
+
if (!hosBusy) setHosCreateOpen(false)
|
|
1819
|
+
},
|
|
1820
|
+
}),
|
|
1821
|
+
h(
|
|
1822
|
+
'form',
|
|
1823
|
+
{
|
|
1824
|
+
className: 'dph-connect',
|
|
1825
|
+
style: { maxWidth: 380 },
|
|
1826
|
+
onSubmit: async (event) => {
|
|
1827
|
+
event.preventDefault()
|
|
1828
|
+
if (hosBusy) return
|
|
1829
|
+
setHosBusy(true)
|
|
1830
|
+
try {
|
|
1831
|
+
const created = await api.hosCreate({ name: hosForm.name, osVersion: hosForm.osVersion, deviceType: 'phone' })
|
|
1832
|
+
setHosCreateOpen(false)
|
|
1833
|
+
setNonce((value) => value + 1)
|
|
1834
|
+
if (created && created.serial) {
|
|
1835
|
+
rememberDevice(created.serial)
|
|
1836
|
+
setSelected(created.serial)
|
|
1837
|
+
}
|
|
1838
|
+
} catch (error) {
|
|
1839
|
+
preview.setToast(error && error.message ? error.message : 'create failed')
|
|
1840
|
+
} finally {
|
|
1841
|
+
setHosBusy(false)
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
},
|
|
1845
|
+
h('div', { className: 'ttl' }, t('createHos')),
|
|
1846
|
+
h('div', { className: 'sub' }, 'DevEco Emulator CLI'),
|
|
1847
|
+
h('label', { className: 'dph-label' }, t('iosName')),
|
|
1848
|
+
h('input', {
|
|
1849
|
+
className: 'dph-input',
|
|
1850
|
+
value: hosForm.name,
|
|
1851
|
+
placeholder: 'dsh-phone-hos',
|
|
1852
|
+
onChange: (event) => setHosForm((prev) => ({ ...prev, name: event.target.value })),
|
|
1853
|
+
spellCheck: false,
|
|
1854
|
+
}),
|
|
1855
|
+
h('label', { className: 'dph-label' }, t('hosImage')),
|
|
1856
|
+
h(
|
|
1857
|
+
'select',
|
|
1858
|
+
{
|
|
1859
|
+
className: 'dph-input dph-select-input',
|
|
1860
|
+
value: hosForm.osVersion,
|
|
1861
|
+
onChange: (event) => setHosForm((prev) => ({ ...prev, osVersion: event.target.value })),
|
|
1862
|
+
},
|
|
1863
|
+
hosImages.map((item) => h('option', { key: item.osVersion, value: item.osVersion }, item.osVersion)),
|
|
1864
|
+
),
|
|
1865
|
+
h(
|
|
1866
|
+
'div',
|
|
1867
|
+
{ className: 'dph-field', style: { marginTop: 12 } },
|
|
1868
|
+
h('button', { type: 'submit', className: 'dph-primary', disabled: hosBusy || !hosForm.name.trim() || !hosForm.osVersion }, hosBusy ? t('loading') : t('create')),
|
|
1869
|
+
h('button', { type: 'button', className: 'dph-ghost', onClick: () => setHosCreateOpen(false) }, t('cancel')),
|
|
1870
|
+
),
|
|
1871
|
+
),
|
|
1872
|
+
)
|
|
1873
|
+
: null
|
|
1874
|
+
|
|
1875
|
+
const addMenu = addMenuOpen
|
|
1876
|
+
? h(
|
|
1877
|
+
'div',
|
|
1878
|
+
null,
|
|
1879
|
+
h('div', { className: 'dph-backdrop', onClick: () => setAddMenuOpen(false) }),
|
|
1880
|
+
h(
|
|
1881
|
+
'div',
|
|
1882
|
+
{ className: 'dph-menu', style: { top: 48, left: 8, width: 270 } },
|
|
1883
|
+
h(
|
|
1884
|
+
'button',
|
|
1885
|
+
{
|
|
1886
|
+
type: 'button',
|
|
1887
|
+
className: 'dph-row',
|
|
1888
|
+
onClick: () => {
|
|
1889
|
+
setAddMenuOpen(false)
|
|
1890
|
+
setConnectOpen(true)
|
|
1891
|
+
},
|
|
1892
|
+
},
|
|
1893
|
+
h('span', { className: 'dph-av', style: { width: 26, height: 26, borderRadius: 7 } }, h(Icon, { name: 'android', size: 16 })),
|
|
1894
|
+
h('span', { className: 'dph-meta' }, h('span', { className: 'dph-model', style: { fontWeight: 400 } }, t('connectAndroid'))),
|
|
1895
|
+
),
|
|
1896
|
+
iosReady
|
|
1897
|
+
? h(
|
|
1898
|
+
'button',
|
|
1899
|
+
{
|
|
1900
|
+
type: 'button',
|
|
1901
|
+
className: 'dph-row',
|
|
1902
|
+
onClick: () => {
|
|
1903
|
+
setAddMenuOpen(false)
|
|
1904
|
+
setIosCreateOpen(true)
|
|
1905
|
+
},
|
|
1906
|
+
},
|
|
1907
|
+
h('span', { className: 'dph-av', style: { width: 26, height: 26, borderRadius: 7 } }, h(Icon, { name: 'apple', size: 16 })),
|
|
1908
|
+
h('span', { className: 'dph-meta' }, h('span', { className: 'dph-model', style: { fontWeight: 400 } }, t('createIos'))),
|
|
1909
|
+
)
|
|
1910
|
+
: null,
|
|
1911
|
+
hosReady
|
|
1912
|
+
? h(
|
|
1913
|
+
'button',
|
|
1914
|
+
{
|
|
1915
|
+
type: 'button',
|
|
1916
|
+
className: 'dph-row',
|
|
1917
|
+
onClick: () => {
|
|
1918
|
+
setAddMenuOpen(false)
|
|
1919
|
+
setHosCreateOpen(true)
|
|
1920
|
+
},
|
|
1921
|
+
},
|
|
1922
|
+
h('span', { className: 'dph-av', style: { width: 26, height: 26, borderRadius: 7 } }, h(Icon, { name: 'harmony', size: 16 })),
|
|
1923
|
+
h('span', { className: 'dph-meta' }, h('span', { className: 'dph-model', style: { fontWeight: 400 } }, t('createHos'))),
|
|
1924
|
+
)
|
|
1925
|
+
: null,
|
|
1926
|
+
),
|
|
1927
|
+
)
|
|
1928
|
+
: null
|
|
1929
|
+
|
|
1930
|
+
const iosCreateSheet = iosCreateOpen
|
|
1931
|
+
? h(
|
|
1932
|
+
'div',
|
|
1933
|
+
null,
|
|
1934
|
+
h('div', {
|
|
1935
|
+
className: 'dph-backdrop',
|
|
1936
|
+
onClick: () => {
|
|
1937
|
+
if (!iosBusy) setIosCreateOpen(false)
|
|
1938
|
+
},
|
|
1939
|
+
}),
|
|
1940
|
+
h(
|
|
1941
|
+
'form',
|
|
1942
|
+
{
|
|
1943
|
+
className: 'dph-connect',
|
|
1944
|
+
style: { maxWidth: 380 },
|
|
1945
|
+
onSubmit: async (event) => {
|
|
1946
|
+
event.preventDefault()
|
|
1947
|
+
if (iosBusy) return
|
|
1948
|
+
setIosBusy(true)
|
|
1949
|
+
try {
|
|
1950
|
+
const created = await api.iosCreate(iosForm)
|
|
1951
|
+
setIosCreateOpen(false)
|
|
1952
|
+
setNonce((value) => value + 1)
|
|
1953
|
+
if (created && created.serial) {
|
|
1954
|
+
rememberDevice(created.serial)
|
|
1955
|
+
setSelected(created.serial)
|
|
1956
|
+
}
|
|
1957
|
+
} catch (error) {
|
|
1958
|
+
preview.setToast(error && error.message ? error.message : 'create failed')
|
|
1959
|
+
} finally {
|
|
1960
|
+
setIosBusy(false)
|
|
1961
|
+
}
|
|
1962
|
+
},
|
|
1963
|
+
},
|
|
1964
|
+
h('div', { className: 'ttl' }, t('createIos')),
|
|
1965
|
+
h('div', { className: 'sub' }, 'xcrun simctl create'),
|
|
1966
|
+
h('label', { className: 'dph-label' }, t('iosName')),
|
|
1967
|
+
h('input', {
|
|
1968
|
+
className: 'dph-input',
|
|
1969
|
+
value: iosForm.name,
|
|
1970
|
+
placeholder: 'dsh-phone',
|
|
1971
|
+
onChange: (event) => setIosForm((prev) => ({ ...prev, name: event.target.value })),
|
|
1972
|
+
spellCheck: false,
|
|
1973
|
+
}),
|
|
1974
|
+
h('label', { className: 'dph-label' }, t('iosDeviceType')),
|
|
1975
|
+
h(
|
|
1976
|
+
'select',
|
|
1977
|
+
{
|
|
1978
|
+
className: 'dph-input dph-select-input',
|
|
1979
|
+
value: iosForm.deviceType,
|
|
1980
|
+
onChange: (event) => setIosForm((prev) => ({ ...prev, deviceType: event.target.value })),
|
|
1981
|
+
},
|
|
1982
|
+
iosTypes.map((item) => h('option', { key: item.identifier, value: item.identifier }, item.name)),
|
|
1983
|
+
),
|
|
1984
|
+
h('label', { className: 'dph-label' }, t('iosRuntime')),
|
|
1985
|
+
h(
|
|
1986
|
+
'select',
|
|
1987
|
+
{
|
|
1988
|
+
className: 'dph-input dph-select-input',
|
|
1989
|
+
value: iosForm.runtime,
|
|
1990
|
+
onChange: (event) => setIosForm((prev) => ({ ...prev, runtime: event.target.value })),
|
|
1991
|
+
},
|
|
1992
|
+
iosRuntimes.map((item) => h('option', { key: item.identifier, value: item.identifier }, item.name)),
|
|
1993
|
+
),
|
|
1994
|
+
h(
|
|
1995
|
+
'div',
|
|
1996
|
+
{ className: 'dph-field', style: { marginTop: 12 } },
|
|
1997
|
+
h('button', { type: 'submit', className: 'dph-primary', disabled: iosBusy || !iosForm.name.trim() || !iosForm.deviceType || !iosForm.runtime }, iosBusy ? t('loading') : t('create')),
|
|
1998
|
+
h('button', { type: 'button', className: 'dph-ghost', onClick: () => setIosCreateOpen(false) }, t('cancel')),
|
|
1999
|
+
),
|
|
2000
|
+
),
|
|
2001
|
+
)
|
|
2002
|
+
: null
|
|
2003
|
+
|
|
1649
2004
|
return h(
|
|
1650
2005
|
'div',
|
|
1651
2006
|
{ className: 'dph-root' },
|
|
@@ -1694,6 +2049,9 @@ window.__ModuleLoader__.load({
|
|
|
1694
2049
|
moreMenu,
|
|
1695
2050
|
connectSheet,
|
|
1696
2051
|
diagSheet,
|
|
2052
|
+
addMenu,
|
|
2053
|
+
iosCreateSheet,
|
|
2054
|
+
hosCreateSheet,
|
|
1697
2055
|
)
|
|
1698
2056
|
}
|
|
1699
2057
|
|