From 0ae5c7b7af79b2071fdbad99cbe5378f015d52cc Mon Sep 17 00:00:00 2001
From: Albrecht Degering
Date: Wed, 2 Sep 2026 13:08:36 +0200
Subject: [PATCH] Release Device Tools 0.2.0
---
.gitea/workflows/verify.yml | 39 ++
CHANGELOG.md | 7 +
README.md | 7 +-
SOURCE.md | 4 +-
package-lock.json | 40 +-
package.json | 10 +-
playwright.config.ts | 22 +-
public/CHANGELOG.md | 7 +
public/LICENSES/npm-runtime-licenses.txt | 6 +-
public/README.md | 7 +-
public/SOURCE.md | 4 +-
public/sw.js | 2 +-
public/toolbox-app.json | 16 +-
src/components/HelpDialog.tsx | 7 +
src/components/Workbench.tsx | 329 ++++++++++++
src/core/device.ts | 656 ++++++++++++++++++++++-
src/styles.css | 168 +++++-
src/toolbox/manifest.source.json | 24 +-
src/version.ts | 2 +-
tests/browser/responsive.spec.ts | 18 +
tests/core/device.test.ts | 165 ++++++
21 files changed, 1490 insertions(+), 50 deletions(-)
create mode 100644 .gitea/workflows/verify.yml
create mode 100644 tests/browser/responsive.spec.ts
diff --git a/.gitea/workflows/verify.yml b/.gitea/workflows/verify.yml
new file mode 100644
index 0000000..b84a7cb
--- /dev/null
+++ b/.gitea/workflows/verify.yml
@@ -0,0 +1,39 @@
+name: Verify
+
+on:
+ push:
+ branches: [main]
+ pull_request:
+ workflow_dispatch:
+
+concurrency:
+ group: verify-${{ gitea.repository }}-${{ gitea.ref }}
+ cancel-in-progress: true
+
+permissions:
+ contents: read
+
+jobs:
+ verify:
+ runs-on: ubuntu-latest
+ timeout-minutes: 45
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/checkout@v4
+ - uses: actions/setup-node@v4
+ with:
+ node-version: "22"
+ cache: npm
+ - name: Select declared npm version
+ run: npm install --global npm@11.17.0
+ - name: Install dependencies
+ run: npm ci
+ - name: Audit runtime dependencies
+ run: npm audit --omit=dev --audit-level=moderate
+ - name: Check, test, and build
+ run: npm run check
+ - name: Install browser engines
+ run: npx playwright install --with-deps chromium firefox webkit
+ - name: Browser tests
+ run: npm run test:browser
diff --git a/CHANGELOG.md b/CHANGELOG.md
index cdd644c..f4f05b0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## 0.2.0 - 2026-09-02
+
+- Added Toolbox manifest compatibility analysis for legacy requirements and
+ required/optional capability profiles with explainable unknowns.
+- Added an explicitly initiated MediaCapabilities codec lab and downloadable,
+ bucketed privacy-preserving evidence.
+
## 0.1.0 - 2026-09-01
- Added passive display, input, codec, media-constraint, PWA and browser API inventory.
diff --git a/README.md b/README.md
index bc0c3b6..8c93e1c 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,9 @@ A local browser capability and privacy lab. It shows useful platform evidence wi
- Passive inventory for secure/top-level context, display/viewport/DPR, touch/pointer/hover, accessibility media queries, media constraints, codec declarations, storage/PWA and browser device-API presence.
- Explicit individual probes for permission state, storage estimate/persistence, WebGL limits, WebGPU adapter presence, media-device counts, battery, network hints and camera/microphone/screen access.
+- Explainable failures that distinguish a missing API, insecure or embedded context, Permissions Policy, user permission, and unavailable/busy operating-system devices, with concrete remediation.
+- Toolbox application compatibility analysis from pasted manifest JSON, including legacy requirements plus required/optional capability profiles and unknown-capability evidence.
+- An explicit MediaCapabilities decoding lab for video/audio codec, dimensions, rate and bitrate, with support/smoothness/power-efficiency results and a separately downloadable bucketed report.
- Camera, microphone and screen streams are never recorded and every returned track stops immediately.
- WebGL deliberately avoids unmasked vendor/renderer extensions; media device IDs and labels are discarded.
- Deterministic redacted JSON/CSV reports with broad buckets for high-entropy values and no timestamp, user agent, locale, identifier or score.
@@ -13,13 +16,13 @@ A local browser capability and privacy lab. It shows useful platform evidence wi
## Privacy boundary
-The passive inventory runs locally on load and does not request permission. Optional probes run only after their named button is pressed; buttons labelled “may prompt” can show browser or operating-system UI. The application performs no canvas/audio sample fingerprinting, persistent storage, telemetry or external request.
+The passive inventory runs locally on load and does not request permission. Optional probes run only after their named button is pressed; buttons labelled “may prompt” can show browser or operating-system UI. Application manifests are treated as inert JSON and no referenced code is loaded. MediaCapabilities checks are likewise opt-in because smoothness and power-efficiency answers can depend on the device. The application performs no canvas/audio sample fingerprinting, persistent storage, telemetry or external request.
Downloaded reports are deliberately less precise than the on-screen local view. They bucket exact dimensions, pixel ratio, storage, graphics limits, hardware counts, battery timing and connection quality. This is a diagnostics report, not proof that an API will work in every embedded or enterprise-policy context.
## Development
-Use Node.js 22+ and npm 11. Run `npm ci`, `npm run check`, `npm run test:browser`, then `npm run package:release -- --force`. The deterministic artifact is `release/device-tools-0.1.0.zip` with a SHA-256 sidecar.
+Use Node.js 22+ and npm 11. Run `npm ci`, `npm run check`, `npm run test:browser`, then `npm run package:release -- --force`. The deterministic artifact is `release/device-tools-0.2.0.zip` with a SHA-256 sidecar.
## Licence
diff --git a/SOURCE.md b/SOURCE.md
index ef1f6a3..3c07db5 100644
--- a/SOURCE.md
+++ b/SOURCE.md
@@ -1,10 +1,10 @@
# Source identity
- Project: Device Tools
-- Version: 0.1.0
+- Version: 0.2.0
- Repository: https://git.add-ideas.de/lotobo/device-tools
- Licence: GPL-3.0-or-later
- Build: Node.js 22+, npm 11, `npm ci && npm run release:artifact`
-- Artifact: `device-tools-0.1.0.zip`
+- Artifact: `device-tools-0.2.0.zip`
The lockfile pins dependencies exactly. Generated release manifests repeat this source identity and releases include detected runtime licence texts under `LICENSES/`.
diff --git a/package-lock.json b/package-lock.json
index c97df70..1ae3dc0 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,22 +1,22 @@
{
"name": "device-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "device-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"license": "GPL-3.0-or-later",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3",
- "@add-ideas/toolbox-helpers": "0.1.0",
- "@add-ideas/toolbox-shell-react": "0.2.3",
+ "@add-ideas/toolbox-contract": "0.3.0",
+ "@add-ideas/toolbox-helpers": "0.2.0",
+ "@add-ideas/toolbox-shell-react": "0.3.0",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
- "@add-ideas/toolbox-testkit": "0.2.3",
+ "@add-ideas/toolbox-testkit": "0.3.0",
"@eslint/js": "10.0.1",
"@playwright/test": "1.62.1",
"@testing-library/jest-dom": "6.9.1",
@@ -42,24 +42,24 @@
}
},
"node_modules/@add-ideas/toolbox-contract": {
- "version": "0.2.3",
- "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.2.3/toolbox-contract-0.2.3.tgz",
- "integrity": "sha512-T0PVSuMT40GjTDfQJhEEY3ZawQq8zz1/ry95JdKI6W39CdLacaRXdGnEpDCMHt+jUbf1Jz7Nat/M5dFCgKVM9A==",
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-contract/-/0.3.0/toolbox-contract-0.3.0.tgz",
+ "integrity": "sha512-dKrK7BjOFwqJaBfJuhKxZKIld4sH0AKjEn6a0yLnbdMUFY+fFv4VSLGV2tNSBD016gumc2iNqOjUj/ld7x4rtA==",
"license": "Apache-2.0"
},
"node_modules/@add-ideas/toolbox-helpers": {
- "version": "0.1.0",
- "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.1.0/toolbox-helpers-0.1.0.tgz",
- "integrity": "sha512-UKl1Oxekedf8D2df86VrnVA53AcMhrnh6iUPXY+k8frirBXotb0yd8SGT+IF/3hcqYwcYe/v9WVFuSgKtIYVnw==",
+ "version": "0.2.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-helpers/-/0.2.0/toolbox-helpers-0.2.0.tgz",
+ "integrity": "sha512-SdOqkw+P+3J3fa5iVkzb5P15rVepB001GNV21Oh8w0CZcVL+YRltgD/s+MVcTyrNijWQf3E5vtQON/3N2LLyKg==",
"license": "GPL-3.0-or-later"
},
"node_modules/@add-ideas/toolbox-shell-react": {
- "version": "0.2.3",
- "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.2.3/toolbox-shell-react-0.2.3.tgz",
- "integrity": "sha512-DT5lQDH48BFkFcmFLZnQh7+Cm73JzBPcmp5WzUXypfkUXpEyDYHzaXgmW4kZ0edSwh4RK4sPmx+JPtK0X4aKCQ==",
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-shell-react/-/0.3.0/toolbox-shell-react-0.3.0.tgz",
+ "integrity": "sha512-74p6JzAOG0YCAKdlc1hLofV4ZIko7vb448S75cIiM88PKm93EHl5VD7g8YVyfM56Ui97UY9dmy+Whiq4sGzpsg==",
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3"
+ "@add-ideas/toolbox-contract": "0.3.0"
},
"peerDependencies": {
"react": ">=18 <20",
@@ -67,13 +67,13 @@
}
},
"node_modules/@add-ideas/toolbox-testkit": {
- "version": "0.2.3",
- "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.2.3/toolbox-testkit-0.2.3.tgz",
- "integrity": "sha512-sq1MwhKWfFKen+N+124hl74qQimRSvmQ9sOU7jdcI+2qCKZ67+2B8rWyezeV80uTFu4Jv6deHksfYQ/tKNV6XQ==",
+ "version": "0.3.0",
+ "resolved": "https://git.add-ideas.de/api/packages/lotobo/npm/%40add-ideas%2Ftoolbox-testkit/-/0.3.0/toolbox-testkit-0.3.0.tgz",
+ "integrity": "sha512-4Fk+oSvZFspOMIXr8Xy040nhAaBsIQAzsGyXWSpjn3+k3yBKq7nB1r5zCHhsXzfdLzvPDAx2KcmSNOhM330D9w==",
"dev": true,
"license": "Apache-2.0",
"dependencies": {
- "@add-ideas/toolbox-contract": "0.2.3"
+ "@add-ideas/toolbox-contract": "0.3.0"
},
"bin": {
"toolbox-check": "dist/cli.js"
diff --git a/package.json b/package.json
index e989a98..ba4cf83 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "device-tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Inspect browser capabilities and privacy-safe device probes locally.",
"license": "GPL-3.0-or-later",
"author": "Albrecht Degering",
@@ -39,14 +39,14 @@
"release:artifact": "npm run check && npm run test:browser && npm run package:release -- --force"
},
"dependencies": {
- "@add-ideas/toolbox-helpers": "0.1.0",
- "@add-ideas/toolbox-contract": "0.2.3",
- "@add-ideas/toolbox-shell-react": "0.2.3",
+ "@add-ideas/toolbox-helpers": "0.2.0",
+ "@add-ideas/toolbox-contract": "0.3.0",
+ "@add-ideas/toolbox-shell-react": "0.3.0",
"react": "19.2.8",
"react-dom": "19.2.8"
},
"devDependencies": {
- "@add-ideas/toolbox-testkit": "0.2.3",
+ "@add-ideas/toolbox-testkit": "0.3.0",
"@eslint/js": "10.0.1",
"@playwright/test": "1.62.1",
"@testing-library/jest-dom": "6.9.1",
diff --git a/playwright.config.ts b/playwright.config.ts
index 7d2a637..2a50369 100644
--- a/playwright.config.ts
+++ b/playwright.config.ts
@@ -15,7 +15,25 @@ export default defineConfig({
timeout: 180_000,
},
projects: [
- { name: "chromium", use: { ...devices["Desktop Chrome"] } },
- { name: "firefox", use: { ...devices["Desktop Firefox"] } },
+ {
+ name: "chromium",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Chrome"] },
+ },
+ {
+ name: "firefox",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Firefox"] },
+ },
+ {
+ name: "webkit",
+ testIgnore: /responsive\.spec\.ts/,
+ use: { ...devices["Desktop Safari"] },
+ },
+ {
+ name: "mobile-chromium",
+ testMatch: /responsive\.spec\.ts/,
+ use: { ...devices["Pixel 5"] },
+ },
],
});
diff --git a/public/CHANGELOG.md b/public/CHANGELOG.md
index cdd644c..f4f05b0 100644
--- a/public/CHANGELOG.md
+++ b/public/CHANGELOG.md
@@ -1,5 +1,12 @@
# Changelog
+## 0.2.0 - 2026-09-02
+
+- Added Toolbox manifest compatibility analysis for legacy requirements and
+ required/optional capability profiles with explainable unknowns.
+- Added an explicitly initiated MediaCapabilities codec lab and downloadable,
+ bucketed privacy-preserving evidence.
+
## 0.1.0 - 2026-09-01
- Added passive display, input, codec, media-constraint, PWA and browser API inventory.
diff --git a/public/LICENSES/npm-runtime-licenses.txt b/public/LICENSES/npm-runtime-licenses.txt
index 45a7c95..4f412f1 100644
--- a/public/LICENSES/npm-runtime-licenses.txt
+++ b/public/LICENSES/npm-runtime-licenses.txt
@@ -1,5 +1,5 @@
==============================================================================
-@add-ideas/toolbox-contract@0.2.3
+@add-ideas/toolbox-contract@0.3.0
Declared licence: Apache-2.0
==============================================================================
--- LICENSE ---
@@ -198,7 +198,7 @@ Declared licence: Apache-2.0
==============================================================================
-@add-ideas/toolbox-helpers@0.1.0
+@add-ideas/toolbox-helpers@0.2.0
Declared licence: GPL-3.0-or-later
==============================================================================
--- LICENSE ---
@@ -879,7 +879,7 @@ Public License instead of this License. But first, please read
==============================================================================
-@add-ideas/toolbox-shell-react@0.2.3
+@add-ideas/toolbox-shell-react@0.3.0
Declared licence: Apache-2.0
==============================================================================
--- LICENSE ---
diff --git a/public/README.md b/public/README.md
index bc0c3b6..8c93e1c 100644
--- a/public/README.md
+++ b/public/README.md
@@ -6,6 +6,9 @@ A local browser capability and privacy lab. It shows useful platform evidence wi
- Passive inventory for secure/top-level context, display/viewport/DPR, touch/pointer/hover, accessibility media queries, media constraints, codec declarations, storage/PWA and browser device-API presence.
- Explicit individual probes for permission state, storage estimate/persistence, WebGL limits, WebGPU adapter presence, media-device counts, battery, network hints and camera/microphone/screen access.
+- Explainable failures that distinguish a missing API, insecure or embedded context, Permissions Policy, user permission, and unavailable/busy operating-system devices, with concrete remediation.
+- Toolbox application compatibility analysis from pasted manifest JSON, including legacy requirements plus required/optional capability profiles and unknown-capability evidence.
+- An explicit MediaCapabilities decoding lab for video/audio codec, dimensions, rate and bitrate, with support/smoothness/power-efficiency results and a separately downloadable bucketed report.
- Camera, microphone and screen streams are never recorded and every returned track stops immediately.
- WebGL deliberately avoids unmasked vendor/renderer extensions; media device IDs and labels are discarded.
- Deterministic redacted JSON/CSV reports with broad buckets for high-entropy values and no timestamp, user agent, locale, identifier or score.
@@ -13,13 +16,13 @@ A local browser capability and privacy lab. It shows useful platform evidence wi
## Privacy boundary
-The passive inventory runs locally on load and does not request permission. Optional probes run only after their named button is pressed; buttons labelled “may prompt” can show browser or operating-system UI. The application performs no canvas/audio sample fingerprinting, persistent storage, telemetry or external request.
+The passive inventory runs locally on load and does not request permission. Optional probes run only after their named button is pressed; buttons labelled “may prompt” can show browser or operating-system UI. Application manifests are treated as inert JSON and no referenced code is loaded. MediaCapabilities checks are likewise opt-in because smoothness and power-efficiency answers can depend on the device. The application performs no canvas/audio sample fingerprinting, persistent storage, telemetry or external request.
Downloaded reports are deliberately less precise than the on-screen local view. They bucket exact dimensions, pixel ratio, storage, graphics limits, hardware counts, battery timing and connection quality. This is a diagnostics report, not proof that an API will work in every embedded or enterprise-policy context.
## Development
-Use Node.js 22+ and npm 11. Run `npm ci`, `npm run check`, `npm run test:browser`, then `npm run package:release -- --force`. The deterministic artifact is `release/device-tools-0.1.0.zip` with a SHA-256 sidecar.
+Use Node.js 22+ and npm 11. Run `npm ci`, `npm run check`, `npm run test:browser`, then `npm run package:release -- --force`. The deterministic artifact is `release/device-tools-0.2.0.zip` with a SHA-256 sidecar.
## Licence
diff --git a/public/SOURCE.md b/public/SOURCE.md
index ef1f6a3..3c07db5 100644
--- a/public/SOURCE.md
+++ b/public/SOURCE.md
@@ -1,10 +1,10 @@
# Source identity
- Project: Device Tools
-- Version: 0.1.0
+- Version: 0.2.0
- Repository: https://git.add-ideas.de/lotobo/device-tools
- Licence: GPL-3.0-or-later
- Build: Node.js 22+, npm 11, `npm ci && npm run release:artifact`
-- Artifact: `device-tools-0.1.0.zip`
+- Artifact: `device-tools-0.2.0.zip`
The lockfile pins dependencies exactly. Generated release manifests repeat this source identity and releases include detected runtime licence texts under `LICENSES/`.
diff --git a/public/sw.js b/public/sw.js
index 208ab06..7e8347c 100644
--- a/public/sw.js
+++ b/public/sw.js
@@ -1,5 +1,5 @@
const CACHE_PREFIX = "device-tools-shell-";
-const CACHE_NAME = CACHE_PREFIX + "0.1.0";
+const CACHE_NAME = CACHE_PREFIX + "0.2.0";
const CORE = ["./", "./manifest.webmanifest", "./favicon.svg"];
self.addEventListener("install", (event) => {
event.waitUntil(
diff --git a/public/toolbox-app.json b/public/toolbox-app.json
index 112d8a7..5e8ade9 100644
--- a/public/toolbox-app.json
+++ b/public/toolbox-app.json
@@ -3,7 +3,7 @@
"schemaVersion": 1,
"id": "de.add-ideas.device-tools",
"name": "Device Tools",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "Inspect capabilities without fingerprinting.",
"entry": "./",
"icon": "./favicon.svg",
@@ -28,6 +28,20 @@
"crossOriginIsolated": false,
"topLevelContext": false
},
+ "io": {
+ "accepts": [
+ {
+ "mediaType": "application/manifest+json",
+ "extensions": [".webmanifest"]
+ },
+ { "mediaType": "application/json", "extensions": [".json"] }
+ ],
+ "produces": [{ "mediaType": "application/json", "extensions": [".json"] }]
+ },
+ "capabilities": {
+ "required": [],
+ "optional": ["media-capabilities", "permissions-api", "secure-context"]
+ },
"privacy": {
"processing": "local",
"fileUploads": false,
diff --git a/src/components/HelpDialog.tsx b/src/components/HelpDialog.tsx
index 7082292..7acf70c 100644
--- a/src/components/HelpDialog.tsx
+++ b/src/components/HelpDialog.tsx
@@ -41,6 +41,13 @@ export function HelpDialog({
microphone, screen and persistence checks can prompt; returned media
tracks are stopped immediately and no content is recorded.
+
+ The compatibility panel reads pasted Toolbox manifests as inert JSON and
+ explains whether each required or optional browser capability is
+ available. The MediaCapabilities lab checks only the codec profile you
+ explicitly submit; its downloadable form replaces exact performance
+ parameters with broad buckets.
+
+ Paste a Toolbox manifest or capability profile. The file is parsed
+ locally and matched against the passive inventory; no application code
+ is loaded.
+
+
+
+
+
+ {profileError && (
+
+ {profileError}
+
+ )}
+ {requirementEvaluation && (
+ <>
+
+ {requirementEvaluation.profile.name} is{" "}
+ {requirementEvaluation.status} in this
+ context.
+
+ Ask the browser about one exact local decoding configuration. This can
+ reveal device-dependent performance characteristics, so it never runs
+ automatically and exported numbers are bucketed.
+