diff --git a/.github/workflows/npm-publish.yml b/.github/workflows/npm-publish.yml index 9a7cc02..4bfc5f4 100644 --- a/.github/workflows/npm-publish.yml +++ b/.github/workflows/npm-publish.yml @@ -1,6 +1,3 @@ -# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created -# For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages - name: NPM Package Publish on: @@ -38,20 +35,15 @@ jobs: with: node-version: 22 registry-url: https://registry.npmjs.org/ - - run: | + - name: Check if version is already published + run: | + PACKAGE_VERSION=$(node -p "require('./package.json').version") PACKAGE_NAME=$(jq -r '.name' package.json) - PACKAGE_VERSION=$(jq -r '.version' package.json) - - # Get the version from npm registry - REGISTRY_VERSION=$(npm show "$PACKAGE_NAME" version) - - # Compare versions - if [ "$PACKAGE_VERSION" == "$REGISTRY_VERSION" ]; then - echo "Version $PACKAGE_VERSION already exists in the npm registry." - exit 0 - else - echo "Version $PACKAGE_VERSION does not exist in the npm registry. Proceeding..." - npm publish --provenance --access public - fi + npm view $PACKAGE_NAME@$PACKAGE_VERSION + continue-on-error: true + id: check_version + - name: Publish + if: steps.check_version.outcome != 'success' + run: npm publish --provenance --access public env: NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} diff --git a/deno-src/index.ts b/deno-src/index.ts index 13883b7..d84e158 100644 --- a/deno-src/index.ts +++ b/deno-src/index.ts @@ -3,6 +3,7 @@ import { expose } from '@kunkun/api/runtime/deno'; import { image } from '@hk/photographer-toolbox'; import { convertDate } from './lib.ts'; import { ExifDateTime, ExifTool } from 'exiftool-vendored'; +import sharp, { FormatEnum } from 'sharp'; export function batchSmartSetImageOriginalDate( imagePaths: string[], @@ -73,5 +74,13 @@ expose({ console.error(err); throw new Error(err); }); + }, + compressImage: async ( + imagePath: string, + format: keyof FormatEnum, + quality: number, + outputPath: string + ) => { + await image.compressImage(sharp(imagePath), format, quality).toFile(outputPath); } } satisfies API); diff --git a/jsr.json b/jsr.json index 2c9f256..47dbae9 100644 --- a/jsr.json +++ b/jsr.json @@ -1,7 +1,7 @@ { "$schema": "https://jsr.io/schema/config-file.v1.json", "name": "@kunkun/ext-image-processing", - "version": "0.0.21", + "version": "0.1.0", "license": "MIT", "lint": false, "exports": "./mod.ts", diff --git a/package.json b/package.json index 20b6eec..1e0b9b4 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.kunkun.sh", "name": "kunkun-ext-image-processing", - "version": "0.0.21", + "version": "0.1.0", "license": "MIT", "repository": "https://github.com/kunkunsh/kunkun-ext-image-processing", "kunkun": { @@ -13,7 +13,9 @@ "type": "iconify", "value": "lucide:image" }, - "demoImages": ["https://i.imgur.com/NLFXPOu.png"], + "demoImages": [ + "https://i.imgur.com/NLFXPOu.png" + ], "permissions": [ "dialog:all", "clipboard:read-files", @@ -70,7 +72,7 @@ "dependencies": { "@hk/photographer-toolbox": "npm:@jsr/hk__photographer-toolbox@^0.1.12", "@internationalized/date": "^3.6.0", - "@kksh/api": "^0.0.52", + "@kksh/api": "^0.1.1", "@kksh/svelte5": "^0.1.12", "@tanstack/table-core": "^8.20.5", "bits-ui": "1.0.0-next.77", @@ -80,6 +82,7 @@ "lucide-svelte": "^0.469.0", "mode-watcher": "^0.5.0", "paneforge": "^0.0.6", + "sharp": "^0.33.5", "svelte-radix": "^2.0.1", "tailwind-merge": "^2.6.0", "tailwind-variants": "^0.3.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b912f6e..43ceb32 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -15,8 +15,8 @@ importers: specifier: ^3.6.0 version: 3.6.0 '@kksh/api': - specifier: ^0.0.52 - version: 0.0.52(axios@1.7.9)(svelte@5.16.6)(typescript@5.7.2) + specifier: ^0.1.1 + version: 0.1.1(axios@1.7.9)(svelte@5.16.6)(typescript@5.7.2) '@kksh/svelte5': specifier: ^0.1.12 version: 0.1.12(lucide-svelte@0.469.0(svelte@5.16.6))(svelte-sonner@0.3.28(svelte@5.16.6))(svelte@5.16.6)(sveltekit-superforms@2.22.1(@sveltejs/kit@2.15.2(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.16.6)(vite@6.0.7(@types/node@22.10.5)(jiti@1.21.7)(yaml@2.7.0)))(svelte@5.16.6)(vite@6.0.7(@types/node@22.10.5)(jiti@1.21.7)(yaml@2.7.0)))(@types/json-schema@7.0.15)(svelte@5.16.6)(typescript@5.7.2))(typescript@5.7.2) @@ -44,6 +44,9 @@ importers: paneforge: specifier: ^0.0.6 version: 0.0.6(svelte@5.16.6) + sharp: + specifier: ^0.33.5 + version: 0.33.5 svelte-radix: specifier: ^2.0.1 version: 2.0.1(svelte@5.16.6) @@ -602,8 +605,8 @@ packages: '@jsr/valibot__valibot@0.42.1': resolution: {integrity: sha512-JjIzyXUQTkmTbiDXUJoFHDigviK12MUd69lGA28fGuhfivzvFv8TgPxkfH3I0wL0skCc5Rl5+g1IFnwYwVZWRw==, tarball: https://npm.jsr.io/~/11/@jsr/valibot__valibot/0.42.1.tgz} - '@kksh/api@0.0.52': - resolution: {integrity: sha512-ss1cGJaO58iGkUcuBCcKCaepX4iquf+78VT8wh0l409proGgN68cTPpSPECuK9r3BrqnIocTOtn9nDtfHdxj+A==} + '@kksh/api@0.1.1': + resolution: {integrity: sha512-/9JLyOSAK4/dZ74LKzbqJ8LRT0otwtecS+I/k1Bs25m+DfYX8ONaWUwuwc5yufus6vqNbfAF/PHOCEs0aAE39A==} '@kksh/svelte5@0.1.12': resolution: {integrity: sha512-7Az+8U842j4vKgIuMeWGEaCmSM5VSNlhUXT1sirEvjkRKzvVpctL/wIPEAQkSL/r1FpG+C0WDw0sGd4vgI70xg==} @@ -633,6 +636,58 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} + '@octokit/auth-token@5.1.2': + resolution: {integrity: sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==} + engines: {node: '>= 18'} + + '@octokit/core@6.1.3': + resolution: {integrity: sha512-z+j7DixNnfpdToYsOutStDgeRzJSMnbj8T1C/oQjB6Aa+kRfNjs/Fn7W6c8bmlt6mfy3FkgeKBRnDjxQow5dow==} + engines: {node: '>= 18'} + + '@octokit/endpoint@10.1.2': + resolution: {integrity: sha512-XybpFv9Ms4hX5OCHMZqyODYqGTZ3H6K6Vva+M9LR7ib/xr1y1ZnlChYv9H680y77Vd/i/k+thXApeRASBQkzhA==} + engines: {node: '>= 18'} + + '@octokit/graphql@8.2.0': + resolution: {integrity: sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==} + engines: {node: '>= 18'} + + '@octokit/openapi-types@23.0.1': + resolution: {integrity: sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==} + + '@octokit/plugin-paginate-rest@11.4.0': + resolution: {integrity: sha512-ttpGck5AYWkwMkMazNCZMqxKqIq1fJBNxBfsFwwfyYKTf914jKkLF0POMS3YkPBwp5g1c2Y4L79gDz01GhSr1g==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-request-log@5.3.1': + resolution: {integrity: sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/plugin-rest-endpoint-methods@13.3.0': + resolution: {integrity: sha512-LUm44shlmkp/6VC+qQgHl3W5vzUP99ZM54zH6BuqkJK4DqfFLhegANd+fM4YRLapTvPm4049iG7F3haANKMYvQ==} + engines: {node: '>= 18'} + peerDependencies: + '@octokit/core': '>=6' + + '@octokit/request-error@6.1.6': + resolution: {integrity: sha512-pqnVKYo/at0NuOjinrgcQYpEbv4snvP3bKMRqHaD9kIsk9u1LCpb2smHZi8/qJfgeNqLo5hNW4Z7FezNdEo0xg==} + engines: {node: '>= 18'} + + '@octokit/request@9.2.0': + resolution: {integrity: sha512-kXLfcxhC4ozCnAXy2ff+cSxpcF0A1UqxjvYMqNuPIeOAzJbVWQ+dy5G2fTylofB/gTbObT8O6JORab+5XtA1Kw==} + engines: {node: '>= 18'} + + '@octokit/rest@21.1.0': + resolution: {integrity: sha512-93iLxcKDJboUpmnUyeJ6cRIi7z7cqTZT1K7kRK4LobGxwTwpsa+2tQQbRQNGy7IFDEAmrtkf4F4wBj3D5rVlJQ==} + engines: {node: '>= 18'} + + '@octokit/types@13.8.0': + resolution: {integrity: sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==} + '@photostructure/tz-lookup@11.0.0': resolution: {integrity: sha512-QMV5/dWtY/MdVPXZs/EApqzyhnqDq1keYEqpS+Xj2uidyaqw2Nk/fWcsszdruIXjdqp1VoWNzsgrO6bUHU1mFw==} @@ -1119,6 +1174,9 @@ packages: resolution: {integrity: sha512-EreW0Vi8TwovhYUHBXXRA5tthuU2ynGsZFlboyMJHCCUXYa2AjgwnE3ubBOJs2xJLcuXFJbi6c/8pH5+FVj8Og==} engines: {node: '>=14'} + before-after-hook@3.0.2: + resolution: {integrity: sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==} + binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} @@ -1432,6 +1490,9 @@ packages: resolution: {integrity: sha512-h5+1OzzfCC3Ef7VbtKdcv7zsstUQwUDlYpUTvjeUsJAssPgLn7QzbboPtL5ro04Mq0rPOsMzl7q5hIbRs2wD1A==} engines: {node: '>=8.0.0'} + fast-content-type-parse@2.0.1: + resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} + fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} @@ -1727,13 +1788,8 @@ packages: keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - kkrpc@0.0.13: - resolution: {integrity: sha512-66ohRjbw2fLO45L1PMkGTP6KHk/Wa7nKJL6r4U/VCBsY+HoIncEdscSQcUgIdv2Da66/UyIC6UMWMvwIqXCZCg==} - peerDependencies: - typescript: ^5.6.3 - - kkrpc@0.0.14: - resolution: {integrity: sha512-SbvObowERwV6UNYMsQOq9aO1f9+XRoZqVoaNChSTeJ7DbsCBeebnAYcPXpPL3wdxS0HNF5hyIxbhg2LkaFjbzQ==} + kkrpc@0.1.1: + resolution: {integrity: sha512-zS75NGmDbMEQNJ7Y1XVNVrLmfuCY94jMCrIcNFHoT+5qLaTzpK1lokXUD9VYrRVMQMwAx8U9BgKjdiS0eDKZHA==} peerDependencies: typescript: ^5.0.0 @@ -2419,8 +2475,8 @@ packages: tanu@0.1.13: resolution: {integrity: sha512-UbRmX7ccZ4wMVOY/Uw+7ji4VOkEYSYJG1+I4qzbnn4qh/jtvVbrm6BFnF12NQQ4+jGv21wKmjb1iFyUSVnBWcQ==} - tauri-api-adapter@0.3.17: - resolution: {integrity: sha512-Y5LrEt9wLMITchScUFOLOddqcajHJF5XxYggzn/6txNPrOXStgvo3x2midl20Eux4cMehhX0HLpbxvEg+ma2VQ==} + tauri-api-adapter@0.3.20: + resolution: {integrity: sha512-tRK25c1d34ZRd5CJAXfrBeTr4eGh0UPshLac1DBm4TBF+EZ1TFCwLpvUj9DQ3VS2gHKA9fNc4J5pNpHh3OD4Og==} peerDependencies: typescript: ^5.0.0 @@ -2532,6 +2588,9 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + universal-user-agent@7.0.2: + resolution: {integrity: sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==} + universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} @@ -3148,9 +3207,10 @@ snapshots: '@jsr/valibot__valibot@0.42.1': {} - '@kksh/api@0.0.52(axios@1.7.9)(svelte@5.16.6)(typescript@5.7.2)': + '@kksh/api@0.1.1(axios@1.7.9)(svelte@5.16.6)(typescript@5.7.2)': dependencies: '@huakunshen/jsr-client': 0.1.5(axios@1.7.9)(typescript@5.7.2) + '@octokit/rest': 21.1.0 '@tauri-apps/api': 2.2.0 '@tauri-apps/cli': 2.2.2 '@tauri-apps/plugin-deep-link': 2.2.0 @@ -3166,13 +3226,13 @@ snapshots: '@tauri-apps/plugin-store': 2.2.0 '@tauri-apps/plugin-updater': 2.3.1 '@tauri-apps/plugin-upload': 2.2.1 - kkrpc: 0.0.13(typescript@5.7.2) + kkrpc: 0.1.1(typescript@5.7.2) lodash: 4.17.21 minimatch: 10.0.1 node-fetch: 3.3.2 semver: 7.6.3 svelte-sonner: 0.3.28(svelte@5.16.6) - tauri-api-adapter: 0.3.17(typescript@5.7.2) + tauri-api-adapter: 0.3.20(typescript@5.7.2) tauri-plugin-network-api: 2.0.5(typescript@5.7.2) tauri-plugin-shellx-api: 2.0.14 tauri-plugin-system-info-api: 2.0.8(typescript@5.7.2) @@ -3233,6 +3293,68 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.18.0 + '@octokit/auth-token@5.1.2': {} + + '@octokit/core@6.1.3': + dependencies: + '@octokit/auth-token': 5.1.2 + '@octokit/graphql': 8.2.0 + '@octokit/request': 9.2.0 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.8.0 + before-after-hook: 3.0.2 + universal-user-agent: 7.0.2 + + '@octokit/endpoint@10.1.2': + dependencies: + '@octokit/types': 13.8.0 + universal-user-agent: 7.0.2 + + '@octokit/graphql@8.2.0': + dependencies: + '@octokit/request': 9.2.0 + '@octokit/types': 13.8.0 + universal-user-agent: 7.0.2 + + '@octokit/openapi-types@23.0.1': {} + + '@octokit/plugin-paginate-rest@11.4.0(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/types': 13.8.0 + + '@octokit/plugin-request-log@5.3.1(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + + '@octokit/plugin-rest-endpoint-methods@13.3.0(@octokit/core@6.1.3)': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/types': 13.8.0 + + '@octokit/request-error@6.1.6': + dependencies: + '@octokit/types': 13.8.0 + + '@octokit/request@9.2.0': + dependencies: + '@octokit/endpoint': 10.1.2 + '@octokit/request-error': 6.1.6 + '@octokit/types': 13.8.0 + fast-content-type-parse: 2.0.1 + universal-user-agent: 7.0.2 + + '@octokit/rest@21.1.0': + dependencies: + '@octokit/core': 6.1.3 + '@octokit/plugin-paginate-rest': 11.4.0(@octokit/core@6.1.3) + '@octokit/plugin-request-log': 5.3.1(@octokit/core@6.1.3) + '@octokit/plugin-rest-endpoint-methods': 13.3.0(@octokit/core@6.1.3) + + '@octokit/types@13.8.0': + dependencies: + '@octokit/openapi-types': 23.0.1 + '@photostructure/tz-lookup@11.0.0': {} '@pkgjs/parseargs@0.11.0': @@ -3714,6 +3836,8 @@ snapshots: batch-cluster@13.0.0: {} + before-after-hook@3.0.2: {} + binary-extensions@2.3.0: {} bits-ui@0.21.16(svelte@5.16.6): @@ -4072,6 +4196,8 @@ snapshots: pure-rand: 6.1.0 optional: true + fast-content-type-parse@2.0.1: {} + fast-deep-equal@3.1.3: {} fast-glob@3.3.3: @@ -4352,15 +4478,7 @@ snapshots: dependencies: json-buffer: 3.0.1 - kkrpc@0.0.13(typescript@5.7.2): - dependencies: - typescript: 5.7.2 - ws: 8.18.0 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - kkrpc@0.0.14(typescript@5.7.2): + kkrpc@0.1.1(typescript@5.7.2): dependencies: typescript: 5.7.2 ws: 8.18.0 @@ -5023,7 +5141,7 @@ snapshots: tslib: 2.8.1 typescript: 4.9.5 - tauri-api-adapter@0.3.17(typescript@5.7.2): + tauri-api-adapter@0.3.20(typescript@5.7.2): dependencies: '@tauri-apps/api': 2.2.0 '@tauri-apps/plugin-dialog': 2.2.0 @@ -5034,7 +5152,7 @@ snapshots: '@tauri-apps/plugin-os': 2.2.0 '@tauri-apps/plugin-shell': 2.2.0 '@tauri-apps/plugin-upload': 2.2.1 - kkrpc: 0.0.14(typescript@5.7.2) + kkrpc: 0.1.1(typescript@5.7.2) rimraf: 6.0.1 shx: 0.3.4 tauri-plugin-clipboard-api: 2.1.11(typescript@5.7.2) @@ -5157,6 +5275,8 @@ snapshots: undici-types@6.20.0: {} + universal-user-agent@7.0.2: {} + universalify@2.0.1: {} update-browserslist-db@1.1.1(browserslist@4.24.3): diff --git a/scripts/build-template-ext.ts b/scripts/build-template-ext.ts index e67ec87..601c605 100644 --- a/scripts/build-template-ext.ts +++ b/scripts/build-template-ext.ts @@ -1,6 +1,6 @@ import { watch } from 'fs'; import { join } from 'path'; -import { refreshTemplateWorkerExtension } from '@kksh/api/dev'; +import { refreshTemplateWorkerCommand } from '@kksh/api/dev'; import { $ } from 'bun'; const entrypoints = ['./template-ext-src/image-info.ts']; @@ -20,7 +20,7 @@ async function build() { // console.log(distFile); // await $`cp ${distFile} "/Users/hk/Library/Application Support/sh.kunkun.desktop/extensions/image-processing/dist/image-info.js"`; if (Bun.argv.includes('dev')) { - await refreshTemplateWorkerExtension(); + await refreshTemplateWorkerCommand(); } } catch (error) { console.error(error); diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index c180a86..d77bf00 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -3,7 +3,7 @@ import { ModeWatcher } from 'mode-watcher'; import { ThemeWrapper, updateTheme } from '@kksh/svelte5'; import { onMount } from 'svelte'; - import { ui } from '@kksh/api/ui/iframe'; + import { ui } from '@kksh/api/ui/custom'; onMount(() => { ui.registerDragRegion(); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 99a9768..3a3a72d 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,7 +1,7 @@ diff --git a/src/types.ts b/src/types.ts index 3bebe1c..29e12ba 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,5 @@ import { image } from '@hk/photographer-toolbox'; +import { type FormatEnum } from 'sharp'; import type { ImageMetadata } from '@hk/photographer-toolbox/types'; export type ImageMetadataMod = ImageMetadata & { @@ -19,4 +20,10 @@ export type API = { baseImagePath: string, targetDateIso: string ) => Promise; + compressImage: ( + imagePath: string, + format: keyof FormatEnum, + quality: number, + outputPath: string + ) => Promise; }; diff --git a/template-ext-src/image-info.ts b/template-ext-src/image-info.ts index 4e9c1ec..836495b 100644 --- a/template-ext-src/image-info.ts +++ b/template-ext-src/image-info.ts @@ -16,10 +16,10 @@ import { system, toast, ui, - WorkerExtension -} from '@kksh/api/ui/worker'; + TemplateUiCommand +} from '@kksh/api/ui/template'; -class ImageInfo extends WorkerExtension { +class ImageInfo extends TemplateUiCommand { api: API | undefined; apiProcess: Child | undefined; imageMetadata: Record = {};