diff --git a/.gitignore b/.gitignore
index d78fcd47e..4d4e978fc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -75,6 +75,7 @@ test/pipelines/input-output-json-pipeline/emscripten-build/
test/pipelines/input-output-json-pipeline/wasi-build/
test/pipelines/bindgen-interface-types-pipeline/python/
test/pipelines/bindgen-interface-types-pipeline/wasi-build/
+test/pipelines/bindgen-interface-types-pipeline/python-web-demo
cypress/screenshots/
cypress/videos/
diff --git a/docs/introduction/packages.md b/docs/introduction/packages.md
index aac92bbc8..9f61e1efd 100644
--- a/docs/introduction/packages.md
+++ b/docs/introduction/packages.md
@@ -6,8 +6,8 @@ request](https://github.com/InsightSoftwareConsortium/itk-wasm/compare) to add i
| Repository |
Description
|
Links |
|-----------------------------|:---------------------------------------------:|:----------------------:|
-| [@itk-wasm/compress-stringify][compress-stringify-repo] |

*Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.* | ![js][js-logo] ![ts][ts-logo]
[💻 Demo][compress-stringify-demo-js]
[🕮 Docs][compress-stringify-docs-js]
[📦 Package][compress-stringify-package-js]
![py][py-logo]
[💻 Demo][compress-stringify-demo-py]
[🕮 Docs][compress-stringify-docs-py]
[📦 Package][compress-stringify-package-py] |
-| [@itk-wasm/dicom][dicom-repo] |
*Read files and images related to DICOM file format.* | ![js][js-logo] ![ts][ts-logo]
[💻 Demo][dicom-demo-js]
[🕮 Docs][dicom-docs-js]
[📦 Package][dicom-package-js]
![py][py-logo]
[🕮 Docs][dicom-docs-py]
[📦 Package][dicom-package-py] |
+| [@itk-wasm/compress-stringify][compress-stringify-repo] | 
*Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.* | ![js][js-logo] ![ts][ts-logo]
[👨💻 Demo][compress-stringify-demo-js]
[🕮 Docs][compress-stringify-docs-js]
[📦 Package][compress-stringify-package-js]
![py][py-logo]
[👨💻 Demo][compress-stringify-demo-py]
[🕮 Docs][compress-stringify-docs-py]
[📦 Package][compress-stringify-package-py] |
+| [@itk-wasm/dicom][dicom-repo] |
*Read files and images related to DICOM file format.* | ![js][js-logo] ![ts][ts-logo]
[👨💻 Demo][dicom-demo-js]
[🕮 Docs][dicom-docs-js]
[📦 Package][dicom-package-js]
![py][py-logo]
[🕮 Docs][dicom-docs-py]
[📦 Package][dicom-package-py] |
[js-logo]: /_static/javascript-logo.svg
[ts-logo]: /_static/typescript-logo.svg
diff --git a/package.json b/package.json
index 0930c8e7e..a34c3a242 100644
--- a/package.json
+++ b/package.json
@@ -63,6 +63,7 @@
"test:cliTest": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/stdout-stderr-pipeline test",
"test:cliBindgen:typescript": "node src/itk-wasm-cli.js -b emscripten-build -s ./test/pipelines/bindgen-interface-types-pipeline bindgen --package-version 1.0.0 --package-name @itk-wasm/bindgen-interface-types-test --package-description \"Exercise interface types for bindgen\"",
"test:cliBindgen:python": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/bindgen-interface-types-pipeline bindgen --interface python --package-version 1.0.0 --package-name itkwasm-bindgen-interface-types-test --package-description \"Exercise interface types for bindgen\"",
+ "test:cliBindgen:python-web-demo": "node src/itk-wasm-cli.js -b wasi-build -s ./test/pipelines/bindgen-interface-types-pipeline bindgen --interface python-web-demo --package-version 1.0.0 --package-name itkwasm-bindgen-interface-types-test --package-description \"Exercise interface types for bindgen\"",
"test:browser": "karma start ./karma.conf.cjs",
"test:browser:debug": "karma start ./karma.conf.cjs --no-single-run",
"test:browser:debug:cypress": "start-server-and-test start http-get://localhost:8083 cypress:open",
diff --git a/packages/compress-stringify/python/itkwasm-compress-stringify/README.md b/packages/compress-stringify/python/itkwasm-compress-stringify/README.md
index 30e691d0c..1193f54bc 100644
--- a/packages/compress-stringify/python/itkwasm-compress-stringify/README.md
+++ b/packages/compress-stringify/python/itkwasm-compress-stringify/README.md
@@ -4,7 +4,7 @@
Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.
-[💻 **Live API Demo** ✨](https://itk-compress-stringify-py-app.on.fleek.co/)
+[👨💻 **Live API Demo** ✨](https://itk-compress-stringify-py-app.on.fleek.co/)
[🕮 **Documentation** 📚](https://itk-wasm-compress-stringify-python-docs.on.fleek.co/)
diff --git a/packages/compress-stringify/typescript/README.md b/packages/compress-stringify/typescript/README.md
index c47aee128..4ecb0202c 100644
--- a/packages/compress-stringify/typescript/README.md
+++ b/packages/compress-stringify/typescript/README.md
@@ -4,7 +4,7 @@
> Zstandard compression and decompression and base64 encoding and decoding in WebAssembly.
-[💻 **Live API Demo** ✨](https://itk-compress-stringify-app.on.fleek.co/ ':include :type=iframe width=100% height=800px')
+[👨💻 **Live API Demo** ✨](https://itk-compress-stringify-app.on.fleek.co/ ':include :type=iframe width=100% height=800px')
[🕮 **Documentation** 📚](https://itk-compress-stringify-docs.on.fleek.co/)
diff --git a/src/bindgen/python-web-demo/input-parameters-python.js b/src/bindgen/python-web-demo/input-parameters-python.js
index 417ec9fa1..d6fb7cc4d 100644
--- a/src/bindgen/python-web-demo/input-parameters-python.js
+++ b/src/bindgen/python-web-demo/input-parameters-python.js
@@ -7,6 +7,7 @@ function inputParametersPython(functionName, indent, parameter, required) {
const parameterName = snakeCase(parameter.name)
const inputIdentifier = `${parameterName}_element`
switch (parameter.type) {
+ case 'INPUT_TEXT_FILE':
case 'INPUT_TEXT_FILE:FILE':
case 'INPUT_TEXT_STREAM':
initResult += `${indent} ${inputIdentifier} = js.document.querySelector('#${functionName}-inputs input[name=${parameter.name}-file]')\n`
@@ -19,6 +20,7 @@ function inputParametersPython(functionName, indent, parameter, required) {
methodResult += `${indent} ${parameterName}_element = js.document.getElementById("${functionName}-${parameterName}-details")\n`
methodResult += `${indent} ${parameterName}_element.innerHTML = f"
{${parameterName}_str[:50] + ' ...'}"\n\n`
break
+ case 'INPUT_BINARY_FILE':
case 'INPUT_BINARY_FILE:FILE':
case 'INPUT_BINARY_STREAM':
initResult += `${indent} ${inputIdentifier} = js.document.querySelector('#${functionName}-inputs input[name=${parameter.name}-file]')\n`
@@ -53,6 +55,48 @@ function inputParametersPython(functionName, indent, parameter, required) {
methodResult += `${indent}def on_${parameterName}_change(self, event):\n`
methodResult += `${indent} self.model.${modelProperty}['${parameterName}'] = int(self.${inputIdentifier}.value)\n\n`
break
+ case 'INPUT_JSON':
+ initResult += `${indent} ${inputIdentifier} = js.document.querySelector('#${functionName}-inputs sl-input[name=${parameter.name}-file]')\n`
+ initResult += `${indent} self.${inputIdentifier} = ${inputIdentifier}\n`
+ initResult += `${indent} add_event_listener(${inputIdentifier}, 'sl-change', self.on_${parameterName}_change)\n\n`
+ methodResult += `${indent}async def on_${parameterName}_change(self, event):\n`
+ methodResult += `${indent} files = event.target.files\n`
+ methodResult += `${indent} array_buffer = await files.item(0).arrayBuffer()\n`
+ methodResult += `${indent} ${parameterName}_str = array_buffer.to_string()\n`
+ methodResult += `${indent} ${parameterName}_value = json.loads(${parameterName}_str)\n`
+ methodResult += `${indent} self.model.${modelProperty}['${parameterName}'] = ${parameterName}_value\n`
+ methodResult += `${indent} ${parameterName}_element = js.document.getElementById("${functionName}-${parameterName}-details")\n`
+ methodResult += `${indent} ${parameterName}_element.innerHTML = f"{js.escapeHtml(js.JSON.stringify(${parameterName}_value), js.interfaceTypeJsonReplacer, 2)}}"\n`
+ methodResult += `${indent} ${parameterName}_element.disabled = False\n\n`
+ break
+ case 'INPUT_IMAGE':
+ // todo
+ result += `${indent}const ${inputIdentifier} = document.querySelector('#${functionName}Inputs input[name=${parameter.name}-file]')\n`
+ result += `${indent}${inputIdentifier}.addEventListener('change', async (event) => {\n`
+ result += `${indent}${indent}const dataTransfer = event.dataTransfer\n`
+ result += `${indent}${indent}const files = event.target.files || dataTransfer.files\n\n`
+ result += `${indent}${indent}const { image, webWorker } = await readImageFile(null, files[0])\n`
+ result += `${indent}${indent}webWorker.terminate()\n`
+ result += `${indent}${indent}model.${modelProperty}.set("${parameterName}", image)\n`
+ result += `${indent}${indent}const details = document.getElementById("${functionName}-${parameter.name}-details")\n`
+ result += `${indent}${indent}details.innerHTML = \`$\{globalThis.escapeHtml(JSON.stringify(image, globalThis.interfaceTypeJsonReplacer, 2))}\`\n`
+ result += `${indent}${indent}details.disabled = false\n`
+ result += `${indent}})\n\n`
+ break
+ case 'INPUT_MESH':
+ // todo
+ result += `${indent}const ${inputIdentifier} = document.querySelector('#${functionName}Inputs input[name=${parameter.name}-file]')\n`
+ result += `${indent}${inputIdentifier}.addEventListener('change', async (event) => {\n`
+ result += `${indent}${indent}const dataTransfer = event.dataTransfer\n`
+ result += `${indent}${indent}const files = event.target.files || dataTransfer.files\n\n`
+ result += `${indent}${indent}const { mesh, webWorker } = await readMeshFile(null, files[0])\n`
+ result += `${indent}${indent}webWorker.terminate()\n`
+ result += `${indent}${indent}model.${modelProperty}.set("${parameterName}", mesh)\n`
+ result += `${indent}${indent}const details = document.getElementById("${functionName}-${parameter.name}-details")\n`
+ result += `${indent}${indent}details.innerHTML = \`$\{globalThis.escapeHtml(JSON.stringify(mesh, globalThis.interfaceTypeJsonReplacer, 2))}\`\n`
+ result += `${indent}${indent}details.disabled = false\n`
+ result += `${indent}})\n\n`
+ break
default:
console.error(`Unexpected interface type: ${parameter.type}`)
process.exit(1)
diff --git a/src/bindgen/python-web-demo/interface-function-python.js b/src/bindgen/python-web-demo/interface-function-python.js
index 116cff729..e35c2d686 100644
--- a/src/bindgen/python-web-demo/interface-function-python.js
+++ b/src/bindgen/python-web-demo/interface-function-python.js
@@ -16,6 +16,7 @@ function interfaceFunctionPython(packageName, interfaceJson, outputPath) {
let result = `from dataclasses import dataclass
from typing import Any, Dict
+import json
import numpy as np
diff --git a/src/bindgen/typescript/resources/demo-app/index.html b/src/bindgen/typescript/resources/demo-app/index.html
index 8ba0b3007..c3b3c68ad 100644
--- a/src/bindgen/typescript/resources/demo-app/index.html
+++ b/src/bindgen/typescript/resources/demo-app/index.html
@@ -21,6 +21,7 @@