dsh-apk-vm 0.6.1 → 0.6.2
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/CHANGELOG.md +12 -0
- package/README.md +21 -1
- package/client.js +47 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.2
|
|
4
|
+
|
|
5
|
+
Sidebar usability.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- **Expand / restore button** in the VM panel header. It turns the panel into a
|
|
10
|
+
full-window overlay so a narrow `dsh-better-sidebar` tab can show a large phone screen;
|
|
11
|
+
press Escape or click the button again to restore the sidebar layout.
|
|
12
|
+
- The overlay hides the APK/debug aside, keeps the H.264 stream and input controls, and
|
|
13
|
+
reuses the existing canvas/WebSocket session (no second stream).
|
|
14
|
+
|
|
3
15
|
## 0.6.1
|
|
4
16
|
|
|
5
17
|
Network panel completeness.
|
package/README.md
CHANGED
|
@@ -8,6 +8,26 @@
|
|
|
8
8
|
|
|
9
9
|
**Run a real Android APK inside a local, rooted, open-source Android VM and preview it in the DeepSeek Harness sidebar — streamed as H.264/WebCodecs video at up to 60 fps.**
|
|
10
10
|
|
|
11
|
+
<p align="center">
|
|
12
|
+
<a href="docs/screenshot-panel.png">
|
|
13
|
+
<img src="docs/screenshot-panel.png" alt="APK Virtual Machine panel with a live H.264 Android screen in DeepSeek Harness" width="820">
|
|
14
|
+
</a>
|
|
15
|
+
</p>
|
|
16
|
+
<p align="center"><sub>Real panel capture: rooted Android 14 guest, live H.264 stream, input controls and installed-app tools — no mockup.</sub></p>
|
|
17
|
+
<p align="center">
|
|
18
|
+
<code>dsh plugin --profile web add dsh-apk-vm</code>
|
|
19
|
+
·
|
|
20
|
+
<a href="examples/README.md">reproducible demo</a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
**Why it is useful**
|
|
24
|
+
|
|
25
|
+
- **Sidebar-first Android** — install and use an APK without opening a separate emulator window.
|
|
26
|
+
- **Real, low-latency stream** — H.264/WebCodecs up to 60 fps with tap, swipe, key and text input.
|
|
27
|
+
- **Agent-native** — `apk_vm` tools, a live UI Inspector, copy-ready selectors and record/replay flows.
|
|
28
|
+
- **Local and inspectable** — open-source AOSP emulator, rooted debug API, network/storage controls, no telemetry.
|
|
29
|
+
- **Expandable viewer** — turn the narrow sidebar panel into a full-window VM with one click; Escape restores it.
|
|
30
|
+
|
|
11
31
|
This repository contains the static DSH plugin `dsh-apk-vm`. It adds a first-class sidebar
|
|
12
32
|
entry ("APK Virtual Machine" / "APK 虚拟机") whose main panel boots an AOSP Android Emulator,
|
|
13
33
|
installs an APK into the guest, streams the guest display into the panel, and forwards
|
|
@@ -24,7 +44,7 @@ same-origin, loopback-only debug HTTP API and an `apk_vm` Agent tool.
|
|
|
24
44
|
> 当前 **Windows x64 与 Linux x86_64(headless)为端到端已验证平台**;macOS 仅做探测/CI
|
|
25
45
|
> 覆盖,详见 `docs/LINUX-E2E.md`。
|
|
26
46
|
|
|
27
|
-
**Status:** `0.6.
|
|
47
|
+
**Status:** `0.6.2` · Apache-2.0 · no npm runtime dependencies · no build step.
|
|
28
48
|
|
|
29
49
|
This is an open-source DSH plugin published on npm as **`dsh-apk-vm`**. Repository:
|
|
30
50
|
[github.com/yuxiaole-bili/dsh-apk-vm](https://github.com/yuxiaole-bili/dsh-apk-vm).
|
package/client.js
CHANGED
|
@@ -103,6 +103,8 @@ window.__ModuleLoader__.load({
|
|
|
103
103
|
'action.installLaunch': 'Install & launch',
|
|
104
104
|
'action.dismiss': 'Dismiss',
|
|
105
105
|
'action.openSidebar': 'Open in side bar',
|
|
106
|
+
'action.expand': 'Expand',
|
|
107
|
+
'action.restore': 'Exit expanded',
|
|
106
108
|
'a11y.refresh': 'Refresh VM state',
|
|
107
109
|
'a11y.retry': 'Retry the request',
|
|
108
110
|
'a11y.openSidebar': 'Open the APK VM in dsh-better-sidebar',
|
|
@@ -336,6 +338,8 @@ window.__ModuleLoader__.load({
|
|
|
336
338
|
'action.installLaunch': '安装并启动',
|
|
337
339
|
'action.dismiss': '关闭',
|
|
338
340
|
'action.openSidebar': '在侧栏打开',
|
|
341
|
+
'action.expand': '放大',
|
|
342
|
+
'action.restore': '退出放大',
|
|
339
343
|
'a11y.refresh': '刷新虚拟机状态',
|
|
340
344
|
'a11y.retry': '重试请求',
|
|
341
345
|
'a11y.openSidebar': '在 dsh-better-sidebar 中打开 APK 虚拟机',
|
|
@@ -1064,7 +1068,17 @@ window.__ModuleLoader__.load({
|
|
|
1064
1068
|
'.apkvm-embedded .apkvm-phonewrap{flex:0 0 auto;min-height:320px}' +
|
|
1065
1069
|
'.apkvm-embedded .apkvm-controls{max-width:none}' +
|
|
1066
1070
|
'@media (max-width:900px){.apkvm-header{flex-wrap:wrap}}' +
|
|
1067
|
-
'.apkvm-embedded .apkvm-header{flex-wrap:wrap}'
|
|
1071
|
+
'.apkvm-embedded .apkvm-header{flex-wrap:wrap}' +
|
|
1072
|
+
'.apkvm-page.apkvm-maximized{position:fixed;inset:0;z-index:2147483000;width:100vw;height:100vh;background:var(--dsw-alias-bg-base);box-shadow:0 0 0 1px var(--dsw-alias-border-l2)}' +
|
|
1073
|
+
'.apkvm-page.apkvm-maximized .apkvm-header{flex:0 0 auto;background:var(--dsw-alias-bg-base)}' +
|
|
1074
|
+
'.apkvm-page.apkvm-maximized .apkvm-body{flex:1 1 auto;min-height:0;padding:10px 16px 14px;gap:10px;overflow:hidden;flex-direction:row}' +
|
|
1075
|
+
'.apkvm-page.apkvm-maximized .apkvm-aside{display:none}' +
|
|
1076
|
+
'.apkvm-page.apkvm-maximized .apkvm-stage{flex:1 1 auto;min-height:0;max-height:none}' +
|
|
1077
|
+
'.apkvm-page.apkvm-maximized .apkvm-phonewrap{flex:1 1 auto;min-height:0;padding:0}' +
|
|
1078
|
+
'.apkvm-page.apkvm-maximized .apkvm-phone{height:100%;max-height:calc(100vh - 150px);padding:6px;border-radius:22px}' +
|
|
1079
|
+
'.apkvm-page.apkvm-maximized .apkvm-screen{height:100%;max-height:calc(100vh - 170px);border-radius:16px}' +
|
|
1080
|
+
'.apkvm-page.apkvm-maximized .apkvm-controls{max-width:760px}' +
|
|
1081
|
+
'.apkvm-page.apkvm-maximized .apkvm-stagehint{max-width:760px}';
|
|
1068
1082
|
|
|
1069
1083
|
/* ------------------------------------------------------------------ *
|
|
1070
1084
|
* Render-error boundary: an unexpected exception inside the panel keeps
|
|
@@ -1373,6 +1387,10 @@ window.__ModuleLoader__.load({
|
|
|
1373
1387
|
var canvasRef = React.useRef(null);
|
|
1374
1388
|
var decoderRef = React.useRef(null);
|
|
1375
1389
|
var wsRef = React.useRef(null);
|
|
1390
|
+
var pageRef = React.useRef(null);
|
|
1391
|
+
var maximizedState = React.useState(false);
|
|
1392
|
+
var isMaximized = maximizedState[0];
|
|
1393
|
+
var setIsMaximized = maximizedState[1];
|
|
1376
1394
|
|
|
1377
1395
|
/* ---- derived snapshot values --------------------------------- */
|
|
1378
1396
|
var vmInfo = snapshot ? snapshot.vm : null;
|
|
@@ -1528,6 +1546,21 @@ window.__ModuleLoader__.load({
|
|
|
1528
1546
|
if (ready) loadNetwork();
|
|
1529
1547
|
}, [ready, activeInstance]);
|
|
1530
1548
|
|
|
1549
|
+
// Escape leaves the expanded VM view; the expand button is inside the
|
|
1550
|
+
// full-screen overlay too, so mouse-only users are never trapped.
|
|
1551
|
+
React.useEffect(function () {
|
|
1552
|
+
if (!isMaximized || typeof document === 'undefined' || typeof document.addEventListener !== 'function') {
|
|
1553
|
+
return undefined;
|
|
1554
|
+
}
|
|
1555
|
+
function onKeyDown(event) {
|
|
1556
|
+
if (event && event.key === 'Escape') setIsMaximized(false);
|
|
1557
|
+
}
|
|
1558
|
+
document.addEventListener('keydown', onKeyDown);
|
|
1559
|
+
return function () {
|
|
1560
|
+
document.removeEventListener('keydown', onKeyDown);
|
|
1561
|
+
};
|
|
1562
|
+
}, [isMaximized]);
|
|
1563
|
+
|
|
1531
1564
|
// Track document visibility so the frame stream pauses when hidden.
|
|
1532
1565
|
React.useEffect(function () {
|
|
1533
1566
|
if (typeof document === 'undefined' || typeof document.addEventListener !== 'function') {
|
|
@@ -4516,6 +4549,17 @@ window.__ModuleLoader__.load({
|
|
|
4516
4549
|
(videoMode === 'h264' ? T('fps.mode.h264') : T('fps.mode.png'))
|
|
4517
4550
|
),
|
|
4518
4551
|
primaryAction,
|
|
4552
|
+
actionBtn(T(isMaximized ? 'action.restore' : 'action.expand'), {
|
|
4553
|
+
key: 'maximize',
|
|
4554
|
+
small: true,
|
|
4555
|
+
icon: h('span', { 'aria-hidden': 'true', style: { fontSize: '14px', lineHeight: '14px' } }, isMaximized ? '⛜' : '⛶'),
|
|
4556
|
+
ariaLabel: T(isMaximized ? 'action.restore' : 'action.expand'),
|
|
4557
|
+
onClick: function () {
|
|
4558
|
+
setIsMaximized(function (value) {
|
|
4559
|
+
return !value;
|
|
4560
|
+
});
|
|
4561
|
+
},
|
|
4562
|
+
}),
|
|
4519
4563
|
!embedded && sidebarService
|
|
4520
4564
|
? actionBtn(T('action.openSidebar'), {
|
|
4521
4565
|
key: 'open-sidebar',
|
|
@@ -4697,7 +4741,8 @@ window.__ModuleLoader__.load({
|
|
|
4697
4741
|
return h(
|
|
4698
4742
|
'div',
|
|
4699
4743
|
{
|
|
4700
|
-
className: 'apkvm-page' + (embedded ? ' apkvm-embedded' : ''),
|
|
4744
|
+
className: 'apkvm-page' + (embedded ? ' apkvm-embedded' : '') + (isMaximized ? ' apkvm-maximized' : ''),
|
|
4745
|
+
ref: pageRef,
|
|
4701
4746
|
onDragEnter: onPageDragEnter,
|
|
4702
4747
|
onDragOver: onPageDragOver,
|
|
4703
4748
|
onDragLeave: onPageDragLeave,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-apk-vm",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Local AOSP Android VM for DeepSeek Harness: preview rooted APKs at up to 60 fps, automate UIAutomator flows, and give each Agent an isolated VM.",
|
|
6
6
|
"license": "Apache-2.0",
|